jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tri...@apache.org
Subject svn commit: r467925 [3/6] - in /jackrabbit/trunk/jackrabbit: applications/test/ applications/test/workspaces/default/ applications/test/workspaces/test/ src/main/config/ src/main/java/org/apache/jackrabbit/core/ src/main/java/org/apache/jackrabbit/core...
Date Thu, 26 Oct 2006 09:11:20 GMT
Added: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/obj/ObjectPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/obj/ObjectPersistenceManager.java?view=auto&rev=467925
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/obj/ObjectPersistenceManager.java (added)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/obj/ObjectPersistenceManager.java Thu Oct 26 02:11:18 2006
@@ -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);
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/obj/ObjectPersistenceManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/obj/ObjectPersistenceManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url rev

Added: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/BLOBStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/BLOBStore.java?view=auto&rev=467925
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/BLOBStore.java (added)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/BLOBStore.java Thu Oct 26 02:11:18 2006
@@ -0,0 +1,72 @@
+/*
+ * 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 java.io.InputStream;
+
+/**
+ * <code>BLOBStore</code> represents an abstract store for binary property
+ * values (BLOBs).
+ *
+ * @see ResourceBasedBLOBStore
+ */
+public interface BLOBStore {
+    /**
+     * Creates a unique identifier for the BLOB data associated with the given
+     * property id and value subscript.
+     *
+     * @param id    id of the property associated with the BLOB data
+     * @param index subscript of the value holding the BLOB data
+     * @return a string identifying the BLOB data
+     */
+    String createId(PropertyId id, int index);
+
+    /**
+     * Stores the BLOB data and returns a unique identifier.
+     *
+     * @param blobId identifier of the BLOB data as returned by
+     *               {@link #createId(PropertyId, int)}
+     * @param in     stream containing the BLOB data
+     * @param size   size of the BLOB data
+     * @throws Exception if an error occured
+     */
+    void put(String blobId, InputStream in, long size) throws Exception;
+
+    /**
+     * Retrieves the BLOB data with the specified id as a binary stream.
+     *
+     * @param blobId identifier of the BLOB data as returned by
+     *               {@link #createId(PropertyId, int)}
+     * @return an input stream that delivers the BLOB data
+     * @throws Exception if an error occured
+     */
+    InputStream get(String blobId) throws Exception;
+
+    /**
+     * Removes the BLOB data with the specified id.
+     *
+     * @param blobId identifier of the BLOB data as returned by
+     *               {@link #createId(PropertyId, int)}
+     * @return <code>true</code> if BLOB data with the given id exists and has
+     *         been successfully removed, <code>false</code> if there's no BLOB
+     *         data with the given id.
+     * @throws Exception if an error occured
+     */
+    boolean remove(String blobId) throws Exception;
+}

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

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

Added: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/FileSystemBLOBStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/FileSystemBLOBStore.java?view=auto&rev=467925
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/FileSystemBLOBStore.java (added)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/FileSystemBLOBStore.java Thu Oct 26 02:11:18 2006
@@ -0,0 +1,129 @@
+/*
+ * 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.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);
+    }
+}

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

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

Added: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/ResourceBasedBLOBStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/ResourceBasedBLOBStore.java?view=auto&rev=467925
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/ResourceBasedBLOBStore.java (added)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/ResourceBasedBLOBStore.java Thu Oct 26 02:11:18 2006
@@ -0,0 +1,36 @@
+/*
+ * 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.fs.FileSystemResource;
+
+/**
+ * <code>ResourceBasedBLOBStore</code> extends the <code>BLOBStore</code>
+ * interface with the method {@link #getResource(String)}
+ */
+public interface ResourceBasedBLOBStore extends BLOBStore {
+    /**
+     * Retrieves the BLOB data with the specified id as a permanent resource.
+     *
+     * @param blobId identifier of the BLOB data as returned by
+     *               {@link #createId(PropertyId, int)}
+     * @return a resource representing the BLOB data
+     * @throws Exception if an error occured
+     */
+    FileSystemResource getResource(String blobId) throws Exception;
+}

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

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

Added: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/Serializer.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/Serializer.java?view=auto&rev=467925
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/Serializer.java (added)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/util/Serializer.java Thu Oct 26 02:11:18 2006
@@ -0,0 +1,364 @@
+/*
+ * 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.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
+import org.apache.jackrabbit.core.nodetype.NodeDefId;
+import org.apache.jackrabbit.core.nodetype.PropDefId;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.value.BLOBFileValue;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.uuid.UUID;
+
+import javax.jcr.PropertyType;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * <code>Serializer</code> is a utility class that provides static methods
+ * for serializing & deserializing <code>ItemState</code> and
+ * <code>NodeReferences</code> objects using a simple binary serialization
+ * format.
+ */
+public final class Serializer {
+
+    private static final 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
+        }
+    }
+}

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

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

Added: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/xml/XMLPersistenceManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/xml/XMLPersistenceManager.java?view=auto&rev=467925
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/xml/XMLPersistenceManager.java (added)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/xml/XMLPersistenceManager.java Thu Oct 26 02:11:18 2006
@@ -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);
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/xml/XMLPersistenceManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/persistence/xml/XMLPersistenceManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url rev

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/repository.xml
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/repository.xml?view=diff&rev=467925&r1=467924&r2=467925
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/repository.xml (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/repository.xml Thu Oct 26 02:11:18 2006
@@ -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>



Mime
View raw message