diff options
12 files changed, 203 insertions, 57 deletions
diff --git a/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java b/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java index 84a176c2f7d..d5ca15f10b9 100644 --- a/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java +++ b/container-search/src/main/java/com/yahoo/prelude/query/WeightedSetItem.java @@ -182,11 +182,12 @@ public class WeightedSetItem extends SimpleTaggableItem { @Override public boolean equals(Object o) { + if (o == this) return true; if ( ! super.equals(o)) return false; var other = (WeightedSetItem)o; if ( ! Objects.equals(this.indexName, other.indexName)) return false; if ( ! Objects.equals(this.set, other.set)) return false; - return false; + return true; } @Override diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java index 18c2e794994..ebd6f230fc7 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -45,12 +45,6 @@ public class Flags { private static volatile TreeMap<FlagId, FlagDefinition> flags = new TreeMap<>(); - public static final UnboundBooleanFlag FORCE_DISK_ENCRYPTION = defineFeatureFlag( - "force-disk-encryption", true, - List.of("hakonhall"), "2021-10-01", "2021-11-01", - "Enable new conditions for when to encrypt disk.", - "Takes effect on next host admin tick."); - public static final UnboundBooleanFlag MAP_USER_NAMESPACE = defineFeatureFlag( "map-user-namespace", false, List.of("freva"), "2021-10-18", "2021-12-01", @@ -213,7 +207,7 @@ public class Flags { public static final UnboundIntFlag MAX_ACTIVATION_INHIBITED_OUT_OF_SYNC_GROUPS = defineIntFlag( "max-activation-inhibited-out-of-sync-groups", 0, - List.of("vekterli"), "2021-02-19", "2021-11-01", + List.of("vekterli"), "2021-02-19", "2022-02-01", "Allows replicas in up to N content groups to not be activated " + "for query visibility if they are out of sync with a majority of other replicas", "Takes effect at redeployment", @@ -248,18 +242,6 @@ public class Flags { "Takes effect on next internal redeployment", APPLICATION_ID); - public static final UnboundIntFlag MAX_ENCRYPTING_HOSTS = defineIntFlag( - "max-encrypting-hosts", 0, - List.of("mpolden", "hakonhall"), "2021-05-27", "2021-11-01", - "The maximum number of hosts allowed to encrypt their disk concurrently", - "Takes effect on next run of HostEncrypter, but any currently encrypting hosts will not be cancelled when reducing the limit"); - - public static final UnboundListFlag<String> DEFER_APPLICATION_ENCRYPTION = defineListFlag( - "defer-application-encryption", List.of(), String.class, - List.of("mpolden", "hakonhall"), "2021-06-23", "2021-11-01", - "List of applications where encryption of their host should be deferred", - "Takes effect on next run of HostEncrypter"); - public static final UnboundDoubleFlag MIN_NODE_RATIO_PER_GROUP = defineDoubleFlag( "min-node-ratio-per-group", 0.0, List.of("geirst", "vekterli"), "2021-07-16", "2021-12-01", diff --git a/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNode.java b/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNode.java index 6a84c7ff950..b90a5aadbb6 100644 --- a/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNode.java +++ b/searchlib/src/main/java/com/yahoo/searchlib/expression/StringResultNode.java @@ -6,6 +6,8 @@ import com.yahoo.vespa.objects.Deserializer; import com.yahoo.vespa.objects.ObjectVisitor; import com.yahoo.vespa.objects.Serializer; +import java.util.Arrays; + /** * This result holds a string. * @@ -16,18 +18,20 @@ public class StringResultNode extends SingleResultNode { // The global class identifier shared with C++. public static final int classId = registerClass(0x4000 + 53, StringResultNode.class); - private static StringResultNode negativeInfinity = new StringResultNode(""); - private static PositiveInfinityResultNode positiveInfinity = new PositiveInfinityResultNode(); + private static final StringResultNode negativeInfinity = new StringResultNode(""); + private static final PositiveInfinityResultNode positiveInfinity = new PositiveInfinityResultNode(); + + private static final byte[] EMPTY_UTF8_ARRAY = new byte[0]; - // The string value of this node. - private String value; + // The string value of this node, in raw UTF-8 octets. + private byte[] utf8Value; /** * Constructs an empty result node. <b>NOTE:</b> This instance is broken until non-optional member data is set. */ public StringResultNode() { super(); - value = ""; + utf8Value = EMPTY_UTF8_ARRAY; } /** @@ -40,6 +44,19 @@ public class StringResultNode extends SingleResultNode { setValue(value); } + private StringResultNode(byte[] rawUtf8Value) { + super(); + utf8Value = rawUtf8Value; + } + + /** + * Creates a new StringResultNode backed by an underlying byte array. The input is + * presumed to be in valid UTF-8 format, but is _not_ checked for validity. + */ + protected static StringResultNode ofUncheckedUtf8Array(byte[] rawUtf8Value) { + return new StringResultNode(rawUtf8Value); + } + /** * Sets the value of this result. * @@ -50,7 +67,7 @@ public class StringResultNode extends SingleResultNode { if (value == null) { throw new IllegalArgumentException("Value can not be null."); } - this.value = value; + this.utf8Value = Utf8.toBytes(value); return this; } @@ -68,13 +85,14 @@ public class StringResultNode extends SingleResultNode { @Override protected void onDeserialize(Deserializer buf) { - value = getUtf8(buf); + // We expect the UTF-8 we get from the backend to be pre-checked and valid. + utf8Value = getRawUtf8Bytes(buf); } @Override public long getInteger() { try { - return Integer.valueOf(value); + return Integer.valueOf(getString()); } catch (java.lang.NumberFormatException e) { return 0; } @@ -83,7 +101,7 @@ public class StringResultNode extends SingleResultNode { @Override public double getFloat() { try { - return Double.valueOf(value); + return Double.valueOf(getString()); } catch (java.lang.NumberFormatException e) { return 0; } @@ -91,50 +109,53 @@ public class StringResultNode extends SingleResultNode { @Override public String getString() { - return value; + return Utf8.toString(utf8Value); } @Override public byte[] getRaw() { - return Utf8.toBytes(value); + return utf8Value; } @Override protected int onCmp(ResultNode rhs) { return (rhs instanceof PositiveInfinityResultNode) ? -1 - : value.compareTo(rhs.getString()); + : internalNonPositiveInfinityCompareTo(rhs); } @Override public int hashCode() { - return super.hashCode() + value.hashCode(); + return super.hashCode() + Arrays.hashCode(utf8Value); } @Override public void visitMembers(ObjectVisitor visitor) { super.visitMembers(visitor); - visitor.visit("value", value); + visitor.visit("value", getString()); } + @Override public void add(ResultNode rhs) { - value += rhs.getString(); + setValue(getString() + rhs.getString()); } + @Override public void min(ResultNode rhs) { - if (value.compareTo(rhs.getString()) > 0) { - value = rhs.getString(); + if (internalNonPositiveInfinityCompareTo(rhs) > 0) { + set(rhs); } } + @Override public void max(ResultNode rhs) { - if (value.compareTo(rhs.getString()) < 0) { - value = rhs.getString(); + if (internalNonPositiveInfinityCompareTo(rhs) < 0) { + set(rhs); } } public void append(ResultNode rhs) { - value += rhs.getString(); + setValue(getString() + rhs.getString()); } @Override @@ -144,16 +165,34 @@ public class StringResultNode extends SingleResultNode { @Override public void set(ResultNode rhs) { - value = rhs.getString(); + if (rhs instanceof StringResultNode) { + utf8Value = ((StringResultNode) rhs).utf8Value; + } else { + setValue(rhs.getString()); + } } @Override public void negate() { - char a[] = value.toCharArray(); + char[] a = getString().toCharArray(); for (int i = 0; i < a.length; i++) { a[i] = (char)-a[i]; } - value = new String(a); + setValue(new String(a)); + } + + private int internalNonPositiveInfinityCompareTo(ResultNode rhs) { + // Note: this may not necessarily be well-defined _semantically_ unless rhs is + // also a StringResultNode. The C++ implementation explicitly expects rhs to be + // such an instance, but this depends on a classId check that is _not_ done in + // the Java implementation... + // We use getString() instead of getRaw() to support implicit stringification + // (legacy Java implementation behavior), but it's not given that this is always + // the desired outcome. + var rhsAsUtf8 = (rhs instanceof StringResultNode) + ? ((StringResultNode)rhs).utf8Value + : Utf8.toBytes(rhs.getString()); + return Arrays.compareUnsigned(utf8Value, rhsAsUtf8); } /** diff --git a/searchlib/src/test/java/com/yahoo/searchlib/expression/ExpressionTestCase.java b/searchlib/src/test/java/com/yahoo/searchlib/expression/ExpressionTestCase.java index c5adfed4974..6e034cce652 100644 --- a/searchlib/src/test/java/com/yahoo/searchlib/expression/ExpressionTestCase.java +++ b/searchlib/src/test/java/com/yahoo/searchlib/expression/ExpressionTestCase.java @@ -705,6 +705,21 @@ public class ExpressionTestCase { assertEquals(0, large.compareTo(large)); } + private void compareAllPairwise(ResultNode... orderedNodes) { + for (int i = 0; i < orderedNodes.length; ++i) { + for (int j = 0; j < orderedNodes.length; ++j) { + var ctx = String.format("lhs(i=%d): %s, rhs(j=%d): %s", i, orderedNodes[i], j, orderedNodes[j]); + if (j < i) { + assertTrue(ctx, orderedNodes[i].compareTo(orderedNodes[j]) > 0); + } else if (j > i) { + assertTrue(ctx, orderedNodes[i].compareTo(orderedNodes[j]) < 0); + } else { // j == i + assertEquals(ctx, orderedNodes[i].compareTo(orderedNodes[j]), 0); + } + } + } + } + @Test public void testStringResultNode() { try { @@ -719,16 +734,87 @@ public class ExpressionTestCase { } catch (IllegalArgumentException e) { // expected } - StringResultNode a = new StringResultNode("7.3"); + StringResultNode a = new StringResultNode("42"); + assertEquals(a.getInteger(), 42); + assertEquals(a.getFloat(), 42, delta); + assertEquals(a.getString(), "42"); + + a = new StringResultNode("7.3"); assertEquals(a.getInteger(), 0); assertEquals(a.getFloat(), 7.3, delta); assertEquals(a.getString(), "7.3"); + byte[] raw = a.getRaw(); assertEquals(raw.length, 3); + assertEquals(Arrays.compareUnsigned(raw, Utf8.toBytes("7.3")), 0); + assertResultNode(a); compare(new StringResultNode(), new StringResultNode("z"), new StringResultNode("zz")); compare(new StringResultNode("z"), new StringResultNode("zz"), new StringResultNode("zzz")); compare(new StringResultNode("a"), new StringResultNode("zz"), new PositiveInfinityResultNode()); + assertEquals(new StringResultNode("zz").compareTo(new StringResultNode("zz")), 0); + + // Remove this assertion if we remove support for implicit stringification + compare(new StringResultNode(), new StringResultNode("12"), new IntegerResultNode(13)); + + var node = new StringResultNode("foo"); + node.append(new StringResultNode("bar")); + assertEquals(node.getString(), "foobar"); + node.add(new StringResultNode("baz")); + assertEquals(node.getString(), "foobarbaz"); + + node.set(new StringResultNode("ABC")); + assertEquals(node.getString(), "ABC"); + + node.max(new StringResultNode("ABA")); + assertEquals(node.getString(), "ABC"); + + node.max(new StringResultNode("ABD")); + assertEquals(node.getString(), "ABD"); + + node.min(new StringResultNode("ABE")); + assertEquals(node.getString(), "ABD"); + + node.min(new StringResultNode("ABC")); + assertEquals(node.getString(), "ABC"); + + node.set(new IntegerResultNode(1234)); + assertEquals(node.getString(), "1234"); + } + + @Test + public void string_result_node_comparison_is_memcmp_unsigned_byte_ordered() { + // Comparison is first by shared prefix (if equal), then ties are resolved by checking length. + // This is not actually valid UTF-8, so we depend on unchecked-ness... + compareAllPairwise(StringResultNode.ofUncheckedUtf8Array(new byte[] { 0 }), + StringResultNode.ofUncheckedUtf8Array(new byte[] { 0, 0 }), + StringResultNode.ofUncheckedUtf8Array(new byte[] { 0, 0, 1 }), + StringResultNode.ofUncheckedUtf8Array(new byte[] { 1 }), + StringResultNode.ofUncheckedUtf8Array(new byte[] { 127 }), // 0x7F + StringResultNode.ofUncheckedUtf8Array(new byte[] { -128 }), // 0x80 + StringResultNode.ofUncheckedUtf8Array(new byte[] { -1 }), // 0xFF + StringResultNode.ofUncheckedUtf8Array(new byte[] { -1, 0 })); + } + + // Ensure that the ordering of string nodes on the container matches that of the C++ backend. + // The backend directly orders on the memcmp order of UTF-8 strings, so the container must do the same. + // In particular, this means that UTF-16 java.lang.String sorting is a no-go. + @Test + public void string_result_node_comparison_is_utf8_byte_ordered() { + // UTF-8 representation of "fried shrimp" emoji U+1F364 which requires a UTF-16 surrogate pair (2 chars) + // Its UTF-16 representation is D83C DF64 + var surrogateUtf8 = new byte[] { -16, -97, -115, -92 }; // F0 9F 8D A4 + // UTF-8 representation of U+F0BA, which exists in the Unicode private usage range. Only requires 1 UTF-16 char. + // Its UTF-16 representation is F0BA + var nonSurrogateUtf8 = new byte[] { -17, -126, -70 }; // EF 82 BA + + var s1 = new StringResultNode(Utf8.toString(surrogateUtf8)); + var s2 = new StringResultNode(Utf8.toString(nonSurrogateUtf8)); + // UTF-8: s2 < s1, since 0xEF < 0xF0 + // UTF-16: s1 < s2, since 0xD83C < 0xF0BA + // Assert that ordering is defined by UTF-8, not (surrogated) UTF-16. + assertTrue(s2.compareTo(s1) < 0); + assertTrue(s1.compareTo(s2) > 0); } @Test diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/Result.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/Result.java index 4cc4c762ccf..93a3b43c211 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/Result.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/Result.java @@ -63,6 +63,11 @@ public class Result { public boolean isSuccess() { return success; } + public boolean isSuccessOrConditionNotMet() { + return isSuccess() || + details.stream().allMatch(d -> d.getResultType() == Result.ResultType.OPERATION_EXECUTED || + d.getResultType() == Result.ResultType.CONDITION_NOT_MET); + } public List<Detail> getDetails() { return details; } diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/SimpleLoggerResultCallback.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/SimpleLoggerResultCallback.java index fcc3e04af78..eba7fbef483 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/SimpleLoggerResultCallback.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/SimpleLoggerResultCallback.java @@ -23,6 +23,7 @@ public class SimpleLoggerResultCallback implements FeedClient.ResultCallback { private int failureCounter = 0; private final AtomicInteger sentDocumentCounter; private final int printStatsForEveryXDocument; + private final boolean ignoreConditionNotMet; private Instant startSampleInstant = Instant.now(); private int startSampleResultCount = 0; @@ -36,9 +37,10 @@ public class SimpleLoggerResultCallback implements FeedClient.ResultCallback { * @param sentDocumentCounter a counter that is increased outside this class, but can be nice to print here. * @param printStatsForEveryXDocument how often to print stats. */ - public SimpleLoggerResultCallback(AtomicInteger sentDocumentCounter, int printStatsForEveryXDocument) { + public SimpleLoggerResultCallback(AtomicInteger sentDocumentCounter, int printStatsForEveryXDocument, boolean ignoreConditionNotMet) { this.sentDocumentCounter = sentDocumentCounter; this.printStatsForEveryXDocument = printStatsForEveryXDocument; + this.ignoreConditionNotMet = ignoreConditionNotMet; } /** @@ -95,7 +97,10 @@ public class SimpleLoggerResultCallback implements FeedClient.ResultCallback { printProgress(); } resultCounter++; - if (!documentResult.isSuccess()) { + boolean success = ignoreConditionNotMet + ? documentResult.isSuccessOrConditionNotMet() + : documentResult.isSuccess(); + if ( ! success ) { failureCounter++; println("Failure: " + documentResult + (documentResult.getDetails().isEmpty() ? "" : ":")); for (Result.Detail detail : documentResult.getDetails()) diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/CommandLineArguments.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/CommandLineArguments.java index 4ccae25f2da..7ccdf3ebd43 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/CommandLineArguments.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/CommandLineArguments.java @@ -193,6 +193,10 @@ public class CommandLineArguments { description = "Run validation tool on input files instead of feeding them.") private boolean validateArg = false; + @Option(name = {"--ignoreConditionNotMet"}, + description = "Ignore condition not met failures.") + private boolean ignoreConditionNotMet = false; + @Option(name = {"--priority"}, description = "Specify priority of sent messages, see documentation ") private String priorityArg = null; @@ -251,6 +255,8 @@ public class CommandLineArguments { public boolean getVerbose() { return verboseArg; } + public boolean getIgnoreConditionNotMet() { return ignoreConditionNotMet; } + public boolean getAddRootElementToXml() { return addRootElementToXml; } SessionParams createSessionParams(boolean useJson) { diff --git a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/Runner.java b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/Runner.java index 60047993f0e..e3e90c8bbfc 100644 --- a/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/Runner.java +++ b/vespa-http-client/src/main/java/com/yahoo/vespa/http/client/runner/Runner.java @@ -76,7 +76,7 @@ public class Runner { ? commandLineArgs.getWhenVerboseEnabledPrintMessageForEveryXDocuments() : Integer.MAX_VALUE; AtomicInteger numSent = new AtomicInteger(0); - SimpleLoggerResultCallback callback = new SimpleLoggerResultCallback(numSent, intervalOfLogging); + SimpleLoggerResultCallback callback = new SimpleLoggerResultCallback(numSent, intervalOfLogging, commandLineArgs.getIgnoreConditionNotMet()); FeedClient feedClient = FeedClientFactory.create(commandLineArgs.createSessionParams(formatInputStream.getFormat()== FormatInputStream.Format.JSON), callback); diff --git a/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/SimpleLoggerResultCallbackTest.java b/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/SimpleLoggerResultCallbackTest.java index 3582ab2696f..5f4a0fdec9a 100644 --- a/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/SimpleLoggerResultCallbackTest.java +++ b/vespa-http-client/src/test/java/com/yahoo/vespa/http/client/SimpleLoggerResultCallbackTest.java @@ -8,14 +8,15 @@ import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class SimpleLoggerResultCallbackTest { @Test public void testAverageCalculation() { - SimpleLoggerResultCallback logger = new SimpleLoggerResultCallback(new AtomicInteger(3), 0); + SimpleLoggerResultCallback logger = new SimpleLoggerResultCallback(new AtomicInteger(3), 0, false); Instant now = Instant.now(); logger.newSamplingPeriod(now); Result result = mock(Result.class); @@ -30,7 +31,7 @@ public class SimpleLoggerResultCallbackTest { @Test public void testAverageCalculationExteremeValues() { - SimpleLoggerResultCallback logger = new SimpleLoggerResultCallback(new AtomicInteger(3), 0); + SimpleLoggerResultCallback logger = new SimpleLoggerResultCallback(new AtomicInteger(3), 0, false); Instant now = Instant.now(); logger.newSamplingPeriod(now); // 0 duration, 0 documents @@ -40,7 +41,7 @@ public class SimpleLoggerResultCallbackTest { @Test public void testOutput() { - SimpleLoggerResultCallback logger = new SimpleLoggerResultCallback(new AtomicInteger(3), 0); + SimpleLoggerResultCallback logger = new SimpleLoggerResultCallback(new AtomicInteger(3), 0, false); Instant now = Instant.now(); logger.newSamplingPeriod(now); Result result = mock(Result.class); @@ -53,11 +54,10 @@ public class SimpleLoggerResultCallbackTest { assertEquals(rate, 15., 0.1 /* delta */); } - @Test - public void testPrintout() { + private void verifyPrintout(boolean ignoreConditionNotMet) { ArrayList<String> outputList = new ArrayList<>(); - SimpleLoggerResultCallback logger = new SimpleLoggerResultCallback(new AtomicInteger(30), 0) { + SimpleLoggerResultCallback logger = new SimpleLoggerResultCallback(new AtomicInteger(30), 0, ignoreConditionNotMet) { @Override protected void println(String output) { outputList.add(output); @@ -70,9 +70,11 @@ public class SimpleLoggerResultCallbackTest { // 2 success, 1 failure Result result = mock(Result.class); when(result.isSuccess()).thenReturn(true); + when(result.isSuccessOrConditionNotMet()).thenReturn(true); logger.onCompletion("1", result); logger.onCompletion("1", result); when(result.isSuccess()).thenReturn(false); + when(result.isSuccessOrConditionNotMet()).thenReturn(false); when(result.toString()).thenReturn("fooError"); logger.onCompletion("1", result); logger.printProgress(); @@ -81,4 +83,10 @@ public class SimpleLoggerResultCallbackTest { assertThat(outputList.toString(), containsString("Failure: fooError")); } + @Test + public void testPrintout() { + verifyPrintout(false); + verifyPrintout(true); + } + } diff --git a/vespajlib/abi-spec.json b/vespajlib/abi-spec.json index c426195bc37..5eeee267cf6 100644 --- a/vespajlib/abi-spec.json +++ b/vespajlib/abi-spec.json @@ -3428,6 +3428,7 @@ "protected static com.yahoo.vespa.objects.Identifiable deserializeOptional(com.yahoo.vespa.objects.Deserializer)", "protected static boolean equals(java.lang.Object, java.lang.Object)", "public void visitMembers(com.yahoo.vespa.objects.ObjectVisitor)", + "protected static byte[] getRawUtf8Bytes(com.yahoo.vespa.objects.Deserializer)", "protected java.lang.String getUtf8(com.yahoo.vespa.objects.Deserializer)", "protected void putUtf8(com.yahoo.vespa.objects.Serializer, java.lang.String)", "public bridge synthetic java.lang.Object clone()" diff --git a/vespajlib/pom.xml b/vespajlib/pom.xml index 7be95661d9a..236e33b3a70 100644 --- a/vespajlib/pom.xml +++ b/vespajlib/pom.xml @@ -122,6 +122,16 @@ </configuration> </plugin> <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <!-- Illegal reflective access by com.yahoo.io.NativeIO --> + <argLine> + --add-opens=java.base/java.io=ALL-UNNAMED + </argLine> + </configuration> + </plugin> + <plugin> <groupId>com.yahoo.vespa</groupId> <artifactId>abi-check-plugin</artifactId> </plugin> diff --git a/vespajlib/src/main/java/com/yahoo/vespa/objects/Identifiable.java b/vespajlib/src/main/java/com/yahoo/vespa/objects/Identifiable.java index 8c11a0cbda1..947b312ac3b 100644 --- a/vespajlib/src/main/java/com/yahoo/vespa/objects/Identifiable.java +++ b/vespajlib/src/main/java/com/yahoo/vespa/objects/Identifiable.java @@ -354,10 +354,13 @@ public class Identifiable extends Selectable implements Cloneable { } } - protected String getUtf8(Deserializer buf) { + protected static byte[] getRawUtf8Bytes(Deserializer buf) { int len = buf.getInt(null); - byte[] arr = buf.getBytes(null, len); - return Utf8.toString(arr); + return buf.getBytes(null, len); + } + + protected String getUtf8(Deserializer buf) { + return Utf8.toString(getRawUtf8Bytes(buf)); } protected void putUtf8(Serializer buf, String val) { |