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 2005/08/02 18:56:16 UTC

svn commit: r227042 - in /incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core: ./ state/ virtual/

Author: stefan
Date: Tue Aug  2 09:56:07 2005
New Revision: 227042

URL: http://svn.apache.org/viewcvs?rev=227042&view=rev
Log:
core.state: 
replaced abstract ItemStateCache with interface of same name and added ItemStateMap, ItemStateReferenceMap, ItemStateReferenceCache & LRUItemStateCache which provide cleaner semantics and improved memory usage behaviour

Added:
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateMap.java   (with props)
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java   (with props)
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateReferenceMap.java   (with props)
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/LRUItemStateCache.java   (with props)
Modified:
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateCache.java
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/TransientItemStateManager.java
    incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/virtual/AbstractVISProvider.java

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java?rev=227042&r1=227041&r2=227042&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java Tue Aug  2 09:56:07 2005
@@ -62,7 +62,6 @@
 import javax.jcr.version.VersionHistory;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.PrintStream;
 import java.util.HashMap;
 import java.util.Iterator;
 
@@ -155,19 +154,6 @@
      */
     public TransactionalItemStateManager getItemStateManager() {
         return stateMgr;
-    }
-
-    /**
-     * Dumps the state of this <code>WorkspaceImpl</code> instance
-     * (used for diagnostic purposes).
-     *
-     * @param ps
-     * @throws RepositoryException
-     */
-    public void dump(PrintStream ps) throws RepositoryException {
-        ps.println("Workspace: " + wspConfig.getName() + " (" + this + ")");
-        ps.println();
-        stateMgr.dump(ps);
     }
 
     /**

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateCache.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateCache.java?rev=227042&r1=227041&r2=227042&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateCache.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateCache.java Tue Aug  2 09:56:07 2005
@@ -16,205 +16,88 @@
  */
 package org.apache.jackrabbit.core.state;
 
-import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.jackrabbit.core.ItemId;
-import org.apache.log4j.Logger;
 
-import java.io.PrintStream;
-import java.util.ArrayList;
+import java.util.Set;
 import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
 
 /**
  * An <code>ItemStateCache</code> maintains a cache of <code>ItemState</code>
  * instances.
  */
-public abstract class ItemStateCache {
-    private static Logger log = Logger.getLogger(ItemStateCache.class);
+public interface ItemStateCache {
+    /**
+     * Returns <code>true</code> if this cache contains an <code>ItemState</code>
+     * object with the specified <code>id</code>.
+     *
+     * @param id id of <code>ItemState</code> object whose presence should be
+     *           tested.
+     * @return <code>true</code> if there's a corresponding cache entry,
+     *         otherwise <code>false</code>.
+     */
+    boolean isCached(ItemId id);
 
     /**
-     * A cache for <code>ItemState</code> instances
+     * Returns the <code>ItemState</code> object with the specified
+     * <code>id</code> if it is present or <code>null</code> if no entry exists
+     * with that <code>id</code>.
+     *
+     * @param id the id of the <code>ItemState</code> object to be returned.
+     * @return the <code>ItemState</code> object with the specified
+     *         <code>id</code> or or <code>null</code> if no entry exists
+     *         with that <code>id</code>
      */
-    private final Map cache;
+    ItemState retrieve(ItemId id);
 
     /**
-     * Monitor for cache object. Derived classes should synchronize on
-     * this monitor when the identity of cached objects is critical, i.e.
-     * when object should not be cached more than once.
+     * Stores the specified <code>ItemState</code> object in the map
+     * using its <code>ItemId</code> as the key.
+     *
+     * @param state the <code>ItemState</code> object to cache
      */
-    protected final Object cacheMonitor = new Object();
+    void cache(ItemState state);
 
     /**
-     * Creates a new <code>ItemStateCache</code> that will use instance
-     * hard references to keys and soft references to values.
+     * Removes the <code>ItemState</code> object with the specified id from
+     * this cache if it is present.
+     *
+     * @param id the id of the <code>ItemState</code> object which should be
+     *           removed from this cache.
      */
-    protected ItemStateCache() {
-        // setup cache with soft references to ItemState instances
-        this(ReferenceMap.HARD, ReferenceMap.SOFT);
-    }
+    void evict(ItemId id);
 
     /**
-     * Creates a new <code>ItemStateCache</code> instance that will use the
-     * specified types of references.
+     * Clears all entries from this cache.
+     */
+    void evictAll();
+
+    /**
+     * Returns <code>true</code> if this cache contains no entries.
      *
-     * @param keyType   the type of reference to use for keys (i.e. the item paths)
-     * @param valueType the type of reference to use for values (i.e. the item-datas)
-     * @see ReferenceMap#HARD
-     * @see ReferenceMap#SOFT
-     * @see ReferenceMap#WEAK
+     * @return <code>true</code> if this cache contains no entries.
      */
-    protected ItemStateCache(int keyType, int valueType) {
-        cache = new ReferenceMap(keyType, valueType);
-    }
+    boolean isEmpty();
 
     /**
-     * Checks if there's already a corresponding cache entry for
-     * <code>id</code>.
+     * Returns the number of entries in this cache.
      *
-     * @param id id of a <code>ItemState</code> object
-     * @return true if there's a corresponding cache entry, otherwise false.
+     * @return number of entries in this cache.
      */
-    protected boolean isCached(ItemId id) {
-        return cache.containsKey(id);
-    }
+    int size();
 
     /**
-     * Returns an item-state reference from the cache.
+     * Returns an unmodifiable set view of the keys (i.e. <code>ItemId</code>
+     * objects) of the cached entries.
      *
-     * @param id the id of the <code>ItemState</code> object whose
-     *           reference should be retrieved from the cache.
-     * @return the <code>ItemState</code> reference stored in the corresponding
-     *         cache entry or <code>null</code> if there's no corresponding
-     *         cache entry.
+     * @return a set view of the keys of the cached entries.
      */
-    protected ItemState retrieve(ItemId id) {
-        return (ItemState) cache.get(id);
-    }
+    Set keySet();
 
     /**
-     * Puts the reference to an <code>ItemState</code> object in the cache using its path as the key.
+     * Returns an unmodifiable collection view of the values (i.e.
+     * <code>ItemState</code> objects) contained in this cache.
      *
-     * @param state the <code>ItemState</code> object to cache
+     * @return a collection view of the values contained in this cache.
      */
-    protected void cache(ItemState state) {
-        ItemId id = state.getId();
-        if (cache.containsKey(id)) {
-            log.warn("overwriting cached entry " + id);
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("caching " + id);
-        }
-        cache.put(id, state);
-    }
-
-    /**
-     * Removes a cache entry for a specific id.
-     *
-     * @param id the id of the <code>ItemState</code> object whose
-     *           reference should be removed from the cache.
-     */
-    protected void evict(ItemId id) {
-        if (log.isDebugEnabled()) {
-            log.debug("removing entry " + id + " from cache");
-        }
-        cache.remove(id);
-    }
-
-    /**
-     * Clears all entries from the cache.
-     */
-    protected void evictAll() {
-        if (log.isDebugEnabled()) {
-            log.debug("removing all entries from cache");
-        }
-        cache.clear();
-    }
-
-    /**
-     * Returns <code>true</code> if the cache contains no entries.
-     *
-     * @return <code>true</code> if the cache contains no entries.
-     */
-    protected boolean isEmpty() {
-        return cache.isEmpty();
-    }
-
-    /**
-     * Returns the number of entries in the cache.
-     *
-     * @return number of entries in the cache.
-     */
-    protected int size() {
-        return cache.size();
-    }
-
-    /**
-     * Returns an iterator of the keys (i.e. <code>ItemId</code> objects)
-     * of the cached entries.
-     *
-     * @return an iterator of the keys of the cached entries.
-     */
-    protected Iterator keys() {
-        // use temp collection to avoid ConcurrentModificationException
-        Collection tmp = new ArrayList(cache.keySet());
-        return tmp.iterator();
-    }
-
-    /**
-     * Returns an iterator of the entries (i.e. <code>ItemState</code> objects)
-     * in the cache.
-     *
-     * @return an iterator of the entries in the cache.
-     */
-    protected Iterator entries() {
-        // use temp collection to avoid ConcurrentModificationException
-        Collection tmp = new ArrayList(cache.values());
-        return tmp.iterator();
-    }
-
-    /**
-     * Dumps the state of this <code>ItemStateCache</code> instance
-     * (used for diagnostic purposes).
-     *
-     * @param ps
-     */
-    void dump(PrintStream ps) {
-        ps.println("entries in cache:");
-        ps.println();
-        Iterator iter = keys();
-        while (iter.hasNext()) {
-            ItemId id = (ItemId) iter.next();
-            ItemState state = retrieve(id);
-            dumpItemState(id, state, ps);
-        }
-    }
-
-    private void dumpItemState(ItemId id, ItemState state, PrintStream ps) {
-        ps.print(state.isNode() ? "Node: " : "Prop: ");
-        switch (state.getStatus()) {
-            case ItemState.STATUS_EXISTING:
-                ps.print("[existing]           ");
-                break;
-            case ItemState.STATUS_EXISTING_MODIFIED:
-                ps.print("[existing, modified] ");
-                break;
-            case ItemState.STATUS_EXISTING_REMOVED:
-                ps.print("[existing, removed]  ");
-                break;
-            case ItemState.STATUS_NEW:
-                ps.print("[new]                ");
-                break;
-            case ItemState.STATUS_STALE_DESTROYED:
-                ps.print("[stale, destroyed]   ");
-                break;
-            case ItemState.STATUS_STALE_MODIFIED:
-                ps.print("[stale, modified]    ");
-                break;
-            case ItemState.STATUS_UNDEFINED:
-                ps.print("[undefined]          ");
-                break;
-        }
-        ps.println(id + " (" + state + ")");
-    }
+    Collection values();
 }

Added: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateMap.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateMap.java?rev=227042&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateMap.java (added)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateMap.java Tue Aug  2 09:56:07 2005
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+import org.apache.jackrabbit.core.ItemId;
+import org.apache.log4j.Logger;
+
+import java.io.PrintStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An <code>ItemStateMap</code> stores <code>ItemState</code> instances using
+ * their <code>ItemId</code>s as key.
+ */
+public class ItemStateMap {
+    private static Logger log = Logger.getLogger(ItemStateMap.class);
+
+    /**
+     * the map backing this <code>ItemStateMap</code> instance
+     */
+    protected final Map map;
+
+    /**
+     * Creates a new HashMap-backed <code>ItemStateMap</code> instance.
+     */
+    public ItemStateMap() {
+        this(new HashMap());
+    }
+
+    /**
+     * Protected constructor for specialized subclasses
+     *
+     * @param map <code>Map</code> implementation to be used as backing store.
+     */
+    protected ItemStateMap(Map map) {
+        this.map = map;
+    }
+
+    //-------------------------------------------------------< public methods >
+    /**
+     * Returns <code>true</code> if this map contains an <code>ItemState</code>
+     * object with the specified <code>id</code>.
+     *
+     * @param id id of <code>ItemState</code> object whose presence should be
+     *           tested.
+     * @return <code>true</code> if there's a corresponding map entry,
+     *         otherwise <code>false</code>.
+     */
+    public boolean contains(ItemId id) {
+        return map.containsKey(id);
+    }
+
+    /**
+     * Returns the <code>ItemState</code> object with the specified
+     * <code>id</code> if it is present or <code>null</code> if no entry exists
+     * with that <code>id</code>.
+     *
+     * @param id the id of the <code>ItemState</code> object to be returned.
+     * @return the <code>ItemState</code> object with the specified
+     *         <code>id</code> or or <code>null</code> if no entry exists
+     *         with that <code>id</code>
+     */
+    public ItemState get(ItemId id) {
+        return (ItemState) map.get(id);
+    }
+
+    /**
+     * Stores the specified <code>ItemState</code> object in the map
+     * using its <code>ItemId</code> as the key.
+     *
+     * @param state the <code>ItemState</code> object to store
+     */
+    public void put(ItemState state) {
+        ItemId id = state.getId();
+        if (map.containsKey(id)) {
+            log.warn("overwriting map entry " + id);
+        }
+        map.put(id, state);
+    }
+
+    /**
+     * Removes the <code>ItemState</code> object with the specified id from
+     * this map if it is present.
+     *
+     * @param id the id of the <code>ItemState</code> object which should be
+     *           removed from this map.
+     */
+    public void remove(ItemId id) {
+        map.remove(id);
+    }
+
+    /**
+     * Removes all entries from this map.
+     */
+    public void clear() {
+        map.clear();
+    }
+
+    /**
+     * Returns <code>true</code> if the map contains no entries.
+     *
+     * @return <code>true</code> if the map contains no entries.
+     */
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    /**
+     * Returns the number of entries in the map.
+     *
+     * @return number of entries in the map.
+     */
+    public int size() {
+        return map.size();
+    }
+
+    /**
+     * Returns an unmodifiable set view of the keys (i.e. <code>ItemId</code>
+     * objects) contained in this map.
+     *
+     * @return a set view of the keys contained in this map.
+     */
+    public Set keySet() {
+        return Collections.unmodifiableSet(map.keySet());
+    }
+
+    /**
+     * Returns an unmodifiable collection view of the values (i.e.
+     * <code>ItemState</code> objects) contained in this map.
+     *
+     * @return a collection view of the values contained in this map.
+     */
+    public Collection values() {
+        return Collections.unmodifiableCollection(map.values());
+    }
+
+    //-------------------------------------------------------< implementation >
+    /**
+     * Dumps the state of this <code>ItemStateMap</code> instance
+     * (used for diagnostic purposes).
+     *
+     * @param ps
+     */
+    protected void dump(PrintStream ps) {
+        ps.println("map entries:");
+        ps.println();
+        Iterator iter = keySet().iterator();
+        while (iter.hasNext()) {
+            ItemId id = (ItemId) iter.next();
+            ItemState state = get(id);
+            dumpItemState(id, state, ps);
+        }
+    }
+
+    private void dumpItemState(ItemId id, ItemState state, PrintStream ps) {
+        ps.print(state.isNode() ? "Node: " : "Prop: ");
+        switch (state.getStatus()) {
+            case ItemState.STATUS_EXISTING:
+                ps.print("[existing]           ");
+                break;
+            case ItemState.STATUS_EXISTING_MODIFIED:
+                ps.print("[existing, modified] ");
+                break;
+            case ItemState.STATUS_EXISTING_REMOVED:
+                ps.print("[existing, removed]  ");
+                break;
+            case ItemState.STATUS_NEW:
+                ps.print("[new]                ");
+                break;
+            case ItemState.STATUS_STALE_DESTROYED:
+                ps.print("[stale, destroyed]   ");
+                break;
+            case ItemState.STATUS_STALE_MODIFIED:
+                ps.print("[stale, modified]    ");
+                break;
+            case ItemState.STATUS_UNDEFINED:
+                ps.print("[undefined]          ");
+                break;
+        }
+        ps.println(id + " (" + state + ")");
+    }
+}

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

Added: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java?rev=227042&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java (added)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateReferenceCache.java Tue Aug  2 09:56:07 2005
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+import org.apache.jackrabbit.core.ItemId;
+import org.apache.log4j.Logger;
+
+import java.util.Set;
+import java.util.Collections;
+import java.util.Collection;
+
+/**
+ * <code>ItemStateReferenceCache</code> internally consists of 2 components:
+ * <ul>
+ * <li>an <code>ItemStateReferenceMap<code> serving as the primary (or main)
+ * cache; it holds weak references to <code>ItemState</code> instances. This
+ * <code>ItemStateCache</code> implementation directly represents the
+ * contents of the primary cache, i.e. {@link #isCached(ItemId)},
+ * {@link #retrieve(ItemId)}}, {@link #size()} etc. only refer to the contents
+ * of the primary cache.</li>
+ * <li>an <code>ItemStateCache</code> implementing a custom eviction policy and
+ * serving as the secondary (or auxiliary) cache; entries that are automatically
+ * flusehd from this secondary cache through its eviction policy (LRU, etc.)
+ * will be indirectly flushed from the primary (reference) cache by the garbage
+ * collector if they are thus rendered weakly reachable.
+ * </li>
+ * </ul>
+ */
+public class ItemStateReferenceCache implements ItemStateCache {
+
+    /** Logger instance */
+    private static Logger log = Logger.getLogger(LRUItemStateCache.class);
+
+    /**
+     * primary cache storing weak references to <code>ItemState</code>
+     * instances.
+     */
+    private final ItemStateReferenceMap refs;
+    /**
+     * secondary cache that automatically flushes entries based on some
+     * eviction policy; entries flushed from the secondary cache will be
+     * indirectly flushed from the primary (reference) cache by the garbage
+     * collector if they thus are rendered weakly reachable.
+     */
+    private final ItemStateCache cache;
+
+    /**
+     * Creates a new <code>ItemStateReferenceCache</code> that uses a
+     * <code>LRUItemStateCache</code> instance as internal secondary
+     * cache.
+     */
+    public ItemStateReferenceCache() {
+        this(new LRUItemStateCache());
+    }
+
+    /**
+     * Creates a new <code>ItemStateReferenceCache</code> that uses the
+     * specified <code>ItemStateCache</code> instance as internal secondary
+     * cache.
+     *
+     * @param cache secondary cache implementing a custom eviction policy
+     */
+    public ItemStateReferenceCache(ItemStateCache cache) {
+        this.cache = cache;
+        refs = new ItemStateReferenceMap();
+    }
+
+    //-------------------------------------------------------< ItemStateCache >
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isCached(ItemId id) {
+        // check primary cache
+        return refs.contains(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ItemState retrieve(ItemId id) {
+        // fake call to update stats of secondary cache
+        cache.retrieve(id);
+
+        // retrieve from primary cache
+        return (ItemState) refs.get(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void cache(ItemState state) {
+        ItemId id = state.getId();
+        if (refs.contains(id)) {
+            log.warn("overwriting cached entry " + id);
+        }
+        // fake call to update stats of secondary cache
+        cache.cache(state);
+        // store weak reference in primary cache
+        refs.put(state);
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void evict(ItemId id) {
+        // fake call to update stats of secondary cache
+        cache.evict(id);
+        // remove from primary cache
+        refs.remove(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void evictAll() {
+        // fake call to update stats of secondary cache
+        cache.evictAll();
+        // remove all weak references from primary cache
+        refs.clear();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isEmpty() {
+        // check primary cache
+        return refs.isEmpty();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int size() {
+        // size of primary cache
+        return refs.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set keySet() {
+        return Collections.unmodifiableSet(refs.keySet());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection values() {
+        return Collections.unmodifiableCollection(refs.values());
+    }
+}

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

Added: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateReferenceMap.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateReferenceMap.java?rev=227042&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateReferenceMap.java (added)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/ItemStateReferenceMap.java Tue Aug  2 09:56:07 2005
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+import org.apache.commons.collections.map.ReferenceMap;
+
+/**
+ * <code>ItemStateReferenceMap</code> is a specialized <code>ItemStateMap</code>
+ * that stores <code>WEAK</code> references to <code>ItemState</code> objects.
+ */
+public class ItemStateReferenceMap extends ItemStateMap {
+
+    /**
+     * Creates a new ReferenceMap-backed <code>ItemStateReferenceMap</code>
+     * instance that stores <code>WEAK</code> references to
+     * <code>ItemState</code> objects. An entry in this map is automatically
+     * removed when the garbage collector determines that its value
+     * (i.e. an <code>ItemState</code> object) is only weakly reachable.
+     */
+    public ItemStateReferenceMap() {
+        super(new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK));
+    }
+}

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

Added: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/LRUItemStateCache.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/LRUItemStateCache.java?rev=227042&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/LRUItemStateCache.java (added)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/LRUItemStateCache.java Tue Aug  2 09:56:07 2005
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+import org.apache.commons.collections.map.LRUMap;
+import org.apache.jackrabbit.core.ItemId;
+import org.apache.log4j.Logger;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * An <code>ItemStateCache</code> implementation that internally uses a
+ * {@link LRUMap} to maintain a cache of <code>ItemState</code> objects.
+ */
+public class LRUItemStateCache implements ItemStateCache {
+    /** Logger instance */
+    private static Logger log = Logger.getLogger(LRUItemStateCache.class);
+
+    /** default maximum size of this cache */
+    public static final int DEFAULT_MAX_SIZE = 5000;
+
+    /**
+     * A cache for <code>ItemState</code> instances
+     */
+    private final LRUMap cache;
+
+    /**
+     * Constructs a new, empty <code>ItemStateCache</code> with a maximum size
+     * of 1000 and a load factor of 0.8.
+     */
+    public LRUItemStateCache() {
+        this(DEFAULT_MAX_SIZE);
+    }
+
+    /**
+     * Constructs a new, empty <code>ItemStateCache</code> with the specified
+     * maximum size.
+     *
+     * @param maxSize the maximum size of the cache, -1 for no limit,
+     */
+    public LRUItemStateCache(int maxSize) {
+        // setup cache
+        cache = new LRUMap(maxSize, true);
+    }
+
+    //-------------------------------------------------------< ItemStateCache >
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isCached(ItemId id) {
+        return cache.containsKey(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ItemState retrieve(ItemId id) {
+        return (ItemState) cache.get(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void cache(ItemState state) {
+        ItemId id = state.getId();
+        if (cache.containsKey(id)) {
+            log.warn("overwriting cached entry " + id);
+        }
+        cache.put(id, state);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void evict(ItemId id) {
+        cache.remove(id);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void evictAll() {
+        cache.clear();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isEmpty() {
+        return cache.isEmpty();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int size() {
+        return cache.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set keySet() {
+        return Collections.unmodifiableSet(cache.keySet());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection values() {
+        return Collections.unmodifiableCollection(cache.values());
+    }
+}

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

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java?rev=227042&r1=227041&r2=227042&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/LocalItemStateManager.java Tue Aug  2 09:56:07 2005
@@ -25,13 +25,12 @@
 import org.apache.log4j.Logger;
 
 import javax.jcr.RepositoryException;
-import java.io.PrintStream;
 
 /**
  * Local <code>ItemStateManager</code> that isolates changes to
  * persistent states from other clients.
  */
-public class LocalItemStateManager extends ItemStateCache
+public class LocalItemStateManager
         implements UpdatableItemStateManager, ItemStateListener {
 
     /**
@@ -40,6 +39,12 @@
     private static Logger log = Logger.getLogger(LocalItemStateManager.class);
 
     /**
+     * cache of weak references to ItemState objects issued by this
+     * ItemStateManager
+     */
+    private final ItemStateReferenceCache cache;
+
+    /**
      * Shared item state manager
      */
     protected final SharedItemStateManager sharedStateMgr;
@@ -70,7 +75,9 @@
      *                       manager. Version item states are not associated with a specific workspace
      *                       instance.
      */
-    public LocalItemStateManager(SharedItemStateManager sharedStateMgr, WorkspaceImpl wspImpl) {
+    public LocalItemStateManager(SharedItemStateManager sharedStateMgr,
+                                 WorkspaceImpl wspImpl) {
+        cache = new ItemStateReferenceCache();
         this.sharedStateMgr = sharedStateMgr;
         this.wspImpl = wspImpl;
     }
@@ -80,7 +87,7 @@
      */
     public void dispose() {
         // clear cache
-        evictAll();
+        cache.evictAll();
     }
 
     /**
@@ -101,7 +108,7 @@
         state = new NodeState(state, state.getStatus(), false);
 
         // put it in cache
-        cache(state);
+        cache.cache(state);
 
         // register as listener
         state.addListener(this);
@@ -126,25 +133,13 @@
         state = new PropertyState(state, state.getStatus(), false);
 
         // put it in cache
-        cache(state);
+        cache.cache(state);
 
         // register as listener
         state.addListener(this);
         return state;
     }
 
-    /**
-     * Dumps the state of this <code>LocalItemStateManager</code> instance
-     * (used for diagnostic purposes).
-     *
-     * @param ps
-     */
-    public void dump(PrintStream ps) {
-        ps.println("LocalItemStateManager (" + this + ")");
-        ps.println();
-        super.dump(ps);
-    }
-
     //-----------------------------------------------------< ItemStateManager >
     /**
      * {@inheritDoc}
@@ -159,8 +154,8 @@
         }
 
         // check cache. synchronized to ensure an entry is not created twice.
-        synchronized (cacheMonitor) {
-            state = retrieve(id);
+        synchronized (cache) {
+            state = cache.retrieve(id);
             if (state == null) {
                 // regular behaviour
                 if (id.denotesNode()) {
@@ -189,7 +184,7 @@
         }
 
         // check cache
-        if (isCached(id)) {
+        if (cache.isCached(id)) {
             return true;
         }
 
@@ -375,7 +370,7 @@
     public void stateDestroyed(ItemState destroyed) {
         destroyed.removeListener(this);
 
-        evict(destroyed.getId());
+        cache.evict(destroyed.getId());
     }
 
     /**
@@ -384,6 +379,6 @@
     public void stateDiscarded(ItemState discarded) {
         discarded.removeListener(this);
 
-        evict(discarded.getId());
+        cache.evict(discarded.getId());
     }
 }

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java?rev=227042&r1=227041&r2=227042&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/SessionItemStateManager.java Tue Aug  2 09:56:07 2005
@@ -93,11 +93,6 @@
     public void dump(PrintStream ps) {
         ps.println("SessionItemStateManager (" + this + ")");
         ps.println();
-        // FIXME hack!
-        if (persistentStateMgr instanceof ItemStateCache) {
-            ((ItemStateCache) persistentStateMgr).dump(ps);
-            ps.println();
-        }
         transientStateMgr.dump(ps);
         ps.println();
     }
@@ -292,7 +287,7 @@
      * @return
      */
     public boolean hasAnyTransientItemStates() {
-        return !transientStateMgr.isEmpty();
+        return transientStateMgr.hasAnyItemStates();
     }
 
     /**

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java?rev=227042&r1=227041&r2=227042&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java Tue Aug  2 09:56:07 2005
@@ -34,7 +34,6 @@
 import javax.jcr.PropertyType;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
-import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Iterator;
 
@@ -46,7 +45,7 @@
  * <code>PersistenceManager</code>. Objects returned by this item state
  * manager are shared among all sessions.
  */
-public class SharedItemStateManager extends ItemStateCache
+public class SharedItemStateManager
         implements ItemStateManager, ItemStateListener {
 
     /**
@@ -55,9 +54,15 @@
     private static Logger log = Logger.getLogger(SharedItemStateManager.class);
 
     /**
-     * Persistence Manager to use for loading and storing items
+     * cache of weak references to ItemState objects issued by this
+     * ItemStateManager 
      */
-    protected final PersistenceManager persistMgr;
+    private final ItemStateReferenceCache cache;
+
+    /**
+     * Persistence Manager used for loading and storing items
+     */
+    private final PersistenceManager persistMgr;
 
     /**
      * Keep a hard reference to the root node state
@@ -67,12 +72,14 @@
     /**
      * Virtual item state providers
      */
-    private VirtualItemStateProvider[] virtualProviders = new VirtualItemStateProvider[0];
+    private VirtualItemStateProvider[] virtualProviders = new
+            VirtualItemStateProvider[0];
 
     /**
      * Read-/Write-Lock to synchronize access on this item state manager.
      */
-    private final ReadWriteLock rwLock = new ReentrantWriterPreferenceReadWriteLock();
+    private final ReadWriteLock rwLock =
+            new ReentrantWriterPreferenceReadWriteLock();
 
     /**
      * Creates a new <code>SharedItemStateManager</code> instance.
@@ -85,149 +92,17 @@
                                   String rootNodeUUID,
                                   NodeTypeRegistry ntReg)
             throws ItemStateException {
-
+        cache = new ItemStateReferenceCache();
         this.persistMgr = persistMgr;
 
         try {
-            root = getNodeState(new NodeId(rootNodeUUID));
+            root = (NodeState) getNonVirtualItemState(new NodeId(rootNodeUUID));
         } catch (NoSuchItemStateException e) {
             // create root node
             root = createRootNodeState(rootNodeUUID, ntReg);
         }
     }
 
-    /**
-     * Disposes this <code>SharedItemStateManager</code> and frees resources.
-     */
-    public void dispose() {
-        // clear cache
-        evictAll();
-    }
-
-    /**
-     * Adds a new virtual item state provider.<p/>
-     * NOTE: This method is not synchronized, because it is called right after
-     * creation only by the same thread and therefore concurrency issues
-     * do not occur. Should this ever change, the synchronization status
-     * has to be re-examined.
-     * @param prov
-     */
-    public void addVirtualItemStateProvider(VirtualItemStateProvider prov) {
-        VirtualItemStateProvider[] provs = new VirtualItemStateProvider[virtualProviders.length + 1];
-        System.arraycopy(virtualProviders, 0, provs, 0, virtualProviders.length);
-        provs[virtualProviders.length] = prov;
-        virtualProviders = provs;
-    }
-
-    /**
-     * Create root node state
-     * @param rootNodeUUID root node UUID
-     * @param ntReg node type registry
-     * @return root node state
-     * @throws ItemStateException if an error occurs
-     */
-    private NodeState createRootNodeState(String rootNodeUUID,
-                                          NodeTypeRegistry ntReg)
-            throws ItemStateException {
-
-        NodeState rootState = createInstance(rootNodeUUID, Constants.REP_ROOT, null);
-
-        // FIXME need to manually setup root node by creating mandatory jcr:primaryType property
-        // @todo delegate setup of root node to NodeTypeInstanceHandler
-
-        // id of the root node's definition
-        NodeDefId nodeDefId;
-        // definition of jcr:primaryType property
-        PropDef propDef;
-        try {
-            nodeDefId = ntReg.getRootNodeDef().getId();
-            EffectiveNodeType ent = ntReg.getEffectiveNodeType(Constants.REP_ROOT);
-            propDef = ent.getApplicablePropertyDef(Constants.JCR_PRIMARYTYPE,
-                    PropertyType.NAME, false);
-        } catch (NoSuchNodeTypeException nsnte) {
-            String msg = "internal error: failed to create root node";
-            log.error(msg, nsnte);
-            throw new ItemStateException(msg, nsnte);
-        } catch (ConstraintViolationException cve) {
-            String msg = "internal error: failed to create root node";
-            log.error(msg, cve);
-            throw new ItemStateException(msg, cve);
-        }
-        rootState.setDefinitionId(nodeDefId);
-
-        // create jcr:primaryType property
-        rootState.addPropertyName(propDef.getName());
-
-        PropertyState prop = createInstance(propDef.getName(), rootNodeUUID);
-        prop.setValues(new InternalValue[]{InternalValue.create(Constants.REP_ROOT)});
-        prop.setType(propDef.getRequiredType());
-        prop.setMultiValued(propDef.isMultiple());
-        prop.setDefinitionId(propDef.getId());
-
-        ChangeLog changeLog = new ChangeLog();
-        changeLog.added(rootState);
-        changeLog.added(prop);
-
-        persistMgr.store(changeLog);
-        changeLog.persisted();
-
-        return rootState;
-    }
-
-    /**
-     * Dumps the state of this <code>SharedItemStateManager</code> instance
-     * (used for diagnostic purposes).
-     *
-     * @param ps
-     */
-    public void dump(PrintStream ps) {
-        ps.println("SharedItemStateManager (" + this + ")");
-        ps.println();
-        super.dump(ps);
-    }
-
-    /**
-     * @param id
-     * @return
-     * @throws NoSuchItemStateException
-     * @throws ItemStateException
-     */
-    private NodeState getNodeState(NodeId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        // load from persisted state
-        NodeState state = persistMgr.load(id);
-        state.setStatus(ItemState.STATUS_EXISTING);
-
-        // put it in cache
-        cache(state);
-
-        // register as listener
-        state.addListener(this);
-        return state;
-    }
-
-    /**
-     * @param id
-     * @return
-     * @throws NoSuchItemStateException
-     * @throws ItemStateException
-     */
-    private PropertyState getPropertyState(PropertyId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        // load from persisted state
-        PropertyState state = persistMgr.load(id);
-        state.setStatus(ItemState.STATUS_EXISTING);
-
-        // put it in cache
-        cache(state);
-
-        // register as listener
-        state.addListener(this);
-        return state;
-    }
-
     //-----------------------------------------------------< ItemStateManager >
     /**
      * {@inheritDoc}
@@ -261,28 +136,6 @@
     }
 
     /**
-     * returns the item state for the given id without considering virtual
-     * item state providers.
-     */
-    private ItemState getNonVirtualItemState(ItemId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        // check cache. synchronized to ensure an entry is not created twice.
-        synchronized (cacheMonitor) {
-            ItemState state = retrieve(id);
-            if (state == null) {
-                // regular behaviour
-                if (id.denotesNode()) {
-                    state = getNodeState((NodeId) id);
-                } else {
-                    state = getPropertyState((PropertyId) id);
-                }
-            }
-            return state;
-        }
-    }
-
-    /**
      * {@inheritDoc}
      */
     public boolean hasItemState(ItemId id) {
@@ -294,7 +147,7 @@
         }
 
         try {
-            if (isCached(id)) {
+            if (cache.isCached(id)) {
                 return true;
             }
 
@@ -321,26 +174,6 @@
     }
 
     /**
-     * Checks if this item state manager has the given item state without
-     * considering the virtual item state managers.
-     */
-    private boolean hasNonVirtualItemState(ItemId id) {
-        if (isCached(id)) {
-            return true;
-        }
-
-        try {
-            if (id.denotesNode()) {
-                return persistMgr.exists((NodeId) id);
-            } else {
-                return persistMgr.exists((PropertyId) id);
-            }
-        } catch (ItemStateException ise) {
-            return false;
-        }
-    }
-
-    /**
      * {@inheritDoc}
      */
     public NodeReferences getNodeReferences(NodeReferencesId id)
@@ -403,100 +236,60 @@
         return false;
     }
 
-    //-------------------------------------------------------- other operations
-
+    //----------------------------------------------------< ItemStateListener >
     /**
-     * Create a new node state instance
-     *
-     * @param uuid         uuid
-     * @param nodeTypeName node type name
-     * @param parentUUID   parent UUID
-     * @return new node state instance
+     * {@inheritDoc}
      */
-    private NodeState createInstance(String uuid, QName nodeTypeName,
-                                     String parentUUID) {
-
-        NodeState state = persistMgr.createNew(new NodeId(uuid));
-        state.setNodeTypeName(nodeTypeName);
-        state.setParentUUID(parentUUID);
-        state.setStatus(ItemState.STATUS_NEW);
-        state.addListener(this);
-
-        return state;
+    public void stateCreated(ItemState created) {
+        cache.cache(created);
     }
 
     /**
-     * Create a new node state instance
-     *
-     * @param other other state associated with new instance
-     * @return new node state instance
+     * {@inheritDoc}
      */
-    private ItemState createInstance(ItemState other) {
-        if (other.isNode()) {
-            NodeState ns = (NodeState) other;
-            return createInstance(ns.getUUID(), ns.getNodeTypeName(), ns.getParentUUID());
-        } else {
-            PropertyState ps = (PropertyState) other;
-            return createInstance(ps.getName(), ps.getParentUUID());
-        }
+    public void stateModified(ItemState modified) {
+        // not interested
     }
 
     /**
-     * Create a new property state instance
-     *
-     * @param propName   property name
-     * @param parentUUID parent UUID
-     * @return new property state instance
+     * {@inheritDoc}
      */
-    PropertyState createInstance(QName propName, String parentUUID) {
-        PropertyState state = persistMgr.createNew(new PropertyId(parentUUID, propName));
-        state.setStatus(ItemState.STATUS_NEW);
-        state.addListener(this);
-
-        return state;
+    public void stateDestroyed(ItemState destroyed) {
+        destroyed.removeListener(this);
+        cache.evict(destroyed.getId());
     }
 
     /**
-     * Load item state from persistent storage.
-     * @param id item id
-     * @return item state
+     * {@inheritDoc}
      */
-    private ItemState loadItemState(ItemId id)
-            throws NoSuchItemStateException, ItemStateException {
-
-        if (id.denotesNode()) {
-            return persistMgr.load((NodeId) id);
-        } else {
-            return persistMgr.load((PropertyId) id);
-        }
+    public void stateDiscarded(ItemState discarded) {
+        discarded.removeListener(this);
+        cache.evict(discarded.getId());
     }
 
+    //-------------------------------------------------< misc. public methods >
     /**
-     * Check targets of modified node references exist.
-     * @param log change log
-     * @throws ItemStateException if some target was not found
+     * Disposes this <code>SharedItemStateManager</code> and frees resources.
      */
-    protected void checkTargetsExist(ChangeLog log) throws ItemStateException {
-        Iterator iter = log.modifiedRefs();
-        while (iter.hasNext()) {
-            NodeReferences refs = (NodeReferences) iter.next();
-            NodeId id = new NodeId(refs.getUUID());
+    public void dispose() {
+        // clear cache
+        cache.evictAll();
+    }
 
-            for (int i = 0; i < virtualProviders.length; i++) {
-                VirtualItemStateProvider provider = virtualProviders[i];
-                if (provider.hasItemState(id)) {
-                    refs = null;
-                    break;
-                }
-            }
-            if (refs != null && refs.hasReferences()) {
-                if (!log.has(id) && !hasItemState(id)) {
-                    String msg = "Target node " + id
-                            + " of REFERENCE property does not exist";
-                    throw new ItemStateException(msg);
-                }
-            }
-        }
+    /**
+     * Adds a new virtual item state provider.<p/>
+     * NOTE: This method is not synchronized, because it is called right after
+     * creation only by the same thread and therefore concurrency issues
+     * do not occur. Should this ever change, the synchronization status
+     * has to be re-examined.
+     * @param prov
+     */
+    public void addVirtualItemStateProvider(VirtualItemStateProvider prov) {
+        VirtualItemStateProvider[] provs =
+                new VirtualItemStateProvider[virtualProviders.length + 1];
+        System.arraycopy(virtualProviders, 0, provs, 0, virtualProviders.length);
+        provs[virtualProviders.length] = prov;
+        virtualProviders = provs;
     }
 
     /**
@@ -664,58 +457,220 @@
         }
     }
 
+    //-------------------------------------------------------< implementation >
     /**
-     * Acquires the read lock on this item state manager.
-     * @throws ItemStateException if the read lock cannot be acquired.
+     * Create a new node state instance
+     *
+     * @param uuid         uuid
+     * @param nodeTypeName node type name
+     * @param parentUUID   parent UUID
+     * @return new node state instance
      */
-    private void acquireReadLock() throws ItemStateException {
+    private NodeState createInstance(String uuid, QName nodeTypeName,
+                                     String parentUUID) {
+
+        NodeState state = persistMgr.createNew(new NodeId(uuid));
+        state.setNodeTypeName(nodeTypeName);
+        state.setParentUUID(parentUUID);
+        state.setStatus(ItemState.STATUS_NEW);
+        state.addListener(this);
+
+        return state;
+    }
+
+    /**
+     * Create root node state
+     * @param rootNodeUUID root node UUID
+     * @param ntReg node type registry
+     * @return root node state
+     * @throws ItemStateException if an error occurs
+     */
+    private NodeState createRootNodeState(String rootNodeUUID,
+                                          NodeTypeRegistry ntReg)
+            throws ItemStateException {
+
+        NodeState rootState = createInstance(rootNodeUUID, Constants.REP_ROOT, null);
+
+        // FIXME need to manually setup root node by creating mandatory jcr:primaryType property
+        // @todo delegate setup of root node to NodeTypeInstanceHandler
+
+        // id of the root node's definition
+        NodeDefId nodeDefId;
+        // definition of jcr:primaryType property
+        PropDef propDef;
         try {
-            rwLock.readLock().acquire();
-        } catch (InterruptedException e) {
-            throw new ItemStateException("Interrupted while acquiring read lock");
+            nodeDefId = ntReg.getRootNodeDef().getId();
+            EffectiveNodeType ent = ntReg.getEffectiveNodeType(Constants.REP_ROOT);
+            propDef = ent.getApplicablePropertyDef(Constants.JCR_PRIMARYTYPE,
+                    PropertyType.NAME, false);
+        } catch (NoSuchNodeTypeException nsnte) {
+            String msg = "internal error: failed to create root node";
+            log.error(msg, nsnte);
+            throw new ItemStateException(msg, nsnte);
+        } catch (ConstraintViolationException cve) {
+            String msg = "internal error: failed to create root node";
+            log.error(msg, cve);
+            throw new ItemStateException(msg, cve);
         }
+        rootState.setDefinitionId(nodeDefId);
+
+        // create jcr:primaryType property
+        rootState.addPropertyName(propDef.getName());
+
+        PropertyState prop = createInstance(propDef.getName(), rootNodeUUID);
+        prop.setValues(new InternalValue[]{InternalValue.create(Constants.REP_ROOT)});
+        prop.setType(propDef.getRequiredType());
+        prop.setMultiValued(propDef.isMultiple());
+        prop.setDefinitionId(propDef.getId());
+
+        ChangeLog changeLog = new ChangeLog();
+        changeLog.added(rootState);
+        changeLog.added(prop);
+
+        persistMgr.store(changeLog);
+        changeLog.persisted();
+
+        return rootState;
     }
 
     /**
-     * Acquires the write lock on this item state manager.
-     * @throws ItemStateException if the write lock cannot be acquired.
+     * Returns the item state for the given id without considering virtual
+     * item state providers.
      */
-    private void acquireWriteLock() throws ItemStateException {
+    private ItemState getNonVirtualItemState(ItemId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        // check cache; synchronized to ensure an entry is not created twice.
+        synchronized (cache) {
+            ItemState state = cache.retrieve(id);
+            if (state == null) {
+                // not found in cache, load from persistent storage
+                state = loadItemState(id);
+                state.setStatus(ItemState.STATUS_EXISTING);
+                // put it in cache
+                cache.cache(state);
+                // register as listener
+                state.addListener(this);
+            }
+            return state;
+        }
+    }
+
+    /**
+     * Checks if this item state manager has the given item state without
+     * considering the virtual item state managers.
+     */
+    private boolean hasNonVirtualItemState(ItemId id) {
+        if (cache.isCached(id)) {
+            return true;
+        }
+
         try {
-            rwLock.writeLock().acquire();
-        } catch (InterruptedException e) {
-            throw new ItemStateException("Interrupted while acquiring write lock");
+            if (id.denotesNode()) {
+                return persistMgr.exists((NodeId) id);
+            } else {
+                return persistMgr.exists((PropertyId) id);
+            }
+        } catch (ItemStateException ise) {
+            return false;
         }
     }
 
-    //----------------------------------------------------< ItemStateListener >
     /**
-     * {@inheritDoc}
+     * Create a new node state instance
+     *
+     * @param other other state associated with new instance
+     * @return new node state instance
      */
-    public void stateCreated(ItemState created) {
-        cache(created);
+    private ItemState createInstance(ItemState other) {
+        if (other.isNode()) {
+            NodeState ns = (NodeState) other;
+            return createInstance(ns.getUUID(), ns.getNodeTypeName(), ns.getParentUUID());
+        } else {
+            PropertyState ps = (PropertyState) other;
+            return createInstance(ps.getName(), ps.getParentUUID());
+        }
     }
 
     /**
-     * {@inheritDoc}
+     * Create a new property state instance
+     *
+     * @param propName   property name
+     * @param parentUUID parent UUID
+     * @return new property state instance
      */
-    public void stateModified(ItemState modified) {
-        // not interested
+    private PropertyState createInstance(QName propName, String parentUUID) {
+        PropertyState state = persistMgr.createNew(new PropertyId(parentUUID, propName));
+        state.setStatus(ItemState.STATUS_NEW);
+        state.addListener(this);
+
+        return state;
     }
 
     /**
-     * {@inheritDoc}
+     * Load item state from persistent storage.
+     * @param id item id
+     * @return item state
      */
-    public void stateDestroyed(ItemState destroyed) {
-        destroyed.removeListener(this);
-        evict(destroyed.getId());
+    private ItemState loadItemState(ItemId id)
+            throws NoSuchItemStateException, ItemStateException {
+
+        if (id.denotesNode()) {
+            return persistMgr.load((NodeId) id);
+        } else {
+            return persistMgr.load((PropertyId) id);
+        }
     }
 
     /**
-     * {@inheritDoc}
+     * Check targets of modified node references exist.
+     * @param log change log
+     * @throws ItemStateException if some target was not found
      */
-    public void stateDiscarded(ItemState discarded) {
-        discarded.removeListener(this);
-        evict(discarded.getId());
+    void checkTargetsExist(ChangeLog log) throws ItemStateException {
+        Iterator iter = log.modifiedRefs();
+        while (iter.hasNext()) {
+            NodeReferences refs = (NodeReferences) iter.next();
+            NodeId id = new NodeId(refs.getUUID());
+
+            for (int i = 0; i < virtualProviders.length; i++) {
+                VirtualItemStateProvider provider = virtualProviders[i];
+                if (provider.hasItemState(id)) {
+                    refs = null;
+                    break;
+                }
+            }
+            if (refs != null && refs.hasReferences()) {
+                if (!log.has(id) && !hasItemState(id)) {
+                    String msg = "Target node " + id
+                            + " of REFERENCE property does not exist";
+                    throw new ItemStateException(msg);
+                }
+            }
+        }
+    }
+
+    /**
+     * Acquires the read lock on this item state manager.
+     * @throws ItemStateException if the read lock cannot be acquired.
+     */
+    private void acquireReadLock() throws ItemStateException {
+        try {
+            rwLock.readLock().acquire();
+        } catch (InterruptedException e) {
+            throw new ItemStateException("Interrupted while acquiring read lock");
+        }
+    }
+
+    /**
+     * Acquires the write lock on this item state manager.
+     * @throws ItemStateException if the write lock cannot be acquired.
+     */
+    private void acquireWriteLock() throws ItemStateException {
+        try {
+            rwLock.writeLock().acquire();
+        } catch (InterruptedException e) {
+            throw new ItemStateException("Interrupted while acquiring write lock");
+        }
     }
 }

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/TransientItemStateManager.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/TransientItemStateManager.java?rev=227042&r1=227041&r2=227042&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/TransientItemStateManager.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/state/TransientItemStateManager.java Tue Aug  2 09:56:07 2005
@@ -16,7 +16,6 @@
  */
 package org.apache.jackrabbit.core.state;
 
-import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.jackrabbit.core.ItemId;
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.PropertyId;
@@ -24,24 +23,39 @@
 import org.apache.log4j.Logger;
 
 import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
 
 /**
  * <code>TransientItemStateManager</code> ...
  */
-class TransientItemStateManager extends ItemStateCache implements ItemStateManager {
+class TransientItemStateManager implements ItemStateManager {
 
     private static Logger log = Logger.getLogger(TransientItemStateManager.class);
 
-    private final Attic attic;
+    /**
+     * map of those states that have been removed transiently
+     */
+    private final ItemStateMap atticMap;
+
+    /**
+     * map of new or modified transient states
+     */
+    private final ItemStateMap transientMap;
+
+    /**
+     * ItemStateManager view of the states in the attic; lazily instantiated
+     * in {@link #getAttic()}
+     */
+    private AtticItemStateManager attic;
 
     /**
      * Creates a new <code>TransientItemStateManager</code> instance.
      */
     TransientItemStateManager() {
-        // we're keeping hard references in the cache
-        super(ReferenceMap.HARD, ReferenceMap.HARD);
-        attic = new Attic();
+        transientMap = new ItemStateMap();
+        atticMap = new ItemStateMap();
     }
 
     /**
@@ -54,10 +68,10 @@
         ps.println("TransientItemStateManager (" + this + ")");
         ps.println();
         ps.print("[transient] ");
-        super.dump(ps);
+        transientMap.dump(ps);
         ps.println();
         ps.print("[attic]     ");
-        attic.dump(ps);
+        atticMap.dump(ps);
     }
 
     //----------------------------------------------------< ItemStateProvider >
@@ -67,7 +81,7 @@
     public ItemState getItemState(ItemId id)
             throws NoSuchItemStateException, ItemStateException {
 
-        ItemState state = retrieve(id);
+        ItemState state = transientMap.get(id);
         if (state != null) {
             return state;
         } else {
@@ -79,7 +93,7 @@
      * {@inheritDoc}
      */
     public boolean hasItemState(ItemId id) {
-        return isCached(id);
+        return transientMap.contains(id);
     }
 
     /**
@@ -104,42 +118,42 @@
      * @return
      */
     boolean hasAnyItemStates() {
-        return !isEmpty();
+        return !transientMap.isEmpty();
     }
 
     /**
      * @return
      */
     boolean hasAnyItemStatesInAttic() {
-        return !attic.isEmpty();
+        return !atticMap.isEmpty();
     }
 
     /**
      * @return
      */
     int getEntriesCount() {
-        return size();
+        return transientMap.size();
     }
 
     /**
      * @return
      */
     int getEntriesInAtticCount() {
-        return attic.size();
+        return atticMap.size();
     }
 
     /**
      * @return
      */
     Iterator getEntries() {
-        return entries();
+        return transientMap.values().iterator();
     }
 
     /**
      * @return
      */
     Iterator getEntriesInAttic() {
-        return attic.entries();
+        return atticMap.values().iterator();
     }
 
     //----------------< methods for creating & discarding ItemState instances >
@@ -157,9 +171,9 @@
 
         NodeId id = new NodeId(uuid);
 
-        // check cache. synchronized to ensure an entry is not created twice.
-        synchronized (cacheMonitor) {
-            if (isCached(id)) {
+        // check map; synchronized to ensure an entry is not created twice.
+        synchronized (transientMap) {
+            if (transientMap.contains(id)) {
                 String msg = "there's already a node state instance with id " + id;
                 log.debug(msg);
                 throw new ItemStateException(msg);
@@ -167,8 +181,8 @@
 
             NodeState state = new NodeState(uuid, nodeTypeName, parentUUID,
                     initialStatus, true);
-            // put it in cache
-            cache(state);
+            // put transient state in the map
+            transientMap.put(state);
             return state;
         }
     }
@@ -184,17 +198,17 @@
 
         ItemId id = overlayedState.getId();
 
-        // check cache. synchronized to ensure an entry is not created twice.
-        synchronized (cacheMonitor) {
-            if (isCached(id)) {
+        // check map; synchronized to ensure an entry is not created twice.
+        synchronized (transientMap) {
+            if (transientMap.contains(id)) {
                 String msg = "there's already a node state instance with id " + id;
                 log.debug(msg);
                 throw new ItemStateException(msg);
             }
 
             NodeState state = new NodeState(overlayedState, initialStatus, true);
-            // put it in cache
-            cache(state);
+            // put transient state in the map
+            transientMap.put(state);
             return state;
         }
     }
@@ -211,17 +225,17 @@
 
         PropertyId id = new PropertyId(parentUUID, propName);
 
-        // check cache. synchronized to ensure an entry is not created twice.
-        synchronized (cacheMonitor) {
-            if (isCached(id)) {
+        // check map; synchronized to ensure an entry is not created twice.
+        synchronized (transientMap) {
+            if (transientMap.contains(id)) {
                 String msg = "there's already a property state instance with id " + id;
                 log.debug(msg);
                 throw new ItemStateException(msg);
             }
 
             PropertyState state = new PropertyState(propName, parentUUID, initialStatus, true);
-            // put it in cache
-            cache(state);
+            // put transient state in the map
+            transientMap.put(state);
             return state;
         }
     }
@@ -238,23 +252,24 @@
         PropertyId id = new PropertyId(overlayedState.getParentUUID(),
                 overlayedState.getName());
 
-        // check cache. synchronized to ensure an entry is not created twice.
-        synchronized (cacheMonitor) {
-            if (isCached(id)) {
+        // check map; synchronized to ensure an entry is not created twice.
+        synchronized (transientMap) {
+            if (transientMap.contains(id)) {
                 String msg = "there's already a property state instance with id " + id;
                 log.debug(msg);
                 throw new ItemStateException(msg);
             }
 
             PropertyState state = new PropertyState(overlayedState, initialStatus, true);
-            // put it in cache
-            cache(state);
+            // put transient state in the map
+            transientMap.put(state);
             return state;
         }
     }
 
     /**
-     * Disposes the specified instance, i.e. discards it and clears it from cache.
+     * Disposes the specified instance, i.e. discards it and removes it from
+     * the map.
      *
      * @param state the <code>ItemState</code> instance that should be disposed
      * @see ItemState#discard()
@@ -263,23 +278,23 @@
         // discard item state, this will invalidate the wrapping Item
         // instance of the transient state
         state.discard();
-        // remove from cache
-        evict(state.getId());
+        // remove from map
+        transientMap.remove(state.getId());
         // give the instance a chance to prepare to get gc'ed
         state.onDisposed();
     }
 
     /**
-     * Transfers the specified instance from the 'active' cache to the attic.
+     * Transfers the specified instance from the 'active' map to the attic.
      *
      * @param state the <code>ItemState</code> instance that should be moved to
      *              the attic
      */
     void moveItemStateToAttic(ItemState state) {
-        // remove from cache
-        evict(state.getId());
+        // remove from map
+        transientMap.remove(state.getId());
         // add to attic
-        attic.cache(state);
+        atticMap.put(state);
     }
 
     /**
@@ -294,7 +309,7 @@
         // instance of the transient state
         state.discard();
         // remove from attic
-        attic.evict(state.getId());
+        atticMap.remove(state.getId());
         // give the instance a chance to prepare to get gc'ed
         state.onDisposed();
     }
@@ -303,14 +318,16 @@
      * Disposes all transient item states in the cache and in the attic.
      */
     void disposeAllItemStates() {
-        // dispose item states in cache
-        Iterator iter = entries();
+        // dispose item states in transient map & attic
+        // (use temp collection to avoid ConcurrentModificationException)
+        Collection tmp = new ArrayList(transientMap.values());
+        Iterator iter = tmp.iterator();
         while (iter.hasNext()) {
             ItemState state = (ItemState) iter.next();
             disposeItemState(state);
         }
-        // dispose item states in attic
-        iter = attic.entries();
+        tmp = new ArrayList(atticMap.values());
+        iter = tmp.iterator();
         while (iter.hasNext()) {
             ItemState state = (ItemState) iter.next();
             disposeItemStateInAttic(state);
@@ -324,14 +341,21 @@
      * @return attic
      */
     ItemStateManager getAttic() {
+        if (attic == null) {
+            attic = new AtticItemStateManager();
+        }
         return attic;
     }
 
     //--------------------------------------------------------< inner classes >
-    class Attic extends ItemStateCache implements ItemStateManager {
+    /**
+     * ItemStateManager view of the states in the attic
+     *
+     * @see TransientItemStateManager#getAttic
+     */
+    private class AtticItemStateManager implements ItemStateManager {
 
-        Attic() {
-            super(ReferenceMap.HARD, ReferenceMap.HARD);
+        AtticItemStateManager() {
         }
 
         /**
@@ -340,7 +364,7 @@
         public ItemState getItemState(ItemId id)
                 throws NoSuchItemStateException, ItemStateException {
 
-            ItemState state = retrieve(id);
+            ItemState state = atticMap.get(id);
             if (state != null) {
                 return state;
             } else {
@@ -352,7 +376,7 @@
          * {@inheritDoc}
          */
         public boolean hasItemState(ItemId id) {
-            return isCached(id);
+            return atticMap.contains(id);
         }
 
         /**
@@ -360,7 +384,7 @@
          */
         public NodeReferences getNodeReferences(NodeReferencesId id)
                 throws NoSuchItemStateException, ItemStateException {
-
+            // n/a
             throw new ItemStateException("getNodeReferences() not implemented");
         }
 
@@ -368,6 +392,7 @@
          * {@inheritDoc}
          */
         public boolean hasNodeReferences(NodeReferencesId id) {
+            // n/a
             return false;
         }
     }

Modified: incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/virtual/AbstractVISProvider.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/virtual/AbstractVISProvider.java?rev=227042&r1=227041&r2=227042&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/virtual/AbstractVISProvider.java (original)
+++ incubator/jackrabbit/trunk/core/src/java/org/apache/jackrabbit/core/virtual/AbstractVISProvider.java Tue Aug  2 09:56:07 2005
@@ -16,7 +16,6 @@
  */
 package org.apache.jackrabbit.core.virtual;
 
-import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.jackrabbit.Constants;
 import org.apache.jackrabbit.core.ItemId;
 import org.apache.jackrabbit.core.NodeId;
@@ -33,6 +32,7 @@
 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.ItemStateReferenceMap;
 import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.uuid.UUID;
 import org.apache.log4j.Logger;
@@ -40,7 +40,6 @@
 import javax.jcr.RepositoryException;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.Map;
 
 /**
  * This Class implements a virtual item state provider, in order to expose the
@@ -70,7 +69,7 @@
     /**
      * the cache node states. key=ItemId, value=ItemState
      */
-    private Map nodes = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT);
+    private ItemStateReferenceMap nodes = new ItemStateReferenceMap();
 
     /**
      * Creates an abstract virtual item state provider
@@ -98,7 +97,7 @@
      */
     public boolean hasItemState(ItemId id) {
         if (id instanceof NodeId) {
-            if (nodes.containsKey(id)) {
+            if (nodes.contains(id)) {
                 return true;
             } else if (id.equals(rootNodeId)) {
                 return true;
@@ -118,7 +117,7 @@
 
         if (id instanceof NodeId) {
             ItemState s;
-            if (nodes.containsKey(id)) {
+            if (nodes.contains(id)) {
                 s = (ItemState) nodes.get(id);
             } else if (id.equals(rootNodeId)) {
                 s = getRootState();
@@ -302,7 +301,7 @@
      */
     protected NodeState cache(NodeState state) {
         if (state != null) {
-            nodes.put(state.getId(), state);
+            nodes.put(state);
             state.addListener(this);
             log.debug("item added to cache. size=" + nodes.size());
         }
@@ -310,12 +309,12 @@
     }
 
     /**
-     * removes the nodes state from the cache
+     * removes the node state from the cache
      *
      * @param id
      */
-    protected NodeState evict(NodeId id) {
-        return (NodeState) nodes.remove(id);
+    protected void evict(NodeId id) {
+        nodes.remove(id);
     }
 
     /**