diff --git oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/BinaryReferenceLoader.java oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/BinaryReferenceLoader.java
new file mode 100644
index 0000000..0b3313b
--- /dev/null
+++ oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/BinaryReferenceLoader.java
@@ -0,0 +1,93 @@
+package org.apache.jackrabbit.oak.upgrade;
+
+import org.apache.jackrabbit.api.ReferenceBinary;
+import org.apache.jackrabbit.core.data.AbstractDataStore;
+import org.apache.jackrabbit.core.data.DataIdentifier;
+import org.apache.jackrabbit.core.data.DataStore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * The BinaryReferenceLoader attempts to optimize the retrieval of
+ * binary references by relying on internals of
+ *
+ * - {@code org.apache.jackrabbit.core.data.AbstractDataStore} and
+ * - {@code org.apache.jackrabbit.core.value.BLOBInDataStore}
+ *
+ * There is a fallback to the default functionality if the binary is not a
+ * {@code BLOBInDataStore}.
+ *
+ * In the optimized the reference is calculated from the binary's
+ * {@code DataIdentifier}.
+ */
+public class BinaryReferenceLoader {
+
+ public static final BinaryReferenceLoader INSTANCE = create();
+
+ private static final Logger LOG = LoggerFactory.getLogger(BinaryReferenceLoader.class);
+
+ private Method getReferenceFromIdentifier;
+
+ private final Field storeField;
+
+ private final Field identifierField;
+
+ public BinaryReferenceLoader(Method getReferenceFromIdentifier, Field storeField, Field identifierField) {
+ this.getReferenceFromIdentifier = getReferenceFromIdentifier;
+ this.storeField = storeField;
+ this.identifierField = identifierField;
+ }
+
+ private static BinaryReferenceLoader create() {
+ try {
+ final Method getReferenceFromIdentifier = AbstractDataStore.class
+ .getDeclaredMethod("getReferenceFromIdentifier", DataIdentifier.class);
+ getReferenceFromIdentifier.setAccessible(true);
+
+ final ClassLoader classLoader = BinaryReferenceLoader.class.getClassLoader();
+ final Class> binaryClass = classLoader.loadClass("org.apache.jackrabbit.core.value.BLOBInDataStore");
+ final Field storeField = binaryClass.getDeclaredField("store");
+ storeField.setAccessible(true);
+ final Field identifierField = binaryClass.getDeclaredField("identifier");
+ identifierField.setAccessible(true);
+ return new BinaryReferenceLoader(getReferenceFromIdentifier, storeField, identifierField);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Failed to create BinaryReferenceLoader", e);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Failed to create BinaryReferenceLoader", e);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Failed to create BinaryReferenceLoader", e);
+ }
+ }
+
+ public String getBinaryReference(ReferenceBinary binary) {
+ if ("org.apache.jackrabbit.core.value.BLOBInDataStore".equals(binary.getClass().getName())) {
+ try {
+ final DataStore dataStore = (DataStore) storeField.get(binary);
+ final DataIdentifier identifier = (DataIdentifier) identifierField.get(binary);
+ return getReferenceFromIdentifier(dataStore, identifier);
+ } catch (IllegalAccessException e) {
+ LOG.error("failed optimization: ", e);
+ // fall through
+ }
+ }
+ return binary.getReference();
+ }
+
+ public String getReferenceFromIdentifier(DataStore dataStore, DataIdentifier identifier) {
+ try {
+ return (String) getReferenceFromIdentifier.invoke(dataStore, identifier);
+ } catch (IllegalAccessException e) {
+ LOG.error("failed optimization: ", e);
+ // fall through
+ } catch (InvocationTargetException e) {
+ LOG.error("failed optimization: ", e);
+ // fall through
+ }
+ return null;
+ }
+}
diff --git oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/JackrabbitNodeState.java oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/JackrabbitNodeState.java
index 46e9fc8..c9ef4db 100644
--- oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/JackrabbitNodeState.java
+++ oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/JackrabbitNodeState.java
@@ -104,14 +104,16 @@ class JackrabbitNodeState extends AbstractNodeState {
private String path;
/**
* Bundle loader based on the source persistence manager.
*/
private final BundleLoader loader;
+ private final BinaryReferenceLoader binaryReferenceLoader;
+
private final String workspaceName;
private final TypePredicate isReferenceable;
private final TypePredicate isOrderable;
private final TypePredicate isVersionable;
@@ -135,14 +137,15 @@ class JackrabbitNodeState extends AbstractNodeState {
private JackrabbitNodeState(
JackrabbitNodeState parent, String name, NodePropBundle bundle) {
this.parent = parent;
this.name = name;
this.path = null;
this.loader = parent.loader;
+ this.binaryReferenceLoader = parent.binaryReferenceLoader;
this.workspaceName = parent.workspaceName;
this.isReferenceable = parent.isReferenceable;
this.isOrderable = parent.isOrderable;
this.isVersionable = parent.isVersionable;
this.isVersionHistory = parent.isVersionHistory;
this.isFrozenNode = parent.isFrozenNode;
this.uriToPrefix = parent.uriToPrefix;
@@ -161,14 +164,15 @@ class JackrabbitNodeState extends AbstractNodeState {
Map uriToPrefix, NodeId id, String path,
String workspaceName, Map versionablePaths,
boolean useBinaryReferences) {
this.parent = null;
this.name = null;
this.path = path;
this.loader = new BundleLoader(source);
+ this.binaryReferenceLoader = BinaryReferenceLoader.INSTANCE;
this.workspaceName = workspaceName;
this.isReferenceable = new TypePredicate(root, MIX_REFERENCEABLE);
this.isOrderable = TypePredicate.isOrderable(root);
this.isVersionable = new TypePredicate(root, MIX_VERSIONABLE);
this.isVersionHistory = new TypePredicate(root, NT_VERSIONHISTORY);
this.isFrozenNode = new TypePredicate(root, NT_FROZENNODE);
this.uriToPrefix = uriToPrefix;
@@ -549,26 +553,50 @@ class JackrabbitNodeState extends AbstractNodeState {
if (!useBinaryReferences) {
return null;
}
try {
Binary binary = value.getBinary();
try {
if (binary instanceof ReferenceBinary) {
+ if (binaryReferenceLoader != null) {
+ return binaryReferenceLoader.getBinaryReference((ReferenceBinary) binary);
+ }
return ((ReferenceBinary) binary).getReference();
} else {
return null;
}
} finally {
binary.dispose();
}
} catch (RepositoryException e) {
warn("Unable to get blob reference", e);
return null;
}
}
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof Blob)) {
+ return false;
+ }
+
+ // make sure reference comparison happens early
+ final Blob that = (Blob) other;
+ final String referenceA = this.getReference();
+ final String referenceB = that.getReference();
+ if (referenceA != null && referenceB != null) {
+ return referenceA.equals(referenceB);
+ }
+
+ return super.equals(that);
+ }
};
}
private String createName(Name name) {
String uri = name.getNamespaceURI();
String local = name.getLocalName();
if (uri == null || uri.isEmpty()) {
diff --git oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/ReferenceOptimizedBlobStore.java oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/ReferenceOptimizedBlobStore.java
new file mode 100644
index 0000000..a35969d
--- /dev/null
+++ oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/ReferenceOptimizedBlobStore.java
@@ -0,0 +1,33 @@
+package org.apache.jackrabbit.oak.upgrade;
+
+import org.apache.jackrabbit.core.data.DataIdentifier;
+import org.apache.jackrabbit.core.data.DataStore;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore;
+
+public class ReferenceOptimizedBlobStore extends DataStoreBlobStore {
+
+ private final BinaryReferenceLoader binaryReferenceLoader;
+
+ private DataStore dataStore;
+
+ public ReferenceOptimizedBlobStore(DataStore dataStore) {
+ super(dataStore);
+ this.dataStore = dataStore;
+ this.binaryReferenceLoader = BinaryReferenceLoader.INSTANCE;
+ }
+
+ @Override
+ public String getReference(String encodedBlobId) {
+ String blobId = extractBlobId(encodedBlobId);
+ final DataIdentifier identifier = new DataIdentifier(blobId);
+ return binaryReferenceLoader.getReferenceFromIdentifier(dataStore, identifier);
+ }
+
+ private String extractBlobId(String encodedBlobId) {
+ int indexOfSep = encodedBlobId.lastIndexOf("#");
+ if (indexOfSep != -1) {
+ return encodedBlobId.substring(0, indexOfSep);
+ }
+ return encodedBlobId;
+ }
+}