You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2021/09/22 13:12:54 UTC
[jackrabbit-filevault] branch master updated: JCRVLT-560 support
multi-value binary properties (#167)
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git
The following commit(s) were added to refs/heads/master by this push:
new bcc24ef JCRVLT-560 support multi-value binary properties (#167)
bcc24ef is described below
commit bcc24efa084ebf52e23ae96aeb0937f9b628dc55
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Wed Sep 22 15:12:47 2021 +0200
JCRVLT-560 support multi-value binary properties (#167)
extend documentation on binary properties
extend tests to cover both binary references and regular binaries
separate Oak and JR2 repository folders for ITs
run JR2 ITs with multiple JVM processes
---
src/site/markdown/config.md | 8 +-
src/site/markdown/docview.md | 41 +--
src/site/markdown/properties.md | 2 +-
src/site/markdown/vaultfs.md | 2 +
vault-core/pom.xml | 23 +-
.../jackrabbit/vault/fs/impl/AggregateImpl.java | 13 +-
.../jackrabbit/vault/util/DocViewProperty.java | 154 +++++++----
.../apache/jackrabbit/vault/util/package-info.java | 2 +-
.../packaging/integration/BinaryPropertiesIT.java | 300 +++++++++++++++++++++
.../packaging/integration/BinarylessExportIT.java | 231 ----------------
.../vault/packaging/integration/ImportIT.java | 1 +
.../packaging/integration/IntegrationTestBase.java | 54 ++--
.../jackrabbit/vault/util/DocViewPropertyTest.java | 51 +++-
13 files changed, 551 insertions(+), 331 deletions(-)
diff --git a/src/site/markdown/config.md b/src/site/markdown/config.md
index c3162f2..35fc31d 100644
--- a/src/site/markdown/config.md
+++ b/src/site/markdown/config.md
@@ -22,13 +22,13 @@ Config
The FileVault Filesystem Configuration can be adjusted with a `META-INF/vault/config.xml` file in content packages or in the [Vault CLI](usage.html).
-It allows to tweak aggregation settings of the [Vault FS](vaultfs.html). The default config is at <https://github.com/apache/jackrabbit-filevault/blob/trunk/vault-core/src/main/resources/org/apache/jackrabbit/vault/fs/config/defaultConfig-1.1.xml>.
+It allows to tweak aggregation settings of the [Vault FS](vaultfs.html). The default config is at <https://github.com/apache/jackrabbit-filevault/blob/trunk/vault-core/src/main/resources/org/apache/jackrabbit/vault/fs/config/defaultConfig-1.1.xml>. The alternative default config at <https://github.com/apache/jackrabbit-filevault/blob/trunk/vault-core/src/main/resources/org/apache/jackrabbit/vault/fs/config/defaultConfig-1.1-binaryless.xml> is automatically used once the package property [...]
The following XML elements below the root element `vaultfs` are considered.
## Properties
-To exclude binaries from exports/imports you can use the property
+To exclude binaries from exports you can use the property
```xml
<properties>
@@ -37,6 +37,8 @@ To exclude binaries from exports/imports you can use the property
</properties>
```
+Using this flag leads to binary references being included in the [FileVault DocView XMLs](docview.html).
+
## Aggregates
Aggregates which influence the serialization during *exports* are configured in `aggregate` child elements having an optional `type` attribute and a `title`.
@@ -47,7 +49,6 @@ Type | Used Class | Description
--- | --- | ---
`file` | `FileAggregator` | Serializes `nt:file` or `nt:resource` nodes into simple files, potentially accompanied by a `.dir` folder containing a `.content.xml` with the metadata.
`filefolder` | `FileFolderAggregator` | Serializes all `nt:hierarchyNode` nodes that have or define a `jcr:content` child node and excludes child nodes that are nt:hierarchyNodes into directories.
-`nodetype`| `NodeTypeAggregator`| Serializes `nt:nodeType` nodes into `*.xcnd` files
`full` | `FullCoverageAggregator` | Serializes full node structures (including all children) into a DocView file named `<nodename>.xml`
`generic` | `GenericAggregator` | the default if no `type` is set, serializes the node and its properties into a `.content.xml` DocView file and the binaries in separate files with extension `.binary`.
@@ -73,6 +74,5 @@ Type | Used Class | Description
--- | --- | ---
`file` | `FileArtifactHandler` | Deserializes `nt:file`s from generic or XML generic serializations
`folder` | `FolderArtifactHandler` | Deserializes folders
-`nodetype` | `NodeTypeArtifactHandler` | Deserializes `nt:nodeType` nodes from `*.xcnd` files
`generic` | `GenericArtifactHandler` | Deserializes DocView artifacts, the default if no `type` is set
diff --git a/src/site/markdown/docview.md b/src/site/markdown/docview.md
index 76f29a9..5e9e851 100644
--- a/src/site/markdown/docview.md
+++ b/src/site/markdown/docview.md
@@ -15,7 +15,7 @@
limitations under the License.
-->
-FileVault DocView
+FileVault Document View (DocView) Format
=================
<!-- MACRO{toc} -->
@@ -23,7 +23,7 @@ FileVault DocView
Overview
----------
-FileVault uses a slightly different version of the Document View specified by [JCR 2.0][jcr-docview].
+FileVault uses a slightly different format than the Document View specified by [JCR 2.0][jcr-docview].
In general all nodes that cannot be serialized as plain directories or as plain files are
serialized into DocView XML files. If the node can only be partially mapped to a directory or file,
it will be accompanied with a `.content.xml` containing the residual content.
@@ -34,9 +34,9 @@ an `example.xml` file, using the (FileVault) DocView format.
For example, a _sling:Folder_ node, named `libs` will be serialized into a directory `libs` and a
`libs/.content.xml` file, using the (FileVault) DocView format.
-Also see the [vault-fs](vaultfs.html) article about this.
+Also see the [Vault FS article](vaultfs.html) about this.
-Deviations from the Standard JCR DocView
+Deviations from the JCR Document View
-----------------------------
### Root Element
@@ -58,14 +58,14 @@ As by that the filename implicitly sets the root element name, its namespace is
### Empty Elements
-The deserialization treats empty elements different as the default [JCR 2.0 Import][import-docview], as
-empty elements never create a new node in the repository but are merely used to define the childnode
+The deserialization treats empty elements different than the default [JCR 2.0 DocView Import][import-docview], as
+empty elements never create a new node in the repository but are merely used to define the child node
sort order.
Nodes/properties below empty elements will never be removed during import ([JCRVLT-251](https://issues.apache.org/jira/browse/JCRVLT-251))
### Property Values
-The probably biggest different to the [JCR 2.0][jcr-docview] is the handling of the property values.
+The probably biggest different to the [JCR 2.0 DocView][jcr-docview] is the handling of the property values.
All properties are serialized as XML attributes as in JCR, but their values have the property type
encoded. The format of the attribute value is:
@@ -75,7 +75,9 @@ property-value := [ "{" property-type "}" ] ( value | "[" [ value { "," value }
If no type is specified, it defaults to [STRING][pt-string].
As types all arguments accepted by [PropertyType.valueFromName(String)](https://docs.adobe.com/docs/en/spec/jsr170/javadocs/jcr-2.0/javax/jcr/PropertyType.html#valueFromName(java.lang.String)) are valid.
-This is all strings defined by the constants whose names start with `TYPENAME_` in [PropertyType](https://docs.adobe.com/docs/en/spec/jsr170/javadocs/jcr-2.0/javax/jcr/PropertyType.html).
+This is all strings defined by the constants whose names start with `TYPENAME_` in [PropertyType](https://docs.adobe.com/docs/en/spec/jsr170/javadocs/jcr-2.0/javax/jcr/PropertyType.html) and `BinaryRef` for binary reference values (see below).
+
+Multi-value properties contain the values as comma-separated list enclosed by `[` and `]`. The special value `\0` must be used to for a singleton multi-value property containing only the empty string.
Examples:
@@ -86,6 +88,14 @@ Examples:
| Boolean | true | "{Boolean}true" |
| Double[]| {1.0, 2.5, 3.0} | "{Double}[1.0,2.5,3.0]" |
+#### Binary Properties
+
+In contrast to [JCR 2.0 DocView][jcr-docview] binary properties are not supported inline via base64 encoding. Instead either a [dedicated .binary file must be used](vaultfs.html#Binary_Properties) or a regular file aggregate which sets the `jcr:content/jcr:data` binary property implicitly.
+
+Only for binary values which also implement [`org.apache.jackrabbit.api.ReferenceBinary`][ref-binary] the string identifier of the *binary reference* is optionally given directly inside in a FileVault DocView ([JCR-3534](https://issues.apache.org/jira/browse/JCR-3534)). This only happens in case the [package property](properties.html) `useBinaryReferences` is set to `true`. Package imports use this approach whenever a binary reference property in FileVault DocView XML is found (with `pro [...]
+
+Empty binary attributes (with values `"{Binary}"`) will always leave the according property in the repository untouched during import (in case such a property already exists). Every other value for binary properties leads to an exception.
+
### Escaping
The raw attribute value is escaped in order to preserve the special semantics:
@@ -93,16 +103,17 @@ The raw attribute value is escaped in order to preserve the special semantics:
| Character | Escape Sequence | Comment |
|-----------|-----------------|---------|
| `\` | `\\` | |
-| `,` | `\,` | Only for multi value properties |
-| `[` | `\[` | Only at the start of single value properties |
-| `{` | `\{` | Only at the start of single value properties |
-| invalid xml | `\uXXXX` | |
+| `,` | `\,` | Only necessary for multi-value properties |
+| `[` | `\[` | Only necessary at the start of single-value properties |
+| `{` | `\{` | Only necessary at the start of single-value properties |
+| invalid xml character| `\uXXXX` | Unicode code points |
+| `<empty string>` | `\0` | Only necessary within singleton multi-value properties to indicate an empty string item ([JCRVLT-4](https://issues.apache.org/jira/browse/JCRVLT-4)). |
Please note, that this escaping only concerns the raw attribute value. If the value contains
-characters that cannot be used in XML attributes, like quotes `"`, they will further be escaped
-by the XML formatter.
+characters that cannot be used in XML attributes, like quotes `"`, the according [XML entities](https://www.w3.org/TR/xml/#dt-escape) need to be used.
[jcr-docview]: https://docs.adobe.com/content/docs/en/spec/jcr/2.0/7_Export.html#7.3%20Document%20View
[import-docview]: https://docs.adobe.com/content/docs/en/spec/jcr/2.0/11_Import.html#11.1%20Importing%20Document%20View
-[pt-string]: https://docs.adobe.com/docs/en/spec/jsr170/javadocs/jcr-2.0/javax/jcr/PropertyType.html#STRING
\ No newline at end of file
+[pt-string]: https://docs.adobe.com/docs/en/spec/jsr170/javadocs/jcr-2.0/javax/jcr/PropertyType.html#STRING
+[ref-binary]: https://jackrabbit.apache.org/oak/docs/apidocs/org/apache/jackrabbit/api/ReferenceBinary.html
\ No newline at end of file
diff --git a/src/site/markdown/properties.md b/src/site/markdown/properties.md
index 2200287..2f8ad8b 100644
--- a/src/site/markdown/properties.md
+++ b/src/site/markdown/properties.md
@@ -76,7 +76,7 @@ Example:
| requiresRestart | If set to `true` indicates that the system should be restarted after this package has been installed | no | `false`
| noIntermediateSaves | If set to `true` indicates no intermediate saves should be performed while installing this package | no | `false`
| subPackageHandling | see [SubPackageHandling][api.SubPackageHandling] | no | `*;install`
-| useBinaryReferences | If set to `true` indicates that binary references should be used instead of the actual binary | no | `false`
+| useBinaryReferences | If set to `true` indicates that [binary references](docview.html#Binary_Properties) should be used instead of the regular binaries. This also leads to using another default Vault FS [configuration](configuration.html). | no | `false`
| packageType | Possible values: `application`, `content`, `container` or `mixed`. Further details at [Package Types](packagetypes.html). | no | ?
| installhook.\<name\>.class | The FQN of the class which acts as an [install hook](installhooks.html). The `<name>` can be an arbitrary string (but must not contain a dot). | no | n/a
| packageFormatVersion | The version of this package as integer value. Versions newer than 2 are not yet supported during installation. | no | 2
diff --git a/src/site/markdown/vaultfs.md b/src/site/markdown/vaultfs.md
index b85b642..c655a2b 100644
--- a/src/site/markdown/vaultfs.md
+++ b/src/site/markdown/vaultfs.md
@@ -244,6 +244,8 @@ is mapped to:
`- data2.binary
```
+Multi-value binary properties are mapped to multiple files named `<property name>[<0-based index>].binary`
+
#### Resource Nodes
There are some cases where `nt:resource` like structures are used that are not held below a `nt:file` node.
diff --git a/vault-core/pom.xml b/vault-core/pom.xml
index 426ad90..4ee152b 100644
--- a/vault-core/pom.xml
+++ b/vault-core/pom.xml
@@ -295,6 +295,13 @@
<version>${oak.version}</version>
<scope>test</scope>
</dependency>
+ <!-- transitive dependency of oak-segment-tar, necessary for filedatastore (https://issues.apache.org/jira/browse/OAK-6542) -->
+ <dependency>
+ <groupId>io.dropwizard.metrics</groupId>
+ <artifactId>metrics-core</artifactId>
+ <version>3.2.3</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>oak-authorization-principalbased</artifactId>
@@ -411,11 +418,13 @@
<configuration>
<systemPropertyVariables>
<oak>true</oak>
- <repoIndex>${surefire.forkNumber}</repoIndex>
+ <repoSuffix>fork${surefire.forkNumber}</repoSuffix>
+ <!-- https://issues.apache.org/jira/browse/OAK-8903 -->
+ <oak.datastore.sharedTransient>true</oak.datastore.sharedTransient>
</systemPropertyVariables>
<argLine>@{jacoco.command} -Xmx1024m</argLine>
<reportNameSuffix>OAK</reportNameSuffix>
- <forkCount>4</forkCount>
+ <forkCount>0.5C</forkCount>
<reuseForks>true</reuseForks>
</configuration>
</execution>
@@ -427,12 +436,12 @@
<configuration>
<systemPropertyVariables>
<oak>false</oak>
- <repoIndex>${surefire.forkNumber}</repoIndex>
+ <repoSuffix>fork${surefire.forkNumber}</repoSuffix>
</systemPropertyVariables>
<reportNameSuffix>JR</reportNameSuffix>
<summaryFile>${project.build.directory}/failsafe-reports/failsafe-summary-jr.xml</summaryFile>
- <!-- <forkCount>4</forkCount>
- <reuseForks>true</reuseForks> -->
+ <forkCount>0.5C</forkCount>
+ <reuseForks>true</reuseForks>
</configuration>
</execution>
<execution>
@@ -442,11 +451,15 @@
</goals>
<configuration>
<summaryFiles>
+ <!-- in addition to default summary file used by Oak -->
<summaryFile>${project.build.directory}/failsafe-reports/failsafe-summary-jr.xml</summaryFile>
</summaryFiles>
</configuration>
</execution>
</executions>
+ <configuration>
+ <reportsDirectory>target/failsafe-reports-${surefire.forkNumber}</reportsDirectory>
+ </configuration>
</plugin>
</plugins>
</build>
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/AggregateImpl.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/AggregateImpl.java
index 6d7c4fe..262f8f1 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/AggregateImpl.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/AggregateImpl.java
@@ -552,9 +552,14 @@ public class AggregateImpl implements Aggregate {
if (prop.getType() == PropertyType.BINARY) {
boolean includeBinary = true;
if (useBinaryReferences) {
- Binary bin = prop.getBinary();
- if (bin != null && bin instanceof ReferenceBinary) {
- String binaryReference = ((ReferenceBinary) bin).getReference();
+ final Value firstValue;
+ if (prop.isMultiple()) {
+ firstValue = prop.getValues().length > 0 ? prop.getValues()[0] : null;
+ } else {
+ firstValue = prop.getValue();
+ }
+ if (firstValue != null && firstValue.getBinary() instanceof ReferenceBinary) {
+ String binaryReference = ((ReferenceBinary) firstValue.getBinary()).getReference();
// do not create a separate binary file if there is a reference
if (binaryReference != null) {
@@ -727,7 +732,7 @@ public class AggregateImpl implements Aggregate {
// in some weird cases, the jcr2spi layer reports
// wrong nodes. in this case, just remove it again
// as leave
- log.warn("Alleged node is gone: {}", path);
+ log.warn("Alleged node is gone: {}", path, e);
sub.invalidate();
sub = null;
}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java
index 23e520d..0c9b065 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java
@@ -25,21 +25,26 @@ import java.util.List;
import java.util.Set;
import javax.jcr.Binary;
+import javax.jcr.InvalidSerializedDataException;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ValueFormatException;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.version.VersionException;
import org.apache.jackrabbit.api.ReferenceBinary;
import org.apache.jackrabbit.commons.jackrabbit.SimpleReferenceBinary;
import org.apache.jackrabbit.util.Text;
import org.apache.jackrabbit.util.XMLChar;
import org.apache.jackrabbit.value.ValueHelper;
+import org.jetbrains.annotations.NotNull;
/**
- * Helper class that represents a (jcr) property in the document view format.
+ * Helper class that represents a JCR property in the FileVault (enhanced) document view format.
* It contains formatting and parsing methods for writing/reading enhanced
* docview properties.
* <br>
@@ -68,7 +73,7 @@ public class DocViewProperty {
public final String[] values;
/**
- * indicates a MV property
+ * indicates a multi-value property
*/
public final boolean isMulti;
@@ -92,21 +97,9 @@ public class DocViewProperty {
}
/**
- * Creates a new property.
- * @param name name of the property
- * @param values values.
- * @param multi multiple flag
- * @param type type of the property
- * @throws IllegalArgumentException if single value property and not exactly 1 value is given.
- */
- public DocViewProperty(String name, String[] values, boolean multi, int type) {
- this(name, values, multi, type, false);
- }
-
- /**
- * Creates a new property based on an array of Values
- * @param name
- * @param values the values (always an array, may be empty)
+ * Creates a new property based on an array of {@link Value}s
+ * @param name the name of the property
+ * @param values the values (always an array, may be empty), must not contain {@code null} items
* @param type the type of the property
* @param isMulti {@code true} in case this is a multivalue property
* @param sort {@code true} in case the value array should be sorted first
@@ -114,7 +107,7 @@ public class DocViewProperty {
* @return the new property
* @throws RepositoryException
*/
- public static DocViewProperty fromValues(String name, Value[] values, int type, boolean isMulti, boolean sort, boolean useBinaryReferences) throws RepositoryException {
+ public static DocViewProperty fromValues(@NotNull String name, @NotNull Value[] values, int type, boolean isMulti, boolean sort, boolean useBinaryReferences) throws RepositoryException {
List<String> strValues = new ArrayList<>();
if (isMulti) {
if (sort) {
@@ -122,26 +115,39 @@ public class DocViewProperty {
}
}
for (Value value : values) {
- strValues.add(serializeValue(value, useBinaryReferences));
+ strValues.add(serializeValue(value, useBinaryReferences));
}
-
- boolean isBinaryRef = false;
- if (type == PropertyType.BINARY && !strValues.get(0).isEmpty()) {
- isBinaryRef = true;
+
+ Boolean isBinaryRef = null;
+ if (type == PropertyType.BINARY) {
+ // either only binary references or regular binaries
+ for (String strValue : strValues) {
+ boolean isCurrentValueBinaryRef = !strValue.isEmpty();
+ if (isBinaryRef == null) {
+ isBinaryRef = isCurrentValueBinaryRef;
+ } else {
+ if (isBinaryRef != isCurrentValueBinaryRef) {
+ throw new ValueFormatException("Mixed binary references and regular binary values in the same multi-value property is not supported");
+ }
+ }
+ }
+ }
+ if (isBinaryRef == null) {
+ isBinaryRef = false;
}
return new DocViewProperty(name, strValues.toArray(new String[0]), isMulti, type, isBinaryRef);
}
/**
- * Creates a new property based on a JCR property object
- * @param prop the jcr property
- * @param sort if {@code true} multivalue properties should be sorted
+ * Creates a new property based on a JCR {@link Property} object
+ * @param prop the JCR property
+ * @param sort if {@code true} multi-value properties should be sorted
* @param useBinaryReferences {@code true} to use binary references
* @return the new property
- * @throws RepositoryException
* @throws IllegalArgumentException if single value property and not exactly 1 value is given.
+ * @throws RepositoryException if another error occurs
*/
- public static DocViewProperty fromProperty(Property prop, boolean sort, boolean useBinaryReferences) throws RepositoryException {
+ public static DocViewProperty fromProperty(@NotNull Property prop, boolean sort, boolean useBinaryReferences) throws RepositoryException {
boolean isMultiValue = prop.getDefinition().isMultiple();
final Value[] values;
if (isMultiValue) {
@@ -163,7 +169,7 @@ public class DocViewProperty {
}
}
if (strValue == null) {
- // leave value empty for other binaries
+ // leave value empty for non reference binaries or where reference is null
strValue = "";
}
} else {
@@ -178,7 +184,19 @@ public class DocViewProperty {
* @param values values.
* @param multi multiple flag
* @param type type of the property
- * @param isRef {@code true} to indicated that this is a binary reference property
+ * @throws IllegalArgumentException if single value property and not exactly 1 value is given.
+ */
+ public DocViewProperty(String name, String[] values, boolean multi, int type) {
+ this(name, values, multi, type, false);
+ }
+
+ /**
+ * Creates a new property.
+ * @param name name of the property
+ * @param values string representation of values.
+ * @param multi indicates if this is a multi-value property
+ * @param type type of the property
+ * @param isRef {@code true} to indicate that this is a binary reference property
* @throws IllegalArgumentException if single value property and not exactly 1 value is given.
*/
public DocViewProperty(String name, String[] values, boolean multi, int type, boolean isRef) {
@@ -322,8 +340,8 @@ public class DocViewProperty {
return new DocViewProperty(name, values, isMulti, type, isBinaryRef);
}
/**
- * Formats the given jcr property value to the enhanced docview syntax.
- * @param prop the jcr property
+ * Formats (serializes) the given JCR property value according to the enhanced docview syntax.
+ * @param prop the JCR property
* @return the formatted string of the property value
* @throws RepositoryException if a repository error occurs
*/
@@ -332,9 +350,9 @@ public class DocViewProperty {
}
/**
- * Formats the given jcr property value to the enhanced docview syntax.
- * @param prop the jcr property
- * @param sort if {@code true} multivalue properties are sorted
+ * Formats (serializes) the given JCR property value to the enhanced docview syntax.
+ * @param prop the JCR property
+ * @param sort if {@code true} multi-value properties are sorted
* @param useBinaryReferences {@code true} to use binary references
* @return the formatted string of the property value
* @throws RepositoryException if a repository error occurs
@@ -480,8 +498,10 @@ public class DocViewProperty {
}
}
if (isMulti) {
- // todo: handle multivalue binaries and reference binaries
Value[] vs = prop == null ? null : prop.getValues();
+ if (type == PropertyType.BINARY) {
+ return applyBinary(node, vs);
+ }
if (vs != null && vs.length == values.length) {
// quick check all values
boolean modified = false;
@@ -504,19 +524,7 @@ public class DocViewProperty {
} else {
Value v = prop == null ? null : prop.getValue();
if (type == PropertyType.BINARY) {
- if (isReferenceProperty) {
- ReferenceBinary ref = new SimpleReferenceBinary(values[0]);
- Binary binary = node.getSession().getValueFactory().createValue(ref).getBinary();
- if (v != null) {
- Binary bin = v.getBinary();
- if (bin.equals(binary)) {
- return false;
- }
- }
- node.setProperty(name, binary);
- }
- // the binary property is always modified (TODO: check if still correct with JCRVLT-110)
- return true;
+ return applyBinary(node, v);
}
if (v == null || !v.getString().equals(values[0])) {
try {
@@ -535,6 +543,52 @@ public class DocViewProperty {
return false;
}
+ private boolean applyBinary(Node node, Value... existingValues) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ List<Value> binaryValues = new ArrayList<>(values.length);
+ if (!isReferenceProperty) {
+ for (String value : values) {
+ // empty string is used for binary properties which should not be touched!
+ if (!value.isEmpty()) {
+ throw new InvalidSerializedDataException("Inline binaries are only supported as binary references, but is " + value);
+ }
+ }
+ // just silently ignore binaries with only empty string values
+ return false;
+ }
+ try {
+ boolean modified = false;
+ for (int n=0; n < values.length; n++) {
+ String value = values[n];
+ ReferenceBinary ref = new SimpleReferenceBinary(value);
+ Value binaryValue = node.getSession().getValueFactory().createValue(ref);
+ binaryValues.add(binaryValue);
+ // compare with existing value
+ if (modified == false && existingValues != null && n < existingValues.length && existingValues[n] != null) {
+ Binary existingBinary = existingValues[0].getBinary();
+ if (!existingBinary.equals(binaryValue.getBinary())) {
+ modified = true;
+ }
+ } else {
+ modified = true;
+ }
+ }
+ if (!modified) {
+ return false;
+ }
+ if (isMulti) {
+ node.setProperty(name, binaryValues.toArray(new Value[0]));
+ } else {
+ node.setProperty(name, binaryValues.get(0));
+ }
+ // the binary property is always modified (TODO: check if still correct with JCRVLT-110)
+ return true;
+ } finally {
+ for (Value value : binaryValues) {
+ value.getBinary().dispose();
+ }
+ }
+ }
+
@Override
public int hashCode() {
final int prime = 31;
@@ -578,7 +632,7 @@ public class DocViewProperty {
*/
@Override
public String toString() {
- return "DocViewProperty [name=" + name + ", values=" + Arrays.toString(values) + ", isMulti=" + isMulti + ", type=" + type
+ return "DocViewProperty [name=" + name + ", values=" + Arrays.toString(values) + ", isMulti=" + isMulti + ", type=" + PropertyType.nameFromValue(type)
+ ", isReferenceProperty=" + isReferenceProperty + "]";
}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/package-info.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/package-info.java
index 8d018d6..f7e8327 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/package-info.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/package-info.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-@Version("2.8.0")
+@Version("2.8.1")
package org.apache.jackrabbit.vault.util;
import org.osgi.annotation.versioning.Version;
\ No newline at end of file
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/BinaryPropertiesIT.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/BinaryPropertiesIT.java
new file mode 100644
index 0000000..3559bd6
--- /dev/null
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/BinaryPropertiesIT.java
@@ -0,0 +1,300 @@
+/*
+ * 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.vault.packaging.integration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.ValueFormatException;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.version.VersionException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.api.ReferenceBinary;
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
+import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
+import org.apache.jackrabbit.vault.fs.config.DefaultMetaInf;
+import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.io.ImportOptions;
+import org.apache.jackrabbit.vault.packaging.ExportOptions;
+import org.apache.jackrabbit.vault.packaging.PackageException;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class BinaryPropertiesIT extends IntegrationTestBase {
+
+ private final static String SMALL_TEXT = "Lorem ipsum"; // less than 16KB data is not stored in the blob store
+ private final static String SMALL_TEXT2 = "The quick brown fox";
+ private final static String BINARY_NODE_PATH = "/tmp/binaryless/node";
+ private final static String BIG_BINARY_PROPERTY = "bigbin";
+ private final static String SMALL_BINARY_PROPERTY = "smallbin";
+ private final static String BIG_BINARY_MV_PROPERTY = "bigbin-mv";
+ private final static String SMALL_BINARY_MV_PROPERTY = "smallbin-mv";
+
+ private final static String FILE_NODE_PATH = "/tmp/binaryless/file";
+
+ private final static int BIG_TEXT_LENGTH = 0x1000 * 64; // 64 KB
+ private final static String BIG_TEXT;
+ private final static String BIG_TEXT2;
+ static {
+ StringBuilder buffer = new StringBuilder(BIG_TEXT_LENGTH);
+ buffer.append("0123456789abcdef");
+ while (buffer.length() < BIG_TEXT_LENGTH) {
+ buffer.append(buffer, 0, buffer.length());
+ }
+ BIG_TEXT = buffer.toString();
+ BIG_TEXT2 = buffer.append("2").toString();
+ }
+
+ @Rule
+ public TemporaryFolder tmpFolder = new TemporaryFolder();
+
+ private Node binaryNode;
+
+ @Parameter
+ public boolean useBinaryReferences; // if true, binaries should not be part of the package (only their references)
+
+ @Parameters(name = "useBinaryReferences:{0}")
+ public static Object[] data() {
+ return new Object[] { Boolean.TRUE, Boolean.FALSE };
+ }
+
+ @BeforeClass
+ public static void initRepository() throws RepositoryException, IOException, InvalidFileStoreVersionException {
+ initRepository(isOak(), true); // always use BlobStore with Oak
+ }
+
+ @Before
+ public void setup() throws RepositoryException, PackageException, IOException {
+ // test only works for Jackrabbit 2.0 or Oak with FileDataStore (as it relies on ReferenceBinary), this is assured though by the overwritten initRepository method
+
+ binaryNode = JcrUtils.getOrCreateByPath(BINARY_NODE_PATH, "nt:unstructured", admin);
+ Property bigProperty = setBinaryProperty(binaryNode, BIG_BINARY_PROPERTY, BIG_TEXT);
+ Property smallProperty = setBinaryProperty(binaryNode, SMALL_BINARY_PROPERTY, SMALL_TEXT);
+ setBinaryMultivalueProperty(binaryNode, BIG_BINARY_MV_PROPERTY, BIG_TEXT, BIG_TEXT2);
+ setBinaryMultivalueProperty(binaryNode, SMALL_BINARY_MV_PROPERTY, SMALL_TEXT, SMALL_TEXT2);
+
+ // some basic checks to make sure that reference binaries are enabled in the repository
+ String referenceBigBinary = ((ReferenceBinary) bigProperty.getBinary()).getReference();
+ assertNotNull(referenceBigBinary);
+ if (isOak()) {
+ // every binary is ReferenceBinary (but does return null for small binaries via ReferenceBinary.getReference())
+ assertTrue(smallProperty.getBinary() instanceof ReferenceBinary);
+ } else {
+ // only large binaries are ReferenceBinary objects in JR2
+ assertFalse(smallProperty.getBinary() instanceof ReferenceBinary);
+ }
+ JcrUtils.putFile(binaryNode.getParent(), "file", "text/plain", IOUtils.toInputStream(BIG_TEXT, StandardCharsets.UTF_8));
+ admin.save();
+ }
+
+ private Property setBinaryProperty(Node node, String name, String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ Binary binary = admin.getValueFactory().createBinary(IOUtils.toInputStream(value, StandardCharsets.UTF_8));
+ try {
+ return node.setProperty(name, binary);
+ } finally {
+ binary.dispose();
+ }
+ }
+
+ private Property setBinaryMultivalueProperty(Node node, String name, String... values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ ValueFactory valueFactory = admin.getValueFactory();
+ List<Binary> binaries = new ArrayList<>(values.length);
+ for (String value : values) {
+ binaries.add(valueFactory.createBinary(IOUtils.toInputStream(value, StandardCharsets.UTF_8)));
+ }
+ try {
+ return node.setProperty(name, binaries.stream().map(b -> valueFactory.createValue(b)).toArray(Value[]::new), PropertyType.BINARY);
+ } finally {
+ binaries.stream().forEach(Binary::dispose);
+ }
+ }
+
+ private void assertBinaryProperty(String path, String input) throws RepositoryException, IOException {
+ assertBinaryProperty(path, -1, input);
+ }
+
+ private void assertBinaryProperty(String path, int index, String expectedInput) throws RepositoryException, IOException {
+ try (InputStream input = IOUtils.toInputStream(expectedInput, StandardCharsets.UTF_8)) {
+ assertBinaryProperty(path, index, input);
+ }
+ }
+
+ private void assertBinaryProperty(String path, int index, InputStream expectedInput) throws RepositoryException, IOException {
+ Property property = admin.getProperty(path);
+ final Value value;
+ if (index == -1) {
+ value = property.getValue();
+ } else {
+ Value[] values = property.getValues();
+ if (values.length <= index) {
+ Assert.fail("Property " + path + " does only contain " + values.length + " items");
+ }
+ value = values[index];
+ }
+ Binary binary = value.getBinary();
+ try (InputStream actualInput = binary.getStream()) {
+ assertTrue("Content not equal", IOUtils.contentEquals(expectedInput, actualInput));
+ }
+ finally {
+ binary.dispose();
+ }
+ }
+
+ private VaultPackage assemblePackage(String rootNodePath) throws IOException, RepositoryException {
+ ExportOptions opts = new ExportOptions();
+ DefaultMetaInf inf = new DefaultMetaInf();
+ DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
+ filter.add(new PathFilterSet(rootNodePath));
+
+ inf.setFilter(filter);
+ Properties props = new Properties();
+ props.setProperty(VaultPackage.NAME_GROUP, "jackrabbit/test");
+ props.setProperty(VaultPackage.NAME_NAME, "test-package");
+ props.setProperty(PackageProperties.NAME_USE_BINARY_REFERENCES, Boolean.toString(useBinaryReferences));
+ inf.setProperties(props);
+ opts.setMetaInf(inf);
+ File tmpFile = tmpFolder.newFile("vaulttest.zip");
+ return packMgr.assemble(admin, opts, tmpFile);
+ }
+
+ @Test
+ public void exportBinary() throws RepositoryException, IOException, PackageException {
+ String nodePath = BINARY_NODE_PATH;
+ try (VaultPackage pkg = assemblePackage(nodePath)) {
+ if (useBinaryReferences) {
+ // make sure that only non-reference binaries are in dedicated artifacts
+ assertNull(pkg.getArchive().getEntry("jcr_root" + BINARY_NODE_PATH + "/" + BIG_BINARY_PROPERTY + ".binary"));
+ assertNull(pkg.getArchive().getEntry("jcr_root" + BINARY_NODE_PATH + "/" + BIG_BINARY_MV_PROPERTY + "[0].binary"));
+ assertNull(pkg.getArchive().getEntry("jcr_root" + BINARY_NODE_PATH + "/" + BIG_BINARY_MV_PROPERTY + "[1].binary"));
+ assertNotNull(pkg.getArchive().getEntry("jcr_root"+ BINARY_NODE_PATH + "/" + SMALL_BINARY_PROPERTY + ".binary"));
+ assertNotNull(pkg.getArchive().getEntry("jcr_root"+ BINARY_NODE_PATH + "/" + SMALL_BINARY_MV_PROPERTY + "[0].binary"));
+ assertNotNull(pkg.getArchive().getEntry("jcr_root"+ BINARY_NODE_PATH + "/" + SMALL_BINARY_MV_PROPERTY + "[1].binary"));
+ } else {
+ assertNotNull(pkg.getArchive().getEntry("jcr_root" + BINARY_NODE_PATH + "/" + BIG_BINARY_PROPERTY + ".binary"));
+ assertNotNull(pkg.getArchive().getEntry("jcr_root" + BINARY_NODE_PATH + "/" + BIG_BINARY_MV_PROPERTY + "[0].binary"));
+ assertNotNull(pkg.getArchive().getEntry("jcr_root" + BINARY_NODE_PATH + "/" + BIG_BINARY_MV_PROPERTY + "[1].binary"));
+ assertNotNull(pkg.getArchive().getEntry("jcr_root"+ BINARY_NODE_PATH + "/" + SMALL_BINARY_PROPERTY + ".binary"));
+ assertNotNull(pkg.getArchive().getEntry("jcr_root"+ BINARY_NODE_PATH + "/" + SMALL_BINARY_MV_PROPERTY + "[0].binary"));
+ assertNotNull(pkg.getArchive().getEntry("jcr_root"+ BINARY_NODE_PATH + "/" + SMALL_BINARY_MV_PROPERTY + "[1].binary"));
+ }
+ clean(nodePath);
+ pkg.extract(admin, getDefaultOptions());
+ }
+ assertNodeExists(nodePath);
+ assertBinaryProperty(nodePath + "/" + BIG_BINARY_PROPERTY, BIG_TEXT);
+ assertBinaryProperty(nodePath + "/" + BIG_BINARY_MV_PROPERTY, 0, BIG_TEXT);
+ assertBinaryProperty(nodePath + "/" + BIG_BINARY_MV_PROPERTY, 1, BIG_TEXT2);
+ assertBinaryProperty(nodePath + "/" + SMALL_BINARY_PROPERTY, SMALL_TEXT);
+ assertBinaryProperty(nodePath + "/" + SMALL_BINARY_MV_PROPERTY, 0, SMALL_TEXT);
+ assertBinaryProperty(nodePath + "/" + SMALL_BINARY_MV_PROPERTY, 1, SMALL_TEXT2);
+ }
+
+ @Test
+ public void exportMultiValueBinaryWithPotentiallyMixedReferences() throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException, IOException, PackageException {
+ setBinaryMultivalueProperty(binaryNode, "mixedMVBinary", SMALL_TEXT, BIG_TEXT);
+ if (useBinaryReferences) {
+ // short text is a non-reference binary but big text is a reference binary
+ Assert.assertThrows(ValueFormatException.class, () -> assemblePackage(binaryNode.getPath()));
+ } else {
+ String nodePath = binaryNode.getPath();
+ try (VaultPackage pkg = assemblePackage(nodePath)) {
+ clean(binaryNode.getPath());
+ pkg.extract(admin, getDefaultOptions());
+ }
+ assertNodeExists(nodePath);
+ assertBinaryProperty(nodePath + "/mixedMVBinary", 0, SMALL_TEXT);
+ assertBinaryProperty(nodePath + "/mixedMVBinary", 1, BIG_TEXT);
+ }
+ }
+
+ @Test
+ public void exportFile() throws RepositoryException, IOException, PackageException {
+ String nodePath = FILE_NODE_PATH;
+ try (VaultPackage pkg = assemblePackage(nodePath)) {
+ if (useBinaryReferences) {
+ assertTrue(pkg.getArchive().getEntry("jcr_root" + FILE_NODE_PATH).isDirectory());
+ } else {
+ assertFalse(pkg.getArchive().getEntry("jcr_root" + FILE_NODE_PATH).isDirectory());
+ }
+ clean(nodePath);
+ pkg.extract(admin, getDefaultOptions());
+ }
+ assertNodeExists(nodePath);
+ Node node = admin.getNode(nodePath);
+ try (InputStream stream = JcrUtils.readFile(node)) {
+ String actualText = IOUtils.toString(stream, "UTF-8");
+ assertEquals(BIG_TEXT, actualText);
+ }
+ }
+
+ /**
+ * Tests if the same package installed twice does not report and update. See JCRVLT-108
+ */
+ @Test
+ public void importTwice() throws RepositoryException, IOException, PackageException {
+ String nodePath = BINARY_NODE_PATH;
+
+ try (VaultPackage pkg = assemblePackage(nodePath)) {
+ clean(nodePath);
+ ImportOptions io = getDefaultOptions();
+ TrackingListener listener = new TrackingListener(null);
+ io.setListener(listener);
+ // extract
+ pkg.extract(admin, io);
+ assertEquals("A", listener.getActions().get(BINARY_NODE_PATH));
+ // and extract again
+ listener = new TrackingListener(null);
+ io.setListener(listener);
+ pkg.extract(admin, io);
+ assertEquals("U", listener.getActions().get(BINARY_NODE_PATH));
+ }
+ }
+}
\ No newline at end of file
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/BinarylessExportIT.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/BinarylessExportIT.java
deleted file mode 100644
index a70cfb0..0000000
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/BinarylessExportIT.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * 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.vault.packaging.integration;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.jackrabbit.api.ReferenceBinary;
-import org.apache.jackrabbit.commons.JcrUtils;
-import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
-import org.apache.jackrabbit.vault.fs.config.DefaultMetaInf;
-import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
-import org.apache.jackrabbit.vault.fs.io.ImportOptions;
-import org.apache.jackrabbit.vault.packaging.ExportOptions;
-import org.apache.jackrabbit.vault.packaging.JcrPackage;
-import org.apache.jackrabbit.vault.packaging.PackageException;
-import org.apache.jackrabbit.vault.packaging.PackageProperties;
-import org.apache.jackrabbit.vault.packaging.VaultPackage;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Properties;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-public class BinarylessExportIT extends IntegrationTestBase {
-
- private final static String SMALL_TEXT = "Lorem ipsum";
- private final static String BINARY_NODE_PATH = "/tmp/binaryless/node";
- private final static String BIG_BINARY_PROPERTY = "bigbin";
- private final static String SMALL_BINARY_PROPERTY = "smallbin";
-
- private final static String FILE_NODE_PATH = "/tmp/binaryless/file";
-
-
- private final static int BIG_TEXT_LENGTH = 0x1000 * 64;
- private final static String BIG_TEXT;
- static {
- StringBuilder buffer = new StringBuilder(BIG_TEXT_LENGTH);
- buffer.append("0123456789abcdef");
- while (buffer.length() < BIG_TEXT_LENGTH) {
- buffer.append(buffer, 0, buffer.length());
- }
- BIG_TEXT = buffer.toString();
- }
-
- @Before
- @Ignore
- public void setup() throws RepositoryException, PackageException, IOException {
- // test only works for Jackrabbit 2.0 or Oak with FileDataStore
- Assume.assumeTrue(!isOak() || useFileStore());
-
- Node binaryNode = JcrUtils.getOrCreateByPath(BINARY_NODE_PATH, "nt:unstructured", admin);
-
- Binary bigBin = admin.getValueFactory().createBinary(IOUtils.toInputStream(BIG_TEXT, "UTF-8"));
- Property bigProperty = binaryNode.setProperty(BIG_BINARY_PROPERTY, bigBin);
- String referenceBigBinary = ((ReferenceBinary) bigProperty.getBinary()).getReference();
- assertNotNull(referenceBigBinary);
-
- Binary smallBin = admin.getValueFactory().createBinary(IOUtils.toInputStream(SMALL_TEXT, "UTF-8"));
- Property smallProperty = binaryNode.setProperty(SMALL_BINARY_PROPERTY, smallBin);
- if (isOak()) {
- assertTrue(smallProperty.getBinary() instanceof ReferenceBinary);
- } else {
- assertFalse(smallProperty.getBinary() instanceof ReferenceBinary);
- }
-
- JcrUtils.putFile(binaryNode.getParent(), "file", "text/plain", IOUtils.toInputStream(BIG_TEXT, "UTF-8"));
- admin.save();
- }
-
-
-
- @Test
- public void exportBinary() throws RepositoryException, IOException, PackageException {
-
- String nodePath = BINARY_NODE_PATH;
- String property = BIG_BINARY_PROPERTY;
-
- ExportOptions opts = new ExportOptions();
- DefaultMetaInf inf = new DefaultMetaInf();
- DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
- filter.add(new PathFilterSet(nodePath));
-
- inf.setFilter(filter);
- Properties props = new Properties();
- props.setProperty(VaultPackage.NAME_GROUP, "jackrabbit/test");
- props.setProperty(VaultPackage.NAME_NAME, "test-package");
- props.setProperty(PackageProperties.NAME_USE_BINARY_REFERENCES, "true");
- inf.setProperties(props);
-
- opts.setMetaInf(inf);
-
- File tmpFile = File.createTempFile("vaulttest", "zip");
- VaultPackage pkg = packMgr.assemble(admin, opts, tmpFile);
-
- assertNull(pkg.getArchive().getEntry("jcr_root" + BINARY_NODE_PATH + "/" + BIG_BINARY_PROPERTY + ".binary"));
- assertNotNull(pkg.getArchive().getEntry("jcr_root"+ BINARY_NODE_PATH + "/" + SMALL_BINARY_PROPERTY + ".binary"));
-
-
- clean(nodePath);
-
- pkg.extract(admin, getDefaultOptions());
-
- assertNodeExists(nodePath);
-
- long actualBinarySize = ((Property) admin.getItem(nodePath + "/" + property)).getBinary().getSize();
-
- assertEquals(BIG_TEXT.getBytes("UTF-8").length, actualBinarySize);
-
- pkg.close();
- tmpFile.delete();
- }
-
-
- @Test
- public void exportFile() throws RepositoryException, IOException, PackageException {
-
- String nodePath = FILE_NODE_PATH;
-
- ExportOptions opts = new ExportOptions();
- DefaultMetaInf inf = new DefaultMetaInf();
- DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
- filter.add(new PathFilterSet(nodePath));
-
- inf.setFilter(filter);
- Properties props = new Properties();
- props.setProperty(VaultPackage.NAME_GROUP, "jackrabbit/test");
- props.setProperty(VaultPackage.NAME_NAME, "test-package");
- props.setProperty(PackageProperties.NAME_USE_BINARY_REFERENCES, "true");
- inf.setProperties(props);
-
- opts.setMetaInf(inf);
-
- File tmpFile = File.createTempFile("vaulttest", "zip");
- VaultPackage pkg = packMgr.assemble(admin, opts, tmpFile);
-
- assertTrue(pkg.getArchive().getEntry("jcr_root" + FILE_NODE_PATH).isDirectory());
-
- clean(nodePath);
-
- pkg.extract(admin, getDefaultOptions());
-
- assertNodeExists(nodePath);
-
- Node node = admin.getNode(nodePath);
-
- InputStream stream = JcrUtils.readFile(node);
-
- String actualText = IOUtils.toString(stream, "UTF-8");
- assertEquals(BIG_TEXT, actualText);
-
- pkg.close();
- tmpFile.delete();
- }
-
- /**
- * Tests if the same package installed twice does not report and update. See JCRVLT-108
- */
- @Test
- public void importTwice() throws RepositoryException, IOException, PackageException {
- String nodePath = BINARY_NODE_PATH;
-
- ExportOptions opts = new ExportOptions();
- DefaultMetaInf inf = new DefaultMetaInf();
- DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
- filter.add(new PathFilterSet(nodePath));
-
- inf.setFilter(filter);
- Properties props = new Properties();
- props.setProperty(VaultPackage.NAME_GROUP, "jackrabbit/test");
- props.setProperty(VaultPackage.NAME_NAME, "test-package");
- props.setProperty(PackageProperties.NAME_USE_BINARY_REFERENCES, "true");
- inf.setProperties(props);
-
- opts.setMetaInf(inf);
-
- File tmpFile = File.createTempFile("vaulttest", "zip");
- VaultPackage pkg = packMgr.assemble(admin, opts, tmpFile);
- pkg.close();
-
- clean(nodePath);
-
- // import again
- JcrPackage pack = packMgr.upload(FileUtils.openInputStream(tmpFile), false);
- assertNotNull(pack);
-
- ImportOptions io = getDefaultOptions();
- TrackingListener listener = new TrackingListener(opts.getListener());
- io.setListener(listener);
- pack.install(io);
- assertEquals("A", listener.getActions().get(BINARY_NODE_PATH));
-
- // and again
- io = getDefaultOptions();
- listener = new TrackingListener(opts.getListener());
- io.setListener(listener);
-
- pack.install(io);
- assertEquals("U", listener.getActions().get(BINARY_NODE_PATH));
-
- tmpFile.delete();
- }
-}
\ No newline at end of file
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/ImportIT.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/ImportIT.java
index 42ffe08..285c418 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/ImportIT.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/ImportIT.java
@@ -381,4 +381,5 @@ public class ImportIT extends IntegrationTestBase {
assertEquals(ConstraintViolationException.class, ExceptionUtils.getRootCause(e).getClass());
}
}
+
}
\ No newline at end of file
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/IntegrationTestBase.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/IntegrationTestBase.java
index a6c1329..8e522bc 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/IntegrationTestBase.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/IntegrationTestBase.java
@@ -128,10 +128,12 @@ public class IntegrationTestBase {
*/
private static final Logger log = LoggerFactory.getLogger(IntegrationTestBase.class);
- private static final String REPO_HOME = "target/repository";
- private static final File DIR_REPO_HOME = new File(REPO_HOME + System.getProperty("repoIndex", "0"));
- private static final File DIR_DATA_STORE = new File(DIR_REPO_HOME, "datastore");
- private static final File DIR_BLOB_STORE = new File(DIR_REPO_HOME, "blobstore");
+
+ private static final File DIR_JR2_REPO_HOME = new File("target", "repository-jr2-" + System.getProperty("repoSuffix", "fork1"));
+ private static final File DIR_OAK_REPO_HOME = new File("target", "repository-oak-" + System.getProperty("repoSuffix", "fork1"));
+
+ private static final File DIR_OAK_FILE_STORE = new File(DIR_OAK_REPO_HOME, "filestore");
+ private static final File DIR_OAK_BLOB_STORE = new File(DIR_OAK_FILE_STORE, "blobstore");
public static final PackageId TMP_PACKAGE_ID = new PackageId("my_packages", "tmp", "");
@@ -178,17 +180,30 @@ public class IntegrationTestBase {
@BeforeClass
public static void initRepository() throws RepositoryException, IOException, InvalidFileStoreVersionException {
- if (isOak()) {
+ initRepository(isOak(), useFileStore());
+ }
+
+ /**
+ *
+ * @param isOak {@code true} in case IT should run against Oak, otherwise runs again JR2
+ * @param useFileStore only evaluated for Oak. Optionally uses a dedicated BlobStore with Oak
+ * @throws RepositoryException
+ * @throws IOException
+ * @throws InvalidFileStoreVersionException
+ */
+ public static void initRepository(boolean isOak, boolean useFileStore) throws RepositoryException, IOException, InvalidFileStoreVersionException {
+ if (isOak) {
Jcr jcr;
- if (useFileStore()) {
+ if (useFileStore) {
BlobStore blobStore = createBlobStore();
- DIR_DATA_STORE.mkdirs();
- fileStore = FileStoreBuilder.fileStoreBuilder(DIR_DATA_STORE)
+ DIR_OAK_FILE_STORE.mkdirs();
+ fileStore = FileStoreBuilder.fileStoreBuilder(DIR_OAK_FILE_STORE)
.withBlobStore(blobStore)
.build();
SegmentNodeStore nodeStore = SegmentNodeStoreBuilders.builder(fileStore).build();
jcr = new Jcr(nodeStore);
} else {
+ // in-memory repo
jcr = new Jcr();
}
@@ -204,7 +219,7 @@ public class IntegrationTestBase {
admin.logout();
} else {
try (InputStream in = IntegrationTestBase.class.getResourceAsStream("repository.xml")) {
- RepositoryConfig cfg = RepositoryConfig.create(in, DIR_REPO_HOME.getPath());
+ RepositoryConfig cfg = RepositoryConfig.create(in, DIR_JR2_REPO_HOME.getPath());
repository = RepositoryImpl.create(cfg);
}
}
@@ -240,14 +255,16 @@ public class IntegrationTestBase {
}
public static boolean useFileStore() {
- return Boolean.getBoolean("fds");
+ // don't use by default because it is slower and pollutes the log
+ return Boolean.parseBoolean(System.getProperty("fds", "false"));
}
private static BlobStore createBlobStore() throws IOException {
- DIR_BLOB_STORE.mkdirs();
+ DIR_OAK_BLOB_STORE.mkdirs();
FileDataStore fds = new FileDataStore();
fds.setMinRecordLength(4092);
- fds.init(DIR_BLOB_STORE.getAbsolutePath());
+ fds.setPath(DIR_OAK_BLOB_STORE.getAbsolutePath());
+ fds.init(DIR_OAK_REPO_HOME.getAbsolutePath());
return new DataStoreBlobStore(fds);
}
@@ -255,17 +272,16 @@ public class IntegrationTestBase {
public static void shutdownRepository() throws IOException {
if (repository instanceof RepositoryImpl) {
((RepositoryImpl) repository).shutdown();
+ FileUtils.deleteDirectory(DIR_JR2_REPO_HOME);
} else if (repository instanceof org.apache.jackrabbit.oak.jcr.repository.RepositoryImpl) {
((org.apache.jackrabbit.oak.jcr.repository.RepositoryImpl) repository).shutdown();
+ if (fileStore != null) {
+ fileStore.close();
+ fileStore = null;
+ }
+ FileUtils.deleteDirectory(DIR_OAK_REPO_HOME);
}
repository = null;
-
- if (fileStore != null) {
- fileStore.close();
- fileStore = null;
- }
-
- FileUtils.deleteDirectory(DIR_REPO_HOME);
}
@Before
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/util/DocViewPropertyTest.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/util/DocViewPropertyTest.java
index d20b447..bf708ad 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/util/DocViewPropertyTest.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/util/DocViewPropertyTest.java
@@ -17,11 +17,21 @@
package org.apache.jackrabbit.vault.util;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import javax.jcr.Binary;
import javax.jcr.Property;
import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
import javax.jcr.Value;
+import javax.jcr.ValueFactory;
import javax.jcr.nodetype.PropertyDefinition;
+import org.apache.jackrabbit.commons.jackrabbit.SimpleReferenceBinary;
+import org.apache.jackrabbit.value.ValueFactoryImpl;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
@@ -203,12 +213,51 @@ public class DocViewPropertyTest {
assertEscaped("hello\\world", "hello\\\\world", true);
}
+ @Test
+ public void testFromValues() throws RepositoryException, IOException {
+ ValueFactory valueFactory = ValueFactoryImpl.getInstance();
+ // test empty multi-value
+ assertEquals(DocViewProperty.fromValues("test", new Value[0], PropertyType.BINARY, true, false, false), true, PropertyType.BINARY);
+ // test single value
+ assertEquals(DocViewProperty.fromValues("test", new Value[] {valueFactory.createValue(1.1)}, PropertyType.DOUBLE, false, false, false), false, PropertyType.DOUBLE, "1.1");
+
+ // binary reference (enabled)
+ Binary binary = new SimpleReferenceBinary("myid");
+ Value value = valueFactory.createValue(binary);
+ assertEquals(DocViewProperty.fromValues("test", new Value[]{ value }, PropertyType.BINARY, false, false, true), false, PropertyType.BINARY, true, "myid");
+ // binary reference (disabled)
+ assertEquals(DocViewProperty.fromValues("test", new Value[]{ value }, PropertyType.BINARY, false, false, false), false, PropertyType.BINARY, false, "");
+
+ // binary reference multi-value (enabled)
+ Binary binary2 = new SimpleReferenceBinary("myid2");
+ Value value2 = valueFactory.createValue(binary2);
+ assertEquals(DocViewProperty.fromValues("test", new Value[]{ value, value2 }, PropertyType.BINARY, true, false, true), true, PropertyType.BINARY, true, "myid", "myid2");
+ // binary reference multi-value (disabled)
+ assertEquals(DocViewProperty.fromValues("test", new Value[]{ value, value2 }, PropertyType.BINARY, true, false, false), true, PropertyType.BINARY, false, "", "");
+
+ // regular binary (references enabled)
+ try (InputStream input = new ByteArrayInputStream("testüøö".getBytes(StandardCharsets.UTF_8))) {
+ value = valueFactory.createValue(valueFactory.createBinary(input));
+ }
+ assertEquals(DocViewProperty.fromValues("test", new Value[]{ value }, PropertyType.BINARY, false, false, true), false, PropertyType.BINARY, false, "");
+ // regular binary (references disabled)
+ assertEquals(DocViewProperty.fromValues("test", new Value[]{ value }, PropertyType.BINARY, false, false, false), false, PropertyType.BINARY, false, "");
+
+ // regular binary multi-value (references enabled)
+ assertEquals(DocViewProperty.fromValues("test", new Value[]{ value, value }, PropertyType.BINARY, true, false, true), true, PropertyType.BINARY, false, "", "");
+ // regular binary multi-value (references disabled)
+ assertEquals(DocViewProperty.fromValues("test", new Value[]{ value, value }, PropertyType.BINARY, true, false, false), true, PropertyType.BINARY, false, "", "");
+ }
+
private void assertEscaped(String original, String expected, boolean multi) {
Assert.assertEquals(expected, DocViewProperty.escape(original, multi));
}
private void assertEquals(DocViewProperty p, boolean multi, int type, String... values) {
- Assert.assertEquals(new DocViewProperty(p.name, values, multi, type), p);
+ assertEquals(p, multi, type, false, values);
}
+ private void assertEquals(DocViewProperty p, boolean multi, int type, boolean isReferenceProperty, String... values) {
+ Assert.assertEquals(new DocViewProperty(p.name, values, multi, type, isReferenceProperty), p);
+ }
}
\ No newline at end of file