jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ste...@apache.org
Subject svn commit: rev 76003 - in incubator/jackrabbit/trunk: . src/conf src/java/org/apache/jackrabbit/core src/java/org/apache/jackrabbit/core/nodetype src/java/org/apache/jackrabbit/core/state
Date Tue, 16 Nov 2004 18:14:39 GMT
Author: stefan
Date: Tue Nov 16 10:14:39 2004
New Revision: 76003

Added:
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java   (contents, props changed)
Modified:
   incubator/jackrabbit/trunk/project.xml
   incubator/jackrabbit/trunk/src/conf/repository.xml
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/RepositoryImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Test.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeDefId.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java
   incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateManager.java
Log:


Modified: incubator/jackrabbit/trunk/project.xml
==============================================================================
--- incubator/jackrabbit/trunk/project.xml	(original)
+++ incubator/jackrabbit/trunk/project.xml	Tue Nov 16 10:14:39 2004
@@ -249,7 +249,8 @@
     </dependency>
     <!--
       cqfs-jackrabbit and cqfs are optional runtime dependencies 
-      (an alternative FileSystem implementation)
+      (an alternative FileSystem implementation); 
+      commons-logging is a dependency of cqfs
     -->
     <dependency>
       <groupId>cqfs</groupId>
@@ -262,6 +263,10 @@
       <artifactId>cqfs</artifactId>
       <version>3.5.6</version>
       <url>http://www.day.com/maven/cqfs/jars/cqfs-3.5.6.jar</url>
+    </dependency>
+    <dependency>
+      <id>commons-logging</id>
+      <version>1.0</version>
     </dependency>
   </dependencies>
 

Modified: incubator/jackrabbit/trunk/src/conf/repository.xml
==============================================================================
--- incubator/jackrabbit/trunk/src/conf/repository.xml	(original)
+++ incubator/jackrabbit/trunk/src/conf/repository.xml	Tue Nov 16 10:14:39 2004
@@ -98,17 +98,17 @@
             virtual file system of the workspace:
             class: FQN of class implementing FileSystem interface
         -->
-        <!--
         <FileSystem class="com.day.jackrabbit.fs.cq.CQFileSystem">
             <param name="path" value="${wsp.home}/wspStore.dat"/>
             <param name="autoRepair" value="false"/>
             <param name="blockSize" value="128"/>
             <param name="autoSync" value="false"/>
         </FileSystem>
-        -->
+        <!--
         <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
             <param name="path" value="${wsp.home}"/>
         </FileSystem>
+        -->
         <!--
             persistence manager of the workspace:
             class: FQN of class implementing PersistenceManager interface

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemImpl.java	Tue Nov 16 10:14:39 2004
@@ -100,9 +100,10 @@
     protected final SessionItemStateManager itemStateMgr;
 
     /**
-     * Listeners (soft references)
+     * Listeners (weak references)
      */
-    protected final Map listeners = Collections.synchronizedMap(new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.SOFT));
+    protected final Map listeners =
+            Collections.synchronizedMap(new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
 
     /**
      * Package private constructor.
@@ -145,8 +146,16 @@
         super.finalize();
     }
 
-    protected void checkItemState() throws InvalidItemStateException {
-        // check status for read operation
+    /**
+     * Performs a sanity check on this item and the associated session.
+     *
+     * @throws RepositoryException if this item has been rendered invalid for some reason
+     */
+    protected void sanityCheck() throws RepositoryException {
+        // check session status
+        session.sanityCheck();
+
+        // check status of this item for read operation
         switch (status) {
             case STATUS_NORMAL:
             case STATUS_MODIFIED:
@@ -973,7 +982,7 @@
      */
     public void remove() throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         Path.PathElement thisName = getPrimaryPath().getNameElement();
 
@@ -1025,153 +1034,156 @@
     /**
      * @see Item#save
      */
-    public synchronized void save()
+    public void save()
             throws AccessDeniedException, LockException,
             ConstraintViolationException, InvalidItemStateException,
             ReferentialIntegrityException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
-        try {
-            /**
-             * turn on temporary path caching for better performance
-             * (assuming that the paths won't change during this save() call)
-             */
-            itemStateMgr.enablePathCaching(true);
-
-            // build list of transient states that should be persisted
-            Collection dirty = getTransientStates();
-            if (dirty.size() == 0) {
-                // no transient items, nothing to do here
-                return;
-            }
+        // synchronize on this session
+        synchronized(session) {
+            try {
+                /**
+                 * turn on temporary path caching for better performance
+                 * (assuming that the paths won't change during this save() call)
+                 */
+                itemStateMgr.enablePathCaching(true);
 
-            ItemState transientState;
+                // build list of transient states that should be persisted
+                Collection dirty = getTransientStates();
+                if (dirty.size() == 0) {
+                    // no transient items, nothing to do here
+                    return;
+                }
 
-            /**
-             * check that parent node is also included in the dirty items list
-             * if dirty node was removed or added (adding/removing a parent/child
-             * link requires that both parent and child are saved)
-             */
-            Iterator iter = dirty.iterator();
-            while (iter.hasNext()) {
-                transientState = (ItemState) iter.next();
-                if (transientState.isNode()) {
-                    NodeState nodeState = (NodeState) transientState;
-                    ArrayList dirtyParents = new ArrayList();
-                    // removed parents
-                    dirtyParents.addAll(nodeState.getRemovedParentUUIDs());
-                    // added parents
-                    dirtyParents.addAll(nodeState.getAddedParentUUIDs());
-                    Iterator parentsIter = dirtyParents.iterator();
-                    while (parentsIter.hasNext()) {
-                        NodeId id = new NodeId((String) parentsIter.next());
-                        NodeState parentState = null;
-                        try {
-                            parentState = (NodeState) itemStateMgr.getTransientItemState(id);
-                        } catch (ItemStateException ise) {
-                            // should never get here...
-                            String msg = "inconsistency: failed to retrieve transient state for " + itemMgr.safeGetJCRPath(id);
-                            log.error(msg);
-                            throw new RepositoryException(msg);
-                        }
-                        // check if parent is also going to be saved
-                        if (!dirty.contains(parentState)) {
-                            // need to save the parent too
-                            String msg = itemMgr.safeGetJCRPath(id) + " needs to be saved also.";
-                            log.error(msg);
-                            throw new RepositoryException(msg);
+                ItemState transientState;
+
+                /**
+                 * check that parent node is also included in the dirty items list
+                 * if dirty node was removed or added (adding/removing a parent/child
+                 * link requires that both parent and child are saved)
+                 */
+                Iterator iter = dirty.iterator();
+                while (iter.hasNext()) {
+                    transientState = (ItemState) iter.next();
+                    if (transientState.isNode()) {
+                        NodeState nodeState = (NodeState) transientState;
+                        ArrayList dirtyParents = new ArrayList();
+                        // removed parents
+                        dirtyParents.addAll(nodeState.getRemovedParentUUIDs());
+                        // added parents
+                        dirtyParents.addAll(nodeState.getAddedParentUUIDs());
+                        Iterator parentsIter = dirtyParents.iterator();
+                        while (parentsIter.hasNext()) {
+                            NodeId id = new NodeId((String) parentsIter.next());
+                            NodeState parentState = null;
+                            try {
+                                parentState = (NodeState) itemStateMgr.getTransientItemState(id);
+                            } catch (ItemStateException ise) {
+                                // should never get here...
+                                String msg = "inconsistency: failed to retrieve transient state for " + itemMgr.safeGetJCRPath(id);
+                                log.error(msg);
+                                throw new RepositoryException(msg);
+                            }
+                            // check if parent is also going to be saved
+                            if (!dirty.contains(parentState)) {
+                                // need to save the parent too
+                                String msg = itemMgr.safeGetJCRPath(id) + " needs to be saved also.";
+                                log.error(msg);
+                                throw new RepositoryException(msg);
+                            }
                         }
                     }
                 }
-            }
 
-            /**
-             * validate access and node type constraints
-             * (this will also validate child removals)
-             */
-            validateTransientItems(dirty.iterator());
-
-            WorkspaceImpl wsp = (WorkspaceImpl) session.getWorkspace();
-
-            // list of events that are generated by saved changes
-            ObservationManagerFactory obsFactory = rep.getObservationManagerFactory(wsp.getName());
-            EventStateCollection events = obsFactory.createEventStateCollection(session,
-                    session.getItemStateManager(), session.getHierarchyManager());
-
-            /**
-             * we need to make sure that we are not interrupted while
-             * verifying/persisting node references
-             */
-            ReferenceManager refMgr = wsp.getReferenceManager();
-            synchronized (refMgr) {
                 /**
-                 * build list of transient descendents in the attic
-                 * (i.e. those marked as 'removed')
+                 * validate access and node type constraints
+                 * (this will also validate child removals)
                  */
-                Collection removed = getRemovedStates();
+                validateTransientItems(dirty.iterator());
 
-                /**
-                 * referential integrity checks:
-                 * make sure that a referenced node cannot be removed and
-                 * that all references are updated and persisted
-                 */
-                checkReferences(dirty.iterator(), removed.iterator(), refMgr);
+                WorkspaceImpl wsp = (WorkspaceImpl) session.getWorkspace();
+
+                // list of events that are generated by saved changes
+                ObservationManagerFactory obsFactory = rep.getObservationManagerFactory(wsp.getName());
+                EventStateCollection events = obsFactory.createEventStateCollection(session,
+                        session.getItemStateManager(), session.getHierarchyManager());
 
                 /**
-                 * create event states for the affected item states and
-                 * prepare them for event dispatch (this step is necessary in order
-                 * to check access rights on items that will be removed)
-                 *
-                 * todo consolidate event generating and dispatching code (ideally one method call after save has succeeded)
+                 * we need to make sure that we are not interrupted while
+                 * verifying/persisting node references
                  */
-                events.createEventStates(dirty);
-                events.createEventStates(removed);
-                events.prepare();
-
-                // definitively remove transient items marked as 'removed'
-                removeTransientItems(removed.iterator());
+                ReferenceManager refMgr = wsp.getReferenceManager();
+                synchronized (refMgr) {
+                    /**
+                     * build list of transient descendents in the attic
+                     * (i.e. those marked as 'removed')
+                     */
+                    Collection removed = getRemovedStates();
 
-                // dispose the transient states marked 'removed'
-                iter = removed.iterator();
-                while (iter.hasNext()) {
-                    transientState = (ItemState) iter.next();
                     /**
-                     * dispose the transient state, it is no longer used
-                     * this will indirectly (through stateDiscarded listener method)
-                     * permanently invalidate the wrapping Item instance
+                     * referential integrity checks:
+                     * make sure that a referenced node cannot be removed and
+                     * that all references are updated and persisted
                      */
-                    itemStateMgr.disposeTransientItemStateInAttic(transientState);
-                }
+                    checkReferences(dirty.iterator(), removed.iterator(), refMgr);
 
-                // initialize version histories for new nodes (might generate new transient state)
-                if (initVersionHistories(dirty.iterator())) {
                     /**
-                     * re-build the list of transient states because the previous call
-                     * generated new transient state
+                     * create event states for the affected item states and
+                     * prepare them for event dispatch (this step is necessary in order
+                     * to check access rights on items that will be removed)
+                     *
+                     * todo consolidate event generating and dispatching code (ideally one method call after save has succeeded)
                      */
-                    dirty = getTransientStates();
-                }
+                    events.createEventStates(dirty);
+                    events.createEventStates(removed);
+                    events.prepare();
+
+                    // definitively remove transient items marked as 'removed'
+                    removeTransientItems(removed.iterator());
+
+                    // dispose the transient states marked 'removed'
+                    iter = removed.iterator();
+                    while (iter.hasNext()) {
+                        transientState = (ItemState) iter.next();
+                        /**
+                         * dispose the transient state, it is no longer used
+                         * this will indirectly (through stateDiscarded listener method)
+                         * permanently invalidate the wrapping Item instance
+                         */
+                        itemStateMgr.disposeTransientItemStateInAttic(transientState);
+                    }
 
-                // persist 'new' or 'modified' transient states
-                persistTransientItems(dirty.iterator());
-            } // synchronized(refMgr)
+                    // initialize version histories for new nodes (might generate new transient state)
+                    if (initVersionHistories(dirty.iterator())) {
+                        /**
+                         * re-build the list of transient states because the previous call
+                         * generated new transient state
+                         */
+                        dirty = getTransientStates();
+                    }
 
-            // now it is safe to dispose the transient states
-            iter = dirty.iterator();
-            while (iter.hasNext()) {
-                transientState = (ItemState) iter.next();
-                // dispose the transient state, it is no longer used
-                itemStateMgr.disposeTransientItemState(transientState);
-            }
+                    // persist 'new' or 'modified' transient states
+                    persistTransientItems(dirty.iterator());
+                } // synchronized(refMgr)
 
-            // all changes are persisted, now dispatch events
-            // forward this to the session to let it decide on the right time for those
-            // events to be dispatched in case of transactional support
-            session.dispatch(events);
-        } finally {
-            // turn off temporary path caching
-            itemStateMgr.enablePathCaching(false);
+                // now it is safe to dispose the transient states
+                iter = dirty.iterator();
+                while (iter.hasNext()) {
+                    transientState = (ItemState) iter.next();
+                    // dispose the transient state, it is no longer used
+                    itemStateMgr.disposeTransientItemState(transientState);
+                }
+
+                // all changes are persisted, now dispatch events
+                // forward this to the session to let it decide on the right time for those
+                // events to be dispatched in case of transactional support
+                session.dispatch(events);
+            } finally {
+                // turn off temporary path caching
+                itemStateMgr.enablePathCaching(false);
+            }
         }
     }
 
@@ -1181,7 +1193,7 @@
     public synchronized void refresh(boolean keepChanges)
             throws InvalidItemStateException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         if (keepChanges) {
             /** todo FIXME should reset Item#status field to STATUS_NORMAL
@@ -1277,7 +1289,7 @@
     public Item getAncestor(int degree)
             throws ItemNotFoundException, AccessDeniedException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         if (degree == 0) {
             return itemMgr.getRootNode();

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/ItemManager.java	Tue Nov 16 10:14:39 2004
@@ -69,7 +69,7 @@
     private Map itemCache;
 
     /**
-     * Creates a new per-workspace instance <code>ItemManager</code> instance.
+     * Creates a new per-session instance <code>ItemManager</code> instance.
      *
      * @param itemStateProvider the item state provider associated with
      *                          the new instance
@@ -84,8 +84,8 @@
         this.session = session;
         this.rootNodeDef = rootNodeDef;
         rootNodeId = new NodeId(rootNodeUUID);
-        // setup item cache with soft references to items
-        itemCache = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT);
+        // setup item cache with weak references to items
+        itemCache = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.WEAK);
     }
 
     /**
@@ -134,6 +134,13 @@
         ps.println();
     }
 
+    /**
+     * Disposes this <code>ItemManager</code> and frees resources.
+     */
+    void dispose() {
+        itemCache.clear();
+    }
+
     //--------------------------------------------------< item access methods >
     /**
      * @param path
@@ -203,6 +210,9 @@
      */
     public synchronized ItemImpl getItem(ItemId id)
             throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+        // check sanity of session
+        session.sanityCheck();
+
         // check privileges
         if (!session.getAccessManager().isGranted(id, AccessManager.READ)) {
             // clear cache
@@ -235,6 +245,9 @@
      */
     synchronized boolean hasChildNodes(NodeId parentId)
             throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+        // check sanity of session
+        session.sanityCheck();
+
         // check privileges
         if (!session.getAccessManager().isGranted(parentId, AccessManager.READ)) {
             // clear cache
@@ -286,6 +299,9 @@
      */
     synchronized NodeIterator getChildNodes(NodeId parentId)
             throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+        // check sanity of session
+        session.sanityCheck();
+
         // check privileges
         if (!session.getAccessManager().isGranted(parentId, AccessManager.READ)) {
             // clear cache
@@ -340,6 +356,9 @@
      */
     synchronized boolean hasChildProperties(NodeId parentId)
             throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+        // check sanity of session
+        session.sanityCheck();
+
         // check privileges
         if (!session.getAccessManager().isGranted(parentId, AccessManager.READ)) {
             ItemImpl item = retrieveItem(parentId);
@@ -392,6 +411,9 @@
      */
     synchronized PropertyIterator getChildProperties(NodeId parentId)
             throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+        // check sanity of session
+        session.sanityCheck();
+
         // check privileges
         if (!session.getAccessManager().isGranted(parentId, AccessManager.READ)) {
             ItemImpl item = retrieveItem(parentId);

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/NodeImpl.java	Tue Nov 16 10:14:39 2004
@@ -227,7 +227,7 @@
                 propState.setValues(genValues);
             } else if (def.getDefaultValues() != null) {
                 Value[] vals = def.getDefaultValues();
-                if (vals.length>0) {
+                if (vals.length > 0) {
                     int length = (def.isMultiple() ? vals.length : 1);
                     InternalValue[] defVals = new InternalValue[length];
                     for (int i = 0; i < length; i++) {
@@ -681,7 +681,7 @@
     public Property internalSetProperty(QName name, InternalValue value)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         int type;
         if (value == null) {
@@ -714,7 +714,7 @@
     protected Property internalSetProperty(QName name, InternalValue[] values)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         int type;
         if (values == null || values.length == 0) {
@@ -754,7 +754,7 @@
      */
     public NodeImpl getNode(QName name, int index) throws ItemNotFoundException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         Path nodePath;
         try {
@@ -831,7 +831,7 @@
     public PropertyImpl getProperty(QName name)
             throws ItemNotFoundException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyId propId = new PropertyId(((NodeState) state).getUUID(), name);
         try {
@@ -866,7 +866,7 @@
             throws ItemExistsException, ConstraintViolationException,
             RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         return internalAddChildNode(nodeName, null);
     }
@@ -878,7 +878,7 @@
             throws ItemExistsException, NoSuchNodeTypeException,
             ConstraintViolationException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         NodeTypeImpl nt = session.getNodeTypeManager().getNodeType(nodeTypeName);
         return internalAddChildNode(nodeName, nt);
@@ -897,7 +897,7 @@
      */
     public PropertyImpl setProperty(String name, QName value) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyImpl prop = getOrCreateProperty(name, PropertyType.NAME, false);
         prop.setValue(value);
@@ -917,7 +917,7 @@
      */
     public PropertyImpl setProperty(String name, QName[] values) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyImpl prop = getOrCreateProperty(name, PropertyType.NAME, true);
         prop.setValue(values);
@@ -938,7 +938,7 @@
     public PropertyImpl setProperty(QName name, Value[] values)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         int type;
         if (values == null || values.length == 0) {
@@ -965,7 +965,7 @@
     public PropertyImpl setProperty(QName name, Value value)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         int type = (value == null) ? PropertyType.STRING : value.getType();
         PropertyImpl prop = getOrCreateProperty(name, type, false);
@@ -1014,7 +1014,7 @@
      */
     public void accept(ItemVisitor visitor) throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         visitor.visit(this);
     }
@@ -1025,7 +1025,7 @@
     public Node getParent()
             throws ItemNotFoundException, AccessDeniedException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if root node
         NodeState thisState = (NodeState) state;
@@ -1047,7 +1047,7 @@
             throws ItemExistsException, PathNotFoundException,
             ConstraintViolationException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         return internalAddNode(relPath, null);
     }
@@ -1060,7 +1060,7 @@
             NoSuchNodeTypeException, ConstraintViolationException,
             RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         NodeTypeImpl nt = (NodeTypeImpl) session.getNodeTypeManager().getNodeType(nodeTypeName);
         return internalAddNode(relPath, nt);
@@ -1196,7 +1196,7 @@
     public Property setProperty(String name, Value[] values)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         int type;
         if (values == null || values.length == 0) {
@@ -1215,7 +1215,7 @@
     public Property setProperty(String name, String[] values)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyImpl prop = getOrCreateProperty(name, PropertyType.STRING, true);
         prop.setValue(values);
@@ -1227,7 +1227,7 @@
      */
     public Property setProperty(String name, String value) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyImpl prop = getOrCreateProperty(name, PropertyType.STRING, false);
         prop.setValue(value);
@@ -1240,7 +1240,7 @@
     public Property setProperty(String name, Value value)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         int type = (value == null) ? PropertyType.STRING : value.getType();
         PropertyImpl prop = getOrCreateProperty(name, type, false);
@@ -1254,7 +1254,7 @@
     public Property setProperty(String name, InputStream value)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyImpl prop = getOrCreateProperty(name, PropertyType.BINARY, false);
         prop.setValue(value);
@@ -1267,7 +1267,7 @@
     public Property setProperty(String name, boolean value)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyImpl prop = getOrCreateProperty(name, PropertyType.BOOLEAN, false);
         prop.setValue(value);
@@ -1280,7 +1280,7 @@
     public Property setProperty(String name, double value)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyImpl prop = getOrCreateProperty(name, PropertyType.DOUBLE, false);
         prop.setValue(value);
@@ -1293,7 +1293,7 @@
     public Property setProperty(String name, long value)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyImpl prop = getOrCreateProperty(name, PropertyType.LONG, false);
         prop.setValue(value);
@@ -1306,7 +1306,7 @@
     public Property setProperty(String name, Calendar value)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyImpl prop = getOrCreateProperty(name, PropertyType.DATE, false);
         prop.setValue(value);
@@ -1319,7 +1319,7 @@
     public Property setProperty(String name, Node value)
             throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyImpl prop = getOrCreateProperty(name, PropertyType.REFERENCE, false);
         prop.setValue(value);
@@ -1332,7 +1332,7 @@
     public Node getNode(String relPath)
             throws PathNotFoundException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         Path nodePath;
         try {
@@ -1361,7 +1361,7 @@
      */
     public NodeIterator getNodes() throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         /**
          * IMPORTANT:
@@ -1388,7 +1388,7 @@
      */
     public PropertyIterator getProperties() throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         /**
          * IMPORTANT:
@@ -1416,7 +1416,7 @@
     public Property getProperty(String relPath)
             throws PathNotFoundException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         Path propPath;
         try {
@@ -1456,7 +1456,7 @@
      */
     public boolean hasNodes() throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         /**
          * hasNodes respects the access rights
@@ -1472,7 +1472,7 @@
      */
     public boolean hasProperties() throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         /**
          * hasProperties respects the access rights
@@ -1503,7 +1503,7 @@
      */
     public boolean isNodeType(QName ntName) throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         if (ntName.equals(nodeType.getQName())) {
             return true;
@@ -1561,7 +1561,7 @@
             throws NoSuchNodeTypeException, ConstraintViolationException,
             RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!safeIsCheckedOut()) {
@@ -1682,7 +1682,7 @@
             throws NoSuchNodeTypeException, ConstraintViolationException,
             RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!safeIsCheckedOut()) {
@@ -1799,7 +1799,7 @@
      */
     public boolean canAddMixin(String mixinName) throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!safeIsCheckedOut()) {
@@ -1890,7 +1890,7 @@
      */
     public NodeIterator getNodes(String namePattern) throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         ArrayList nodes = new ArrayList();
         // traverse children using a special filtering 'collector'
@@ -1903,7 +1903,7 @@
      */
     public PropertyIterator getProperties(String namePattern) throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         ArrayList properties = new ArrayList();
         // traverse children using a special filtering 'collector'
@@ -1916,7 +1916,7 @@
      */
     public Item getPrimaryItem() throws ItemNotFoundException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyIterator propIter = getProperties();
         // check properties first
@@ -1956,7 +1956,7 @@
      */
     public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         if (!isNodeType(NodeTypeRegistry.MIX_REFERENCEABLE)) {
             throw new UnsupportedRepositoryOperationException();
@@ -1971,7 +1971,7 @@
      */
     public String internalGetUUID() throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
         return ((NodeState) state).getUUID();
     }
 

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/PropertyImpl.java	Tue Nov 16 10:14:39 2004
@@ -125,7 +125,7 @@
     protected void internalSetValue(InternalValue[] values, int type)
             throws ConstraintViolationException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check for null value
         if (values == null) {
@@ -185,7 +185,7 @@
      */
     public void setValue(QName name) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -238,7 +238,7 @@
      */
     public void setValue(QName[] names) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -303,7 +303,7 @@
     public InternalValue[] internalGetValues() throws RepositoryException {
 
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (!definition.isMultiple()) {
@@ -322,7 +322,7 @@
      */
     public InternalValue internalGetValue() throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (definition.isMultiple()) {
@@ -339,7 +339,7 @@
      */
     public Value[] getValues() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (!definition.isMultiple()) {
@@ -360,7 +360,7 @@
      */
     public Value getValue() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (definition.isMultiple()) {
@@ -383,7 +383,7 @@
      */
     public String getString() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (definition.isMultiple()) {
@@ -398,7 +398,7 @@
      */
     public InputStream getStream() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (definition.isMultiple()) {
@@ -413,7 +413,7 @@
      */
     public long getLong() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (definition.isMultiple()) {
@@ -435,7 +435,7 @@
      */
     public double getDouble() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (definition.isMultiple()) {
@@ -458,7 +458,7 @@
      */
     public Calendar getDate() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (definition.isMultiple()) {
@@ -481,7 +481,7 @@
      */
     public boolean getBoolean() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (definition.isMultiple()) {
@@ -504,7 +504,7 @@
      */
     public Node getNode() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (definition.isMultiple()) {
@@ -527,7 +527,7 @@
      */
     public void setValue(Calendar date) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -572,7 +572,7 @@
      */
     public void setValue(double number) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -612,7 +612,7 @@
      */
     public void setValue(InputStream stream) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -663,7 +663,7 @@
      */
     public void setValue(String string) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -707,7 +707,7 @@
      */
     public void setValue(String[] strings) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -758,7 +758,7 @@
      */
     public void setValue(boolean b) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -798,7 +798,7 @@
      */
     public void setValue(Node target) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -850,7 +850,7 @@
      */
     public void setValue(long number) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -890,7 +890,7 @@
      */
     public synchronized void setValue(Value value) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -936,7 +936,7 @@
      */
     public void setValue(Value[] values) throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check if versioning allows write
         if (!((NodeImpl) getParent()).safeIsCheckedOut()) {
@@ -989,7 +989,7 @@
      */
     public long getLength() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (definition.isMultiple()) {
@@ -1041,7 +1041,7 @@
      */
     public long[] getLengths() throws ValueFormatException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         // check multi-value flag
         if (!definition.isMultiple()) {
@@ -1126,7 +1126,7 @@
      */
     public void accept(ItemVisitor visitor) throws RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         visitor.visit(this);
     }
@@ -1137,7 +1137,7 @@
     public Node getParent()
             throws ItemNotFoundException, AccessDeniedException, RepositoryException {
         // check state of this instance
-        checkItemState();
+        sanityCheck();
 
         PropertyState thisState = (PropertyState) state;
         return (Node) itemMgr.getItem(new NodeId(thisState.getParentUUID()));

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	Tue Nov 16 10:14:39 2004
@@ -29,6 +29,7 @@
 import org.apache.jackrabbit.core.util.uuid.UUID;
 import org.apache.jackrabbit.core.version.PersistentVersionManager;
 import org.apache.log4j.Logger;
+import org.apache.commons.collections.ReferenceMap;
 
 import javax.jcr.*;
 import javax.jcr.observation.Event;
@@ -42,7 +43,7 @@
 /**
  * A <code>RepositoryImpl</code> ...
  */
-public class RepositoryImpl implements Repository, EventListener {
+public class RepositoryImpl implements Repository, SessionListener, EventListener {
 
     private static Logger log = Logger.getLogger(RepositoryImpl.class);
 
@@ -94,14 +95,18 @@
     // map of workspace names and workspace configurations
     private final HashMap wspConfigs = new HashMap();
 
-    // map of workspace names and workspace item state managers
-    // (might be shared among multiple workspace instances representing
-    // the same named workspace, i.e. the same physical storage)
+    /**
+     * map of workspace names and workspace item state managers
+     * (might be shared among multiple workspace instances representing
+     * the same named workspace, i.e. the same physical storage)
+     */
     private final HashMap wspStateMgrs = new HashMap();
 
-    // map of workspace names and workspace reference managers
-    // (might be shared among multiple workspace instances representing
-    // the same named workspace, i.e. the same physical storage)
+    /**
+     * map of workspace names and workspace reference managers
+     * (might be shared among multiple workspace instances representing
+     * the same named workspace, i.e. the same physical storage)
+     */
     private final HashMap wspRefMgrs = new HashMap();
 
     // map of workspace names and observation managers
@@ -113,6 +118,12 @@
     // map of workspace names and system sessions
     private final HashMap wspSystemSessions = new HashMap();
 
+    /**
+     * active sessions (weak references)
+     */
+    private final ReferenceMap activeSessions =
+            new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK);
+
     // misc. statistics
     private long nodesCount = 0;
     private long propsCount = 0;
@@ -274,7 +285,7 @@
          */
 
         // check system root node of system workspace
-        // (by now, we just create a system root node in all workspaces)
+        // (for now, we just create a system root node in all workspaces)
         Iterator wspNames = wspConfigs.keySet().iterator();
         while (wspNames.hasNext()) {
             String wspName = (String) wspNames.next();
@@ -578,26 +589,50 @@
             searchMgr.close();
         }
 
-        /**
-         * todo close sessions, close item state mgr's, free resources, etc.
-         */
+        // close active user sessions
+        for (Iterator it = activeSessions.values().iterator(); it.hasNext();) {
+            SessionImpl session = (SessionImpl) it.next();
+            session.removeListener(this);
+            session.logout();
+        }
+        activeSessions.clear();
+
+        // close system sessions
+        for (Iterator it = wspSystemSessions.values().iterator(); it.hasNext();) {
+            SessionImpl session = (SessionImpl) it.next();
+            session.removeListener(this);
+            session.logout();
+        }
+        wspSystemSessions.clear();
+
+        // dispose persistent item state  mgr's
+        for (Iterator it = wspStateMgrs.values().iterator(); it.hasNext();) {
+            PersistentItemStateManager stateMgr =
+                    (PersistentItemStateManager) it.next();
+            stateMgr.dispose();
+        }
 
+        // shutdown persistence managers & file systems
         for (Iterator it = wspConfigs.values().iterator(); it.hasNext();) {
             WorkspaceConfig wspConfig = (WorkspaceConfig) it.next();
             try {
                 // close persistence manager
                 wspConfig.getPersistenceManager().close();
             } catch (Exception e) {
-                log.error("Error while closing persistence manager of workspace " + wspConfig.getName(), 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);
+                log.error("error while closing filesystem of workspace " + wspConfig.getName(), e);
             }
         }
 
+        /**
+         * todo further cleanup tasks, free resources, etc.
+         */
+
         // persist repository properties
         try {
             storeRepProps();
@@ -609,7 +644,7 @@
             // close repository file system
             repStore.close();
         } catch (FileSystemException e) {
-            log.error("Error while closing repository filesystem", e);
+            log.error("error while closing repository filesystem", e);
         }
 
         // make sure this instance is not used anymore
@@ -729,11 +764,15 @@
         }
         if (credentials == null) {
             // anonymous login
-            return new XASessionImpl(this, ANONYMOUS_CREDENTIALS, wspConfig, txMgr);
+            SessionImpl ses = new XASessionImpl(this, ANONYMOUS_CREDENTIALS, wspConfig, txMgr);
+            activeSessions.put(ses, ses);
+            return ses;
         } else if (credentials instanceof SimpleCredentials) {
             // username/password credentials
             // @todo implement authentication/authorization
-            return new XASessionImpl(this, credentials, wspConfig, txMgr);
+            Session ses = new XASessionImpl(this, credentials, wspConfig, txMgr);
+            activeSessions.put(ses, ses);
+            return ses;
         } else {
             String msg = "login failed: incompatible credentials";
             log.error(msg);
@@ -747,6 +786,15 @@
     public Session login(String workspaceName)
             throws LoginException, NoSuchWorkspaceException, RepositoryException {
         return login(null, workspaceName);
+    }
+
+    //------------------------------------------------------< SessionListener >
+    /**
+     * @see SessionListener#loggedOut(SessionImpl)
+     */
+    public void loggedOut(SessionImpl session) {
+        // remove session from active sessions
+        activeSessions.remove(session);
     }
 
     //--------------------------------------------------------< EventListener >

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java	Tue Nov 16 10:14:39 2004
@@ -15,6 +15,7 @@
  */
 package org.apache.jackrabbit.core;
 
+import org.apache.commons.collections.ReferenceMap;
 import org.apache.jackrabbit.core.config.WorkspaceConfig;
 import org.apache.jackrabbit.core.nodetype.*;
 import org.apache.jackrabbit.core.observation.EventStateCollection;
@@ -37,9 +38,7 @@
 import java.io.InputStream;
 import java.io.PrintStream;
 import java.security.AccessControlException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.*;
 
 /**
  * A <code>SessionImpl</code> ...
@@ -57,6 +56,11 @@
     public static final String SET_PROPERTY_ACTION = "set_property";
 
     /**
+     * flag indicating whether this session is alive
+     */
+    protected boolean alive;
+
+    /**
      * the repository that issued this session
      */
     protected final RepositoryImpl rep;
@@ -65,6 +69,7 @@
      * the user ID that was used to acquire this session
      */
     protected final String userId;
+
     /**
      * the attibutes of this session
      */
@@ -111,6 +116,11 @@
     protected final VersionManager versionMgr;
 
     /**
+     * Listeners (weak references)
+     */
+    protected final Map listeners = new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK);
+
+    /**
      * Protected constructor.
      *
      * @param rep
@@ -120,6 +130,7 @@
     protected SessionImpl(RepositoryImpl rep, Credentials credentials,
                           WorkspaceConfig wspConfig)
             throws RepositoryException {
+        alive = true;
         this.rep = rep;
         if (credentials instanceof SimpleCredentials) {
             SimpleCredentials sc = (SimpleCredentials) credentials;
@@ -168,6 +179,7 @@
      */
     protected SessionImpl(RepositoryImpl rep, String userId, WorkspaceConfig wspConfig)
             throws RepositoryException {
+        alive = true;
         this.rep = rep;
         this.userId = userId;
         nsMappings = new TransientNamespaceMappings(rep.getNamespaceRegistry());
@@ -221,6 +233,20 @@
     }
 
     /**
+     * Performs a sanity check on this session.
+     *
+     * @throws RepositoryException if this session has been rendered invalid
+     *                             for some reason (e.g. if this session has
+     *                             been closed explicitly or if it has expired)
+     */
+    protected void sanityCheck() throws RepositoryException {
+        // check session status
+        if (!alive) {
+            throw new RepositoryException("this session has been closed");
+        }
+    }
+
+    /**
      * Returns the <code>AccessManager</code> associated with this session.
      *
      * @return the <code>AccessManager</code> associated with this session
@@ -307,12 +333,59 @@
         return rep.getWorkspaceNames();
     }
 
+    /**
+     * Notify the listeners that this session has been closed.
+     */
+    protected void notifyLoggedOut() {
+        // copy listeners to array to avoid ConcurrentModificationException
+        SessionListener[] la = new SessionListener[listeners.size()];
+        Iterator iter = listeners.values().iterator();
+        int cnt = 0;
+        while (iter.hasNext()) {
+            la[cnt++] = (SessionListener) iter.next();
+        }
+        for (int i = 0; i < la.length; i++) {
+            if (la[i] != null) {
+                la[i].loggedOut(this);
+            }
+        }
+    }
+
+    /**
+     * Add a <code>SessionListener</code>
+     *
+     * @param listener the new listener to be informed on modifications
+     */
+    public void addListener(SessionListener listener) {
+        if (!listeners.containsKey(listener)) {
+            listeners.put(listener, listener);
+        }
+    }
+
+    /**
+     * Remove a <code>SessionListener</code>
+     *
+     * @param listener an existing listener
+     */
+    public void removeListener(SessionListener listener) {
+        listeners.remove(listener);
+    }
+
     //--------------------------------------------------------------< Session >
     /**
      * @see Session#checkPermission(String, String)
      */
     public void checkPermission(String absPath, String actions)
             throws AccessControlException {
+        // check sanity of this session
+        try {
+            sanityCheck();
+        } catch (RepositoryException re) {
+            String msg = "failed to check READ permission on " + absPath;
+            log.warn(msg, re);
+            throw new AccessControlException(READ_ACTION);
+        }
+
         // build the set of actions to be checked
         String[] strings = actions.split(",");
         HashSet set = new HashSet();
@@ -461,6 +534,9 @@
      */
     public Session impersonate(Credentials otherCredentials)
             throws LoginException, RepositoryException {
+        // check sanity of this session
+        sanityCheck();
+
         // @todo reimplement impersonate(Credentials) correctly
 
         // check if the credentials of this session allow to 'impersonate'
@@ -483,6 +559,9 @@
      * @see Session#getRootNode
      */
     public Node getRootNode() throws RepositoryException {
+        // check sanity of this session
+        sanityCheck();
+
         return getItemManager().getRootNode();
     }
 
@@ -490,6 +569,9 @@
      * @see Session#getNodeByUUID(String)
      */
     public Node getNodeByUUID(String uuid) throws ItemNotFoundException, RepositoryException {
+        // check sanity of this session
+        sanityCheck();
+
         try {
             NodeImpl node = (NodeImpl) getItemManager().getItem(new NodeId(uuid));
             if (node.isNodeType(NodeTypeRegistry.MIX_REFERENCEABLE)) {
@@ -507,6 +589,9 @@
      * @see Session#getItem(String)
      */
     public Item getItem(String absPath) throws PathNotFoundException, RepositoryException {
+        // check sanity of this session
+        sanityCheck();
+
         try {
             return getItemManager().getItem(Path.create(absPath, getNamespaceResolver(), true));
         } catch (AccessDeniedException ade) {
@@ -523,6 +608,9 @@
      */
     public boolean itemExists(String absPath) {
         try {
+            // check sanity of this session
+            sanityCheck();
+
             getItemManager().getItem(Path.create(absPath, getNamespaceResolver(), true));
             return true;
         } catch (RepositoryException re) {
@@ -539,6 +627,9 @@
     public void save() throws AccessDeniedException, LockException,
             ConstraintViolationException, InvalidItemStateException,
             RepositoryException {
+        // check sanity of this session
+        sanityCheck();
+
         getItemManager().getRootNode().save();
     }
 
@@ -546,9 +637,12 @@
      * @see Session#refresh(boolean)
      */
     public void refresh(boolean keepChanges) throws RepositoryException {
+        // check sanity of this session
+        sanityCheck();
+
         if (!keepChanges) {
             // optimization
-            getItemStateManager().disposeAllTransientItemStates();
+            itemStateMgr.disposeAllTransientItemStates();
             return;
         }
         getItemManager().getRootNode().refresh(keepChanges);
@@ -558,7 +652,10 @@
      * @see Session#hasPendingChanges
      */
     public boolean hasPendingChanges() throws RepositoryException {
-        return getItemStateManager().hasAnyTransientItemStates();
+        // check sanity of this session
+        sanityCheck();
+
+        return itemStateMgr.hasAnyTransientItemStates();
     }
 
     /**
@@ -567,6 +664,8 @@
     public void move(String srcAbsPath, String destAbsPath)
             throws ItemExistsException, PathNotFoundException,
             ConstraintViolationException, RepositoryException {
+        // check sanity of this session
+        sanityCheck();
 
         // check paths & get node instances
 
@@ -683,6 +782,9 @@
      * @see Session#getImportContentHandler(String)
      */
     public ContentHandler getImportContentHandler(String parentAbsPath) throws PathNotFoundException, RepositoryException {
+        // check sanity of this session
+        sanityCheck();
+
         Item item = null;
         try {
             item = getItemManager().getItem(Path.create(parentAbsPath, getNamespaceResolver(), true));
@@ -709,23 +811,14 @@
             throws IOException, PathNotFoundException, ItemExistsException,
             ConstraintViolationException, InvalidSerializedDataException,
             RepositoryException {
+        // check sanity of this session
+        sanityCheck();
+
         ImportHandler handler = (ImportHandler) getImportContentHandler(parentAbsPath);
         try {
             XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
-/*
-	    parser.setFeature("http://xml.org/sax/features/validation", true);
-	    parser.setFeature("http://apache.org/xml/features/validation/schema", true);
-	    parser.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
-*/
             parser.setContentHandler(handler);
             parser.setErrorHandler(handler);
-/*
-	    // validate against system view schema
-	    URL urlSchema = this.class.getClassLoader().getResource("javax/jcr/systemview.xsd");
-	    parser.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", urlSchema.toString());
-	    parser.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation",
-		    urlSchema.toString() + " " + "http://www.jcp.org/jcr/sv/1.0");
-*/
             parser.parse(new InputSource(in));
         } catch (SAXException se) {
             // check for wrapped repository exception
@@ -743,14 +836,26 @@
     /**
      * @see Session#logout()
      */
-    public void logout() {
+    public synchronized void logout() {
+        if (!alive) {
+            // ignore
+            return;
+        }
+
         // discard all transient changes
-        getItemStateManager().disposeAllTransientItemStates();
+        itemStateMgr.disposeAllTransientItemStates();
+        // dispose item manager
+        itemMgr.dispose();
+        // dispose workspace
+        wsp.dispose();
 
-        // @todo invalidate session, release session-scoped locks, free resources, prepare to get gc'ed etc.
+        // @todo release session-scoped locks, free resources, prepare to get gc'ed etc.
 
-        log.debug("disposing workspace...");
-        wsp.dispose();
+        // invalidate session
+        alive = false;
+
+        // finally notify listeners that session has been closed
+        notifyLoggedOut();
     }
 
     /**

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java
==============================================================================
--- (empty file)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionListener.java	Tue Nov 16 10:14:39 2004
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * The <code>SessionListener</code> interface allows an implementing
+ * object to be informed about changes on a <code>Session</code>.
+ *
+ * @see SessionImpl#addListener
+ */
+public interface SessionListener {
+
+    /**
+     * Called when a <code>Session</code> has been 'closed' by
+     * calling <code>{@link javax.jcr.Session#logout()}</code.
+     *
+     * @param session the <code>Session</code> that has been 'closed'
+     */
+    public void loggedOut(SessionImpl session);
+}

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Test.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Test.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/Test.java	Tue Nov 16 10:14:39 2004
@@ -287,14 +287,14 @@
         System.out.println();
         ((WorkspaceImpl) wsp).dump(System.out);
 
-        //wsp.exportSysView("/", new FileOutputStream("d:/temp/sv_export1.xml"), false, false);
-        wsp.exportDocView("/", new FileOutputStream("d:/temp/dv_export1.xml"), false, false);
+        wsp.exportSysView("/", new FileOutputStream("d:/temp/sv_export1.xml"), false, false);
+        //wsp.exportDocView("/", new FileOutputStream("d:/temp/dv_export1.xml"), false, false);
 
         repProps = r.getProperties();
         System.out.println("repository properties:");
         System.out.println(repProps);
 
-        ((RepositoryImpl) r).shutdown();
+        //((RepositoryImpl) r).shutdown();
     }
 
     public static void importNode(File file, Node parent) throws Exception {

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/WorkspaceImpl.java	Tue Nov 16 10:14:39 2004
@@ -154,6 +154,16 @@
         }
     }
 
+    /**
+     * Performs a sanity check on this workspace and the associated session.
+     * @throws RepositoryException if this workspace has been rendered invalid
+     * for some reason
+     */
+    protected void sanityCheck() throws RepositoryException {
+        // check session status
+        session.sanityCheck();
+    }
+
     //-----------< misc. static helper methods for cross-workspace operations >
     /**
      * @param nodePath

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeDefId.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeDefId.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeDefId.java	Tue Nov 16 10:14:39 2004
@@ -34,7 +34,7 @@
         if (def == null) {
             throw new IllegalArgumentException("ChildNodeDef argument can not be null");
         }
-        // build key (format: <declaringNodeType>/<name>/<defaultPrimaryType>/<requiredPrimaryTypes>)
+        // build key (format: <declaringNodeType>/<name>/<requiredPrimaryTypes>)
         StringBuffer sb = new StringBuffer();
 
         sb.append(def.getDeclaringNodeType().toString());
@@ -43,10 +43,6 @@
             sb.append('*');
         } else {
             sb.append(def.getName().toString());
-        }
-        sb.append('/');
-        if (def.getDefaultPrimaryType() != null) {
-            sb.append(def.getDefaultPrimaryType());
         }
         sb.append('/');
         // set of required node type names, sorted in ascending order

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/nodetype/NodeTypeRegistry.java	Tue Nov 16 10:14:39 2004
@@ -120,7 +120,8 @@
     /**
      * Listeners (soft references)
      */
-    private final Map listeners = Collections.synchronizedMap(new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.SOFT));
+    private final Map listeners =
+            Collections.synchronizedMap(new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
 
     /**
      * Create a new <code>NodeTypeRegistry</codes>

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/ItemState.java	Tue Nov 16 10:14:39 2004
@@ -83,9 +83,10 @@
     protected ItemId id;
 
     /**
-     * Listeners (soft references)
+     * Listeners (weak references)
      */
-    protected final transient Map listeners = Collections.synchronizedMap(new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.SOFT));
+    protected final transient Map listeners =
+            Collections.synchronizedMap(new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK));
 
     // the backing persistent item state (may be null)
     protected transient ItemState overlayedState;

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateManager.java
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateManager.java	(original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/state/PersistentItemStateManager.java	Tue Nov 16 10:14:39 2004
@@ -61,6 +61,14 @@
         }
     }
 
+    /**
+     * Disposes this <code>PersistentItemStateManager</code> and frees resources.
+     */
+    public void dispose() {
+        // clear cache
+        evictAll();
+    }
+
     private PersistentNodeState createPersistentRootNodeState(String rootNodeUUID,
                                                               NodeTypeRegistry ntReg)
             throws ItemStateException {

Mime
View raw message