You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2010/09/29 18:06:54 UTC

svn commit: r1002707 [1/2] - in /jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence: bundle/ pool/ util/

Author: jukka
Date: Wed Sep 29 16:06:53 2010
New Revision: 1002707

URL: http://svn.apache.org/viewvc?rev=1002707&view=rev
Log:
JCR-2762: Optimize bundle serialization

Refactor serialization and deserialization code from ItemStateBinding and BundleBinding to separate BundleReader and BundleWriter classes. This makes it easier to later on track the bundle version as it can be kept as an instance variable of an BundeReader object.

Make the use of DataInputStream in readBundle() and checkBundle() an implementation detail that's not exposed in the method signature.

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleReader.java
      - copied, changed from r1002668, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleBinding.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleWriter.java   (with props)
Removed:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/ItemStateBinding.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleFsPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/PostgreSQLPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleDbPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleFsPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/PostgreSQLPersistenceManager.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleBinding.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java?rev=1002707&r1=1002706&r2=1002707&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleDbPersistenceManager.java Wed Sep 29 16:06:53 2010
@@ -852,11 +852,10 @@ public class BundleDbPersistenceManager 
                     try {
                         // parse and check bundle
                         // checkBundle will log any problems itself
-                        DataInputStream din = new DataInputStream(new ByteArrayInputStream(data));
-                        if (binding.checkBundle(din)) {
-                            // reset stream for readBundle()
-                            din = new DataInputStream(new ByteArrayInputStream(data));
-                            NodePropBundle bundle = binding.readBundle(din, id);
+                        if (binding.checkBundle(
+                                new ByteArrayInputStream(data))) {
+                            NodePropBundle bundle = binding.readBundle(
+                                    new ByteArrayInputStream(data), id);
                             checkBundleConsistency(id, bundle, fix, modifications);
                         } else {
                             log.error("invalid bundle '" + id + "', see previous BundleBinding error log entry");
@@ -1150,19 +1149,16 @@ public class BundleDbPersistenceManager 
             }
             Blob b = rs.getBlob(1);
             byte[] bytes = getBytes(b);
-            DataInputStream din = new DataInputStream(new ByteArrayInputStream(bytes));
 
             if (checkBeforeLoading) {
-                if (binding.checkBundle(din)) {
-                    // reset stream for readBundle()
-                    din = new DataInputStream(new ByteArrayInputStream(bytes));
-                } else {
+                if (!binding.checkBundle(new ByteArrayInputStream(bytes))) {
                     // gets wrapped as proper ItemStateException below
                     throw new Exception("invalid bundle, see previous BundleBinding error log entry");
                 }
             }
 
-            NodePropBundle bundle = binding.readBundle(din, id);
+            NodePropBundle bundle =
+                binding.readBundle(new ByteArrayInputStream(bytes), id);
             bundle.setSize(bytes.length);
             return bundle;
         } catch (Exception e) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleFsPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleFsPersistenceManager.java?rev=1002707&r1=1002706&r2=1002707&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleFsPersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/BundleFsPersistenceManager.java Wed Sep 29 16:06:53 2010
@@ -37,7 +37,6 @@ import org.apache.jackrabbit.core.state.
 import org.apache.jackrabbit.core.state.NoSuchItemStateException;
 import org.apache.jackrabbit.core.state.NodeReferences;
 
-import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.InputStream;
@@ -235,24 +234,24 @@ public class BundleFsPersistenceManager 
      */
     protected synchronized NodePropBundle loadBundle(NodeId id)
             throws ItemStateException {
-        DataInputStream din = null;
         try {
             String path = buildNodeFilePath(null, id).toString();
             if (!itemFs.exists(path)) {
                 return null;
             }
             InputStream in = itemFs.getInputStream(path);
-            TrackingInputStream cin = new TrackingInputStream(in);
-            din = new DataInputStream(cin);
-            NodePropBundle bundle = binding.readBundle(din, id);
-            bundle.setSize(cin.getPosition());
-            return bundle;
+            try {
+                TrackingInputStream cin = new TrackingInputStream(in);
+                NodePropBundle bundle = binding.readBundle(cin, id);
+                bundle.setSize(cin.getPosition());
+                return bundle;
+            } finally {
+                IOUtils.closeQuietly(in);
+            }
         } catch (Exception e) {
             String msg = "failed to read bundle: " + id + ": " + e;
             log.error(msg);
             throw new ItemStateException(msg, e);
-        } finally {
-            IOUtils.closeQuietly(din);
         }
     }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/PostgreSQLPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/PostgreSQLPersistenceManager.java?rev=1002707&r1=1002706&r2=1002707&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/PostgreSQLPersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/PostgreSQLPersistenceManager.java Wed Sep 29 16:06:53 2010
@@ -24,7 +24,6 @@ import org.apache.jackrabbit.core.util.T
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.DataInputStream;
 import java.io.InputStream;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -96,8 +95,7 @@ public class PostgreSQLPersistenceManage
                     InputStream input = rs.getBinaryStream(1);
                     try {
                         TrackingInputStream cin = new TrackingInputStream(input);
-                        DataInputStream din = new DataInputStream(cin);
-                        NodePropBundle bundle = binding.readBundle(din, id);
+                        NodePropBundle bundle = binding.readBundle(cin, id);
                         bundle.setSize(cin.getPosition());
                         return bundle;
                     } finally {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleDbPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleDbPersistenceManager.java?rev=1002707&r1=1002706&r2=1002707&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleDbPersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleDbPersistenceManager.java Wed Sep 29 16:06:53 2010
@@ -18,7 +18,6 @@ package org.apache.jackrabbit.core.persi
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FilterInputStream;
@@ -811,11 +810,9 @@ public class BundleDbPersistenceManager 
                     try {
                         // parse and check bundle
                         // checkBundle will log any problems itself
-                        DataInputStream din = new DataInputStream(new ByteArrayInputStream(data));
-                        if (binding.checkBundle(din)) {
-                            // reset stream for readBundle()
-                            din = new DataInputStream(new ByteArrayInputStream(data));
-                            NodePropBundle bundle = binding.readBundle(din, id);
+                        if (binding.checkBundle(new ByteArrayInputStream(data))) {
+                            NodePropBundle bundle = binding.readBundle(
+                                    new ByteArrayInputStream(data), id);
                             checkBundleConsistency(id, bundle, fix, modifications);
                         } else {
                             log.error("invalid bundle '" + id + "', see previous BundleBinding error log entry");
@@ -1078,19 +1075,16 @@ public class BundleDbPersistenceManager 
             }
             Blob b = rs.getBlob(1);
             byte[] bytes = getBytes(b);
-            DataInputStream din = new DataInputStream(new ByteArrayInputStream(bytes));
 
             if (checkBeforeLoading) {
-                if (binding.checkBundle(din)) {
-                    // reset stream for readBundle()
-                    din = new DataInputStream(new ByteArrayInputStream(bytes));
-                } else {
+                if (!binding.checkBundle(new ByteArrayInputStream(bytes))) {
                     // gets wrapped as proper ItemStateException below
                     throw new Exception("invalid bundle, see previous BundleBinding error log entry");
                 }
             }
 
-            NodePropBundle bundle = binding.readBundle(din, id);
+            NodePropBundle bundle =
+                binding.readBundle(new ByteArrayInputStream(bytes), id);
             bundle.setSize(bytes.length);
             return bundle;
         } catch (Exception e) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleFsPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleFsPersistenceManager.java?rev=1002707&r1=1002706&r2=1002707&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleFsPersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/BundleFsPersistenceManager.java Wed Sep 29 16:06:53 2010
@@ -37,7 +37,6 @@ import org.apache.jackrabbit.core.state.
 import org.apache.jackrabbit.core.state.NoSuchItemStateException;
 import org.apache.jackrabbit.core.state.NodeReferences;
 
-import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.InputStream;
@@ -234,24 +233,24 @@ public class BundleFsPersistenceManager 
      */
     protected synchronized NodePropBundle loadBundle(NodeId id)
             throws ItemStateException {
-        DataInputStream din = null;
         try {
             String path = buildNodeFilePath(null, id).toString();
             if (!itemFs.exists(path)) {
                 return null;
             }
             InputStream in = itemFs.getInputStream(path);
-            TrackingInputStream cin = new TrackingInputStream(in);
-            din = new DataInputStream(cin);
-            NodePropBundle bundle = binding.readBundle(din, id);
-            bundle.setSize(cin.getPosition());
-            return bundle;
+            try {
+                TrackingInputStream cin = new TrackingInputStream(in);
+                NodePropBundle bundle = binding.readBundle(cin, id);
+                bundle.setSize(cin.getPosition());
+                return bundle;
+            } finally {
+                IOUtils.closeQuietly(in);
+            }
         } catch (Exception e) {
             String msg = "failed to read bundle: " + id + ": " + e;
             log.error(msg);
             throw new ItemStateException(msg, e);
-        } finally {
-            IOUtils.closeQuietly(din);
         }
     }
 

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/PostgreSQLPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/PostgreSQLPersistenceManager.java?rev=1002707&r1=1002706&r2=1002707&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/PostgreSQLPersistenceManager.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/pool/PostgreSQLPersistenceManager.java Wed Sep 29 16:06:53 2010
@@ -16,7 +16,6 @@
  */
 package org.apache.jackrabbit.core.persistence.pool;
 
-import java.io.DataInputStream;
 import java.io.InputStream;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -101,8 +100,7 @@ public class PostgreSQLPersistenceManage
                 InputStream input = rs.getBinaryStream(1);
                 try {
                     TrackingInputStream cin = new TrackingInputStream(input);
-                    DataInputStream din = new DataInputStream(cin);
-                    NodePropBundle bundle = binding.readBundle(din, id);
+                    NodePropBundle bundle = binding.readBundle(cin, id);
                     bundle.setSize(cin.getPosition());
                     return bundle;
                 } finally {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleBinding.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleBinding.java?rev=1002707&r1=1002706&r2=1002707&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleBinding.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleBinding.java Wed Sep 29 16:06:53 2010
@@ -16,41 +16,69 @@
  */
 package org.apache.jackrabbit.core.persistence.util;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.apache.commons.io.IOUtils;
-import org.apache.jackrabbit.core.id.NodeId;
-import org.apache.jackrabbit.core.id.PropertyId;
-import org.apache.jackrabbit.core.util.StringIndex;
-import org.apache.jackrabbit.core.value.InternalValue;
-import org.apache.jackrabbit.core.data.DataStore;
-import org.apache.jackrabbit.spi.Name;
-import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
-import org.apache.jackrabbit.spi.commons.name.NameConstants;
-
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.HashSet;
-import java.util.Set;
-import java.math.BigDecimal;
 
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
+import org.apache.jackrabbit.core.data.DataStore;
+import org.apache.jackrabbit.core.id.NodeId;
+import org.apache.jackrabbit.core.util.StringIndex;
 
 /**
  * This Class implements efficient serialization methods for item states.
  */
-public class BundleBinding extends ItemStateBinding {
+public class BundleBinding {
+
+    static final int BINARY_IN_BLOB_STORE = -1;
+
+    static final int BINARY_IN_DATA_STORE = -2;
+
+    /**
+     * serialization version 1
+     */
+    static final int VERSION_1 = 1;
+
+    /**
+     * serialization version 2
+     */
+    static final int VERSION_2 = 2;
+
+    /**
+     * current version
+     */
+    static final int VERSION_CURRENT = VERSION_2;
 
-    private static final int BINARY_IN_BLOB_STORE = -1;
-    private static final int BINARY_IN_DATA_STORE = -2;
+    /**
+     * the namespace index
+     */
+    protected final StringIndex nsIndex;
+
+    /**
+     * the name index
+     */
+    protected final StringIndex nameIndex;
+
+    /**
+     * the blob store
+     */
+    @SuppressWarnings("deprecation")
+    protected final BLOBStore blobStore;
 
     /**
-     * default logger
+     * minimum size of binaries to store in blob store
      */
-    private static Logger log = LoggerFactory.getLogger(BundleBinding.class);
+    protected long minBlobSize = 0x4000; // 16k
+
+    /**
+     * the error handling
+     */
+    protected final ErrorHandling errorHandling;
+
+    /**
+     * Data store for binary properties.
+     */
+    protected final DataStore dataStore;
 
     /**
      * Creates a new bundle binding
@@ -61,703 +89,80 @@ public class BundleBinding extends ItemS
      * @param nameIndex the name index
      * @param dataStore the data store
      */
-    public BundleBinding(ErrorHandling errorHandling, BLOBStore blobStore,
-                         StringIndex nsIndex, StringIndex nameIndex, DataStore dataStore) {
-        super(errorHandling, blobStore, nsIndex, nameIndex, dataStore);
+    @SuppressWarnings("deprecation")
+    public BundleBinding(
+            ErrorHandling errorHandling, BLOBStore blobStore,
+            StringIndex nsIndex, StringIndex nameIndex, DataStore dataStore) {
+        this.errorHandling = errorHandling;
+        this.nsIndex = nsIndex;
+        this.nameIndex = nameIndex;
+        this.blobStore = blobStore;
+        this.dataStore = dataStore;
     }
 
     /**
-     * Deserializes a <code>NodePropBundle</code> from a data input stream.
-     *
-     * @param in the input stream
-     * @param id the node id for the new bundle
-     * @return the bundle
-     * @throws IOException if an I/O error occurs.
+     * Returns the minimum blob size
+     * @see #setMinBlobSize(long) for details.
+     * @return the minimum blob size
      */
-    public NodePropBundle readBundle(DataInputStream in, NodeId id)
-            throws IOException {
-        NodePropBundle bundle = new NodePropBundle(id);
-
-        // read version and primary type...special handling
-        int index = in.readInt();
-
-        // get version
-        int version = (index >> 24) & 0xff;
-        index &= 0x00ffffff;
-        String uri = nsIndex.indexToString(index);
-        String local = nameIndex.indexToString(in.readInt());
-        Name nodeTypeName = NameFactoryImpl.getInstance().create(uri, local);
-
-        // primaryType
-        bundle.setNodeTypeName(nodeTypeName);
-
-        // parentUUID
-        bundle.setParentId(readNodeId(in));
-
-        // definitionId
-        in.readUTF();
-
-        // mixin types
-        Set<Name> mixinTypeNames = new HashSet<Name>();
-        Name name = readIndexedQName(in);
-        while (name != null) {
-            mixinTypeNames.add(name);
-            name = readIndexedQName(in);
-        }
-        bundle.setMixinTypeNames(mixinTypeNames);
-
-        // properties
-        name = readIndexedQName(in);
-        while (name != null) {
-            PropertyId pId = new PropertyId(bundle.getId(), name);
-            // skip redundant primaryType, mixinTypes and uuid properties
-            if (name.equals(NameConstants.JCR_PRIMARYTYPE)
-                || name.equals(NameConstants.JCR_MIXINTYPES)
-                || name.equals(NameConstants.JCR_UUID)) {
-                readPropertyEntry(in, pId);
-                name = readIndexedQName(in);
-                continue;
-            }
-            NodePropBundle.PropertyEntry pState = readPropertyEntry(in, pId);
-            bundle.addProperty(pState);
-            name = readIndexedQName(in);
-        }
-
-        // set referenceable flag
-        bundle.setReferenceable(in.readBoolean());
-
-        // child nodes (list of uuid/name pairs)
-        NodeId childId = readNodeId(in);
-        while (childId != null) {
-            bundle.addChildNodeEntry(readQName(in), childId);
-            childId = readNodeId(in);
-        }
-
-        // read modcount, since version 1.0
-        if (version >= VERSION_1) {
-            bundle.setModCount(readModCount(in));
-        }
-
-        // read shared set, since version 2.0
-        Set<NodeId> sharedSet = new HashSet<NodeId>();
-        if (version >= VERSION_2) {
-            // shared set (list of parent uuids)
-            NodeId parentId = readNodeId(in);
-            while (parentId != null) {
-                sharedSet.add(parentId);
-                parentId = readNodeId(in);
-            }
-        }
-        bundle.setSharedSet(sharedSet);
-
-        return bundle;
+    public long getMinBlobSize() {
+        return minBlobSize;
     }
 
     /**
-     * Checks a <code>NodePropBundle</code> from a data input stream.
+     * Sets the minimum blob size. Binary values that are shorted than this given
+     * size will be inlined in the serialization stream, binary value that are
+     * longer, will be stored in the blob store. default is 4k.
      *
-     * @param in the input stream
-     * @return <code>true</code> if the data is valid;
-     *         <code>false</code> otherwise.
+     * @param minBlobSize the minimum blob size.
      */
-    public boolean checkBundle(DataInputStream in) {
-        int version;
-        // primaryType & version
-        try {
-            // read version and primary type...special handling
-            int index = in.readInt();
-
-            // get version
-            version = (index >> 24) & 0xff;
-            index &= 0x00ffffff;
-            String uri = nsIndex.indexToString(index);
-            String local = nameIndex.indexToString(in.readInt());
-            Name nodeTypeName = NameFactoryImpl.getInstance().create(uri, local);
-
-            log.debug("Serialzation Version: " + version);
-            log.debug("NodeTypeName: " + nodeTypeName);
-        } catch (IOException e) {
-            log.error("Error while reading NodeTypeName: " + e);
-            return false;
-        }
-        try {
-            NodeId parentId = readNodeId(in);
-            log.debug("ParentUUID: " + parentId);
-        } catch (IOException e) {
-            log.error("Error while reading ParentUUID: " + e);
-            return false;
-        }
-        try {
-            String definitionId = in.readUTF();
-            log.debug("DefinitionId: " + definitionId);
-        } catch (IOException e) {
-            log.error("Error while reading DefinitionId: " + e);
-            return false;
-        }
-        try {
-            Name mixinName = readIndexedQName(in);
-            while (mixinName != null) {
-                log.debug("MixinTypeName: " + mixinName);
-                mixinName = readIndexedQName(in);
-            }
-        } catch (IOException e) {
-            log.error("Error while reading MixinTypes: " + e);
-            return false;
-        }
-        try {
-            Name propName = readIndexedQName(in);
-            while (propName != null) {
-                log.debug("PropertyName: " + propName);
-                if (!checkPropertyState(in)) {
-                    return false;
-                }
-                propName = readIndexedQName(in);
-            }
-        } catch (IOException e) {
-            log.error("Error while reading property names: " + e);
-            return false;
-        }
-        try {
-            boolean hasUUID = in.readBoolean();
-            log.debug("hasUUID: " + hasUUID);
-        } catch (IOException e) {
-            log.error("Error while reading 'hasUUID': " + e);
-            return false;
-        }
-        try {
-            NodeId cneId = readNodeId(in);
-            while (cneId != null) {
-                Name cneName = readQName(in);
-                log.debug("ChildNodentry: " + cneId + ":" + cneName);
-                cneId = readNodeId(in);
-            }
-        } catch (IOException e) {
-            log.error("Error while reading child node entry: " + e);
-            return false;
-        }
-
-        if (version >= VERSION_1) {
-            try {
-                short modCount = readModCount(in);
-                log.debug("modCount: " + modCount);
-            } catch (IOException e) {
-                log.error("Error while reading mod cout: " + e);
-                return false;
-            }
-        }
-
-        return true;
+    public void setMinBlobSize(long minBlobSize) {
+        this.minBlobSize = minBlobSize;
     }
 
     /**
-     * Serializes a <code>NodePropBundle</code> to a data output stream
-     *
-     * @param out the output stream
-     * @param bundle the bundle to serialize
-     * @throws IOException if an I/O error occurs.
+     * Returns the blob store that is assosiated with this binding.
+     * @return the blob store
      */
-    public void writeBundle(DataOutputStream out, NodePropBundle bundle)
-            throws IOException {
-        long size = out.size();
-
-        // primaryType and version
-        out.writeInt((VERSION_CURRENT << 24) | nsIndex.stringToIndex(bundle.getNodeTypeName().getNamespaceURI()));
-        out.writeInt(nameIndex.stringToIndex(bundle.getNodeTypeName().getLocalName()));
-
-        // parentUUID
-        writeNodeId(out, bundle.getParentId());
-
-        // definitionId
-        out.writeUTF("");
-
-        // mixin types
-        for (Name name : bundle.getMixinTypeNames()) {
-            writeIndexedQName(out, name);
-        }
-        writeIndexedQName(out, null);
-
-        // properties
-        for (Name pName : bundle.getPropertyNames()) {
-            // skip redundant primaryType, mixinTypes and uuid properties
-            if (pName.equals(NameConstants.JCR_PRIMARYTYPE)
-                || pName.equals(NameConstants.JCR_MIXINTYPES)
-                || pName.equals(NameConstants.JCR_UUID)) {
-                continue;
-            }
-            NodePropBundle.PropertyEntry pState = bundle.getPropertyEntry(pName);
-            if (pState == null) {
-                log.error("PropertyState missing in bundle: " + pName);
-            } else {
-                writeIndexedQName(out, pName);
-                writeState(out, pState);
-            }
-        }
-        writeIndexedQName(out, null);
-
-        // write uuid flag
-        out.writeBoolean(bundle.isReferenceable());
-
-        // child nodes (list of uuid/name pairs)
-        for (NodePropBundle.ChildNodeEntry entry : bundle.getChildNodeEntries()) {
-            writeNodeId(out, entry.getId());  // uuid
-            writeQName(out, entry.getName());   // name
-        }
-        writeNodeId(out, null);
-
-        // write mod count
-        writeModCount(out, bundle.getModCount());
-
-        // write shared set
-        for (NodeId nodeId: bundle.getSharedSet()) {
-            writeNodeId(out, nodeId);
-        }
-        writeNodeId(out, null);
-
-        // set size of bundle
-        bundle.setSize(out.size() - size);
+    @SuppressWarnings("deprecation")
+    public BLOBStore getBlobStore() {
+        return blobStore;
     }
 
     /**
-     * Deserializes a <code>PropertyState</code> from the data input stream.
+     * Deserializes a <code>NodePropBundle</code> from a data input stream.
      *
      * @param in the input stream
-     * @param id the property id for the new property entry
-     * @return the property entry
+     * @param id the node id for the new bundle
+     * @return the bundle
      * @throws IOException if an I/O error occurs.
      */
-    public NodePropBundle.PropertyEntry readPropertyEntry(DataInputStream in, PropertyId id)
+    public NodePropBundle readBundle(InputStream in, NodeId id)
             throws IOException {
-        NodePropBundle.PropertyEntry entry = new NodePropBundle.PropertyEntry(id);
-        // type and modcount
-        int type = in.readInt();
-        entry.setModCount((short) ((type >> 16) & 0x0ffff));
-        type &= 0x0ffff;
-        entry.setType(type);
-
-        // multiValued
-        entry.setMultiValued(in.readBoolean());
-        // definitionId
-        in.readUTF();
-        // values
-        int count = in.readInt();   // count
-        InternalValue[] values = new InternalValue[count];
-        String[] blobIds = new String[count];
-        for (int i = 0; i < count; i++) {
-            InternalValue val;
-            switch (type) {
-                case PropertyType.BINARY:
-                    int size = in.readInt();
-                    if (size == BINARY_IN_DATA_STORE) {
-                        val = InternalValue.create(dataStore, in.readUTF());
-                    } else if (size == BINARY_IN_BLOB_STORE) {
-                        blobIds[i] = in.readUTF();
-                        try {
-                            if (blobStore instanceof ResourceBasedBLOBStore) {
-                                val = InternalValue.create(((ResourceBasedBLOBStore) blobStore).getResource(blobIds[i]));
-                            } else {
-                                val = InternalValue.create(blobStore.get(blobIds[i]));
-                            }
-                        } catch (IOException e) {
-                            if (errorHandling.ignoreMissingBlobs()) {
-                                log.warn("Ignoring error while reading blob-resource: " + e);
-                                val = InternalValue.create(new byte[0]);
-                            } else {
-                                throw e;
-                            }
-                        } catch (Exception e) {
-                            throw new IOException("Unable to create property value: " + e.toString());
-                        }
-                    } else {
-                        // short values into memory
-                        byte[] data = new byte[size];
-                        in.readFully(data);
-                        val = InternalValue.create(data);
-                    }
-                    break;
-                case PropertyType.DOUBLE:
-                    val = InternalValue.create(in.readDouble());
-                    break;
-                case PropertyType.DECIMAL:
-                    val = InternalValue.create(readDecimal(in));
-                    break;
-                case PropertyType.LONG:
-                    val = InternalValue.create(in.readLong());
-                    break;
-                case PropertyType.BOOLEAN:
-                    val = InternalValue.create(in.readBoolean());
-                    break;
-                case PropertyType.NAME:
-                    val = InternalValue.create(readQName(in));
-                    break;
-                case PropertyType.WEAKREFERENCE:
-                    val = InternalValue.create(readNodeId(in), true);
-                    break;
-                case PropertyType.REFERENCE:
-                    val = InternalValue.create(readNodeId(in), false);
-                    break;
-                default:
-                    // because writeUTF(String) has a size limit of 64k,
-                    // Strings are serialized as <length><byte[]>
-                    int len = in.readInt();
-                    byte[] bytes = new byte[len];
-                    in.readFully(bytes);
-                    val = InternalValue.valueOf(new String(bytes, "UTF-8"), type);
-            }
-            values[i] = val;
-        }
-        entry.setValues(values);
-        entry.setBlobIds(blobIds);
-
-        return entry;
+        return new BundleReader(this, in).readBundle(id);
     }
 
     /**
-     * Checks a <code>PropertyState</code> from the data input stream.
+     * Checks a <code>NodePropBundle</code> from a data input stream.
      *
      * @param in the input stream
      * @return <code>true</code> if the data is valid;
      *         <code>false</code> otherwise.
      */
-    public boolean checkPropertyState(DataInputStream in) {
-        int type;
-        try {
-            type = in.readInt();
-            short modCount = (short) ((type >> 16) | 0xffff);
-            type &= 0xffff;
-            log.debug("  PropertyType: " + PropertyType.nameFromValue(type));
-            log.debug("  ModCount: " + modCount);
-        } catch (IOException e) {
-            log.error("Error while reading property type: " + e);
-            return false;
-        }
-        try {
-            boolean isMV = in.readBoolean();
-            log.debug("  MultiValued: " + isMV);
-        } catch (IOException e) {
-            log.error("Error while reading multivalued: " + e);
-            return false;
-        }
-        try {
-            String defintionId = in.readUTF();
-            log.debug("  DefinitionId: " + defintionId);
-        } catch (IOException e) {
-            log.error("Error while reading definition id: " + e);
-            return false;
-        }
-
-        int count;
-        try {
-            count = in.readInt();
-            log.debug("  num values: " + count);
-        } catch (IOException e) {
-            log.error("Error while reading number of values: " + e);
-            return false;
-        }
-        for (int i = 0; i < count; i++) {
-            switch (type) {
-                case PropertyType.BINARY:
-                    int size;
-                    try {
-                        size = in.readInt();
-                        log.debug("  binary size: " + size);
-                    } catch (IOException e) {
-                        log.error("Error while reading size of binary: " + e);
-                        return false;
-                    }
-                    if (size == BINARY_IN_DATA_STORE) {
-                        try {
-                            String s = in.readUTF();
-                            // truncate log output
-                            if (s.length() > 80) {
-                                s = s.substring(80) + "...";
-                            }
-                            log.debug("  global data store id: " + s);
-                        } catch (IOException e) {
-                            log.error("Error while reading blob id: " + e);
-                            return false;
-                        }
-                    } else if (size == BINARY_IN_BLOB_STORE) {
-                        try {
-                            String s = in.readUTF();
-                            log.debug("  blobid: " + s);
-                        } catch (IOException e) {
-                            log.error("Error while reading blob id: " + e);
-                            return false;
-                        }
-                    } else {
-                        // short values into memory
-                        byte[] data = new byte[size];
-                        try {
-                            in.readFully(data);
-                            log.debug("  binary: " + data.length + " bytes");
-                        } catch (IOException e) {
-                            log.error("Error while reading inlined binary: " + e);
-                            return false;
-                        }
-                    }
-                    break;
-                case PropertyType.DOUBLE:
-                    try {
-                        double d = in.readDouble();
-                        log.debug("  double: " + d);
-                    } catch (IOException e) {
-                        log.error("Error while reading double value: " + e);
-                        return false;
-                    }
-                    break;
-                case PropertyType.DECIMAL:
-                    try {
-                        BigDecimal d = readDecimal(in);
-                        log.debug("  decimal: " + d);
-                    } catch (IOException e) {
-                        log.error("Error while reading decimal value: " + e);
-                        return false;
-                    }
-                    break;
-                case PropertyType.LONG:
-                    try {
-                        double l = in.readLong();
-                        log.debug("  long: " + l);
-                    } catch (IOException e) {
-                        log.error("Error while reading long value: " + e);
-                        return false;
-                    }
-                    break;
-                case PropertyType.BOOLEAN:
-                    try {
-                        boolean b = in.readBoolean();
-                        log.debug("  boolean: " + b);
-                    } catch (IOException e) {
-                        log.error("Error while reading boolean value: " + e);
-                        return false;
-                    }
-                    break;
-                case PropertyType.NAME:
-                    try {
-                        Name name = readQName(in);
-                        log.debug("  name: " + name);
-                    } catch (IOException e) {
-                        log.error("Error while reading name value: " + e);
-                        return false;
-                    }
-                    break;
-                case PropertyType.WEAKREFERENCE:
-                case PropertyType.REFERENCE:
-                    try {
-                        NodeId id = readNodeId(in);
-                        log.debug("  reference: " + id);
-                    } catch (IOException e) {
-                        log.error("Error while reading reference value: " + e);
-                        return false;
-                    }
-                    break;
-                default:
-                    // because writeUTF(String) has a size limit of 64k,
-                    // Strings are serialized as <length><byte[]>
-                    int len;
-                    try {
-                        len = in.readInt();
-                        log.debug("  size of string value: " + len);
-                    } catch (IOException e) {
-                        log.error("Error while reading size of string value: " + e);
-                        return false;
-                    }
-                    try {
-                        byte[] bytes = new byte[len];
-                        in.readFully(bytes);
-                        String s = new String(bytes, "UTF-8");
-                        // truncate log output
-                        if (s.length() > 80) {
-                            s = s.substring(80) + "...";
-                        }
-                        log.debug("  string: " + s);
-                    } catch (IOException e) {
-                        log.error("Error while reading string value: " + e);
-                        return false;
-                    }
-            }
-        }
-        return true;
+    public boolean checkBundle(InputStream in) {
+        return new BundleReader(this, in).checkBundle();
     }
 
-
     /**
-     * Serializes a <code>PropertyState</code> to the data output stream
+     * Serializes a <code>NodePropBundle</code> to a data output stream
      *
      * @param out the output stream
-     * @param state the property entry to store
+     * @param bundle the bundle to serialize
      * @throws IOException if an I/O error occurs.
      */
-    public void writeState(DataOutputStream out, NodePropBundle.PropertyEntry state)
+    public void writeBundle(DataOutputStream out, NodePropBundle bundle)
             throws IOException {
-        // type & mod count
-        out.writeInt(state.getType() | (state.getModCount() << 16));
-        // multiValued
-        out.writeBoolean(state.isMultiValued());
-        // definitionId
-        out.writeUTF("");
-        // values
-        InternalValue[] values = state.getValues();
-        out.writeInt(values.length); // count
-        for (int i = 0; i < values.length; i++) {
-            InternalValue val = values[i];
-            switch (state.getType()) {
-                case PropertyType.BINARY:
-                    try {
-                        long size = val.getLength();
-                        if (dataStore != null) {
-                            int maxMemorySize = dataStore.getMinRecordLength() - 1;
-                            if (size < maxMemorySize) {
-                                writeSmallBinary(out, val, state, i);
-                            } else {
-                                out.writeInt(BINARY_IN_DATA_STORE);
-                                val.store(dataStore);
-                                out.writeUTF(val.toString());
-                            }
-                            break;
-                        }
-                        // special handling required for binary value:
-                        // spool binary value to file in blob store
-                        if (size < 0) {
-                            log.warn("Blob has negative size. Potential loss of data. "
-                                    + "id={} idx={}", state.getId(), String.valueOf(i));
-                            out.writeInt(0);
-                            values[i] = InternalValue.create(new byte[0]);
-                            val.discard();
-                        } else if (size > minBlobSize) {
-                            out.writeInt(BINARY_IN_BLOB_STORE);
-                            String blobId = state.getBlobId(i);
-                            if (blobId == null) {
-                                try {
-                                    InputStream in = val.getStream();
-                                    try {
-                                        blobId = blobStore.createId(state.getId(), i);
-                                        blobStore.put(blobId, in, size);
-                                        state.setBlobId(blobId, i);
-                                    } finally {
-                                        IOUtils.closeQuietly(in);
-                                    }
-                                } catch (Exception e) {
-                                    String msg = "Error while storing blob. id="
-                                            + state.getId() + " idx=" + i + " size=" + size;
-                                    log.error(msg, e);
-                                    throw new IOException(msg);
-                                }
-                                try {
-                                    // replace value instance with value
-                                    // backed by resource in blob store and delete temp file
-                                    if (blobStore instanceof ResourceBasedBLOBStore) {
-                                        values[i] = InternalValue.create(((ResourceBasedBLOBStore) blobStore).getResource(blobId));
-                                    } else {
-                                        values[i] = InternalValue.create(blobStore.get(blobId));
-                                    }
-                                } catch (Exception e) {
-                                    log.error("Error while reloading blob. truncating. id="
-                                            + state.getId() + " idx=" + i + " size=" + size, e);
-                                    values[i] = InternalValue.create(new byte[0]);
-                                }
-                                val.discard();
-                            }
-                            // store id of blob as property value
-                            out.writeUTF(blobId);   // value
-                        } else {
-                            // delete evt. blob
-                            byte[] data = writeSmallBinary(out, val, state, i);
-                            // replace value instance with value
-                            // backed by resource in blob store and delete temp file
-                            values[i] = InternalValue.create(data);
-                            val.discard();
-                        }
-                    } catch (RepositoryException e) {
-                        String msg = "Error while storing blob. id="
-                            + state.getId() + " idx=" + i + " value=" + val;
-                        log.error(msg, e);
-                        throw new IOException(msg);
-                    }
-                    break;
-                case PropertyType.DOUBLE:
-                    try {
-                        out.writeDouble(val.getDouble());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        throw new IOException("Unexpected error while writing DOUBLE value.");
-                    }
-                    break;
-                case PropertyType.DECIMAL:
-                    try {
-                        writeDecimal(out, val.getDecimal());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        throw new IOException("Unexpected error while writing DECIMAL value.");
-                    }
-                    break;
-                case PropertyType.LONG:
-                    try {
-                        out.writeLong(val.getLong());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        throw new IOException("Unexpected error while writing LONG value.");
-                    }
-                    break;
-                case PropertyType.BOOLEAN:
-                    try {
-                        out.writeBoolean(val.getBoolean());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        throw new IOException("Unexpected error while writing BOOLEAN value.");
-                    }
-                    break;
-                case PropertyType.NAME:
-                    try {
-                        writeQName(out, val.getName());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        throw new IOException("Unexpected error while writing NAME value.");
-                    }
-                    break;
-                case PropertyType.WEAKREFERENCE:
-                case PropertyType.REFERENCE:
-                    writeNodeId(out, val.getNodeId());
-                    break;
-                default:
-                    // because writeUTF(String) has a size limit of 64k,
-                    // we're using write(byte[]) instead
-                    byte[] bytes = val.toString().getBytes("UTF-8");
-                    out.writeInt(bytes.length); // length of byte[]
-                    out.write(bytes);   // byte[]
-            }
-        }
+        new BundleWriter(this, out).writeBundle(bundle);
     }
 
-    /**
-     * Write a small binary value and return the data.
-     *
-     * @param out the output stream to write
-     * @param value the binary value
-     * @param state the property state (for error messages)
-     * @param i the index (for error messages)
-     * @return the data
-     * @throws IOException if the data could not be read
-     */
-    private byte[] writeSmallBinary(DataOutputStream out, InternalValue value, NodePropBundle.PropertyEntry state, int i) throws IOException {
-        try {
-            int size = (int) value.getLength();
-            out.writeInt(size);
-            byte[] data = new byte[size];
-            DataInputStream in =
-                new DataInputStream(value.getStream());
-            try {
-                in.readFully(data);
-            } finally {
-                IOUtils.closeQuietly(in);
-            }
-            out.write(data, 0, data.length);
-            return data;
-        } catch (Exception e) {
-            String msg = "Error while storing blob. id="
-                    + state.getId() + " idx=" + i + " value=" + value;
-            log.error(msg, e);
-            throw new IOException(msg);
-        }
-    }
 }

Copied: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleReader.java (from r1002668, jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleBinding.java)
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleReader.java?p2=jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleReader.java&p1=jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleBinding.java&r1=1002668&r2=1002707&rev=1002707&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleBinding.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/util/BundleReader.java Wed Sep 29 16:06:53 2010
@@ -18,18 +18,16 @@ package org.apache.jackrabbit.core.persi
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.core.id.NodeId;
 import org.apache.jackrabbit.core.id.PropertyId;
-import org.apache.jackrabbit.core.util.StringIndex;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
+import org.apache.jackrabbit.core.state.NodeState;
 import org.apache.jackrabbit.core.value.InternalValue;
-import org.apache.jackrabbit.core.data.DataStore;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
 
 import java.io.DataInputStream;
-import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashSet;
@@ -37,45 +35,40 @@ import java.util.Set;
 import java.math.BigDecimal;
 
 import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
 
 /**
- * This Class implements efficient serialization methods for item states.
+ * Bundle deserializater.
+ *
+ * @see BundleWriter
  */
-public class BundleBinding extends ItemStateBinding {
+class BundleReader {
 
-    private static final int BINARY_IN_BLOB_STORE = -1;
-    private static final int BINARY_IN_DATA_STORE = -2;
+    /** Logger instance */
+    private static Logger log = LoggerFactory.getLogger(BundleReader.class);
 
-    /**
-     * default logger
-     */
-    private static Logger log = LoggerFactory.getLogger(BundleBinding.class);
+    private final BundleBinding binding;
+
+    private final DataInputStream in;
 
     /**
-     * Creates a new bundle binding
+     * Creates a new bundle deserializer.
      *
-     * @param errorHandling the error handling
-     * @param blobStore the blobstore for retrieving blobs
-     * @param nsIndex the namespace index
-     * @param nameIndex the name index
-     * @param dataStore the data store
+     * @param binding bundle binding
+     * @param stream stream from which the bundle is read
      */
-    public BundleBinding(ErrorHandling errorHandling, BLOBStore blobStore,
-                         StringIndex nsIndex, StringIndex nameIndex, DataStore dataStore) {
-        super(errorHandling, blobStore, nsIndex, nameIndex, dataStore);
+    public BundleReader(BundleBinding binding, InputStream stream) {
+        this.binding = binding;
+        this.in = new DataInputStream(stream);
     }
 
     /**
      * Deserializes a <code>NodePropBundle</code> from a data input stream.
      *
-     * @param in the input stream
      * @param id the node id for the new bundle
      * @return the bundle
      * @throws IOException if an I/O error occurs.
      */
-    public NodePropBundle readBundle(DataInputStream in, NodeId id)
-            throws IOException {
+    public NodePropBundle readBundle(NodeId id) throws IOException {
         NodePropBundle bundle = new NodePropBundle(id);
 
         // read version and primary type...special handling
@@ -84,68 +77,68 @@ public class BundleBinding extends ItemS
         // get version
         int version = (index >> 24) & 0xff;
         index &= 0x00ffffff;
-        String uri = nsIndex.indexToString(index);
-        String local = nameIndex.indexToString(in.readInt());
+        String uri = binding.nsIndex.indexToString(index);
+        String local = binding.nameIndex.indexToString(in.readInt());
         Name nodeTypeName = NameFactoryImpl.getInstance().create(uri, local);
 
         // primaryType
         bundle.setNodeTypeName(nodeTypeName);
 
         // parentUUID
-        bundle.setParentId(readNodeId(in));
+        bundle.setParentId(readNodeId());
 
         // definitionId
         in.readUTF();
 
         // mixin types
         Set<Name> mixinTypeNames = new HashSet<Name>();
-        Name name = readIndexedQName(in);
+        Name name = readIndexedQName();
         while (name != null) {
             mixinTypeNames.add(name);
-            name = readIndexedQName(in);
+            name = readIndexedQName();
         }
         bundle.setMixinTypeNames(mixinTypeNames);
 
         // properties
-        name = readIndexedQName(in);
+        name = readIndexedQName();
         while (name != null) {
             PropertyId pId = new PropertyId(bundle.getId(), name);
             // skip redundant primaryType, mixinTypes and uuid properties
             if (name.equals(NameConstants.JCR_PRIMARYTYPE)
                 || name.equals(NameConstants.JCR_MIXINTYPES)
                 || name.equals(NameConstants.JCR_UUID)) {
-                readPropertyEntry(in, pId);
-                name = readIndexedQName(in);
+                readPropertyEntry(pId);
+                name = readIndexedQName();
                 continue;
             }
-            NodePropBundle.PropertyEntry pState = readPropertyEntry(in, pId);
+            NodePropBundle.PropertyEntry pState = readPropertyEntry(pId);
             bundle.addProperty(pState);
-            name = readIndexedQName(in);
+            name = readIndexedQName();
         }
 
         // set referenceable flag
         bundle.setReferenceable(in.readBoolean());
 
         // child nodes (list of uuid/name pairs)
-        NodeId childId = readNodeId(in);
+        NodeId childId = readNodeId();
         while (childId != null) {
-            bundle.addChildNodeEntry(readQName(in), childId);
-            childId = readNodeId(in);
+            bundle.addChildNodeEntry(readQName(), childId);
+            childId = readNodeId();
         }
 
         // read modcount, since version 1.0
-        if (version >= VERSION_1) {
-            bundle.setModCount(readModCount(in));
+        if (version >= BundleBinding.VERSION_1) {
+            bundle.setModCount(readModCount());
         }
 
         // read shared set, since version 2.0
         Set<NodeId> sharedSet = new HashSet<NodeId>();
-        if (version >= VERSION_2) {
+        if (version >= BundleBinding.VERSION_2) {
             // shared set (list of parent uuids)
-            NodeId parentId = readNodeId(in);
+            NodeId parentId = readNodeId();
             while (parentId != null) {
                 sharedSet.add(parentId);
-                parentId = readNodeId(in);
+                parentId = readNodeId();
             }
         }
         bundle.setSharedSet(sharedSet);
@@ -156,11 +149,10 @@ public class BundleBinding extends ItemS
     /**
      * Checks a <code>NodePropBundle</code> from a data input stream.
      *
-     * @param in the input stream
      * @return <code>true</code> if the data is valid;
      *         <code>false</code> otherwise.
      */
-    public boolean checkBundle(DataInputStream in) {
+    public boolean checkBundle() {
         int version;
         // primaryType & version
         try {
@@ -170,8 +162,8 @@ public class BundleBinding extends ItemS
             // get version
             version = (index >> 24) & 0xff;
             index &= 0x00ffffff;
-            String uri = nsIndex.indexToString(index);
-            String local = nameIndex.indexToString(in.readInt());
+            String uri = binding.nsIndex.indexToString(index);
+            String local = binding.nameIndex.indexToString(in.readInt());
             Name nodeTypeName = NameFactoryImpl.getInstance().create(uri, local);
 
             log.debug("Serialzation Version: " + version);
@@ -181,7 +173,7 @@ public class BundleBinding extends ItemS
             return false;
         }
         try {
-            NodeId parentId = readNodeId(in);
+            NodeId parentId = readNodeId();
             log.debug("ParentUUID: " + parentId);
         } catch (IOException e) {
             log.error("Error while reading ParentUUID: " + e);
@@ -195,23 +187,23 @@ public class BundleBinding extends ItemS
             return false;
         }
         try {
-            Name mixinName = readIndexedQName(in);
+            Name mixinName = readIndexedQName();
             while (mixinName != null) {
                 log.debug("MixinTypeName: " + mixinName);
-                mixinName = readIndexedQName(in);
+                mixinName = readIndexedQName();
             }
         } catch (IOException e) {
             log.error("Error while reading MixinTypes: " + e);
             return false;
         }
         try {
-            Name propName = readIndexedQName(in);
+            Name propName = readIndexedQName();
             while (propName != null) {
                 log.debug("PropertyName: " + propName);
-                if (!checkPropertyState(in)) {
+                if (!checkPropertyState()) {
                     return false;
                 }
-                propName = readIndexedQName(in);
+                propName = readIndexedQName();
             }
         } catch (IOException e) {
             log.error("Error while reading property names: " + e);
@@ -225,20 +217,20 @@ public class BundleBinding extends ItemS
             return false;
         }
         try {
-            NodeId cneId = readNodeId(in);
+            NodeId cneId = readNodeId();
             while (cneId != null) {
-                Name cneName = readQName(in);
+                Name cneName = readQName();
                 log.debug("ChildNodentry: " + cneId + ":" + cneName);
-                cneId = readNodeId(in);
+                cneId = readNodeId();
             }
         } catch (IOException e) {
             log.error("Error while reading child node entry: " + e);
             return false;
         }
 
-        if (version >= VERSION_1) {
+        if (version >= BundleBinding.VERSION_1) {
             try {
-                short modCount = readModCount(in);
+                short modCount = readModCount();
                 log.debug("modCount: " + modCount);
             } catch (IOException e) {
                 log.error("Error while reading mod cout: " + e);
@@ -250,82 +242,13 @@ public class BundleBinding extends ItemS
     }
 
     /**
-     * Serializes a <code>NodePropBundle</code> to a data output stream
-     *
-     * @param out the output stream
-     * @param bundle the bundle to serialize
-     * @throws IOException if an I/O error occurs.
-     */
-    public void writeBundle(DataOutputStream out, NodePropBundle bundle)
-            throws IOException {
-        long size = out.size();
-
-        // primaryType and version
-        out.writeInt((VERSION_CURRENT << 24) | nsIndex.stringToIndex(bundle.getNodeTypeName().getNamespaceURI()));
-        out.writeInt(nameIndex.stringToIndex(bundle.getNodeTypeName().getLocalName()));
-
-        // parentUUID
-        writeNodeId(out, bundle.getParentId());
-
-        // definitionId
-        out.writeUTF("");
-
-        // mixin types
-        for (Name name : bundle.getMixinTypeNames()) {
-            writeIndexedQName(out, name);
-        }
-        writeIndexedQName(out, null);
-
-        // properties
-        for (Name pName : bundle.getPropertyNames()) {
-            // skip redundant primaryType, mixinTypes and uuid properties
-            if (pName.equals(NameConstants.JCR_PRIMARYTYPE)
-                || pName.equals(NameConstants.JCR_MIXINTYPES)
-                || pName.equals(NameConstants.JCR_UUID)) {
-                continue;
-            }
-            NodePropBundle.PropertyEntry pState = bundle.getPropertyEntry(pName);
-            if (pState == null) {
-                log.error("PropertyState missing in bundle: " + pName);
-            } else {
-                writeIndexedQName(out, pName);
-                writeState(out, pState);
-            }
-        }
-        writeIndexedQName(out, null);
-
-        // write uuid flag
-        out.writeBoolean(bundle.isReferenceable());
-
-        // child nodes (list of uuid/name pairs)
-        for (NodePropBundle.ChildNodeEntry entry : bundle.getChildNodeEntries()) {
-            writeNodeId(out, entry.getId());  // uuid
-            writeQName(out, entry.getName());   // name
-        }
-        writeNodeId(out, null);
-
-        // write mod count
-        writeModCount(out, bundle.getModCount());
-
-        // write shared set
-        for (NodeId nodeId: bundle.getSharedSet()) {
-            writeNodeId(out, nodeId);
-        }
-        writeNodeId(out, null);
-
-        // set size of bundle
-        bundle.setSize(out.size() - size);
-    }
-
-    /**
      * Deserializes a <code>PropertyState</code> from the data input stream.
      *
-     * @param in the input stream
      * @param id the property id for the new property entry
      * @return the property entry
      * @throws IOException if an I/O error occurs.
      */
-    public NodePropBundle.PropertyEntry readPropertyEntry(DataInputStream in, PropertyId id)
+    private NodePropBundle.PropertyEntry readPropertyEntry(PropertyId id)
             throws IOException {
         NodePropBundle.PropertyEntry entry = new NodePropBundle.PropertyEntry(id);
         // type and modcount
@@ -347,18 +270,19 @@ public class BundleBinding extends ItemS
             switch (type) {
                 case PropertyType.BINARY:
                     int size = in.readInt();
-                    if (size == BINARY_IN_DATA_STORE) {
-                        val = InternalValue.create(dataStore, in.readUTF());
-                    } else if (size == BINARY_IN_BLOB_STORE) {
+                    if (size == BundleBinding.BINARY_IN_DATA_STORE) {
+                        val = InternalValue.create(binding.dataStore, in.readUTF());
+                    } else if (size == BundleBinding.BINARY_IN_BLOB_STORE) {
                         blobIds[i] = in.readUTF();
                         try {
+                            BLOBStore blobStore = binding.getBlobStore();
                             if (blobStore instanceof ResourceBasedBLOBStore) {
                                 val = InternalValue.create(((ResourceBasedBLOBStore) blobStore).getResource(blobIds[i]));
                             } else {
                                 val = InternalValue.create(blobStore.get(blobIds[i]));
                             }
                         } catch (IOException e) {
-                            if (errorHandling.ignoreMissingBlobs()) {
+                            if (binding.errorHandling.ignoreMissingBlobs()) {
                                 log.warn("Ignoring error while reading blob-resource: " + e);
                                 val = InternalValue.create(new byte[0]);
                             } else {
@@ -378,7 +302,7 @@ public class BundleBinding extends ItemS
                     val = InternalValue.create(in.readDouble());
                     break;
                 case PropertyType.DECIMAL:
-                    val = InternalValue.create(readDecimal(in));
+                    val = InternalValue.create(readDecimal());
                     break;
                 case PropertyType.LONG:
                     val = InternalValue.create(in.readLong());
@@ -387,13 +311,13 @@ public class BundleBinding extends ItemS
                     val = InternalValue.create(in.readBoolean());
                     break;
                 case PropertyType.NAME:
-                    val = InternalValue.create(readQName(in));
+                    val = InternalValue.create(readQName());
                     break;
                 case PropertyType.WEAKREFERENCE:
-                    val = InternalValue.create(readNodeId(in), true);
+                    val = InternalValue.create(readNodeId(), true);
                     break;
                 case PropertyType.REFERENCE:
-                    val = InternalValue.create(readNodeId(in), false);
+                    val = InternalValue.create(readNodeId(), false);
                     break;
                 default:
                     // because writeUTF(String) has a size limit of 64k,
@@ -414,11 +338,10 @@ public class BundleBinding extends ItemS
     /**
      * Checks a <code>PropertyState</code> from the data input stream.
      *
-     * @param in the input stream
      * @return <code>true</code> if the data is valid;
      *         <code>false</code> otherwise.
      */
-    public boolean checkPropertyState(DataInputStream in) {
+    private boolean checkPropertyState() {
         int type;
         try {
             type = in.readInt();
@@ -464,7 +387,7 @@ public class BundleBinding extends ItemS
                         log.error("Error while reading size of binary: " + e);
                         return false;
                     }
-                    if (size == BINARY_IN_DATA_STORE) {
+                    if (size == BundleBinding.BINARY_IN_DATA_STORE) {
                         try {
                             String s = in.readUTF();
                             // truncate log output
@@ -476,7 +399,7 @@ public class BundleBinding extends ItemS
                             log.error("Error while reading blob id: " + e);
                             return false;
                         }
-                    } else if (size == BINARY_IN_BLOB_STORE) {
+                    } else if (size == BundleBinding.BINARY_IN_BLOB_STORE) {
                         try {
                             String s = in.readUTF();
                             log.debug("  blobid: " + s);
@@ -507,7 +430,7 @@ public class BundleBinding extends ItemS
                     break;
                 case PropertyType.DECIMAL:
                     try {
-                        BigDecimal d = readDecimal(in);
+                        BigDecimal d = readDecimal();
                         log.debug("  decimal: " + d);
                     } catch (IOException e) {
                         log.error("Error while reading decimal value: " + e);
@@ -534,7 +457,7 @@ public class BundleBinding extends ItemS
                     break;
                 case PropertyType.NAME:
                     try {
-                        Name name = readQName(in);
+                        Name name = readQName();
                         log.debug("  name: " + name);
                     } catch (IOException e) {
                         log.error("Error while reading name value: " + e);
@@ -544,7 +467,7 @@ public class BundleBinding extends ItemS
                 case PropertyType.WEAKREFERENCE:
                 case PropertyType.REFERENCE:
                     try {
-                        NodeId id = readNodeId(in);
+                        NodeId id = readNodeId();
                         log.debug("  reference: " + id);
                     } catch (IOException e) {
                         log.error("Error while reading reference value: " + e);
@@ -580,184 +503,132 @@ public class BundleBinding extends ItemS
         return true;
     }
 
-
     /**
-     * Serializes a <code>PropertyState</code> to the data output stream
+     * Deserializes a <code>NodeState</code> from the data input stream.
      *
-     * @param out the output stream
-     * @param state the property entry to store
-     * @throws IOException if an I/O error occurs.
+     * @param id the id of the nodestate to read
+     * @param pMgr the persistence manager
+     * @return the node state
+     * @throws IOException in an I/O error occurs.
      */
-    public void writeState(DataOutputStream out, NodePropBundle.PropertyEntry state)
+    private NodeState readState(NodeId id, PersistenceManager pMgr)
             throws IOException {
-        // type & mod count
-        out.writeInt(state.getType() | (state.getModCount() << 16));
-        // multiValued
-        out.writeBoolean(state.isMultiValued());
+        NodeState state = pMgr.createNew(id);
+        // primaryType & version
+        int index = in.readInt();
+        int version = (index >> 24) & 0x0ff;
+        String uri = binding.nsIndex.indexToString(index & 0x0ffffff);
+        String local = in.readUTF();
+        state.setNodeTypeName(NameFactoryImpl.getInstance().create(uri, local));
+
+        // parentUUID
+        state.setParentId(readNodeId());
         // definitionId
-        out.writeUTF("");
-        // values
-        InternalValue[] values = state.getValues();
-        out.writeInt(values.length); // count
-        for (int i = 0; i < values.length; i++) {
-            InternalValue val = values[i];
-            switch (state.getType()) {
-                case PropertyType.BINARY:
-                    try {
-                        long size = val.getLength();
-                        if (dataStore != null) {
-                            int maxMemorySize = dataStore.getMinRecordLength() - 1;
-                            if (size < maxMemorySize) {
-                                writeSmallBinary(out, val, state, i);
-                            } else {
-                                out.writeInt(BINARY_IN_DATA_STORE);
-                                val.store(dataStore);
-                                out.writeUTF(val.toString());
-                            }
-                            break;
-                        }
-                        // special handling required for binary value:
-                        // spool binary value to file in blob store
-                        if (size < 0) {
-                            log.warn("Blob has negative size. Potential loss of data. "
-                                    + "id={} idx={}", state.getId(), String.valueOf(i));
-                            out.writeInt(0);
-                            values[i] = InternalValue.create(new byte[0]);
-                            val.discard();
-                        } else if (size > minBlobSize) {
-                            out.writeInt(BINARY_IN_BLOB_STORE);
-                            String blobId = state.getBlobId(i);
-                            if (blobId == null) {
-                                try {
-                                    InputStream in = val.getStream();
-                                    try {
-                                        blobId = blobStore.createId(state.getId(), i);
-                                        blobStore.put(blobId, in, size);
-                                        state.setBlobId(blobId, i);
-                                    } finally {
-                                        IOUtils.closeQuietly(in);
-                                    }
-                                } catch (Exception e) {
-                                    String msg = "Error while storing blob. id="
-                                            + state.getId() + " idx=" + i + " size=" + size;
-                                    log.error(msg, e);
-                                    throw new IOException(msg);
-                                }
-                                try {
-                                    // replace value instance with value
-                                    // backed by resource in blob store and delete temp file
-                                    if (blobStore instanceof ResourceBasedBLOBStore) {
-                                        values[i] = InternalValue.create(((ResourceBasedBLOBStore) blobStore).getResource(blobId));
-                                    } else {
-                                        values[i] = InternalValue.create(blobStore.get(blobId));
-                                    }
-                                } catch (Exception e) {
-                                    log.error("Error while reloading blob. truncating. id="
-                                            + state.getId() + " idx=" + i + " size=" + size, e);
-                                    values[i] = InternalValue.create(new byte[0]);
-                                }
-                                val.discard();
-                            }
-                            // store id of blob as property value
-                            out.writeUTF(blobId);   // value
-                        } else {
-                            // delete evt. blob
-                            byte[] data = writeSmallBinary(out, val, state, i);
-                            // replace value instance with value
-                            // backed by resource in blob store and delete temp file
-                            values[i] = InternalValue.create(data);
-                            val.discard();
-                        }
-                    } catch (RepositoryException e) {
-                        String msg = "Error while storing blob. id="
-                            + state.getId() + " idx=" + i + " value=" + val;
-                        log.error(msg, e);
-                        throw new IOException(msg);
-                    }
-                    break;
-                case PropertyType.DOUBLE:
-                    try {
-                        out.writeDouble(val.getDouble());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        throw new IOException("Unexpected error while writing DOUBLE value.");
-                    }
-                    break;
-                case PropertyType.DECIMAL:
-                    try {
-                        writeDecimal(out, val.getDecimal());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        throw new IOException("Unexpected error while writing DECIMAL value.");
-                    }
-                    break;
-                case PropertyType.LONG:
-                    try {
-                        out.writeLong(val.getLong());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        throw new IOException("Unexpected error while writing LONG value.");
-                    }
-                    break;
-                case PropertyType.BOOLEAN:
-                    try {
-                        out.writeBoolean(val.getBoolean());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        throw new IOException("Unexpected error while writing BOOLEAN value.");
-                    }
-                    break;
-                case PropertyType.NAME:
-                    try {
-                        writeQName(out, val.getName());
-                    } catch (RepositoryException e) {
-                        // should never occur
-                        throw new IOException("Unexpected error while writing NAME value.");
-                    }
-                    break;
-                case PropertyType.WEAKREFERENCE:
-                case PropertyType.REFERENCE:
-                    writeNodeId(out, val.getNodeId());
-                    break;
-                default:
-                    // because writeUTF(String) has a size limit of 64k,
-                    // we're using write(byte[]) instead
-                    byte[] bytes = val.toString().getBytes("UTF-8");
-                    out.writeInt(bytes.length); // length of byte[]
-                    out.write(bytes);   // byte[]
+        in.readUTF();
+
+        // mixin types
+        int count = in.readInt();   // count
+        Set<Name> set = new HashSet<Name>(count);
+        for (int i = 0; i < count; i++) {
+            set.add(readQName()); // name
+        }
+        if (set.size() > 0) {
+            state.setMixinTypeNames(set);
+        }
+        // properties (names)
+        count = in.readInt();   // count
+        for (int i = 0; i < count; i++) {
+            state.addPropertyName(readIndexedQName()); // name
+        }
+        // child nodes (list of name/uuid pairs)
+        count = in.readInt();   // count
+        for (int i = 0; i < count; i++) {
+            Name name = readQName();
+            NodeId parentId = readNodeId();
+            state.addChildNodeEntry(name, parentId);
+        }
+
+        if (version >= BundleBinding.VERSION_1) {
+            state.setModCount(readModCount());
+        }
+        if (version >= BundleBinding.VERSION_2) {
+            // shared set (list of parent uuids)
+            count = in.readInt();   // count
+            for (int i = 0; i < count; i++) {
+                state.addShare(readNodeId());
             }
         }
+        return state;
     }
 
     /**
-     * Write a small binary value and return the data.
+     * Deserializes a node identifier
      *
-     * @param out the output stream to write
-     * @param value the binary value
-     * @param state the property state (for error messages)
-     * @param i the index (for error messages)
-     * @return the data
-     * @throws IOException if the data could not be read
-     */
-    private byte[] writeSmallBinary(DataOutputStream out, InternalValue value, NodePropBundle.PropertyEntry state, int i) throws IOException {
-        try {
-            int size = (int) value.getLength();
-            out.writeInt(size);
-            byte[] data = new byte[size];
-            DataInputStream in =
-                new DataInputStream(value.getStream());
-            try {
-                in.readFully(data);
-            } finally {
-                IOUtils.closeQuietly(in);
-            }
-            out.write(data, 0, data.length);
-            return data;
-        } catch (Exception e) {
-            String msg = "Error while storing blob. id="
-                    + state.getId() + " idx=" + i + " value=" + value;
-            log.error(msg, e);
-            throw new IOException(msg);
+     * @return the node id
+     * @throws IOException in an I/O error occurs.
+     */
+    private NodeId readNodeId() throws IOException {
+        if (in.readBoolean()) {
+            byte[] bytes = new byte[16];
+            in.readFully(bytes);
+            return new NodeId(bytes);
+        } else {
+            return null;
         }
     }
+
+    /**
+     * Deserializes a BigDecimal
+     *
+     * @return the decimal
+     * @throws IOException in an I/O error occurs.
+     */
+    private BigDecimal readDecimal() throws IOException {
+        if (in.readBoolean()) {
+            // TODO more efficient serialization format
+            return new BigDecimal(in.readUTF());
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Deserializes a Name
+     *
+     * @return the qname
+     * @throws IOException in an I/O error occurs.
+     */
+    private Name readQName() throws IOException {
+        String uri = binding.nsIndex.indexToString(in.readInt());
+        String local = in.readUTF();
+        return NameFactoryImpl.getInstance().create(uri, local);
+    }
+
+    /**
+     * Deserializes a mod-count
+     *
+     * @return the mod count
+     * @throws IOException in an I/O error occurs.
+     */
+    private short readModCount() throws IOException {
+        return in.readShort();
+    }
+
+    /**
+     * Deserializes an indexed Name
+     *
+     * @return the qname
+     * @throws IOException in an I/O error occurs.
+     */
+    private Name readIndexedQName() throws IOException {
+        int index = in.readInt();
+        if (index < 0) {
+            return null;
+        } else {
+            String uri = binding.nsIndex.indexToString(index);
+            String local = binding.nameIndex.indexToString(in.readInt());
+            return NameFactoryImpl.getInstance().create(uri, local);
+        }
+    }
+
 }