You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by th...@apache.org on 2009/01/27 17:00:56 UTC

svn commit: r738119 - in /jackrabbit/trunk/jackrabbit-core/src: main/java/org/apache/jackrabbit/core/data/ main/java/org/apache/jackrabbit/core/data/db/ main/java/org/apache/jackrabbit/core/value/ test/java/org/apache/jackrabbit/core/data/

Author: thomasm
Date: Tue Jan 27 16:00:55 2009
New Revision: 738119

URL: http://svn.apache.org/viewvc?rev=738119&view=rev
Log:
JCR-1892 Unique ID for org.apache.jackrabbit.value.BinaryValue

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BinaryValueImpl.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/DataStore.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/FileDataStore.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/db/DbDataStore.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BLOBFileValue.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BLOBInDataStore.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/InternalValue.java
    jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/TestTwoGetStreams.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/DataStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/DataStore.java?rev=738119&r1=738118&r2=738119&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/DataStore.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/DataStore.java Tue Jan 27 16:00:55 2009
@@ -35,6 +35,15 @@
 public interface DataStore {
 
     /**
+     * Check if a record for the given identifier exists, and return it if yes.
+     * If no record exists, this method returns null.
+     * 
+     * @param identifier data identifier
+     * @return the record if found, and null if not
+     */
+    DataRecord getRecordIfStored(DataIdentifier identifier) throws DataStoreException;
+
+    /**
      * Returns the identified data record. The given identifier should be
      * the identifier of a previously saved data record. Since records are
      * never removed, there should never be cases where the identified record

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/FileDataStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/FileDataStore.java?rev=738119&r1=738118&r2=738119&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/FileDataStore.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/FileDataStore.java Tue Jan 27 16:00:55 2009
@@ -122,27 +122,41 @@
         directory = new File(path);
         directory.mkdirs();
     }
-
+    
     /**
-     * Returns the record with the given identifier. Note that this method
-     * performs no sanity checks on the given identifier. It is up to the
-     * caller to ensure that only identifiers of previously created data
-     * records are used.
-     *
-     * @param identifier data identifier
-     * @return identified data record
+     * {@inheritDoc}
      */
-    public DataRecord getRecord(DataIdentifier identifier) {
+    public DataRecord getRecordIfStored(DataIdentifier identifier) {
         File file = getFile(identifier);
         synchronized (this) {
-            if (minModifiedDate != 0 && file.exists() && file.canWrite()) {
+            if (!file.exists()) {
+                return null;
+            }
+            if (minModifiedDate != 0 && file.canWrite()) {
                 if (file.lastModified() < minModifiedDate) {
                     file.setLastModified(System.currentTimeMillis());
                 }
             }
             usesIdentifier(identifier);
+            return new FileDataRecord(identifier, file);
+        }
+    }
+    
+    /**
+     * Returns the record with the given identifier. Note that this method
+     * performs no sanity checks on the given identifier. It is up to the
+     * caller to ensure that only identifiers of previously created data
+     * records are used.
+     *
+     * @param identifier data identifier
+     * @return identified data record
+     */
+    public DataRecord getRecord(DataIdentifier identifier) throws DataStoreException {
+        DataRecord record = getRecordIfStored(identifier);
+        if (record == null) {
+            throw new DataStoreException("Record not found: " + identifier);
         }
-        return new FileDataRecord(identifier, file);
+        return record;
     }
 
     private void usesIdentifier(DataIdentifier identifier) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/db/DbDataStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/db/DbDataStore.java?rev=738119&r1=738118&r2=738119&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/db/DbDataStore.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/data/db/DbDataStore.java Tue Jan 27 16:00:55 2009
@@ -488,7 +488,7 @@
     /**
      * {@inheritDoc}
      */
-    public DataRecord getRecord(DataIdentifier identifier) throws DataStoreException {
+    public DataRecord getRecordIfStored(DataIdentifier identifier) throws DataStoreException {
         ConnectionRecoveryManager conn = getConnection();
         usesIdentifier(identifier);
         ResultSet rs = null;
@@ -513,6 +513,17 @@
     }
     
     /**
+     * {@inheritDoc}
+     */
+    public DataRecord getRecord(DataIdentifier identifier) throws DataStoreException {
+        DataRecord record = getRecordIfStored(identifier);
+        if (record == null) {
+            throw new DataStoreException("Record not found: " + identifier);
+        }
+        return record;
+    }
+    
+    /**
      * Open the input stream. This method sets those fields of the caller
      * that need to be closed once the input stream is read.
      * 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BLOBFileValue.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BLOBFileValue.java?rev=738119&r1=738118&r2=738119&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BLOBFileValue.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BLOBFileValue.java Tue Jan 27 16:00:55 2009
@@ -23,6 +23,7 @@
 import javax.jcr.RepositoryException;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.core.data.DataIdentifier;
 
 /**
  * Represents binary data which is backed by a resource or byte[].
@@ -118,5 +119,14 @@
      * @return true if the value is small
      */
     public abstract boolean isSmall();
+    
+    /**
+     * Get the data identifier if one is available.
+     * 
+     * @return the data identifier or null
+     */
+    public DataIdentifier getDataIdentifier() {
+        return null;
+    }
 
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BLOBInDataStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BLOBInDataStore.java?rev=738119&r1=738118&r2=738119&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BLOBInDataStore.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BLOBInDataStore.java Tue Jan 27 16:00:55 2009
@@ -60,6 +60,10 @@
     public void discard() {
         // do nothing
     }
+    
+    public DataIdentifier getDataIdentifier() {
+        return identifier;
+    }
 
     /**
      * {@inheritDoc}
@@ -115,6 +119,10 @@
         DataIdentifier identifier = new DataIdentifier(id);
         return new BLOBInDataStore(store, identifier);
     }
+    
+    static BLOBInDataStore getInstance(DataStore store, DataIdentifier identifier) {
+        return new BLOBInDataStore(store, identifier);
+    }
 
     static BLOBInDataStore getInstance(DataStore store, InputStream in) throws DataStoreException {
         DataRecord rec = store.addRecord(in);

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BinaryValueImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BinaryValueImpl.java?rev=738119&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BinaryValueImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/BinaryValueImpl.java Tue Jan 27 16:00:55 2009
@@ -0,0 +1,67 @@
+/*
+ * 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.core.value;
+
+import javax.jcr.RepositoryException;
+import org.apache.jackrabbit.api.JackrabbitValue;
+import org.apache.jackrabbit.core.data.DataIdentifier;
+import org.apache.jackrabbit.value.BinaryValue;
+
+/**
+ * Represents a binary value that is backed by a blob file value.
+ */
+public class BinaryValueImpl extends BinaryValue implements JackrabbitValue {
+    
+    private final BLOBFileValue blob;
+
+    /**
+     * Construct a new object from the given blob.
+     * 
+     * @param blob the blob
+     */
+    BinaryValueImpl(BLOBFileValue blob) throws RepositoryException {
+        super(blob.getStream());
+        this.blob = blob;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getContentIdentity() {
+        DataIdentifier id = blob.getDataIdentifier();
+        return id == null ? null : id.toString();
+    }
+
+    /**
+     * Get the wrapped blob file value
+     * 
+     * @return the blob file value
+     */
+    BLOBFileValue getBlob() {
+        return blob;
+    }
+    
+    /**
+     * Get the data identifier if one is available.
+     * 
+     * @return the data identifier or null
+     */
+    DataIdentifier getDataIdentifier() {
+        return blob.getDataIdentifier();
+    }
+
+}

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/InternalValue.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/InternalValue.java?rev=738119&r1=738118&r2=738119&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/InternalValue.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/value/InternalValue.java Tue Jan 27 16:00:55 2009
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.core.value;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.core.data.DataIdentifier;
 import org.apache.jackrabbit.core.data.DataStore;
 import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.spi.commons.conversion.MalformedPathException;
@@ -26,7 +27,6 @@
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.util.ISO8601;
 import org.apache.jackrabbit.uuid.UUID;
-import org.apache.jackrabbit.value.BinaryValue;
 import org.apache.jackrabbit.value.BooleanValue;
 import org.apache.jackrabbit.value.DateValue;
 import org.apache.jackrabbit.value.DoubleValue;
@@ -121,19 +121,37 @@
         }
         switch (value.getType()) {
             case PropertyType.BINARY:
+                InternalValue result;
                 if (USE_DATA_STORE) {
-                    return new InternalValue(getBLOBFileValue(store, value.getStream(), true));
-                }
-                if (value instanceof BLOBFileValue) {
-                    return new InternalValue((BLOBFileValue) value);
+                    BLOBFileValue blob = null;
+                    if (value instanceof BinaryValueImpl) {
+                        BinaryValueImpl bin = (BinaryValueImpl) value;
+                        DataIdentifier identifier = bin.getDataIdentifier();
+                        if (identifier != null) {
+                            // access the record to ensure it is not garbage collected
+                            if (store.getRecordIfStored(identifier) != null) {
+                                // it exists - so we don't need to stream it again
+                                // but we need to create a new object because the original
+                                // one might be in a different data store (repository)
+                                blob = BLOBInDataStore.getInstance(store, identifier);
+                            }
+                        }
+                    }
+                    if (blob == null) {
+                        blob = getBLOBFileValue(store, value.getStream(), true);
+                    }
+                    result = new InternalValue(blob);
+                } else if (value instanceof BLOBFileValue) {
+                    result = new InternalValue((BLOBFileValue) value);
                 } else {
                     InputStream stream = value.getStream();
                     try {
-                        return createTemporary(stream);
+                        result = createTemporary(stream);
                     } finally {
                         IOUtils.closeQuietly(stream);
                     }
                 }
+                return result;
             case PropertyType.BOOLEAN:
                 return create(value.getBoolean());
             case PropertyType.DATE:
@@ -361,7 +379,7 @@
             throws RepositoryException {
         switch (type) {
             case PropertyType.BINARY:
-                return new BinaryValue(((BLOBFileValue) val).getStream());
+                return new BinaryValueImpl((BLOBFileValue) val);
             case PropertyType.BOOLEAN:
                 return new BooleanValue(((Boolean) val));
             case PropertyType.DATE:

Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/TestTwoGetStreams.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/TestTwoGetStreams.java?rev=738119&r1=738118&r2=738119&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/TestTwoGetStreams.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/core/data/TestTwoGetStreams.java Tue Jan 27 16:00:55 2009
@@ -20,8 +20,12 @@
 import java.io.InputStream;
 
 import javax.jcr.Node;
+import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.Value;
 
+import org.apache.jackrabbit.api.JackrabbitValue;
+import org.apache.jackrabbit.core.RepositoryImpl;
 import org.apache.jackrabbit.test.AbstractJCRTest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -34,11 +38,63 @@
     private static Logger log = LoggerFactory.getLogger(TestTwoGetStreams.class);
 
     private static final int STREAM_LENGTH = 1 * 1024 * 1024;
+    
+    private boolean isDataStoreEnabled() throws RepositoryException {
+        Session session = helper.getSuperuserSession();
+        RepositoryImpl rep = (RepositoryImpl) session.getRepository();        
+        return rep.getDataStore() != null;
+    }
+    
+    /**
+     * Test the JackrabbitValue.getContentIdentity feature.
+     */
+    public void testContentIdentity() throws Exception {
+        if (!isDataStoreEnabled()) {
+            log.info("testContentIdentity skipped. Data store is not used.");
+            return;
+        }
+        
+        Session session = helper.getSuperuserSession();
+        
+        Node root = session.getRootNode();
+        root.setProperty("p1", new RandomInputStream(1, STREAM_LENGTH));
+        root.setProperty("p2", new RandomInputStream(1, STREAM_LENGTH));
+        session.save();
+
+        Value v1 = root.getProperty("p1").getValue();
+        Value v2 = root.getProperty("p2").getValue();
+        if (v1 instanceof JackrabbitValue && v2 instanceof JackrabbitValue) {
+            JackrabbitValue j1 = (JackrabbitValue) v1;
+            JackrabbitValue j2 = (JackrabbitValue) v2;
+            String id1 = j1.getContentIdentity();
+            String id2 = j2.getContentIdentity();
+            assertNotNull(id1);
+            assertEquals(id1, id2);
+        }
+        
+        // copying a value should not stream the content
+        long time = System.currentTimeMillis();
+        for (int i = 0; i < 100; i++) {
+            Value v = root.getProperty("p1").getValue();
+            root.setProperty("p3", v);
+        }
+        session.save();
+        time = System.currentTimeMillis() - time;
+        // streaming the value again and again takes about 4.3 seconds
+        // on my computer; copying the data identifier takes about 16 ms
+        assertTrue(time < 500);
 
+    }
+    
     /**
      * Test reading from two concurrently opened streams.
      */
     public void testTwoGetStreams() throws Exception {
+        if (!isDataStoreEnabled()) {
+            log.info("testContentIdentity skipped. Data store is not used.");
+            return;
+        }
+        
         Session session = helper.getSuperuserSession();
         Node root = session.getRootNode();
         root.setProperty("p1", new RandomInputStream(1, STREAM_LENGTH));
@@ -65,6 +121,11 @@
      * Tests reading concurrently from two different streams.
      */
     public void testTwoStreamsConcurrently() throws Exception {
+        if (!isDataStoreEnabled()) {
+            log.info("testContentIdentity skipped. Data store is not used.");
+            return;
+        }
+        
         Session session = helper.getSuperuserSession();
         Node root = session.getRootNode();
         root.setProperty("p1", new RandomInputStream(1, STREAM_LENGTH));
@@ -91,6 +152,11 @@
      * same property.
      */
     public void testTwoStreamsFromSamePropertyConcurrently() throws Exception {
+        if (!isDataStoreEnabled()) {
+            log.info("testContentIdentity skipped. Data store is not used.");
+            return;
+        }
+        
         Session session = helper.getSuperuserSession();
         Node root = session.getRootNode();
         root.setProperty("p1", new RandomInputStream(1, STREAM_LENGTH));