You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2004/11/05 15:00:36 UTC

svn commit: rev 56661 - in incubator/jackrabbit/trunk/src: conf java/org/apache/jackrabbit/core java/org/apache/jackrabbit/core/state/mem

Author: stefan
Date: Fri Nov  5 06:00:35 2004
New Revision: 56661

Added:
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/mem/
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java   (contents, props changed)
Modified:
   incubator/jackrabbit/trunk/src/conf/repository.xml
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
Log:
added simple in-memory persistence manager for testing purposes

Modified: incubator/jackrabbit/trunk/src/conf/repository.xml
==============================================================================
--- incubator/jackrabbit/trunk/src/conf/repository.xml	(original)
+++ incubator/jackrabbit/trunk/src/conf/repository.xml	Fri Nov  5 06:00:35 2004
@@ -107,7 +107,16 @@
             persistence manager of the workspace:
             class: FQN of class implementing PersistenceManager interface
         -->
-        <!-- <PersistenceManager class="org.apache.jackrabbit.core.state.xml.XMLPersistenceManager"> -->
+        <!--
+        <PersistenceManager class="org.apache.jackrabbit.core.state.xml.XMLPersistenceManager">
+        -->
+        <!--
+        <PersistenceManager class="org.apache.jackrabbit.core.state.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">
             <!-- <param name="someParam" value="someValue"/> -->
         </PersistenceManager>

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java	Fri Nov  5 06:00:35 2004
@@ -556,16 +556,16 @@
         for (Iterator it = wspConfigs.values().iterator(); it.hasNext();) {
             WorkspaceConfig wspConfig = (WorkspaceConfig) it.next();
             try {
-                // close workspace file system
-                wspConfig.getFileSystem().close();
-            } catch (FileSystemException e) {
-                log.error("Error while closing filesystem of workspace " + wspConfig.getName(), e);
-            }
-            try {
                 // close persistence manager
                 wspConfig.getPersistenceManager().close();
             } catch (Exception e) {
                 log.error("Error while closing persistence manager of workspace " + wspConfig.getName(), e);
+            }
+            try {
+                // close workspace file system
+                wspConfig.getFileSystem().close();
+            } catch (FileSystemException e) {
+                log.error("Error while closing filesystem of workspace " + wspConfig.getName(), e);
             }
         }
 

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/mem/InMemPersistenceManager.java	Fri Nov  5 06:00:35 2004
@@ -0,0 +1,601 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state.mem;
+
+import org.apache.jackrabbit.core.*;
+import org.apache.jackrabbit.core.config.WorkspaceConfig;
+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.*;
+import org.apache.jackrabbit.core.state.obj.BLOBStore;
+import org.apache.jackrabbit.core.state.obj.ObjectPersistenceManager;
+import org.apache.log4j.Logger;
+
+import javax.jcr.PropertyType;
+import java.io.*;
+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
+ * serialization format.
+ * <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 implements BLOBStore, PersistenceManager {
+
+    private static Logger log = Logger.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;
+
+    /**
+     * 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: node
+                    String s = in.readUTF();    // id
+                    id = NodeId.valueOf(s);
+                }
+                int length = in.readInt();  // data length
+                byte[] data = new byte[length];
+                in.read(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
+                NodeId id = NodeId.valueOf(s);
+                int length = in.readInt();  // data length
+                byte[] data = new byte[length];
+                in.read(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()) {
+                NodeId id = (NodeId) 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();
+        }
+    }
+
+    //------------------------------------------------------------< BLOBStore >
+    /**
+     * @see BLOBStore#get
+     */
+    public FileSystemResource get(String blobId) throws Exception {
+        return new FileSystemResource(blobFS, blobId);
+    }
+
+    /**
+     * @see BLOBStore#put
+     */
+    public String put(PropertyId id, int index, InputStream in, long size) throws Exception {
+        String path = buildBlobFilePath(id.getParentUUID(), id.getName(), index);
+        OutputStream out = null;
+        FileSystemResource internalBlobFile = new FileSystemResource(blobFS, path);
+        internalBlobFile.makeParentDirs();
+        try {
+            out = new BufferedOutputStream(internalBlobFile.getOutputStream());
+            byte[] buffer = new byte[8192];
+            int read = 0;
+            while ((read = in.read(buffer)) > 0) {
+                out.write(buffer, 0, read);
+            }
+        } finally {
+            out.close();
+        }
+        return path;
+    }
+
+    /**
+     * @see BLOBStore#remove
+     */
+    public boolean remove(String blobId) throws Exception {
+        FileSystemResource res = new FileSystemResource(blobFS, blobId);
+        if (!res.exists()) {
+            return false;
+        }
+        // delete resource and prune empty parent folders
+        res.delete(true);
+        return true;
+    }
+
+    //---------------------------------------------------< PersistenceManager >
+    /**
+     * @see PersistenceManager#init
+     */
+    public void init(WorkspaceConfig wspConfig) throws Exception {
+        if (initialized) {
+            throw new IllegalStateException("already initialized");
+        }
+
+        stateStore = new HashMap(initialCapacity, loadFactor);
+        refsStore = new HashMap(initialCapacity, loadFactor);
+
+        wspFS = wspConfig.getFileSystem();
+        
+        /**
+         * store blob's in local file system in a sub directory
+         * of the workspace home directory
+         */
+        LocalFileSystem blobFS = new LocalFileSystem();
+        blobFS.setPath(wspConfig.getHomeDir() + "/blobs");
+        blobFS.init();
+        this.blobFS = blobFS;
+
+        if (persistent) {
+            // deserialize contents of state and refs stores
+            loadContents();
+        }
+
+        initialized = true;
+    }
+
+    /**
+     * @see PersistenceManager#close
+     */
+    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 {
+                // clean 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 store
+            blobFS.close();
+            blobFS = null;
+
+            stateStore.clear();
+            stateStore = null;
+            refsStore.clear();
+            refsStore = null;
+        } finally {
+            initialized = false;
+        }
+    }
+
+    /**
+     * @see PersistenceManager#load(PersistentNodeState)
+     */
+    public synchronized void load(PersistentNodeState state)
+            throws NoSuchItemStateException, ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        byte[] data = (byte[]) stateStore.get(state.getId());
+        if (data == null) {
+            throw new NoSuchItemStateException(state.getId().toString());
+        }
+
+        ByteArrayInputStream in = new ByteArrayInputStream(data);
+        try {
+            ObjectPersistenceManager.deserialize(state, in);
+            // there's no need to close a ByteArrayInputStream
+            //in.close();
+        } catch (Exception e) {
+            String msg = "failed to read node state: " + state.getId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * @see PersistenceManager#load(PersistentPropertyState)
+     */
+    public synchronized void load(PersistentPropertyState state)
+            throws NoSuchItemStateException, ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        byte[] data = (byte[]) stateStore.get(state.getId());
+        if (data == null) {
+            throw new NoSuchItemStateException(state.getId().toString());
+        }
+
+        ByteArrayInputStream in = new ByteArrayInputStream(data);
+        try {
+            ObjectPersistenceManager.deserialize(state, in, this);
+            // there's no need to close a ByteArrayInputStream
+            //in.close();
+        } catch (Exception e) {
+            String msg = "failed to read property state: " + state.getId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * @see PersistenceManager#store
+     */
+    public synchronized void store(PersistentNodeState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize node state
+            ObjectPersistenceManager.serialize(state, out);
+
+            // store in serialized format in map for better memory efficiency
+            stateStore.put(state.getId(), out.toByteArray());
+            // 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);
+        }
+    }
+
+    /**
+     * @see PersistenceManager#store
+     */
+    public synchronized void store(PersistentPropertyState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize property state
+            ObjectPersistenceManager.serialize(state, out, this);
+
+            // store in serialized format in map for better memory efficiency
+            stateStore.put(state.getId(), out.toByteArray());
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to store property state: " + state.getId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * @see PersistenceManager#destroy
+     */
+    public synchronized void destroy(PersistentNodeState state) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // remove node state
+        stateStore.remove(state.getId());
+    }
+
+    /**
+     * @see PersistenceManager#destroy
+     */
+    public synchronized void destroy(PersistentPropertyState 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.getId());
+    }
+
+    /**
+     * @see PersistenceManager#load(NodeReferences)
+     */
+    public synchronized void load(NodeReferences refs)
+            throws NoSuchItemStateException, ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        byte[] data = (byte[]) refsStore.get(refs.getTargetId());
+        if (data == null) {
+            throw new NoSuchItemStateException(refs.getTargetId().toString());
+        }
+
+        ByteArrayInputStream in = new ByteArrayInputStream(data);
+        try {
+            ObjectPersistenceManager.deserialize(refs, in);
+            // there's no need to close a ByteArrayInputStream
+            //in.close();
+        } catch (Exception e) {
+            String msg = "failed to load references: " + refs.getTargetId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * @see PersistenceManager#store(NodeReferences)
+     */
+    public synchronized void store(NodeReferences refs) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream(INITIAL_BUFFER_SIZE);
+            // serialize references
+            ObjectPersistenceManager.serialize(refs, out);
+
+            // store in serialized format in map for better memory efficiency
+            stateStore.put(refs.getTargetId(), out.toByteArray());
+            // there's no need to close a ByteArrayOutputStream
+            //out.close();
+        } catch (Exception e) {
+            String msg = "failed to store references: " + refs.getTargetId();
+            log.error(msg, e);
+            throw new ItemStateException(msg, e);
+        }
+    }
+
+    /**
+     * @see PersistenceManager#destroy(NodeReferences)
+     */
+    public synchronized void destroy(NodeReferences refs) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        // remove node references
+        stateStore.remove(refs.getTargetId());
+    }
+
+    /**
+     * @see PersistenceManager#exists(ItemId id)
+     */
+    public boolean exists(ItemId id) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        return stateStore.containsKey(id);
+    }
+
+    /**
+     * @see PersistenceManager#referencesExist(NodeId targetId)
+     */
+    public boolean referencesExist(NodeId targetId) throws ItemStateException {
+        if (!initialized) {
+            throw new IllegalStateException("not initialized");
+        }
+
+        return refsStore.containsKey(targetId);
+    }
+}