jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ste...@apache.org
Subject svn commit: r329224 [1/2] - in /incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core: state/db/ state/mem/ state/obj/ state/util/ state/xml/ value/
Date Fri, 28 Oct 2005 15:29:20 GMT
Author: stefan
Date: Fri Oct 28 08:29:07 2005
New Revision: 329224

URL: http://svn.apache.org/viewcvs?rev=329224&view=rev
Log:
- cleaned up persistence manager code in core
- improved space efficiency of simple serialization format as
  used by ObjectPersistenceManager, InMemPersistenceManager
  and SimpleDbPersistenceManager
- added configuration option to SimpleDbPersistenceManager
  whether BLOBs should be stored externally in the file system
  (default) or if they should be stored inline in the db
- prepared persistence managers for coming data model change 
  (new ItemState#modCount field)

see http://thread.gmane.org/gmane.comp.apache.jackrabbit.devel/4137

Added:
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/BLOBStore.java   (with props)
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/FileSystemBLOBStore.java   (with props)
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/ResourceBasedBLOBStore.java   (with props)
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/Serializer.java   (with props)
Removed:
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/obj/BLOBStore.java
Modified:
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/DerbyPersistenceManager.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/SimpleDbPersistenceManager.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/daffodil.ddl
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/default.ddl
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/derby.ddl
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/mssql.ddl
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/mysql.ddl
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/obj/ObjectPersistenceManager.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/xml/XMLPersistenceManager.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/value/BLOBFileValue.java

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/DerbyPersistenceManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/DerbyPersistenceManager.java?rev=329224&r1=329223&r2=329224&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/DerbyPersistenceManager.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/DerbyPersistenceManager.java Fri Oct 28 08:29:07 2005
@@ -40,6 +40,9 @@
  * (default: <code>"derby"</code>)</li>
  * <li><code>user</code>: the database user (default: <code>""</code>)</li>
  * <li><code>password</code>: the user's password (default: <code>""</code>)</li>
+ * <li><code>externalBLOBs</code>: if <code>true</code> (the default) BINARY
+ * values (BLOBs) are stored in the local file system;
+ * if <code>false</code> BLOBs are stored in the database</li>
  * </ul>
  * See also {@link SimpleDbPersistenceManager}.
  * <p/>
@@ -48,6 +51,7 @@
  *   &lt;PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager"&gt;
  *       &lt;param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/&gt;
  *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
+ *       &lt;param name="externalBLOBs" value="false"/&gt;
  *  &lt;/PersistenceManager&gt;
  * </pre>
  */

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/SimpleDbPersistenceManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/SimpleDbPersistenceManager.java?rev=329224&r1=329223&r2=329224&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/SimpleDbPersistenceManager.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/SimpleDbPersistenceManager.java Fri Oct 28 08:29:07 2005
@@ -19,8 +19,6 @@
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.PropertyId;
 import org.apache.jackrabbit.core.fs.FileSystem;
-import org.apache.jackrabbit.core.fs.FileSystemPathUtil;
-import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.fs.local.LocalFileSystem;
 import org.apache.jackrabbit.core.state.AbstractPersistenceManager;
 import org.apache.jackrabbit.core.state.ChangeLog;
@@ -32,24 +30,22 @@
 import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.core.state.PMContext;
 import org.apache.jackrabbit.core.state.PropertyState;
-import org.apache.jackrabbit.core.state.obj.BLOBStore;
-import org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager;
+import org.apache.jackrabbit.core.state.util.BLOBStore;
+import org.apache.jackrabbit.core.state.util.FileSystemBLOBStore;
+import org.apache.jackrabbit.core.state.util.Serializer;
 import org.apache.jackrabbit.core.value.BLOBFileValue;
 import org.apache.jackrabbit.core.value.InternalValue;
-import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.util.Text;
 import org.apache.log4j.Logger;
 
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
-import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.InputStreamReader;
-import java.io.BufferedReader;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
@@ -61,8 +57,9 @@
  * <code>SimpleDbPersistenceManager</code> is a generic JDBC-based
  * <code>PersistenceManager</code> for Jackrabbit that persists
  * <code>ItemState</code> and <code>NodeReferences</code> objects using a
- * simple custom serialization format and a very basic non-normalized database
- * schema (in essence tables with one 'key' and one 'data' column).
+ * simple custom binary serialization format (see {@link Serializer}) and a
+ * very basic non-normalized database schema (in essence tables with one 'key'
+ * and one 'data' column).
  * <p/>
  * It is configured through the following properties:
  * <ul>
@@ -73,6 +70,9 @@
  * <li><code>schema</code>: type of schema to be used
  * (e.g. <code>mysql</code>, <code>mssql</code>, etc.); </li>
  * <li><code>schemaObjectPrefix</code>: prefix to be prepended to schema objects</li>
+ * <li><code>externalBLOBs</code>: if <code>true</code> (the default) BINARY
+ * values (BLOBs) are stored in the local file system;
+ * if <code>false</code> BLOBs are stored in the database</li>
  * </ul>
  * The required schema objects are automatically created by executing the DDL
  * statements read from the [schema].ddl file. The .ddl file is read from the
@@ -89,7 +89,8 @@
  *       &lt;param name="url" value="jdbc:mysql:///test"/&gt;
  *       &lt;param name="schema" value="mysql"/&gt;
  *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
- *  &lt;/PersistenceManager&gt;
+ *       &lt;param name="externalBLOBs" value="false"/&gt;
+ *   &lt;/PersistenceManager&gt;
  * </pre>
  * The following is a fragment from a sample configuration using Daffodil One$DB Embedded:
  * <pre>
@@ -100,13 +101,16 @@
  *       &lt;param name="password" value="daffodil"/&gt;
  *       &lt;param name="schema" value="daffodil"/&gt;
  *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
- *  &lt;/PersistenceManager&gt;
+ *       &lt;param name="externalBLOBs" value="false"/&gt;
+ *   &lt;/PersistenceManager&gt;
  * </pre>
+ * See also {@link DerbyPersistenceManager}.
  */
-public class SimpleDbPersistenceManager extends AbstractPersistenceManager
-        implements BLOBStore {
+public class SimpleDbPersistenceManager extends AbstractPersistenceManager {
 
-    /** Logger instance */
+    /**
+     * Logger instance
+     */
     private static Logger log = Logger.getLogger(SimpleDbPersistenceManager.class);
 
     protected static final String SCHEMA_OBJECT_PREFIX_VARIABLE =
@@ -121,6 +125,8 @@
     protected String schema;
     protected String schemaObjectPrefix;
 
+    protected boolean externalBLOBs;
+
     // initial size of buffer used to serialize objects
     protected static final int INITIAL_BUFFER_SIZE = 1024;
 
@@ -131,22 +137,41 @@
     protected PreparedStatement nodeStateInsert;
     protected PreparedStatement nodeStateUpdate;
     protected PreparedStatement nodeStateSelect;
+    protected PreparedStatement nodeStateSelectExist;
     protected PreparedStatement nodeStateDelete;
 
     // shared prepared statements for PropertyState management
     protected PreparedStatement propertyStateInsert;
     protected PreparedStatement propertyStateUpdate;
     protected PreparedStatement propertyStateSelect;
+    protected PreparedStatement propertyStateSelectExist;
     protected PreparedStatement propertyStateDelete;
 
     // shared prepared statements for NodeReference management
     protected PreparedStatement nodeReferenceInsert;
     protected PreparedStatement nodeReferenceUpdate;
     protected PreparedStatement nodeReferenceSelect;
+    protected PreparedStatement nodeReferenceSelectExist;
     protected PreparedStatement nodeReferenceDelete;
 
-    /** file system where BLOB data is stored */
+    // shared prepared statements for BLOB management
+    // (if <code>externalBLOBs==false</code>)
+    protected PreparedStatement blobInsert;
+    protected PreparedStatement blobUpdate;
+    protected PreparedStatement blobSelect;
+    protected PreparedStatement blobSelectExist;
+    protected PreparedStatement blobDelete;
+
+    /**
+     * file system where BLOB data is stored
+     * (if <code>externalBLOBs==true</code>)
+     */
     protected FileSystem blobFS;
+    /**
+     * BLOBStore that manages BLOB data in the file system
+     * (if <code>externalBLOBs==true</code>)
+     */
+    protected BLOBStore blobStore;
 
     /**
      * Creates a new <code>SimpleDbPersistenceManager</code> instance.
@@ -154,6 +179,7 @@
     public SimpleDbPersistenceManager() {
         schema = "default";
         schemaObjectPrefix = "";
+        externalBLOBs = true;
         initialized = false;
     }
 
@@ -207,47 +233,16 @@
         this.schema = schema;
     }
 
-    //------------------------------------------------------------< BLOBStore >
-    /**
-     * {@inheritDoc}
-     */
-    public FileSystemResource get(String blobId) throws Exception {
-        return new FileSystemResource(blobFS, blobId);
+    public boolean isExternalBLOBs() {
+        return externalBLOBs;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public String put(PropertyId id, int index, InputStream in, long size)
-            throws Exception {
-        String path = buildBlobFilePath(id.getParentUUID(), id.getName(), index);
-        OutputStream out = null;
-        FileSystemResource internalBlobFile = new FileSystemResource(blobFS, path);
-        internalBlobFile.makeParentDirs();
-        try {
-            out = new BufferedOutputStream(internalBlobFile.getOutputStream());
-            byte[] buffer = new byte[8192];
-            int read;
-            while ((read = in.read(buffer)) > 0) {
-                out.write(buffer, 0, read);
-            }
-        } finally {
-            out.close();
-        }
-        return path;
+    public void setExternalBLOBs(boolean externalBLOBs) {
+        this.externalBLOBs = externalBLOBs;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public boolean remove(String blobId) throws Exception {
-        FileSystemResource res = new FileSystemResource(blobFS, blobId);
-        if (!res.exists()) {
-            return false;
-        }
-        // delete resource and prune empty parent folders
-        res.delete(true);
-        return true;
+    public void setExternalBLOBs(String externalBLOBs) {
+        this.externalBLOBs = Boolean.valueOf(externalBLOBs).booleanValue();
     }
 
     //---------------------------------------------------< PersistenceManager >
@@ -267,15 +262,6 @@
         // check if schema objects exist and create them if necessary
         checkSchema();
 
-        /**
-         * store blob's in local file system in a sub directory
-         * of the workspace home directory
-         */
-        LocalFileSystem blobFS = new LocalFileSystem();
-        blobFS.setRoot(new File(context.getHomeDir(), "blobs"));
-        blobFS.init();
-        this.blobFS = blobFS;
-
         // prepare statements
         nodeStateInsert =
                 con.prepareStatement("insert into "
@@ -286,6 +272,9 @@
         nodeStateSelect =
                 con.prepareStatement("select NODE_DATA from "
                 + schemaObjectPrefix + "NODE where NODE_ID = ?");
+        nodeStateSelectExist =
+                con.prepareStatement("select 1 from "
+                + schemaObjectPrefix + "NODE where NODE_ID = ?");
         nodeStateDelete =
                 con.prepareStatement("delete from "
                 + schemaObjectPrefix + "NODE where NODE_ID = ?");
@@ -299,6 +288,9 @@
         propertyStateSelect =
                 con.prepareStatement("select PROP_DATA from "
                 + schemaObjectPrefix + "PROP where PROP_ID = ?");
+        propertyStateSelectExist =
+                con.prepareStatement("select 1 from "
+                + schemaObjectPrefix + "PROP where PROP_ID = ?");
         propertyStateDelete =
                 con.prepareStatement("delete from "
                 + schemaObjectPrefix + "PROP where PROP_ID = ?");
@@ -312,10 +304,46 @@
         nodeReferenceSelect =
                 con.prepareStatement("select REFS_DATA from "
                 + schemaObjectPrefix + "REFS where NODE_ID = ?");
+        nodeReferenceSelectExist =
+                con.prepareStatement("select 1 from "
+                + schemaObjectPrefix + "REFS where NODE_ID = ?");
         nodeReferenceDelete =
                 con.prepareStatement("delete from "
                 + schemaObjectPrefix + "REFS where NODE_ID = ?");
 
+        if (externalBLOBs) {
+            /**
+             * store BLOBs in local file system in a sub directory
+             * of the workspace home directory
+             */
+            LocalFileSystem blobFS = new LocalFileSystem();
+            blobFS.setRoot(new File(context.getHomeDir(), "blobs"));
+            blobFS.init();
+            this.blobFS = blobFS;
+            blobStore = new FileSystemBLOBStore(blobFS);
+        } else {
+            /**
+             * store BLOBs in db
+             */
+            blobStore = new DbBLOBStore();
+
+            blobInsert =
+                    con.prepareStatement("insert into "
+                    + schemaObjectPrefix + "BINVAL (BINVAL_DATA, BINVAL_ID) values (?, ?)");
+            blobUpdate =
+                    con.prepareStatement("update "
+                    + schemaObjectPrefix + "BINVAL set BINVAL_DATA = ? where BINVAL_ID = ?");
+            blobSelect =
+                    con.prepareStatement("select BINVAL_DATA from "
+                    + schemaObjectPrefix + "BINVAL where BINVAL_ID = ?");
+            blobSelectExist =
+                    con.prepareStatement("select 1 from "
+                    + schemaObjectPrefix + "BINVAL where BINVAL_ID = ?");
+            blobDelete =
+                    con.prepareStatement("delete from "
+                    + schemaObjectPrefix + "BINVAL where BINVAL_ID = ?");
+        }
+
         initialized = true;
     }
 
@@ -332,23 +360,37 @@
             closeStatement(nodeStateInsert);
             closeStatement(nodeStateUpdate);
             closeStatement(nodeStateSelect);
+            closeStatement(nodeStateSelectExist);
             closeStatement(nodeStateDelete);
 
             closeStatement(propertyStateInsert);
             closeStatement(propertyStateUpdate);
             closeStatement(propertyStateSelect);
+            closeStatement(propertyStateSelectExist);
             closeStatement(propertyStateDelete);
 
             closeStatement(nodeReferenceInsert);
             closeStatement(nodeReferenceUpdate);
             closeStatement(nodeReferenceSelect);
+            closeStatement(nodeReferenceSelectExist);
             closeStatement(nodeReferenceDelete);
 
+            if (!externalBLOBs) {
+                closeStatement(blobInsert);
+                closeStatement(blobUpdate);
+                closeStatement(blobSelect);
+                closeStatement(blobSelectExist);
+                closeStatement(blobDelete);
+            } else {
+                // close BLOB file system
+                blobFS.close();
+                blobFS = null;
+            }
+            blobStore = null;
+
             // close jdbc connection
             con.close();
-            // close blob store
-            blobFS.close();
-            blobFS = null;
+
         } finally {
             initialized = false;
         }
@@ -410,7 +452,7 @@
 
             in = rs.getBinaryStream(1);
             NodeState state = createNew(id);
-            ObjectPersistenceManager.deserialize(state, in);
+            Serializer.deserialize(state, in);
 
             return state;
         } catch (Exception e) {
@@ -449,7 +491,7 @@
 
             in = rs.getBinaryStream(1);
             PropertyState state = createNew(id);
-            ObjectPersistenceManager.deserialize(state, in, this);
+            Serializer.deserialize(state, in, blobStore);
 
             return state;
         } catch (Exception e) {
@@ -489,14 +531,14 @@
             ByteArrayOutputStream out =
                     new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
             // serialize node state
-            ObjectPersistenceManager.serialize(state, out);
+            Serializer.serialize(state, out);
 
             // we are synchronized on this instance, therefore we do not
             // not have to additionally synchronize on the preparedStatement
 
             stmt.setBytes(1, out.toByteArray());
             stmt.setString(2, state.getId().toString());
-            stmt.execute();
+            stmt.executeUpdate();
 
             // there's no need to close a ByteArrayOutputStream
             //out.close();
@@ -533,14 +575,14 @@
             ByteArrayOutputStream out =
                     new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
             // serialize property state
-            ObjectPersistenceManager.serialize(state, out, this);
+            Serializer.serialize(state, out, blobStore);
 
             // we are synchronized on this instance, therefore we do not
             // not have to additionally synchronize on the preparedStatement
 
             stmt.setBytes(1, out.toByteArray());
             stmt.setString(2, state.getId().toString());
-            stmt.execute();
+            stmt.executeUpdate();
 
             // there's no need to close a ByteArrayOutputStream
             //out.close();
@@ -565,7 +607,7 @@
         PreparedStatement stmt = nodeStateDelete;
         try {
             stmt.setString(1, state.getId().toString());
-            stmt.execute();
+            stmt.executeUpdate();
         } catch (Exception e) {
             String msg = "failed to delete node state: " + state.getId();
             log.error(msg, e);
@@ -584,7 +626,7 @@
             throw new IllegalStateException("not initialized");
         }
 
-        // delete binary values (stored as files)
+        // make sure binary values (BLOBs) are properly removed
         InternalValue[] values = state.getValues();
         if (values != null) {
             for (int i = 0; i < values.length; i++) {
@@ -592,8 +634,15 @@
                 if (val != null) {
                     if (val.getType() == PropertyType.BINARY) {
                         BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
-                        // delete blob file and prune empty parent folders
+                        // delete internal resource representation of BLOB value
                         blobVal.delete(true);
+                        // also remove from BLOBStore
+                        String blobId = blobStore.createId((PropertyId) state.getId(), i);
+                        try {
+                            blobStore.remove(blobId);
+                        } catch (Exception e) {
+                            log.warn("failed to remove from BLOBStore: " + blobId, e);
+                        }
                     }
                 }
             }
@@ -602,7 +651,7 @@
         PreparedStatement stmt = propertyStateDelete;
         try {
             stmt.setString(1, state.getId().toString());
-            stmt.execute();
+            stmt.executeUpdate();
         } catch (Exception e) {
             String msg = "failed to delete property state: " + state.getId();
             log.error(msg, e);
@@ -634,7 +683,7 @@
 
             in = rs.getBinaryStream(1);
             NodeReferences refs = new NodeReferences(targetId);
-            ObjectPersistenceManager.deserialize(refs, in);
+            Serializer.deserialize(refs, in);
 
             return refs;
         } catch (Exception e) {
@@ -674,14 +723,14 @@
             ByteArrayOutputStream out =
                     new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
             // serialize references
-            ObjectPersistenceManager.serialize(refs, out);
+            Serializer.serialize(refs, out);
 
             // we are synchronized on this instance, therefore we do not
             // not have to additionally synchronize on the preparedStatement
 
             stmt.setBytes(1, out.toByteArray());
             stmt.setString(2, refs.getTargetId().toString());
-            stmt.execute();
+            stmt.executeUpdate();
 
             // there's no need to close a ByteArrayOutputStream
             //out.close();
@@ -706,7 +755,7 @@
         PreparedStatement stmt = nodeReferenceDelete;
         try {
             stmt.setString(1, refs.getTargetId().toString());
-            stmt.execute();
+            stmt.executeUpdate();
         } catch (Exception e) {
             String msg = "failed to delete references: " + refs.getTargetId();
             log.error(msg, e);
@@ -724,7 +773,7 @@
             throw new IllegalStateException("not initialized");
         }
 
-        PreparedStatement stmt = nodeStateSelect;
+        PreparedStatement stmt = nodeStateSelectExist;
         ResultSet rs = null;
         try {
             stmt.setString(1, id.toString());
@@ -751,7 +800,7 @@
             throw new IllegalStateException("not initialized");
         }
 
-        PreparedStatement stmt = propertyStateSelect;
+        PreparedStatement stmt = propertyStateSelectExist;
         ResultSet rs = null;
         try {
             stmt.setString(1, id.toString());
@@ -779,7 +828,7 @@
             throw new IllegalStateException("not initialized");
         }
 
-        PreparedStatement stmt = nodeReferenceSelect;
+        PreparedStatement stmt = nodeReferenceSelectExist;
         ResultSet rs = null;
         try {
             stmt.setString(1, targetId.toString());
@@ -860,33 +909,10 @@
         log.debug("      dump:", se);
     }
 
-    protected static String buildBlobFilePath(String parentUUID,
-                                              QName propName, int index) {
-        StringBuffer sb = new StringBuffer();
-        char[] chars = parentUUID.toCharArray();
-        int cnt = 0;
-        for (int i = 0; i < chars.length; i++) {
-            if (chars[i] == '-') {
-                continue;
-            }
-            //if (cnt > 0 && cnt % 4 == 0) {
-            if (cnt == 2 || cnt == 4) {
-                sb.append(FileSystem.SEPARATOR_CHAR);
-            }
-            sb.append(chars[i]);
-            cnt++;
-        }
-        sb.append(FileSystem.SEPARATOR_CHAR);
-        sb.append(FileSystemPathUtil.escapeName(propName.toString()));
-        sb.append('.');
-        sb.append(index);
-        sb.append(".bin");
-        return sb.toString();
-    }
-
     /**
      * Checks if the required schema objects exist and creates them if they
      * don't exist yet.
+     *
      * @throws Exception if an error occurs
      */
     protected void checkSchema() throws Exception {
@@ -915,7 +941,7 @@
                     // replace prefix variable
                     sql = Text.replace(sql, SCHEMA_OBJECT_PREFIX_VARIABLE, schemaObjectPrefix);
                     // execute sql stmt
-                    stmt.execute(sql);
+                    stmt.executeUpdate(sql);
                     // read next sql stmt
                     sql = reader.readLine();
                 }
@@ -924,6 +950,121 @@
             } finally {
                 closeStream(in);
                 closeStatement(stmt);
+            }
+        }
+    }
+
+    //--------------------------------------------------------< inner classes >
+    class DbBLOBStore implements BLOBStore {
+        /**
+         * {@inheritDoc}
+         */
+        public String createId(PropertyId id, int index) {
+            // the blobId is a simple string concatenation of id plus index
+            StringBuffer sb = new StringBuffer();
+            sb.append(id.toString());
+            sb.append('[');
+            sb.append(index);
+            sb.append(']');
+            return sb.toString();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public synchronized InputStream get(String blobId) throws Exception {
+            PreparedStatement stmt = blobSelect;
+            try {
+                stmt.setString(1, blobId);
+                stmt.execute();
+                final ResultSet rs = stmt.getResultSet();
+                if (!rs.next()) {
+                    throw new Exception("no such BLOB: " + blobId);
+                }
+                final InputStream in = rs.getBinaryStream(1);
+
+                /**
+                 * return an InputStream wrapper in order to
+                 * close the ResultSet when the stream is closed
+                 */
+                return new InputStream() {
+                    public int read() throws IOException {
+                        return in.read();
+                    }
+
+                    public void close() throws IOException {
+                        in.close();
+                        // close ResultSet
+                        closeResultSet(rs);
+                    }
+
+                    public int available() throws IOException {
+                        return in.available();
+                    }
+
+                    public void mark(int readlimit) {
+                        in.mark(readlimit);
+                    }
+
+                    public boolean markSupported() {
+                        return in.markSupported();
+                    }
+
+                    public int read(byte b[]) throws IOException {
+                        return in.read(b);
+                    }
+
+                    public int read(byte b[], int off, int len) throws IOException {
+                        return in.read(b, off, len);
+                    }
+
+                    public void reset() throws IOException {
+                        in.reset();
+                    }
+
+                    public long skip(long n) throws IOException {
+                        return in.skip(n);
+                    }
+                };
+            } finally {
+                resetStatement(stmt);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public synchronized void put(String blobId, InputStream in, long size)
+                throws Exception {
+            PreparedStatement stmt = blobSelectExist;
+            try {
+                stmt.setString(1, blobId);
+                stmt.execute();
+                ResultSet rs = stmt.getResultSet();
+                // a BLOB exists if the result has at least one entry
+                boolean exists = rs.next();
+                resetStatement(stmt);
+                closeResultSet(rs);
+
+                stmt = (exists) ? blobUpdate : blobInsert;
+                stmt.setBinaryStream(1, in, (int) size);
+                stmt.setString(2, blobId);
+                stmt.executeUpdate();
+            } finally {
+                resetStatement(stmt);
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public synchronized boolean remove(String blobId) throws Exception {
+            PreparedStatement stmt = blobDelete;
+            try {
+                stmt.setString(1, blobId);
+                return stmt.executeUpdate() == 1;
+            } finally {
+                resetStatement(stmt);
             }
         }
     }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/daffodil.ddl
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/daffodil.ddl?rev=329224&r1=329223&r2=329224&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/daffodil.ddl (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/daffodil.ddl Fri Oct 28 08:29:07 2005
@@ -4,3 +4,5 @@
 create index ${schemaObjectPrefix}PROP_IDX on ${schemaObjectPrefix}PROP (PROP_ID)
 create table ${schemaObjectPrefix}REFS (NODE_ID char(36) not null, REFS_DATA blob not null)
 create index ${schemaObjectPrefix}REFS_IDX on ${schemaObjectPrefix}REFS (NODE_ID)
+create table ${schemaObjectPrefix}BINVAL (BINVAL_ID varchar(1024) not null, BINVAL_DATA blob not null)
+create index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/default.ddl
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/default.ddl?rev=329224&r1=329223&r2=329224&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/default.ddl (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/default.ddl Fri Oct 28 08:29:07 2005
@@ -4,3 +4,5 @@
 create unique index ${schemaObjectPrefix}PROP_IDX on ${schemaObjectPrefix}PROP (PROP_ID)
 create table ${schemaObjectPrefix}REFS (NODE_ID char(36) not null, REFS_DATA varbinary not null)
 create unique index ${schemaObjectPrefix}REFS_IDX on ${schemaObjectPrefix}REFS (NODE_ID)
+create table ${schemaObjectPrefix}BINVAL (BINVAL_ID varchar not null, BINVAL_DATA varbinary not null)
+create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/derby.ddl
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/derby.ddl?rev=329224&r1=329223&r2=329224&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/derby.ddl (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/derby.ddl Fri Oct 28 08:29:07 2005
@@ -4,3 +4,5 @@
 create unique index ${schemaObjectPrefix}PROP_IDX on ${schemaObjectPrefix}PROP (PROP_ID)
 create table ${schemaObjectPrefix}REFS (NODE_ID char(36) not null, REFS_DATA blob not null)
 create unique index ${schemaObjectPrefix}REFS_IDX on ${schemaObjectPrefix}REFS (NODE_ID)
+create table ${schemaObjectPrefix}BINVAL (BINVAL_ID varchar(1024) not null, BINVAL_DATA blob(100M) not null)
+create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/mssql.ddl
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/mssql.ddl?rev=329224&r1=329223&r2=329224&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/mssql.ddl (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/mssql.ddl Fri Oct 28 08:29:07 2005
@@ -4,3 +4,5 @@
 create unique index ${schemaObjectPrefix}PROP_IDX on ${schemaObjectPrefix}PROP (PROP_ID)
 create table ${schemaObjectPrefix}REFS (NODE_ID char(36) not null, REFS_DATA varbinary not null)
 create unique index ${schemaObjectPrefix}REFS_IDX on ${schemaObjectPrefix}REFS (NODE_ID)
+create cached table ${schemaObjectPrefix}BINVAL (BINVAL_ID varchar not null, BINVAL_DATA image not null)
+create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/mysql.ddl
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/mysql.ddl?rev=329224&r1=329223&r2=329224&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/mysql.ddl (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/db/mysql.ddl Fri Oct 28 08:29:07 2005
@@ -4,3 +4,5 @@
 create unique index ${schemaObjectPrefix}PROP_IDX on ${schemaObjectPrefix}PROP (PROP_ID)
 create table ${schemaObjectPrefix}REFS (NODE_ID char(36) not null, REFS_DATA blob not null)
 create unique index ${schemaObjectPrefix}REFS_IDX on ${schemaObjectPrefix}REFS (NODE_ID)
+create table ${schemaObjectPrefix}BINVAL (BINVAL_ID varchar(255) not null, BINVAL_DATA longblob not null)
+create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java?rev=329224&r1=329223&r2=329224&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java Fri Oct 28 08:29:07 2005
@@ -31,8 +31,9 @@
 import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.core.state.PMContext;
 import org.apache.jackrabbit.core.state.PropertyState;
-import org.apache.jackrabbit.core.state.obj.BLOBStore;
-import org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager;
+import org.apache.jackrabbit.core.state.util.BLOBStore;
+import org.apache.jackrabbit.core.state.util.FileSystemBLOBStore;
+import org.apache.jackrabbit.core.state.util.Serializer;
 import org.apache.jackrabbit.core.value.BLOBFileValue;
 import org.apache.jackrabbit.core.value.InternalValue;
 import org.apache.jackrabbit.name.QName;
@@ -46,8 +47,6 @@
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -56,7 +55,7 @@
  * <code>InMemPersistenceManager</code> is a very simple <code>HashMap</code>-based
  * <code>PersistenceManager</code> for Jackrabbit that keeps all data in memory
  * and that is capable of storing and loading its contents using a simple custom
- * serialization format.
+ * binary serialization format (see {@link Serializer}).
  * <p/>
  * It is configured through the following properties:
  * <ul>
@@ -68,8 +67,7 @@
  * </ul>
  * <b>Please note that this class should only be used for testing purposes.</b>
  */
-public class InMemPersistenceManager extends AbstractPersistenceManager
-        implements BLOBStore {
+public class InMemPersistenceManager extends AbstractPersistenceManager {
 
     private static Logger log = Logger.getLogger(InMemPersistenceManager.class);
 
@@ -89,6 +87,8 @@
 
     // file system where BLOB data is stored
     protected FileSystem blobFS;
+    // BLOBStore that manages BLOB data in the file system
+    protected BLOBStore blobStore;
 
     /**
      * file system where the content of the hash maps are read from/written to
@@ -193,9 +193,9 @@
                     String s = in.readUTF();    // id
                     id = NodeId.valueOf(s);
                 } else {
-                    // entry type: node
+                    // entry type: property
                     String s = in.readUTF();    // id
-                    id = NodeId.valueOf(s);
+                    id = PropertyId.valueOf(s);
                 }
                 int length = in.readInt();  // data length
                 byte[] data = new byte[length];
@@ -283,48 +283,6 @@
         }
     }
 
-    //------------------------------------------------------------< BLOBStore >
-    /**
-     * {@inheritDoc}
-     */
-    public FileSystemResource get(String blobId) throws Exception {
-        return new FileSystemResource(blobFS, blobId);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public String put(PropertyId id, int index, InputStream in, long size) throws Exception {
-        String path = buildBlobFilePath(id.getParentUUID(), id.getName(), index);
-        OutputStream out = null;
-        FileSystemResource internalBlobFile = new FileSystemResource(blobFS, path);
-        internalBlobFile.makeParentDirs();
-        try {
-            out = new BufferedOutputStream(internalBlobFile.getOutputStream());
-            byte[] buffer = new byte[8192];
-            int read;
-            while ((read = in.read(buffer)) > 0) {
-                out.write(buffer, 0, read);
-            }
-        } finally {
-            out.close();
-        }
-        return path;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean remove(String blobId) throws Exception {
-        FileSystemResource res = new FileSystemResource(blobFS, blobId);
-        if (!res.exists()) {
-            return false;
-        }
-        // delete resource and prune empty parent folders
-        res.delete(true);
-        return true;
-    }
-
     //---------------------------------------------------< PersistenceManager >
     /**
      * {@inheritDoc}
@@ -340,13 +298,14 @@
         wspFS = context.getFileSystem();
 
         /**
-         * store blob's in local file system in a sub directory
+         * store BLOB data in local file system in a sub directory
          * of the workspace home directory
          */
         LocalFileSystem blobFS = new LocalFileSystem();
         blobFS.setRoot(new File(context.getHomeDir(), "blobs"));
         blobFS.init();
         this.blobFS = blobFS;
+        blobStore = new FileSystemBLOBStore(blobFS);
 
         if (persistent) {
             // deserialize contents of state and refs stores
@@ -369,7 +328,7 @@
                 // serialize contents of state and refs stores
                 storeContents();
             } else {
-                // clean out blob store
+                // clear out blob store
                 try {
                     String[] folders = blobFS.listFolders("/");
                     for (int i = 0; i < folders.length; i++) {
@@ -384,9 +343,10 @@
                 }
             }
 
-            // close blob store
+            // close BLOB file system
             blobFS.close();
             blobFS = null;
+            blobStore = null;
 
             stateStore.clear();
             stateStore = null;
@@ -415,7 +375,7 @@
         ByteArrayInputStream in = new ByteArrayInputStream(data);
         try {
             NodeState state = createNew(id);
-            ObjectPersistenceManager.deserialize(state, in);
+            Serializer.deserialize(state, in);
             return state;
         } catch (Exception e) {
             String msg = "failed to read node state: " + id;
@@ -442,7 +402,7 @@
         ByteArrayInputStream in = new ByteArrayInputStream(data);
         try {
             PropertyState state = createNew(id);
-            ObjectPersistenceManager.deserialize(state, in, this);
+            Serializer.deserialize(state, in, blobStore);
             return state;
         } catch (Exception e) {
             String msg = "failed to read property state: " + id;
@@ -460,9 +420,10 @@
         }
 
         try {
-            ByteArrayOutputStream out = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
             // serialize node state
-            ObjectPersistenceManager.serialize(state, out);
+            Serializer.serialize(state, out);
 
             // store in serialized format in map for better memory efficiency
             stateStore.put(state.getId(), out.toByteArray());
@@ -484,9 +445,10 @@
         }
 
         try {
-            ByteArrayOutputStream out = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
             // serialize property state
-            ObjectPersistenceManager.serialize(state, out, this);
+            Serializer.serialize(state, out, blobStore);
 
             // store in serialized format in map for better memory efficiency
             stateStore.put(state.getId(), out.toByteArray());
@@ -556,7 +518,7 @@
         ByteArrayInputStream in = new ByteArrayInputStream(data);
         try {
             NodeReferences refs = new NodeReferences(id);
-            ObjectPersistenceManager.deserialize(refs, in);
+            Serializer.deserialize(refs, in);
             return refs;
         } catch (Exception e) {
             String msg = "failed to load references: " + id.getUUID();
@@ -574,9 +536,10 @@
         }
 
         try {
-            ByteArrayOutputStream out = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
             // serialize references
-            ObjectPersistenceManager.serialize(refs, out);
+            Serializer.serialize(refs, out);
 
             // store in serialized format in map for better memory efficiency
             stateStore.put(refs.getTargetId(), out.toByteArray());

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/obj/ObjectPersistenceManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/obj/ObjectPersistenceManager.java?rev=329224&r1=329223&r2=329224&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/obj/ObjectPersistenceManager.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/obj/ObjectPersistenceManager.java Fri Oct 28 08:29:07 2005
@@ -21,11 +21,8 @@
 import org.apache.jackrabbit.core.fs.BasedFileSystem;
 import org.apache.jackrabbit.core.fs.FileSystem;
 import org.apache.jackrabbit.core.fs.FileSystemException;
-import org.apache.jackrabbit.core.fs.FileSystemPathUtil;
 import org.apache.jackrabbit.core.fs.FileSystemResource;
 import org.apache.jackrabbit.core.fs.local.LocalFileSystem;
-import org.apache.jackrabbit.core.nodetype.NodeDefId;
-import org.apache.jackrabbit.core.nodetype.PropDefId;
 import org.apache.jackrabbit.core.state.AbstractPersistenceManager;
 import org.apache.jackrabbit.core.state.ItemStateException;
 import org.apache.jackrabbit.core.state.NoSuchItemStateException;
@@ -34,6 +31,9 @@
 import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.core.state.PMContext;
 import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.state.util.BLOBStore;
+import org.apache.jackrabbit.core.state.util.FileSystemBLOBStore;
+import org.apache.jackrabbit.core.state.util.Serializer;
 import org.apache.jackrabbit.core.value.BLOBFileValue;
 import org.apache.jackrabbit.core.value.InternalValue;
 import org.apache.jackrabbit.name.QName;
@@ -42,36 +42,22 @@
 import javax.jcr.PropertyType;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
 import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
 
 /**
  * <code>ObjectPersistenceManager</code> is a <code>FileSystem</code>-based
  * <code>PersistenceManager</code> that persists <code>ItemState</code>
- * and <code>NodeReferences</code> objects using a simple custom serialization
- * format.
+ * and <code>NodeReferences</code> objects using a simple custom binary
+ * serialization format (see {@link Serializer}).
  */
-public class ObjectPersistenceManager extends AbstractPersistenceManager
-        implements BLOBStore {
+public class ObjectPersistenceManager extends AbstractPersistenceManager {
 
     private static Logger log = Logger.getLogger(ObjectPersistenceManager.class);
 
     /**
-     * encoding used for serializing String values
-     */
-    private static final String ENCODING = "UTF-8";
-
-    /**
      * hexdigits for toString
      */
     private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
@@ -86,6 +72,8 @@
     private FileSystem itemStateFS;
     // file system where BLOB data is stored
     private FileSystem blobFS;
+    // BLOBStore that manages BLOB data in the file system
+    private BLOBStore blobStore;
 
     /**
      * Creates a new <code>ObjectPersistenceManager</code> instance.
@@ -134,11 +122,6 @@
         return buildNodeFolderPath(parentUUID) + FileSystem.SEPARATOR + fileName;
     }
 
-    private static String buildBlobFilePath(String parentUUID, QName propName, int i) {
-        return buildNodeFolderPath(parentUUID) + FileSystem.SEPARATOR
-                + FileSystemPathUtil.escapeName(propName.toString()) + "." + i + ".bin";
-    }
-
     private static String buildNodeFilePath(String uuid) {
         return buildNodeFolderPath(uuid) + FileSystem.SEPARATOR + NODEFILENAME;
     }
@@ -147,305 +130,6 @@
         return buildNodeFolderPath(uuid) + FileSystem.SEPARATOR + NODEREFSFILENAME;
     }
 
-    //------------------------------------------------< static helper methods >
-    /**
-     * Serializes the specified <code>state</code> object to the given
-     * <code>stream</code>.
-     *
-     * @param state  <code>state</code> to serialize
-     * @param stream the stream where the <code>state</code> should be serialized to
-     * @throws Exception if an error occurs during the serialization
-     * @see #deserialize(NodeState, InputStream)
-     */
-    public static void serialize(NodeState state, OutputStream stream)
-            throws Exception {
-        DataOutputStream out = new DataOutputStream(stream);
-        // uuid
-        out.writeUTF(state.getUUID());
-        // primaryType
-        out.writeUTF(state.getNodeTypeName().toString());
-        // parentUUID
-        out.writeUTF(state.getParentUUID() == null ? "" : state.getParentUUID());
-        // definitionId
-        out.writeUTF(state.getDefinitionId().toString());
-        // mixin types
-        Collection c = state.getMixinTypeNames();
-        out.writeInt(c.size()); // count
-        for (Iterator iter = c.iterator(); iter.hasNext();) {
-            out.writeUTF(iter.next().toString());   // name
-        }
-        // properties (names)
-        c = state.getPropertyNames();
-        out.writeInt(c.size()); // count
-        for (Iterator iter = c.iterator(); iter.hasNext();) {
-            QName propName = (QName) iter.next();
-            out.writeUTF(propName.toString());   // name
-        }
-        // child nodes (list of name/uuid pairs)
-        c = state.getChildNodeEntries();
-        out.writeInt(c.size()); // count
-        for (Iterator iter = c.iterator(); iter.hasNext();) {
-            NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) iter.next();
-            out.writeUTF(entry.getName().toString());   // name
-            out.writeUTF(entry.getUUID());  // uuid
-        }
-    }
-
-    /**
-     * Deserializes a <code>state</code> object from the given <code>stream</code>.
-     *
-     * @param state  <code>state</code> to deserialize
-     * @param stream the stream where the <code>state</code> should be deserialized from
-     * @throws Exception if an error occurs during the deserialization
-     * @see #serialize(NodeState, OutputStream)
-     */
-    public static void deserialize(NodeState state, InputStream stream)
-            throws Exception {
-        DataInputStream in = new DataInputStream(stream);
-        // check uuid
-        String s = in.readUTF();
-        if (!state.getUUID().equals(s)) {
-            String msg = "invalid serialized state: uuid mismatch";
-            log.debug(msg);
-            throw new ItemStateException(msg);
-        }
-
-        // deserialize node state
-
-        // primaryType
-        s = in.readUTF();
-        state.setNodeTypeName(QName.valueOf(s));
-        // parentUUID
-        s = in.readUTF();
-        if (s.length() > 0) {
-            state.setParentUUID(s);
-        }
-        // definitionId
-        s = in.readUTF();
-        state.setDefinitionId(NodeDefId.valueOf(s));
-        // mixin types
-        int count = in.readInt();   // count
-        Set set = new HashSet(count);
-        for (int i = 0; i < count; i++) {
-            set.add(QName.valueOf(in.readUTF())); // name
-        }
-        if (set.size() > 0) {
-            state.setMixinTypeNames(set);
-        }
-        // properties (names)
-        count = in.readInt();   // count
-        for (int i = 0; i < count; i++) {
-            state.addPropertyName(QName.valueOf(in.readUTF())); // name
-        }
-        // child nodes (list of name/uuid pairs)
-        count = in.readInt();   // count
-        for (int i = 0; i < count; i++) {
-            QName name = QName.valueOf(in.readUTF());    // name
-            String s1 = in.readUTF();   // uuid
-            state.addChildNodeEntry(name, s1);
-        }
-    }
-
-    /**
-     * Serializes the specified <code>state</code> object to the given
-     * <code>stream</code>.
-     *
-     * @param state     <code>state</code> to serialize
-     * @param stream    the stream where the <code>state</code> should be serialized to
-     * @param blobStore handler for blob data
-     * @throws Exception if an error occurs during the serialization
-     * @see #deserialize(PropertyState, InputStream, BLOBStore)
-     */
-    public static void serialize(PropertyState state,
-                                 OutputStream stream,
-                                 BLOBStore blobStore)
-            throws Exception {
-        DataOutputStream out = new DataOutputStream(stream);
-        // type
-        out.writeInt(state.getType());
-        // multiValued
-        out.writeBoolean(state.isMultiValued());
-        // definitionId
-        out.writeUTF(state.getDefinitionId().toString());
-        // values
-        InternalValue[] values = state.getValues();
-        out.writeInt(values.length); // count
-        for (int i = 0; i < values.length; i++) {
-            InternalValue val = values[i];
-            if (state.getType() == PropertyType.BINARY) {
-                // special handling required for binary value:
-                // spool binary value to file in blob store
-                BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
-                InputStream in = blobVal.getStream();
-                String blobId;
-                try {
-                    blobId = blobStore.put((PropertyId) state.getId(), i, in,
-                            blobVal.getLength());
-                } finally {
-                    try {
-                        in.close();
-                    } catch (IOException e) {
-                        // ignore
-                    }
-                }
-                // store id of blob as property value
-                out.writeUTF(blobId);   // value
-                // replace value instance with value
-                // backed by resource in blob store and delete temp file
-                values[i] = InternalValue.create(blobStore.get(blobId));
-                blobVal.discard();
-                blobVal = null; // gc hint
-            } else {
-                /**
-                 * because writeUTF(String) has a size limit of 65k,
-                 * we're using write(byte[]) instead
-                 */
-                //out.writeUTF(val.toString());   // value
-                byte[] bytes = val.toString().getBytes(ENCODING);
-                out.writeInt(bytes.length); // lenght of byte[]
-                out.write(bytes);   // byte[]
-            }
-        }
-    }
-
-    /**
-     * Deserializes a <code>state</code> object from the given <code>stream</code>.
-     *
-     * @param state     <code>state</code> to deserialize
-     * @param stream    the stream where the <code>state</code> should be deserialized from
-     * @param blobStore handler for blob data
-     * @throws Exception if an error occurs during the deserialization
-     * @see #serialize(PropertyState, OutputStream, BLOBStore)
-     */
-    public static void deserialize(PropertyState state,
-                                   InputStream stream,
-                                   BLOBStore blobStore)
-            throws Exception {
-        DataInputStream in = new DataInputStream(stream);
-
-        // type
-        int type = in.readInt();
-        state.setType(type);
-        // multiValued
-        boolean multiValued = in.readBoolean();
-        state.setMultiValued(multiValued);
-        // definitionId
-        String s = in.readUTF();
-        state.setDefinitionId(PropDefId.valueOf(s));
-        // values
-        int count = in.readInt();   // count
-        InternalValue[] values = new InternalValue[count];
-        for (int i = 0; i < count; i++) {
-            InternalValue val;
-            if (type == PropertyType.BINARY) {
-                s = in.readUTF();   // value (i.e. blobId)
-                // special handling required for binary value:
-                // the value stores the id of the blob resource in the blob store
-                val = InternalValue.create(blobStore.get(s));
-            } else {
-                /**
-                 * because writeUTF(String) has a size limit of 65k,
-                 * Strings are serialized as <length><byte[]>
-                 */
-                //s = in.readUTF();   // value
-                int len = in.readInt(); // lenght of byte[]
-                byte[] bytes = new byte[len];
-                in.read(bytes); // byte[]
-                s = new String(bytes, ENCODING);
-                val = InternalValue.valueOf(s, type);
-            }
-            values[i] = val;
-        }
-        state.setValues(values);
-    }
-
-    /**
-     * Serializes the specified <code>NodeReferences</code> object to the given
-     * <code>stream</code>.
-     *
-     * @param refs   object to serialize
-     * @param stream the stream where the object should be serialized to
-     * @throws IOException if an error occurs during the serialization
-     * @see #deserialize(NodeReferences, InputStream)
-     */
-    public static void serialize(NodeReferences refs, OutputStream stream)
-            throws IOException {
-        DataOutputStream out = new DataOutputStream(stream);
-
-        // references
-        Collection c = refs.getReferences();
-        out.writeInt(c.size()); // count
-        for (Iterator iter = c.iterator(); iter.hasNext();) {
-            PropertyId propId = (PropertyId) iter.next();
-            out.writeUTF(propId.toString());   // propertyId
-        }
-    }
-
-    /**
-     * Deserializes a <code>NodeReferences</code> object from the given
-     * <code>stream</code>.
-     *
-     * @param refs   object to deserialize
-     * @param stream the stream where the object should be deserialized from
-     * @throws Exception if an error occurs during the deserialization
-     * @see #serialize(NodeReferences, OutputStream)
-     */
-    public static void deserialize(NodeReferences refs, InputStream stream)
-            throws Exception {
-        DataInputStream in = new DataInputStream(stream);
-
-        refs.clearAllReferences();
-
-        // references
-        int count = in.readInt();   // count
-        for (int i = 0; i < count; i++) {
-            refs.addReference(PropertyId.valueOf(in.readUTF()));    // propertyId
-        }
-    }
-
-    //------------------------------------------------------------< BLOBStore >
-    /**
-     * {@inheritDoc}
-     */
-    public FileSystemResource get(String blobId) throws Exception {
-        return new FileSystemResource(blobFS, blobId);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public String put(PropertyId id, int index, InputStream in, long size)
-            throws Exception {
-        String path = buildBlobFilePath(id.getParentUUID(), id.getName(), index);
-        OutputStream out = null;
-        FileSystemResource internalBlobFile = new FileSystemResource(blobFS, path);
-        internalBlobFile.makeParentDirs();
-        try {
-            out = new BufferedOutputStream(internalBlobFile.getOutputStream());
-            byte[] buffer = new byte[8192];
-            int read;
-            while ((read = in.read(buffer)) > 0) {
-                out.write(buffer, 0, read);
-            }
-        } finally {
-            out.close();
-        }
-        return path;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean remove(String blobId) throws Exception {
-        FileSystemResource res = new FileSystemResource(blobFS, blobId);
-        if (!res.exists()) {
-            return false;
-        }
-        // delete resource and prune empty parent folders
-        res.delete(true);
-        return true;
-    }
-
     //---------------------------------------------------< PersistenceManager >
     /**
      * {@inheritDoc}
@@ -459,13 +143,14 @@
         itemStateFS = new BasedFileSystem(wspFS, "/data");
 
         /**
-         * store blob's in local file system in a sub directory
+         * store BLOB data in local file system in a sub directory
          * of the workspace home directory
          */
         LocalFileSystem blobFS = new LocalFileSystem();
         blobFS.setRoot(new File(context.getHomeDir(), "blobs"));
         blobFS.init();
         this.blobFS = blobFS;
+        blobStore = new FileSystemBLOBStore(blobFS);
 
         initialized = true;
     }
@@ -479,9 +164,10 @@
         }
 
         try {
-            // close blob store
+            // close BLOB file system
             blobFS.close();
             blobFS = null;
+            blobStore = null;
             /**
              * there's no need close the item state store because it
              * is based in the workspace's file system which is
@@ -518,7 +204,7 @@
                     new BufferedInputStream(itemStateFS.getInputStream(nodeFilePath));
             try {
                 NodeState state = createNew(id);
-                deserialize(state, in);
+                Serializer.deserialize(state, in);
                 return state;
             } catch (Exception e) {
                 String msg = "failed to read node state: " + id.getUUID();
@@ -560,7 +246,7 @@
                     new BufferedInputStream(itemStateFS.getInputStream(propFilePath));
             try {
                 PropertyState state = createNew(id);
-                deserialize(state, in, this);
+                Serializer.deserialize(state, in, blobStore);
                 return state;
             } finally {
                 in.close();
@@ -599,7 +285,7 @@
                     new BufferedInputStream(itemStateFS.getInputStream(refsFilePath));
             try {
                 NodeReferences refs = new NodeReferences(id);
-                deserialize(refs, in);
+                Serializer.deserialize(refs, in);
                 return refs;
             } finally {
                 in.close();
@@ -627,7 +313,7 @@
             BufferedOutputStream out = new BufferedOutputStream(nodeFile.getOutputStream());
             try {
                 // serialize node state
-                serialize(state, out);
+                Serializer.serialize(state, out);
                 return;
             } finally {
                 out.close();
@@ -654,7 +340,7 @@
             BufferedOutputStream out = new BufferedOutputStream(propFile.getOutputStream());
             try {
                 // serialize property state
-                serialize(state, out, this);
+                Serializer.serialize(state, out, blobStore);
                 return;
             } finally {
                 out.close();
@@ -681,7 +367,7 @@
             refsFile.makeParentDirs();
             OutputStream out = new BufferedOutputStream(refsFile.getOutputStream());
             try {
-                serialize(refs, out);
+                Serializer.serialize(refs, out);
             } finally {
                 out.close();
             }

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/BLOBStore.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/BLOBStore.java?rev=329224&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/BLOBStore.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/BLOBStore.java Fri Oct 28 08:29:07 2005
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.state.util;
+
+import org.apache.jackrabbit.core.PropertyId;
+
+import java.io.InputStream;
+
+/**
+ * <code>BLOBStore</code> represents an abstract store for binary property
+ * values (BLOBs).
+ *
+ * @see ResourceBasedBLOBStore
+ */
+public interface BLOBStore {
+    /**
+     * Creates a unique identifier for the BLOB data associated with the given
+     * property id and value subscript.
+     *
+     * @param id    id of the property associated with the BLOB data
+     * @param index subscript of the value holding the BLOB data
+     * @return a string identifying the BLOB data
+     */
+    String createId(PropertyId id, int index);
+
+    /**
+     * Stores the BLOB data and returns a unique identifier.
+     *
+     * @param blobId identifier of the BLOB data as returned by
+     *               {@link #createId(PropertyId, int)}
+     * @param in     stream containing the BLOB data
+     * @param size   size of the BLOB data
+     * @throws Exception if an error occured
+     */
+    void put(String blobId, InputStream in, long size) throws Exception;
+
+    /**
+     * Retrieves the BLOB data with the specified id as a binary stream.
+     *
+     * @param blobId identifier of the BLOB data as returned by
+     *               {@link #createId(PropertyId, int)}
+     * @return an input stream that delivers the BLOB data
+     * @throws Exception if an error occured
+     */
+    InputStream get(String blobId) throws Exception;
+
+    /**
+     * Removes the BLOB data with the specified id.
+     *
+     * @param blobId identifier of the BLOB data as returned by
+     *               {@link #createId(PropertyId, int)}
+     * @return <code>true</code> if BLOB data with the given id exists and has
+     *         been successfully removed, <code>false</code> if there's no BLOB
+     *         data with the given id.
+     * @throws Exception if an error occured
+     */
+    boolean remove(String blobId) throws Exception;
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/BLOBStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/BLOBStore.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/FileSystemBLOBStore.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/FileSystemBLOBStore.java?rev=329224&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/FileSystemBLOBStore.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/FileSystemBLOBStore.java Fri Oct 28 08:29:07 2005
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.state.util;
+
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.fs.FileSystem;
+import org.apache.jackrabbit.core.fs.FileSystemPathUtil;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
+
+import java.io.BufferedOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * <code>FileSystemBLOBStore</code> is a <code>ResourceBasedBLOBStore</code>
+ * implementation that stores BLOB data in a <code>FileSystem</code>.
+ */
+public class FileSystemBLOBStore implements ResourceBasedBLOBStore {
+
+    /**
+     * the file system where the BLOBs are stored
+     */
+    private final FileSystem fs;
+
+    /**
+     * Creates a new <code>FileSystemBLOBStore</code> instance.
+     *
+     * @param fs file system for storing the BLOB data
+     */
+    public FileSystemBLOBStore(FileSystem fs) {
+        this.fs = fs;
+    }
+
+    //------------------------------------------------------------< BLOBStore >
+    /**
+     * {@inheritDoc}
+     */
+    public String createId(PropertyId id, int index) {
+        // the blobId is a file system path
+        StringBuffer sb = new StringBuffer();
+        char[] chars = id.getParentUUID().toCharArray();
+        int cnt = 0;
+        for (int i = 0; i < chars.length; i++) {
+            if (chars[i] == '-') {
+                continue;
+            }
+            //if (cnt > 0 && cnt % 4 == 0) {
+            if (cnt == 2 || cnt == 4) {
+                sb.append(FileSystem.SEPARATOR_CHAR);
+            }
+            sb.append(chars[i]);
+            cnt++;
+        }
+        sb.append(FileSystem.SEPARATOR_CHAR);
+        sb.append(FileSystemPathUtil.escapeName(id.getName().toString()));
+        sb.append('.');
+        sb.append(index);
+        sb.append(".bin");
+        return sb.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public InputStream get(String blobId) throws Exception {
+        return getResource(blobId).getInputStream();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void put(String blobId, InputStream in, long size) throws Exception {
+        OutputStream out = null;
+        // the blobId is a file system path
+        FileSystemResource internalBlobFile = new FileSystemResource(fs, blobId);
+        internalBlobFile.makeParentDirs();
+        try {
+            out = new BufferedOutputStream(internalBlobFile.getOutputStream());
+            byte[] buffer = new byte[8192];
+            int read;
+            while ((read = in.read(buffer)) > 0) {
+                out.write(buffer, 0, read);
+            }
+        } finally {
+            out.close();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean remove(String blobId) throws Exception {
+        FileSystemResource res = new FileSystemResource(fs, blobId);
+        if (!res.exists()) {
+            return false;
+        }
+        // delete resource and prune empty parent folders
+        res.delete(true);
+        return true;
+    }
+
+    //-----------------------------------------------< ResourceBasedBLOBStore >
+    /**
+     * {@inheritDoc}
+     */
+    public FileSystemResource getResource(String blobId)
+            throws Exception {
+        return new FileSystemResource(fs, blobId);
+    }
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/FileSystemBLOBStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/FileSystemBLOBStore.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/ResourceBasedBLOBStore.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/ResourceBasedBLOBStore.java?rev=329224&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/ResourceBasedBLOBStore.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/ResourceBasedBLOBStore.java Fri Oct 28 08:29:07 2005
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.state.util;
+
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
+
+/**
+ * <code>ResourceBasedBLOBStore</code> extends the <code>BLOBStore</code>
+ * interface with the method {@link #getResource(String)}
+ */
+public interface ResourceBasedBLOBStore extends BLOBStore {
+    /**
+     * Retrieves the BLOB data with the specified id as a permanent resource.
+     *
+     * @param blobId identifier of the BLOB data as returned by
+     *               {@link #createId(PropertyId, int)}
+     * @return a resource representing the BLOB data
+     * @throws Exception if an error occured
+     */
+    FileSystemResource getResource(String blobId) throws Exception;
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/ResourceBasedBLOBStore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/ResourceBasedBLOBStore.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/Serializer.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/Serializer.java?rev=329224&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/Serializer.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/Serializer.java Fri Oct 28 08:29:07 2005
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed 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.state.util;
+
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
+import org.apache.jackrabbit.core.nodetype.NodeDefId;
+import org.apache.jackrabbit.core.nodetype.PropDefId;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.value.BLOBFileValue;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.uuid.UUID;
+
+import javax.jcr.PropertyType;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * <code>Serializer</code> is a utility class that provides static methods
+ * for serializing & deserializing <code>ItemState</code> and
+ * <code>NodeReferences</code> objects using a simple binary serialization
+ * format.
+ */
+public final class Serializer {
+
+    private static final UUID NULL_UUID_PLACEHOLDER =
+            UUID.fromString("00000000-0000-0000-0000-000000000000");
+
+    /**
+     * encoding used for serializing String values
+     */
+    private static final String ENCODING = "UTF-8";
+
+    /**
+     * Serializes the specified <code>NodeState</code> object to the given
+     * binary <code>stream</code>.
+     *
+     * @param state  <code>state</code> to serialize
+     * @param stream the stream where the <code>state</code> should be
+     *               serialized to
+     * @throws Exception if an error occurs during the serialization
+     * @see #deserialize(NodeState, InputStream)
+     */
+    public static void serialize(NodeState state, OutputStream stream)
+            throws Exception {
+        DataOutputStream out = new DataOutputStream(stream);
+
+        // primaryType
+        out.writeUTF(state.getNodeTypeName().toString());
+        // parentUUID
+        if (state.getParentUUID() == null) {
+            out.write(NULL_UUID_PLACEHOLDER.getRawBytes());
+        } else {
+            out.write(UUID.fromString(state.getParentUUID()).getRawBytes());
+        }
+        // definitionId
+        out.writeUTF(state.getDefinitionId().toString());
+        // mixin types
+        Collection c = state.getMixinTypeNames();
+        out.writeInt(c.size()); // count
+        for (Iterator iter = c.iterator(); iter.hasNext();) {
+            out.writeUTF(iter.next().toString());   // name
+        }
+        // modCount @todo store real modCount
+        out.writeInt(0);
+        // properties (names)
+        c = state.getPropertyNames();
+        out.writeInt(c.size()); // count
+        for (Iterator iter = c.iterator(); iter.hasNext();) {
+            QName propName = (QName) iter.next();
+            out.writeUTF(propName.toString());   // name
+        }
+        // child nodes (list of name/uuid pairs)
+        c = state.getChildNodeEntries();
+        out.writeInt(c.size()); // count
+        for (Iterator iter = c.iterator(); iter.hasNext();) {
+            NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) iter.next();
+            out.writeUTF(entry.getName().toString());   // name
+            out.write(UUID.fromString(entry.getUUID()).getRawBytes());  // uuid
+        }
+    }
+
+    /**
+     * Deserializes a <code>NodeState</code> object from the given binary
+     * <code>stream</code>.
+     *
+     * @param state  <code>state</code> to deserialize
+     * @param stream the stream where the <code>state</code> should be deserialized from
+     * @throws Exception if an error occurs during the deserialization
+     * @see #serialize(NodeState, OutputStream)
+     */
+    public static void deserialize(NodeState state, InputStream stream)
+            throws Exception {
+        DataInputStream in = new DataInputStream(stream);
+
+        // primaryType
+        String s = in.readUTF();
+        state.setNodeTypeName(QName.valueOf(s));
+        // parentUUID (may be null)
+        byte[] uuidBytes = new byte[UUID.UUID_BYTE_LENGTH];
+        in.read(uuidBytes);
+        UUID uuid = new UUID(uuidBytes);
+        if (!uuid.equals(NULL_UUID_PLACEHOLDER)) {
+            state.setParentUUID(uuid.toString());
+        }
+        // definitionId
+        s = in.readUTF();
+        state.setDefinitionId(NodeDefId.valueOf(s));
+        // mixin types
+        int count = in.readInt();   // count
+        Set set = new HashSet(count);
+        for (int i = 0; i < count; i++) {
+            set.add(QName.valueOf(in.readUTF())); // name
+        }
+        if (set.size() > 0) {
+            state.setMixinTypeNames(set);
+        }
+        // modCount
+        int modCount = in.readInt();
+        // @todo set modCount
+        //state.setModCount(modCount);
+        // properties (names)
+        count = in.readInt();   // count
+        for (int i = 0; i < count; i++) {
+            state.addPropertyName(QName.valueOf(in.readUTF())); // name
+        }
+        // child nodes (list of name/uuid pairs)
+        count = in.readInt();   // count
+        for (int i = 0; i < count; i++) {
+            QName name = QName.valueOf(in.readUTF());    // name
+            // uuid
+            in.read(uuidBytes);
+            state.addChildNodeEntry(name, new UUID(uuidBytes).toString());
+        }
+    }
+
+    /**
+     * Serializes the specified <code>PropertyState</code> object to the given
+     * binary <code>stream</code>. Binary values are stored in the specified
+     * <code>BLOBStore</code>.
+     *
+     * @param state     <code>state</code> to serialize
+     * @param stream    the stream where the <code>state</code> should be
+     *                  serialized to
+     * @param blobStore handler for BLOB data
+     * @throws Exception if an error occurs during the serialization
+     * @see #deserialize(PropertyState, InputStream, BLOBStore)
+     */
+    public static void serialize(PropertyState state,
+                                 OutputStream stream,
+                                 BLOBStore blobStore)
+            throws Exception {
+        DataOutputStream out = new DataOutputStream(stream);
+
+        // type
+        out.writeInt(state.getType());
+        // multiValued
+        out.writeBoolean(state.isMultiValued());
+        // definitionId
+        out.writeUTF(state.getDefinitionId().toString());
+        // modCount @todo store real modCount
+        out.writeInt(0);
+        // values
+        InternalValue[] values = state.getValues();
+        out.writeInt(values.length); // count
+        for (int i = 0; i < values.length; i++) {
+            InternalValue val = values[i];
+            if (state.getType() == PropertyType.BINARY) {
+                // special handling required for binary value:
+                // put binary value in BLOB store
+                BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
+                InputStream in = blobVal.getStream();
+                String blobId = blobStore.createId((PropertyId) state.getId(), i);
+                try {
+                    blobStore.put(blobId, in, blobVal.getLength());
+                } finally {
+                    try {
+                        in.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+                // store id of BLOB as property value
+                out.writeUTF(blobId);   // value
+                // replace value instance with value backed by resource
+                // in BLOB store and discard old value instance (e.g. temp file)
+                values[i] = InternalValue.create(blobStore.get(blobId));
+                blobVal.discard();
+            } else {
+                /**
+                 * because writeUTF(String) has a size limit of 65k,
+                 */
+                //out.writeUTF(val.toString());   // value
+                byte[] bytes = val.toString().getBytes(ENCODING);
+                out.writeInt(bytes.length); // lenght of byte[]
+                out.write(bytes);   // byte[]
+            }
+        }
+    }
+
+    /**
+     * Deserializes a <code>PropertyState</code> object from the given binary
+     * <code>stream</code>. Binary values are retrieved from the specified
+     * <code>BLOBStore</code>.
+     *
+     * @param state     <code>state</code> to deserialize
+     * @param stream    the stream where the <code>state</code> should be
+     *                  deserialized from
+     * @param blobStore handler for BLOB data
+     * @throws Exception if an error occurs during the deserialization
+     * @see #serialize(PropertyState, OutputStream, BLOBStore)
+     */
+    public static void deserialize(PropertyState state,
+                                   InputStream stream,
+                                   BLOBStore blobStore)
+            throws Exception {
+        DataInputStream in = new DataInputStream(stream);
+
+        // type
+        int type = in.readInt();
+        state.setType(type);
+        // multiValued
+        boolean multiValued = in.readBoolean();
+        state.setMultiValued(multiValued);
+        // definitionId
+        String s = in.readUTF();
+        state.setDefinitionId(PropDefId.valueOf(s));
+        // modCount
+        int modCount = in.readInt();
+        // @todo set modCount
+        //state.setModCount(modCount);
+        // values
+        int count = in.readInt();   // count
+        InternalValue[] values = new InternalValue[count];
+        for (int i = 0; i < count; i++) {
+            InternalValue val;
+            if (type == PropertyType.BINARY) {
+                s = in.readUTF();   // value (i.e. blobId)
+                // special handling required for binary value:
+                // the value stores the id of the BLOB data
+                // in the BLOB store
+                if (blobStore instanceof ResourceBasedBLOBStore) {
+                    // optimization: if the BLOB store is resource-based
+                    // retrieve the resource directly rather than having
+                    // to read the BLOB from an input stream
+                    FileSystemResource fsRes =
+                            ((ResourceBasedBLOBStore) blobStore).getResource(s);
+                    val = InternalValue.create(fsRes);
+                } else {
+                    val = InternalValue.create(blobStore.get(s));
+                }
+            } else {
+                /**
+                 * because writeUTF(String) has a size limit of 65k,
+                 * Strings are serialized as <length><byte[]>
+                 */
+                //s = in.readUTF();   // value
+                int len = in.readInt(); // lenght of byte[]
+                byte[] bytes = new byte[len];
+                in.read(bytes); // byte[]
+                s = new String(bytes, ENCODING);
+                val = InternalValue.valueOf(s, type);
+            }
+            values[i] = val;
+        }
+        state.setValues(values);
+    }
+
+    /**
+     * Serializes the specified <code>NodeReferences</code> object to the given
+     * binary <code>stream</code>.
+     *
+     * @param refs   object to serialize
+     * @param stream the stream where the object should be serialized to
+     * @throws Exception if an error occurs during the serialization
+     * @see #deserialize(NodeReferences, InputStream)
+     */
+    public static void serialize(NodeReferences refs, OutputStream stream)
+            throws Exception {
+        DataOutputStream out = new DataOutputStream(stream);
+
+        // references
+        Collection c = refs.getReferences();
+        out.writeInt(c.size()); // count
+        for (Iterator iter = c.iterator(); iter.hasNext();) {
+            PropertyId propId = (PropertyId) iter.next();
+            out.writeUTF(propId.toString());   // propertyId
+        }
+    }
+
+    /**
+     * Deserializes a <code>NodeReferences</code> object from the given
+     * binary <code>stream</code>.
+     *
+     * @param refs   object to deserialize
+     * @param stream the stream where the object should be deserialized from
+     * @throws Exception if an error occurs during the deserialization
+     * @see #serialize(NodeReferences, OutputStream)
+     */
+    public static void deserialize(NodeReferences refs, InputStream stream)
+            throws Exception {
+        DataInputStream in = new DataInputStream(stream);
+
+        refs.clearAllReferences();
+
+        // references
+        int count = in.readInt();   // count
+        for (int i = 0; i < count; i++) {
+            refs.addReference(PropertyId.valueOf(in.readUTF()));    // propertyId
+        }
+    }
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/Serializer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/util/Serializer.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url



Mime
View raw message