From 81bbfe5d65afeb8582ab178ae313d8248a39d4f6 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Thu, 1 Aug 2024 15:01:25 +0300 Subject: [PATCH 01/31] OAK-10973 - Performance tune property compression/decompression --- .../plugins/document/DocumentPropertyState.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java index 78f9c6c488a..f27b9c41bef 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java @@ -66,6 +66,7 @@ final class DocumentPropertyState implements PropertyState { private PropertyState parsed; private final byte[] compressedValue; private final Compression compression; + private String decompressedValue; private static int COMPRESSION_THRESHOLD = SystemPropertySupplier .create("oak.documentMK.stringCompressionThreshold ", -1).loggingTo(LOG).get(); @@ -169,12 +170,15 @@ String getValue() { } private String decompress(byte[] value) { - try { - return new String(compression.getInputStream(new ByteArrayInputStream(value)).readAllBytes(), StandardCharsets.UTF_8); - } catch (IOException e) { - LOG.error("Failed to decompress property {} value: ", getName(), e); - return "\"{}\""; + if (decompressedValue == null) { + try { + return new String(compression.getInputStream(new ByteArrayInputStream(value)).readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + LOG.error("Failed to decompress property {} value: ", getName(), e); + return "\"{}\""; + } } + return decompressedValue; } byte[] getCompressedValue() { From c9e2678a0a5ddf0871250b02f7411b8f0575f09c Mon Sep 17 00:00:00 2001 From: pirlogea Date: Thu, 1 Aug 2024 15:45:23 +0300 Subject: [PATCH 02/31] OAK-10973 - Performance tune property compression/decompression --- .../oak/plugins/document/DocumentPropertyState.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java index f27b9c41bef..298b896969f 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java @@ -166,7 +166,13 @@ public int count() { */ @NotNull String getValue() { - return value != null ? value : decompress(this.compressedValue); + if (value != null) { + return value; + } + if (decompressedValue == null) { + decompressedValue = decompress(this.compressedValue); + } + return decompressedValue; } private String decompress(byte[] value) { From a7e0def678e39eac09ae7f9cfebb1a4a84d1639e Mon Sep 17 00:00:00 2001 From: pirlogea Date: Thu, 1 Aug 2024 16:12:02 +0300 Subject: [PATCH 03/31] OAK-10973 - Performance tune property compression/decompression revert --- .../oak/plugins/document/DocumentPropertyState.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java index 298b896969f..f27b9c41bef 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java @@ -166,13 +166,7 @@ public int count() { */ @NotNull String getValue() { - if (value != null) { - return value; - } - if (decompressedValue == null) { - decompressedValue = decompress(this.compressedValue); - } - return decompressedValue; + return value != null ? value : decompress(this.compressedValue); } private String decompress(byte[] value) { From 5567b285bcdd727509075832d067466f49b182ba Mon Sep 17 00:00:00 2001 From: pirlogea Date: Thu, 1 Aug 2024 16:52:11 +0300 Subject: [PATCH 04/31] OAK-10973 - Performance tune property compression/decompression set decompressedValue --- .../oak/plugins/document/DocumentPropertyState.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java index f27b9c41bef..0aa10d36cb2 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java @@ -172,10 +172,10 @@ String getValue() { private String decompress(byte[] value) { if (decompressedValue == null) { try { - return new String(compression.getInputStream(new ByteArrayInputStream(value)).readAllBytes(), StandardCharsets.UTF_8); + decompressedValue = new String(compression.getInputStream(new ByteArrayInputStream(value)).readAllBytes(), StandardCharsets.UTF_8); } catch (IOException e) { LOG.error("Failed to decompress property {} value: ", getName(), e); - return "\"{}\""; + decompressedValue = "\"{}\""; } } return decompressedValue; From fb073690d614f813e980d6e22069af86043e4f3c Mon Sep 17 00:00:00 2001 From: pirlogea Date: Mon, 5 Aug 2024 10:18:45 +0300 Subject: [PATCH 05/31] OAK-10973 - Performance tune property compression/decompression set decompressedValue --- .../CompressedDocumentPropertyState.java | 358 +++++++++++++++++ .../plugins/document/DocumentNodeStore.java | 6 +- .../document/DocumentPropertyState.java | 108 +----- .../DocumentPropertyStateFactory.java | 15 + .../CompressedDocumentPropertyStateTest.java | 359 ++++++++++++++++++ .../document/DocumentPropertyStateTest.java | 315 --------------- 6 files changed, 748 insertions(+), 413 deletions(-) create mode 100644 oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java create mode 100644 oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java create mode 100644 oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java new file mode 100644 index 00000000000..bf8dcfd7255 --- /dev/null +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java @@ -0,0 +1,358 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.plugins.document; + +import static java.util.Collections.emptyList; +import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +import javax.jcr.PropertyType; + +import org.apache.jackrabbit.guava.common.collect.Lists; +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.commons.Compression; +import org.apache.jackrabbit.oak.commons.LongUtils; +import org.apache.jackrabbit.oak.commons.json.JsopReader; +import org.apache.jackrabbit.oak.commons.json.JsopTokenizer; +import org.apache.jackrabbit.oak.commons.properties.SystemPropertySupplier; +import org.apache.jackrabbit.oak.json.TypeCodes; +import org.apache.jackrabbit.oak.plugins.memory.AbstractPropertyState; +import org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState; +import org.apache.jackrabbit.oak.plugins.memory.BooleanPropertyState; +import org.apache.jackrabbit.oak.plugins.memory.DoublePropertyState; +import org.apache.jackrabbit.oak.plugins.memory.LongPropertyState; +import org.apache.jackrabbit.oak.plugins.memory.PropertyStates; +import org.apache.jackrabbit.oak.plugins.memory.StringPropertyState; +import org.apache.jackrabbit.oak.plugins.value.Conversions; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * PropertyState implementation with lazy parsing of the JSOP encoded value. + */ +public final class CompressedDocumentPropertyState implements PropertyState { + + private static final Logger LOG = LoggerFactory.getLogger(CompressedDocumentPropertyState.class); + + private final DocumentNodeStore store; + + private final String name; + + private final String value; + + private PropertyState parsed; + private final byte[] compressedValue; + private final Compression compression; + private String decompressedValue; + + private static int COMPRESSION_THRESHOLD = SystemPropertySupplier + .create("oak.documentMK.stringCompressionThreshold ", -1).loggingTo(LOG).get(); + + CompressedDocumentPropertyState(DocumentNodeStore store, String name, String value, Compression compression) { + this.store = store; + this.name = name; + this.compression = compression; + int size = value.length(); + byte[] compressedValue = null; + if (size > COMPRESSION_THRESHOLD) { + try { + compressedValue = compress(value.getBytes(StandardCharsets.UTF_8)); + value = null; + } catch (IOException e) { + LOG.warn("Failed to compress property {} value: ", name, e); + } + } + this.value = value; + this.compressedValue = compressedValue; + } + + private byte[] compress(byte[] value) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + OutputStream compressionOutputStream = compression.getOutputStream(out); + compressionOutputStream.write(value); + compressionOutputStream.close(); + return out.toByteArray(); + } + + @NotNull + @Override + public String getName() { + return name; + } + + @Override + public boolean isArray() { + return parsed().isArray(); + } + + @Override + public Type getType() { + return parsed().getType(); + } + + @NotNull + @Override + public T getValue(Type type) { + return parsed().getValue(type); + } + + @NotNull + @Override + public T getValue(Type type, int index) { + return parsed().getValue(type, index); + } + + @Override + public long size() { + return parsed().size(); + } + + @Override + public long size(int index) { + long size; + PropertyState parsed = parsed(); + if (parsed.getType() == Type.BINARIES) { + size = parsed.getValue(Type.BINARY, index).length(); + } else { + size = parsed.size(index); + } + return size; + } + + @Override + public int count() { + return parsed().count(); + } + + /** + * Returns the raw un-parsed value as passed to the constructor of this + * property state. + * + * @return the raw un-parsed value. + */ + @NotNull + String getValue() { + return value != null ? value : decompress(this.compressedValue); + } + + private String decompress(byte[] value) { + if (decompressedValue == null) { + try { + decompressedValue = new String(compression.getInputStream(new ByteArrayInputStream(value)).readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + LOG.error("Failed to decompress property {} value: ", getName(), e); + decompressedValue = "\"{}\""; + } + } + return decompressedValue; + } + + byte[] getCompressedValue() { + return compressedValue; + } + + //------------------------------------------------------------< Object >-- + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } else if (object instanceof CompressedDocumentPropertyState) { + CompressedDocumentPropertyState other = (CompressedDocumentPropertyState) object; + if (!this.name.equals(other.name) || !Arrays.equals(this.compressedValue, other.compressedValue)) { + return false; + } + if (this.compressedValue == null && other.compressedValue == null) { + return value.equals(other.value); + } else { + // Compare length and content of compressed values + if (this.compressedValue.length != other.compressedValue.length) { + return false; + } + return Arrays.equals(this.compressedValue, other.compressedValue); + } + } + // fall back to default equality check in AbstractPropertyState + return object instanceof PropertyState + && AbstractPropertyState.equal(parsed(), (PropertyState) object); + } + + @Override + public int hashCode() { + return AbstractPropertyState.hashCode(this); + } + + @Override + public String toString() { + return AbstractPropertyState.toString(this); + } + + static void setCompressionThreshold(int compressionThreshold) { + COMPRESSION_THRESHOLD = compressionThreshold; + } + + //----------------------------< internal >---------------------------------- + + private PropertyState parsed() { + if (parsed == null) { + JsopReader reader = new JsopTokenizer(getValue()); + if (reader.matches('[')) { + parsed = readArrayProperty(name, reader); + } else { + parsed = readProperty(name, reader); + } + } + return parsed; + } + + /** + * Read a {@code PropertyState} from a {@link JsopReader} + * @param name The name of the property state + * @param reader The reader + * @return new property state + */ + PropertyState readProperty(String name, JsopReader reader) { + return readProperty(name, store, reader); + } + + /** + * Read a {@code PropertyState} from a {@link JsopReader}. + * + * @param name the name of the property state + * @param store the store + * @param reader the reader + * @return new property state + */ + static PropertyState readProperty(String name, DocumentNodeStore store, JsopReader reader) { + if (reader.matches(JsopReader.NUMBER)) { + String number = reader.getToken(); + Long maybeLong = LongUtils.tryParse(number); + if (maybeLong == null) { + return new DoublePropertyState(name, Double.parseDouble(number)); + } else { + return new LongPropertyState(name, maybeLong); + } + } else if (reader.matches(JsopReader.TRUE)) { + return BooleanPropertyState.booleanProperty(name, true); + } else if (reader.matches(JsopReader.FALSE)) { + return BooleanPropertyState.booleanProperty(name, false); + } else if (reader.matches(JsopReader.STRING)) { + String jsonString = reader.getToken(); + if (jsonString.startsWith(TypeCodes.EMPTY_ARRAY)) { + int type = PropertyType.valueFromName(jsonString.substring(TypeCodes.EMPTY_ARRAY.length())); + return PropertyStates.createProperty(name, emptyList(), Type.fromTag(type, true)); + } + int split = TypeCodes.split(jsonString); + if (split != -1) { + int type = TypeCodes.decodeType(split, jsonString); + String value = TypeCodes.decodeName(split, jsonString); + if (type == PropertyType.BINARY) { + + return BinaryPropertyState.binaryProperty(name, store.getBlobFromBlobId(value)); + } else { + return createProperty(name, StringCache.get(value), type); + } + } else { + return StringPropertyState.stringProperty(name, StringCache.get(jsonString)); + } + } else { + throw new IllegalArgumentException("Unexpected token: " + reader.getToken()); + } + } + + /** + * Read a multi valued {@code PropertyState} from a {@link JsopReader}. + * + * @param name the name of the property state + * @param reader the reader + * @return new property state + */ + PropertyState readArrayProperty(String name, JsopReader reader) { + return readArrayProperty(name, store, reader); + } + + /** + * Read a multi valued {@code PropertyState} from a {@link JsopReader}. + * + * @param name the name of the property state + * @param store the store + * @param reader the reader + * @return new property state + */ + static PropertyState readArrayProperty(String name, DocumentNodeStore store, JsopReader reader) { + int type = PropertyType.STRING; + List values = Lists.newArrayList(); + while (!reader.matches(']')) { + if (reader.matches(JsopReader.NUMBER)) { + String number = reader.getToken(); + Long maybeLong = LongUtils.tryParse(number); + if (maybeLong == null) { + type = PropertyType.DOUBLE; + values.add(Double.parseDouble(number)); + } else { + type = PropertyType.LONG; + values.add(maybeLong); + } + } else if (reader.matches(JsopReader.TRUE)) { + type = PropertyType.BOOLEAN; + values.add(true); + } else if (reader.matches(JsopReader.FALSE)) { + type = PropertyType.BOOLEAN; + values.add(false); + } else if (reader.matches(JsopReader.STRING)) { + String jsonString = reader.getToken(); + int split = TypeCodes.split(jsonString); + if (split != -1) { + type = TypeCodes.decodeType(split, jsonString); + String value = TypeCodes.decodeName(split, jsonString); + if (type == PropertyType.BINARY) { + values.add(store.getBlobFromBlobId(value)); + } else if (type == PropertyType.DOUBLE) { + values.add(Conversions.convert(value).toDouble()); + } else if (type == PropertyType.DECIMAL) { + values.add(Conversions.convert(value).toDecimal()); + } else { + values.add(StringCache.get(value)); + } + } else { + type = PropertyType.STRING; + values.add(StringCache.get(jsonString)); + } + } else { + throw new IllegalArgumentException("Unexpected token: " + reader.getToken()); + } + reader.matches(','); + } + return createProperty(name, values, Type.fromTag(type, true)); + } +} + + + + + + + diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java index f5ed5650755..4be33b22072 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java @@ -77,6 +77,7 @@ import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.cache.CacheStats; +import org.apache.jackrabbit.oak.commons.Compression; import org.apache.jackrabbit.oak.commons.PerfLogger; import org.apache.jackrabbit.oak.commons.json.JsopStream; import org.apache.jackrabbit.oak.commons.json.JsopWriter; @@ -1353,7 +1354,7 @@ AbstractDocumentNodeState getSecondaryNodeState(@NotNull final Path path, @NotNull public PropertyState createPropertyState(String name, String value){ - return new DocumentPropertyState(this, name, checkNotNull(value)); + return DocumentPropertyStateFactory.createPropertyState(this, name, value, Compression.NONE); } /** @@ -3214,8 +3215,7 @@ private long getBinarySize(@Nullable String json) { if (json == null) { return -1; } - PropertyState p = new DocumentPropertyState( - DocumentNodeStore.this, "p", json); + PropertyState p = DocumentPropertyStateFactory.createPropertyState(this, "p", json, Compression.NONE); if (p.getType().tag() != PropertyType.BINARY) { return -1; } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java index 0aa10d36cb2..872985e5664 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java @@ -19,12 +19,6 @@ import static java.util.Collections.emptyList; import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.List; import javax.jcr.PropertyType; @@ -32,11 +26,8 @@ import org.apache.jackrabbit.guava.common.collect.Lists; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; -import org.apache.jackrabbit.oak.commons.Compression; -import org.apache.jackrabbit.oak.commons.LongUtils; import org.apache.jackrabbit.oak.commons.json.JsopReader; import org.apache.jackrabbit.oak.commons.json.JsopTokenizer; -import org.apache.jackrabbit.oak.commons.properties.SystemPropertySupplier; import org.apache.jackrabbit.oak.json.TypeCodes; import org.apache.jackrabbit.oak.plugins.memory.AbstractPropertyState; import org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState; @@ -47,16 +38,12 @@ import org.apache.jackrabbit.oak.plugins.memory.StringPropertyState; import org.apache.jackrabbit.oak.plugins.value.Conversions; import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * PropertyState implementation with lazy parsing of the JSOP encoded value. */ final class DocumentPropertyState implements PropertyState { - private static final Logger LOG = LoggerFactory.getLogger(DocumentPropertyState.class); - private final DocumentNodeStore store; private final String name; @@ -64,48 +51,11 @@ final class DocumentPropertyState implements PropertyState { private final String value; private PropertyState parsed; - private final byte[] compressedValue; - private final Compression compression; - private String decompressedValue; - - private static int COMPRESSION_THRESHOLD = SystemPropertySupplier - .create("oak.documentMK.stringCompressionThreshold ", -1).loggingTo(LOG).get(); DocumentPropertyState(DocumentNodeStore store, String name, String value) { - this(store, name, value, Compression.GZIP); - } - - DocumentPropertyState(DocumentNodeStore store, String name, String value, Compression compression) { this.store = store; this.name = name; - if (compression == null || COMPRESSION_THRESHOLD == -1) { - this.value = value; - this.compression = null; - this.compressedValue = null; - } else { - this.compression = compression; - int size = value.length(); - String localValue = value; - byte[] localCompressedValue = null; - if (size > COMPRESSION_THRESHOLD) { - try { - localCompressedValue = compress(value.getBytes(StandardCharsets.UTF_8)); - localValue = null; - } catch (IOException e) { - LOG.warn("Failed to compress property {} value: ", name, e); - } - } - this.value = localValue; - this.compressedValue = localCompressedValue; - } - } - - private byte[] compress(byte[] value) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - OutputStream compressionOutputStream = compression.getOutputStream(out); - compressionOutputStream.write(value); - compressionOutputStream.close(); - return out.toByteArray(); + this.value = value; } @NotNull @@ -166,23 +116,7 @@ public int count() { */ @NotNull String getValue() { - return value != null ? value : decompress(this.compressedValue); - } - - private String decompress(byte[] value) { - if (decompressedValue == null) { - try { - decompressedValue = new String(compression.getInputStream(new ByteArrayInputStream(value)).readAllBytes(), StandardCharsets.UTF_8); - } catch (IOException e) { - LOG.error("Failed to decompress property {} value: ", getName(), e); - decompressedValue = "\"{}\""; - } - } - return decompressedValue; - } - - byte[] getCompressedValue() { - return compressedValue; + return value; } //------------------------------------------------------------< Object >-- @@ -193,18 +127,8 @@ public boolean equals(Object object) { return true; } else if (object instanceof DocumentPropertyState) { DocumentPropertyState other = (DocumentPropertyState) object; - if (!this.name.equals(other.name) || !Arrays.equals(this.compressedValue, other.compressedValue)) { - return false; - } - if (this.compressedValue == null && other.compressedValue == null) { - return value.equals(other.value); - } else { - // Compare length and content of compressed values - if (this.compressedValue.length != other.compressedValue.length) { - return false; - } - return Arrays.equals(this.compressedValue, other.compressedValue); - } + return this.name.equals(other.name) + && this.value.equals(other.value); } // fall back to default equality check in AbstractPropertyState return object instanceof PropertyState @@ -221,15 +145,11 @@ public String toString() { return AbstractPropertyState.toString(this); } - static void setCompressionThreshold(int compressionThreshold) { - COMPRESSION_THRESHOLD = compressionThreshold; - } - //----------------------------< internal >---------------------------------- private PropertyState parsed() { if (parsed == null) { - JsopReader reader = new JsopTokenizer(getValue()); + JsopReader reader = new JsopTokenizer(value); if (reader.matches('[')) { parsed = readArrayProperty(name, reader); } else { @@ -260,11 +180,10 @@ PropertyState readProperty(String name, JsopReader reader) { static PropertyState readProperty(String name, DocumentNodeStore store, JsopReader reader) { if (reader.matches(JsopReader.NUMBER)) { String number = reader.getToken(); - Long maybeLong = LongUtils.tryParse(number); - if (maybeLong == null) { + try { + return new LongPropertyState(name, Long.parseLong(number)); + } catch (NumberFormatException e) { return new DoublePropertyState(name, Double.parseDouble(number)); - } else { - return new LongPropertyState(name, maybeLong); } } else if (reader.matches(JsopReader.TRUE)) { return BooleanPropertyState.booleanProperty(name, true); @@ -319,13 +238,12 @@ static PropertyState readArrayProperty(String name, DocumentNodeStore store, Jso while (!reader.matches(']')) { if (reader.matches(JsopReader.NUMBER)) { String number = reader.getToken(); - Long maybeLong = LongUtils.tryParse(number); - if (maybeLong == null) { + try { + type = PropertyType.LONG; + values.add(Long.parseLong(number)); + } catch (NumberFormatException e) { type = PropertyType.DOUBLE; values.add(Double.parseDouble(number)); - } else { - type = PropertyType.LONG; - values.add(maybeLong); } } else if (reader.matches(JsopReader.TRUE)) { type = PropertyType.BOOLEAN; @@ -359,4 +277,4 @@ static PropertyState readArrayProperty(String name, DocumentNodeStore store, Jso } return createProperty(name, values, Type.fromTag(type, true)); } -} \ No newline at end of file +} diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java new file mode 100644 index 00000000000..3d55eda9eef --- /dev/null +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java @@ -0,0 +1,15 @@ +package org.apache.jackrabbit.oak.plugins.document; + +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.commons.Compression; + +public class DocumentPropertyStateFactory { + + public static PropertyState createPropertyState(DocumentNodeStore store, String name, String value, Compression compression) { + if (compression != null && !compression.equals(Compression.NONE)) { + return new CompressedDocumentPropertyState(store, name, value, compression); + } else { + return new DocumentPropertyState(store, name, value); + } + } +} diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java new file mode 100644 index 00000000000..fa9ec711c26 --- /dev/null +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java @@ -0,0 +1,359 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.plugins.document; + +import static org.apache.jackrabbit.guava.common.collect.Lists.newArrayList; +import static org.apache.jackrabbit.guava.common.collect.Sets.newHashSet; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assume.assumeTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.jackrabbit.oak.api.Blob; +import org.apache.jackrabbit.oak.api.CommitFailedException; +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.commons.Compression; +import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore; +import org.apache.jackrabbit.oak.plugins.document.mongo.MongoTestUtils; +import org.apache.jackrabbit.oak.spi.blob.BlobStore; +import org.apache.jackrabbit.oak.spi.blob.MemoryBlobStore; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; +import org.junit.*; + +import com.mongodb.ReadPreference; + +public class CompressedDocumentPropertyStateTest { + + private static final int BLOB_SIZE = 16 * 1024; + private static final String TEST_NODE = "test"; + private static final String STRING_HUGEVALUE = RandomStringUtils.random(10050, "dummytest"); + private static final int DEFAULT_COMPRESSION_THRESHOLD = 1024; + private static final int DISABLED_COMPRESSION = -1; + + @Rule + public DocumentMKBuilderProvider builderProvider = new DocumentMKBuilderProvider(); + + private Set reads = newHashSet(); + + private BlobStore bs = new MemoryBlobStore() { + @Override + public InputStream getInputStream(String blobId) + throws IOException { + reads.add(blobId); + return super.getInputStream(blobId); + } + }; + + private DocumentNodeStore ns; + + @Before + public void before() throws Exception { + ns = builderProvider.newBuilder().setBlobStore(bs).getNodeStore(); + } + + @After + public void tearDown() { + try { + ns.dispose(); + } finally { + CompressedDocumentPropertyState.setCompressionThreshold(DISABLED_COMPRESSION); + } + } + + // OAK-5462 + @Test + public void multiValuedBinarySize() throws Exception { + NodeBuilder builder = ns.getRoot().builder(); + List blobs = newArrayList(); + for (int i = 0; i < 3; i++) { + blobs.add(builder.createBlob(new RandomStream(BLOB_SIZE, i))); + } + builder.child("test").setProperty("p", blobs, Type.BINARIES); + TestUtils.merge(ns, builder); + + PropertyState p = ns.getRoot().getChildNode("test").getProperty("p"); + assertEquals(Type.BINARIES, p.getType()); + assertEquals(3, p.count()); + + reads.clear(); + assertEquals(BLOB_SIZE, p.size(0)); + // must not read the blob via stream + assertEquals(0, reads.size()); + } + + @Test + public void multiValuedAboveThresholdSize() throws Exception { + NodeBuilder builder = ns.getRoot().builder(); + List blobs = newArrayList(); + for (int i = 0; i < 13; i++) { + blobs.add(builder.createBlob(new RandomStream(BLOB_SIZE, i))); + } + builder.child(TEST_NODE).setProperty("p", blobs, Type.BINARIES); + TestUtils.merge(ns, builder); + + PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); + assertEquals(Type.BINARIES, Objects.requireNonNull(p).getType()); + assertEquals(13, p.count()); + + reads.clear(); + assertEquals(BLOB_SIZE, p.size(0)); + // must not read the blob via stream + assertEquals(0, reads.size()); + } + + @Test + public void stringBelowThresholdSize() throws Exception { + NodeBuilder builder = ns.getRoot().builder(); + builder.child(TEST_NODE).setProperty("p", "dummy", Type.STRING); + TestUtils.merge(ns, builder); + + PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); + assertEquals(Type.STRING, Objects.requireNonNull(p).getType()); + assertEquals(1, p.count()); + + reads.clear(); + assertEquals(5, p.size(0)); + // must not read the string via stream + assertEquals(0, reads.size()); + } + + @Test + public void stringAboveThresholdSize() throws Exception { + NodeBuilder builder = ns.getRoot().builder(); + builder.child(TEST_NODE).setProperty("p", STRING_HUGEVALUE, Type.STRING); + TestUtils.merge(ns, builder); + + PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); + assertEquals(Type.STRING, Objects.requireNonNull(p).getType()); + assertEquals(1, p.count()); + + reads.clear(); + assertEquals(10050, p.size(0)); + // must not read the string via streams + assertEquals(0, reads.size()); + } + + @Test + public void compressValueThrowsException() throws IOException, NoSuchFieldException, IllegalAccessException { + DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); + Compression mockCompression = mock(Compression.class); + when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IOException("Compression failed")); + + CompressedDocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); + CompressedDocumentPropertyState documentPropertyState = new CompressedDocumentPropertyState(mockDocumentStore, "p", "\"" + STRING_HUGEVALUE + "\"", mockCompression); + + assertEquals(documentPropertyState.getValue(Type.STRING), STRING_HUGEVALUE); + + verify(mockCompression, times(1)).getOutputStream(any(OutputStream.class)); + } + + @Test + public void uncompressValueThrowsException() throws IOException, NoSuchFieldException, IllegalAccessException { + + DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); + Compression mockCompression = mock(Compression.class); + OutputStream mockOutputStream= mock(OutputStream.class); + when(mockCompression.getOutputStream(any(OutputStream.class))).thenReturn(mockOutputStream); + when(mockCompression.getInputStream(any(InputStream.class))).thenThrow(new IOException("Compression failed")); + + CompressedDocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); + CompressedDocumentPropertyState documentPropertyState = new CompressedDocumentPropertyState(mockDocumentStore, "p", STRING_HUGEVALUE, mockCompression); + + assertEquals(documentPropertyState.getValue(Type.STRING), "{}"); + + verify(mockCompression, times(1)).getInputStream(any(InputStream.class)); + } + + @Test + public void stringAboveThresholdSizeNoCompression() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + + CompressedDocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); + + CompressedDocumentPropertyState state = new CompressedDocumentPropertyState(store, "propertyName", "\"" + STRING_HUGEVALUE + "\"", Compression.NONE); + + byte[] result = state.getCompressedValue(); + + assertEquals(result.length, STRING_HUGEVALUE.length() + 2 ); + + assertEquals(state.getValue(Type.STRING), STRING_HUGEVALUE); + assertEquals(STRING_HUGEVALUE, state.getValue(Type.STRING)); + } + + @Test + public void testInterestingStringsWithoutCompression() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + String[] tests = new String[] { "simple:foo", "cr:a\n\b", "dquote:a\"b", "bs:a\\b", "euro:a\u201c", "gclef:\uD834\uDD1E", + "tab:a\tb", "nul:a\u0000b"}; + + for (String test : tests) { + CompressedDocumentPropertyState state = new CompressedDocumentPropertyState(store, "propertyName", test, Compression.GZIP); + assertEquals(test, state.getValue()); + } + } + + @Test + public void testInterestingStringsWithCompression() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + String[] tests = new String[]{"simple:foo", "cr:a\n\b", "dquote:a\"b", "bs:a\\b", "euro:a\u201c", "gclef:\uD834\uDD1E", + "tab:a\tb", "nul:a\u0000b"}; + + CompressedDocumentPropertyState.setCompressionThreshold(1); + for (String test : tests) { + CompressedDocumentPropertyState state = new CompressedDocumentPropertyState(store, "propertyName", test, Compression.GZIP); + assertEquals(test, state.getValue()); + } + } + + @Test + public void testBrokenSurrogateWithoutCompressionForMongo() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, false); + } + + @Test + public void testBrokenSurrogateWithoutCompressionForRDB() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, false); + } + + @Test + public void testBrokenSurrogateWithoutCompressionForInMemory() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, false); + } + + @Test + public void testBrokenSurrogateWithCompressionForMongo() throws CommitFailedException { + CompressedDocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, true); + } + + @Test + public void testBrokenSurrogateWithCompressionForRDB() throws CommitFailedException { + CompressedDocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, true); + } + + @Test + public void testBrokenSurrogateWithCompressionForInMemory() throws CommitFailedException { + CompressedDocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, true); + } + + private void getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture fixture, boolean compressionEnabled) throws CommitFailedException { + assumeTrue(fixture.isAvailable()); + String test = "brokensurrogate:dfsa\ud800"; + DocumentStore store = null; + DocumentNodeStore nodeStore = null; + + try { + store = fixture.createDocumentStore(); + + if (store instanceof MongoDocumentStore) { + // Enforce primary read preference, otherwise tests may fail on a + // replica set with a read preference configured to secondary. + // Revision GC usually runs with a modified range way in the past, + // which means changes made it to the secondary, but not in this + // test using a virtual clock + MongoTestUtils.setReadPreference(store, ReadPreference.primary()); + } + nodeStore = new DocumentMK.Builder().setDocumentStore(store).getNodeStore(); + + createPropAndCheckValue(nodeStore, test, compressionEnabled); + + } finally { + if (nodeStore != null) { + nodeStore.dispose(); + } + } + + } + + private void createPropAndCheckValue(DocumentNodeStore nodeStore, String test, boolean compressionEnabled) throws CommitFailedException { + NodeBuilder builder = nodeStore.getRoot().builder(); + if (compressionEnabled) { + CompressedDocumentPropertyState.setCompressionThreshold(1); + } + builder.child(TEST_NODE).setProperty("p", test, Type.STRING); + TestUtils.merge(nodeStore, builder); + + PropertyState p = nodeStore.getRoot().getChildNode(TEST_NODE).getProperty("p"); + assertEquals(Objects.requireNonNull(p).getValue(Type.STRING), test); + } + + @Test + public void testEqualsWithoutCompression() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + String name = "propertyName"; + String value = "testValue"; + Compression compression = Compression.GZIP; + + CompressedDocumentPropertyState state1 = new CompressedDocumentPropertyState(store, name, value, compression); + CompressedDocumentPropertyState state2 = new CompressedDocumentPropertyState(store, name, value, compression); + + assertEquals(state1, state2); + + // Test for inequality + CompressedDocumentPropertyState state4 = new CompressedDocumentPropertyState(store, "differentName", value, compression); + assertNotEquals(state1, state4); + } + + @Test + public void testEqualsWithCompression() throws IOException { + DocumentNodeStore store = mock(DocumentNodeStore.class); + String name = "propertyName"; + String value = "testValue"; + Compression compression = Compression.GZIP; + + CompressedDocumentPropertyState.setCompressionThreshold(1); + // Create two DocumentPropertyState instances with the same properties + CompressedDocumentPropertyState state1 = new CompressedDocumentPropertyState(store, name, value, compression); + CompressedDocumentPropertyState state2 = new CompressedDocumentPropertyState(store, name, value, compression); + + // Check that the compressed values are not null + assertNotNull(state1.getCompressedValue()); + assertNotNull(state2.getCompressedValue()); + + // Check that the equals method returns true + assertEquals(state1, state2); + + // Decompress the values + String decompressedValue1 = state1.getValue(); + String decompressedValue2 = state2.getValue(); + + // Check that the decompressed values are equal to the original value + assertEquals(value, decompressedValue1); + assertEquals(value, decompressedValue2); + + // Check that the equals method still returns true after decompression + assertEquals(state1, state2); + } +} diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java index 02c0dda73ec..081aa982320 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java @@ -19,49 +19,26 @@ import static org.apache.jackrabbit.guava.common.collect.Lists.newArrayList; import static org.apache.jackrabbit.guava.common.collect.Sets.newHashSet; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assume.assumeTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.InvocationTargetException; import java.util.List; -import java.util.Objects; import java.util.Set; import org.apache.commons.lang3.RandomStringUtils; import org.apache.jackrabbit.oak.api.Blob; -import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; -import org.apache.jackrabbit.oak.commons.Compression; -import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore; -import org.apache.jackrabbit.oak.plugins.document.mongo.MongoTestUtils; import org.apache.jackrabbit.oak.spi.blob.BlobStore; import org.apache.jackrabbit.oak.spi.blob.MemoryBlobStore; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import com.mongodb.ReadPreference; - public class DocumentPropertyStateTest { private static final int BLOB_SIZE = 16 * 1024; - private static final String TEST_NODE = "test"; - private static final String STRING_HUGEVALUE = RandomStringUtils.random(10050, "dummytest"); - private static final int DEFAULT_COMPRESSION_THRESHOLD = 1024; - private static final int DISABLED_COMPRESSION = -1; @Rule public DocumentMKBuilderProvider builderProvider = new DocumentMKBuilderProvider(); @@ -84,15 +61,6 @@ public void before() throws Exception { ns = builderProvider.newBuilder().setBlobStore(bs).getNodeStore(); } - @After - public void tearDown() { - try { - ns.dispose(); - } finally { - DocumentPropertyState.setCompressionThreshold(DISABLED_COMPRESSION); - } - } - // OAK-5462 @Test public void multiValuedBinarySize() throws Exception { @@ -113,287 +81,4 @@ public void multiValuedBinarySize() throws Exception { // must not read the blob via stream assertEquals(0, reads.size()); } - - @Test - public void multiValuedAboveThresholdSize() throws Exception { - NodeBuilder builder = ns.getRoot().builder(); - List blobs = newArrayList(); - for (int i = 0; i < 13; i++) { - blobs.add(builder.createBlob(new RandomStream(BLOB_SIZE, i))); - } - builder.child(TEST_NODE).setProperty("p", blobs, Type.BINARIES); - TestUtils.merge(ns, builder); - - PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); - assertEquals(Type.BINARIES, Objects.requireNonNull(p).getType()); - assertEquals(13, p.count()); - - reads.clear(); - assertEquals(BLOB_SIZE, p.size(0)); - // must not read the blob via stream - assertEquals(0, reads.size()); - } - - @Test - public void stringBelowThresholdSize() throws Exception { - NodeBuilder builder = ns.getRoot().builder(); - builder.child(TEST_NODE).setProperty("p", "dummy", Type.STRING); - TestUtils.merge(ns, builder); - - PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); - assertEquals(Type.STRING, Objects.requireNonNull(p).getType()); - assertEquals(1, p.count()); - - reads.clear(); - assertEquals(5, p.size(0)); - // must not read the string via stream - assertEquals(0, reads.size()); - } - - @Test - public void stringAboveThresholdSize() throws Exception { - NodeBuilder builder = ns.getRoot().builder(); - builder.child(TEST_NODE).setProperty("p", STRING_HUGEVALUE, Type.STRING); - TestUtils.merge(ns, builder); - - PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); - assertEquals(Type.STRING, Objects.requireNonNull(p).getType()); - assertEquals(1, p.count()); - - reads.clear(); - assertEquals(10050, p.size(0)); - // must not read the string via streams - assertEquals(0, reads.size()); - } - - @Test - public void compressValueThrowsException() throws IOException, NoSuchFieldException, IllegalAccessException { - DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); - Compression mockCompression = mock(Compression.class); - when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IOException("Compression failed")); - - DocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); - DocumentPropertyState documentPropertyState = new DocumentPropertyState(mockDocumentStore, "p", "\"" + STRING_HUGEVALUE + "\"", mockCompression); - - assertEquals(documentPropertyState.getValue(Type.STRING), STRING_HUGEVALUE); - - verify(mockCompression, times(1)).getOutputStream(any(OutputStream.class)); - } - - @Test - public void uncompressValueThrowsException() throws IOException, NoSuchFieldException, IllegalAccessException { - - DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); - Compression mockCompression = mock(Compression.class); - OutputStream mockOutputStream= mock(OutputStream.class); - when(mockCompression.getOutputStream(any(OutputStream.class))).thenReturn(mockOutputStream); - when(mockCompression.getInputStream(any(InputStream.class))).thenThrow(new IOException("Compression failed")); - - DocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); - DocumentPropertyState documentPropertyState = new DocumentPropertyState(mockDocumentStore, "p", STRING_HUGEVALUE, mockCompression); - - assertEquals(documentPropertyState.getValue(Type.STRING), "{}"); - - verify(mockCompression, times(1)).getInputStream(any(InputStream.class)); - } - - @Test - public void defaultValueSetToMinusOne() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { - DocumentNodeStore store = mock(DocumentNodeStore.class); - - DocumentPropertyState.setCompressionThreshold(-1); - DocumentPropertyState state = new DocumentPropertyState(store, "propertyNameNew", "\"" + STRING_HUGEVALUE + "\"", Compression.GZIP); - - byte[] result = state.getCompressedValue(); - - assertNull(result); - assertEquals(state.getValue(Type.STRING), STRING_HUGEVALUE); - } - - @Test - public void stringAboveThresholdSizeNoCompression() { - DocumentNodeStore store = mock(DocumentNodeStore.class); - - DocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); - - DocumentPropertyState state = new DocumentPropertyState(store, "propertyName", "\"" + STRING_HUGEVALUE + "\"", Compression.NONE); - - byte[] result = state.getCompressedValue(); - - assertEquals(result.length, STRING_HUGEVALUE.length() + 2 ); - - assertEquals(state.getValue(Type.STRING), STRING_HUGEVALUE); - assertEquals(STRING_HUGEVALUE, state.getValue(Type.STRING)); - } - - @Test - public void testInterestingStringsWithoutCompression() { - DocumentNodeStore store = mock(DocumentNodeStore.class); - String[] tests = new String[] { "simple:foo", "cr:a\n\b", "dquote:a\"b", "bs:a\\b", "euro:a\u201c", "gclef:\uD834\uDD1E", - "tab:a\tb", "nul:a\u0000b"}; - - for (String test : tests) { - DocumentPropertyState state = new DocumentPropertyState(store, "propertyName", test, Compression.GZIP); - assertEquals(test, state.getValue()); - } - } - - @Test - public void testInterestingStringsWithCompression() { - DocumentNodeStore store = mock(DocumentNodeStore.class); - String[] tests = new String[]{"simple:foo", "cr:a\n\b", "dquote:a\"b", "bs:a\\b", "euro:a\u201c", "gclef:\uD834\uDD1E", - "tab:a\tb", "nul:a\u0000b"}; - - DocumentPropertyState.setCompressionThreshold(1); - for (String test : tests) { - DocumentPropertyState state = new DocumentPropertyState(store, "propertyName", test, Compression.GZIP); - assertEquals(test, state.getValue()); - } - } - - @Test - public void testBrokenSurrogateWithoutCompressionForMongo() throws CommitFailedException { - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, false); - } - - @Test - public void testBrokenSurrogateWithoutCompressionForRDB() throws CommitFailedException { - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, false); - } - - @Test - public void testBrokenSurrogateWithoutCompressionForInMemory() throws CommitFailedException { - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, false); - } - - @Test - public void testBrokenSurrogateWithCompressionForMongo() throws CommitFailedException { - DocumentPropertyState.setCompressionThreshold(1); - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, true); - } - - @Test - public void testBrokenSurrogateWithCompressionForRDB() throws CommitFailedException { - DocumentPropertyState.setCompressionThreshold(1); - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, true); - } - - @Test - public void testBrokenSurrogateWithCompressionForInMemory() throws CommitFailedException { - DocumentPropertyState.setCompressionThreshold(1); - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, true); - } - - private void getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture fixture, boolean compressionEnabled) throws CommitFailedException { - assumeTrue(fixture.isAvailable()); - String test = "brokensurrogate:dfsa\ud800"; - DocumentStore store = null; - DocumentNodeStore nodeStore = null; - - try { - store = fixture.createDocumentStore(); - - if (store instanceof MongoDocumentStore) { - // Enforce primary read preference, otherwise tests may fail on a - // replica set with a read preference configured to secondary. - // Revision GC usually runs with a modified range way in the past, - // which means changes made it to the secondary, but not in this - // test using a virtual clock - MongoTestUtils.setReadPreference(store, ReadPreference.primary()); - } - nodeStore = new DocumentMK.Builder().setDocumentStore(store).getNodeStore(); - - createPropAndCheckValue(nodeStore, test, compressionEnabled); - - } finally { - if (nodeStore != null) { - nodeStore.dispose(); - } - } - - } - - private void createPropAndCheckValue(DocumentNodeStore nodeStore, String test, boolean compressionEnabled) throws CommitFailedException { - NodeBuilder builder = nodeStore.getRoot().builder(); - if (compressionEnabled) { - DocumentPropertyState.setCompressionThreshold(1); - } - builder.child(TEST_NODE).setProperty("p", test, Type.STRING); - TestUtils.merge(nodeStore, builder); - - PropertyState p = nodeStore.getRoot().getChildNode(TEST_NODE).getProperty("p"); - assertEquals(Objects.requireNonNull(p).getValue(Type.STRING), test); - } - - @Test - public void testEqualsWithoutCompression() { - DocumentNodeStore store = mock(DocumentNodeStore.class); - String name = "propertyName"; - String value = "testValue"; - Compression compression = Compression.GZIP; - - DocumentPropertyState state1 = new DocumentPropertyState(store, name, value, compression); - DocumentPropertyState state2 = new DocumentPropertyState(store, name, value, compression); - - assertEquals(state1, state2); - - // Test for inequality - DocumentPropertyState state4 = new DocumentPropertyState(store, "differentName", value, compression); - assertNotEquals(state1, state4); - } - - @Test - public void testEqualsWithCompression() throws IOException { - DocumentNodeStore store = mock(DocumentNodeStore.class); - String name = "propertyName"; - String value = "testValue"; - Compression compression = Compression.GZIP; - - DocumentPropertyState.setCompressionThreshold(1); - // Create two DocumentPropertyState instances with the same properties - DocumentPropertyState state1 = new DocumentPropertyState(store, name, value, compression); - DocumentPropertyState state2 = new DocumentPropertyState(store, name, value, compression); - - // Check that the compressed values are not null - assertNotNull(state1.getCompressedValue()); - assertNotNull(state2.getCompressedValue()); - - // Check that the equals method returns true - assertEquals(state1, state2); - - // Decompress the values - String decompressedValue1 = state1.getValue(); - String decompressedValue2 = state2.getValue(); - - // Check that the decompressed values are equal to the original value - assertEquals(value, decompressedValue1); - assertEquals(value, decompressedValue2); - - // Check that the equals method still returns true after decompression - assertEquals(state1, state2); - } - - @Test - public void testOneCompressOtherUncompressInEquals() throws IOException { - DocumentNodeStore store = mock(DocumentNodeStore.class); - String name = "propertyName"; - String value = "testValue"; - Compression compression = Compression.GZIP; - - // Create a DocumentPropertyState instance with a compressed value - DocumentPropertyState.setCompressionThreshold(1); - DocumentPropertyState state1 = new DocumentPropertyState(store, name, value, compression); - - // Create a DocumentPropertyState instance with an uncompressed value - DocumentPropertyState.setCompressionThreshold(-1); - DocumentPropertyState state2 = new DocumentPropertyState(store, name, value, compression); - - assertNotEquals(state1, state2); - - // Decompress the value of the first instance - String decompressedValue1 = state1.getValue(); - - // Check that the decompressed value is equal to the original value - assertEquals(value, decompressedValue1); - } } \ No newline at end of file From 5c8cc87fa7b7b1a4a0073ef97f6fddc8c0db66ad Mon Sep 17 00:00:00 2001 From: pirlogea Date: Mon, 5 Aug 2024 12:03:17 +0300 Subject: [PATCH 06/31] OAK-10973 - Performance tune property compression/decompression separate DocumentPropertyState from CompressedDocumentPropertyState --- .../jackrabbit/oak/plugins/document/DocumentNodeStore.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java index 4be33b22072..f5ed5650755 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java @@ -77,7 +77,6 @@ import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.cache.CacheStats; -import org.apache.jackrabbit.oak.commons.Compression; import org.apache.jackrabbit.oak.commons.PerfLogger; import org.apache.jackrabbit.oak.commons.json.JsopStream; import org.apache.jackrabbit.oak.commons.json.JsopWriter; @@ -1354,7 +1353,7 @@ AbstractDocumentNodeState getSecondaryNodeState(@NotNull final Path path, @NotNull public PropertyState createPropertyState(String name, String value){ - return DocumentPropertyStateFactory.createPropertyState(this, name, value, Compression.NONE); + return new DocumentPropertyState(this, name, checkNotNull(value)); } /** @@ -3215,7 +3214,8 @@ private long getBinarySize(@Nullable String json) { if (json == null) { return -1; } - PropertyState p = DocumentPropertyStateFactory.createPropertyState(this, "p", json, Compression.NONE); + PropertyState p = new DocumentPropertyState( + DocumentNodeStore.this, "p", json); if (p.getType().tag() != PropertyType.BINARY) { return -1; } From 537278b99aa563f2352beff7722e32dc2bd7fff7 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 6 Aug 2024 11:11:16 +0300 Subject: [PATCH 07/31] OAK-10973 separate DocumentPropertyState from CompressedDocumentPropertyState delete decompressedValue --- .../document/CompressedDocumentPropertyState.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java index bf8dcfd7255..054f4c9a3bc 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java @@ -66,7 +66,6 @@ public final class CompressedDocumentPropertyState implements PropertyState { private PropertyState parsed; private final byte[] compressedValue; private final Compression compression; - private String decompressedValue; private static int COMPRESSION_THRESHOLD = SystemPropertySupplier .create("oak.documentMK.stringCompressionThreshold ", -1).loggingTo(LOG).get(); @@ -159,15 +158,12 @@ String getValue() { } private String decompress(byte[] value) { - if (decompressedValue == null) { - try { - decompressedValue = new String(compression.getInputStream(new ByteArrayInputStream(value)).readAllBytes(), StandardCharsets.UTF_8); - } catch (IOException e) { - LOG.error("Failed to decompress property {} value: ", getName(), e); - decompressedValue = "\"{}\""; - } + try { + return new String(compression.getInputStream(new ByteArrayInputStream(value)).readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + LOG.error("Failed to decompress property {} value: ", getName(), e); + return "\"{}\""; } - return decompressedValue; } byte[] getCompressedValue() { From 497715bfb7212a965659e574236ecb05b0f89a6a Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 6 Aug 2024 16:50:54 +0300 Subject: [PATCH 08/31] OAK-10973 separate DocumentPropertyState from CompressedDocumentPropertyState refactor tests --- .../CompressedDocumentPropertyStateTest.java | 166 +++--------------- .../document/DocumentPropertyStateTest.java | 82 ++++++++- 2 files changed, 101 insertions(+), 147 deletions(-) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java index fa9ec711c26..3f95c537dda 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java @@ -16,13 +16,10 @@ */ package org.apache.jackrabbit.oak.plugins.document; -import static org.apache.jackrabbit.guava.common.collect.Lists.newArrayList; import static org.apache.jackrabbit.guava.common.collect.Sets.newHashSet; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -32,30 +29,22 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Objects; import java.util.Set; import org.apache.commons.lang3.RandomStringUtils; -import org.apache.jackrabbit.oak.api.Blob; -import org.apache.jackrabbit.oak.api.CommitFailedException; -import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.Compression; -import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore; -import org.apache.jackrabbit.oak.plugins.document.mongo.MongoTestUtils; import org.apache.jackrabbit.oak.spi.blob.BlobStore; import org.apache.jackrabbit.oak.spi.blob.MemoryBlobStore; -import org.apache.jackrabbit.oak.spi.state.NodeBuilder; -import org.junit.*; - -import com.mongodb.ReadPreference; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; public class CompressedDocumentPropertyStateTest { - private static final int BLOB_SIZE = 16 * 1024; - private static final String TEST_NODE = "test"; private static final String STRING_HUGEVALUE = RandomStringUtils.random(10050, "dummytest"); private static final int DEFAULT_COMPRESSION_THRESHOLD = 1024; private static final int DISABLED_COMPRESSION = -1; @@ -90,81 +79,40 @@ public void tearDown() { } } - // OAK-5462 - @Test - public void multiValuedBinarySize() throws Exception { - NodeBuilder builder = ns.getRoot().builder(); - List blobs = newArrayList(); - for (int i = 0; i < 3; i++) { - blobs.add(builder.createBlob(new RandomStream(BLOB_SIZE, i))); - } - builder.child("test").setProperty("p", blobs, Type.BINARIES); - TestUtils.merge(ns, builder); - - PropertyState p = ns.getRoot().getChildNode("test").getProperty("p"); - assertEquals(Type.BINARIES, p.getType()); - assertEquals(3, p.count()); - - reads.clear(); - assertEquals(BLOB_SIZE, p.size(0)); - // must not read the blob via stream - assertEquals(0, reads.size()); - } - - @Test - public void multiValuedAboveThresholdSize() throws Exception { - NodeBuilder builder = ns.getRoot().builder(); - List blobs = newArrayList(); - for (int i = 0; i < 13; i++) { - blobs.add(builder.createBlob(new RandomStream(BLOB_SIZE, i))); - } - builder.child(TEST_NODE).setProperty("p", blobs, Type.BINARIES); - TestUtils.merge(ns, builder); - - PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); - assertEquals(Type.BINARIES, Objects.requireNonNull(p).getType()); - assertEquals(13, p.count()); - - reads.clear(); - assertEquals(BLOB_SIZE, p.size(0)); - // must not read the blob via stream - assertEquals(0, reads.size()); - } - @Test public void stringBelowThresholdSize() throws Exception { - NodeBuilder builder = ns.getRoot().builder(); - builder.child(TEST_NODE).setProperty("p", "dummy", Type.STRING); - TestUtils.merge(ns, builder); + DocumentNodeStore store = mock(DocumentNodeStore.class); + CompressedDocumentPropertyState.setCompressionThreshold(10000); + + CompressedDocumentPropertyState state = new CompressedDocumentPropertyState(store, "p", "\"" + STRING_HUGEVALUE + "\"", Compression.GZIP); - PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); - assertEquals(Type.STRING, Objects.requireNonNull(p).getType()); - assertEquals(1, p.count()); + assertEquals(Type.STRING, state.getType()); + assertEquals(1, state.count()); reads.clear(); - assertEquals(5, p.size(0)); - // must not read the string via stream + assertEquals(10050, state.size(0)); + // must not read the string via streams assertEquals(0, reads.size()); } @Test public void stringAboveThresholdSize() throws Exception { - NodeBuilder builder = ns.getRoot().builder(); - builder.child(TEST_NODE).setProperty("p", STRING_HUGEVALUE, Type.STRING); - TestUtils.merge(ns, builder); + DocumentNodeStore store = mock(DocumentNodeStore.class); + CompressedDocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); - PropertyState p = ns.getRoot().getChildNode(TEST_NODE).getProperty("p"); - assertEquals(Type.STRING, Objects.requireNonNull(p).getType()); - assertEquals(1, p.count()); + CompressedDocumentPropertyState state = new CompressedDocumentPropertyState(store, "p", "\"" + STRING_HUGEVALUE + "\"", Compression.GZIP); + + assertEquals(Type.STRING, state.getType()); + assertEquals(1, state.count()); reads.clear(); - assertEquals(10050, p.size(0)); + assertEquals(10050, state.size(0)); // must not read the string via streams assertEquals(0, reads.size()); } @Test - public void compressValueThrowsException() throws IOException, NoSuchFieldException, IllegalAccessException { + public void compressValueThrowsException() throws IOException { DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); Compression mockCompression = mock(Compression.class); when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IOException("Compression failed")); @@ -235,80 +183,6 @@ public void testInterestingStringsWithCompression() { } } - @Test - public void testBrokenSurrogateWithoutCompressionForMongo() throws CommitFailedException { - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, false); - } - - @Test - public void testBrokenSurrogateWithoutCompressionForRDB() throws CommitFailedException { - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, false); - } - - @Test - public void testBrokenSurrogateWithoutCompressionForInMemory() throws CommitFailedException { - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, false); - } - - @Test - public void testBrokenSurrogateWithCompressionForMongo() throws CommitFailedException { - CompressedDocumentPropertyState.setCompressionThreshold(1); - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, true); - } - - @Test - public void testBrokenSurrogateWithCompressionForRDB() throws CommitFailedException { - CompressedDocumentPropertyState.setCompressionThreshold(1); - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, true); - } - - @Test - public void testBrokenSurrogateWithCompressionForInMemory() throws CommitFailedException { - CompressedDocumentPropertyState.setCompressionThreshold(1); - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, true); - } - - private void getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture fixture, boolean compressionEnabled) throws CommitFailedException { - assumeTrue(fixture.isAvailable()); - String test = "brokensurrogate:dfsa\ud800"; - DocumentStore store = null; - DocumentNodeStore nodeStore = null; - - try { - store = fixture.createDocumentStore(); - - if (store instanceof MongoDocumentStore) { - // Enforce primary read preference, otherwise tests may fail on a - // replica set with a read preference configured to secondary. - // Revision GC usually runs with a modified range way in the past, - // which means changes made it to the secondary, but not in this - // test using a virtual clock - MongoTestUtils.setReadPreference(store, ReadPreference.primary()); - } - nodeStore = new DocumentMK.Builder().setDocumentStore(store).getNodeStore(); - - createPropAndCheckValue(nodeStore, test, compressionEnabled); - - } finally { - if (nodeStore != null) { - nodeStore.dispose(); - } - } - - } - - private void createPropAndCheckValue(DocumentNodeStore nodeStore, String test, boolean compressionEnabled) throws CommitFailedException { - NodeBuilder builder = nodeStore.getRoot().builder(); - if (compressionEnabled) { - CompressedDocumentPropertyState.setCompressionThreshold(1); - } - builder.child(TEST_NODE).setProperty("p", test, Type.STRING); - TestUtils.merge(nodeStore, builder); - - PropertyState p = nodeStore.getRoot().getChildNode(TEST_NODE).getProperty("p"); - assertEquals(Objects.requireNonNull(p).getValue(Type.STRING), test); - } - @Test public void testEqualsWithoutCompression() { DocumentNodeStore store = mock(DocumentNodeStore.class); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java index 081aa982320..c277c83341d 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java @@ -19,16 +19,21 @@ import static org.apache.jackrabbit.guava.common.collect.Lists.newArrayList; import static org.apache.jackrabbit.guava.common.collect.Sets.newHashSet; import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeTrue; import java.io.IOException; import java.io.InputStream; import java.util.List; +import java.util.Objects; import java.util.Set; -import org.apache.commons.lang3.RandomStringUtils; +import com.mongodb.ReadPreference; import org.apache.jackrabbit.oak.api.Blob; +import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore; +import org.apache.jackrabbit.oak.plugins.document.mongo.MongoTestUtils; import org.apache.jackrabbit.oak.spi.blob.BlobStore; import org.apache.jackrabbit.oak.spi.blob.MemoryBlobStore; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; @@ -81,4 +86,79 @@ public void multiValuedBinarySize() throws Exception { // must not read the blob via stream assertEquals(0, reads.size()); } + + @Test + public void testBrokenSurrogateWithoutCompressionForMongo() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, false); + } + + @Test + public void testBrokenSurrogateWithoutCompressionForRDB() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, false); + } + + @Test + public void testBrokenSurrogateWithoutCompressionForInMemory() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, false); + } + + @Test + public void testBrokenSurrogateWithCompressionForMongo() throws CommitFailedException { + CompressedDocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, true); + } + + @Test + public void testBrokenSurrogateWithCompressionForRDB() throws CommitFailedException { + CompressedDocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, true); + } + + @Test + public void testBrokenSurrogateWithCompressionForInMemory() throws CommitFailedException { + CompressedDocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, true); + } + + private void getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture fixture, boolean compressionEnabled) throws CommitFailedException { + assumeTrue(fixture.isAvailable()); + String test = "brokensurrogate:dfsa\ud800"; + DocumentStore store = null; + DocumentNodeStore nodeStore = null; + + try { + store = fixture.createDocumentStore(); + + if (store instanceof MongoDocumentStore) { + // Enforce primary read preference, otherwise tests may fail on a + // replica set with a read preference configured to secondary. + // Revision GC usually runs with a modified range way in the past, + // which means changes made it to the secondary, but not in this + // test using a virtual clock + MongoTestUtils.setReadPreference(store, ReadPreference.primary()); + } + nodeStore = new DocumentMK.Builder().setDocumentStore(store).getNodeStore(); + + createPropAndCheckValue(nodeStore, test, compressionEnabled); + + } finally { + if (nodeStore != null) { + nodeStore.dispose(); + } + } + + } + + private void createPropAndCheckValue(DocumentNodeStore nodeStore, String test, boolean compressionEnabled) throws CommitFailedException { + NodeBuilder builder = nodeStore.getRoot().builder(); + + if (compressionEnabled) { + CompressedDocumentPropertyState.setCompressionThreshold(1); + } + builder.child("test").setProperty("p", test, Type.STRING); + TestUtils.merge(nodeStore, builder); + + PropertyState p = nodeStore.getRoot().getChildNode("test").getProperty("p"); + assertEquals(Objects.requireNonNull(p).getValue(Type.STRING), test); + } } \ No newline at end of file From d911237d0beb48b4f8eccfdafb4d5106f410e8a8 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Thu, 8 Aug 2024 12:36:09 +0300 Subject: [PATCH 09/31] OAK-10973 separate DocumentPropertyState from CompressedDocumentPropertyState refactor factory method --- .../CompressedDocumentPropertyState.java | 26 +++---- .../DocumentPropertyStateFactory.java | 35 ++++++++- .../CompressedDocumentPropertyStateTest.java | 71 +------------------ 3 files changed, 47 insertions(+), 85 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java index 054f4c9a3bc..0288f269e68 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java @@ -61,8 +61,6 @@ public final class CompressedDocumentPropertyState implements PropertyState { private final String name; - private final String value; - private PropertyState parsed; private final byte[] compressedValue; private final Compression compression; @@ -74,18 +72,12 @@ public final class CompressedDocumentPropertyState implements PropertyState { this.store = store; this.name = name; this.compression = compression; - int size = value.length(); - byte[] compressedValue = null; - if (size > COMPRESSION_THRESHOLD) { - try { - compressedValue = compress(value.getBytes(StandardCharsets.UTF_8)); - value = null; - } catch (IOException e) { - LOG.warn("Failed to compress property {} value: ", name, e); - } + try { + this.compressedValue = compress(value.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + LOG.warn("Failed to compress property {} value: ", name, e); + throw new IllegalArgumentException("Failed to compress value", e); } - this.value = value; - this.compressedValue = compressedValue; } private byte[] compress(byte[] value) throws IOException { @@ -154,7 +146,7 @@ public int count() { */ @NotNull String getValue() { - return value != null ? value : decompress(this.compressedValue); + return decompress(this.compressedValue); } private String decompress(byte[] value) { @@ -182,7 +174,7 @@ public boolean equals(Object object) { return false; } if (this.compressedValue == null && other.compressedValue == null) { - return value.equals(other.value); + return getValue().equals(other.getValue()); } else { // Compare length and content of compressed values if (this.compressedValue.length != other.compressedValue.length) { @@ -206,6 +198,10 @@ public String toString() { return AbstractPropertyState.toString(this); } + static int getCompressionThreshold() { + return COMPRESSION_THRESHOLD; + } + static void setCompressionThreshold(int compressionThreshold) { COMPRESSION_THRESHOLD = compressionThreshold; } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java index 3d55eda9eef..32d2a9c67e2 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java @@ -1,15 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.apache.jackrabbit.oak.plugins.document; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.commons.Compression; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class DocumentPropertyStateFactory { + private static final Logger LOG = LoggerFactory.getLogger(DocumentPropertyStateFactory.class); + public static PropertyState createPropertyState(DocumentNodeStore store, String name, String value, Compression compression) { - if (compression != null && !compression.equals(Compression.NONE)) { - return new CompressedDocumentPropertyState(store, name, value, compression); + + if (compression != null && !compression.equals(Compression.NONE) && value.length() > CompressedDocumentPropertyState.getCompressionThreshold()) { + try { + return new CompressedDocumentPropertyState(store, name, value, compression); + } catch (Exception e) { + LOG.warn("Failed to compress property {} value: ", name, e); + return new DocumentPropertyState(store, name, value); + } + } else { return new DocumentPropertyState(store, name, value); } } + + public static PropertyState createPropertyState(DocumentNodeStore store, String name, String value) { + return createPropertyState(store, name, value, Compression.GZIP); + } } diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java index 3f95c537dda..2afbdbafeef 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java @@ -18,7 +18,6 @@ import static org.apache.jackrabbit.guava.common.collect.Sets.newHashSet; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -29,8 +28,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.List; -import java.util.Objects; import java.util.Set; import org.apache.commons.lang3.RandomStringUtils; @@ -95,27 +92,11 @@ public void stringBelowThresholdSize() throws Exception { assertEquals(0, reads.size()); } - @Test - public void stringAboveThresholdSize() throws Exception { - DocumentNodeStore store = mock(DocumentNodeStore.class); - CompressedDocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); - - CompressedDocumentPropertyState state = new CompressedDocumentPropertyState(store, "p", "\"" + STRING_HUGEVALUE + "\"", Compression.GZIP); - - assertEquals(Type.STRING, state.getType()); - assertEquals(1, state.count()); - - reads.clear(); - assertEquals(10050, state.size(0)); - // must not read the string via streams - assertEquals(0, reads.size()); - } - - @Test + @Test(expected = IllegalArgumentException.class) public void compressValueThrowsException() throws IOException { DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); Compression mockCompression = mock(Compression.class); - when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IOException("Compression failed")); + when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IllegalArgumentException("Compression failed")); CompressedDocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); CompressedDocumentPropertyState documentPropertyState = new CompressedDocumentPropertyState(mockDocumentStore, "p", "\"" + STRING_HUGEVALUE + "\"", mockCompression); @@ -126,24 +107,7 @@ public void compressValueThrowsException() throws IOException { } @Test - public void uncompressValueThrowsException() throws IOException, NoSuchFieldException, IllegalAccessException { - - DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); - Compression mockCompression = mock(Compression.class); - OutputStream mockOutputStream= mock(OutputStream.class); - when(mockCompression.getOutputStream(any(OutputStream.class))).thenReturn(mockOutputStream); - when(mockCompression.getInputStream(any(InputStream.class))).thenThrow(new IOException("Compression failed")); - - CompressedDocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); - CompressedDocumentPropertyState documentPropertyState = new CompressedDocumentPropertyState(mockDocumentStore, "p", STRING_HUGEVALUE, mockCompression); - - assertEquals(documentPropertyState.getValue(Type.STRING), "{}"); - - verify(mockCompression, times(1)).getInputStream(any(InputStream.class)); - } - - @Test - public void stringAboveThresholdSizeNoCompression() { + public void stringAboveThresholdSize() { DocumentNodeStore store = mock(DocumentNodeStore.class); CompressedDocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); @@ -158,18 +122,6 @@ public void stringAboveThresholdSizeNoCompression() { assertEquals(STRING_HUGEVALUE, state.getValue(Type.STRING)); } - @Test - public void testInterestingStringsWithoutCompression() { - DocumentNodeStore store = mock(DocumentNodeStore.class); - String[] tests = new String[] { "simple:foo", "cr:a\n\b", "dquote:a\"b", "bs:a\\b", "euro:a\u201c", "gclef:\uD834\uDD1E", - "tab:a\tb", "nul:a\u0000b"}; - - for (String test : tests) { - CompressedDocumentPropertyState state = new CompressedDocumentPropertyState(store, "propertyName", test, Compression.GZIP); - assertEquals(test, state.getValue()); - } - } - @Test public void testInterestingStringsWithCompression() { DocumentNodeStore store = mock(DocumentNodeStore.class); @@ -183,23 +135,6 @@ public void testInterestingStringsWithCompression() { } } - @Test - public void testEqualsWithoutCompression() { - DocumentNodeStore store = mock(DocumentNodeStore.class); - String name = "propertyName"; - String value = "testValue"; - Compression compression = Compression.GZIP; - - CompressedDocumentPropertyState state1 = new CompressedDocumentPropertyState(store, name, value, compression); - CompressedDocumentPropertyState state2 = new CompressedDocumentPropertyState(store, name, value, compression); - - assertEquals(state1, state2); - - // Test for inequality - CompressedDocumentPropertyState state4 = new CompressedDocumentPropertyState(store, "differentName", value, compression); - assertNotEquals(state1, state4); - } - @Test public void testEqualsWithCompression() throws IOException { DocumentNodeStore store = mock(DocumentNodeStore.class); From b41a7bc6f7feec939d8efb54a7b07d6b3141ef4f Mon Sep 17 00:00:00 2001 From: pirlogea Date: Thu, 8 Aug 2024 15:01:47 +0300 Subject: [PATCH 10/31] OAK-10973 call DocumentPropertyState with factory method --- .../jackrabbit/oak/plugins/document/DocumentNodeStore.java | 5 ++--- .../oak/plugins/document/DocumentPropertyStateFactory.java | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java index f5ed5650755..776f39ad8aa 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java @@ -1353,7 +1353,7 @@ AbstractDocumentNodeState getSecondaryNodeState(@NotNull final Path path, @NotNull public PropertyState createPropertyState(String name, String value){ - return new DocumentPropertyState(this, name, checkNotNull(value)); + return DocumentPropertyStateFactory.createPropertyState(this, name, checkNotNull(value)); } /** @@ -3214,8 +3214,7 @@ private long getBinarySize(@Nullable String json) { if (json == null) { return -1; } - PropertyState p = new DocumentPropertyState( - DocumentNodeStore.this, "p", json); + PropertyState p = DocumentPropertyStateFactory.createPropertyState(DocumentNodeStore.this, "p", json); if (p.getType().tag() != PropertyType.BINARY) { return -1; } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java index 32d2a9c67e2..f0f265c0d2e 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java @@ -41,6 +41,6 @@ public static PropertyState createPropertyState(DocumentNodeStore store, String } public static PropertyState createPropertyState(DocumentNodeStore store, String name, String value) { - return createPropertyState(store, name, value, Compression.GZIP); + return createPropertyState(store, name, value, Compression.NONE); } } From cfc5c3c021acebc9e3cdfae8e2d957b6dd148452 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Thu, 8 Aug 2024 16:15:45 +0300 Subject: [PATCH 11/31] OAK-10973 -- added tests for DocumentPropertyStateFactoryTest --- .../DocumentPropertyStateFactoryTest.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java new file mode 100644 index 00000000000..4f64f3ead5b --- /dev/null +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.jackrabbit.oak.plugins.document; + +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.commons.Compression; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class DocumentPropertyStateFactoryTest { + + @Test + public void createPropertyStateWithCompressionNone() { + DocumentNodeStore store = Mockito.mock(DocumentNodeStore.class); + String name = "testName"; + String value = "testValue"; + + PropertyState propertyState = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\"", Compression.NONE); + + assertTrue(propertyState instanceof DocumentPropertyState); + assertEquals(name, propertyState.getName()); + assertEquals(value, propertyState.getValue(Type.STRING)); + } + + @Test + public void createPropertyStateWithCompressionGzip() { + DocumentNodeStore store = Mockito.mock(DocumentNodeStore.class); + String name = "testName"; + String value = "testValue"; + + PropertyState propertyState = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\"", Compression.GZIP); + + assertTrue(propertyState instanceof CompressedDocumentPropertyState); + assertEquals(name, propertyState.getName()); + assertEquals(value, propertyState.getValue(Type.STRING)); + } + + @Test + public void createPropertyStateWithDefaultCompression() { + DocumentNodeStore store = Mockito.mock(DocumentNodeStore.class); + String name = "testName"; + String value = "testValue"; + + PropertyState propertyState = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\""); + + assertTrue(propertyState instanceof DocumentPropertyState); + assertEquals(name, propertyState.getName()); + assertEquals(value, propertyState.getValue(Type.STRING)); + } + + @Test + public void createPropertyStateWithCompressionThresholdExceeded() { + DocumentNodeStore store = Mockito.mock(DocumentNodeStore.class); + String name = "testName"; + String value = "a".repeat(CompressedDocumentPropertyState.getCompressionThreshold() + 1); + + PropertyState propertyState = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\"", Compression.GZIP); + + assertTrue(propertyState instanceof CompressedDocumentPropertyState); + assertEquals(name, propertyState.getName()); + assertEquals(value, propertyState.getValue(Type.STRING)); + } + + @Test + public void createPropertyStateWithCompressionThresholdNotExceeded() { + DocumentNodeStore store = Mockito.mock(DocumentNodeStore.class); + String name = "testName"; + CompressedDocumentPropertyState.setCompressionThreshold(15); + String value = "testValue"; + + PropertyState propertyState = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\"", Compression.GZIP); + + assertTrue(propertyState instanceof DocumentPropertyState); + assertEquals(name, propertyState.getName()); + assertEquals(value, propertyState.getValue(Type.STRING)); + } +} From e77cbb7bf8df121e99f3f19dcde5c8cee1896cc5 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Thu, 8 Aug 2024 17:20:14 +0300 Subject: [PATCH 12/31] OAK-10973 -- added tests for DocumentPropertyStateFactoryTest --- .../oak/plugins/document/DocumentPropertyStateFactoryTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java index 4f64f3ead5b..575c282c401 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java @@ -70,7 +70,7 @@ public void createPropertyStateWithDefaultCompression() { public void createPropertyStateWithCompressionThresholdExceeded() { DocumentNodeStore store = Mockito.mock(DocumentNodeStore.class); String name = "testName"; - String value = "a".repeat(CompressedDocumentPropertyState.getCompressionThreshold() + 1); + String value = "testValue"; PropertyState propertyState = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\"", Compression.GZIP); From 6546b1d930fe7af808d6dabfb7198b752d7ccfc0 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Mon, 12 Aug 2024 10:55:14 +0300 Subject: [PATCH 13/31] OAK-10973 -- added another test method --- .../DocumentPropertyStateFactoryTest.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java index 575c282c401..a24eb8adec3 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java @@ -22,8 +22,16 @@ import org.junit.Test; import org.mockito.Mockito; +import java.io.IOException; +import java.io.OutputStream; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.times; public class DocumentPropertyStateFactoryTest { @@ -92,4 +100,18 @@ public void createPropertyStateWithCompressionThresholdNotExceeded() { assertEquals(name, propertyState.getName()); assertEquals(value, propertyState.getValue(Type.STRING)); } + + @Test(expected = IllegalArgumentException.class) + public void createPropertyStateWithCompressionThresholdExceededFail() throws IOException { + DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); + Compression mockCompression = mock(Compression.class); + when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IOException("Compression failed")); + + CompressedDocumentPropertyState.setCompressionThreshold(5); + CompressedDocumentPropertyState documentPropertyState = new CompressedDocumentPropertyState(mockDocumentStore, "p", "\"" + "testValue" + "\"", mockCompression); + + assertEquals(documentPropertyState.getValue(Type.STRING), "tesValue"); + + verify(mockCompression, times(1)).getOutputStream(any(OutputStream.class)); + } } From 8300cd32134e7a0b3c1d6ffb5f480ff712323fac Mon Sep 17 00:00:00 2001 From: pirlogea Date: Mon, 12 Aug 2024 13:52:43 +0300 Subject: [PATCH 14/31] OAK-10973 -- set default GZIP for compression --- .../oak/plugins/document/DocumentPropertyStateFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java index f0f265c0d2e..32d2a9c67e2 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java @@ -41,6 +41,6 @@ public static PropertyState createPropertyState(DocumentNodeStore store, String } public static PropertyState createPropertyState(DocumentNodeStore store, String name, String value) { - return createPropertyState(store, name, value, Compression.NONE); + return createPropertyState(store, name, value, Compression.GZIP); } } From bcbc4bd5c834eaec970ecc8ab9ccdc25becd0126 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Mon, 12 Aug 2024 14:16:58 +0300 Subject: [PATCH 15/31] OAK-10973 -- refactor --- .../document/CompressedDocumentPropertyState.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java index 0288f269e68..f3d2b8affaa 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java @@ -51,7 +51,7 @@ import org.slf4j.LoggerFactory; /** - * PropertyState implementation with lazy parsing of the JSOP encoded value. + * PropertyState compression implementation with lazy parsing of the JSOP encoded value. */ public final class CompressedDocumentPropertyState implements PropertyState { @@ -340,11 +340,4 @@ static PropertyState readArrayProperty(String name, DocumentNodeStore store, Jso } return createProperty(name, values, Type.fromTag(type, true)); } -} - - - - - - - +} \ No newline at end of file From 3b9cd921685485e008e37dbf43af8ceb13c6b5a2 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Mon, 12 Aug 2024 15:00:14 +0300 Subject: [PATCH 16/31] OAK-10973 -- manually added b04de7ea9dce0859de09f90964fa650a7bf5a978 revision --- .../plugins/document/DocumentPropertyState.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java index 872985e5664..d2bf4c925eb 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java @@ -26,6 +26,7 @@ import org.apache.jackrabbit.guava.common.collect.Lists; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.commons.LongUtils; import org.apache.jackrabbit.oak.commons.json.JsopReader; import org.apache.jackrabbit.oak.commons.json.JsopTokenizer; import org.apache.jackrabbit.oak.json.TypeCodes; @@ -180,10 +181,11 @@ PropertyState readProperty(String name, JsopReader reader) { static PropertyState readProperty(String name, DocumentNodeStore store, JsopReader reader) { if (reader.matches(JsopReader.NUMBER)) { String number = reader.getToken(); - try { - return new LongPropertyState(name, Long.parseLong(number)); - } catch (NumberFormatException e) { + Long maybeLong = LongUtils.tryParse(number); + if (maybeLong == null) { return new DoublePropertyState(name, Double.parseDouble(number)); + } else { + return new LongPropertyState(name, maybeLong); } } else if (reader.matches(JsopReader.TRUE)) { return BooleanPropertyState.booleanProperty(name, true); @@ -238,12 +240,13 @@ static PropertyState readArrayProperty(String name, DocumentNodeStore store, Jso while (!reader.matches(']')) { if (reader.matches(JsopReader.NUMBER)) { String number = reader.getToken(); - try { - type = PropertyType.LONG; - values.add(Long.parseLong(number)); - } catch (NumberFormatException e) { + Long maybeLong = LongUtils.tryParse(number); + if (maybeLong == null) { type = PropertyType.DOUBLE; values.add(Double.parseDouble(number)); + } else { + type = PropertyType.LONG; + values.add(maybeLong); } } else if (reader.matches(JsopReader.TRUE)) { type = PropertyType.BOOLEAN; From dc2c58c9fa65f23a4e3425188ecd71a52823272c Mon Sep 17 00:00:00 2001 From: pirlogea Date: Mon, 12 Aug 2024 15:04:04 +0300 Subject: [PATCH 17/31] OAK-10973 -- replace guava collection with jdk one --- .../plugins/document/CompressedDocumentPropertyStateTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java index 2afbdbafeef..c8658d8aad8 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java @@ -16,7 +16,6 @@ */ package org.apache.jackrabbit.oak.plugins.document; -import static org.apache.jackrabbit.guava.common.collect.Sets.newHashSet; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; @@ -28,6 +27,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.HashSet; import java.util.Set; import org.apache.commons.lang3.RandomStringUtils; @@ -49,7 +49,7 @@ public class CompressedDocumentPropertyStateTest { @Rule public DocumentMKBuilderProvider builderProvider = new DocumentMKBuilderProvider(); - private Set reads = newHashSet(); + private Set reads = new HashSet<>(); private BlobStore bs = new MemoryBlobStore() { @Override From 8e959de2db73f8211e7bd843cc2393cb3614cf43 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Mon, 12 Aug 2024 15:54:58 +0300 Subject: [PATCH 18/31] OAK-10973 -- refactor tests for default Compression GZIP --- .../DocumentPropertyStateFactoryTest.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java index a24eb8adec3..c759cc50e3a 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java @@ -19,6 +19,8 @@ import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.Compression; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -35,6 +37,19 @@ public class DocumentPropertyStateFactoryTest { + private static final int COMPRESSION_THRESHOLD = 5; + private static final int DISABLED_COMPRESSION = -1; + + @Before + public void before() throws Exception { + CompressedDocumentPropertyState.setCompressionThreshold(COMPRESSION_THRESHOLD); + } + + @After + public void tearDown() { + CompressedDocumentPropertyState.setCompressionThreshold(DISABLED_COMPRESSION); + } + @Test public void createPropertyStateWithCompressionNone() { DocumentNodeStore store = Mockito.mock(DocumentNodeStore.class); @@ -67,9 +82,10 @@ public void createPropertyStateWithDefaultCompression() { String name = "testName"; String value = "testValue"; + assertEquals(CompressedDocumentPropertyState.getCompressionThreshold(), COMPRESSION_THRESHOLD); PropertyState propertyState = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\""); - assertTrue(propertyState instanceof DocumentPropertyState); + assertTrue(propertyState instanceof CompressedDocumentPropertyState); assertEquals(name, propertyState.getName()); assertEquals(value, propertyState.getValue(Type.STRING)); } @@ -102,7 +118,7 @@ public void createPropertyStateWithCompressionThresholdNotExceeded() { } @Test(expected = IllegalArgumentException.class) - public void createPropertyStateWithCompressionThresholdExceededFail() throws IOException { + public void createPropertyStateWithCompressionThrowsException() throws IOException { DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); Compression mockCompression = mock(Compression.class); when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IOException("Compression failed")); From 8f00576e3610d1e320bdfd28e672edb4a9b32abc Mon Sep 17 00:00:00 2001 From: pirlogea Date: Mon, 12 Aug 2024 16:38:10 +0300 Subject: [PATCH 19/31] OAK-10973 -- refactor guava collection --- .../oak/plugins/document/CompressedDocumentPropertyState.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java index f3d2b8affaa..7952f22366b 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java @@ -24,12 +24,12 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.jcr.PropertyType; -import org.apache.jackrabbit.guava.common.collect.Lists; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.Compression; @@ -296,7 +296,7 @@ PropertyState readArrayProperty(String name, JsopReader reader) { */ static PropertyState readArrayProperty(String name, DocumentNodeStore store, JsopReader reader) { int type = PropertyType.STRING; - List values = Lists.newArrayList(); + List values = new ArrayList<>(); while (!reader.matches(']')) { if (reader.matches(JsopReader.NUMBER)) { String number = reader.getToken(); From 32745a9413ac213b4ce08880974e0c2d3fa0d4bb Mon Sep 17 00:00:00 2001 From: pirlogea Date: Mon, 12 Aug 2024 17:33:54 +0300 Subject: [PATCH 20/31] OAK-10973 -- refactor guava collection and move test broken surrogate to CompressedDocumentPropertyStateTest --- .../CompressedDocumentPropertyStateTest.java | 84 +++++++++++++++++ .../document/DocumentPropertyStateTest.java | 89 +------------------ 2 files changed, 88 insertions(+), 85 deletions(-) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java index c8658d8aad8..6b7f54e3902 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -28,13 +29,20 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.HashSet; +import java.util.Objects; import java.util.Set; +import com.mongodb.ReadPreference; import org.apache.commons.lang3.RandomStringUtils; +import org.apache.jackrabbit.oak.api.CommitFailedException; +import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.Compression; +import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore; +import org.apache.jackrabbit.oak.plugins.document.mongo.MongoTestUtils; import org.apache.jackrabbit.oak.spi.blob.BlobStore; import org.apache.jackrabbit.oak.spi.blob.MemoryBlobStore; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -135,6 +143,82 @@ public void testInterestingStringsWithCompression() { } } + @Test + public void testBrokenSurrogateWithoutCompressionForMongo() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, false); + } + + @Test + public void testBrokenSurrogateWithoutCompressionForRDB() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, false); + } + + @Test + public void testBrokenSurrogateWithoutCompressionForInMemory() throws CommitFailedException { + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, false); + } + + @Test + public void testBrokenSurrogateWithCompressionForMongo() throws CommitFailedException { + CompressedDocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, true); + } + + @Test + public void testBrokenSurrogateWithCompressionForRDB() throws CommitFailedException { + CompressedDocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, true); + } + + @Test + public void testBrokenSurrogateWithCompressionForInMemory() throws CommitFailedException { + CompressedDocumentPropertyState.setCompressionThreshold(1); + getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, true); + } + + private void getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture fixture, boolean compressionEnabled) throws CommitFailedException { + assumeTrue(fixture.isAvailable()); + String test = "brokensurrogate:dfsa\ud800"; + DocumentStore store = null; + DocumentNodeStore nodeStore = null; + + try { + store = fixture.createDocumentStore(); + + if (store instanceof MongoDocumentStore) { + // Enforce primary read preference, otherwise tests may fail on a + // replica set with a read preference configured to secondary. + // Revision GC usually runs with a modified range way in the past, + // which means changes made it to the secondary, but not in this + // test using a virtual clock + MongoTestUtils.setReadPreference(store, ReadPreference.primary()); + } + nodeStore = new DocumentMK.Builder().setDocumentStore(store).getNodeStore(); + + createPropAndCheckValue(nodeStore, test, compressionEnabled); + + } finally { + if (nodeStore != null) { + nodeStore.dispose(); + } + } + + } + + private void createPropAndCheckValue(DocumentNodeStore nodeStore, String test, boolean compressionEnabled) throws CommitFailedException { + NodeBuilder builder = nodeStore.getRoot().builder(); + + if (compressionEnabled) { + CompressedDocumentPropertyState.setCompressionThreshold(1); + } + builder.child("test").setProperty("p", test, Type.STRING); + TestUtils.merge(nodeStore, builder); + + PropertyState p = nodeStore.getRoot().getChildNode("test").getProperty("p"); + assertEquals(Objects.requireNonNull(p).getValue(Type.STRING), test); + } + + @Test public void testEqualsWithCompression() throws IOException { DocumentNodeStore store = mock(DocumentNodeStore.class); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java index c277c83341d..43a68184fc0 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateTest.java @@ -16,24 +16,18 @@ */ package org.apache.jackrabbit.oak.plugins.document; -import static org.apache.jackrabbit.guava.common.collect.Lists.newArrayList; -import static org.apache.jackrabbit.guava.common.collect.Sets.newHashSet; import static org.junit.Assert.assertEquals; -import static org.junit.Assume.assumeTrue; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; -import java.util.Objects; import java.util.Set; -import com.mongodb.ReadPreference; import org.apache.jackrabbit.oak.api.Blob; -import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; -import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore; -import org.apache.jackrabbit.oak.plugins.document.mongo.MongoTestUtils; import org.apache.jackrabbit.oak.spi.blob.BlobStore; import org.apache.jackrabbit.oak.spi.blob.MemoryBlobStore; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; @@ -48,7 +42,7 @@ public class DocumentPropertyStateTest { @Rule public DocumentMKBuilderProvider builderProvider = new DocumentMKBuilderProvider(); - private Set reads = newHashSet(); + private Set reads = new HashSet<>(); private BlobStore bs = new MemoryBlobStore() { @Override @@ -70,7 +64,7 @@ public void before() throws Exception { @Test public void multiValuedBinarySize() throws Exception { NodeBuilder builder = ns.getRoot().builder(); - List blobs = newArrayList(); + List blobs = new ArrayList<>(); for (int i = 0; i < 3; i++) { blobs.add(builder.createBlob(new RandomStream(BLOB_SIZE, i))); } @@ -86,79 +80,4 @@ public void multiValuedBinarySize() throws Exception { // must not read the blob via stream assertEquals(0, reads.size()); } - - @Test - public void testBrokenSurrogateWithoutCompressionForMongo() throws CommitFailedException { - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, false); - } - - @Test - public void testBrokenSurrogateWithoutCompressionForRDB() throws CommitFailedException { - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, false); - } - - @Test - public void testBrokenSurrogateWithoutCompressionForInMemory() throws CommitFailedException { - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, false); - } - - @Test - public void testBrokenSurrogateWithCompressionForMongo() throws CommitFailedException { - CompressedDocumentPropertyState.setCompressionThreshold(1); - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MONGO, true); - } - - @Test - public void testBrokenSurrogateWithCompressionForRDB() throws CommitFailedException { - CompressedDocumentPropertyState.setCompressionThreshold(1); - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.RDB_H2, true); - } - - @Test - public void testBrokenSurrogateWithCompressionForInMemory() throws CommitFailedException { - CompressedDocumentPropertyState.setCompressionThreshold(1); - getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture.MEMORY, true); - } - - private void getBrokenSurrogateAndInitializeDifferentStores(DocumentStoreFixture fixture, boolean compressionEnabled) throws CommitFailedException { - assumeTrue(fixture.isAvailable()); - String test = "brokensurrogate:dfsa\ud800"; - DocumentStore store = null; - DocumentNodeStore nodeStore = null; - - try { - store = fixture.createDocumentStore(); - - if (store instanceof MongoDocumentStore) { - // Enforce primary read preference, otherwise tests may fail on a - // replica set with a read preference configured to secondary. - // Revision GC usually runs with a modified range way in the past, - // which means changes made it to the secondary, but not in this - // test using a virtual clock - MongoTestUtils.setReadPreference(store, ReadPreference.primary()); - } - nodeStore = new DocumentMK.Builder().setDocumentStore(store).getNodeStore(); - - createPropAndCheckValue(nodeStore, test, compressionEnabled); - - } finally { - if (nodeStore != null) { - nodeStore.dispose(); - } - } - - } - - private void createPropAndCheckValue(DocumentNodeStore nodeStore, String test, boolean compressionEnabled) throws CommitFailedException { - NodeBuilder builder = nodeStore.getRoot().builder(); - - if (compressionEnabled) { - CompressedDocumentPropertyState.setCompressionThreshold(1); - } - builder.child("test").setProperty("p", test, Type.STRING); - TestUtils.merge(nodeStore, builder); - - PropertyState p = nodeStore.getRoot().getChildNode("test").getProperty("p"); - assertEquals(Objects.requireNonNull(p).getValue(Type.STRING), test); - } } \ No newline at end of file From c85c79eee38dddb6b036a3bdc2d0c7d8cf53a667 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 09:17:45 +0300 Subject: [PATCH 21/31] OAK-10973 -- added -1 condition into factory method --- .../oak/plugins/document/DocumentPropertyStateFactory.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java index 32d2a9c67e2..e0578b9375c 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java @@ -27,7 +27,9 @@ public class DocumentPropertyStateFactory { public static PropertyState createPropertyState(DocumentNodeStore store, String name, String value, Compression compression) { - if (compression != null && !compression.equals(Compression.NONE) && value.length() > CompressedDocumentPropertyState.getCompressionThreshold()) { + if (compression != null && !compression.equals(Compression.NONE) && + value.length() > CompressedDocumentPropertyState.getCompressionThreshold() + && CompressedDocumentPropertyState.getCompressionThreshold() != -1) { try { return new CompressedDocumentPropertyState(store, name, value, compression); } catch (Exception e) { From 1980813af765bc63adabcf734b4bc1d4d02bee7a Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 10:03:19 +0300 Subject: [PATCH 22/31] OAK-10973 -- added test for -1 --- .../DocumentPropertyStateFactoryTest.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java index c759cc50e3a..ef9aa2dcdb0 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java @@ -16,6 +16,7 @@ */ package org.apache.jackrabbit.oak.plugins.document; +import org.apache.commons.lang3.RandomStringUtils; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.Compression; @@ -39,6 +40,7 @@ public class DocumentPropertyStateFactoryTest { private static final int COMPRESSION_THRESHOLD = 5; private static final int DISABLED_COMPRESSION = -1; + private static final String STRING_HUGEVALUE = RandomStringUtils.random(10050, "dummytest"); @Before public void before() throws Exception { @@ -107,8 +109,8 @@ public void createPropertyStateWithCompressionThresholdExceeded() { public void createPropertyStateWithCompressionThresholdNotExceeded() { DocumentNodeStore store = Mockito.mock(DocumentNodeStore.class); String name = "testName"; - CompressedDocumentPropertyState.setCompressionThreshold(15); String value = "testValue"; + CompressedDocumentPropertyState.setCompressionThreshold(15); PropertyState propertyState = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\"", Compression.GZIP); @@ -117,6 +119,19 @@ public void createPropertyStateWithCompressionThresholdNotExceeded() { assertEquals(value, propertyState.getValue(Type.STRING)); } + @Test + public void defaultValueSetToMinusOne() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + String name = "propertyNameNew"; + + CompressedDocumentPropertyState.setCompressionThreshold(-1); + PropertyState state = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + STRING_HUGEVALUE + "\""); + + assertTrue(state instanceof DocumentPropertyState); + assertEquals(name, state.getName()); + assertEquals(STRING_HUGEVALUE, state.getValue(Type.STRING)); + } + @Test(expected = IllegalArgumentException.class) public void createPropertyStateWithCompressionThrowsException() throws IOException { DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); From 43960b78e9518f6973e34f1d0b3d850b2db2c291 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 12:07:49 +0300 Subject: [PATCH 23/31] OAK-10973 -- remove guava from DocumentPropertyState --- .../oak/plugins/document/DocumentPropertyState.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java index d2bf4c925eb..c5dbe71673d 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyState.java @@ -19,11 +19,11 @@ import static java.util.Collections.emptyList; import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty; +import java.util.ArrayList; import java.util.List; import javax.jcr.PropertyType; -import org.apache.jackrabbit.guava.common.collect.Lists; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.LongUtils; @@ -236,7 +236,7 @@ PropertyState readArrayProperty(String name, JsopReader reader) { */ static PropertyState readArrayProperty(String name, DocumentNodeStore store, JsopReader reader) { int type = PropertyType.STRING; - List values = Lists.newArrayList(); + List values = new ArrayList(); while (!reader.matches(']')) { if (reader.matches(JsopReader.NUMBER)) { String number = reader.getToken(); From d3621a2b74d95a5523060150919581ca5aa328e6 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 12:47:41 +0300 Subject: [PATCH 24/31] OAK-10973 -- remove duplicate static methods in CompressedDocumentPropertyState --- .../CompressedDocumentPropertyState.java | 104 +----------------- 1 file changed, 2 insertions(+), 102 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java index 7952f22366b..2d25eb23857 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java @@ -227,52 +227,7 @@ private PropertyState parsed() { * @return new property state */ PropertyState readProperty(String name, JsopReader reader) { - return readProperty(name, store, reader); - } - - /** - * Read a {@code PropertyState} from a {@link JsopReader}. - * - * @param name the name of the property state - * @param store the store - * @param reader the reader - * @return new property state - */ - static PropertyState readProperty(String name, DocumentNodeStore store, JsopReader reader) { - if (reader.matches(JsopReader.NUMBER)) { - String number = reader.getToken(); - Long maybeLong = LongUtils.tryParse(number); - if (maybeLong == null) { - return new DoublePropertyState(name, Double.parseDouble(number)); - } else { - return new LongPropertyState(name, maybeLong); - } - } else if (reader.matches(JsopReader.TRUE)) { - return BooleanPropertyState.booleanProperty(name, true); - } else if (reader.matches(JsopReader.FALSE)) { - return BooleanPropertyState.booleanProperty(name, false); - } else if (reader.matches(JsopReader.STRING)) { - String jsonString = reader.getToken(); - if (jsonString.startsWith(TypeCodes.EMPTY_ARRAY)) { - int type = PropertyType.valueFromName(jsonString.substring(TypeCodes.EMPTY_ARRAY.length())); - return PropertyStates.createProperty(name, emptyList(), Type.fromTag(type, true)); - } - int split = TypeCodes.split(jsonString); - if (split != -1) { - int type = TypeCodes.decodeType(split, jsonString); - String value = TypeCodes.decodeName(split, jsonString); - if (type == PropertyType.BINARY) { - - return BinaryPropertyState.binaryProperty(name, store.getBlobFromBlobId(value)); - } else { - return createProperty(name, StringCache.get(value), type); - } - } else { - return StringPropertyState.stringProperty(name, StringCache.get(jsonString)); - } - } else { - throw new IllegalArgumentException("Unexpected token: " + reader.getToken()); - } + return DocumentPropertyState.readProperty(name, store, reader); } /** @@ -283,61 +238,6 @@ static PropertyState readProperty(String name, DocumentNodeStore store, JsopRead * @return new property state */ PropertyState readArrayProperty(String name, JsopReader reader) { - return readArrayProperty(name, store, reader); - } - - /** - * Read a multi valued {@code PropertyState} from a {@link JsopReader}. - * - * @param name the name of the property state - * @param store the store - * @param reader the reader - * @return new property state - */ - static PropertyState readArrayProperty(String name, DocumentNodeStore store, JsopReader reader) { - int type = PropertyType.STRING; - List values = new ArrayList<>(); - while (!reader.matches(']')) { - if (reader.matches(JsopReader.NUMBER)) { - String number = reader.getToken(); - Long maybeLong = LongUtils.tryParse(number); - if (maybeLong == null) { - type = PropertyType.DOUBLE; - values.add(Double.parseDouble(number)); - } else { - type = PropertyType.LONG; - values.add(maybeLong); - } - } else if (reader.matches(JsopReader.TRUE)) { - type = PropertyType.BOOLEAN; - values.add(true); - } else if (reader.matches(JsopReader.FALSE)) { - type = PropertyType.BOOLEAN; - values.add(false); - } else if (reader.matches(JsopReader.STRING)) { - String jsonString = reader.getToken(); - int split = TypeCodes.split(jsonString); - if (split != -1) { - type = TypeCodes.decodeType(split, jsonString); - String value = TypeCodes.decodeName(split, jsonString); - if (type == PropertyType.BINARY) { - values.add(store.getBlobFromBlobId(value)); - } else if (type == PropertyType.DOUBLE) { - values.add(Conversions.convert(value).toDouble()); - } else if (type == PropertyType.DECIMAL) { - values.add(Conversions.convert(value).toDecimal()); - } else { - values.add(StringCache.get(value)); - } - } else { - type = PropertyType.STRING; - values.add(StringCache.get(jsonString)); - } - } else { - throw new IllegalArgumentException("Unexpected token: " + reader.getToken()); - } - reader.matches(','); - } - return createProperty(name, values, Type.fromTag(type, true)); + return DocumentPropertyState.readArrayProperty(name, store, reader); } } \ No newline at end of file From 102db74d32a448ebdaefbe0d244291de50f5a83d Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 13:27:12 +0300 Subject: [PATCH 25/31] OAK-10973 -- added required test methods CompressedDocumentPropertyStateTest --- .../CompressedDocumentPropertyStateTest.java | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java index 6b7f54e3902..293155ced01 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java @@ -16,8 +16,7 @@ */ package org.apache.jackrabbit.oak.plugins.document; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -130,6 +129,57 @@ public void stringAboveThresholdSize() { assertEquals(STRING_HUGEVALUE, state.getValue(Type.STRING)); } + @Test + public void uncompressValueThrowsException() throws IOException { + + DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); + Compression mockCompression = mock(Compression.class); + OutputStream mockOutputStream= mock(OutputStream.class); + when(mockCompression.getOutputStream(any(OutputStream.class))).thenReturn(mockOutputStream); + when(mockCompression.getInputStream(any(InputStream.class))).thenThrow(new IOException("Compression failed")); + + CompressedDocumentPropertyState.setCompressionThreshold(DEFAULT_COMPRESSION_THRESHOLD); + PropertyState documentPropertyState = DocumentPropertyStateFactory.createPropertyState(mockDocumentStore, "p", STRING_HUGEVALUE, mockCompression); + + assertEquals(documentPropertyState.getValue(Type.STRING), "{}"); + + verify(mockCompression, times(1)).getInputStream(any(InputStream.class)); + } + + @Test + public void testInterestingStringsWithoutCompression() { + DocumentNodeStore store = mock(DocumentNodeStore.class); + String[] tests = new String[] { "simple:foo", "cr:a\n\b", "dquote:a\"b", "bs:a\\b", "euro:a\u201c", "gclef:\uD834\uDD1E", + "tab:a\tb", "nul:a\u0000b"}; + + for (String test : tests) { + DocumentPropertyState state = (DocumentPropertyState) DocumentPropertyStateFactory.createPropertyState(store, "propertyName", test, Compression.GZIP); + assertEquals(test, state.getValue()); + } + } + + @Test + public void testOneCompressOtherUncompressInEquals() throws IOException { + DocumentNodeStore store = mock(DocumentNodeStore.class); + String name = "propertyName"; + String value = "testValue"; + Compression compression = Compression.GZIP; + + // Create a PropertyState instance with a compressed value + CompressedDocumentPropertyState.setCompressionThreshold(1); + PropertyState state1 = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\""); + + // Create a PropertyState instance with an uncompressed value + CompressedDocumentPropertyState.setCompressionThreshold(-1); + PropertyState state2 = DocumentPropertyStateFactory.createPropertyState(store, name, value); + + // Decompress the value of the first instance + String decompressedValue1 = state1.getValue(Type.STRING); + + // Check that the decompressed value is equal to the original value + assertEquals(value, decompressedValue1); + } + @Test public void testInterestingStringsWithCompression() { DocumentNodeStore store = mock(DocumentNodeStore.class); From ff16bea325810789c827aa7e7a39b656a8d6e4d3 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 14:32:42 +0300 Subject: [PATCH 26/31] OAK-10973 -- refactor if statement --- .../oak/plugins/document/DocumentPropertyStateFactory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java index e0578b9375c..71a8c82c7d5 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactory.java @@ -28,8 +28,8 @@ public class DocumentPropertyStateFactory { public static PropertyState createPropertyState(DocumentNodeStore store, String name, String value, Compression compression) { if (compression != null && !compression.equals(Compression.NONE) && - value.length() > CompressedDocumentPropertyState.getCompressionThreshold() - && CompressedDocumentPropertyState.getCompressionThreshold() != -1) { + CompressedDocumentPropertyState.getCompressionThreshold() != -1 + && value.length() > CompressedDocumentPropertyState.getCompressionThreshold()) { try { return new CompressedDocumentPropertyState(store, name, value, compression); } catch (Exception e) { From 933a2fecc4ae6fc1cf294d432f0dade32cf54a18 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 14:36:55 +0300 Subject: [PATCH 27/31] OAK-10973 -- refactor if statement --- .../CompressedDocumentPropertyStateTest.java | 16 ---------------- .../DocumentPropertyStateFactoryTest.java | 8 ++++---- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java index 293155ced01..d370570acab 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java @@ -83,22 +83,6 @@ public void tearDown() { } } - @Test - public void stringBelowThresholdSize() throws Exception { - DocumentNodeStore store = mock(DocumentNodeStore.class); - CompressedDocumentPropertyState.setCompressionThreshold(10000); - - CompressedDocumentPropertyState state = new CompressedDocumentPropertyState(store, "p", "\"" + STRING_HUGEVALUE + "\"", Compression.GZIP); - - assertEquals(Type.STRING, state.getType()); - assertEquals(1, state.count()); - - reads.clear(); - assertEquals(10050, state.size(0)); - // must not read the string via streams - assertEquals(0, reads.size()); - } - @Test(expected = IllegalArgumentException.class) public void compressValueThrowsException() throws IOException { DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java index ef9aa2dcdb0..8123105ff60 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentPropertyStateFactoryTest.java @@ -132,16 +132,16 @@ public void defaultValueSetToMinusOne() { assertEquals(STRING_HUGEVALUE, state.getValue(Type.STRING)); } - @Test(expected = IllegalArgumentException.class) + @Test public void createPropertyStateWithCompressionThrowsException() throws IOException { DocumentNodeStore mockDocumentStore = mock(DocumentNodeStore.class); Compression mockCompression = mock(Compression.class); - when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IOException("Compression failed")); + when(mockCompression.getOutputStream(any(OutputStream.class))).thenThrow(new IllegalArgumentException("Compression failed")); CompressedDocumentPropertyState.setCompressionThreshold(5); - CompressedDocumentPropertyState documentPropertyState = new CompressedDocumentPropertyState(mockDocumentStore, "p", "\"" + "testValue" + "\"", mockCompression); + PropertyState documentPropertyState = DocumentPropertyStateFactory.createPropertyState(mockDocumentStore, "p", "\"" + "testValue" + "\"", mockCompression); - assertEquals(documentPropertyState.getValue(Type.STRING), "tesValue"); + assertEquals(documentPropertyState.getValue(Type.STRING), "testValue"); verify(mockCompression, times(1)).getOutputStream(any(OutputStream.class)); } From 17ea660d2b0501ebd6c6990d94db1253f3ea1515 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 14:46:46 +0300 Subject: [PATCH 28/31] OAK-10973 -- refactor test methods --- .../plugins/document/CompressedDocumentPropertyStateTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java index d370570acab..a5dd209842a 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java @@ -155,7 +155,9 @@ public void testOneCompressOtherUncompressInEquals() throws IOException { // Create a PropertyState instance with an uncompressed value CompressedDocumentPropertyState.setCompressionThreshold(-1); - PropertyState state2 = DocumentPropertyStateFactory.createPropertyState(store, name, value); + PropertyState state2 = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\""); + + assertEquals(state1, state2); // Decompress the value of the first instance String decompressedValue1 = state1.getValue(Type.STRING); From d5711fd03553a4d9f6b0404a180b996bef74d581 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 14:48:12 +0300 Subject: [PATCH 29/31] OAK-10973 -- refactor test methods --- .../plugins/document/CompressedDocumentPropertyStateTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java index a5dd209842a..4bb841c9d8f 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java @@ -157,7 +157,7 @@ public void testOneCompressOtherUncompressInEquals() throws IOException { CompressedDocumentPropertyState.setCompressionThreshold(-1); PropertyState state2 = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\""); - assertEquals(state1, state2); + assertEquals(state2, state1); // Decompress the value of the first instance String decompressedValue1 = state1.getValue(Type.STRING); From f5484e2d778663233d31ee9a0aa92acd2aeec758 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 14:49:12 +0300 Subject: [PATCH 30/31] OAK-10973 -- refactor test methods --- .../plugins/document/CompressedDocumentPropertyStateTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java index 4bb841c9d8f..64fca703028 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyStateTest.java @@ -158,6 +158,7 @@ public void testOneCompressOtherUncompressInEquals() throws IOException { PropertyState state2 = DocumentPropertyStateFactory.createPropertyState(store, name, "\"" + value + "\""); assertEquals(state2, state1); + assertEquals(state1, state2); // Decompress the value of the first instance String decompressedValue1 = state1.getValue(Type.STRING); From 8d14734f2bd5525557d69c717a87f575145f8911 Mon Sep 17 00:00:00 2001 From: pirlogea Date: Tue, 13 Aug 2024 15:35:49 +0300 Subject: [PATCH 31/31] OAK-10973 -- remove unused imports --- .../CompressedDocumentPropertyState.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java index 2d25eb23857..216d7009566 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/CompressedDocumentPropertyState.java @@ -16,36 +16,20 @@ */ package org.apache.jackrabbit.oak.plugins.document; -import static java.util.Collections.emptyList; -import static org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; - -import javax.jcr.PropertyType; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.commons.Compression; -import org.apache.jackrabbit.oak.commons.LongUtils; import org.apache.jackrabbit.oak.commons.json.JsopReader; import org.apache.jackrabbit.oak.commons.json.JsopTokenizer; import org.apache.jackrabbit.oak.commons.properties.SystemPropertySupplier; -import org.apache.jackrabbit.oak.json.TypeCodes; import org.apache.jackrabbit.oak.plugins.memory.AbstractPropertyState; -import org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState; -import org.apache.jackrabbit.oak.plugins.memory.BooleanPropertyState; -import org.apache.jackrabbit.oak.plugins.memory.DoublePropertyState; -import org.apache.jackrabbit.oak.plugins.memory.LongPropertyState; -import org.apache.jackrabbit.oak.plugins.memory.PropertyStates; -import org.apache.jackrabbit.oak.plugins.memory.StringPropertyState; -import org.apache.jackrabbit.oak.plugins.value.Conversions; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory;