Skip to content

Markings#3469

Open
jalphonso wants to merge 60 commits into
integrationfrom
accumulo-access
Open

Markings#3469
jalphonso wants to merge 60 commits into
integrationfrom
accumulo-access

Conversation

@jalphonso
Copy link
Copy Markdown
Collaborator

No description provided.

@jalphonso jalphonso self-assigned this Mar 23, 2026
@jalphonso jalphonso marked this pull request as draft March 23, 2026 17:25
@jalphonso jalphonso force-pushed the accumulo-access branch 8 times, most recently from e86e7ba to 0e0b4d2 Compare March 26, 2026 01:35
@jalphonso jalphonso force-pushed the accumulo-access branch 3 times, most recently from 07765cd to 65db1ad Compare March 27, 2026 17:27
@jalphonso jalphonso force-pushed the accumulo-access branch 5 times, most recently from 8bc250c to 98f8567 Compare April 2, 2026 17:24
@jalphonso jalphonso force-pushed the accumulo-access branch 2 times, most recently from cceab28 to e72fd54 Compare April 7, 2026 17:48
@jalphonso jalphonso marked this pull request as ready for review May 12, 2026 19:43
public void addColumn(String columnName, TypedValue columnTypedValue, Map<String,String> markings, String columnVisibility, Long timestamp) {
public void addColumn(String columnName, TypedValue columnTypedValue, Markings<?> markings, String columnVisibility, Long timestamp) {

columnName = columnName.replaceAll(" ", "_");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this isn't part of your change set, but consider the fact that for single character strings, the following is generally more performant because the character to replace isn't treated as a regular expression. Also, generally I prefer to assign it to a new immutable variable instead of reusing a variable name, the impact to the stack is minimal.

Suggested change
columnName = columnName.replaceAll(" ", "_");
final String cleanColumnName = columnName.replace(' ', '_');

There's a second replaceAll on single characters later in this method as well

Comment on lines +55 to 80
if (null == this.markings) {
this.markings = markings;
} else {
// if new markings are the same as the old markings, skip all of this
// they are the same and the markings value has already been validated
if (!this.markings.equals(markings)) {
// incoming markings are null so skip remaining logic
if (null != markings) {
// we have existing markings, but they aren't valid so we update if the new ones are valid
if (this.markings.isEmpty() && !markings.isEmpty()) {
this.markings = markings;
} else {
// otherwise combine
try {
Set<ColumnVisibility> columnVisibilities = Sets.newHashSet();
columnVisibilities.add(this.markings.toColumnVisibility());
columnVisibilities.add(markings.toColumnVisibility());
ColumnVisibility combinedVisibility = markingFunctions.combineVisibilities(columnVisibilities);

// use combined marking as new markings
this.markings = markingFunctions.translateFromColumnVisibility(combinedVisibility);
} catch (MarkingFunctions.Exception e) {
LOGGER.error("Invalid markings {} skipping column {} = {}", markings, columnName, columnTypedValue, e);
return;
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps something like the following instead?

try {
    this.markings = markingFunctions.combine(this.markings, markings);
}
catch (MarkingFunctions.Exception e) {
    LOGGER.error("Invalid markings {} skipping column {} = {}", markings, cleanColumnName, columnTypedValue, e);
    return;
}

request.setFieldValue(fieldValue);
Map<String,String> markings = new HashMap<>();
markings.put(MarkingFunctions.Default.COLUMN_VISIBILITY, columnVisibility);
markings.put("columnVisibility", columnVisibility);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
markings.put("columnVisibility", columnVisibility);
markings.put(AccessExpressionMarkings.COLUMN_VISIBILITY_KEY, columnVisibility);

Comment on lines +125 to +140
public AccessExpressionMarkings combine(Markings<?> markings1, Markings<?> markings2) {
if (markings2 != null) {
if (markings1 == null) {
markings1 = markings2;
} else {
if (markings1 instanceof AccessExpressionMarkings && markings2 instanceof AccessExpressionMarkings) {
AccessExpressionMarkings aem1 = (AccessExpressionMarkings) markings1;
AccessExpressionMarkings aem2 = (AccessExpressionMarkings) markings2;
return combine(List.of(aem1, aem2));
} else {
throw new RuntimeException(
String.format("Unknown markings class %s or %s", markings1.getClass().getName(), markings2.getClass().getName()));
}
}
}
return markings1 == null ? null : (AccessExpressionMarkings) markings1;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider something a little less complex? Still, not a fan of the potential ClassCastExceptions here, so this suggestion might need more thought if we want to avoid or handle those more cleanly.

Suggested change
public AccessExpressionMarkings combine(Markings<?> markings1, Markings<?> markings2) {
if (markings2 != null) {
if (markings1 == null) {
markings1 = markings2;
} else {
if (markings1 instanceof AccessExpressionMarkings && markings2 instanceof AccessExpressionMarkings) {
AccessExpressionMarkings aem1 = (AccessExpressionMarkings) markings1;
AccessExpressionMarkings aem2 = (AccessExpressionMarkings) markings2;
return combine(List.of(aem1, aem2));
} else {
throw new RuntimeException(
String.format("Unknown markings class %s or %s", markings1.getClass().getName(), markings2.getClass().getName()));
}
}
}
return markings1 == null ? null : (AccessExpressionMarkings) markings1;
public AccessExpressionMarkings combine(Markings<?> markings1, Markings<?> markings2) {
if (markings2 == null) {
return (AccessExpressionMarkings) markings1;
}
if (markings1 == null) {
return (AccessExpressionMarkings) markings2;
}
if (markings1 instanceof AccessExpressionMarkings && markings2 instanceof AccessExpressionMarkings) {
AccessExpressionMarkings aem1 = (AccessExpressionMarkings) markings1;
AccessExpressionMarkings aem2 = (AccessExpressionMarkings) markings2;
return combine(List.of(aem1, aem2));
}
throw new RuntimeException(String.format("Unknown markings class %s or %s", markings1.getClass().getName(), markings2.getClass().getName()));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice coverage, but it would be cool to see a few more unhappy-path tests here (e.g., mismatched parenthesis, illegal operators like && and || or + or ,)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, maybe a couple more bizarre tests here like A&A|B&B|(A&A&A) or (A|B|C)&(B|C|D) can normalize clean things like this up?

private static final Logger log = Logger.getLogger(FrequencyMetadataAggregator.class);
private static final String NULL_BYTE = "\0";
private static final MarkingFunctions markingFunctions = MarkingFunctions.Factory.createMarkingFunctions();
private final MarkingFunctions<?> markingFunctions;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if the prevalence of MarkingFunctions<?> and other wildcard generic types throughout this PR is suggesting that we should consider just using plain sub-classing instead of generics the code. Replacing the <?> everywhere it's used it going to be very difficult, but maybe that's not the end goal.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a direct dependency to accumulo-utils in the dictionary api pom?

Markings<?> currMarkings = columnMarkingsMap.get(columnName);

if (currMarkings.equals(markings) == false) {
if (!currMarkings.equals(markings)) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider a null check here?

for (Map.Entry<String,String> entry : combinedColumnMarkings.entrySet()) {
String columnName = entry.getKey();
String combinedString = entry.getValue();
int x = combinedString.lastIndexOf(':');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider whether the combined string might have colons in it from the values created in CacheableQueryRowImpl where we encode `accessExpression + ':' + columnVisibility' and this get this split incorrect.

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public interface Markings<T> {

T getMarkings();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider whether we can just drop this method entirely from the interface definition and just remove the generic type on Markings.


@Test
public void testJaxbMarshal() throws Exception {
JAXBContext ctx = JAXBContext.newInstance(AccessExpressionMarkings.class);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could move all the JAXB boilerplate into a method:

AccessExpressionMarkings original = // ... test setup
AccessExpressionMarkings copy = makeJaxbRoundtrip(original);

// assertions

public void testProtostuffRoundTripEmpty() {
AccessExpressionMarkings original = AccessExpressionMarkings.builder().accessExpression(null).build();

LinkedBuffer buffer = LinkedBuffer.allocate();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as JAXB roundtrip boilerplate.

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we bubble these versions up to the top level parent and just inherit here?

<datawave.webservice.namespace>http://webservice.datawave.nsa/v1</datawave.webservice.namespace>
<version.commons-codec>1.17.1</version.commons-codec>
<version.datawave>7.33.1</version.datawave>
<version.datawave>7.39.0-SNAPSHOT</version.datawave>
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have changes in other repos that we'll need before we can merge this?

import io.protostuff.Message;
import io.protostuff.Output;
import io.protostuff.Schema;
import lombok.Setter;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the only Lombok addition? If the plan is to use it across the board, I'm cool but if this is the only usage, I'd suggest we drop the dependency and leave the original setter.

DESC desc = this.responseObjectFactory.getDescription();
Map<String,String> markings = Maps.newHashMap();
markings.put("columnVisibility", columnVisibility);
Markings<?> markings = AccessExpressionMarkings.builder().accessExpression(ACCESS.newExpression(columnVisibility)).build();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like a large portion of AccessExpressionMarkings are being built from columnVisibilities. Thoughts on adding that to the builder?

AccessExpressionMarkings.builder().colvis(columnVisibility).build();

Multimap<Map.Entry<String,String>,DefaultDescription> descriptions = HashMultimap.create();
descriptions.put(new AbstractMap.SimpleEntry<>("fooField", "fooType"), new DefaultDescription("my foo field", markings));
descriptions.put(new AbstractMap.SimpleEntry<>("barField", "barType"), new DefaultDescription("my bar field", markings));
descriptions.put(new AbstractMap.SimpleEntry<>("fooField", "fooType"), new DefaultDescription("my foo field", AccessExpressionMarkings.create("USER")));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not your code but a nice improvement over the entry creation is Map.entry(key, value) instead of the inner SimpleEntry thing here.

String oldColViz = AccessExpressionUtil.normalize(AccessExpressionUtil.toAccessExpression(oldColumnVisibility)).getExpression();
String thisVis = AccessExpressionUtil.normalize(AccessExpressionUtil.toAccessExpression(thisViz)).getExpression();
if (!oldColViz.equals(thisVis)) {
log.trace("Skipping key that does not match with column visibility: {}", e.getKey());
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small touch, but could check if trace is enabled before the log statement. also on line 880

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants