Index: applications/test/workspaces/test/workspace.xml
===================================================================
--- applications/test/workspaces/test/workspace.xml	(revision 465518)
+++ applications/test/workspaces/test/workspace.xml	(working copy)
@@ -27,7 +27,7 @@
       persistence of the workspace:
       class: FQN of class implementing PersistenceManager interface
   -->
-  <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+  <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
      <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
      <param name="schemaObjectPrefix" value="${wsp.name}_"/>
   </PersistenceManager>
Index: applications/test/workspaces/default/workspace.xml
===================================================================
--- applications/test/workspaces/default/workspace.xml	(revision 465518)
+++ applications/test/workspaces/default/workspace.xml	(working copy)
@@ -27,7 +27,7 @@
       persistence of the workspace:
       class: FQN of class implementing PersistenceManager interface
   -->
-  <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+  <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
      <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
      <param name="schemaObjectPrefix" value="${wsp.name}_"/>
   </PersistenceManager>
Index: applications/test/repository.xml
===================================================================
--- applications/test/repository.xml	(revision 465518)
+++ applications/test/repository.xml	(working copy)
@@ -193,7 +193,7 @@
             persistence manager of the workspace:
             class: FQN of class implementing the PersistenceManager interface
         -->
-        <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
           <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
           <param name="schemaObjectPrefix" value="${wsp.name}_"/>
         </PersistenceManager>
@@ -224,7 +224,7 @@
             a 'normal' persistence manager, but this could change in future
             implementations.
         -->
-        <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
           <param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>
           <param name="schemaObjectPrefix" value="version_"/>
         </PersistenceManager>
Index: src/test/java/org/apache/jackrabbit/core/config/WorkspaceConfigTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/config/WorkspaceConfigTest.java	(revision 465518)
+++ src/test/java/org/apache/jackrabbit/core/config/WorkspaceConfigTest.java	(working copy)
@@ -53,7 +53,7 @@
 
         PersistenceManagerConfig pmc = config.getPersistenceManagerConfig();
         assertEquals(
-                "org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager",
+                "org.apache.jackrabbit.core.persistence.obj.ObjectPersistenceManager",
                 pmc.getClassName());
         assertTrue(pmc.getParameters().isEmpty());
 
Index: src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java
===================================================================
--- src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java	(revision 465518)
+++ src/test/java/org/apache/jackrabbit/core/config/RepositoryConfigTest.java	(working copy)
@@ -16,9 +16,11 @@
  */
 package org.apache.jackrabbit.core.config;
 
+import junit.framework.TestCase;
+import org.xml.sax.InputSource;
+
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -26,10 +28,6 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 
-import junit.framework.TestCase;
-
-import org.xml.sax.InputSource;
-
 /**
  * Test cases for repository configuration handling.
  */
@@ -191,7 +189,7 @@
         VersioningConfig vc = config.getVersioningConfig();
         assertEquals(new File(REPOSITORY_HOME, "version"), vc.getHomeDir());
         assertEquals(
-                "org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager",
+                "org.apache.jackrabbit.core.persistence.obj.ObjectPersistenceManager",
                 vc.getPersistenceManagerConfig().getClassName());
         assertTrue(vc.getPersistenceManagerConfig().getParameters().isEmpty());
     }
Index: src/test/java/org/apache/jackrabbit/core/config/workspace.xml
===================================================================
--- src/test/java/org/apache/jackrabbit/core/config/workspace.xml	(revision 465518)
+++ src/test/java/org/apache/jackrabbit/core/config/workspace.xml	(working copy)
@@ -27,7 +27,7 @@
       persistence of the workspace:
       class: FQN of class implementing PersistenceManager interface
   -->
-  <PersistenceManager class="org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager"/>
+  <PersistenceManager class="org.apache.jackrabbit.core.persistence.obj.ObjectPersistenceManager"/>
   <!--
       Search index and the file system it uses.
   -->
Index: src/test/java/org/apache/jackrabbit/core/config/repository.xml
===================================================================
--- src/test/java/org/apache/jackrabbit/core/config/repository.xml	(revision 465518)
+++ src/test/java/org/apache/jackrabbit/core/config/repository.xml	(working copy)
@@ -32,11 +32,11 @@
             a Security element that specifies the name of the app-entry
             in the JAAS config and the access manager
 
-            a Workspaces element that specifies the location of the 
-            workspaces root directory, the name of the default workspace,
-            the maximum idle time before a workspace is automatically
-            shutdown (optional) and the workspace configuration root directory
-            within the virtual repository file system (optional)
+            a Workspaces element that specifies the location of the 
+            workspaces root directory, the name of the default workspace,
+            the maximum idle time before a workspace is automatically
+            shutdown (optional) and the workspace configuration root directory
+            within the virtual repository file system (optional)
 
             a Workspace element that is used as a workspace configuration
             template; it is used to create the initial workspace if there's
@@ -204,16 +204,16 @@
             class: FQN of class implementing the PersistenceManager interface
         -->
         <!--
-        <PersistenceManager class="org.apache.jackrabbit.core.state.xml.XMLPersistenceManager"/>
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.xml.XMLPersistenceManager"/>
         -->
         <!--
-        <PersistenceManager class="org.apache.jackrabbit.core.state.mem.InMemPersistenceManager">
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
             <param name="initialCapacity" value="100000"/>
             <param name="loadFactor" value="0.3"/>
             <param name="persistent" value="true"/>
         </PersistenceManager>
         -->
-        <PersistenceManager class="org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager"/>
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.obj.ObjectPersistenceManager"/>
         <!--
             Search index and the file system it uses.
         -->
@@ -247,7 +247,7 @@
             a 'normal' persistence manager, but this could change in future
             implementations.
         -->
-        <PersistenceManager class="org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager"/>
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.obj.ObjectPersistenceManager"/>
 
     </Versioning>
 </Repository>
Index: src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java	(revision 0)
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence;
+
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.persistence.PMContext;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.state.NoSuchItemStateException;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeReferencesId;
+import org.apache.jackrabbit.core.state.ChangeLog;
+
+/**
+ * Persistence manager interface. Persistence managers are
+ * internal Jackrabbit components that handle the persistent
+ * storage of content nodes and properties. A persistence
+ * manager knows how to retrieve the persistent states of
+ * content items and how to atomically save a set of changes
+ * to the persistent state.
+ * <p>
+ * Each workspace of a Jackrabbit content repository uses separate
+ * persistence manager to store the content in that workspace. Also
+ * the Jackrabbit version handler uses a separate persistence manager.
+ * The persistence managers in use are configured in the Jackrabbit
+ * XML configuration files. The configured persistence managers are
+ * instantiated and initialized using the JavaBeans conventions.
+ *
+ * <h2>Persistence manager life cycle</h2>
+ * <p>
+ * The life cycle of a persistence manager instance contains four phases:
+ * <ol>
+ *   <li>Instantiation, where the instance is created and possible
+ *       configuration properties are set using the JavaBean conventions.
+ *       During this phase the persistence manager should not attempt to
+ *       reference any external resources.
+ *   <li>Initialization, where the {@link #init(PMContext) init} method
+ *       is invoked to bind the persistence manager with a given
+ *       {@link PMContext context}.
+ *   <li>Normal usage, where the various create, load, exists, and store
+ *       methods of the persistence manager are used to manage the
+ *       persistent content items.
+ *   <li>Closing, where the {@link #close() close} method is invoked
+ *       to close the persistence manager and release all acquired
+ *       resources.
+ * </ol>
+ */
+public interface PersistenceManager {
+
+    /**
+     * Initializes the persistence manager. The persistence manager is
+     * permanently bound to the given context, and any required external
+     * resources are acquired.
+     * <p>
+     * An appropriate exception is thrown if the persistence manager
+     * initialization fails for whatever reason. In this case the
+     * state of the persistence manager is undefined and the instance
+     * should be discarded.
+     *
+     * @param context persistence manager context
+     * @throws Exception if the persistence manager intialization failed
+     */
+    void init(PMContext context) throws Exception;
+
+    /**
+     * Closes the persistence manager. The consistency of the persistent
+     * storage is guaranteed and all acquired resources are released.
+     * It is an error to invoke any methods on a closed persistence manager,
+     * and implementations are free to enforce this constraint by throwing
+     * IllegalStateExceptions in such cases.
+     * <p>
+     * An appropriate exception is thrown if the persistence manager
+     * could not be closed properly. In this case the state of the
+     * persistence manager is undefined and the instance should be
+     * discarded.
+     *
+     * @throws Exception if the persistence manager failed to close properly
+     */
+    void close() throws Exception;
+
+    /**
+     * Creates a new node state instance with the given id.
+     *
+     * @param id node id
+     * @return node state instance
+     */
+    NodeState createNew(NodeId id);
+
+    /**
+     * Creates a new property state instance with the given id.
+     *
+     * @param id property id
+     * @return property state instance
+     */
+    PropertyState createNew(PropertyId id);
+
+    /**
+     * Load the persistent members of a node state.
+     *
+     * @param id node id
+     * @return loaded node state
+     * @throws NoSuchItemStateException if the node state does not exist
+     * @throws ItemStateException if another error occurs
+     */
+    NodeState load(NodeId id)
+            throws NoSuchItemStateException, ItemStateException;
+
+    /**
+     * Load the persistent members of a property state.
+     *
+     * @param id property id
+     * @return loaded property state
+     * @throws NoSuchItemStateException if the property state does not exist
+     * @throws ItemStateException if another error occurs
+     */
+    PropertyState load(PropertyId id)
+            throws NoSuchItemStateException, ItemStateException;
+
+    /**
+     * Load the persistent members of a node references object.
+     *
+     * @param id reference target node id
+     * @throws NoSuchItemStateException if the target node does not exist
+     * @throws ItemStateException if another error occurs
+     */
+    NodeReferences load(NodeReferencesId id)
+            throws NoSuchItemStateException, ItemStateException;
+
+    /**
+     * Checks whether the identified node exists.
+     *
+     * @param id node id
+     * @return <code>true</code> if the node exists,
+     *         <code>false</code> otherwise
+     * @throws ItemStateException on persistence manager errors
+     */
+    boolean exists(NodeId id) throws ItemStateException;
+
+    /**
+     * Checks whether the identified property exists.
+     *
+     * @param id property id
+     * @return <code>true</code> if the property exists,
+     *         <code>false</code> otherwise
+     * @throws ItemStateException on persistence manager errors
+     */
+    boolean exists(PropertyId id) throws ItemStateException;
+
+    /**
+     * Checks whether references of the identified target node exist.
+     *
+     * @param targetId target node id
+     * @return <code>true</code> if the references exist,
+     *         <code>false</code> otherwise
+     * @throws ItemStateException on persistence manager errors
+     */
+    boolean exists(NodeReferencesId targetId) throws ItemStateException;
+
+    /**
+     * Atomically saves the given set of changes.
+     *
+     * @param changeLog change log containing states that were changed
+     * @throws ItemStateException if the changes could not be saved
+     */
+    void store(ChangeLog changeLog) throws ItemStateException;
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/PersistenceManager.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/xml/XMLPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/xml/XMLPersistenceManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/xml/XMLPersistenceManager.java	(revision 0)
@@ -0,0 +1,919 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.xml;
+
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+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.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.persistence.AbstractPersistenceManager;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.NoSuchItemStateException;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeReferencesId;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.persistence.PMContext;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.persistence.util.FileSystemBLOBStore;
+import org.apache.jackrabbit.core.persistence.util.ResourceBasedBLOBStore;
+import org.apache.jackrabbit.core.util.DOMWalker;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.PropertyType;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * <code>XMLPersistenceManager</code> is a <code>FileSystem</code>-based
+ * <code>PersistenceManager</code> that persists <code>ItemState</code>
+ * and <code>NodeReferences</code> objects in XML format.
+ */
+public class XMLPersistenceManager extends AbstractPersistenceManager {
+
+    private static Logger log = LoggerFactory.getLogger(XMLPersistenceManager.class);
+
+    /**
+     * hexdigits for toString
+     */
+    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
+
+    /**
+     * The default encoding used in serialization
+     */
+    public static final String DEFAULT_ENCODING = "UTF-8";
+
+    /**
+     * The XML elements and attributes used in serialization
+     */
+    private static final String NODE_ELEMENT = "node";
+    private static final String UUID_ATTRIBUTE = "uuid";
+    private static final String NODETYPE_ATTRIBUTE = "nodeType";
+    private static final String PARENTUUID_ATTRIBUTE = "parentUUID";
+    private static final String DEFINITIONID_ATTRIBUTE = "definitionId";
+    private static final String MODCOUNT_ATTRIBUTE = "modCount";
+
+    private static final String MIXINTYPES_ELEMENT = "mixinTypes";
+    private static final String MIXINTYPE_ELEMENT = "mixinType";
+
+    private static final String PROPERTIES_ELEMENT = "properties";
+    private static final String PROPERTY_ELEMENT = "property";
+    private static final String NAME_ATTRIBUTE = "name";
+    private static final String TYPE_ATTRIBUTE = "type";
+    private static final String MULTIVALUED_ATTRIBUTE = "multiValued";
+
+    private static final String VALUES_ELEMENT = "values";
+    private static final String VALUE_ELEMENT = "value";
+
+    private static final String NODES_ELEMENT = "nodes";
+
+    private static final String NODEREFERENCES_ELEMENT = "references";
+    private static final String TARGETID_ATTRIBUTE = "targetId";
+    private static final String NODEREFERENCE_ELEMENT = "reference";
+    private static final String PROPERTYID_ATTRIBUTE = "propertyId";
+
+    private static final String NODEFILENAME = ".node.xml";
+
+    private static final String NODEREFSFILENAME = ".references.xml";
+
+    private boolean initialized;
+
+    // file system where the item state is stored
+    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>XMLPersistenceManager</code> instance.
+     */
+    public XMLPersistenceManager() {
+        initialized = false;
+    }
+
+    private String buildNodeFolderPath(NodeId id) {
+        StringBuffer sb = new StringBuffer();
+        char[] chars = id.getUUID().toString().toCharArray();
+        int cnt = 0;
+        for (int i = 0; i < chars.length; i++) {
+            if (chars[i] == '-') {
+                continue;
+            }
+            //if (cnt > 0 && cnt % 4 == 0) {
+            if (cnt == 4 || cnt == 8) {
+                sb.append('/');
+            }
+            sb.append(chars[i]);
+            cnt++;
+        }
+        return sb.toString();
+    }
+
+    private String buildPropFilePath(PropertyId id) {
+        String fileName;
+        try {
+            MessageDigest md5 = MessageDigest.getInstance("MD5");
+            md5.update(id.getName().getNamespaceURI().getBytes());
+            md5.update(id.getName().getLocalName().getBytes());
+            byte[] bytes = md5.digest();
+            char[] chars = new char[32];
+            for (int i = 0, j = 0; i < 16; i++) {
+                chars[j++] = HEXDIGITS[(bytes[i] >> 4) & 0x0f];
+                chars[j++] = HEXDIGITS[bytes[i] & 0x0f];
+            }
+            fileName = new String(chars) + ".xml";
+        } catch (NoSuchAlgorithmException nsae) {
+            // should never get here as MD5 should always be available in the JRE
+            String msg = "MD5 not available";
+            log.error(msg, nsae);
+            throw new InternalError(msg + nsae);
+        }
+        return buildNodeFolderPath(id.getParentId()) + "/" + fileName;
+    }
+
+    private String buildNodeFilePath(NodeId id) {
+        return buildNodeFolderPath(id) + "/" + NODEFILENAME;
+    }
+
+    private String buildNodeReferencesFilePath(NodeReferencesId id) {
+        return buildNodeFolderPath(id.getTargetId()) + "/" + NODEREFSFILENAME;
+    }
+
+    private void readState(DOMWalker walker, NodeState state)
+            throws ItemStateException {
+        // first do some paranoid sanity checks
+        if (!walker.getName().equals(NODE_ELEMENT)) {
+            String msg = "invalid serialization format (unexpected element: "
+                    + walker.getName() + ")";
+            log.debug(msg);
+            throw new ItemStateException(msg);
+        }
+        // check uuid
+        if (!state.getNodeId().getUUID().toString().equals(walker.getAttribute(UUID_ATTRIBUTE))) {
+            String msg = "invalid serialized state: uuid mismatch";
+            log.debug(msg);
+            throw new ItemStateException(msg);
+        }
+        // check nodetype
+        String ntName = walker.getAttribute(NODETYPE_ATTRIBUTE);
+        if (!QName.valueOf(ntName).equals(state.getNodeTypeName())) {
+            String msg = "invalid serialized state: nodetype mismatch";
+            log.debug(msg);
+            throw new ItemStateException(msg);
+        }
+
+        // now we're ready to read state
+
+        // primary parent
+        String parentUUID = walker.getAttribute(PARENTUUID_ATTRIBUTE);
+        if (parentUUID.length() > 0) {
+            state.setParentId(NodeId.valueOf(parentUUID));
+        }
+
+        // definition id
+        String definitionId = walker.getAttribute(DEFINITIONID_ATTRIBUTE);
+        state.setDefinitionId(NodeDefId.valueOf(definitionId));
+
+        // modification count
+        String modCount = walker.getAttribute(MODCOUNT_ATTRIBUTE);
+        state.setModCount(Short.parseShort(modCount));
+
+        // mixin types
+        if (walker.enterElement(MIXINTYPES_ELEMENT)) {
+            Set mixins = new HashSet();
+            while (walker.iterateElements(MIXINTYPE_ELEMENT)) {
+                mixins.add(QName.valueOf(walker.getAttribute(NAME_ATTRIBUTE)));
+            }
+            if (mixins.size() > 0) {
+                state.setMixinTypeNames(mixins);
+            }
+            walker.leaveElement();
+        }
+
+        // property entries
+        if (walker.enterElement(PROPERTIES_ELEMENT)) {
+            while (walker.iterateElements(PROPERTY_ELEMENT)) {
+                String propName = walker.getAttribute(NAME_ATTRIBUTE);
+                // @todo deserialize type and values
+                state.addPropertyName(QName.valueOf(propName));
+            }
+            walker.leaveElement();
+        }
+
+        // child node entries
+        if (walker.enterElement(NODES_ELEMENT)) {
+            while (walker.iterateElements(NODE_ELEMENT)) {
+                String childName = walker.getAttribute(NAME_ATTRIBUTE);
+                String childUUID = walker.getAttribute(UUID_ATTRIBUTE);
+                state.addChildNodeEntry(QName.valueOf(childName), NodeId.valueOf(childUUID));
+            }
+            walker.leaveElement();
+        }
+    }
+
+    private void readState(DOMWalker walker, PropertyState state)
+            throws ItemStateException {
+        // first do some paranoid sanity checks
+        if (!walker.getName().equals(PROPERTY_ELEMENT)) {
+            String msg = "invalid serialization format (unexpected element: "
+                    + walker.getName() + ")";
+            log.debug(msg);
+            throw new ItemStateException(msg);
+        }
+        // check name
+        if (!state.getName().equals(QName.valueOf(walker.getAttribute(NAME_ATTRIBUTE)))) {
+            String msg = "invalid serialized state: name mismatch";
+            log.debug(msg);
+            throw new ItemStateException(msg);
+        }
+        // check parentUUID
+        NodeId parentId = NodeId.valueOf(walker.getAttribute(PARENTUUID_ATTRIBUTE));
+        if (!parentId.equals(state.getParentId())) {
+            String msg = "invalid serialized state: parentUUID mismatch";
+            log.debug(msg);
+            throw new ItemStateException(msg);
+        }
+
+        // now we're ready to read state
+
+        // type
+        String typeName = walker.getAttribute(TYPE_ATTRIBUTE);
+        int type;
+        try {
+            type = PropertyType.valueFromName(typeName);
+        } catch (IllegalArgumentException iae) {
+            // should never be getting here
+            throw new ItemStateException("unexpected property-type: " + typeName, iae);
+        }
+        state.setType(type);
+
+        // multiValued
+        String multiValued = walker.getAttribute(MULTIVALUED_ATTRIBUTE);
+        state.setMultiValued(Boolean.getBoolean(multiValued));
+
+        // definition id
+        String definitionId = walker.getAttribute(DEFINITIONID_ATTRIBUTE);
+        state.setDefinitionId(PropDefId.valueOf(definitionId));
+
+        // modification count
+        String modCount = walker.getAttribute(MODCOUNT_ATTRIBUTE);
+        state.setModCount(Short.parseShort(modCount));
+
+        // values
+        ArrayList values = new ArrayList();
+        if (walker.enterElement(VALUES_ELEMENT)) {
+            while (walker.iterateElements(VALUE_ELEMENT)) {
+                // read serialized value
+                String content = walker.getContent();
+                if (PropertyType.STRING == type) {
+                    // STRING value can be empty; ignore length
+                    values.add(InternalValue.valueOf(content, type));
+                } else if (content.length() > 0) {
+                    // non-empty non-STRING value
+                    if (type == PropertyType.BINARY) {
+                        try {
+                            // 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(content);
+                                values.add(InternalValue.create(fsRes));
+                            } else {
+                                InputStream in = blobStore.get(content);
+                                try {
+                                    values.add(InternalValue.create(in, false));
+                                } finally {
+                                    try {
+                                        in.close();
+                                    } catch (IOException e) {
+                                        // ignore
+                                    }
+                                }
+                            }
+                        } catch (Exception e) {
+                            String msg = "error while reading serialized binary value";
+                            log.debug(msg);
+                            throw new ItemStateException(msg, e);
+                        }
+                    } else {
+                        // non-empty non-STRING non-BINARY value
+                        values.add(InternalValue.valueOf(content, type));
+                    }
+                } else {
+                    // empty non-STRING value
+                    log.warn(state.getPropertyId() + ": ignoring empty value of type "
+                            + PropertyType.nameFromValue(type));
+                }
+            }
+            walker.leaveElement();
+        }
+        state.setValues((InternalValue[])
+                values.toArray(new InternalValue[values.size()]));
+    }
+
+    private void readState(DOMWalker walker, NodeReferences refs)
+            throws ItemStateException {
+        // first do some paranoid sanity checks
+        if (!walker.getName().equals(NODEREFERENCES_ELEMENT)) {
+            String msg = "invalid serialization format (unexpected element: " + walker.getName() + ")";
+            log.debug(msg);
+            throw new ItemStateException(msg);
+        }
+        // check targetId
+        if (!refs.getId().equals(NodeReferencesId.valueOf(walker.getAttribute(TARGETID_ATTRIBUTE)))) {
+            String msg = "invalid serialized state: targetId  mismatch";
+            log.debug(msg);
+            throw new ItemStateException(msg);
+        }
+
+        // now we're ready to read the references data
+
+        // property id's
+        refs.clearAllReferences();
+        while (walker.iterateElements(NODEREFERENCE_ELEMENT)) {
+            refs.addReference(PropertyId.valueOf(walker.getAttribute(PROPERTYID_ATTRIBUTE)));
+        }
+    }
+
+    //---------------------------------------------------< PersistenceManager >
+    /**
+     * {@inheritDoc}
+     */
+    public void init(PMContext context) throws Exception {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+
+        itemStateFS = new BasedFileSystem(context.getFileSystem(), "/data");
+
+        /**
+         * 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;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void close() throws Exception {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            // 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
+             * closed by the repository
+             */
+        } finally {
+            initialized = false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized NodeState load(NodeId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        Exception e = null;
+        String nodeFilePath = buildNodeFilePath(id);
+
+        try {
+            if (!itemStateFS.isFile(nodeFilePath)) {
+                throw new NoSuchItemStateException(id.toString());
+            }
+            InputStream in = itemStateFS.getInputStream(nodeFilePath);
+
+            try {
+                DOMWalker walker = new DOMWalker(in);
+                String ntName = walker.getAttribute(NODETYPE_ATTRIBUTE);
+
+                NodeState state = createNew(id);
+                state.setNodeTypeName(QName.valueOf(ntName));
+                readState(walker, state);
+                return state;
+            } finally {
+                in.close();
+            }
+        } catch (IOException ioe) {
+            e = ioe;
+            // fall through
+        } catch (FileSystemException fse) {
+            e = fse;
+            // fall through
+        }
+        String msg = "failed to read node state: " + id;
+        log.debug(msg);
+        throw new ItemStateException(msg, e);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized PropertyState load(PropertyId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        Exception e = null;
+        String propFilePath = buildPropFilePath(id);
+
+        try {
+            if (!itemStateFS.isFile(propFilePath)) {
+                throw new NoSuchItemStateException(id.toString());
+            }
+            InputStream in = itemStateFS.getInputStream(propFilePath);
+            try {
+                DOMWalker walker = new DOMWalker(in);
+                PropertyState state = createNew(id);
+                readState(walker, state);
+                return state;
+            } finally {
+                in.close();
+            }
+        } catch (IOException ioe) {
+            e = ioe;
+            // fall through
+        } catch (FileSystemException fse) {
+            e = fse;
+            // fall through
+        }
+        String msg = "failed to read property state: " + id.toString();
+        log.debug(msg);
+        throw new ItemStateException(msg, e);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void store(NodeState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        NodeId id = state.getNodeId();
+        String nodeFilePath = buildNodeFilePath(id);
+        FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
+        try {
+            nodeFile.makeParentDirs();
+            OutputStream os = nodeFile.getOutputStream();
+            Writer writer = null;
+            try {
+                String encoding = DEFAULT_ENCODING;
+                try {
+                    writer = new BufferedWriter(new OutputStreamWriter(os, encoding));
+                } catch (UnsupportedEncodingException e) {
+                    // should never get here!
+                    OutputStreamWriter osw = new OutputStreamWriter(os);
+                    encoding = osw.getEncoding();
+                    writer = new BufferedWriter(osw);
+                }
+
+                writer.write("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n");
+                writer.write("<" + NODE_ELEMENT + " "
+                        + UUID_ATTRIBUTE + "=\"" + id.getUUID() + "\" "
+                        + PARENTUUID_ATTRIBUTE + "=\"" + (state.getParentId() == null ? "" : state.getParentId().getUUID().toString()) + "\" "
+                        + DEFINITIONID_ATTRIBUTE + "=\"" + state.getDefinitionId().toString() + "\" "
+                        + MODCOUNT_ATTRIBUTE + "=\"" + state.getModCount() + "\" "
+                        + NODETYPE_ATTRIBUTE + "=\"" + Text.encodeIllegalXMLCharacters(state.getNodeTypeName().toString()) + "\">\n");
+
+                // mixin types
+                writer.write("\t<" + MIXINTYPES_ELEMENT + ">\n");
+                Iterator iter = state.getMixinTypeNames().iterator();
+                while (iter.hasNext()) {
+                    writer.write("\t\t<" + MIXINTYPE_ELEMENT + " "
+                            + NAME_ATTRIBUTE + "=\"" + Text.encodeIllegalXMLCharacters(iter.next().toString()) + "\"/>\n");
+                }
+                writer.write("\t</" + MIXINTYPES_ELEMENT + ">\n");
+
+                // properties
+                writer.write("\t<" + PROPERTIES_ELEMENT + ">\n");
+                iter = state.getPropertyNames().iterator();
+                while (iter.hasNext()) {
+                    QName propName = (QName) iter.next();
+                    writer.write("\t\t<" + PROPERTY_ELEMENT + " "
+                            + NAME_ATTRIBUTE + "=\"" + Text.encodeIllegalXMLCharacters(propName.toString()) + "\">\n");
+                    // @todo serialize type, definition id and values
+                    writer.write("\t\t</" + PROPERTY_ELEMENT + ">\n");
+                }
+                writer.write("\t</" + PROPERTIES_ELEMENT + ">\n");
+
+                // child nodes
+                writer.write("\t<" + NODES_ELEMENT + ">\n");
+                iter = state.getChildNodeEntries().iterator();
+                while (iter.hasNext()) {
+                    NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) iter.next();
+                    writer.write("\t\t<" + NODE_ELEMENT + " "
+                            + NAME_ATTRIBUTE + "=\"" + Text.encodeIllegalXMLCharacters(entry.getName().toString()) + "\" "
+                            + UUID_ATTRIBUTE + "=\"" + entry.getId().getUUID().toString() + "\">\n");
+                    writer.write("\t\t</" + NODE_ELEMENT + ">\n");
+                }
+                writer.write("\t</" + NODES_ELEMENT + ">\n");
+
+                writer.write("</" + NODE_ELEMENT + ">\n");
+            } finally {
+                writer.close();
+            }
+        } catch (Exception e) {
+            String msg = "failed to write node state: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void store(PropertyState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        String propFilePath = buildPropFilePath(state.getPropertyId());
+        FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
+        try {
+            propFile.makeParentDirs();
+            OutputStream os = propFile.getOutputStream();
+            // write property state to xml file
+            Writer writer = null;
+            try {
+                String encoding = DEFAULT_ENCODING;
+                try {
+                    writer = new BufferedWriter(new OutputStreamWriter(os, encoding));
+                } catch (UnsupportedEncodingException e) {
+                    // should never get here!
+                    OutputStreamWriter osw = new OutputStreamWriter(os);
+                    encoding = osw.getEncoding();
+                    writer = new BufferedWriter(osw);
+                }
+
+                String typeName;
+                int type = state.getType();
+                try {
+                    typeName = PropertyType.nameFromValue(type);
+                } catch (IllegalArgumentException iae) {
+                    // should never be getting here
+                    throw new ItemStateException("unexpected property-type ordinal: " + type, iae);
+                }
+
+                writer.write("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n");
+                writer.write("<" + PROPERTY_ELEMENT + " "
+                        + NAME_ATTRIBUTE + "=\"" + Text.encodeIllegalXMLCharacters(state.getName().toString()) + "\" "
+                        + PARENTUUID_ATTRIBUTE + "=\"" + state.getParentId().getUUID() + "\" "
+                        + MULTIVALUED_ATTRIBUTE + "=\"" + Boolean.toString(state.isMultiValued()) + "\" "
+                        + DEFINITIONID_ATTRIBUTE + "=\"" + state.getDefinitionId().toString() + "\" "
+                        + MODCOUNT_ATTRIBUTE + "=\"" + state.getModCount() + "\" "
+                        + TYPE_ATTRIBUTE + "=\"" + typeName + "\">\n");
+                // values
+                writer.write("\t<" + VALUES_ELEMENT + ">\n");
+                InternalValue[] values = state.getValues();
+                if (values != null) {
+                    for (int i = 0; i < values.length; i++) {
+                        writer.write("\t\t<" + VALUE_ELEMENT + ">");
+                        InternalValue val = values[i];
+                        if (val != null) {
+                            if (type == 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(state.getPropertyId(), i);
+                                try {
+                                    blobStore.put(blobId, in, blobVal.getLength());
+                                } finally {
+                                    try {
+                                        in.close();
+                                    } catch (IOException e) {
+                                        // ignore
+                                    }
+                                }
+                                // store id of BLOB as property value
+                                writer.write(blobId);
+                                // replace value instance with value backed by resource
+                                // in BLOB store and discard old value instance (e.g. temp file)
+                                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(blobId);
+                                    values[i] = InternalValue.create(fsRes);
+                                } else {
+                                    in = blobStore.get(blobId);
+                                    try {
+                                        values[i] = InternalValue.create(in, false);
+                                    } finally {
+                                        try {
+                                            in.close();
+                                        } catch (IOException e) {
+                                            // ignore
+                                        }
+                                    }
+                                }
+                                blobVal.discard();
+                            } else {
+                                writer.write(Text.encodeIllegalXMLCharacters(val.toString()));
+                            }
+                        }
+                        writer.write("</" + VALUE_ELEMENT + ">\n");
+                    }
+                }
+                writer.write("\t</" + VALUES_ELEMENT + ">\n");
+                writer.write("</" + PROPERTY_ELEMENT + ">\n");
+            } finally {
+                writer.close();
+            }
+        } catch (Exception e) {
+            String msg = "failed to store property state: " + state.getParentId() + "/" + state.getName();
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void destroy(NodeState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        NodeId id = state.getNodeId();
+        String nodeFilePath = buildNodeFilePath(id);
+        FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
+        try {
+            if (nodeFile.exists()) {
+                // delete resource and prune empty parent folders
+                nodeFile.delete(true);
+            }
+        } catch (FileSystemException fse) {
+            String msg = "failed to delete node state: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void destroy(PropertyState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // delete binary values (stored as files)
+        InternalValue[] values = state.getValues();
+        if (values != null) {
+            for (int i = 0; i < values.length; i++) {
+                InternalValue val = values[i];
+                if (val != null) {
+                    if (val.getType() == PropertyType.BINARY) {
+                        BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
+                        // delete blob file and prune empty parent folders
+                        blobVal.delete(true);
+                    }
+                }
+            }
+        }
+        // delete property file
+        String propFilePath = buildPropFilePath(state.getPropertyId());
+        FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
+        try {
+            if (propFile.exists()) {
+                // delete resource and prune empty parent folders
+                propFile.delete(true);
+            }
+        } catch (FileSystemException fse) {
+            String msg = "failed to delete property state: " + state.getParentId() + "/" + state.getName();
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized NodeReferences load(NodeReferencesId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        Exception e = null;
+        String refsFilePath = buildNodeReferencesFilePath(id);
+        try {
+            if (!itemStateFS.isFile(refsFilePath)) {
+                throw new NoSuchItemStateException(id.toString());
+            }
+
+            InputStream in = itemStateFS.getInputStream(refsFilePath);
+
+            try {
+                DOMWalker walker = new DOMWalker(in);
+                NodeReferences refs = new NodeReferences(id);
+                readState(walker, refs);
+                return refs;
+            } finally {
+                in.close();
+            }
+        } catch (IOException ioe) {
+            e = ioe;
+            // fall through
+        } catch (FileSystemException fse) {
+            e = fse;
+            // fall through
+        }
+        String msg = "failed to load references: " + id;
+        log.debug(msg);
+        throw new ItemStateException(msg, e);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void store(NodeReferences refs) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        NodeReferencesId id = refs.getId();
+        String refsFilePath = buildNodeReferencesFilePath(id);
+        FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
+        try {
+            refsFile.makeParentDirs();
+            OutputStream os = refsFile.getOutputStream();
+            BufferedWriter writer = null;
+            try {
+                String encoding = DEFAULT_ENCODING;
+                try {
+                    writer = new BufferedWriter(new OutputStreamWriter(os, encoding));
+                } catch (UnsupportedEncodingException e) {
+                    // should never get here!
+                    OutputStreamWriter osw = new OutputStreamWriter(os);
+                    encoding = osw.getEncoding();
+                    writer = new BufferedWriter(osw);
+                }
+                writer.write("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n");
+                writer.write("<" + NODEREFERENCES_ELEMENT + " "
+                        + TARGETID_ATTRIBUTE + "=\"" + refs.getId() + "\">\n");
+                // write references (i.e. the id's of the REFERENCE properties)
+                Iterator iter = refs.getReferences().iterator();
+                while (iter.hasNext()) {
+                    PropertyId propId = (PropertyId) iter.next();
+                    writer.write("\t<" + NODEREFERENCE_ELEMENT + " "
+                            + PROPERTYID_ATTRIBUTE + "=\"" + propId + "\"/>\n");
+                }
+                writer.write("</" + NODEREFERENCES_ELEMENT + ">\n");
+            } finally {
+                writer.close();
+            }
+        } catch (Exception e) {
+            String msg = "failed to store references: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void destroy(NodeReferences refs) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        NodeReferencesId id = refs.getId();
+        String refsFilePath = buildNodeReferencesFilePath(id);
+        FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
+        try {
+            if (refsFile.exists()) {
+                // delete resource and prune empty parent folders
+                refsFile.delete(true);
+            }
+        } catch (FileSystemException fse) {
+            String msg = "failed to delete references: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized boolean exists(NodeId id) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            String nodeFilePath = buildNodeFilePath(id);
+            FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
+            return nodeFile.exists();
+        } catch (FileSystemException fse) {
+            String msg = "failed to check existence of item state: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized boolean exists(PropertyId id) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            String propFilePath = buildPropFilePath(id);
+            FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
+            return propFile.exists();
+        } catch (FileSystemException fse) {
+            String msg = "failed to check existence of item state: " + id;
+            log.error(msg, fse);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized boolean exists(NodeReferencesId id)
+            throws ItemStateException {
+
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            String refsFilePath = buildNodeReferencesFilePath(id);
+            FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
+            return refsFile.exists();
+        } catch (FileSystemException fse) {
+            String msg = "failed to check existence of references: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/xml/XMLPersistenceManager.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/PMContext.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/PMContext.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/PMContext.java	(revision 0)
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence;
+
+import org.apache.jackrabbit.core.fs.FileSystem;
+import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
+
+import javax.jcr.NamespaceRegistry;
+import java.io.File;
+
+/**
+ * A <code>PMContext</code> is used to provide context information for a
+ * <code>PersistenceManager</code>.
+ *
+ * @see PersistenceManager#init(PMContext)
+ */
+public class PMContext {
+
+    /**
+     * the physcial home dir
+     */
+    private final File physicalHomeDir;
+
+    /**
+     * the virtual jackrabbit filesystem
+     */
+    private final FileSystem fs;
+
+    /**
+     * namespace registry
+     */
+    private final NamespaceRegistry nsReg;
+
+    /**
+     * node type registry
+     */
+    private final NodeTypeRegistry ntReg;
+
+    /**
+     * uuid of the root node
+     */
+    private final NodeId rootNodeId;
+
+    /**
+     * Creates a new <code>PMContext</code>.
+     *
+     * @param homeDir the physical home directory
+     * @param fs the virtual jackrabbit filesystem
+     * @param rootNodeId id of the root node
+     * @param nsReg        namespace registry
+     * @param ntReg        node type registry
+     */
+    public PMContext(File homeDir,
+                     FileSystem fs,
+                     NodeId rootNodeId,
+                     NamespaceRegistry nsReg,
+                     NodeTypeRegistry ntReg) {
+        this.physicalHomeDir = homeDir;
+        this.fs = fs;
+        this.rootNodeId = rootNodeId;
+        this.nsReg = nsReg;
+        this.ntReg = ntReg;
+    }
+
+
+    /**
+     * Returns the physical home directory for this persistence manager
+     * @return the physical home directory for this persistence manager
+     */
+    public File getHomeDir() {
+        return physicalHomeDir;
+    }
+
+    /**
+     * Returns the virtual filesystem for this persistence manager
+     * @return the virtual filesystem for this persistence manager
+     */
+    public FileSystem getFileSystem() {
+        return fs;
+    }
+
+    /**
+     * Returns the id of the root node
+     * @return the id of the root node
+     */
+    public NodeId getRootNodeId() {
+        return rootNodeId;
+    }
+
+    /**
+     * Returns the namespace registry
+     *
+     * @return the namespace registry
+     */
+    public NamespaceRegistry getNamespaceRegistry() {
+        return nsReg;
+    }
+
+    /**
+     * Returns the node type registry
+     *
+     * @return the node type registry
+     */
+    public NodeTypeRegistry getNodeTypeRegistry() {
+        return ntReg;
+    }
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/PMContext.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java	(revision 0)
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence;
+
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.state.ChangeLog;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
+
+import java.util.Iterator;
+
+/**
+ * Implementation <code>PersistenceManager</code> that handles some
+ * concepts.
+ */
+public abstract class AbstractPersistenceManager implements PersistenceManager {
+
+    /**
+     * {@inheritDoc}
+     */
+    public NodeState createNew(NodeId id) {
+        return new NodeState(id, null, null, NodeState.STATUS_NEW, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PropertyState createNew(PropertyId id) {
+        return new PropertyState(id, PropertyState.STATUS_NEW, false);
+    }
+
+    /**
+     * Right now, this iterates over all items in the changelog and
+     * calls the individual methods that handle single item states
+     * or node references objects. Properly implemented, this method
+     * should ensure that changes are either written completely to
+     * the underlying persistence layer, or not at all.
+     *
+     * {@inheritDoc}
+     */
+    public synchronized void store(ChangeLog changeLog) throws ItemStateException {
+        Iterator iter = changeLog.deletedStates();
+        while (iter.hasNext()) {
+            ItemState state = (ItemState) iter.next();
+            if (state.isNode()) {
+                destroy((NodeState) state);
+            } else {
+                destroy((PropertyState) state);
+            }
+        }
+        iter = changeLog.addedStates();
+        while (iter.hasNext()) {
+            ItemState state = (ItemState) iter.next();
+            if (state.isNode()) {
+                store((NodeState) state);
+            } else {
+                store((PropertyState) state);
+            }
+        }
+        iter = changeLog.modifiedStates();
+        while (iter.hasNext()) {
+            ItemState state = (ItemState) iter.next();
+            if (state.isNode()) {
+                store((NodeState) state);
+            } else {
+                store((PropertyState) state);
+            }
+        }
+        iter = changeLog.modifiedRefs();
+        while (iter.hasNext()) {
+            NodeReferences refs = (NodeReferences) iter.next();
+            if (refs.hasReferences()) {
+                store(refs);
+            } else {
+                if (exists(refs.getId())) {
+                    destroy(refs);
+                }
+            }
+        }
+    }
+
+    /**
+     * Store a node state. Subclass responsibility.
+     *
+     * @param state node state to store
+     * @throws ItemStateException if an error occurs
+     */
+    protected abstract void store(NodeState state) throws ItemStateException;
+
+    /**
+     * Store a property state. Subclass responsibility.
+     *
+     * @param state property state to store
+     * @throws ItemStateException if an error occurs
+     */
+    protected abstract void store(PropertyState state) throws ItemStateException;
+
+    /**
+     * Store a references object. Subclass responsibility.
+     *
+     * @param refs references object to store
+     * @throws ItemStateException if an error occurs
+     */
+    protected abstract void store(NodeReferences refs) throws ItemStateException;
+
+    /**
+     * Destroy a node state. Subclass responsibility.
+     *
+     * @param state node state to destroy
+     * @throws ItemStateException if an error occurs
+     */
+    protected abstract void destroy(NodeState state) throws ItemStateException;
+
+    /**
+     * Destroy a property state. Subclass responsibility.
+     *
+     * @param state property state to destroy
+     * @throws ItemStateException if an error occurs
+     */
+    protected abstract void destroy(PropertyState state) throws ItemStateException;
+
+    /**
+     * Destroy a node references object. Subclass responsibility.
+     *
+     * @param refs node references object to destroy
+     * @throws ItemStateException if an error occurs
+     */
+    protected abstract void destroy(NodeReferences refs)
+            throws ItemStateException;
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/AbstractPersistenceManager.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/db/SimpleDbPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/SimpleDbPersistenceManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/SimpleDbPersistenceManager.java	(revision 0)
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.db;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+import org.apache.jackrabbit.core.persistence.util.Serializer;
+import org.apache.jackrabbit.core.persistence.db.DatabasePersistenceManager;
+
+/**
+ * <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 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>
+ * <li><code>driver</code>: the FQN name of the JDBC driver class</li>
+ * <li><code>url</code>: the database url of the form <code>jdbc:subprotocol:subname</code></li>
+ * <li><code>user</code>: the database user</li>
+ * <li><code>password</code>: the user's password</li>
+ * <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
+ * resources by calling <code>getClass().getResourceAsStream(schema + ".ddl")</code>.
+ * Every line in the specified .ddl file is executed separatly by calling
+ * <code>java.sql.Statement.execute(String)</code> where every occurence of the
+ * the string <code>"${schemaObjectPrefix}"</code> has been replaced with the
+ * value of the property <code>schemaObjectPrefix</code>.
+ * <p/>
+ * The following is a fragment from a sample configuration using MySQL:
+ * <pre>
+ *   &lt;PersistenceManager class="org.apache.jackrabbit.core.persistence.db.SimpleDbPersistenceManager"&gt;
+ *       &lt;param name="driver" value="com.mysql.jdbc.Driver"/&gt;
+ *       &lt;param name="url" value="jdbc:mysql:///test?autoReconnect=true"/&gt;
+ *       &lt;param name="schema" value="mysql"/&gt;
+ *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&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>
+ *   &lt;PersistenceManager class="org.apache.jackrabbit.core.persistence.db.SimpleDbPersistenceManager"&gt;
+ *       &lt;param name="driver" value="in.co.daffodil.db.jdbc.DaffodilDBDriver"/&gt;
+ *       &lt;param name="url" value="jdbc:daffodilDB_embedded:${wsp.name};path=${wsp.home}/../../databases;create=true"/&gt;
+ *       &lt;param name="user" value="daffodil"/&gt;
+ *       &lt;param name="password" value="daffodil"/&gt;
+ *       &lt;param name="schema" value="daffodil"/&gt;
+ *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
+ *       &lt;param name="externalBLOBs" value="false"/&gt;
+ *   &lt;/PersistenceManager&gt;
+ * </pre>
+ * The following is a fragment from a sample configuration using DB2:
+ * <pre>
+ *   &lt;PersistenceManager class="org.apache.jackrabbit.core.persistence.db.SimpleDbPersistenceManager"&gt;
+ *       &lt;param name="driver" value="com.ibm.db2.jcc.DB2Driver"/&gt;
+ *       &lt;param name="url" value="jdbc:db2:test"/&gt;
+ *       &lt;param name="schema" value="db2"/&gt;
+ *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
+ *       &lt;param name="externalBLOBs" value="false"/&gt;
+ *   &lt;/PersistenceManager&gt;
+ * </pre>
+ * The following is a fragment from a sample configuration using MSSQL:
+ * <pre>
+ *   &lt;PersistenceManager class="org.apache.jackrabbit.core.persistence.db.SimpleDbPersistenceManager"&gt;
+ *       &lt;param name="driver" value="com.microsoft.jdbc.sqlserver.SQLServerDriver"/&gt;
+ *       &lt;param name="url" value="jdbc:microsoft:sqlserver://localhost:1433;;DatabaseName=test;SelectMethod=Cursor;"/&gt;
+ *       &lt;param name="schema" value="mssql"/&gt;
+ *       &lt;param name="user" value="sa"/&gt;
+ *       &lt;param name="password" value=""/&gt;
+ *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
+ *       &lt;param name="externalBLOBs" value="false"/&gt;
+ *   &lt;/PersistenceManager&gt;
+ * </pre>
+ * The following is a fragment from a sample configuration using PostgreSQL:
+ * <pre>
+ *   &lt;PersistenceManager class="org.apache.jackrabbit.core.persistence.db.SimpleDbPersistenceManager"&gt;
+ *       &lt;param name="driver" value="org.postgresql.Driver"/&gt;
+ *       &lt;param name="url" value="jdbc:postgresql://localhost/test"/&gt;
+ *       &lt;param name="schema" value="postgresql"/&gt;
+ *       &lt;param name="user" value="postgres"/&gt;
+ *       &lt;param name="password" value="postgres"/&gt;
+ *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
+ *       &lt;param name="externalBLOBs" value="false"/&gt;
+ *   &lt;/PersistenceManager&gt;
+ * </pre>
+ * See also {@link DerbyPersistenceManager}, {@link OraclePersistenceManager}.
+ */
+public class SimpleDbPersistenceManager extends DatabasePersistenceManager {
+
+    protected String driver;
+    protected String url;
+    protected String user;
+    protected String password;
+
+    //----------------------------------------------------< setters & getters >
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    public void setUser(String user) {
+        this.user = user;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getDriver() {
+        return driver;
+    }
+
+    public void setDriver(String driver) {
+        this.driver = driver;
+    }
+
+    //------------------------------------------< DatabasePersistenceManager >
+
+    /**
+     * Returns a JDBC connection acquired using the JDBC {@link DriverManager}.
+     *
+     * @throws ClassNotFoundException if the JDBC driver class is not found
+     * @throws SQLException if a database access error occurs
+     * @see DatabasePersistenceManager#getConnection()
+     */
+    protected Connection getConnection() throws ClassNotFoundException, SQLException {
+        Class.forName(driver);
+        Connection connection = DriverManager.getConnection(url, user, password);
+        return connection;
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/db/SimpleDbPersistenceManager.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/db/mssql.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/mssql.ddl	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/mssql.ddl	(revision 0)
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA image not null)
+create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
+create table ${schemaObjectPrefix}PROP (PROP_ID varchar(1024) not null, PROP_DATA image not null)
+create unique index ${schemaObjectPrefix}PROP_IDX on ${schemaObjectPrefix}PROP (PROP_ID)
+create table ${schemaObjectPrefix}REFS (NODE_ID char(36) not null, REFS_DATA image 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 image not null)
+create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)
Index: src/main/java/org/apache/jackrabbit/core/persistence/db/DatabasePersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/DatabasePersistenceManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/DatabasePersistenceManager.java	(revision 0)
@@ -0,0 +1,1075 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.db;
+
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.persistence.AbstractPersistenceManager;
+import org.apache.jackrabbit.core.fs.FileSystem;
+import org.apache.jackrabbit.core.fs.local.LocalFileSystem;
+import org.apache.jackrabbit.core.state.ChangeLog;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.NoSuchItemStateException;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeReferencesId;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.persistence.PMContext;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.core.persistence.db.SimpleDbPersistenceManager;
+import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.persistence.util.FileSystemBLOBStore;
+import org.apache.jackrabbit.core.persistence.util.Serializer;
+import org.apache.jackrabbit.core.value.BLOBFileValue;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.FilterInputStream;
+import java.io.ByteArrayInputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.DatabaseMetaData;
+
+/**
+ * Abstract base class for database persistence managers. This class
+ * contains common functionality for database persistence manager subclasses
+ * that normally differ only in the way the database connection is acquired.
+ * Subclasses should override the {@link #getConnection()} method to return
+ * the configured database connection.
+ * <p>
+ * See the {@link SimpleDbPersistenceManager} for a detailed description
+ * of the available configuration options and database behaviour.
+ */
+public abstract class DatabasePersistenceManager extends AbstractPersistenceManager {
+
+    /**
+     * Logger instance
+     */
+    private static Logger log = LoggerFactory.getLogger(DatabasePersistenceManager.class);
+
+    protected static final String SCHEMA_OBJECT_PREFIX_VARIABLE =
+            "${schemaObjectPrefix}";
+
+    protected boolean initialized;
+
+    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;
+
+    // jdbc connection
+    protected Connection con;
+
+    // shared prepared statements for NodeState management
+    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;
+
+    // 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>DatabasePersistenceManager</code> instance.
+     */
+    public DatabasePersistenceManager() {
+        schema = "default";
+        schemaObjectPrefix = "";
+        externalBLOBs = true;
+        initialized = false;
+    }
+
+    //----------------------------------------------------< setters & getters >
+
+    public String getSchemaObjectPrefix() {
+        return schemaObjectPrefix;
+    }
+
+    public void setSchemaObjectPrefix(String schemaObjectPrefix) {
+        // make sure prefix is all uppercase
+        this.schemaObjectPrefix = schemaObjectPrefix.toUpperCase();
+    }
+
+    public String getSchema() {
+        return schema;
+    }
+
+    public void setSchema(String schema) {
+        this.schema = schema;
+    }
+
+    public boolean isExternalBLOBs() {
+        return externalBLOBs;
+    }
+
+    public void setExternalBLOBs(boolean externalBLOBs) {
+        this.externalBLOBs = externalBLOBs;
+    }
+
+    public void setExternalBLOBs(String externalBLOBs) {
+        this.externalBLOBs = Boolean.valueOf(externalBLOBs).booleanValue();
+    }
+
+    //---------------------------------------------------< PersistenceManager >
+    /**
+     * {@inheritDoc}
+     */
+    public void init(PMContext context) throws Exception {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+
+        // setup jdbc connection
+        initConnection();
+
+        // make sure schemaObjectPrefix consists of legal name characters only
+        prepareSchemaObjectPrefix();
+
+        // check if schema objects exist and create them if necessary
+        checkSchema();
+
+        // prepare statements
+        nodeStateInsert =
+                con.prepareStatement("insert into "
+                + schemaObjectPrefix + "NODE (NODE_DATA, NODE_ID) values (?, ?)");
+        nodeStateUpdate =
+                con.prepareStatement("update "
+                + schemaObjectPrefix + "NODE set NODE_DATA = ? where NODE_ID = ?");
+        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 = ?");
+
+        propertyStateInsert =
+                con.prepareStatement("insert into "
+                + schemaObjectPrefix + "PROP (PROP_DATA, PROP_ID) values (?, ?)");
+        propertyStateUpdate =
+                con.prepareStatement("update "
+                + schemaObjectPrefix + "PROP set PROP_DATA = ? where PROP_ID = ?");
+        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 = ?");
+
+        nodeReferenceInsert =
+                con.prepareStatement("insert into "
+                + schemaObjectPrefix + "REFS (REFS_DATA, NODE_ID) values (?, ?)");
+        nodeReferenceUpdate =
+                con.prepareStatement("update "
+                + schemaObjectPrefix + "REFS set REFS_DATA = ? where NODE_ID = ?");
+        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;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void close() throws Exception {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            // close shared prepared statements
+            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
+            closeConnection(con);
+
+        } finally {
+            initialized = false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void store(ChangeLog changeLog)
+            throws ItemStateException {
+        ItemStateException ise = null;
+        try {
+            super.store(changeLog);
+        } catch (ItemStateException e) {
+            ise = e;
+        } finally {
+            if (ise == null) {
+                // storing the changes succeeded, now commit the changes
+                try {
+                    con.commit();
+                } catch (SQLException e) {
+                    String msg = "committing change log failed";
+                    log.error(msg, e);
+                    throw new ItemStateException(msg, e);
+                }
+            } else {
+                // storing the changes failed, rollback changes
+                try {
+                    con.rollback();
+                } catch (SQLException e) {
+                    String msg = "rollback of change log failed";
+                    log.error(msg, e);
+                }
+                // re-throw original exception
+                throw ise;
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public NodeState load(NodeId id)
+            throws NoSuchItemStateException, ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        PreparedStatement stmt = nodeStateSelect;
+        synchronized (stmt) {
+            ResultSet rs = null;
+            InputStream in = null;
+            try {
+                stmt.setString(1, id.toString());
+                stmt.execute();
+                rs = stmt.getResultSet();
+                if (!rs.next()) {
+                    throw new NoSuchItemStateException(id.toString());
+                }
+
+                in = rs.getBinaryStream(1);
+                NodeState state = createNew(id);
+                Serializer.deserialize(state, in);
+
+                return state;
+            } catch (Exception e) {
+                if (e instanceof NoSuchItemStateException) {
+                    throw (NoSuchItemStateException) e;
+                }
+                String msg = "failed to read node state: " + id;
+                log.error(msg, e);
+                throw new ItemStateException(msg, e);
+            } finally {
+                closeStream(in);
+                closeResultSet(rs);
+                resetStatement(stmt);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PropertyState load(PropertyId id)
+            throws NoSuchItemStateException, ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        PreparedStatement stmt = propertyStateSelect;
+        synchronized (stmt) {
+            ResultSet rs = null;
+            InputStream in = null;
+            try {
+                stmt.setString(1, id.toString());
+                stmt.execute();
+                rs = stmt.getResultSet();
+                if (!rs.next()) {
+                    throw new NoSuchItemStateException(id.toString());
+                }
+
+                in = rs.getBinaryStream(1);
+                PropertyState state = createNew(id);
+                Serializer.deserialize(state, in, blobStore);
+
+                return state;
+            } catch (Exception e) {
+                if (e instanceof NoSuchItemStateException) {
+                    throw (NoSuchItemStateException) e;
+                }
+                String msg = "failed to read property state: " + id;
+                log.error(msg, e);
+                throw new ItemStateException(msg, e);
+            } finally {
+                closeStream(in);
+                closeResultSet(rs);
+                resetStatement(stmt);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This method uses shared <code>PreparedStatement</code>s which must
+     * be executed strictly sequentially. Because this method synchronizes on
+     * the persistence manager instance there is no need to synchronize on the
+     * shared statement. If the method would not be sychronized the shared
+     * statements would have to be synchronized.
+     */
+    public synchronized void store(NodeState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // check if insert or update
+        boolean update = state.getStatus() != ItemState.STATUS_NEW;
+        //boolean update = exists(state.getId());
+        PreparedStatement stmt = (update) ? nodeStateUpdate : nodeStateInsert;
+
+        try {
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize node state
+            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.getNodeId().toString());
+            stmt.executeUpdate();
+
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to write node state: " + state.getNodeId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        } finally {
+            resetStatement(stmt);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This method uses shared <code>PreparedStatement</code>s which must
+     * be executed strictly sequentially. Because this method synchronizes on
+     * the persistence manager instance there is no need to synchronize on the
+     * shared statement. If the method would not be sychronized the shared
+     * statements would have to be synchronized.
+     */
+    public synchronized void store(PropertyState state)
+            throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // check if insert or update
+        boolean update = state.getStatus() != ItemState.STATUS_NEW;
+        //boolean update = exists(state.getId());
+        PreparedStatement stmt = (update) ? propertyStateUpdate : propertyStateInsert;
+
+        try {
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize property state
+            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.getPropertyId().toString());
+            stmt.executeUpdate();
+
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to write property state: " + state.getPropertyId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        } finally {
+            resetStatement(stmt);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void destroy(NodeState state)
+            throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        PreparedStatement stmt = nodeStateDelete;
+        try {
+            stmt.setString(1, state.getNodeId().toString());
+            stmt.executeUpdate();
+        } catch (Exception e) {
+            String msg = "failed to delete node state: " + state.getNodeId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        } finally {
+            resetStatement(stmt);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void destroy(PropertyState state)
+            throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // make sure binary values (BLOBs) are properly removed
+        InternalValue[] values = state.getValues();
+        if (values != null) {
+            for (int i = 0; i < values.length; i++) {
+                InternalValue val = values[i];
+                if (val != null) {
+                    if (val.getType() == PropertyType.BINARY) {
+                        BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
+                        // delete internal resource representation of BLOB value
+                        blobVal.delete(true);
+                        // also remove from BLOBStore
+                        String blobId = blobStore.createId(state.getPropertyId(), i);
+                        try {
+                            blobStore.remove(blobId);
+                        } catch (Exception e) {
+                            log.warn("failed to remove from BLOBStore: " + blobId, e);
+                        }
+                    }
+                }
+            }
+        }
+
+        PreparedStatement stmt = propertyStateDelete;
+        try {
+            stmt.setString(1, state.getPropertyId().toString());
+            stmt.executeUpdate();
+        } catch (Exception e) {
+            String msg = "failed to delete property state: " + state.getPropertyId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        } finally {
+            resetStatement(stmt);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public NodeReferences load(NodeReferencesId targetId)
+            throws NoSuchItemStateException, ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        PreparedStatement stmt = nodeReferenceSelect;
+        synchronized (stmt) {
+            ResultSet rs = null;
+            InputStream in = null;
+            try {
+                stmt.setString(1, targetId.toString());
+                stmt.execute();
+                rs = stmt.getResultSet();
+                if (!rs.next()) {
+                    throw new NoSuchItemStateException(targetId.toString());
+                }
+
+                in = rs.getBinaryStream(1);
+                NodeReferences refs = new NodeReferences(targetId);
+                Serializer.deserialize(refs, in);
+
+                return refs;
+            } catch (Exception e) {
+                if (e instanceof NoSuchItemStateException) {
+                    throw (NoSuchItemStateException) e;
+                }
+                String msg = "failed to read node references: " + targetId;
+                log.error(msg, e);
+                throw new ItemStateException(msg, e);
+            } finally {
+                closeStream(in);
+                closeResultSet(rs);
+                resetStatement(stmt);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * This method uses shared <code>PreparedStatement</code>s which must
+     * be executed strictly sequentially. Because this method synchronizes on
+     * the persistence manager instance there is no need to synchronize on the
+     * shared statement. If the method would not be sychronized the shared
+     * statements would have to be synchronized.
+     */
+    public synchronized void store(NodeReferences refs)
+            throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // check if insert or update
+        boolean update = exists(refs.getId());
+        PreparedStatement stmt = (update) ? nodeReferenceUpdate : nodeReferenceInsert;
+
+        try {
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize references
+            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.getId().toString());
+            stmt.executeUpdate();
+
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to write node references: " + refs.getId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        } finally {
+            resetStatement(stmt);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void destroy(NodeReferences refs)
+            throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        PreparedStatement stmt = nodeReferenceDelete;
+        try {
+            stmt.setString(1, refs.getId().toString());
+            stmt.executeUpdate();
+        } catch (Exception e) {
+            String msg = "failed to delete node references: " + refs.getId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        } finally {
+            resetStatement(stmt);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean exists(NodeId id) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        PreparedStatement stmt = nodeStateSelectExist;
+        synchronized (stmt) {
+            ResultSet rs = null;
+            try {
+                stmt.setString(1, id.toString());
+                stmt.execute();
+                rs = stmt.getResultSet();
+
+                // a node state exists if the result has at least one entry
+                return rs.next();
+            } catch (Exception e) {
+                String msg = "failed to check existence of node state: " + id;
+                log.error(msg, e);
+                throw new ItemStateException(msg, e);
+            } finally {
+                closeResultSet(rs);
+                resetStatement(stmt);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean exists(PropertyId id) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        PreparedStatement stmt = propertyStateSelectExist;
+        synchronized (stmt) {
+            ResultSet rs = null;
+            try {
+                stmt.setString(1, id.toString());
+                stmt.execute();
+                rs = stmt.getResultSet();
+
+                // a property state exists if the result has at least one entry
+                return rs.next();
+            } catch (Exception e) {
+                String msg = "failed to check existence of property state: " + id;
+                log.error(msg, e);
+                throw new ItemStateException(msg, e);
+            } finally {
+                closeResultSet(rs);
+                resetStatement(stmt);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean exists(NodeReferencesId targetId) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        PreparedStatement stmt = nodeReferenceSelectExist;
+        synchronized (stmt) {
+            ResultSet rs = null;
+            try {
+                stmt.setString(1, targetId.toString());
+                stmt.execute();
+                rs = stmt.getResultSet();
+
+                // a reference exists if the result has at least one entry
+                return rs.next();
+            } catch (Exception e) {
+                String msg = "failed to check existence of node references: "
+                        + targetId;
+                log.error(msg, e);
+                throw new ItemStateException(msg, e);
+            } finally {
+                closeResultSet(rs);
+                resetStatement(stmt);
+            }
+        }
+    }
+
+    //----------------------------------< misc. helper methods & overridables >
+
+    /**
+     * Initializes the database connection used by this file system.
+     * <p>
+     * Subclasses should normally override the {@link #getConnection()}
+     * method instead of this one. The default implementation calls
+     * {@link #getConnection()} to get the database connection and disables
+     * the autocommit feature.
+     *
+     * @throws Exception if an error occurs
+     */
+    protected void initConnection() throws Exception {
+        con = getConnection();
+        con.setAutoCommit(false);
+    }
+
+    /**
+     * Abstract factory method for creating a new database connection. This
+     * method is called by {@link #init(PMContext)} when the persistence
+     * manager is started. The returned connection should come with the default
+     * JDBC settings, as the {@link #init(PMContext)} method will explicitly
+     * set the <code>autoCommit</code> and other properties as needed.
+     * <p>
+     * Note that the returned database connection is kept during the entire
+     * lifetime of the persistence manager, after which it is closed by
+     * {@link #close()} using the {@link #closeConnection(Connection)} method.
+     *
+     * @return new connection
+     * @throws Exception if an error occurs
+     */
+    protected Connection getConnection() throws Exception {
+        throw new UnsupportedOperationException("Override in a subclass!");
+    }
+
+    /**
+     * Closes the given database connection. This method is called by
+     * {@link #close()} to close the connection acquired using
+     * {@link #getConnection()} when the persistence manager was started.
+     * <p>
+     * The default implementation just calls the {@link Connection#close()}
+     * method of the given connection, but subclasses can override this
+     * method to provide more extensive database and connection cleanup.
+     *
+     * @param connection database connection
+     * @throws Exception if an error occurs
+     */
+    protected void closeConnection(Connection connection) throws Exception {
+        connection.close();
+    }
+
+    /**
+     * Resets the given <code>PreparedStatement</code> by clearing the parameters
+     * and warnings contained.
+     * <p/>
+     * NOTE: This method MUST be called in a synchronized context as neither
+     * this method nor the <code>PreparedStatement</code> instance on which it
+     * operates are thread safe.
+     *
+     * @param stmt The <code>PreparedStatement</code> to reset. If
+     *             <code>null</code> this method does nothing.
+     */
+    protected void resetStatement(PreparedStatement stmt) {
+        if (stmt != null) {
+            try {
+                stmt.clearParameters();
+                stmt.clearWarnings();
+            } catch (SQLException se) {
+                logException("failed resetting PreparedStatement", se);
+            }
+        }
+    }
+
+    protected void closeResultSet(ResultSet rs) {
+        if (rs != null) {
+            try {
+                rs.close();
+            } catch (SQLException se) {
+                logException("failed closing ResultSet", se);
+            }
+        }
+    }
+
+    protected void closeStream(InputStream in) {
+        if (in != null) {
+            try {
+                in.close();
+            } catch (IOException ignore) {
+            }
+        }
+    }
+
+    protected void closeStatement(Statement stmt) {
+        if (stmt != null) {
+            try {
+                stmt.close();
+            } catch (SQLException se) {
+                logException("failed closing Statement", se);
+            }
+        }
+    }
+
+    protected void logException(String message, SQLException se) {
+        if (message != null) {
+            log.error(message);
+        }
+        log.error("    reason: " + se.getMessage());
+        log.error("state/code: " + se.getSQLState() + "/" + se.getErrorCode());
+        log.debug("      dump:", se);
+    }
+
+    /**
+     * Makes sure that <code>schemaObjectPrefix</code> does only consist of
+     * characters that are allowed in names on the target database. Illegal
+     * characters will be escaped as necessary.
+     *
+     * @throws Exception if an error occurs
+     */
+    protected void prepareSchemaObjectPrefix() throws Exception {
+        DatabaseMetaData metaData = con.getMetaData();
+        String legalChars = metaData.getExtraNameCharacters();
+        legalChars += "ABCDEFGHIJKLMNOPQRSTUVWXZY0123456789_";
+
+        String prefix = schemaObjectPrefix.toUpperCase();
+        StringBuffer escaped = new StringBuffer();
+        for (int i = 0; i < prefix.length(); i++) {
+            char c = prefix.charAt(i);
+            if (legalChars.indexOf(c) == -1) {
+                escaped.append("_x");
+                String hex = Integer.toHexString(c);
+                escaped.append("0000".toCharArray(), 0, 4 - hex.length());
+                escaped.append(hex);
+                escaped.append("_");
+            } else {
+                escaped.append(c);
+            }
+        }
+        schemaObjectPrefix = escaped.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 {
+        DatabaseMetaData metaData = con.getMetaData();
+        String tableName = schemaObjectPrefix + "NODE";
+        if (metaData.storesLowerCaseIdentifiers()) {
+            tableName = tableName.toLowerCase();
+        } else if (metaData.storesUpperCaseIdentifiers()) {
+            tableName = tableName.toUpperCase();
+        }
+
+        ResultSet rs = metaData.getTables(null, null, tableName, null);
+        boolean schemaExists;
+        try {
+            schemaExists = rs.next();
+        } finally {
+            rs.close();
+        }
+
+        if (!schemaExists) {
+            // read ddl from resources
+            InputStream in = getClass().getResourceAsStream(schema + ".ddl");
+            if (in == null) {
+                String msg = "Configuration error: unknown schema '" + schema + "'";
+                log.debug(msg);
+                throw new RepositoryException(msg);
+            }
+            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+            Statement stmt = con.createStatement();
+            try {
+                String sql = reader.readLine();
+                while (sql != null) {
+                    // Skip comments and empty lines
+                    if (!sql.startsWith("#") && sql.length() > 0) {
+                        // replace prefix variable
+                        sql = Text.replace(sql, SCHEMA_OBJECT_PREFIX_VARIABLE, schemaObjectPrefix);
+                        // execute sql stmt
+                        stmt.executeUpdate(sql);
+                    }
+                    // read next sql stmt
+                    sql = reader.readLine();
+                }
+                // commit the changes
+                con.commit();
+            } 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 InputStream get(String blobId) throws Exception {
+            PreparedStatement stmt = blobSelect;
+            synchronized (stmt) {
+                try {
+                    stmt.setString(1, blobId);
+                    stmt.execute();
+                    final ResultSet rs = stmt.getResultSet();
+                    if (!rs.next()) {
+                        closeResultSet(rs);
+                        throw new Exception("no such BLOB: " + blobId);
+                    }
+                    InputStream in = rs.getBinaryStream(1);
+                    if (in == null) {
+                        // some databases treat zero-length values as NULL;
+                        // return empty InputStream in such a case
+                        closeResultSet(rs);
+                        return new ByteArrayInputStream(new byte[0]);
+                    }
+
+                    /**
+                     * return an InputStream wrapper in order to
+                     * close the ResultSet when the stream is closed
+                     */
+                    return new FilterInputStream(in) {
+                        public void close() throws IOException {
+                            in.close();
+                            // now it's safe to close ResultSet
+                            closeResultSet(rs);
+                        }
+                    };
+                } 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);
+            }
+        }
+    }
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/db/DatabasePersistenceManager.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/db/postgresql.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/postgresql.ddl	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/postgresql.ddl	(revision 0)
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA bytea not null)
+create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
+create table ${schemaObjectPrefix}PROP (PROP_ID varchar not null, PROP_DATA bytea not null)
+create unique index ${schemaObjectPrefix}PROP_IDX on ${schemaObjectPrefix}PROP (PROP_ID)
+create table ${schemaObjectPrefix}REFS (NODE_ID char(36) not null, REFS_DATA bytea not null)
+create unique index ${schemaObjectPrefix}REFS_IDX on ${schemaObjectPrefix}REFS (NODE_ID)
+create table ${schemaObjectPrefix}BINVAL (BINVAL_ID varchar not null, BINVAL_DATA bytea not null)
+create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)
Index: src/main/java/org/apache/jackrabbit/core/persistence/db/DerbyPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/DerbyPersistenceManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/DerbyPersistenceManager.java	(revision 0)
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.db;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.core.persistence.db.DatabasePersistenceManager;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+/**
+ * <code>DerbyPersistenceManager</code> is a JDBC-based
+ * <code>PersistenceManager</code> for Jackrabbit that persists
+ * <code>ItemState</code> and <code>NodeReferences</code> objects in an
+ * embedded Derby database using a simple custom serialization format 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>
+ * <li><code>url</code>: the database url of the form
+ * <code>"jdbc:derby:[db];[attributes]"</code></li>
+ * <li><code>schemaObjectPrefix</code>: prefix to be prepended to schema objects</li>
+ * <li><code>driver</code>: the FQN name of the JDBC driver class
+ * (default: <code>"org.apache.derby.jdbc.EmbeddedDriver"</code>)</li>
+ * <li><code>schema</code>: type of schema to be used
+ * (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/>
+ * The following is a fragment from a sample configuration:
+ * <pre>
+ *   &lt;PersistenceManager class="org.apache.jackrabbit.core.persistence.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>
+ */
+public class DerbyPersistenceManager extends SimpleDbPersistenceManager {
+
+    /**
+     * Logger instance
+     */
+    private static Logger log = LoggerFactory.getLogger(DerbyPersistenceManager.class);
+
+    /**
+     * Creates a new <code>SimpleDbPersistenceManager</code> instance.
+     */
+    public DerbyPersistenceManager() {
+        // preset some attributes to reasonable defaults
+        schema = "derby";
+        driver = "org.apache.derby.jdbc.EmbeddedDriver";
+        schemaObjectPrefix = "";
+        user = "";
+        password = "";
+    }
+
+    //------------------------------------------< DatabasePersistenceManager >
+
+    /**
+     * Closes the given connection by shutting down the embedded Derby
+     * database.
+     *
+     * @param connection database connection
+     * @throws SQLException if an error occurs
+     * @see DatabasePersistenceManager#closeConnection(Connection)
+     */
+    protected void closeConnection(Connection connection) throws SQLException {
+        // prepare connection url for issuing shutdown command
+        String url = connection.getMetaData().getURL();
+        int pos = url.lastIndexOf(';');
+        if (pos != -1) {
+            // strip any attributes from connection url
+            url = url.substring(0, pos);
+        }
+        url += ";shutdown=true";
+
+        // we have to reset the connection to 'autoCommit=true' before closing it;
+        // otherwise Derby would mysteriously complain about some pending uncommitted
+        // changes which can't possibly be true.
+        // @todo further investigate
+        connection.setAutoCommit(true);
+
+        // now it's safe to shutdown the embedded Derby database
+        try {
+            DriverManager.getConnection(url);
+        } catch (SQLException e) {
+            // a shutdown command always raises a SQLException
+            log.info(e.getMessage());
+        }
+    }
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/db/DerbyPersistenceManager.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/db/OraclePersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/OraclePersistenceManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/OraclePersistenceManager.java	(revision 0)
@@ -0,0 +1,414 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.db;
+
+import org.apache.jackrabbit.core.persistence.PMContext;
+import org.apache.jackrabbit.core.persistence.util.Serializer;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.state.ItemState;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.DatabaseMetaData;
+import java.sql.Statement;
+
+/**
+ * <code>OraclePersistenceManager</code> is a JDBC-based
+ * <code>PersistenceManager</code> for Jackrabbit that persists
+ * <code>ItemState</code> and <code>NodeReferences</code> objects in Oracle
+ * database using a simple custom serialization format 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>
+ * <li><code>driver</code>: the FQN name of the JDBC driver class
+ * (default: <code>"oracle.jdbc.OracleDriver"</code>)</li>
+ * <li><code>schema</code>: type of schema to be used
+ * (default: <code>"oracle"</code>)</li>
+ * <li><code>url</code>: the database url (e.g.
+ * <code>"jdbc:oracle:thin:@[host]:[port]:[sid]"</code>)</li>
+ * <li><code>user</code>: the database user</li>
+ * <li><code>password</code>: the user's password</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>
+ * See also {@link SimpleDbPersistenceManager}.
+ * <p/>
+ * The following is a fragment from a sample configuration:
+ * <pre>
+ *   &lt;PersistenceManager class="org.apache.jackrabbit.core.persistence.db.OraclePersistenceManager"&gt;
+ *       &lt;param name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/&gt;
+ *       &lt;param name="user" value="scott"/&gt;
+ *       &lt;param name="password" value="tiger"/&gt;
+ *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
+ *       &lt;param name="externalBLOBs" value="false"/&gt;
+ *  &lt;/PersistenceManager&gt;
+ * </pre>
+ */
+public class OraclePersistenceManager extends SimpleDbPersistenceManager {
+
+    /**
+     * Logger instance
+     */
+    private static Logger log = LoggerFactory.getLogger(OraclePersistenceManager.class);
+
+    private Class blobClass;
+    private Integer DURATION_SESSION_CONSTANT;
+    private Integer MODE_READWRITE_CONSTANT;
+
+    /**
+     * Creates a new <code>OraclePersistenceManager</code> instance.
+     */
+    public OraclePersistenceManager() {
+        // preset some attributes to reasonable defaults
+        schema = "oracle";
+        driver = "oracle.jdbc.OracleDriver";
+        schemaObjectPrefix = "";
+        user = "";
+        password = "";
+        initialized = false;
+    }
+
+    //---------------------------------< SimpleDbPersistenceManager overrides >
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Retrieve the <code>oracle.sql.BLOB</code> class via reflection, and
+     * initialize the values for the <code>DURATION_SESSION</code> and
+     * <code>MODE_READWRITE</code> constants defined there.
+     * @see oracle.sql.BLOB#DURATION_SESSION
+     * @see oracle.sql.BLOB#MODE_READWRITE
+     */
+    public void init(PMContext context) throws Exception {
+        super.init(context);
+
+        if (!externalBLOBs) {
+            blobStore = new OracleBLOBStore();
+        }
+
+        // initialize oracle.sql.BLOB class & constants
+
+        // use the Connection object for using the exact same
+        // class loader that the Oracle driver was loaded with
+        blobClass = con.getClass().getClassLoader().loadClass("oracle.sql.BLOB");
+        DURATION_SESSION_CONSTANT =
+                new Integer(blobClass.getField("DURATION_SESSION").getInt(null));
+        MODE_READWRITE_CONSTANT =
+                new Integer(blobClass.getField("MODE_READWRITE").getInt(null));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void store(NodeState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // check if insert or update
+        boolean update = state.getStatus() != ItemState.STATUS_NEW;
+        //boolean update = exists((NodeId) state.getId());
+        PreparedStatement stmt = (update) ? nodeStateUpdate : nodeStateInsert;
+
+        Blob blob = null;
+        try {
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize node state
+            Serializer.serialize(state, out);
+
+            // we are synchronized on this instance, therefore we do not
+            // not have to additionally synchronize on the preparedStatement
+
+            blob = createTemporaryBlob(new ByteArrayInputStream(out.toByteArray()));
+            stmt.setBlob(1, blob);
+            stmt.setString(2, state.getId().toString());
+            stmt.executeUpdate();
+
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to write node state: " + state.getId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        } finally {
+            resetStatement(stmt);
+            if (blob != null) {
+                try {
+                    freeTemporaryBlob(blob);
+                } catch (Exception e1) {
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void store(PropertyState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // check if insert or update
+        boolean update = state.getStatus() != ItemState.STATUS_NEW;
+        //boolean update = exists((PropertyId) state.getId());
+        PreparedStatement stmt = (update) ? propertyStateUpdate : propertyStateInsert;
+
+        Blob blob = null;
+        try {
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize property state
+            Serializer.serialize(state, out, blobStore);
+
+            // we are synchronized on this instance, therefore we do not
+            // not have to additionally synchronize on the preparedStatement
+
+            blob = createTemporaryBlob(new ByteArrayInputStream(out.toByteArray()));
+            stmt.setBlob(1, blob);
+            stmt.setString(2, state.getId().toString());
+            stmt.executeUpdate();
+
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to write property state: " + state.getId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        } finally {
+            resetStatement(stmt);
+            if (blob != null) {
+                try {
+                    freeTemporaryBlob(blob);
+                } catch (Exception e1) {
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void store(NodeReferences refs) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // check if insert or update
+        boolean update = exists(refs.getId());
+        PreparedStatement stmt = (update) ? nodeReferenceUpdate : nodeReferenceInsert;
+
+        Blob blob = null;
+        try {
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize references
+            Serializer.serialize(refs, out);
+
+            // we are synchronized on this instance, therefore we do not
+            // not have to additionally synchronize on the preparedStatement
+
+            blob = createTemporaryBlob(new ByteArrayInputStream(out.toByteArray()));
+            stmt.setBlob(1, blob);
+            stmt.setString(2, refs.getId().toString());
+            stmt.executeUpdate();
+
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to write node references: " + refs.getId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        } finally {
+            resetStatement(stmt);
+            if (blob != null) {
+                try {
+                    freeTemporaryBlob(blob);
+                } catch (Exception e1) {
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Overridden in order to support multiple oracle schemas. Note that
+     * schema names in Oracle correspond to the username of the connection.
+     * See http://issues.apache.org/jira/browse/JCR-582
+     *
+     * @throws Exception if an error occurs
+     */
+    protected void checkSchema() throws Exception {
+        DatabaseMetaData metaData = con.getMetaData();
+        String tableName = schemaObjectPrefix + "NODE";
+        if (metaData.storesLowerCaseIdentifiers()) {
+            tableName = tableName.toLowerCase();
+        } else if (metaData.storesUpperCaseIdentifiers()) {
+            tableName = tableName.toUpperCase();
+        }
+        String userName = metaData.getUserName();
+
+        ResultSet rs = metaData.getTables(null, userName, tableName, null);
+        boolean schemaExists;
+        try {
+            schemaExists = rs.next();
+        } finally {
+            rs.close();
+        }
+
+        if (!schemaExists) {
+            // read ddl from resources
+            InputStream in = getClass().getResourceAsStream(schema + ".ddl");
+            if (in == null) {
+                String msg = "Configuration error: unknown schema '" + schema + "'";
+                log.debug(msg);
+                throw new RepositoryException(msg);
+            }
+            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+            Statement stmt = con.createStatement();
+            try {
+                String sql = reader.readLine();
+                while (sql != null) {
+                    // Skip comments and empty lines
+                    if (!sql.startsWith("#") && sql.length() > 0) {
+                        // replace prefix variable
+                        sql = Text.replace(sql, SCHEMA_OBJECT_PREFIX_VARIABLE, schemaObjectPrefix);
+                        // execute sql stmt
+                        stmt.executeUpdate(sql);
+                    }
+                    // read next sql stmt
+                    sql = reader.readLine();
+                }
+                // commit the changes
+                con.commit();
+            } finally {
+                closeStream(in);
+                closeStatement(stmt);
+            }
+        }
+    }
+
+    //----------------------------------------< oracle-specific blob handling >
+    /**
+     * Creates a temporary oracle.sql.BLOB instance via reflection and spools
+     * the contents of the specified stream.
+     */
+    protected Blob createTemporaryBlob(InputStream in) throws Exception {
+        /*
+        BLOB blob = BLOB.createTemporary(con, false, BLOB.DURATION_SESSION);
+        blob.open(BLOB.MODE_READWRITE);
+        OutputStream out = blob.getBinaryOutputStream();
+        ...
+        out.flush();
+        out.close();
+        blob.close();
+        return blob;
+        */
+        Method createTemporary = blobClass.getMethod("createTemporary",
+                new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE});
+        Object blob = createTemporary.invoke(null,
+                new Object[]{con, Boolean.FALSE, DURATION_SESSION_CONSTANT});
+        Method open = blobClass.getMethod("open", new Class[]{Integer.TYPE});
+        open.invoke(blob, new Object[]{MODE_READWRITE_CONSTANT});
+        Method getBinaryOutputStream =
+                blobClass.getMethod("getBinaryOutputStream", new Class[0]);
+        OutputStream out = (OutputStream) getBinaryOutputStream.invoke(blob, null);
+        try {
+            int read;
+            byte[] buf = new byte[8192];
+            while ((read = in.read(buf, 0, buf.length)) > -1) {
+                out.write(buf, 0, read);
+            }
+        } finally {
+            try {
+                out.flush();
+            } catch (IOException ioe) {
+            }
+            out.close();
+        }
+        Method close = blobClass.getMethod("close", new Class[0]);
+        close.invoke(blob, null);
+        return (Blob) blob;
+    }
+
+    /**
+     * Frees a temporary oracle.sql.BLOB instance via reflection.
+     */
+    protected void freeTemporaryBlob(Object blob) throws Exception {
+        // blob.freeTemporary();
+        Method freeTemporary = blobClass.getMethod("freeTemporary", new Class[0]);
+        freeTemporary.invoke(blob, null);
+    }
+
+    //--------------------------------------------------------< inner classes >
+    class OracleBLOBStore extends DbBLOBStore {
+        /**
+         * {@inheritDoc}
+         */
+        public synchronized void put(String blobId, InputStream in, long size)
+                throws Exception {
+            PreparedStatement stmt = blobSelectExist;
+            Blob blob = null;
+            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;
+
+                blob = createTemporaryBlob(in);
+                stmt.setBlob(1, blob);
+                stmt.setString(2, blobId);
+                stmt.executeUpdate();
+            } finally {
+                resetStatement(stmt);
+                if (blob != null) {
+                    try {
+                        freeTemporaryBlob(blob);
+                    } catch (Exception e1) {
+                    }
+                }
+            }
+        }
+    }
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/db/OraclePersistenceManager.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/db/default.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/default.ddl	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/default.ddl	(revision 0)
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA varbinary not null)
+create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
+create table ${schemaObjectPrefix}PROP (PROP_ID varchar not null, PROP_DATA varbinary not null)
+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)
Index: src/main/java/org/apache/jackrabbit/core/persistence/db/mysql.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/mysql.ddl	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/mysql.ddl	(revision 0)
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA blob not null)
+create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
+create table ${schemaObjectPrefix}PROP (PROP_ID varchar(255) not null, PROP_DATA blob not null)
+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)
Index: src/main/java/org/apache/jackrabbit/core/persistence/db/derby.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/derby.ddl	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/derby.ddl	(revision 0)
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA blob not null)
+create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
+create table ${schemaObjectPrefix}PROP (PROP_ID varchar(1024) not null, PROP_DATA blob not null)
+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)
Index: src/main/java/org/apache/jackrabbit/core/persistence/db/oracle.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/oracle.ddl	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/oracle.ddl	(revision 0)
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA blob not null)
+create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
+create table ${schemaObjectPrefix}PROP (PROP_ID varchar2(1024) not null, PROP_DATA blob not null)
+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 varchar2(1024) not null, BINVAL_DATA blob null)
+create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)
Index: src/main/java/org/apache/jackrabbit/core/persistence/db/db2.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/db2.ddl	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/db2.ddl	(revision 0)
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA blob not null)
+create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
+create table ${schemaObjectPrefix}PROP (PROP_ID varchar(1000) not null, PROP_DATA blob not null)
+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(1000) not null, BINVAL_DATA blob(100M) not null)
+create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)
Index: src/main/java/org/apache/jackrabbit/core/persistence/db/JNDIDatabasePersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/JNDIDatabasePersistenceManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/JNDIDatabasePersistenceManager.java	(revision 0)
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.db;
+
+import org.apache.jackrabbit.core.persistence.db.DatabasePersistenceManager;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+
+/**
+ * Database persistence manager that uses JNDI to acquire the database
+ * connection. The JNDI location of the {@link DataSource} to be used in
+ * given as the <code>dataSourceLocation</code> configuration property.
+ * See the {@link SimpleDbPersistenceManager} for more configuration
+ * details.
+ * <p>
+ * <strong>WARNING:</strong> The acquired database connection is kept
+ * for the entire lifetime of the persistence manager instance. The
+ * configured data source should be prepared for this.
+ */
+public class JNDIDatabasePersistenceManager extends DatabasePersistenceManager {
+
+    /**
+     * JNDI location of the data source used to acquire database connections.
+     */
+    private String dataSourceLocation;
+
+    //----------------------------------------------------< setters & getters >
+
+    /**
+     * Returns the JNDI location of the data source.
+     *
+     * @return data source location
+     */
+    public String getDataSourceLocation() {
+        return dataSourceLocation;
+    }
+
+    /**
+     * Sets the JNDI location of the data source.
+     *
+     * @param dataSourceLocation data source location
+     */
+    public void setDataSourceLocation(String dataSourceLocation) {
+        this.dataSourceLocation = dataSourceLocation;
+    }
+
+    //-------------------------------------------< DatabasePersistenceManager >
+
+    /**
+     * Returns a JDBC connection from a {@link DataSource} acquired from JNDI
+     * with the configured data source location.
+     *
+     * @return new database connection
+     * @throws NamingException if the given data source location does not exist
+     * @throws SQLException if a database access error occurs
+     * @see DatabasePersistenceManager#getConnection()
+     */
+    protected Connection getConnection() throws NamingException, SQLException {
+        InitialContext ic = new InitialContext();
+        DataSource dataSource = (DataSource) ic.lookup(dataSourceLocation);
+        return dataSource.getConnection();
+    }
+
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/db/JNDIDatabasePersistenceManager.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/db/daffodil.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/daffodil.ddl	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/daffodil.ddl	(revision 0)
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA blob not null)
+create index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
+create table ${schemaObjectPrefix}PROP (PROP_ID varchar(1024) not null, PROP_DATA blob not null)
+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)
Index: src/main/java/org/apache/jackrabbit/core/persistence/db/package.html
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/db/package.html	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/db/package.html	(revision 0)
@@ -0,0 +1,32 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<body>
+This package contains the class <code>{@link SimpleDbPersistenceManager}</code>,
+a simple generic JDBC-based <code>PersistenceManager</code> for Jackrabbit.
+<p/>
+It also contains [schemaName].ddl files which are read by
+<code>{@link SimpleDbPersistenceManager}</code>  in order to automatically
+create the required schema objects on the target database. Every line in a
+[schemaName].ddl file is executed separatly by calling
+<code>java.sql.Statement.execute(String)</code> where every occurence of the
+the string <code>"${schemaObjectPrefix}"</code> has been replaced with the
+value of the property <code>schemaObjectPrefix</code> (see
+ <code>{@link SimpleDbPersistenceManager#setSchemaObjectPrefix(String)}</code>).
+The schema name is either set programmtically by calling
+<code>{@link SimpleDbPersistenceManager#setSchema(String)}</code> or configured
+through the <code>schema</code> bean property.
+</body>

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/db/package.html
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/obj/ObjectPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/obj/ObjectPersistenceManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/obj/ObjectPersistenceManager.java	(revision 0)
@@ -0,0 +1,516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.obj;
+
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+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.FileSystemResource;
+import org.apache.jackrabbit.core.fs.local.LocalFileSystem;
+import org.apache.jackrabbit.core.persistence.AbstractPersistenceManager;
+import org.apache.jackrabbit.core.persistence.PMContext;
+import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.persistence.util.FileSystemBLOBStore;
+import org.apache.jackrabbit.core.persistence.util.Serializer;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.NoSuchItemStateException;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeReferencesId;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.PropertyType;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * <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 binary
+ * serialization format (see {@link Serializer}).
+ */
+public class ObjectPersistenceManager extends AbstractPersistenceManager {
+
+    private static Logger log = LoggerFactory.getLogger(ObjectPersistenceManager.class);
+
+    /**
+     * hexdigits for toString
+     */
+    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
+
+    private static final String NODEFILENAME = ".node";
+
+    private static final String NODEREFSFILENAME = ".references";
+
+    private boolean initialized;
+
+    // file system where the item state is stored
+    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.
+     */
+    public ObjectPersistenceManager() {
+        initialized = false;
+    }
+
+    private static String buildNodeFolderPath(NodeId id) {
+        StringBuffer sb = new StringBuffer();
+        char[] chars = id.getUUID().toString().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++;
+        }
+        return sb.toString();
+    }
+
+    private static String buildPropFilePath(PropertyId id) {
+        String fileName;
+        try {
+            MessageDigest md5 = MessageDigest.getInstance("MD5");
+            md5.update(id.getName().getNamespaceURI().getBytes());
+            md5.update(id.getName().getLocalName().getBytes());
+            byte[] bytes = md5.digest();
+            char[] chars = new char[32];
+            for (int i = 0, j = 0; i < 16; i++) {
+                chars[j++] = HEXDIGITS[(bytes[i] >> 4) & 0x0f];
+                chars[j++] = HEXDIGITS[bytes[i] & 0x0f];
+            }
+            fileName = new String(chars);
+        } catch (NoSuchAlgorithmException nsae) {
+            // should never get here as MD5 should always be available in the JRE
+            String msg = "MD5 not available: ";
+            log.error(msg, nsae);
+            throw new InternalError(msg + nsae);
+        }
+        return buildNodeFolderPath(id.getParentId()) + FileSystem.SEPARATOR + fileName;
+    }
+
+    private static String buildNodeFilePath(NodeId id) {
+        return buildNodeFolderPath(id) + FileSystem.SEPARATOR + NODEFILENAME;
+    }
+
+    private static String buildNodeReferencesFilePath(NodeReferencesId id) {
+        return buildNodeFolderPath(id.getTargetId()) + FileSystem.SEPARATOR + NODEREFSFILENAME;
+    }
+
+    //---------------------------------------------------< PersistenceManager >
+    /**
+     * {@inheritDoc}
+     */
+    public void init(PMContext context) throws Exception {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+
+        FileSystem wspFS = context.getFileSystem();
+        itemStateFS = new BasedFileSystem(wspFS, "/data");
+
+        /**
+         * 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;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void close() throws Exception {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            // 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
+             * closed by the repository
+             */
+        } finally {
+            initialized = false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized NodeState load(NodeId id)
+            throws NoSuchItemStateException, ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        String nodeFilePath = buildNodeFilePath(id);
+
+        try {
+            if (!itemStateFS.isFile(nodeFilePath)) {
+                throw new NoSuchItemStateException(nodeFilePath);
+            }
+        } catch (FileSystemException fse) {
+            String msg = "failed to read node state: " + nodeFilePath;
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+
+        try {
+            BufferedInputStream in =
+                    new BufferedInputStream(itemStateFS.getInputStream(nodeFilePath));
+            try {
+                NodeState state = createNew(id);
+                Serializer.deserialize(state, in);
+                return state;
+            } catch (Exception e) {
+                String msg = "failed to read node state: " + id.getUUID();
+                log.debug(msg);
+                throw new ItemStateException(msg, e);
+            } finally {
+                in.close();
+            }
+        } catch (Exception e) {
+            String msg = "failed to read node state: " + nodeFilePath;
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized PropertyState load(PropertyId id)
+            throws NoSuchItemStateException, ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        String propFilePath = buildPropFilePath(id);
+
+        try {
+            if (!itemStateFS.isFile(propFilePath)) {
+                throw new NoSuchItemStateException(propFilePath);
+            }
+        } catch (FileSystemException fse) {
+            String msg = "failed to read property state: " + propFilePath;
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+
+        try {
+            BufferedInputStream in =
+                    new BufferedInputStream(itemStateFS.getInputStream(propFilePath));
+            try {
+                PropertyState state = createNew(id);
+                Serializer.deserialize(state, in, blobStore);
+                return state;
+            } finally {
+                in.close();
+            }
+        } catch (Exception e) {
+            String msg = "failed to read property state: " + propFilePath;
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized NodeReferences load(NodeReferencesId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        String refsFilePath = buildNodeReferencesFilePath(id);
+
+        try {
+            if (!itemStateFS.isFile(refsFilePath)) {
+                throw new NoSuchItemStateException(id.toString());
+            }
+        } catch (FileSystemException fse) {
+            String msg = "failed to load references: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+
+        try {
+            BufferedInputStream in =
+                    new BufferedInputStream(itemStateFS.getInputStream(refsFilePath));
+            try {
+                NodeReferences refs = new NodeReferences(id);
+                Serializer.deserialize(refs, in);
+                return refs;
+            } finally {
+                in.close();
+            }
+        } catch (Exception e) {
+            String msg = "failed to load references: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void store(NodeState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        String nodeFilePath = buildNodeFilePath(state.getNodeId());
+        FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
+        try {
+            nodeFile.makeParentDirs();
+            BufferedOutputStream out = new BufferedOutputStream(nodeFile.getOutputStream());
+            try {
+                // serialize node state
+                Serializer.serialize(state, out);
+            } finally {
+                out.close();
+            }
+        } catch (Exception e) {
+            String msg = "failed to write node state: " + state.getNodeId();
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void store(PropertyState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        String propFilePath = buildPropFilePath(state.getPropertyId());
+        FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
+        try {
+            propFile.makeParentDirs();
+            BufferedOutputStream out = new BufferedOutputStream(propFile.getOutputStream());
+            try {
+                // serialize property state
+                Serializer.serialize(state, out, blobStore);
+            } finally {
+                out.close();
+            }
+        } catch (Exception e) {
+            String msg = "failed to store property state: " + state.getParentId() + "/" + state.getName();
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void store(NodeReferences refs) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        String refsFilePath = buildNodeReferencesFilePath(refs.getId());
+        FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
+        try {
+            refsFile.makeParentDirs();
+            OutputStream out = new BufferedOutputStream(refsFile.getOutputStream());
+            try {
+                Serializer.serialize(refs, out);
+            } finally {
+                out.close();
+            }
+        } catch (Exception e) {
+            String msg = "failed to store references: " + refs.getId();
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void destroy(NodeState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        String nodeFilePath = buildNodeFilePath(state.getNodeId());
+        FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
+        try {
+            if (nodeFile.exists()) {
+                // delete resource and prune empty parent folders
+                nodeFile.delete(true);
+            }
+        } catch (FileSystemException fse) {
+            String msg = "failed to delete node state: " + state.getNodeId();
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void destroy(PropertyState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // delete binary values (stored as files)
+        InternalValue[] values = state.getValues();
+        if (values != null) {
+            for (int i = 0; i < values.length; i++) {
+                InternalValue val = values[i];
+                if (val != null) {
+                    if (val.getType() == PropertyType.BINARY) {
+                        BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
+                        // delete blob file and prune empty parent folders
+                        blobVal.delete(true);
+                    }
+                }
+            }
+        }
+        // delete property file
+        String propFilePath = buildPropFilePath(state.getPropertyId());
+        FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
+        try {
+            if (propFile.exists()) {
+                // delete resource and prune empty parent folders
+                propFile.delete(true);
+            }
+        } catch (FileSystemException fse) {
+            String msg = "failed to delete property state: " + state.getParentId() + "/" + state.getName();
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void destroy(NodeReferences refs) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        String refsFilePath = buildNodeReferencesFilePath(refs.getId());
+        FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
+        try {
+            if (refsFile.exists()) {
+                // delete resource and prune empty parent folders
+                refsFile.delete(true);
+            }
+        } catch (FileSystemException fse) {
+            String msg = "failed to delete node references: " + refs.getId();
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized boolean exists(PropertyId id) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            String propFilePath = buildPropFilePath(id);
+            FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
+            return propFile.exists();
+        } catch (FileSystemException fse) {
+            String msg = "failed to check existence of item state: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized boolean exists(NodeId id) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            String nodeFilePath = buildNodeFilePath(id);
+            FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
+            return nodeFile.exists();
+        } catch (FileSystemException fse) {
+            String msg = "failed to check existence of item state: " + id;
+            log.error(msg, fse);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized boolean exists(NodeReferencesId id)
+            throws ItemStateException {
+
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            String refsFilePath = buildNodeReferencesFilePath(id);
+            FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
+            return refsFile.exists();
+        } catch (FileSystemException fse) {
+            String msg = "failed to check existence of references: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, fse);
+        }
+    }
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/obj/ObjectPersistenceManager.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/util/FileSystemBLOBStore.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/util/FileSystemBLOBStore.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/util/FileSystemBLOBStore.java	(revision 0)
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.util;
+
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.persistence.util.ResourceBasedBLOBStore;
+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 an absolute file system path
+        StringBuffer sb = new StringBuffer();
+        sb.append(FileSystem.SEPARATOR_CHAR);
+        char[] chars = id.getParentId().getUUID().toString().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 an absolute 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 {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean remove(String blobId) throws Exception {
+        // the blobId is an absolute file system path
+        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 {
+        // the blobId is an absolute file system path
+        return new FileSystemResource(fs, blobId);
+    }
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/util/FileSystemBLOBStore.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/util/ResourceBasedBLOBStore.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/util/ResourceBasedBLOBStore.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/util/ResourceBasedBLOBStore.java	(revision 0)
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.util;
+
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+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;
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/util/ResourceBasedBLOBStore.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/util/Serializer.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/util/Serializer.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/util/Serializer.java	(revision 0)
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.util;
+
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.persistence.util.ResourceBasedBLOBStore;
+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;
+import java.util.Arrays;
+
+/**
+ * <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 byte[] NULL_UUID_PLACEHOLDER_BYTES = new byte[] {
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+
+    /**
+     * 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.getParentId() == null) {
+            out.write(NULL_UUID_PLACEHOLDER_BYTES);
+        } else {
+            out.write(state.getParentId().getUUID().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
+        out.writeShort(state.getModCount());
+        // 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(entry.getId().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.readFully(uuidBytes);
+        if (!Arrays.equals(uuidBytes, NULL_UUID_PLACEHOLDER_BYTES)) {
+            state.setParentId(new NodeId(new UUID(uuidBytes)));
+        }
+        // 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
+        short modCount = in.readShort();
+        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.readFully(uuidBytes);
+            state.addChildNodeEntry(name, new NodeId(new UUID(uuidBytes)));
+        }
+    }
+
+    /**
+     * 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
+        out.writeShort(state.getModCount());
+        // 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(state.getPropertyId(), 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)
+                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(blobId);
+                    values[i] = InternalValue.create(fsRes);
+                } else {
+                    in = blobStore.get(blobId);
+                    try {
+                        values[i] = InternalValue.create(in, false);
+                    } finally {
+                        try {
+                            in.close();
+                        } catch (IOException e) {
+                            // ignore
+                        }
+                    }
+                }
+                blobVal.discard();
+            } else {
+                /**
+                 * because writeUTF(String) has a size limit of 65k,
+                 * Strings are serialized as <length><byte[]>
+                 */
+                //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
+        short modCount = in.readShort();
+        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 {
+                    InputStream is = blobStore.get(s);
+                    try {
+                        val = InternalValue.create(is, false);
+                    } finally {
+                        try {
+                            is.close();
+                        } catch (IOException e) {
+                            // ignore
+                        }
+                    }
+                }
+            } 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.readFully(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
+        }
+    }
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/util/Serializer.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/util/BLOBStore.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/util/BLOBStore.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/util/BLOBStore.java	(revision 0)
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.util;
+
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.persistence.util.ResourceBasedBLOBStore;
+
+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;
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/util/BLOBStore.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemPersistenceManager.java	(revision 0)
+++ src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemPersistenceManager.java	(revision 0)
@@ -0,0 +1,597 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.persistence.mem;
+
+import org.apache.jackrabbit.core.ItemId;
+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.persistence.AbstractPersistenceManager;
+import org.apache.jackrabbit.core.persistence.PMContext;
+import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.state.NoSuchItemStateException;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeReferencesId;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.persistence.util.FileSystemBLOBStore;
+import org.apache.jackrabbit.core.persistence.util.Serializer;
+import org.apache.jackrabbit.core.value.BLOBFileValue;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.name.QName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.PropertyType;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * <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
+ * binary serialization format (see {@link Serializer}).
+ * <p/>
+ * It is configured through the following properties:
+ * <ul>
+ * <li><code>initialCapacity</code>: initial capacity of the hash map used to store the data</li>
+ * <li><code>loadFactor</code>: load factor of the hash map used to store the data</li>
+ * <li><code>persistent</code>: if <code>true</code> the contents of the hash map
+ * is loaded on startup and stored on shutdown;
+ * if <code>false</code> nothing is persisted</li>
+ * </ul>
+ * <b>Please note that this class should only be used for testing purposes.</b>
+ */
+public class InMemPersistenceManager extends AbstractPersistenceManager {
+
+    private static Logger log = LoggerFactory.getLogger(InMemPersistenceManager.class);
+
+    protected boolean initialized;
+
+    protected Map stateStore;
+    protected Map refsStore;
+
+    // initial size of buffer used to serialize objects
+    protected static final int INITIAL_BUFFER_SIZE = 1024;
+
+    // some constants used in serialization
+    protected static final String STATE_FILE_PATH = "/data/.state.bin";
+    protected static final String REFS_FILE_PATH = "/data/.refs.bin";
+    protected static final byte NODE_ENTRY = 0;
+    protected static final byte PROP_ENTRY = 1;
+
+    // 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
+     * (if <code>persistent==true</code>)
+     */
+    protected FileSystem wspFS;
+
+    // initial capacity
+    protected int initialCapacity = 32768;
+    // load factor for the hash map
+    protected float loadFactor = 0.75f;
+    // should hash map be persisted?
+    protected boolean persistent = true;
+
+    /**
+     * Creates a new <code>InMemPersistenceManager</code> instance.
+     */
+    public InMemPersistenceManager() {
+        initialized = false;
+    }
+
+    public void setInitialCapacity(int initialCapacity) {
+        this.initialCapacity = initialCapacity;
+    }
+
+    public void setInitialCapacity(String initialCapacity) {
+        this.initialCapacity = Integer.valueOf(initialCapacity).intValue();
+    }
+
+    public String getInitialCapacity() {
+        return Integer.toString(initialCapacity);
+    }
+
+    public void setLoadFactor(float loadFactor) {
+        this.loadFactor = loadFactor;
+    }
+
+    public void setLoadFactor(String loadFactor) {
+        this.loadFactor = Float.valueOf(loadFactor).floatValue();
+    }
+
+    public String getLoadFactor() {
+        return Float.toString(loadFactor);
+    }
+
+    public boolean isPersistent() {
+        return persistent;
+    }
+
+    public void setPersistent(boolean persistent) {
+        this.persistent = persistent;
+    }
+
+    public void setPersistent(String persistent) {
+        this.persistent = Boolean.valueOf(persistent).booleanValue();
+    }
+
+    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();
+    }
+
+    /**
+     * Reads the content of the hash maps from the file system
+     *
+     * @throws Exception if an error occurs
+     */
+    public synchronized void loadContents() throws Exception {
+        // read item states
+        FileSystemResource fsRes = new FileSystemResource(wspFS, STATE_FILE_PATH);
+        if (!fsRes.exists()) {
+            return;
+        }
+        BufferedInputStream bis = new BufferedInputStream(fsRes.getInputStream());
+        DataInputStream in = new DataInputStream(bis);
+
+        try {
+            int n = in.readInt();   // number of entries
+            while (n-- > 0) {
+                byte type = in.readByte();  // entry type
+                ItemId id;
+                if (type == NODE_ENTRY) {
+                    // entry type: node
+                    String s = in.readUTF();    // id
+                    id = NodeId.valueOf(s);
+                } else {
+                    // entry type: property
+                    String s = in.readUTF();    // id
+                    id = PropertyId.valueOf(s);
+                }
+                int length = in.readInt();  // data length
+                byte[] data = new byte[length];
+                in.readFully(data);  // data
+                // store in map
+                stateStore.put(id, data);
+            }
+        } finally {
+            in.close();
+        }
+
+        // read references
+        fsRes = new FileSystemResource(wspFS, REFS_FILE_PATH);
+        bis = new BufferedInputStream(fsRes.getInputStream());
+        in = new DataInputStream(bis);
+
+        try {
+            int n = in.readInt();   // number of entries
+            while (n-- > 0) {
+                String s = in.readUTF();    // target id
+                NodeReferencesId id = (NodeReferencesId) NodeReferencesId.valueOf(s);
+                int length = in.readInt();  // data length
+                byte[] data = new byte[length];
+                in.readFully(data);  // data
+                // store in map
+                refsStore.put(id, data);
+            }
+        } finally {
+            in.close();
+        }
+    }
+
+    /**
+     * Writes the content of the hash maps to the file system
+     *
+     * @throws Exception if an error occurs
+     */
+    public synchronized void storeContents() throws Exception {
+        // write item states
+        FileSystemResource fsRes = new FileSystemResource(wspFS, STATE_FILE_PATH);
+        fsRes.makeParentDirs();
+        BufferedOutputStream bos = new BufferedOutputStream(fsRes.getOutputStream());
+        DataOutputStream out = new DataOutputStream(bos);
+
+        try {
+
+            out.writeInt(stateStore.size());    // number of entries
+            // entries
+            Iterator iterKeys = stateStore.keySet().iterator();
+            while (iterKeys.hasNext()) {
+                ItemId id = (ItemId) iterKeys.next();
+                if (id.denotesNode()) {
+                    out.writeByte(NODE_ENTRY);  // entry type
+                } else {
+                    out.writeByte(PROP_ENTRY);  // entry type
+                }
+                out.writeUTF(id.toString());    // id
+                byte[] data = (byte[]) stateStore.get(id);
+                out.writeInt(data.length);  // data length
+                out.write(data);    // data
+            }
+        } finally {
+            out.close();
+        }
+
+        // write references
+        fsRes = new FileSystemResource(wspFS, REFS_FILE_PATH);
+        fsRes.makeParentDirs();
+        bos = new BufferedOutputStream(fsRes.getOutputStream());
+        out = new DataOutputStream(bos);
+
+        try {
+            out.writeInt(refsStore.size()); // number of entries
+            // entries
+            Iterator iterKeys = refsStore.keySet().iterator();
+            while (iterKeys.hasNext()) {
+                NodeReferencesId id = (NodeReferencesId) iterKeys.next();
+                out.writeUTF(id.toString());    // target id
+                byte[] data = (byte[]) refsStore.get(id);
+                out.writeInt(data.length);  // data length
+                out.write(data);    // data
+            }
+        } finally {
+            out.close();
+        }
+    }
+
+    //---------------------------------------------------< PersistenceManager >
+    /**
+     * {@inheritDoc}
+     */
+    public void init(PMContext context) throws Exception {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+
+        stateStore = new HashMap(initialCapacity, loadFactor);
+        refsStore = new HashMap(initialCapacity, loadFactor);
+
+        wspFS = context.getFileSystem();
+
+        /**
+         * 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
+            loadContents();
+        }
+
+        initialized = true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized void close() throws Exception {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            if (persistent) {
+                // serialize contents of state and refs stores
+                storeContents();
+            } else {
+                // clear out blob store
+                try {
+                    String[] folders = blobFS.listFolders("/");
+                    for (int i = 0; i < folders.length; i++) {
+                        blobFS.deleteFolder(folders[i]);
+                    }
+                    String[] files = blobFS.listFiles("/");
+                    for (int i = 0; i < files.length; i++) {
+                        blobFS.deleteFile(files[i]);
+                    }
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+
+            // close BLOB file system
+            blobFS.close();
+            blobFS = null;
+            blobStore = null;
+
+            stateStore.clear();
+            stateStore = null;
+            refsStore.clear();
+            refsStore = null;
+        } finally {
+            initialized = false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized NodeState load(NodeId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        byte[] data = (byte[]) stateStore.get(id);
+        if (data == null) {
+            throw new NoSuchItemStateException(id.toString());
+        }
+
+        ByteArrayInputStream in = new ByteArrayInputStream(data);
+        try {
+            NodeState state = createNew(id);
+            Serializer.deserialize(state, in);
+            return state;
+        } catch (Exception e) {
+            String msg = "failed to read node state: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized PropertyState load(PropertyId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        byte[] data = (byte[]) stateStore.get(id);
+        if (data == null) {
+            throw new NoSuchItemStateException(id.toString());
+        }
+
+        ByteArrayInputStream in = new ByteArrayInputStream(data);
+        try {
+            PropertyState state = createNew(id);
+            Serializer.deserialize(state, in, blobStore);
+            return state;
+        } catch (Exception e) {
+            String msg = "failed to read property state: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void store(NodeState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize node state
+            Serializer.serialize(state, out);
+
+            // store in serialized format in map for better memory efficiency
+            stateStore.put(state.getNodeId(), out.toByteArray());
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to write node state: " + state.getNodeId();
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void store(PropertyState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize property state
+            Serializer.serialize(state, out, blobStore);
+
+            // store in serialized format in map for better memory efficiency
+            stateStore.put(state.getPropertyId(), out.toByteArray());
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to store property state: " + state.getPropertyId();
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void destroy(NodeState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // remove node state
+        stateStore.remove(state.getNodeId());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void destroy(PropertyState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // delete binary values (stored as files)
+        InternalValue[] values = state.getValues();
+        if (values != null) {
+            for (int i = 0; i < values.length; i++) {
+                InternalValue val = values[i];
+                if (val != null) {
+                    if (val.getType() == PropertyType.BINARY) {
+                        BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
+                        // delete blob file and prune empty parent folders
+                        blobVal.delete(true);
+                    }
+                }
+            }
+        }
+
+        // remove property state
+        stateStore.remove(state.getPropertyId());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public synchronized NodeReferences load(NodeReferencesId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        byte[] data = (byte[]) refsStore.get(id);
+        if (data == null) {
+            throw new NoSuchItemStateException(id.toString());
+        }
+
+        ByteArrayInputStream in = new ByteArrayInputStream(data);
+        try {
+            NodeReferences refs = new NodeReferences(id);
+            Serializer.deserialize(refs, in);
+            return refs;
+        } catch (Exception e) {
+            String msg = "failed to load references: " + id;
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void store(NodeReferences refs) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            ByteArrayOutputStream out =
+                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize references
+            Serializer.serialize(refs, out);
+
+            // store in serialized format in map for better memory efficiency
+            refsStore.put(refs.getId(), out.toByteArray());
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to store references: " + refs.getId();
+            log.debug(msg);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void destroy(NodeReferences refs) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // remove node references
+        refsStore.remove(refs.getId());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean exists(PropertyId id) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+        return stateStore.containsKey(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean exists(NodeId id) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+        return stateStore.containsKey(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean exists(NodeReferencesId id) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+        return refsStore.containsKey(id);
+    }
+}

Property changes on: src/main/java/org/apache/jackrabbit/core/persistence/mem/InMemPersistenceManager.java
___________________________________________________________________
Name: svn:keywords
   + author date id revision url rev
Name: svn:eol-style
   + native

Index: src/main/java/org/apache/jackrabbit/core/repository.xml
===================================================================
--- src/main/java/org/apache/jackrabbit/core/repository.xml	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/repository.xml	(working copy)
@@ -193,7 +193,7 @@
             persistence manager of the workspace:
             class: FQN of class implementing the PersistenceManager interface
         -->
-        <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
           <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
           <param name="schemaObjectPrefix" value="${wsp.name}_"/>
         </PersistenceManager>
@@ -224,7 +224,7 @@
             a 'normal' persistence manager, but this could change in future
             implementations.
         -->
-        <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
           <param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>
           <param name="schemaObjectPrefix" value="version_"/>
         </PersistenceManager>
Index: src/main/java/org/apache/jackrabbit/core/state/AbstractPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/AbstractPersistenceManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/AbstractPersistenceManager.java	(working copy)
@@ -14,129 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
 package org.apache.jackrabbit.core.state;
 
-import org.apache.jackrabbit.core.NodeId;
-import org.apache.jackrabbit.core.PropertyId;
-
-import java.util.Iterator;
-
 /**
- * Implementation <code>PersistenceManager</code> that handles some
- * concepts.
+ * Legacy class kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.AbstractPersistenceManager}
+ *             instead.
  */
-public abstract class AbstractPersistenceManager implements PersistenceManager {
+public abstract class AbstractPersistenceManager extends org.apache.jackrabbit.core.persistence.AbstractPersistenceManager {
 
-    /**
-     * {@inheritDoc}
-     */
-    public NodeState createNew(NodeId id) {
-        return new NodeState(id, null, null, NodeState.STATUS_NEW, false);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public PropertyState createNew(PropertyId id) {
-        return new PropertyState(id, PropertyState.STATUS_NEW, false);
-    }
-
-    /**
-     * Right now, this iterates over all items in the changelog and
-     * calls the individual methods that handle single item states
-     * or node references objects. Properly implemented, this method
-     * should ensure that changes are either written completely to
-     * the underlying persistence layer, or not at all.
-     *
-     * {@inheritDoc}
-     */
-    public synchronized void store(ChangeLog changeLog) throws ItemStateException {
-        Iterator iter = changeLog.deletedStates();
-        while (iter.hasNext()) {
-            ItemState state = (ItemState) iter.next();
-            if (state.isNode()) {
-                destroy((NodeState) state);
-            } else {
-                destroy((PropertyState) state);
-            }
-        }
-        iter = changeLog.addedStates();
-        while (iter.hasNext()) {
-            ItemState state = (ItemState) iter.next();
-            if (state.isNode()) {
-                store((NodeState) state);
-            } else {
-                store((PropertyState) state);
-            }
-        }
-        iter = changeLog.modifiedStates();
-        while (iter.hasNext()) {
-            ItemState state = (ItemState) iter.next();
-            if (state.isNode()) {
-                store((NodeState) state);
-            } else {
-                store((PropertyState) state);
-            }
-        }
-        iter = changeLog.modifiedRefs();
-        while (iter.hasNext()) {
-            NodeReferences refs = (NodeReferences) iter.next();
-            if (refs.hasReferences()) {
-                store(refs);
-            } else {
-                if (exists(refs.getId())) {
-                    destroy(refs);
-                }
-            }
-        }
-    }
-
-    /**
-     * Store a node state. Subclass responsibility.
-     *
-     * @param state node state to store
-     * @throws ItemStateException if an error occurs
-     */
-    protected abstract void store(NodeState state) throws ItemStateException;
-
-    /**
-     * Store a property state. Subclass responsibility.
-     *
-     * @param state property state to store
-     * @throws ItemStateException if an error occurs
-     */
-    protected abstract void store(PropertyState state) throws ItemStateException;
-
-    /**
-     * Store a references object. Subclass responsibility.
-     *
-     * @param refs references object to store
-     * @throws ItemStateException if an error occurs
-     */
-    protected abstract void store(NodeReferences refs) throws ItemStateException;
-
-    /**
-     * Destroy a node state. Subclass responsibility.
-     *
-     * @param state node state to destroy
-     * @throws ItemStateException if an error occurs
-     */
-    protected abstract void destroy(NodeState state) throws ItemStateException;
-
-    /**
-     * Destroy a property state. Subclass responsibility.
-     *
-     * @param state property state to destroy
-     * @throws ItemStateException if an error occurs
-     */
-    protected abstract void destroy(PropertyState state) throws ItemStateException;
-
-    /**
-     * Destroy a node references object. Subclass responsibility.
-     *
-     * @param refs node references object to destroy
-     * @throws ItemStateException if an error occurs
-     */
-    protected abstract void destroy(NodeReferences refs)
-            throws ItemStateException;
-}
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/db/SimpleDbPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/SimpleDbPersistenceManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/SimpleDbPersistenceManager.java	(working copy)
@@ -14,154 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.core.state.db;
 
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
-import org.apache.jackrabbit.core.state.util.Serializer;
-
-/**
- * <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 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>
- * <li><code>driver</code>: the FQN name of the JDBC driver class</li>
- * <li><code>url</code>: the database url of the form <code>jdbc:subprotocol:subname</code></li>
- * <li><code>user</code>: the database user</li>
- * <li><code>password</code>: the user's password</li>
- * <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
- * resources by calling <code>getClass().getResourceAsStream(schema + ".ddl")</code>.
- * Every line in the specified .ddl file is executed separatly by calling
- * <code>java.sql.Statement.execute(String)</code> where every occurence of the
- * the string <code>"${schemaObjectPrefix}"</code> has been replaced with the
- * value of the property <code>schemaObjectPrefix</code>.
- * <p/>
- * The following is a fragment from a sample configuration using MySQL:
- * <pre>
- *   &lt;PersistenceManager class="org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager"&gt;
- *       &lt;param name="driver" value="com.mysql.jdbc.Driver"/&gt;
- *       &lt;param name="url" value="jdbc:mysql:///test?autoReconnect=true"/&gt;
- *       &lt;param name="schema" value="mysql"/&gt;
- *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&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>
- *   &lt;PersistenceManager class="org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager"&gt;
- *       &lt;param name="driver" value="in.co.daffodil.db.jdbc.DaffodilDBDriver"/&gt;
- *       &lt;param name="url" value="jdbc:daffodilDB_embedded:${wsp.name};path=${wsp.home}/../../databases;create=true"/&gt;
- *       &lt;param name="user" value="daffodil"/&gt;
- *       &lt;param name="password" value="daffodil"/&gt;
- *       &lt;param name="schema" value="daffodil"/&gt;
- *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
- *       &lt;param name="externalBLOBs" value="false"/&gt;
- *   &lt;/PersistenceManager&gt;
- * </pre>
- * The following is a fragment from a sample configuration using DB2:
- * <pre>
- *   &lt;PersistenceManager class="org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager"&gt;
- *       &lt;param name="driver" value="com.ibm.db2.jcc.DB2Driver"/&gt;
- *       &lt;param name="url" value="jdbc:db2:test"/&gt;
- *       &lt;param name="schema" value="db2"/&gt;
- *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
- *       &lt;param name="externalBLOBs" value="false"/&gt;
- *   &lt;/PersistenceManager&gt;
- * </pre>
- * The following is a fragment from a sample configuration using MSSQL:
- * <pre>
- *   &lt;PersistenceManager class="org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager"&gt;
- *       &lt;param name="driver" value="com.microsoft.jdbc.sqlserver.SQLServerDriver"/&gt;
- *       &lt;param name="url" value="jdbc:microsoft:sqlserver://localhost:1433;;DatabaseName=test;SelectMethod=Cursor;"/&gt;
- *       &lt;param name="schema" value="mssql"/&gt;
- *       &lt;param name="user" value="sa"/&gt;
- *       &lt;param name="password" value=""/&gt;
- *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
- *       &lt;param name="externalBLOBs" value="false"/&gt;
- *   &lt;/PersistenceManager&gt;
- * </pre>
- * The following is a fragment from a sample configuration using PostgreSQL:
- * <pre>
- *   &lt;PersistenceManager class="org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager"&gt;
- *       &lt;param name="driver" value="org.postgresql.Driver"/&gt;
- *       &lt;param name="url" value="jdbc:postgresql://localhost/test"/&gt;
- *       &lt;param name="schema" value="postgresql"/&gt;
- *       &lt;param name="user" value="postgres"/&gt;
- *       &lt;param name="password" value="postgres"/&gt;
- *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
- *       &lt;param name="externalBLOBs" value="false"/&gt;
- *   &lt;/PersistenceManager&gt;
- * </pre>
- * See also {@link DerbyPersistenceManager}, {@link OraclePersistenceManager}.
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
  */
-public class SimpleDbPersistenceManager extends DatabasePersistenceManager {
+package org.apache.jackrabbit.core.state.db;
 
-    protected String driver;
-    protected String url;
-    protected String user;
-    protected String password;
-
-    //----------------------------------------------------< setters & getters >
-    public String getUrl() {
-        return url;
-    }
-
-    public void setUrl(String url) {
-        this.url = url;
-    }
-
-    public String getUser() {
-        return user;
-    }
-
-    public void setUser(String user) {
-        this.user = user;
-    }
-
-    public String getPassword() {
-        return password;
-    }
-
-    public void setPassword(String password) {
-        this.password = password;
-    }
-
-    public String getDriver() {
-        return driver;
-    }
-
-    public void setDriver(String driver) {
-        this.driver = driver;
-    }
-
-    //------------------------------------------< DatabasePersistenceManager >
-
-    /**
-     * Returns a JDBC connection acquired using the JDBC {@link DriverManager}.
-     *
-     * @throws ClassNotFoundException if the JDBC driver class is not found
-     * @throws SQLException if a database access error occurs
-     * @see DatabasePersistenceManager#getConnection()
-     */
-    protected Connection getConnection() throws ClassNotFoundException, SQLException {
-        Class.forName(driver);
-        Connection connection = DriverManager.getConnection(url, user, password);
-        return connection;
-    }
-
-}
+/**
+ * Legacy class kept for backward compatibility reasons.
+  * @deprecated use {@link org.apache.jackrabbit.core.persistence.db.SimpleDbPersistenceManager}
+  *             instead.
+  */
+public class SimpleDbPersistenceManager
+        extends org.apache.jackrabbit.core.persistence.db.SimpleDbPersistenceManager {
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/db/mssql.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/mssql.ddl	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/mssql.ddl	(working copy)
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA image not null)
-create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
-create table ${schemaObjectPrefix}PROP (PROP_ID varchar(1024) not null, PROP_DATA image not null)
-create unique index ${schemaObjectPrefix}PROP_IDX on ${schemaObjectPrefix}PROP (PROP_ID)
-create table ${schemaObjectPrefix}REFS (NODE_ID char(36) not null, REFS_DATA image 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 image not null)
-create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)
Index: src/main/java/org/apache/jackrabbit/core/state/db/DatabasePersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/DatabasePersistenceManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/DatabasePersistenceManager.java	(working copy)
@@ -14,1061 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
 package org.apache.jackrabbit.core.state.db;
 
-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.local.LocalFileSystem;
-import org.apache.jackrabbit.core.state.AbstractPersistenceManager;
-import org.apache.jackrabbit.core.state.ChangeLog;
-import org.apache.jackrabbit.core.state.ItemStateException;
-import org.apache.jackrabbit.core.state.NoSuchItemStateException;
-import org.apache.jackrabbit.core.state.NodeReferences;
-import org.apache.jackrabbit.core.state.NodeReferencesId;
-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.ItemState;
-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.util.Text;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import java.io.BufferedReader;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.FilterInputStream;
-import java.io.ByteArrayInputStream;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.DatabaseMetaData;
-
 /**
- * Abstract base class for database persistence managers. This class
- * contains common functionality for database persistence manager subclasses
- * that normally differ only in the way the database connection is acquired.
- * Subclasses should override the {@link #getConnection()} method to return
- * the configured database connection.
- * <p>
- * See the {@link SimpleDbPersistenceManager} for a detailed description
- * of the available configuration options and database behaviour.
+ * Legacy class kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.db.DatabasePersistenceManager}
+ *             instead.
  */
-public abstract class DatabasePersistenceManager extends AbstractPersistenceManager {
-
-    /**
-     * Logger instance
-     */
-    private static Logger log = LoggerFactory.getLogger(DatabasePersistenceManager.class);
-
-    protected static final String SCHEMA_OBJECT_PREFIX_VARIABLE =
-            "${schemaObjectPrefix}";
-
-    protected boolean initialized;
-
-    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;
-
-    // jdbc connection
-    protected Connection con;
-
-    // shared prepared statements for NodeState management
-    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;
-
-    // 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>DatabasePersistenceManager</code> instance.
-     */
-    public DatabasePersistenceManager() {
-        schema = "default";
-        schemaObjectPrefix = "";
-        externalBLOBs = true;
-        initialized = false;
-    }
-
-    //----------------------------------------------------< setters & getters >
-
-    public String getSchemaObjectPrefix() {
-        return schemaObjectPrefix;
-    }
-
-    public void setSchemaObjectPrefix(String schemaObjectPrefix) {
-        // make sure prefix is all uppercase
-        this.schemaObjectPrefix = schemaObjectPrefix.toUpperCase();
-    }
-
-    public String getSchema() {
-        return schema;
-    }
-
-    public void setSchema(String schema) {
-        this.schema = schema;
-    }
-
-    public boolean isExternalBLOBs() {
-        return externalBLOBs;
-    }
-
-    public void setExternalBLOBs(boolean externalBLOBs) {
-        this.externalBLOBs = externalBLOBs;
-    }
-
-    public void setExternalBLOBs(String externalBLOBs) {
-        this.externalBLOBs = Boolean.valueOf(externalBLOBs).booleanValue();
-    }
-
-    //---------------------------------------------------< PersistenceManager >
-    /**
-     * {@inheritDoc}
-     */
-    public void init(PMContext context) throws Exception {
-        if (initialized) {
-            throw new IllegalStateException("already initialized");
-        }
-
-        // setup jdbc connection
-        initConnection();
-
-        // make sure schemaObjectPrefix consists of legal name characters only
-        prepareSchemaObjectPrefix();
-
-        // check if schema objects exist and create them if necessary
-        checkSchema();
-
-        // prepare statements
-        nodeStateInsert =
-                con.prepareStatement("insert into "
-                + schemaObjectPrefix + "NODE (NODE_DATA, NODE_ID) values (?, ?)");
-        nodeStateUpdate =
-                con.prepareStatement("update "
-                + schemaObjectPrefix + "NODE set NODE_DATA = ? where NODE_ID = ?");
-        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 = ?");
-
-        propertyStateInsert =
-                con.prepareStatement("insert into "
-                + schemaObjectPrefix + "PROP (PROP_DATA, PROP_ID) values (?, ?)");
-        propertyStateUpdate =
-                con.prepareStatement("update "
-                + schemaObjectPrefix + "PROP set PROP_DATA = ? where PROP_ID = ?");
-        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 = ?");
-
-        nodeReferenceInsert =
-                con.prepareStatement("insert into "
-                + schemaObjectPrefix + "REFS (REFS_DATA, NODE_ID) values (?, ?)");
-        nodeReferenceUpdate =
-                con.prepareStatement("update "
-                + schemaObjectPrefix + "REFS set REFS_DATA = ? where NODE_ID = ?");
-        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;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void close() throws Exception {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            // close shared prepared statements
-            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
-            closeConnection(con);
-
-        } finally {
-            initialized = false;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void store(ChangeLog changeLog)
-            throws ItemStateException {
-        ItemStateException ise = null;
-        try {
-            super.store(changeLog);
-        } catch (ItemStateException e) {
-            ise = e;
-        } finally {
-            if (ise == null) {
-                // storing the changes succeeded, now commit the changes
-                try {
-                    con.commit();
-                } catch (SQLException e) {
-                    String msg = "committing change log failed";
-                    log.error(msg, e);
-                    throw new ItemStateException(msg, e);
-                }
-            } else {
-                // storing the changes failed, rollback changes
-                try {
-                    con.rollback();
-                } catch (SQLException e) {
-                    String msg = "rollback of change log failed";
-                    log.error(msg, e);
-                }
-                // re-throw original exception
-                throw ise;
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public NodeState load(NodeId id)
-            throws NoSuchItemStateException, ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        PreparedStatement stmt = nodeStateSelect;
-        synchronized (stmt) {
-            ResultSet rs = null;
-            InputStream in = null;
-            try {
-                stmt.setString(1, id.toString());
-                stmt.execute();
-                rs = stmt.getResultSet();
-                if (!rs.next()) {
-                    throw new NoSuchItemStateException(id.toString());
-                }
-
-                in = rs.getBinaryStream(1);
-                NodeState state = createNew(id);
-                Serializer.deserialize(state, in);
-
-                return state;
-            } catch (Exception e) {
-                if (e instanceof NoSuchItemStateException) {
-                    throw (NoSuchItemStateException) e;
-                }
-                String msg = "failed to read node state: " + id;
-                log.error(msg, e);
-                throw new ItemStateException(msg, e);
-            } finally {
-                closeStream(in);
-                closeResultSet(rs);
-                resetStatement(stmt);
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public PropertyState load(PropertyId id)
-            throws NoSuchItemStateException, ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        PreparedStatement stmt = propertyStateSelect;
-        synchronized (stmt) {
-            ResultSet rs = null;
-            InputStream in = null;
-            try {
-                stmt.setString(1, id.toString());
-                stmt.execute();
-                rs = stmt.getResultSet();
-                if (!rs.next()) {
-                    throw new NoSuchItemStateException(id.toString());
-                }
-
-                in = rs.getBinaryStream(1);
-                PropertyState state = createNew(id);
-                Serializer.deserialize(state, in, blobStore);
-
-                return state;
-            } catch (Exception e) {
-                if (e instanceof NoSuchItemStateException) {
-                    throw (NoSuchItemStateException) e;
-                }
-                String msg = "failed to read property state: " + id;
-                log.error(msg, e);
-                throw new ItemStateException(msg, e);
-            } finally {
-                closeStream(in);
-                closeResultSet(rs);
-                resetStatement(stmt);
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p/>
-     * This method uses shared <code>PreparedStatement</code>s which must
-     * be executed strictly sequentially. Because this method synchronizes on
-     * the persistence manager instance there is no need to synchronize on the
-     * shared statement. If the method would not be sychronized the shared
-     * statements would have to be synchronized.
-     */
-    public synchronized void store(NodeState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // check if insert or update
-        boolean update = state.getStatus() != ItemState.STATUS_NEW;
-        //boolean update = exists(state.getId());
-        PreparedStatement stmt = (update) ? nodeStateUpdate : nodeStateInsert;
-
-        try {
-            ByteArrayOutputStream out =
-                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
-            // serialize node state
-            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.getNodeId().toString());
-            stmt.executeUpdate();
-
-            // there's no need to close a ByteArrayOutputStream
-            //out.close();
-        } catch (Exception e) {
-            String msg = "failed to write node state: " + state.getNodeId();
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p/>
-     * This method uses shared <code>PreparedStatement</code>s which must
-     * be executed strictly sequentially. Because this method synchronizes on
-     * the persistence manager instance there is no need to synchronize on the
-     * shared statement. If the method would not be sychronized the shared
-     * statements would have to be synchronized.
-     */
-    public synchronized void store(PropertyState state)
-            throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // check if insert or update
-        boolean update = state.getStatus() != ItemState.STATUS_NEW;
-        //boolean update = exists(state.getId());
-        PreparedStatement stmt = (update) ? propertyStateUpdate : propertyStateInsert;
-
-        try {
-            ByteArrayOutputStream out =
-                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
-            // serialize property state
-            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.getPropertyId().toString());
-            stmt.executeUpdate();
-
-            // there's no need to close a ByteArrayOutputStream
-            //out.close();
-        } catch (Exception e) {
-            String msg = "failed to write property state: " + state.getPropertyId();
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void destroy(NodeState state)
-            throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        PreparedStatement stmt = nodeStateDelete;
-        try {
-            stmt.setString(1, state.getNodeId().toString());
-            stmt.executeUpdate();
-        } catch (Exception e) {
-            String msg = "failed to delete node state: " + state.getNodeId();
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void destroy(PropertyState state)
-            throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // make sure binary values (BLOBs) are properly removed
-        InternalValue[] values = state.getValues();
-        if (values != null) {
-            for (int i = 0; i < values.length; i++) {
-                InternalValue val = values[i];
-                if (val != null) {
-                    if (val.getType() == PropertyType.BINARY) {
-                        BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
-                        // delete internal resource representation of BLOB value
-                        blobVal.delete(true);
-                        // also remove from BLOBStore
-                        String blobId = blobStore.createId(state.getPropertyId(), i);
-                        try {
-                            blobStore.remove(blobId);
-                        } catch (Exception e) {
-                            log.warn("failed to remove from BLOBStore: " + blobId, e);
-                        }
-                    }
-                }
-            }
-        }
-
-        PreparedStatement stmt = propertyStateDelete;
-        try {
-            stmt.setString(1, state.getPropertyId().toString());
-            stmt.executeUpdate();
-        } catch (Exception e) {
-            String msg = "failed to delete property state: " + state.getPropertyId();
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public NodeReferences load(NodeReferencesId targetId)
-            throws NoSuchItemStateException, ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        PreparedStatement stmt = nodeReferenceSelect;
-        synchronized (stmt) {
-            ResultSet rs = null;
-            InputStream in = null;
-            try {
-                stmt.setString(1, targetId.toString());
-                stmt.execute();
-                rs = stmt.getResultSet();
-                if (!rs.next()) {
-                    throw new NoSuchItemStateException(targetId.toString());
-                }
-
-                in = rs.getBinaryStream(1);
-                NodeReferences refs = new NodeReferences(targetId);
-                Serializer.deserialize(refs, in);
-
-                return refs;
-            } catch (Exception e) {
-                if (e instanceof NoSuchItemStateException) {
-                    throw (NoSuchItemStateException) e;
-                }
-                String msg = "failed to read node references: " + targetId;
-                log.error(msg, e);
-                throw new ItemStateException(msg, e);
-            } finally {
-                closeStream(in);
-                closeResultSet(rs);
-                resetStatement(stmt);
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p/>
-     * This method uses shared <code>PreparedStatement</code>s which must
-     * be executed strictly sequentially. Because this method synchronizes on
-     * the persistence manager instance there is no need to synchronize on the
-     * shared statement. If the method would not be sychronized the shared
-     * statements would have to be synchronized.
-     */
-    public synchronized void store(NodeReferences refs)
-            throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // check if insert or update
-        boolean update = exists(refs.getId());
-        PreparedStatement stmt = (update) ? nodeReferenceUpdate : nodeReferenceInsert;
-
-        try {
-            ByteArrayOutputStream out =
-                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
-            // serialize references
-            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.getId().toString());
-            stmt.executeUpdate();
-
-            // there's no need to close a ByteArrayOutputStream
-            //out.close();
-        } catch (Exception e) {
-            String msg = "failed to write node references: " + refs.getId();
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void destroy(NodeReferences refs)
-            throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        PreparedStatement stmt = nodeReferenceDelete;
-        try {
-            stmt.setString(1, refs.getId().toString());
-            stmt.executeUpdate();
-        } catch (Exception e) {
-            String msg = "failed to delete node references: " + refs.getId();
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean exists(NodeId id) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        PreparedStatement stmt = nodeStateSelectExist;
-        synchronized (stmt) {
-            ResultSet rs = null;
-            try {
-                stmt.setString(1, id.toString());
-                stmt.execute();
-                rs = stmt.getResultSet();
-
-                // a node state exists if the result has at least one entry
-                return rs.next();
-            } catch (Exception e) {
-                String msg = "failed to check existence of node state: " + id;
-                log.error(msg, e);
-                throw new ItemStateException(msg, e);
-            } finally {
-                closeResultSet(rs);
-                resetStatement(stmt);
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean exists(PropertyId id) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        PreparedStatement stmt = propertyStateSelectExist;
-        synchronized (stmt) {
-            ResultSet rs = null;
-            try {
-                stmt.setString(1, id.toString());
-                stmt.execute();
-                rs = stmt.getResultSet();
-
-                // a property state exists if the result has at least one entry
-                return rs.next();
-            } catch (Exception e) {
-                String msg = "failed to check existence of property state: " + id;
-                log.error(msg, e);
-                throw new ItemStateException(msg, e);
-            } finally {
-                closeResultSet(rs);
-                resetStatement(stmt);
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean exists(NodeReferencesId targetId) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        PreparedStatement stmt = nodeReferenceSelectExist;
-        synchronized (stmt) {
-            ResultSet rs = null;
-            try {
-                stmt.setString(1, targetId.toString());
-                stmt.execute();
-                rs = stmt.getResultSet();
-
-                // a reference exists if the result has at least one entry
-                return rs.next();
-            } catch (Exception e) {
-                String msg = "failed to check existence of node references: "
-                        + targetId;
-                log.error(msg, e);
-                throw new ItemStateException(msg, e);
-            } finally {
-                closeResultSet(rs);
-                resetStatement(stmt);
-            }
-        }
-    }
-
-    //----------------------------------< misc. helper methods & overridables >
-
-    /**
-     * Initializes the database connection used by this file system.
-     * <p>
-     * Subclasses should normally override the {@link #getConnection()}
-     * method instead of this one. The default implementation calls
-     * {@link #getConnection()} to get the database connection and disables
-     * the autocommit feature.
-     *
-     * @throws Exception if an error occurs
-     */
-    protected void initConnection() throws Exception {
-        con = getConnection();
-        con.setAutoCommit(false);
-    }
-
-    /**
-     * Abstract factory method for creating a new database connection. This
-     * method is called by {@link #init(PMContext)} when the persistence
-     * manager is started. The returned connection should come with the default
-     * JDBC settings, as the {@link #init(PMContext)} method will explicitly
-     * set the <code>autoCommit</code> and other properties as needed.
-     * <p>
-     * Note that the returned database connection is kept during the entire
-     * lifetime of the persistence manager, after which it is closed by
-     * {@link #close()} using the {@link #closeConnection(Connection)} method.
-     *
-     * @return new connection
-     * @throws Exception if an error occurs
-     */
-    protected Connection getConnection() throws Exception {
-        throw new UnsupportedOperationException("Override in a subclass!");
-    }
-
-    /**
-     * Closes the given database connection. This method is called by
-     * {@link #close()} to close the connection acquired using
-     * {@link #getConnection()} when the persistence manager was started.
-     * <p>
-     * The default implementation just calls the {@link Connection#close()}
-     * method of the given connection, but subclasses can override this
-     * method to provide more extensive database and connection cleanup.
-     *
-     * @param connection database connection
-     * @throws Exception if an error occurs
-     */
-    protected void closeConnection(Connection connection) throws Exception {
-        connection.close();
-    }
-
-    /**
-     * Resets the given <code>PreparedStatement</code> by clearing the parameters
-     * and warnings contained.
-     * <p/>
-     * NOTE: This method MUST be called in a synchronized context as neither
-     * this method nor the <code>PreparedStatement</code> instance on which it
-     * operates are thread safe.
-     *
-     * @param stmt The <code>PreparedStatement</code> to reset. If
-     *             <code>null</code> this method does nothing.
-     */
-    protected void resetStatement(PreparedStatement stmt) {
-        if (stmt != null) {
-            try {
-                stmt.clearParameters();
-                stmt.clearWarnings();
-            } catch (SQLException se) {
-                logException("failed resetting PreparedStatement", se);
-            }
-        }
-    }
-
-    protected void closeResultSet(ResultSet rs) {
-        if (rs != null) {
-            try {
-                rs.close();
-            } catch (SQLException se) {
-                logException("failed closing ResultSet", se);
-            }
-        }
-    }
-
-    protected void closeStream(InputStream in) {
-        if (in != null) {
-            try {
-                in.close();
-            } catch (IOException ignore) {
-            }
-        }
-    }
-
-    protected void closeStatement(Statement stmt) {
-        if (stmt != null) {
-            try {
-                stmt.close();
-            } catch (SQLException se) {
-                logException("failed closing Statement", se);
-            }
-        }
-    }
-
-    protected void logException(String message, SQLException se) {
-        if (message != null) {
-            log.error(message);
-        }
-        log.error("    reason: " + se.getMessage());
-        log.error("state/code: " + se.getSQLState() + "/" + se.getErrorCode());
-        log.debug("      dump:", se);
-    }
-
-    /**
-     * Makes sure that <code>schemaObjectPrefix</code> does only consist of
-     * characters that are allowed in names on the target database. Illegal
-     * characters will be escaped as necessary.
-     *
-     * @throws Exception if an error occurs
-     */
-    protected void prepareSchemaObjectPrefix() throws Exception {
-        DatabaseMetaData metaData = con.getMetaData();
-        String legalChars = metaData.getExtraNameCharacters();
-        legalChars += "ABCDEFGHIJKLMNOPQRSTUVWXZY0123456789_";
-
-        String prefix = schemaObjectPrefix.toUpperCase();
-        StringBuffer escaped = new StringBuffer();
-        for (int i = 0; i < prefix.length(); i++) {
-            char c = prefix.charAt(i);
-            if (legalChars.indexOf(c) == -1) {
-                escaped.append("_x");
-                String hex = Integer.toHexString(c);
-                escaped.append("0000".toCharArray(), 0, 4 - hex.length());
-                escaped.append(hex);
-                escaped.append("_");
-            } else {
-                escaped.append(c);
-            }
-        }
-        schemaObjectPrefix = escaped.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 {
-        DatabaseMetaData metaData = con.getMetaData();
-        String tableName = schemaObjectPrefix + "NODE";
-        if (metaData.storesLowerCaseIdentifiers()) {
-            tableName = tableName.toLowerCase();
-        } else if (metaData.storesUpperCaseIdentifiers()) {
-            tableName = tableName.toUpperCase();
-        }
-
-        ResultSet rs = metaData.getTables(null, null, tableName, null);
-        boolean schemaExists;
-        try {
-            schemaExists = rs.next();
-        } finally {
-            rs.close();
-        }
-
-        if (!schemaExists) {
-            // read ddl from resources
-            InputStream in = getClass().getResourceAsStream(schema + ".ddl");
-            if (in == null) {
-                String msg = "Configuration error: unknown schema '" + schema + "'";
-                log.debug(msg);
-                throw new RepositoryException(msg);
-            }
-            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
-            Statement stmt = con.createStatement();
-            try {
-                String sql = reader.readLine();
-                while (sql != null) {
-                    // Skip comments and empty lines
-                    if (!sql.startsWith("#") && sql.length() > 0) {
-                        // replace prefix variable
-                        sql = Text.replace(sql, SCHEMA_OBJECT_PREFIX_VARIABLE, schemaObjectPrefix);
-                        // execute sql stmt
-                        stmt.executeUpdate(sql);
-                    }
-                    // read next sql stmt
-                    sql = reader.readLine();
-                }
-                // commit the changes
-                con.commit();
-            } 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 InputStream get(String blobId) throws Exception {
-            PreparedStatement stmt = blobSelect;
-            synchronized (stmt) {
-                try {
-                    stmt.setString(1, blobId);
-                    stmt.execute();
-                    final ResultSet rs = stmt.getResultSet();
-                    if (!rs.next()) {
-                        closeResultSet(rs);
-                        throw new Exception("no such BLOB: " + blobId);
-                    }
-                    InputStream in = rs.getBinaryStream(1);
-                    if (in == null) {
-                        // some databases treat zero-length values as NULL;
-                        // return empty InputStream in such a case
-                        closeResultSet(rs);
-                        return new ByteArrayInputStream(new byte[0]);
-                    }
-
-                    /**
-                     * return an InputStream wrapper in order to
-                     * close the ResultSet when the stream is closed
-                     */
-                    return new FilterInputStream(in) {
-                        public void close() throws IOException {
-                            in.close();
-                            // now it's safe to close ResultSet
-                            closeResultSet(rs);
-                        }
-                    };
-                } 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);
-            }
-        }
-    }
-}
+public abstract class DatabasePersistenceManager
+        extends org.apache.jackrabbit.core.persistence.db.DatabasePersistenceManager {
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/db/DerbyPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/DerbyPersistenceManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/DerbyPersistenceManager.java	(working copy)
@@ -14,100 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.core.state.db;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-
-/**
- * <code>DerbyPersistenceManager</code> is a JDBC-based
- * <code>PersistenceManager</code> for Jackrabbit that persists
- * <code>ItemState</code> and <code>NodeReferences</code> objects in an
- * embedded Derby database using a simple custom serialization format 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>
- * <li><code>url</code>: the database url of the form
- * <code>"jdbc:derby:[db];[attributes]"</code></li>
- * <li><code>schemaObjectPrefix</code>: prefix to be prepended to schema objects</li>
- * <li><code>driver</code>: the FQN name of the JDBC driver class
- * (default: <code>"org.apache.derby.jdbc.EmbeddedDriver"</code>)</li>
- * <li><code>schema</code>: type of schema to be used
- * (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/>
- * The following is a fragment from a sample configuration:
- * <pre>
- *   &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>
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
  */
-public class DerbyPersistenceManager extends SimpleDbPersistenceManager {
+package org.apache.jackrabbit.core.state.db;
 
-    /**
-     * Logger instance
-     */
-    private static Logger log = LoggerFactory.getLogger(DerbyPersistenceManager.class);
-
-    /**
-     * Creates a new <code>SimpleDbPersistenceManager</code> instance.
-     */
-    public DerbyPersistenceManager() {
-        // preset some attributes to reasonable defaults
-        schema = "derby";
-        driver = "org.apache.derby.jdbc.EmbeddedDriver";
-        schemaObjectPrefix = "";
-        user = "";
-        password = "";
-    }
-
-    //------------------------------------------< DatabasePersistenceManager >
-
-    /**
-     * Closes the given connection by shutting down the embedded Derby
-     * database.
-     *
-     * @param connection database connection
-     * @throws SQLException if an error occurs
-     * @see DatabasePersistenceManager#closeConnection(Connection)
-     */
-    protected void closeConnection(Connection connection) throws SQLException {
-        // prepare connection url for issuing shutdown command
-        String url = connection.getMetaData().getURL();
-        int pos = url.lastIndexOf(';');
-        if (pos != -1) {
-            // strip any attributes from connection url
-            url = url.substring(0, pos);
-        }
-        url += ";shutdown=true";
-
-        // we have to reset the connection to 'autoCommit=true' before closing it;
-        // otherwise Derby would mysteriously complain about some pending uncommitted
-        // changes which can't possibly be true.
-        // @todo further investigate
-        connection.setAutoCommit(true);
-
-        // now it's safe to shutdown the embedded Derby database
-        try {
-            DriverManager.getConnection(url);
-        } catch (SQLException e) {
-            // a shutdown command always raises a SQLException
-            log.info(e.getMessage());
-        }
-    }
-}
+/**
+ * Legacy class kept for backward compatibility reasons.
+  * @deprecated use {@link org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager}
+  *             instead.
+  */
+public class DerbyPersistenceManager
+        extends org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager {
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/db/OraclePersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/OraclePersistenceManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/OraclePersistenceManager.java	(working copy)
@@ -14,401 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.core.state.db;
 
-import org.apache.jackrabbit.core.state.PMContext;
-import org.apache.jackrabbit.core.state.NodeReferences;
-import org.apache.jackrabbit.core.state.ItemStateException;
-import org.apache.jackrabbit.core.state.NodeState;
-import org.apache.jackrabbit.core.state.PropertyState;
-import org.apache.jackrabbit.core.state.ItemState;
-import org.apache.jackrabbit.core.state.util.Serializer;
-import org.apache.jackrabbit.util.Text;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.jcr.RepositoryException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.lang.reflect.Method;
-import java.sql.Blob;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.DatabaseMetaData;
-import java.sql.Statement;
-
-/**
- * <code>OraclePersistenceManager</code> is a JDBC-based
- * <code>PersistenceManager</code> for Jackrabbit that persists
- * <code>ItemState</code> and <code>NodeReferences</code> objects in Oracle
- * database using a simple custom serialization format 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>
- * <li><code>driver</code>: the FQN name of the JDBC driver class
- * (default: <code>"oracle.jdbc.OracleDriver"</code>)</li>
- * <li><code>schema</code>: type of schema to be used
- * (default: <code>"oracle"</code>)</li>
- * <li><code>url</code>: the database url (e.g.
- * <code>"jdbc:oracle:thin:@[host]:[port]:[sid]"</code>)</li>
- * <li><code>user</code>: the database user</li>
- * <li><code>password</code>: the user's password</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>
- * See also {@link SimpleDbPersistenceManager}.
- * <p/>
- * The following is a fragment from a sample configuration:
- * <pre>
- *   &lt;PersistenceManager class="org.apache.jackrabbit.core.state.db.OraclePersistenceManager"&gt;
- *       &lt;param name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/&gt;
- *       &lt;param name="user" value="scott"/&gt;
- *       &lt;param name="password" value="tiger"/&gt;
- *       &lt;param name="schemaObjectPrefix" value="${wsp.name}_"/&gt;
- *       &lt;param name="externalBLOBs" value="false"/&gt;
- *  &lt;/PersistenceManager&gt;
- * </pre>
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
  */
-public class OraclePersistenceManager extends SimpleDbPersistenceManager {
+package org.apache.jackrabbit.core.state.db;
 
-    /**
-     * Logger instance
-     */
-    private static Logger log = LoggerFactory.getLogger(OraclePersistenceManager.class);
-
-    private Class blobClass;
-    private Integer DURATION_SESSION_CONSTANT;
-    private Integer MODE_READWRITE_CONSTANT;
-
-    /**
-     * Creates a new <code>OraclePersistenceManager</code> instance.
-     */
-    public OraclePersistenceManager() {
-        // preset some attributes to reasonable defaults
-        schema = "oracle";
-        driver = "oracle.jdbc.OracleDriver";
-        schemaObjectPrefix = "";
-        user = "";
-        password = "";
-        initialized = false;
-    }
-
-    //---------------------------------< SimpleDbPersistenceManager overrides >
-    /**
-     * {@inheritDoc}
-     * <p/>
-     * Retrieve the <code>oracle.sql.BLOB</code> class via reflection, and
-     * initialize the values for the <code>DURATION_SESSION</code> and
-     * <code>MODE_READWRITE</code> constants defined there.
-     * @see oracle.sql.BLOB#DURATION_SESSION
-     * @see oracle.sql.BLOB#MODE_READWRITE
-     */
-    public void init(PMContext context) throws Exception {
-        super.init(context);
-
-        if (!externalBLOBs) {
-            blobStore = new OracleBLOBStore();
-        }
-
-        // initialize oracle.sql.BLOB class & constants
-
-        // use the Connection object for using the exact same
-        // class loader that the Oracle driver was loaded with
-        blobClass = con.getClass().getClassLoader().loadClass("oracle.sql.BLOB");
-        DURATION_SESSION_CONSTANT =
-                new Integer(blobClass.getField("DURATION_SESSION").getInt(null));
-        MODE_READWRITE_CONSTANT =
-                new Integer(blobClass.getField("MODE_READWRITE").getInt(null));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void store(NodeState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // check if insert or update
-        boolean update = state.getStatus() != ItemState.STATUS_NEW;
-        //boolean update = exists((NodeId) state.getId());
-        PreparedStatement stmt = (update) ? nodeStateUpdate : nodeStateInsert;
-
-        Blob blob = null;
-        try {
-            ByteArrayOutputStream out =
-                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
-            // serialize node state
-            Serializer.serialize(state, out);
-
-            // we are synchronized on this instance, therefore we do not
-            // not have to additionally synchronize on the preparedStatement
-
-            blob = createTemporaryBlob(new ByteArrayInputStream(out.toByteArray()));
-            stmt.setBlob(1, blob);
-            stmt.setString(2, state.getId().toString());
-            stmt.executeUpdate();
-
-            // there's no need to close a ByteArrayOutputStream
-            //out.close();
-        } catch (Exception e) {
-            String msg = "failed to write node state: " + state.getId();
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
-            if (blob != null) {
-                try {
-                    freeTemporaryBlob(blob);
-                } catch (Exception e1) {
-                }
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void store(PropertyState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // check if insert or update
-        boolean update = state.getStatus() != ItemState.STATUS_NEW;
-        //boolean update = exists((PropertyId) state.getId());
-        PreparedStatement stmt = (update) ? propertyStateUpdate : propertyStateInsert;
-
-        Blob blob = null;
-        try {
-            ByteArrayOutputStream out =
-                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
-            // serialize property state
-            Serializer.serialize(state, out, blobStore);
-
-            // we are synchronized on this instance, therefore we do not
-            // not have to additionally synchronize on the preparedStatement
-
-            blob = createTemporaryBlob(new ByteArrayInputStream(out.toByteArray()));
-            stmt.setBlob(1, blob);
-            stmt.setString(2, state.getId().toString());
-            stmt.executeUpdate();
-
-            // there's no need to close a ByteArrayOutputStream
-            //out.close();
-        } catch (Exception e) {
-            String msg = "failed to write property state: " + state.getId();
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
-            if (blob != null) {
-                try {
-                    freeTemporaryBlob(blob);
-                } catch (Exception e1) {
-                }
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void store(NodeReferences refs) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // check if insert or update
-        boolean update = exists(refs.getId());
-        PreparedStatement stmt = (update) ? nodeReferenceUpdate : nodeReferenceInsert;
-
-        Blob blob = null;
-        try {
-            ByteArrayOutputStream out =
-                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
-            // serialize references
-            Serializer.serialize(refs, out);
-
-            // we are synchronized on this instance, therefore we do not
-            // not have to additionally synchronize on the preparedStatement
-
-            blob = createTemporaryBlob(new ByteArrayInputStream(out.toByteArray()));
-            stmt.setBlob(1, blob);
-            stmt.setString(2, refs.getId().toString());
-            stmt.executeUpdate();
-
-            // there's no need to close a ByteArrayOutputStream
-            //out.close();
-        } catch (Exception e) {
-            String msg = "failed to write node references: " + refs.getId();
-            log.error(msg, e);
-            throw new ItemStateException(msg, e);
-        } finally {
-            resetStatement(stmt);
-            if (blob != null) {
-                try {
-                    freeTemporaryBlob(blob);
-                } catch (Exception e1) {
-                }
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p/>
-     * Overridden in order to support multiple oracle schemas. Note that
-     * schema names in Oracle correspond to the username of the connection.
-     * See http://issues.apache.org/jira/browse/JCR-582
-     *
-     * @throws Exception if an error occurs
-     */
-    protected void checkSchema() throws Exception {
-        DatabaseMetaData metaData = con.getMetaData();
-        String tableName = schemaObjectPrefix + "NODE";
-        if (metaData.storesLowerCaseIdentifiers()) {
-            tableName = tableName.toLowerCase();
-        } else if (metaData.storesUpperCaseIdentifiers()) {
-            tableName = tableName.toUpperCase();
-        }
-        String userName = metaData.getUserName();
-
-        ResultSet rs = metaData.getTables(null, userName, tableName, null);
-        boolean schemaExists;
-        try {
-            schemaExists = rs.next();
-        } finally {
-            rs.close();
-        }
-
-        if (!schemaExists) {
-            // read ddl from resources
-            InputStream in = getClass().getResourceAsStream(schema + ".ddl");
-            if (in == null) {
-                String msg = "Configuration error: unknown schema '" + schema + "'";
-                log.debug(msg);
-                throw new RepositoryException(msg);
-            }
-            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
-            Statement stmt = con.createStatement();
-            try {
-                String sql = reader.readLine();
-                while (sql != null) {
-                    // Skip comments and empty lines
-                    if (!sql.startsWith("#") && sql.length() > 0) {
-                        // replace prefix variable
-                        sql = Text.replace(sql, SCHEMA_OBJECT_PREFIX_VARIABLE, schemaObjectPrefix);
-                        // execute sql stmt
-                        stmt.executeUpdate(sql);
-                    }
-                    // read next sql stmt
-                    sql = reader.readLine();
-                }
-                // commit the changes
-                con.commit();
-            } finally {
-                closeStream(in);
-                closeStatement(stmt);
-            }
-        }
-    }
-
-    //----------------------------------------< oracle-specific blob handling >
-    /**
-     * Creates a temporary oracle.sql.BLOB instance via reflection and spools
-     * the contents of the specified stream.
-     */
-    protected Blob createTemporaryBlob(InputStream in) throws Exception {
-        /*
-        BLOB blob = BLOB.createTemporary(con, false, BLOB.DURATION_SESSION);
-        blob.open(BLOB.MODE_READWRITE);
-        OutputStream out = blob.getBinaryOutputStream();
-        ...
-        out.flush();
-        out.close();
-        blob.close();
-        return blob;
-        */
-        Method createTemporary = blobClass.getMethod("createTemporary",
-                new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE});
-        Object blob = createTemporary.invoke(null,
-                new Object[]{con, Boolean.FALSE, DURATION_SESSION_CONSTANT});
-        Method open = blobClass.getMethod("open", new Class[]{Integer.TYPE});
-        open.invoke(blob, new Object[]{MODE_READWRITE_CONSTANT});
-        Method getBinaryOutputStream =
-                blobClass.getMethod("getBinaryOutputStream", new Class[0]);
-        OutputStream out = (OutputStream) getBinaryOutputStream.invoke(blob, null);
-        try {
-            int read;
-            byte[] buf = new byte[8192];
-            while ((read = in.read(buf, 0, buf.length)) > -1) {
-                out.write(buf, 0, read);
-            }
-        } finally {
-            try {
-                out.flush();
-            } catch (IOException ioe) {
-            }
-            out.close();
-        }
-        Method close = blobClass.getMethod("close", new Class[0]);
-        close.invoke(blob, null);
-        return (Blob) blob;
-    }
-
-    /**
-     * Frees a temporary oracle.sql.BLOB instance via reflection.
-     */
-    protected void freeTemporaryBlob(Object blob) throws Exception {
-        // blob.freeTemporary();
-        Method freeTemporary = blobClass.getMethod("freeTemporary", new Class[0]);
-        freeTemporary.invoke(blob, null);
-    }
-
-    //--------------------------------------------------------< inner classes >
-    class OracleBLOBStore extends DbBLOBStore {
-        /**
-         * {@inheritDoc}
-         */
-        public synchronized void put(String blobId, InputStream in, long size)
-                throws Exception {
-            PreparedStatement stmt = blobSelectExist;
-            Blob blob = null;
-            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;
-
-                blob = createTemporaryBlob(in);
-                stmt.setBlob(1, blob);
-                stmt.setString(2, blobId);
-                stmt.executeUpdate();
-            } finally {
-                resetStatement(stmt);
-                if (blob != null) {
-                    try {
-                        freeTemporaryBlob(blob);
-                    } catch (Exception e1) {
-                    }
-                }
-            }
-        }
-    }
-}
+/**
+ * Legacy class kept for backward compatibility reasons.
+  * @deprecated use {@link org.apache.jackrabbit.core.persistence.db.OraclePersistenceManager}
+  *             instead.
+  */
+public class OraclePersistenceManager
+        extends org.apache.jackrabbit.core.persistence.db.OraclePersistenceManager {
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/db/postgresql.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/postgresql.ddl	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/postgresql.ddl	(working copy)
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA bytea not null)
-create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
-create table ${schemaObjectPrefix}PROP (PROP_ID varchar not null, PROP_DATA bytea not null)
-create unique index ${schemaObjectPrefix}PROP_IDX on ${schemaObjectPrefix}PROP (PROP_ID)
-create table ${schemaObjectPrefix}REFS (NODE_ID char(36) not null, REFS_DATA bytea not null)
-create unique index ${schemaObjectPrefix}REFS_IDX on ${schemaObjectPrefix}REFS (NODE_ID)
-create table ${schemaObjectPrefix}BINVAL (BINVAL_ID varchar not null, BINVAL_DATA bytea not null)
-create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)
Index: src/main/java/org/apache/jackrabbit/core/state/db/default.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/default.ddl	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/default.ddl	(working copy)
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA varbinary not null)
-create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
-create table ${schemaObjectPrefix}PROP (PROP_ID varchar not null, PROP_DATA varbinary not null)
-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)
Index: src/main/java/org/apache/jackrabbit/core/state/db/derby.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/derby.ddl	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/derby.ddl	(working copy)
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA blob not null)
-create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
-create table ${schemaObjectPrefix}PROP (PROP_ID varchar(1024) not null, PROP_DATA blob not null)
-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)
Index: src/main/java/org/apache/jackrabbit/core/state/db/mysql.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/mysql.ddl	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/mysql.ddl	(working copy)
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA blob not null)
-create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
-create table ${schemaObjectPrefix}PROP (PROP_ID varchar(255) not null, PROP_DATA blob not null)
-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)
Index: src/main/java/org/apache/jackrabbit/core/state/db/oracle.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/oracle.ddl	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/oracle.ddl	(working copy)
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA blob not null)
-create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
-create table ${schemaObjectPrefix}PROP (PROP_ID varchar2(1024) not null, PROP_DATA blob not null)
-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 varchar2(1024) not null, BINVAL_DATA blob null)
-create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)
Index: src/main/java/org/apache/jackrabbit/core/state/db/JNDIDatabasePersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/JNDIDatabasePersistenceManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/JNDIDatabasePersistenceManager.java	(working copy)
@@ -14,68 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.core.state.db;
 
-import java.sql.Connection;
-import java.sql.SQLException;
-
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-import javax.sql.DataSource;
-
-/**
- * Database persistence manager that uses JNDI to acquire the database
- * connection. The JNDI location of the {@link DataSource} to be used in
- * given as the <code>dataSourceLocation</code> configuration property.
- * See the {@link SimpleDbPersistenceManager} for more configuration
- * details.
- * <p>
- * <strong>WARNING:</strong> The acquired database connection is kept
- * for the entire lifetime of the persistence manager instance. The
- * configured data source should be prepared for this.
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
  */
-public class JNDIDatabasePersistenceManager extends DatabasePersistenceManager {
+package org.apache.jackrabbit.core.state.db;
 
-    /**
-     * JNDI location of the data source used to acquire database connections.
-     */
-    private String dataSourceLocation;
-
-    //----------------------------------------------------< setters & getters >
-
-    /**
-     * Returns the JNDI location of the data source.
-     *
-     * @return data source location
-     */
-    public String getDataSourceLocation() {
-        return dataSourceLocation;
-    }
-
-    /**
-     * Sets the JNDI location of the data source.
-     *
-     * @param dataSourceLocation data source location
-     */
-    public void setDataSourceLocation(String dataSourceLocation) {
-        this.dataSourceLocation = dataSourceLocation;
-    }
-
-    //-------------------------------------------< DatabasePersistenceManager >
-
-    /**
-     * Returns a JDBC connection from a {@link DataSource} acquired from JNDI
-     * with the configured data source location.
-     *
-     * @return new database connection
-     * @throws NamingException if the given data source location does not exist
-     * @throws SQLException if a database access error occurs
-     * @see DatabasePersistenceManager#getConnection()
-     */
-    protected Connection getConnection() throws NamingException, SQLException {
-        InitialContext ic = new InitialContext();
-        DataSource dataSource = (DataSource) ic.lookup(dataSourceLocation);
-        return dataSource.getConnection();
-    }
-
-}
+/**
+ * Legacy class kept for backward compatibility reasons.
+  * @deprecated use {@link org.apache.jackrabbit.core.persistence.db.JNDIDatabasePersistenceManager}
+  *             instead.
+  */
+public class JNDIDatabasePersistenceManager
+        extends org.apache.jackrabbit.core.persistence.db.JNDIDatabasePersistenceManager {
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/db/db2.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/db2.ddl	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/db2.ddl	(working copy)
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA blob not null)
-create unique index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
-create table ${schemaObjectPrefix}PROP (PROP_ID varchar(1000) not null, PROP_DATA blob not null)
-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(1000) not null, BINVAL_DATA blob(100M) not null)
-create unique index ${schemaObjectPrefix}BINVAL_IDX on ${schemaObjectPrefix}BINVAL (BINVAL_ID)
Index: src/main/java/org/apache/jackrabbit/core/state/db/daffodil.ddl
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/daffodil.ddl	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/daffodil.ddl	(working copy)
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-create table ${schemaObjectPrefix}NODE (NODE_ID char(36) not null, NODE_DATA blob not null)
-create index ${schemaObjectPrefix}NODE_IDX on ${schemaObjectPrefix}NODE (NODE_ID)
-create table ${schemaObjectPrefix}PROP (PROP_ID varchar(1024) not null, PROP_DATA blob not null)
-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)
Index: src/main/java/org/apache/jackrabbit/core/state/db/package.html
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/db/package.html	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/db/package.html	(working copy)
@@ -1,32 +0,0 @@
-<!--
-   Licensed to the Apache Software Foundation (ASF) under one or more
-   contributor license agreements.  See the NOTICE file distributed with
-   this work for additional information regarding copyright ownership.
-   The ASF licenses this file to You under the Apache License, Version 2.0
-   (the "License"); you may not use this file except in compliance with
-   the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
--->
-<body>
-This package contains the class <code>{@link SimpleDbPersistenceManager}</code>,
-a simple generic JDBC-based <code>PersistenceManager</code> for Jackrabbit.
-<p/>
-It also contains [schemaName].ddl files which are read by
-<code>{@link SimpleDbPersistenceManager}</code>  in order to automatically
-create the required schema objects on the target database. Every line in a
-[schemaName].ddl file is executed separatly by calling
-<code>java.sql.Statement.execute(String)</code> where every occurence of the
-the string <code>"${schemaObjectPrefix}"</code> has been replaced with the
-value of the property <code>schemaObjectPrefix</code> (see
- <code>{@link SimpleDbPersistenceManager#setSchemaObjectPrefix(String)}</code>).
-The schema name is either set programmtically by calling
-<code>{@link SimpleDbPersistenceManager#setSchema(String)}</code> or configured
-through the <code>schema</code> bean property.
-</body>
Index: src/main/java/org/apache/jackrabbit/core/state/PMContext.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/PMContext.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/PMContext.java	(working copy)
@@ -14,109 +14,39 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
 package org.apache.jackrabbit.core.state;
 
+import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.fs.FileSystem;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
-import org.apache.jackrabbit.core.NodeId;
 
 import javax.jcr.NamespaceRegistry;
 import java.io.File;
 
 /**
- * A <code>PMContext</code> is used to provide context information for a
- * <code>PersistenceManager</code>.
- *
- * @see PersistenceManager#init(PMContext)
+ * Legacy class kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.PMContext}
+ *             instead.
  */
-public class PMContext {
+public class PMContext extends org.apache.jackrabbit.core.persistence.PMContext {
 
-    /**
-     * the physcial home dir
-     */
-    private final File physicalHomeDir;
-
-    /**
-     * the virtual jackrabbit filesystem
-     */
-    private final FileSystem fs;
-
-    /**
-     * namespace registry
-     */
-    private final NamespaceRegistry nsReg;
-
-    /**
-     * node type registry
-     */
-    private final NodeTypeRegistry ntReg;
-
-    /**
-     * uuid of the root node
-     */
-    private final NodeId rootNodeId;
-
-    /**
-     * Creates a new <code>PMContext</code>.
-     *
-     * @param homeDir the physical home directory
-     * @param fs the virtual jackrabbit filesystem
-     * @param rootNodeId id of the root node
-     * @param nsReg        namespace registry
-     * @param ntReg        node type registry
-     */
-    public PMContext(File homeDir,
-                     FileSystem fs,
-                     NodeId rootNodeId,
-                     NamespaceRegistry nsReg,
-                     NodeTypeRegistry ntReg) {
-        this.physicalHomeDir = homeDir;
-        this.fs = fs;
-        this.rootNodeId = rootNodeId;
-        this.nsReg = nsReg;
-        this.ntReg = ntReg;
+    public PMContext(File homeDir, FileSystem fs, NodeId rootNodeId,
+                         NamespaceRegistry nsReg, NodeTypeRegistry ntReg) {
+        super(homeDir, fs, rootNodeId, nsReg, ntReg);
     }
-
-
-    /**
-     * Returns the physical home directory for this persistence manager
-     * @return the physical home directory for this persistence manager
-     */
-    public File getHomeDir() {
-        return physicalHomeDir;
-    }
-
-    /**
-     * Returns the virtual filesystem for this persistence manager
-     * @return the virtual filesystem for this persistence manager
-     */
-    public FileSystem getFileSystem() {
-        return fs;
-    }
-
-    /**
-     * Returns the id of the root node
-     * @return the id of the root node
-     */
-    public NodeId getRootNodeId() {
-        return rootNodeId;
-    }
-
-    /**
-     * Returns the namespace registry
-     *
-     * @return the namespace registry
-     */
-    public NamespaceRegistry getNamespaceRegistry() {
-        return nsReg;
-    }
-
-    /**
-     * Returns the node type registry
-     *
-     * @return the node type registry
-     */
-    public NodeTypeRegistry getNodeTypeRegistry() {
-        return ntReg;
-    }
-}
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java	(working copy)
@@ -21,6 +21,7 @@
 import org.apache.jackrabbit.core.ItemId;
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
 import org.apache.jackrabbit.core.version.XAVersionManager;
 import org.apache.jackrabbit.core.nodetype.EffectiveNodeType;
 import org.apache.jackrabbit.core.nodetype.NodeDefId;
Index: src/main/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java	(working copy)
@@ -14,584 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
 package org.apache.jackrabbit.core.state.mem;
 
-import org.apache.jackrabbit.core.ItemId;
-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.ItemStateException;
-import org.apache.jackrabbit.core.state.NoSuchItemStateException;
-import org.apache.jackrabbit.core.state.NodeReferences;
-import org.apache.jackrabbit.core.state.NodeReferencesId;
-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;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.jcr.PropertyType;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
 /**
- * <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
- * binary serialization format (see {@link Serializer}).
- * <p/>
- * It is configured through the following properties:
- * <ul>
- * <li><code>initialCapacity</code>: initial capacity of the hash map used to store the data</li>
- * <li><code>loadFactor</code>: load factor of the hash map used to store the data</li>
- * <li><code>persistent</code>: if <code>true</code> the contents of the hash map
- * is loaded on startup and stored on shutdown;
- * if <code>false</code> nothing is persisted</li>
- * </ul>
- * <b>Please note that this class should only be used for testing purposes.</b>
+ * Legacy class kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager}
+ *             instead.
  */
-public class InMemPersistenceManager extends AbstractPersistenceManager {
-
-    private static Logger log = LoggerFactory.getLogger(InMemPersistenceManager.class);
-
-    protected boolean initialized;
-
-    protected Map stateStore;
-    protected Map refsStore;
-
-    // initial size of buffer used to serialize objects
-    protected static final int INITIAL_BUFFER_SIZE = 1024;
-
-    // some constants used in serialization
-    protected static final String STATE_FILE_PATH = "/data/.state.bin";
-    protected static final String REFS_FILE_PATH = "/data/.refs.bin";
-    protected static final byte NODE_ENTRY = 0;
-    protected static final byte PROP_ENTRY = 1;
-
-    // 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
-     * (if <code>persistent==true</code>)
-     */
-    protected FileSystem wspFS;
-
-    // initial capacity
-    protected int initialCapacity = 32768;
-    // load factor for the hash map
-    protected float loadFactor = 0.75f;
-    // should hash map be persisted?
-    protected boolean persistent = true;
-
-    /**
-     * Creates a new <code>InMemPersistenceManager</code> instance.
-     */
-    public InMemPersistenceManager() {
-        initialized = false;
-    }
-
-    public void setInitialCapacity(int initialCapacity) {
-        this.initialCapacity = initialCapacity;
-    }
-
-    public void setInitialCapacity(String initialCapacity) {
-        this.initialCapacity = Integer.valueOf(initialCapacity).intValue();
-    }
-
-    public String getInitialCapacity() {
-        return Integer.toString(initialCapacity);
-    }
-
-    public void setLoadFactor(float loadFactor) {
-        this.loadFactor = loadFactor;
-    }
-
-    public void setLoadFactor(String loadFactor) {
-        this.loadFactor = Float.valueOf(loadFactor).floatValue();
-    }
-
-    public String getLoadFactor() {
-        return Float.toString(loadFactor);
-    }
-
-    public boolean isPersistent() {
-        return persistent;
-    }
-
-    public void setPersistent(boolean persistent) {
-        this.persistent = persistent;
-    }
-
-    public void setPersistent(String persistent) {
-        this.persistent = Boolean.valueOf(persistent).booleanValue();
-    }
-
-    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();
-    }
-
-    /**
-     * Reads the content of the hash maps from the file system
-     *
-     * @throws Exception if an error occurs
-     */
-    public synchronized void loadContents() throws Exception {
-        // read item states
-        FileSystemResource fsRes = new FileSystemResource(wspFS, STATE_FILE_PATH);
-        if (!fsRes.exists()) {
-            return;
-        }
-        BufferedInputStream bis = new BufferedInputStream(fsRes.getInputStream());
-        DataInputStream in = new DataInputStream(bis);
-
-        try {
-            int n = in.readInt();   // number of entries
-            while (n-- > 0) {
-                byte type = in.readByte();  // entry type
-                ItemId id;
-                if (type == NODE_ENTRY) {
-                    // entry type: node
-                    String s = in.readUTF();    // id
-                    id = NodeId.valueOf(s);
-                } else {
-                    // entry type: property
-                    String s = in.readUTF();    // id
-                    id = PropertyId.valueOf(s);
-                }
-                int length = in.readInt();  // data length
-                byte[] data = new byte[length];
-                in.readFully(data);  // data
-                // store in map
-                stateStore.put(id, data);
-            }
-        } finally {
-            in.close();
-        }
-
-        // read references
-        fsRes = new FileSystemResource(wspFS, REFS_FILE_PATH);
-        bis = new BufferedInputStream(fsRes.getInputStream());
-        in = new DataInputStream(bis);
-
-        try {
-            int n = in.readInt();   // number of entries
-            while (n-- > 0) {
-                String s = in.readUTF();    // target id
-                NodeReferencesId id = (NodeReferencesId) NodeReferencesId.valueOf(s);
-                int length = in.readInt();  // data length
-                byte[] data = new byte[length];
-                in.readFully(data);  // data
-                // store in map
-                refsStore.put(id, data);
-            }
-        } finally {
-            in.close();
-        }
-    }
-
-    /**
-     * Writes the content of the hash maps to the file system
-     *
-     * @throws Exception if an error occurs
-     */
-    public synchronized void storeContents() throws Exception {
-        // write item states
-        FileSystemResource fsRes = new FileSystemResource(wspFS, STATE_FILE_PATH);
-        fsRes.makeParentDirs();
-        BufferedOutputStream bos = new BufferedOutputStream(fsRes.getOutputStream());
-        DataOutputStream out = new DataOutputStream(bos);
-
-        try {
-
-            out.writeInt(stateStore.size());    // number of entries
-            // entries
-            Iterator iterKeys = stateStore.keySet().iterator();
-            while (iterKeys.hasNext()) {
-                ItemId id = (ItemId) iterKeys.next();
-                if (id.denotesNode()) {
-                    out.writeByte(NODE_ENTRY);  // entry type
-                } else {
-                    out.writeByte(PROP_ENTRY);  // entry type
-                }
-                out.writeUTF(id.toString());    // id
-                byte[] data = (byte[]) stateStore.get(id);
-                out.writeInt(data.length);  // data length
-                out.write(data);    // data
-            }
-        } finally {
-            out.close();
-        }
-
-        // write references
-        fsRes = new FileSystemResource(wspFS, REFS_FILE_PATH);
-        fsRes.makeParentDirs();
-        bos = new BufferedOutputStream(fsRes.getOutputStream());
-        out = new DataOutputStream(bos);
-
-        try {
-            out.writeInt(refsStore.size()); // number of entries
-            // entries
-            Iterator iterKeys = refsStore.keySet().iterator();
-            while (iterKeys.hasNext()) {
-                NodeReferencesId id = (NodeReferencesId) iterKeys.next();
-                out.writeUTF(id.toString());    // target id
-                byte[] data = (byte[]) refsStore.get(id);
-                out.writeInt(data.length);  // data length
-                out.write(data);    // data
-            }
-        } finally {
-            out.close();
-        }
-    }
-
-    //---------------------------------------------------< PersistenceManager >
-    /**
-     * {@inheritDoc}
-     */
-    public void init(PMContext context) throws Exception {
-        if (initialized) {
-            throw new IllegalStateException("already initialized");
-        }
-
-        stateStore = new HashMap(initialCapacity, loadFactor);
-        refsStore = new HashMap(initialCapacity, loadFactor);
-
-        wspFS = context.getFileSystem();
-
-        /**
-         * 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
-            loadContents();
-        }
-
-        initialized = true;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void close() throws Exception {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            if (persistent) {
-                // serialize contents of state and refs stores
-                storeContents();
-            } else {
-                // clear out blob store
-                try {
-                    String[] folders = blobFS.listFolders("/");
-                    for (int i = 0; i < folders.length; i++) {
-                        blobFS.deleteFolder(folders[i]);
-                    }
-                    String[] files = blobFS.listFiles("/");
-                    for (int i = 0; i < files.length; i++) {
-                        blobFS.deleteFile(files[i]);
-                    }
-                } catch (Exception e) {
-                    // ignore
-                }
-            }
-
-            // close BLOB file system
-            blobFS.close();
-            blobFS = null;
-            blobStore = null;
-
-            stateStore.clear();
-            stateStore = null;
-            refsStore.clear();
-            refsStore = null;
-        } finally {
-            initialized = false;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized NodeState load(NodeId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        byte[] data = (byte[]) stateStore.get(id);
-        if (data == null) {
-            throw new NoSuchItemStateException(id.toString());
-        }
-
-        ByteArrayInputStream in = new ByteArrayInputStream(data);
-        try {
-            NodeState state = createNew(id);
-            Serializer.deserialize(state, in);
-            return state;
-        } catch (Exception e) {
-            String msg = "failed to read node state: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized PropertyState load(PropertyId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        byte[] data = (byte[]) stateStore.get(id);
-        if (data == null) {
-            throw new NoSuchItemStateException(id.toString());
-        }
-
-        ByteArrayInputStream in = new ByteArrayInputStream(data);
-        try {
-            PropertyState state = createNew(id);
-            Serializer.deserialize(state, in, blobStore);
-            return state;
-        } catch (Exception e) {
-            String msg = "failed to read property state: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void store(NodeState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            ByteArrayOutputStream out =
-                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
-            // serialize node state
-            Serializer.serialize(state, out);
-
-            // store in serialized format in map for better memory efficiency
-            stateStore.put(state.getNodeId(), out.toByteArray());
-            // there's no need to close a ByteArrayOutputStream
-            //out.close();
-        } catch (Exception e) {
-            String msg = "failed to write node state: " + state.getNodeId();
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void store(PropertyState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            ByteArrayOutputStream out =
-                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
-            // serialize property state
-            Serializer.serialize(state, out, blobStore);
-
-            // store in serialized format in map for better memory efficiency
-            stateStore.put(state.getPropertyId(), out.toByteArray());
-            // there's no need to close a ByteArrayOutputStream
-            //out.close();
-        } catch (Exception e) {
-            String msg = "failed to store property state: " + state.getPropertyId();
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void destroy(NodeState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // remove node state
-        stateStore.remove(state.getNodeId());
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void destroy(PropertyState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // delete binary values (stored as files)
-        InternalValue[] values = state.getValues();
-        if (values != null) {
-            for (int i = 0; i < values.length; i++) {
-                InternalValue val = values[i];
-                if (val != null) {
-                    if (val.getType() == PropertyType.BINARY) {
-                        BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
-                        // delete blob file and prune empty parent folders
-                        blobVal.delete(true);
-                    }
-                }
-            }
-        }
-
-        // remove property state
-        stateStore.remove(state.getPropertyId());
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized NodeReferences load(NodeReferencesId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        byte[] data = (byte[]) refsStore.get(id);
-        if (data == null) {
-            throw new NoSuchItemStateException(id.toString());
-        }
-
-        ByteArrayInputStream in = new ByteArrayInputStream(data);
-        try {
-            NodeReferences refs = new NodeReferences(id);
-            Serializer.deserialize(refs, in);
-            return refs;
-        } catch (Exception e) {
-            String msg = "failed to load references: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void store(NodeReferences refs) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            ByteArrayOutputStream out =
-                    new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
-            // serialize references
-            Serializer.serialize(refs, out);
-
-            // store in serialized format in map for better memory efficiency
-            refsStore.put(refs.getId(), out.toByteArray());
-            // there's no need to close a ByteArrayOutputStream
-            //out.close();
-        } catch (Exception e) {
-            String msg = "failed to store references: " + refs.getId();
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void destroy(NodeReferences refs) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // remove node references
-        refsStore.remove(refs.getId());
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean exists(PropertyId id) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-        return stateStore.containsKey(id);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean exists(NodeId id) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-        return stateStore.containsKey(id);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean exists(NodeReferencesId id) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-        return refsStore.containsKey(id);
-    }
-}
+public class InMemPersistenceManager
+        extends org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager {
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/PersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/PersistenceManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/PersistenceManager.java	(working copy)
@@ -14,162 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.core.state;
 
-import org.apache.jackrabbit.core.NodeId;
-import org.apache.jackrabbit.core.PropertyId;
-
-/**
- * Persistence manager interface. Persistence managers are
- * internal Jackrabbit components that handle the persistent
- * storage of content nodes and properties. A persistence
- * manager knows how to retrieve the persistent states of
- * content items and how to atomically save a set of changes
- * to the persistent state.
- * <p>
- * Each workspace of a Jackrabbit content repository uses separate
- * persistence manager to store the content in that workspace. Also
- * the Jackrabbit version handler uses a separate persistence manager.
- * The persistence managers in use are configured in the Jackrabbit
- * XML configuration files. The configured persistence managers are
- * instantiated and initialized using the JavaBeans conventions.
+/*
+ * $URL$
+ * $Id$
  *
- * <h2>Persistence manager life cycle</h2>
- * <p>
- * The life cycle of a persistence manager instance contains four phases:
- * <ol>
- *   <li>Instantiation, where the instance is created and possible
- *       configuration properties are set using the JavaBean conventions.
- *       During this phase the persistence manager should not attempt to
- *       reference any external resources.
- *   <li>Initialization, where the {@link #init(PMContext) init} method
- *       is invoked to bind the persistence manager with a given
- *       {@link PMContext context}.
- *   <li>Normal usage, where the various create, load, exists, and store
- *       methods of the persistence manager are used to manage the
- *       persistent content items.
- *   <li>Closing, where the {@link #close() close} method is invoked
- *       to close the persistence manager and release all acquired
- *       resources.
- * </ol>
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
  */
-public interface PersistenceManager {
+package org.apache.jackrabbit.core.state;
 
-    /**
-     * Initializes the persistence manager. The persistence manager is
-     * permanently bound to the given context, and any required external
-     * resources are acquired.
-     * <p>
-     * An appropriate exception is thrown if the persistence manager
-     * initialization fails for whatever reason. In this case the
-     * state of the persistence manager is undefined and the instance
-     * should be discarded.
-     *
-     * @param context persistence manager context
-     * @throws Exception if the persistence manager intialization failed
-     */
-    void init(PMContext context) throws Exception;
-
-    /**
-     * Closes the persistence manager. The consistency of the persistent
-     * storage is guaranteed and all acquired resources are released.
-     * It is an error to invoke any methods on a closed persistence manager,
-     * and implementations are free to enforce this constraint by throwing
-     * IllegalStateExceptions in such cases.
-     * <p>
-     * An appropriate exception is thrown if the persistence manager
-     * could not be closed properly. In this case the state of the
-     * persistence manager is undefined and the instance should be
-     * discarded.
-     *
-     * @throws Exception if the persistence manager failed to close properly
-     */
-    void close() throws Exception;
-
-    /**
-     * Creates a new node state instance with the given id.
-     *
-     * @param id node id
-     * @return node state instance
-     */
-    NodeState createNew(NodeId id);
-
-    /**
-     * Creates a new property state instance with the given id.
-     *
-     * @param id property id
-     * @return property state instance
-     */
-    PropertyState createNew(PropertyId id);
-
-    /**
-     * Load the persistent members of a node state.
-     *
-     * @param id node id
-     * @return loaded node state
-     * @throws NoSuchItemStateException if the node state does not exist
-     * @throws ItemStateException if another error occurs
-     */
-    NodeState load(NodeId id)
-            throws NoSuchItemStateException, ItemStateException;
-
-    /**
-     * Load the persistent members of a property state.
-     *
-     * @param id property id
-     * @return loaded property state
-     * @throws NoSuchItemStateException if the property state does not exist
-     * @throws ItemStateException if another error occurs
-     */
-    PropertyState load(PropertyId id)
-            throws NoSuchItemStateException, ItemStateException;
-
-    /**
-     * Load the persistent members of a node references object.
-     *
-     * @param id reference target node id
-     * @throws NoSuchItemStateException if the target node does not exist
-     * @throws ItemStateException if another error occurs
-     */
-    NodeReferences load(NodeReferencesId id)
-            throws NoSuchItemStateException, ItemStateException;
-
-    /**
-     * Checks whether the identified node exists.
-     *
-     * @param id node id
-     * @return <code>true</code> if the node exists,
-     *         <code>false</code> otherwise
-     * @throws ItemStateException on persistence manager errors
-     */
-    boolean exists(NodeId id) throws ItemStateException;
-
-    /**
-     * Checks whether the identified property exists.
-     *
-     * @param id property id
-     * @return <code>true</code> if the property exists,
-     *         <code>false</code> otherwise
-     * @throws ItemStateException on persistence manager errors
-     */
-    boolean exists(PropertyId id) throws ItemStateException;
-
-    /**
-     * Checks whether references of the identified target node exist.
-     *
-     * @param targetId target node id
-     * @return <code>true</code> if the references exist,
-     *         <code>false</code> otherwise
-     * @throws ItemStateException on persistence manager errors
-     */
-    boolean exists(NodeReferencesId targetId) throws ItemStateException;
-
-    /**
-     * Atomically saves the given set of changes.
-     *
-     * @param changeLog change log containing states that were changed
-     * @throws ItemStateException if the changes could not be saved
-     */
-    void store(ChangeLog changeLog) throws ItemStateException;
-
-}
+/**
+ * Legacy interface kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.PersistenceManager}
+ *             instead.
+ */
+public interface PersistenceManager
+        extends org.apache.jackrabbit.core.persistence.PersistenceManager {
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/xml/XMLPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/xml/XMLPersistenceManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/xml/XMLPersistenceManager.java	(working copy)
@@ -14,906 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
 package org.apache.jackrabbit.core.state.xml;
 
-import org.apache.jackrabbit.core.NodeId;
-import org.apache.jackrabbit.core.PropertyId;
-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.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;
-import org.apache.jackrabbit.core.state.NodeReferences;
-import org.apache.jackrabbit.core.state.NodeReferencesId;
-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.ResourceBasedBLOBStore;
-import org.apache.jackrabbit.core.util.DOMWalker;
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.jcr.PropertyType;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
 /**
- * <code>XMLPersistenceManager</code> is a <code>FileSystem</code>-based
- * <code>PersistenceManager</code> that persists <code>ItemState</code>
- * and <code>NodeReferences</code> objects in XML format.
+ * Legacy class kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.xml.XMLPersistenceManager}
+ *             instead.
  */
-public class XMLPersistenceManager extends AbstractPersistenceManager {
+public class XMLPersistenceManager
+        extends org.apache.jackrabbit.core.persistence.xml.XMLPersistenceManager {
 
-    private static Logger log = LoggerFactory.getLogger(XMLPersistenceManager.class);
-
-    /**
-     * hexdigits for toString
-     */
-    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
-
-    /**
-     * The default encoding used in serialization
-     */
-    public static final String DEFAULT_ENCODING = "UTF-8";
-
-    /**
-     * The XML elements and attributes used in serialization
-     */
-    private static final String NODE_ELEMENT = "node";
-    private static final String UUID_ATTRIBUTE = "uuid";
-    private static final String NODETYPE_ATTRIBUTE = "nodeType";
-    private static final String PARENTUUID_ATTRIBUTE = "parentUUID";
-    private static final String DEFINITIONID_ATTRIBUTE = "definitionId";
-    private static final String MODCOUNT_ATTRIBUTE = "modCount";
-
-    private static final String MIXINTYPES_ELEMENT = "mixinTypes";
-    private static final String MIXINTYPE_ELEMENT = "mixinType";
-
-    private static final String PROPERTIES_ELEMENT = "properties";
-    private static final String PROPERTY_ELEMENT = "property";
-    private static final String NAME_ATTRIBUTE = "name";
-    private static final String TYPE_ATTRIBUTE = "type";
-    private static final String MULTIVALUED_ATTRIBUTE = "multiValued";
-
-    private static final String VALUES_ELEMENT = "values";
-    private static final String VALUE_ELEMENT = "value";
-
-    private static final String NODES_ELEMENT = "nodes";
-
-    private static final String NODEREFERENCES_ELEMENT = "references";
-    private static final String TARGETID_ATTRIBUTE = "targetId";
-    private static final String NODEREFERENCE_ELEMENT = "reference";
-    private static final String PROPERTYID_ATTRIBUTE = "propertyId";
-
-    private static final String NODEFILENAME = ".node.xml";
-
-    private static final String NODEREFSFILENAME = ".references.xml";
-
-    private boolean initialized;
-
-    // file system where the item state is stored
-    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>XMLPersistenceManager</code> instance.
-     */
-    public XMLPersistenceManager() {
-        initialized = false;
-    }
-
-    private String buildNodeFolderPath(NodeId id) {
-        StringBuffer sb = new StringBuffer();
-        char[] chars = id.getUUID().toString().toCharArray();
-        int cnt = 0;
-        for (int i = 0; i < chars.length; i++) {
-            if (chars[i] == '-') {
-                continue;
-            }
-            //if (cnt > 0 && cnt % 4 == 0) {
-            if (cnt == 4 || cnt == 8) {
-                sb.append('/');
-            }
-            sb.append(chars[i]);
-            cnt++;
-        }
-        return sb.toString();
-    }
-
-    private String buildPropFilePath(PropertyId id) {
-        String fileName;
-        try {
-            MessageDigest md5 = MessageDigest.getInstance("MD5");
-            md5.update(id.getName().getNamespaceURI().getBytes());
-            md5.update(id.getName().getLocalName().getBytes());
-            byte[] bytes = md5.digest();
-            char[] chars = new char[32];
-            for (int i = 0, j = 0; i < 16; i++) {
-                chars[j++] = HEXDIGITS[(bytes[i] >> 4) & 0x0f];
-                chars[j++] = HEXDIGITS[bytes[i] & 0x0f];
-            }
-            fileName = new String(chars) + ".xml";
-        } catch (NoSuchAlgorithmException nsae) {
-            // should never get here as MD5 should always be available in the JRE
-            String msg = "MD5 not available";
-            log.error(msg, nsae);
-            throw new InternalError(msg + nsae);
-        }
-        return buildNodeFolderPath(id.getParentId()) + "/" + fileName;
-    }
-
-    private String buildNodeFilePath(NodeId id) {
-        return buildNodeFolderPath(id) + "/" + NODEFILENAME;
-    }
-
-    private String buildNodeReferencesFilePath(NodeReferencesId id) {
-        return buildNodeFolderPath(id.getTargetId()) + "/" + NODEREFSFILENAME;
-    }
-
-    private void readState(DOMWalker walker, NodeState state)
-            throws ItemStateException {
-        // first do some paranoid sanity checks
-        if (!walker.getName().equals(NODE_ELEMENT)) {
-            String msg = "invalid serialization format (unexpected element: "
-                    + walker.getName() + ")";
-            log.debug(msg);
-            throw new ItemStateException(msg);
-        }
-        // check uuid
-        if (!state.getNodeId().getUUID().toString().equals(walker.getAttribute(UUID_ATTRIBUTE))) {
-            String msg = "invalid serialized state: uuid mismatch";
-            log.debug(msg);
-            throw new ItemStateException(msg);
-        }
-        // check nodetype
-        String ntName = walker.getAttribute(NODETYPE_ATTRIBUTE);
-        if (!QName.valueOf(ntName).equals(state.getNodeTypeName())) {
-            String msg = "invalid serialized state: nodetype mismatch";
-            log.debug(msg);
-            throw new ItemStateException(msg);
-        }
-
-        // now we're ready to read state
-
-        // primary parent
-        String parentUUID = walker.getAttribute(PARENTUUID_ATTRIBUTE);
-        if (parentUUID.length() > 0) {
-            state.setParentId(NodeId.valueOf(parentUUID));
-        }
-
-        // definition id
-        String definitionId = walker.getAttribute(DEFINITIONID_ATTRIBUTE);
-        state.setDefinitionId(NodeDefId.valueOf(definitionId));
-
-        // modification count
-        String modCount = walker.getAttribute(MODCOUNT_ATTRIBUTE);
-        state.setModCount(Short.parseShort(modCount));
-
-        // mixin types
-        if (walker.enterElement(MIXINTYPES_ELEMENT)) {
-            Set mixins = new HashSet();
-            while (walker.iterateElements(MIXINTYPE_ELEMENT)) {
-                mixins.add(QName.valueOf(walker.getAttribute(NAME_ATTRIBUTE)));
-            }
-            if (mixins.size() > 0) {
-                state.setMixinTypeNames(mixins);
-            }
-            walker.leaveElement();
-        }
-
-        // property entries
-        if (walker.enterElement(PROPERTIES_ELEMENT)) {
-            while (walker.iterateElements(PROPERTY_ELEMENT)) {
-                String propName = walker.getAttribute(NAME_ATTRIBUTE);
-                // @todo deserialize type and values
-                state.addPropertyName(QName.valueOf(propName));
-            }
-            walker.leaveElement();
-        }
-
-        // child node entries
-        if (walker.enterElement(NODES_ELEMENT)) {
-            while (walker.iterateElements(NODE_ELEMENT)) {
-                String childName = walker.getAttribute(NAME_ATTRIBUTE);
-                String childUUID = walker.getAttribute(UUID_ATTRIBUTE);
-                state.addChildNodeEntry(QName.valueOf(childName), NodeId.valueOf(childUUID));
-            }
-            walker.leaveElement();
-        }
-    }
-
-    private void readState(DOMWalker walker, PropertyState state)
-            throws ItemStateException {
-        // first do some paranoid sanity checks
-        if (!walker.getName().equals(PROPERTY_ELEMENT)) {
-            String msg = "invalid serialization format (unexpected element: "
-                    + walker.getName() + ")";
-            log.debug(msg);
-            throw new ItemStateException(msg);
-        }
-        // check name
-        if (!state.getName().equals(QName.valueOf(walker.getAttribute(NAME_ATTRIBUTE)))) {
-            String msg = "invalid serialized state: name mismatch";
-            log.debug(msg);
-            throw new ItemStateException(msg);
-        }
-        // check parentUUID
-        NodeId parentId = NodeId.valueOf(walker.getAttribute(PARENTUUID_ATTRIBUTE));
-        if (!parentId.equals(state.getParentId())) {
-            String msg = "invalid serialized state: parentUUID mismatch";
-            log.debug(msg);
-            throw new ItemStateException(msg);
-        }
-
-        // now we're ready to read state
-
-        // type
-        String typeName = walker.getAttribute(TYPE_ATTRIBUTE);
-        int type;
-        try {
-            type = PropertyType.valueFromName(typeName);
-        } catch (IllegalArgumentException iae) {
-            // should never be getting here
-            throw new ItemStateException("unexpected property-type: " + typeName, iae);
-        }
-        state.setType(type);
-
-        // multiValued
-        String multiValued = walker.getAttribute(MULTIVALUED_ATTRIBUTE);
-        state.setMultiValued(Boolean.getBoolean(multiValued));
-
-        // definition id
-        String definitionId = walker.getAttribute(DEFINITIONID_ATTRIBUTE);
-        state.setDefinitionId(PropDefId.valueOf(definitionId));
-
-        // modification count
-        String modCount = walker.getAttribute(MODCOUNT_ATTRIBUTE);
-        state.setModCount(Short.parseShort(modCount));
-
-        // values
-        ArrayList values = new ArrayList();
-        if (walker.enterElement(VALUES_ELEMENT)) {
-            while (walker.iterateElements(VALUE_ELEMENT)) {
-                // read serialized value
-                String content = walker.getContent();
-                if (PropertyType.STRING == type) {
-                    // STRING value can be empty; ignore length
-                    values.add(InternalValue.valueOf(content, type));
-                } else if (content.length() > 0) {
-                    // non-empty non-STRING value
-                    if (type == PropertyType.BINARY) {
-                        try {
-                            // 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(content);
-                                values.add(InternalValue.create(fsRes));
-                            } else {
-                                InputStream in = blobStore.get(content);
-                                try {
-                                    values.add(InternalValue.create(in, false));
-                                } finally {
-                                    try {
-                                        in.close();
-                                    } catch (IOException e) {
-                                        // ignore
-                                    }
-                                }
-                            }
-                        } catch (Exception e) {
-                            String msg = "error while reading serialized binary value";
-                            log.debug(msg);
-                            throw new ItemStateException(msg, e);
-                        }
-                    } else {
-                        // non-empty non-STRING non-BINARY value
-                        values.add(InternalValue.valueOf(content, type));
-                    }
-                } else {
-                    // empty non-STRING value
-                    log.warn(state.getPropertyId() + ": ignoring empty value of type "
-                            + PropertyType.nameFromValue(type));
-                }
-            }
-            walker.leaveElement();
-        }
-        state.setValues((InternalValue[])
-                values.toArray(new InternalValue[values.size()]));
-    }
-
-    private void readState(DOMWalker walker, NodeReferences refs)
-            throws ItemStateException {
-        // first do some paranoid sanity checks
-        if (!walker.getName().equals(NODEREFERENCES_ELEMENT)) {
-            String msg = "invalid serialization format (unexpected element: " + walker.getName() + ")";
-            log.debug(msg);
-            throw new ItemStateException(msg);
-        }
-        // check targetId
-        if (!refs.getId().equals(NodeReferencesId.valueOf(walker.getAttribute(TARGETID_ATTRIBUTE)))) {
-            String msg = "invalid serialized state: targetId  mismatch";
-            log.debug(msg);
-            throw new ItemStateException(msg);
-        }
-
-        // now we're ready to read the references data
-
-        // property id's
-        refs.clearAllReferences();
-        while (walker.iterateElements(NODEREFERENCE_ELEMENT)) {
-            refs.addReference(PropertyId.valueOf(walker.getAttribute(PROPERTYID_ATTRIBUTE)));
-        }
-    }
-
-    //---------------------------------------------------< PersistenceManager >
-    /**
-     * {@inheritDoc}
-     */
-    public void init(PMContext context) throws Exception {
-        if (initialized) {
-            throw new IllegalStateException("already initialized");
-        }
-
-        itemStateFS = new BasedFileSystem(context.getFileSystem(), "/data");
-
-        /**
-         * 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;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void close() throws Exception {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            // 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
-             * closed by the repository
-             */
-        } finally {
-            initialized = false;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized NodeState load(NodeId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        Exception e = null;
-        String nodeFilePath = buildNodeFilePath(id);
-
-        try {
-            if (!itemStateFS.isFile(nodeFilePath)) {
-                throw new NoSuchItemStateException(id.toString());
-            }
-            InputStream in = itemStateFS.getInputStream(nodeFilePath);
-
-            try {
-                DOMWalker walker = new DOMWalker(in);
-                String ntName = walker.getAttribute(NODETYPE_ATTRIBUTE);
-
-                NodeState state = createNew(id);
-                state.setNodeTypeName(QName.valueOf(ntName));
-                readState(walker, state);
-                return state;
-            } finally {
-                in.close();
-            }
-        } catch (IOException ioe) {
-            e = ioe;
-            // fall through
-        } catch (FileSystemException fse) {
-            e = fse;
-            // fall through
-        }
-        String msg = "failed to read node state: " + id;
-        log.debug(msg);
-        throw new ItemStateException(msg, e);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized PropertyState load(PropertyId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        Exception e = null;
-        String propFilePath = buildPropFilePath(id);
-
-        try {
-            if (!itemStateFS.isFile(propFilePath)) {
-                throw new NoSuchItemStateException(id.toString());
-            }
-            InputStream in = itemStateFS.getInputStream(propFilePath);
-            try {
-                DOMWalker walker = new DOMWalker(in);
-                PropertyState state = createNew(id);
-                readState(walker, state);
-                return state;
-            } finally {
-                in.close();
-            }
-        } catch (IOException ioe) {
-            e = ioe;
-            // fall through
-        } catch (FileSystemException fse) {
-            e = fse;
-            // fall through
-        }
-        String msg = "failed to read property state: " + id.toString();
-        log.debug(msg);
-        throw new ItemStateException(msg, e);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void store(NodeState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        NodeId id = state.getNodeId();
-        String nodeFilePath = buildNodeFilePath(id);
-        FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
-        try {
-            nodeFile.makeParentDirs();
-            OutputStream os = nodeFile.getOutputStream();
-            Writer writer = null;
-            try {
-                String encoding = DEFAULT_ENCODING;
-                try {
-                    writer = new BufferedWriter(new OutputStreamWriter(os, encoding));
-                } catch (UnsupportedEncodingException e) {
-                    // should never get here!
-                    OutputStreamWriter osw = new OutputStreamWriter(os);
-                    encoding = osw.getEncoding();
-                    writer = new BufferedWriter(osw);
-                }
-
-                writer.write("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n");
-                writer.write("<" + NODE_ELEMENT + " "
-                        + UUID_ATTRIBUTE + "=\"" + id.getUUID() + "\" "
-                        + PARENTUUID_ATTRIBUTE + "=\"" + (state.getParentId() == null ? "" : state.getParentId().getUUID().toString()) + "\" "
-                        + DEFINITIONID_ATTRIBUTE + "=\"" + state.getDefinitionId().toString() + "\" "
-                        + MODCOUNT_ATTRIBUTE + "=\"" + state.getModCount() + "\" "
-                        + NODETYPE_ATTRIBUTE + "=\"" + Text.encodeIllegalXMLCharacters(state.getNodeTypeName().toString()) + "\">\n");
-
-                // mixin types
-                writer.write("\t<" + MIXINTYPES_ELEMENT + ">\n");
-                Iterator iter = state.getMixinTypeNames().iterator();
-                while (iter.hasNext()) {
-                    writer.write("\t\t<" + MIXINTYPE_ELEMENT + " "
-                            + NAME_ATTRIBUTE + "=\"" + Text.encodeIllegalXMLCharacters(iter.next().toString()) + "\"/>\n");
-                }
-                writer.write("\t</" + MIXINTYPES_ELEMENT + ">\n");
-
-                // properties
-                writer.write("\t<" + PROPERTIES_ELEMENT + ">\n");
-                iter = state.getPropertyNames().iterator();
-                while (iter.hasNext()) {
-                    QName propName = (QName) iter.next();
-                    writer.write("\t\t<" + PROPERTY_ELEMENT + " "
-                            + NAME_ATTRIBUTE + "=\"" + Text.encodeIllegalXMLCharacters(propName.toString()) + "\">\n");
-                    // @todo serialize type, definition id and values
-                    writer.write("\t\t</" + PROPERTY_ELEMENT + ">\n");
-                }
-                writer.write("\t</" + PROPERTIES_ELEMENT + ">\n");
-
-                // child nodes
-                writer.write("\t<" + NODES_ELEMENT + ">\n");
-                iter = state.getChildNodeEntries().iterator();
-                while (iter.hasNext()) {
-                    NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) iter.next();
-                    writer.write("\t\t<" + NODE_ELEMENT + " "
-                            + NAME_ATTRIBUTE + "=\"" + Text.encodeIllegalXMLCharacters(entry.getName().toString()) + "\" "
-                            + UUID_ATTRIBUTE + "=\"" + entry.getId().getUUID().toString() + "\">\n");
-                    writer.write("\t\t</" + NODE_ELEMENT + ">\n");
-                }
-                writer.write("\t</" + NODES_ELEMENT + ">\n");
-
-                writer.write("</" + NODE_ELEMENT + ">\n");
-            } finally {
-                writer.close();
-            }
-        } catch (Exception e) {
-            String msg = "failed to write node state: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void store(PropertyState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        String propFilePath = buildPropFilePath(state.getPropertyId());
-        FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
-        try {
-            propFile.makeParentDirs();
-            OutputStream os = propFile.getOutputStream();
-            // write property state to xml file
-            Writer writer = null;
-            try {
-                String encoding = DEFAULT_ENCODING;
-                try {
-                    writer = new BufferedWriter(new OutputStreamWriter(os, encoding));
-                } catch (UnsupportedEncodingException e) {
-                    // should never get here!
-                    OutputStreamWriter osw = new OutputStreamWriter(os);
-                    encoding = osw.getEncoding();
-                    writer = new BufferedWriter(osw);
-                }
-
-                String typeName;
-                int type = state.getType();
-                try {
-                    typeName = PropertyType.nameFromValue(type);
-                } catch (IllegalArgumentException iae) {
-                    // should never be getting here
-                    throw new ItemStateException("unexpected property-type ordinal: " + type, iae);
-                }
-
-                writer.write("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n");
-                writer.write("<" + PROPERTY_ELEMENT + " "
-                        + NAME_ATTRIBUTE + "=\"" + Text.encodeIllegalXMLCharacters(state.getName().toString()) + "\" "
-                        + PARENTUUID_ATTRIBUTE + "=\"" + state.getParentId().getUUID() + "\" "
-                        + MULTIVALUED_ATTRIBUTE + "=\"" + Boolean.toString(state.isMultiValued()) + "\" "
-                        + DEFINITIONID_ATTRIBUTE + "=\"" + state.getDefinitionId().toString() + "\" "
-                        + MODCOUNT_ATTRIBUTE + "=\"" + state.getModCount() + "\" "
-                        + TYPE_ATTRIBUTE + "=\"" + typeName + "\">\n");
-                // values
-                writer.write("\t<" + VALUES_ELEMENT + ">\n");
-                InternalValue[] values = state.getValues();
-                if (values != null) {
-                    for (int i = 0; i < values.length; i++) {
-                        writer.write("\t\t<" + VALUE_ELEMENT + ">");
-                        InternalValue val = values[i];
-                        if (val != null) {
-                            if (type == 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(state.getPropertyId(), i);
-                                try {
-                                    blobStore.put(blobId, in, blobVal.getLength());
-                                } finally {
-                                    try {
-                                        in.close();
-                                    } catch (IOException e) {
-                                        // ignore
-                                    }
-                                }
-                                // store id of BLOB as property value
-                                writer.write(blobId);
-                                // replace value instance with value backed by resource
-                                // in BLOB store and discard old value instance (e.g. temp file)
-                                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(blobId);
-                                    values[i] = InternalValue.create(fsRes);
-                                } else {
-                                    in = blobStore.get(blobId);
-                                    try {
-                                        values[i] = InternalValue.create(in, false);
-                                    } finally {
-                                        try {
-                                            in.close();
-                                        } catch (IOException e) {
-                                            // ignore
-                                        }
-                                    }
-                                }
-                                blobVal.discard();
-                            } else {
-                                writer.write(Text.encodeIllegalXMLCharacters(val.toString()));
-                            }
-                        }
-                        writer.write("</" + VALUE_ELEMENT + ">\n");
-                    }
-                }
-                writer.write("\t</" + VALUES_ELEMENT + ">\n");
-                writer.write("</" + PROPERTY_ELEMENT + ">\n");
-            } finally {
-                writer.close();
-            }
-        } catch (Exception e) {
-            String msg = "failed to store property state: " + state.getParentId() + "/" + state.getName();
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void destroy(NodeState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        NodeId id = state.getNodeId();
-        String nodeFilePath = buildNodeFilePath(id);
-        FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
-        try {
-            if (nodeFile.exists()) {
-                // delete resource and prune empty parent folders
-                nodeFile.delete(true);
-            }
-        } catch (FileSystemException fse) {
-            String msg = "failed to delete node state: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void destroy(PropertyState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // delete binary values (stored as files)
-        InternalValue[] values = state.getValues();
-        if (values != null) {
-            for (int i = 0; i < values.length; i++) {
-                InternalValue val = values[i];
-                if (val != null) {
-                    if (val.getType() == PropertyType.BINARY) {
-                        BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
-                        // delete blob file and prune empty parent folders
-                        blobVal.delete(true);
-                    }
-                }
-            }
-        }
-        // delete property file
-        String propFilePath = buildPropFilePath(state.getPropertyId());
-        FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
-        try {
-            if (propFile.exists()) {
-                // delete resource and prune empty parent folders
-                propFile.delete(true);
-            }
-        } catch (FileSystemException fse) {
-            String msg = "failed to delete property state: " + state.getParentId() + "/" + state.getName();
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized NodeReferences load(NodeReferencesId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        Exception e = null;
-        String refsFilePath = buildNodeReferencesFilePath(id);
-        try {
-            if (!itemStateFS.isFile(refsFilePath)) {
-                throw new NoSuchItemStateException(id.toString());
-            }
-
-            InputStream in = itemStateFS.getInputStream(refsFilePath);
-
-            try {
-                DOMWalker walker = new DOMWalker(in);
-                NodeReferences refs = new NodeReferences(id);
-                readState(walker, refs);
-                return refs;
-            } finally {
-                in.close();
-            }
-        } catch (IOException ioe) {
-            e = ioe;
-            // fall through
-        } catch (FileSystemException fse) {
-            e = fse;
-            // fall through
-        }
-        String msg = "failed to load references: " + id;
-        log.debug(msg);
-        throw new ItemStateException(msg, e);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void store(NodeReferences refs) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        NodeReferencesId id = refs.getId();
-        String refsFilePath = buildNodeReferencesFilePath(id);
-        FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
-        try {
-            refsFile.makeParentDirs();
-            OutputStream os = refsFile.getOutputStream();
-            BufferedWriter writer = null;
-            try {
-                String encoding = DEFAULT_ENCODING;
-                try {
-                    writer = new BufferedWriter(new OutputStreamWriter(os, encoding));
-                } catch (UnsupportedEncodingException e) {
-                    // should never get here!
-                    OutputStreamWriter osw = new OutputStreamWriter(os);
-                    encoding = osw.getEncoding();
-                    writer = new BufferedWriter(osw);
-                }
-                writer.write("<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n");
-                writer.write("<" + NODEREFERENCES_ELEMENT + " "
-                        + TARGETID_ATTRIBUTE + "=\"" + refs.getId() + "\">\n");
-                // write references (i.e. the id's of the REFERENCE properties)
-                Iterator iter = refs.getReferences().iterator();
-                while (iter.hasNext()) {
-                    PropertyId propId = (PropertyId) iter.next();
-                    writer.write("\t<" + NODEREFERENCE_ELEMENT + " "
-                            + PROPERTYID_ATTRIBUTE + "=\"" + propId + "\"/>\n");
-                }
-                writer.write("</" + NODEREFERENCES_ELEMENT + ">\n");
-            } finally {
-                writer.close();
-            }
-        } catch (Exception e) {
-            String msg = "failed to store references: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void destroy(NodeReferences refs) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        NodeReferencesId id = refs.getId();
-        String refsFilePath = buildNodeReferencesFilePath(id);
-        FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
-        try {
-            if (refsFile.exists()) {
-                // delete resource and prune empty parent folders
-                refsFile.delete(true);
-            }
-        } catch (FileSystemException fse) {
-            String msg = "failed to delete references: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized boolean exists(NodeId id) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            String nodeFilePath = buildNodeFilePath(id);
-            FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
-            return nodeFile.exists();
-        } catch (FileSystemException fse) {
-            String msg = "failed to check existence of item state: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized boolean exists(PropertyId id) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            String propFilePath = buildPropFilePath(id);
-            FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
-            return propFile.exists();
-        } catch (FileSystemException fse) {
-            String msg = "failed to check existence of item state: " + id;
-            log.error(msg, fse);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized boolean exists(NodeReferencesId id)
-            throws ItemStateException {
-
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            String refsFilePath = buildNodeReferencesFilePath(id);
-            FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
-            return refsFile.exists();
-        } catch (FileSystemException fse) {
-            String msg = "failed to check existence of references: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-}
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/obj/ObjectPersistenceManager.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/obj/ObjectPersistenceManager.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/obj/ObjectPersistenceManager.java	(working copy)
@@ -14,503 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
 package org.apache.jackrabbit.core.state.obj;
 
-import org.apache.jackrabbit.core.NodeId;
-import org.apache.jackrabbit.core.PropertyId;
-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.FileSystemResource;
-import org.apache.jackrabbit.core.fs.local.LocalFileSystem;
-import org.apache.jackrabbit.core.state.AbstractPersistenceManager;
-import org.apache.jackrabbit.core.state.ItemStateException;
-import org.apache.jackrabbit.core.state.NoSuchItemStateException;
-import org.apache.jackrabbit.core.state.NodeReferences;
-import org.apache.jackrabbit.core.state.NodeReferencesId;
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.jcr.PropertyType;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.OutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
 /**
- * <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 binary
- * serialization format (see {@link Serializer}).
+ * Legacy class kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.obj.ObjectPersistenceManager}
+ *             instead.
  */
-public class ObjectPersistenceManager extends AbstractPersistenceManager {
-
-    private static Logger log = LoggerFactory.getLogger(ObjectPersistenceManager.class);
-
-    /**
-     * hexdigits for toString
-     */
-    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
-
-    private static final String NODEFILENAME = ".node";
-
-    private static final String NODEREFSFILENAME = ".references";
-
-    private boolean initialized;
-
-    // file system where the item state is stored
-    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.
-     */
-    public ObjectPersistenceManager() {
-        initialized = false;
-    }
-
-    private static String buildNodeFolderPath(NodeId id) {
-        StringBuffer sb = new StringBuffer();
-        char[] chars = id.getUUID().toString().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++;
-        }
-        return sb.toString();
-    }
-
-    private static String buildPropFilePath(PropertyId id) {
-        String fileName;
-        try {
-            MessageDigest md5 = MessageDigest.getInstance("MD5");
-            md5.update(id.getName().getNamespaceURI().getBytes());
-            md5.update(id.getName().getLocalName().getBytes());
-            byte[] bytes = md5.digest();
-            char[] chars = new char[32];
-            for (int i = 0, j = 0; i < 16; i++) {
-                chars[j++] = HEXDIGITS[(bytes[i] >> 4) & 0x0f];
-                chars[j++] = HEXDIGITS[bytes[i] & 0x0f];
-            }
-            fileName = new String(chars);
-        } catch (NoSuchAlgorithmException nsae) {
-            // should never get here as MD5 should always be available in the JRE
-            String msg = "MD5 not available: ";
-            log.error(msg, nsae);
-            throw new InternalError(msg + nsae);
-        }
-        return buildNodeFolderPath(id.getParentId()) + FileSystem.SEPARATOR + fileName;
-    }
-
-    private static String buildNodeFilePath(NodeId id) {
-        return buildNodeFolderPath(id) + FileSystem.SEPARATOR + NODEFILENAME;
-    }
-
-    private static String buildNodeReferencesFilePath(NodeReferencesId id) {
-        return buildNodeFolderPath(id.getTargetId()) + FileSystem.SEPARATOR + NODEREFSFILENAME;
-    }
-
-    //---------------------------------------------------< PersistenceManager >
-    /**
-     * {@inheritDoc}
-     */
-    public void init(PMContext context) throws Exception {
-        if (initialized) {
-            throw new IllegalStateException("already initialized");
-        }
-
-        FileSystem wspFS = context.getFileSystem();
-        itemStateFS = new BasedFileSystem(wspFS, "/data");
-
-        /**
-         * 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;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized void close() throws Exception {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            // 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
-             * closed by the repository
-             */
-        } finally {
-            initialized = false;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized NodeState load(NodeId id)
-            throws NoSuchItemStateException, ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        String nodeFilePath = buildNodeFilePath(id);
-
-        try {
-            if (!itemStateFS.isFile(nodeFilePath)) {
-                throw new NoSuchItemStateException(nodeFilePath);
-            }
-        } catch (FileSystemException fse) {
-            String msg = "failed to read node state: " + nodeFilePath;
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-
-        try {
-            BufferedInputStream in =
-                    new BufferedInputStream(itemStateFS.getInputStream(nodeFilePath));
-            try {
-                NodeState state = createNew(id);
-                Serializer.deserialize(state, in);
-                return state;
-            } catch (Exception e) {
-                String msg = "failed to read node state: " + id.getUUID();
-                log.debug(msg);
-                throw new ItemStateException(msg, e);
-            } finally {
-                in.close();
-            }
-        } catch (Exception e) {
-            String msg = "failed to read node state: " + nodeFilePath;
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized PropertyState load(PropertyId id)
-            throws NoSuchItemStateException, ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        String propFilePath = buildPropFilePath(id);
-
-        try {
-            if (!itemStateFS.isFile(propFilePath)) {
-                throw new NoSuchItemStateException(propFilePath);
-            }
-        } catch (FileSystemException fse) {
-            String msg = "failed to read property state: " + propFilePath;
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-
-        try {
-            BufferedInputStream in =
-                    new BufferedInputStream(itemStateFS.getInputStream(propFilePath));
-            try {
-                PropertyState state = createNew(id);
-                Serializer.deserialize(state, in, blobStore);
-                return state;
-            } finally {
-                in.close();
-            }
-        } catch (Exception e) {
-            String msg = "failed to read property state: " + propFilePath;
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized NodeReferences load(NodeReferencesId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        String refsFilePath = buildNodeReferencesFilePath(id);
-
-        try {
-            if (!itemStateFS.isFile(refsFilePath)) {
-                throw new NoSuchItemStateException(id.toString());
-            }
-        } catch (FileSystemException fse) {
-            String msg = "failed to load references: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-
-        try {
-            BufferedInputStream in =
-                    new BufferedInputStream(itemStateFS.getInputStream(refsFilePath));
-            try {
-                NodeReferences refs = new NodeReferences(id);
-                Serializer.deserialize(refs, in);
-                return refs;
-            } finally {
-                in.close();
-            }
-        } catch (Exception e) {
-            String msg = "failed to load references: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void store(NodeState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        String nodeFilePath = buildNodeFilePath(state.getNodeId());
-        FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
-        try {
-            nodeFile.makeParentDirs();
-            BufferedOutputStream out = new BufferedOutputStream(nodeFile.getOutputStream());
-            try {
-                // serialize node state
-                Serializer.serialize(state, out);
-            } finally {
-                out.close();
-            }
-        } catch (Exception e) {
-            String msg = "failed to write node state: " + state.getNodeId();
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void store(PropertyState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        String propFilePath = buildPropFilePath(state.getPropertyId());
-        FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
-        try {
-            propFile.makeParentDirs();
-            BufferedOutputStream out = new BufferedOutputStream(propFile.getOutputStream());
-            try {
-                // serialize property state
-                Serializer.serialize(state, out, blobStore);
-            } finally {
-                out.close();
-            }
-        } catch (Exception e) {
-            String msg = "failed to store property state: " + state.getParentId() + "/" + state.getName();
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void store(NodeReferences refs) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        String refsFilePath = buildNodeReferencesFilePath(refs.getId());
-        FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
-        try {
-            refsFile.makeParentDirs();
-            OutputStream out = new BufferedOutputStream(refsFile.getOutputStream());
-            try {
-                Serializer.serialize(refs, out);
-            } finally {
-                out.close();
-            }
-        } catch (Exception e) {
-            String msg = "failed to store references: " + refs.getId();
-            log.debug(msg);
-            throw new ItemStateException(msg, e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void destroy(NodeState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        String nodeFilePath = buildNodeFilePath(state.getNodeId());
-        FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
-        try {
-            if (nodeFile.exists()) {
-                // delete resource and prune empty parent folders
-                nodeFile.delete(true);
-            }
-        } catch (FileSystemException fse) {
-            String msg = "failed to delete node state: " + state.getNodeId();
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void destroy(PropertyState state) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        // delete binary values (stored as files)
-        InternalValue[] values = state.getValues();
-        if (values != null) {
-            for (int i = 0; i < values.length; i++) {
-                InternalValue val = values[i];
-                if (val != null) {
-                    if (val.getType() == PropertyType.BINARY) {
-                        BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
-                        // delete blob file and prune empty parent folders
-                        blobVal.delete(true);
-                    }
-                }
-            }
-        }
-        // delete property file
-        String propFilePath = buildPropFilePath(state.getPropertyId());
-        FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
-        try {
-            if (propFile.exists()) {
-                // delete resource and prune empty parent folders
-                propFile.delete(true);
-            }
-        } catch (FileSystemException fse) {
-            String msg = "failed to delete property state: " + state.getParentId() + "/" + state.getName();
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void destroy(NodeReferences refs) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        String refsFilePath = buildNodeReferencesFilePath(refs.getId());
-        FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
-        try {
-            if (refsFile.exists()) {
-                // delete resource and prune empty parent folders
-                refsFile.delete(true);
-            }
-        } catch (FileSystemException fse) {
-            String msg = "failed to delete node references: " + refs.getId();
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized boolean exists(PropertyId id) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            String propFilePath = buildPropFilePath(id);
-            FileSystemResource propFile = new FileSystemResource(itemStateFS, propFilePath);
-            return propFile.exists();
-        } catch (FileSystemException fse) {
-            String msg = "failed to check existence of item state: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized boolean exists(NodeId id) throws ItemStateException {
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            String nodeFilePath = buildNodeFilePath(id);
-            FileSystemResource nodeFile = new FileSystemResource(itemStateFS, nodeFilePath);
-            return nodeFile.exists();
-        } catch (FileSystemException fse) {
-            String msg = "failed to check existence of item state: " + id;
-            log.error(msg, fse);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public synchronized boolean exists(NodeReferencesId id)
-            throws ItemStateException {
-
-        if (!initialized) {
-            throw new IllegalStateException("not initialized");
-        }
-
-        try {
-            String refsFilePath = buildNodeReferencesFilePath(id);
-            FileSystemResource refsFile = new FileSystemResource(itemStateFS, refsFilePath);
-            return refsFile.exists();
-        } catch (FileSystemException fse) {
-            String msg = "failed to check existence of references: " + id;
-            log.debug(msg);
-            throw new ItemStateException(msg, fse);
-        }
-    }
-}
+public class ObjectPersistenceManager
+        extends org.apache.jackrabbit.core.persistence.obj.ObjectPersistenceManager {
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/util/FileSystemBLOBStore.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/util/FileSystemBLOBStore.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/util/FileSystemBLOBStore.java	(working copy)
@@ -14,116 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
 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>.
+ * Legacy class kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.util.FileSystemBLOBStore}
+ *             instead.
  */
-public class FileSystemBLOBStore implements ResourceBasedBLOBStore {
+public class FileSystemBLOBStore extends org.apache.jackrabbit.core.persistence.util.FileSystemBLOBStore {
 
-    /**
-     * 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;
+        super(fs);
     }
-
-    //------------------------------------------------------------< BLOBStore >
-    /**
-     * {@inheritDoc}
-     */
-    public String createId(PropertyId id, int index) {
-        // the blobId is an absolute file system path
-        StringBuffer sb = new StringBuffer();
-        sb.append(FileSystem.SEPARATOR_CHAR);
-        char[] chars = id.getParentId().getUUID().toString().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 an absolute 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 {
-            if (out != null) {
-                out.close();
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public boolean remove(String blobId) throws Exception {
-        // the blobId is an absolute file system path
-        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 {
-        // the blobId is an absolute file system path
-        return new FileSystemResource(fs, blobId);
-    }
-}
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/util/ResourceBasedBLOBStore.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/util/ResourceBasedBLOBStore.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/util/ResourceBasedBLOBStore.java	(working copy)
@@ -14,23 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
 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)}
+ * Legacy interface kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.util.ResourceBasedBLOBStore}
+ *             instead.
  */
-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;
-}
+public interface ResourceBasedBLOBStore
+        extends org.apache.jackrabbit.core.persistence.util.ResourceBasedBLOBStore {
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/util/Serializer.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/util/Serializer.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/util/Serializer.java	(working copy)
@@ -14,351 +14,87 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
 package org.apache.jackrabbit.core.state.util;
 
-import org.apache.jackrabbit.core.PropertyId;
-import org.apache.jackrabbit.core.NodeId;
-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.persistence.util.BLOBStore;
 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;
-import java.util.Arrays;
 
 /**
- * <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.
+ * Legacy class kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.util.Serializer}
+ *             instead.
  */
 public final class Serializer {
 
-    private static final byte[] NULL_UUID_PLACEHOLDER_BYTES = new byte[] {
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-    };
-
     /**
-     * encoding used for serializing String values
+     * @deprecated use {@link org.apache.jackrabbit.core.persistence.util.Serializer#serialize(NodeState, OutputStream)}
      */
-    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.getParentId() == null) {
-            out.write(NULL_UUID_PLACEHOLDER_BYTES);
-        } else {
-            out.write(state.getParentId().getUUID().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
-        out.writeShort(state.getModCount());
-        // 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(entry.getId().getUUID().getRawBytes());    // uuid
-        }
+        serialize(state, stream);
     }
 
     /**
-     * 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)
+     * @deprecated use {@link org.apache.jackrabbit.core.persistence.util.Serializer#deserialize(NodeState, InputStream)}
      */
     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.readFully(uuidBytes);
-        if (!Arrays.equals(uuidBytes, NULL_UUID_PLACEHOLDER_BYTES)) {
-            state.setParentId(new NodeId(new UUID(uuidBytes)));
-        }
-        // 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
-        short modCount = in.readShort();
-        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.readFully(uuidBytes);
-            state.addChildNodeEntry(name, new NodeId(new UUID(uuidBytes)));
-        }
+        deserialize(state, stream);
     }
 
     /**
-     * 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)
+     * @deprecated use {@link org.apache.jackrabbit.core.persistence.util.Serializer#serialize(PropertyState, OutputStream, 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
-        out.writeShort(state.getModCount());
-        // 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(state.getPropertyId(), 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)
-                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(blobId);
-                    values[i] = InternalValue.create(fsRes);
-                } else {
-                    in = blobStore.get(blobId);
-                    try {
-                        values[i] = InternalValue.create(in, false);
-                    } finally {
-                        try {
-                            in.close();
-                        } catch (IOException e) {
-                            // ignore
-                        }
-                    }
-                }
-                blobVal.discard();
-            } else {
-                /**
-                 * because writeUTF(String) has a size limit of 65k,
-                 * Strings are serialized as <length><byte[]>
-                 */
-                //out.writeUTF(val.toString());   // value
-                byte[] bytes = val.toString().getBytes(ENCODING);
-                out.writeInt(bytes.length); // lenght of byte[]
-                out.write(bytes);   // byte[]
-            }
-        }
+        serialize(state, stream, blobStore);
     }
 
     /**
-     * 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)
+     * @deprecated use {@link org.apache.jackrabbit.core.persistence.util.Serializer#deserialize(PropertyState, InputStream, 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
-        short modCount = in.readShort();
-        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 {
-                    InputStream is = blobStore.get(s);
-                    try {
-                        val = InternalValue.create(is, false);
-                    } finally {
-                        try {
-                            is.close();
-                        } catch (IOException e) {
-                            // ignore
-                        }
-                    }
-                }
-            } 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.readFully(bytes); // byte[]
-                s = new String(bytes, ENCODING);
-                val = InternalValue.valueOf(s, type);
-            }
-            values[i] = val;
-        }
-        state.setValues(values);
+        deserialize(state, stream, blobStore);
     }
 
     /**
-     * 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)
+     * @deprecated use {@link org.apache.jackrabbit.core.persistence.util.Serializer#serialize(NodeReferences, OutputStream)}
      */
     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
-        }
+        serialize(refs, stream);
     }
 
     /**
-     * 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)
+     * @deprecated user {@link org.apache.jackrabbit.core.persistence.util.Serializer#deserialize(NodeReferences, InputStream)}
      */
     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
-        }
+        deserialize(refs, stream);
     }
-}
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/state/util/BLOBStore.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/state/util/BLOBStore.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/state/util/BLOBStore.java	(working copy)
@@ -14,59 +14,29 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+/*
+ * $URL$
+ * $Id$
+ *
+ * Copyright 1997-2006 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
 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
+ * Legacy interface kept for backward compatibility reasons.
+ * @deprecated use {@link org.apache.jackrabbit.core.persistence.util.BLOBStore}
+ *             instead.
  */
-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);
+public interface BLOBStore
+        extends org.apache.jackrabbit.core.persistence.util.BLOBStore {
 
-    /**
-     * 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;
-}
+}
\ No newline at end of file
Index: src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java	(working copy)
@@ -42,8 +42,8 @@
 import org.apache.jackrabbit.core.observation.ObservationDispatcher;
 import org.apache.jackrabbit.core.security.AuthContext;
 import org.apache.jackrabbit.core.state.ItemStateException;
-import org.apache.jackrabbit.core.state.PMContext;
-import org.apache.jackrabbit.core.state.PersistenceManager;
+import org.apache.jackrabbit.core.persistence.PMContext;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
 import org.apache.jackrabbit.core.state.SharedItemStateManager;
 import org.apache.jackrabbit.core.version.VersionManager;
 import org.apache.jackrabbit.core.version.VersionManagerImpl;
Index: src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java
===================================================================
--- src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/version/VersionManagerImpl.java	(working copy)
@@ -21,6 +21,7 @@
 import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.PropertyId;
 import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
 import org.apache.jackrabbit.core.fs.FileSystem;
 import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
 import org.apache.jackrabbit.core.observation.DelegatingObservationDispatcher;
@@ -32,7 +33,6 @@
 import org.apache.jackrabbit.core.state.NodeReferences;
 import org.apache.jackrabbit.core.state.NodeReferencesId;
 import org.apache.jackrabbit.core.state.NodeState;
-import org.apache.jackrabbit.core.state.PersistenceManager;
 import org.apache.jackrabbit.core.state.PropertyState;
 import org.apache.jackrabbit.core.state.SharedItemStateManager;
 import org.apache.jackrabbit.core.state.ItemStateListener;
Index: src/main/java/org/apache/jackrabbit/core/test-repository.xml
===================================================================
--- src/main/java/org/apache/jackrabbit/core/test-repository.xml	(revision 465518)
+++ src/main/java/org/apache/jackrabbit/core/test-repository.xml	(working copy)
@@ -67,7 +67,7 @@
             persistence manager of the workspace:
             class: FQN of class implementing the PersistenceManager interface
         -->
-        <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
           <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
           <param name="schemaObjectPrefix" value="${wsp.name}_"/>
         </PersistenceManager>
@@ -98,7 +98,7 @@
             a 'normal' persistence manager, but this could change in future
             implementations.
         -->
-        <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
           <param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>
           <param name="schemaObjectPrefix" value="version_"/>
         </PersistenceManager>
Index: src/main/config/repository.xml
===================================================================
--- src/main/config/repository.xml	(revision 465518)
+++ src/main/config/repository.xml	(working copy)
@@ -203,7 +203,7 @@
             persistence manager of the workspace:
             class: FQN of class implementing the PersistenceManager interface
         -->
-        <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
             <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
             <param name="schemaObjectPrefix" value="${wsp.name}_"/>
         </PersistenceManager>
@@ -282,7 +282,7 @@
             a 'normal' persistence manager, but this could change in future
             implementations.
         -->
-        <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
             <param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>
             <param name="schemaObjectPrefix" value="version_"/>
         </PersistenceManager>
Index: src/site/xdoc/doc/config.xml
===================================================================
--- src/site/xdoc/doc/config.xml	(revision 465518)
+++ src/site/xdoc/doc/config.xml	(working copy)
@@ -160,7 +160,7 @@
     <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
         <param name="path" value="${wsp.home}"/>
     </FileSystem>
-    <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+    <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
         <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
         <param name="schemaObjectPrefix" value="${wsp.name}_"/>
     </PersistenceManager>
@@ -200,7 +200,7 @@
     <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
         <param name="path" value="${rep.home}/version"/>
     </FileSystem>
-    <PersistenceManager class="org.apache.jackrabbit.core.state.db.DerbyPersistenceManager">
+    <PersistenceManager class="org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager">
         <param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>
         <param name="schemaObjectPrefix" value="version_"/>
     </PersistenceManager>
@@ -249,7 +249,7 @@
         The <code>class</code> attribute of the <code>PersistenceManager</code>
         element contains the fully qualified class name of the persistence
         manager implementation class. The class must implement the
-        <code>org.apache.jackrabbit.core.state.PersistenceManager</code>
+        <code>org.apache.jackrabbit.core.persistence.PersistenceManager</code>
         interface and have an empty default constructor. String properties
         can be assigned using the <code>param</code> elements according to
         the JavaBean conventions.
@@ -260,7 +260,9 @@
       </p>
       <subsection name="InMemPersistenceManager" id="InMemPersistenceManager">
         <p>
-          The <code>org.apache.jackrabbit.core.state.mem.InMemPersistenceManager</code>
+          The <code>
+            org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager
+        </code>
           class implements a simple persistence manager that keeps all content
           in memory. A custom binary serializer is optionally used to load
           and store the persistent state during initialization and shutdown
@@ -298,7 +300,9 @@
       </subsection>
       <subsection name="ObjectPersistenceManager" id="ObjectPersistenceManager">
         <p>
-          The <code>org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager</code>
+          The <code>
+            org.apache.jackrabbit.core.persistence.obj.ObjectPersistenceManager
+        </code>
           class implements a persistence manager that uses a custom binary
           serialization format to store the managed content directly in
           the underlying virtual file system. The virtual file system is
@@ -315,7 +319,9 @@
       </subsection>
       <subsection name="XMLPersistenceManager" id="XMLPersistenceManager">
         <p>
-          The <code>org.apache.jackrabbit.core.state.xml.XMLPersistenceManager</code>
+          The <code>
+            org.apache.jackrabbit.core.persistence.xml.XMLPersistenceManager
+        </code>
           class implements a persistence manager that uses XML files stored
           in the underlying virtual file system. The difference between this
           persistence manager and the ObjectPersistenceManager is the storage
@@ -334,7 +340,9 @@
       </subsection>
       <subsection name="SimpleDbPersistenceManager" id="SimpleDbPersistenceManager">
         <p>
-          The <code>org.apache.jackrabbit.core.state.db.SimpleDbPersistenceManager</code>
+          The <code>
+            org.apache.jackrabbit.core.persistence.db.SimpleDbPersistenceManager
+        </code>
           class implements a generic Java Database Connectivity (JDBC) -based
           persistence manager. Content is stored in a set of simple
           non-normalized tables that are created automatically unless they
@@ -383,7 +391,9 @@
       </subsection>
       <subsection name="DerbyPersistenceManager" id="DerbyPersistenceManager">
         <p>
-          The <code>org.apache.jackrabbit.core.state.db.DerbyPersistenceManager</code>
+          The <code>
+            org.apache.jackrabbit.core.persistence.db.DerbyPersistenceManager
+        </code>
           is a subclass of the <code>SimpleDbPersistenceManager</code>. It
           implements a persistence manager that uses an embedded
           <a href="http://db.apache.org/derby/">Apache Derby</a> database
