You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by th...@apache.org on 2009/03/12 11:49:31 UTC

svn commit: r752831 - in /jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core: SessionImpl.java TransientRepository.java state/StateChangeDispatcher.java

Author: thomasm
Date: Thu Mar 12 10:49:25 2009
New Revision: 752831

URL: http://svn.apache.org/viewvc?rev=752831&view=rev
Log:
JCR-1216 Unreferenced sessions should get garbage collected

Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransientRepository.java
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/StateChangeDispatcher.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java?rev=752831&r1=752830&r2=752831&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/SessionImpl.java Thu Mar 12 10:49:25 2009
@@ -226,6 +226,12 @@
      * Retention and Hold Manager
      */
     private RetentionManager retentionManager;
+        
+    /**
+     * The stack trace knows who opened this session. It is logged
+     * if the session is finalized, but Session.logout() was never called.
+     */
+    private Exception openStackTrace = new Exception("Stack Trace");
 
     /**
      * Internal helper class for common validation checks (lock status, checkout
@@ -1542,5 +1548,17 @@
         ps.println();
         itemStateMgr.dump(ps);
     }
+    
+    /**
+     * Finalize the session. If the application doesn't close Session.logout(), 
+     * the session is closed automatically; however a warning is written to the log file, 
+     * together with the stack trace of where the session was opened.
+     */
+    public void finalize() {
+        if (alive) {
+            log.warn("Unclosed session detected. The session was opened here: ", openStackTrace);
+            logout();
+        }
+    }
 
 }

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransientRepository.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransientRepository.java?rev=752831&r1=752830&r2=752831&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransientRepository.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/TransientRepository.java Thu Mar 12 10:49:25 2009
@@ -25,14 +25,14 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Properties;
-import java.util.Set;
 
 import javax.jcr.Credentials;
-import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 
+import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.api.JackrabbitRepository;
 import org.apache.jackrabbit.core.config.ConfigurationException;
@@ -57,11 +57,6 @@
         LoggerFactory.getLogger(TransientRepository.class);
 
     /**
-     * Buffer size for copying the default repository configuration file.
-     */
-    private static final int BUFFER_SIZE = 4096;
-
-    /**
      * Resource path of the default repository configuration file.
      */
     private static final String DEFAULT_REPOSITORY_XML = "repository.xml";
@@ -123,7 +118,7 @@
      * repository instance is automatically shut down until a new session
      * is opened.
      */
-    private final Set sessions;
+    private final Map sessions = new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK);
 
     /**
      * The static repository descriptors. The default {@link RepositoryImpl}
@@ -142,7 +137,6 @@
     public TransientRepository(RepositoryFactory factory) throws IOException {
         this.factory = factory;
         this.repository = null;
-        this.sessions = new HashSet();
         this.descriptors = new Properties();
 
         // FIXME: The current RepositoryImpl class does not allow static
@@ -331,9 +325,9 @@
 
         try {
             logger.debug("Opening a new session");
-            Session session = repository.login(credentials, workspaceName);
-            sessions.add(session);
-            ((SessionImpl) session).addListener(this);
+            SessionImpl session = (SessionImpl) repository.login(credentials, workspaceName);
+            sessions.put(session, session);
+            session.addListener(this);
             logger.info("Session opened");
 
             return session;
@@ -393,7 +387,7 @@
      * @see Session#logout()
      */
     public synchronized void shutdown() {
-        Iterator iterator = new HashSet(sessions).iterator();
+        Iterator iterator = new HashSet(sessions.keySet()).iterator();
         while (iterator.hasNext()) {
             Session session = (Session) iterator.next();
             session.logout();
@@ -410,7 +404,7 @@
      * @see SessionListener#loggedOut(SessionImpl)
      */
     public synchronized void loggedOut(SessionImpl session) {
-        assert sessions.contains(session);
+        assert sessions.containsKey(session);
         sessions.remove(session);
         logger.info("Session closed");
         if (sessions.isEmpty()) {

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/StateChangeDispatcher.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/StateChangeDispatcher.java?rev=752831&r1=752830&r2=752831&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/StateChangeDispatcher.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/StateChangeDispatcher.java Thu Mar 12 10:49:25 2009
@@ -20,6 +20,8 @@
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.spi.Name;
 
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
 import java.util.Collection;
 import java.util.Iterator;
 
@@ -30,12 +32,14 @@
 public class StateChangeDispatcher {
 
     /**
-     * Simple item state listeners
+     * Simple item state listeners.
+     * A copy on write array list is used so that no synchronization is required.
      */
     private final Collection listeners = new CopyOnWriteArrayList();
 
     /**
      * Node state listeners
+     * A copy on write array list is used so that no synchronization is required.
      */
     private final transient Collection nsListeners = new CopyOnWriteArrayList();
 
@@ -44,14 +48,29 @@
      * @param listener the new listener to be informed on modifications
      */
     public void addListener(ItemStateListener listener) {
-        assert (!listeners.contains(listener));
-        listeners.add(listener);
+        assert getReference(listeners, listener) == null;
+        listeners.add(new WeakReference(listener));
 
         if (listener instanceof NodeStateListener) {
-            assert (!nsListeners.contains(listener));
-            nsListeners.add(listener);
+            assert getReference(nsListeners, listener) == null;
+            nsListeners.add(new WeakReference(listener));
         }
     }
+    
+    private Reference getReference(Collection coll, ItemStateListener listener) {
+        Iterator iter = coll.iterator();
+        while (iter.hasNext()) {
+            Reference ref = (Reference) iter.next();
+            Object o = ref.get();
+            if (o == listener) {
+                return ref;
+            } else if (o == null) {
+                // clean up unreferenced objects
+                coll.remove(ref);
+            }
+        }
+        return null;
+    }
 
     /**
      * Remove an <code>ItemStateListener</code>
@@ -59,9 +78,9 @@
      */
     public void removeListener(ItemStateListener listener) {
         if (listener instanceof NodeStateListener) {
-            nsListeners.remove(listener);
+            nsListeners.remove(getReference(nsListeners, listener));
         }
-        listeners.remove(listener);
+        listeners.remove(getReference(listeners, listener));
     }
 
     /**
@@ -71,7 +90,11 @@
     public void notifyStateCreated(ItemState created) {
         Iterator iter = listeners.iterator();
         while (iter.hasNext()) {
-            ((ItemStateListener) iter.next()).stateCreated(created);
+            Reference ref = (Reference) iter.next();
+            ItemStateListener l = (ItemStateListener) ref.get();
+            if (l != null) {
+                l.stateCreated(created);
+            }
         }
     }
 
@@ -82,7 +105,11 @@
     public void notifyStateModified(ItemState modified) {
         Iterator iter = listeners.iterator();
         while (iter.hasNext()) {
-            ((ItemStateListener) iter.next()).stateModified(modified);
+            Reference ref = (Reference) iter.next();
+            ItemStateListener l = (ItemStateListener) ref.get();
+            if (l != null) {
+                l.stateModified(modified);
+            }
         }
     }
 
@@ -93,7 +120,11 @@
     public void notifyStateDestroyed(ItemState destroyed) {
         Iterator iter = listeners.iterator();
         while (iter.hasNext()) {
-            ((ItemStateListener) iter.next()).stateDestroyed(destroyed);
+            Reference ref = (Reference) iter.next();
+            ItemStateListener l = (ItemStateListener) ref.get();
+            if (l != null) {
+                l.stateDestroyed(destroyed);
+            }            
         }
     }
 
@@ -104,7 +135,11 @@
     public void notifyStateDiscarded(ItemState discarded) {
         Iterator iter = listeners.iterator();
         while (iter.hasNext()) {
-            ((ItemStateListener) iter.next()).stateDiscarded(discarded);
+            Reference ref = (Reference) iter.next();
+            ItemStateListener l = (ItemStateListener) ref.get();
+            if (l != null) {
+                l.stateDiscarded(discarded);
+            }               
         }
     }
 
@@ -118,7 +153,11 @@
     public void notifyNodeAdded(NodeState state, Name name, int index, NodeId id) {
         Iterator iter = nsListeners.iterator();
         while (iter.hasNext()) {
-            ((NodeStateListener) iter.next()).nodeAdded(state, name, index, id);
+            Reference ref = (Reference) iter.next();
+            NodeStateListener n = (NodeStateListener) ref.get();
+            if (n != null) {
+                n.nodeAdded(state, name, index, id);
+            }                 
         }
     }
 
@@ -129,7 +168,11 @@
     public void notifyNodesReplaced(NodeState state) {
         Iterator iter = nsListeners.iterator();
         while (iter.hasNext()) {
-            ((NodeStateListener) iter.next()).nodesReplaced(state);
+            Reference ref = (Reference) iter.next();
+            NodeStateListener n = (NodeStateListener) ref.get();
+            if (n != null) {
+                n.nodesReplaced(state);
+            }              
         }
     }
 
@@ -140,7 +183,11 @@
     public void notifyNodeModified(NodeState state) {
         Iterator iter = nsListeners.iterator();
         while (iter.hasNext()) {
-            ((NodeStateListener) iter.next()).nodeModified(state);
+            Reference ref = (Reference) iter.next();
+            NodeStateListener n = (NodeStateListener) ref.get();
+            if (n != null) {
+                n.nodeModified(state);
+            }               
         }
     }
 
@@ -154,7 +201,11 @@
     public void notifyNodeRemoved(NodeState state, Name name, int index, NodeId id) {
         Iterator iter = nsListeners.iterator();
         while (iter.hasNext()) {
-            ((NodeStateListener) iter.next()).nodeRemoved(state, name, index, id);
+            Reference ref = (Reference) iter.next();
+            NodeStateListener n = (NodeStateListener) ref.get();
+            if (n != null) {
+                n.nodeRemoved(state, name, index, id);
+            }               
         }
     }