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

svn commit: r410482 - /jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java

Author: tripod
Date: Wed May 31 02:29:35 2006
New Revision: 410482

URL: http://svn.apache.org/viewvc?rev=410482&view=rev
Log:
JCR-445 - repository is locked by WorkspaceJanitor when another workspace is reindexing

Modified:
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java?rev=410482&r1=410481&r2=410482&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java Wed May 31 02:29:35 2006
@@ -17,6 +17,8 @@
 package org.apache.jackrabbit.core;
 
 import EDU.oswego.cs.dl.util.concurrent.Mutex;
+import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock;
+import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock;
 import org.apache.commons.collections.map.ReferenceMap;
 import org.apache.jackrabbit.api.JackrabbitRepository;
 import org.apache.jackrabbit.core.config.FileSystemConfig;
@@ -166,13 +168,6 @@
     private final ReferenceMap activeSessions =
             new ReferenceMap(ReferenceMap.WEAK, ReferenceMap.WEAK);
 
-    /**
-     * workspace janitor thread that is responsible for temporarily
-     * shutting down workspaces that have been idle for a specific
-     * amount of time
-     */
-    private Thread wspJanitor;
-
     // misc. statistics
     private long nodesCount = 0;
     private long propsCount = 0;
@@ -260,7 +255,10 @@
         int maxIdleTime = repConfig.getWorkspaceMaxIdleTime();
         if (maxIdleTime != 0) {
             // start workspace janitor thread
-            wspJanitor = new WorkspaceJanitor(maxIdleTime * 1000);
+            Thread wspJanitor = new Thread(new WorkspaceJanitor(maxIdleTime * 1000));
+            wspJanitor.setName("WorkspaceJanitor");
+            wspJanitor.setPriority(Thread.MIN_PRIORITY);
+            wspJanitor.setDaemon(true);
             wspJanitor.start();
         }
 
@@ -502,7 +500,10 @@
 
     private void initWorkspace(WorkspaceInfo wspInfo) throws RepositoryException {
         // first initialize workspace info
-        wspInfo.initialize();
+        if (!wspInfo.initialize()) {
+            return;
+        }
+
         // get system session and Workspace instance
         SessionImpl sysSession = wspInfo.getSystemSession();
         WorkspaceImpl wsp = (WorkspaceImpl) sysSession.getWorkspace();
@@ -613,7 +614,9 @@
      * @see javax.jcr.Workspace#getAccessibleWorkspaceNames()
      */
     String[] getWorkspaceNames() {
-        return (String[]) wspInfos.keySet().toArray(new String[wspInfos.keySet().size()]);
+        synchronized (wspInfos) {
+            return (String[]) wspInfos.keySet().toArray(new String[wspInfos.keySet().size()]);
+        }
     }
 
     /**
@@ -632,21 +635,22 @@
         // check sanity of this instance
         sanityCheck();
 
-        WorkspaceInfo wspInfo = (WorkspaceInfo) wspInfos.get(workspaceName);
-        if (wspInfo == null) {
-            throw new NoSuchWorkspaceException(workspaceName);
+        WorkspaceInfo wspInfo;
+        synchronized (wspInfos) {
+            wspInfo = (WorkspaceInfo) wspInfos.get(workspaceName);
+            if (wspInfo == null) {
+                throw new NoSuchWorkspaceException(workspaceName);
+            }
         }
 
-        synchronized (wspInfo) {
-            if (!wspInfo.isInitialized()) {
-                try {
-                    initWorkspace(wspInfo);
-                } catch (RepositoryException e) {
-                    log.error("Unable to initialize workspace '" + workspaceName + "'", e);
-                    throw new NoSuchWorkspaceException(workspaceName);
-                }
-            }
+        try {
+            initWorkspace(wspInfo);
+        } catch (RepositoryException e) {
+            log.error("Unable to initialize workspace '" + workspaceName + "'", e);
+            throw new NoSuchWorkspaceException(workspaceName);
         }
+        // reset idle timestamp
+        wspInfo.setIdleTimestamp(0);
         return wspInfo;
     }
 
@@ -658,17 +662,19 @@
      *                             already exists or if another error occurs
      * @see SessionImpl#createWorkspace(String)
      */
-    protected synchronized void createWorkspace(String workspaceName)
+    protected void createWorkspace(String workspaceName)
             throws RepositoryException {
-        if (wspInfos.containsKey(workspaceName)) {
-            throw new RepositoryException("workspace '"
-                    + workspaceName + "' already exists.");
-        }
+        synchronized (wspInfos) {
+            if (wspInfos.containsKey(workspaceName)) {
+                throw new RepositoryException("workspace '"
+                        + workspaceName + "' already exists.");
+            }
 
-        // create the workspace configuration
-        WorkspaceConfig config = repConfig.createWorkspaceConfig(workspaceName);
-        WorkspaceInfo info = createWorkspaceInfo(config);
-        wspInfos.put(workspaceName, info);
+            // create the workspace configuration
+            WorkspaceConfig config = repConfig.createWorkspaceConfig(workspaceName);
+            WorkspaceInfo info = createWorkspaceInfo(config);
+            wspInfos.put(workspaceName, info);
+        }
     }
 
     /**
@@ -682,18 +688,20 @@
      *                             exists or if another error occurs
      * @see SessionImpl#createWorkspace(String,InputSource)
      */
-    protected synchronized void createWorkspace(String workspaceName,
+    protected void createWorkspace(String workspaceName,
                                                 InputSource configTemplate)
             throws RepositoryException {
-        if (wspInfos.containsKey(workspaceName)) {
-            throw new RepositoryException("workspace '"
-                    + workspaceName + "' already exists.");
-        }
+        synchronized (wspInfos) {
+            if (wspInfos.containsKey(workspaceName)) {
+                throw new RepositoryException("workspace '"
+                        + workspaceName + "' already exists.");
+            }
 
-        // create the workspace configuration
-        WorkspaceConfig config = repConfig.createWorkspaceConfig(workspaceName, configTemplate);
-        WorkspaceInfo info = createWorkspaceInfo(config);
-        wspInfos.put(workspaceName, info);
+            // create the workspace configuration
+            WorkspaceConfig config = repConfig.createWorkspaceConfig(workspaceName, configTemplate);
+            WorkspaceInfo info = createWorkspaceInfo(config);
+            wspInfos.put(workspaceName, info);
+        }
     }
 
     SharedItemStateManager getWorkspaceStateManager(String workspaceName)
@@ -784,16 +792,13 @@
      *                                  workspace
      * @throws RepositoryException      if another error occurs
      */
-    protected final synchronized SessionImpl createSession(AuthContext loginContext,
+    protected final SessionImpl createSession(AuthContext loginContext,
                                                            String workspaceName)
             throws NoSuchWorkspaceException, AccessDeniedException,
             RepositoryException {
         WorkspaceInfo wspInfo = getWorkspaceInfo(workspaceName);
         SessionImpl ses = createSessionInstance(loginContext, wspInfo.getConfig());
-        ses.addListener(this);
-        activeSessions.put(ses, ses);
-        // reset idle timestamp
-        wspInfo.setIdleTimestamp(0);
+        markActive(ses);
         return ses;
     }
 
@@ -814,19 +819,29 @@
      *                                  workspace
      * @throws RepositoryException      if another error occurs
      */
-    protected final synchronized SessionImpl createSession(Subject subject,
+    protected final SessionImpl createSession(Subject subject,
                                                            String workspaceName)
             throws NoSuchWorkspaceException, AccessDeniedException,
             RepositoryException {
         WorkspaceInfo wspInfo = getWorkspaceInfo(workspaceName);
         SessionImpl ses = createSessionInstance(subject, wspInfo.getConfig());
-        ses.addListener(this);
-        activeSessions.put(ses, ses);
-        // reset idle timestamp
-        wspInfo.setIdleTimestamp(0);
+        markActive(ses);
         return ses;
     }
 
+    /**
+     * Puts the given session to the list of active sessions and registers this
+     * repository as listener.
+     *
+     * @param session
+     */
+    protected void markActive(SessionImpl session) {
+        synchronized (activeSessions) {
+            session.addListener(this);
+            activeSessions.put(session, session);
+        }
+    }
+
     //-------------------------------------------------< JackrabbitRepository >
     /**
      * Shuts down this repository.
@@ -844,10 +859,13 @@
         // (copy sessions to array to avoid ConcurrentModificationException;
         // manually copy entries rather than calling ReferenceMap#toArray() in
         // order to work around  http://issues.apache.org/bugzilla/show_bug.cgi?id=25551)
-        int cnt = 0;
-        SessionImpl[] sa = new SessionImpl[activeSessions.size()];
-        for (Iterator it = activeSessions.values().iterator(); it.hasNext(); cnt++) {
-            sa[cnt] = (SessionImpl) it.next();
+        SessionImpl[] sa;
+        synchronized (activeSessions) {
+            int cnt = 0;
+            sa = new SessionImpl[activeSessions.size()];
+            for (Iterator it = activeSessions.values().iterator(); it.hasNext(); cnt++) {
+                sa[cnt] = (SessionImpl) it.next();
+            }
         }
         for (int i = 0; i < sa.length; i++) {
             if (sa[i] != null) {
@@ -856,12 +874,10 @@
         }
 
         // shut down workspaces
-        for (Iterator it = wspInfos.values().iterator(); it.hasNext();) {
-            WorkspaceInfo wspInfo = (WorkspaceInfo) it.next();
-            synchronized (wspInfo) {
-                if (wspInfo.isInitialized()) {
-                    wspInfo.dispose();
-                }
+        synchronized (wspInfos) {
+            for (Iterator it = wspInfos.values().iterator(); it.hasNext();) {
+                WorkspaceInfo wspInfo = (WorkspaceInfo) it.next();
+                wspInfo.dispose();
             }
         }
 
@@ -892,11 +908,8 @@
 
         // make sure this instance is not used anymore
         disposed = true;
-
-        if (wspJanitor != null) {
-            wspJanitor.interrupt();
-            wspJanitor = null;
-        }
+        // wakeup eventual waiters
+        notifyAll();
 
         // finally release repository lock
         releaseRepositoryLock();
@@ -924,7 +937,7 @@
      * Sets the default properties of the repository.
      * <p/>
      * This method loads the <code>Properties</code> from the
-     * <code>org/apache/jackrabbit/core/repository.properties</code> resource
+     * <code>com/day/crx/core/repository.properties</code> resource
      * found in the class path and (re)sets the statistics properties, if not
      * present.
      *
@@ -1154,9 +1167,11 @@
     /**
      * {@inheritDoc}
      */
-    public synchronized void loggedOut(SessionImpl session) {
-        // remove session from active sessions
-        activeSessions.remove(session);
+    public void loggedOut(SessionImpl session) {
+        synchronized (activeSessions) {
+            // remove session from active sessions
+            activeSessions.remove(session);
+        }
     }
 
     //--------------------------------------------------------< EventListener >
@@ -1297,6 +1312,12 @@
         private boolean initialized;
 
         /**
+         * lock that guards the init sequence
+         */
+        private final ReadWriteLock initLock =
+                new ReentrantWriterPreferenceReadWriteLock();
+
+        /**
          * timestamp when the workspace has been determined being idle
          */
         private long idleTimestamp;
@@ -1364,8 +1385,16 @@
          *
          * @return <code>true</code> if this workspace info is initialized.
          */
-        synchronized boolean isInitialized() {
-            return initialized;
+        boolean isInitialized() {
+            try {
+                initLock.readLock().attempt(0);
+            } catch (InterruptedException e) {
+                return false;
+            }
+            // can't use 'finally' pattern here
+            boolean ret = initialized;
+            initLock.readLock().release();
+            return ret;
         }
 
         /**
@@ -1373,8 +1402,8 @@
          *
          * @return the workspace file system
          */
-        synchronized FileSystem getFileSystem() {
-            if (!initialized) {
+        FileSystem getFileSystem() {
+            if (!isInitialized()) {
                 throw new IllegalStateException("not initialized");
             }
 
@@ -1387,9 +1416,9 @@
          * @return the workspace persistence manager
          * @throws RepositoryException if the persistence manager could not be instantiated/initialized
          */
-        synchronized PersistenceManager getPersistenceManager()
+        PersistenceManager getPersistenceManager()
                 throws RepositoryException {
-            if (!initialized) {
+            if (!isInitialized()) {
                 throw new IllegalStateException("not initialized");
             }
 
@@ -1403,9 +1432,9 @@
          * @throws RepositoryException if the workspace item state provider
          *                             could not be created
          */
-        synchronized SharedItemStateManager getItemStateProvider()
+        SharedItemStateManager getItemStateProvider()
                 throws RepositoryException {
-            if (!initialized) {
+            if (!isInitialized()) {
                 throw new IllegalStateException("not initialized");
             }
 
@@ -1417,8 +1446,8 @@
          *
          * @return the observation manager factory for this workspace
          */
-        synchronized ObservationManagerFactory getObservationManagerFactory() {
-            if (!initialized) {
+        ObservationManagerFactory getObservationManagerFactory() {
+            if (!isInitialized()) {
                 throw new IllegalStateException("not initialized");
             }
 
@@ -1432,27 +1461,29 @@
          *         if no <code>SearchManager</code>
          * @throws RepositoryException if the search manager could not be created
          */
-        synchronized SearchManager getSearchManager() throws RepositoryException {
-            if (!initialized) {
+        SearchManager getSearchManager() throws RepositoryException {
+            if (!isInitialized()) {
                 throw new IllegalStateException("not initialized");
             }
 
-            if (searchMgr == null) {
-                if (config.getSearchConfig() == null) {
-                    // no search index configured
-                    return null;
+            synchronized (this) {
+                if (searchMgr == null) {
+                    if (config.getSearchConfig() == null) {
+                        // no search index configured
+                        return null;
+                    }
+                    // search manager is lazily instantiated in order to avoid
+                    // 'chicken & egg' bootstrap problems
+                    searchMgr = new SearchManager(config.getSearchConfig(),
+                            nsReg,
+                            ntReg,
+                            itemStateMgr,
+                            rootNodeId,
+                            getSystemSearchManager(getName()),
+                            SYSTEM_ROOT_NODE_ID);
                 }
-                // search manager is lazily instantiated in order to avoid
-                // 'chicken & egg' bootstrap problems
-                searchMgr = new SearchManager(config.getSearchConfig(),
-                        nsReg,
-                        ntReg,
-                        itemStateMgr,
-                        rootNodeId,
-                        getSystemSearchManager(getName()),
-                        SYSTEM_ROOT_NODE_ID);
+                return searchMgr;
             }
-            return searchMgr;
         }
 
         /**
@@ -1461,17 +1492,19 @@
          * @return the lock manager for this workspace
          * @throws RepositoryException if the lock manager could not be created
          */
-        synchronized LockManager getLockManager() throws RepositoryException {
-            if (!initialized) {
+        LockManager getLockManager() throws RepositoryException {
+            if (!isInitialized()) {
                 throw new IllegalStateException("not initialized");
             }
 
-            // lock manager is lazily instantiated in order to avoid
-            // 'chicken & egg' bootstrap problems
-            if (lockMgr == null) {
-                lockMgr = new LockManagerImpl(getSystemSession(), fs);
+            synchronized (this) {
+                // lock manager is lazily instantiated in order to avoid
+                // 'chicken & egg' bootstrap problems
+                if (lockMgr == null) {
+                    lockMgr = new LockManagerImpl(getSystemSession(), fs);
+                }
+                return lockMgr;
             }
-            return lockMgr;
         }
 
         /**
@@ -1480,17 +1513,19 @@
          * @return the system session for this workspace
          * @throws RepositoryException if the system session could not be created
          */
-        synchronized SystemSession getSystemSession() throws RepositoryException {
-            if (!initialized) {
+        SystemSession getSystemSession() throws RepositoryException {
+            if (!isInitialized()) {
                 throw new IllegalStateException("not initialized");
             }
 
-            // system session is lazily instantiated in order to avoid
-            // 'chicken & egg' bootstrap problems
-            if (systemSession == null) {
-                systemSession = SystemSession.create(RepositoryImpl.this, config);
+            synchronized (this) {
+                // system session is lazily instantiated in order to avoid
+                // 'chicken & egg' bootstrap problems
+                if (systemSession == null) {
+                    systemSession = SystemSession.create(RepositoryImpl.this, config);
+                }
+                return systemSession;
             }
-            return systemSession;
         }
 
         /**
@@ -1510,116 +1545,169 @@
          * <li>lock manager</li>
          * <li>search manager</li>
          * </ul>
+         * @return <code>true</code> if this info was initialized.
          */
-        synchronized void initialize() throws RepositoryException {
-            if (initialized) {
-                throw new IllegalStateException("already initialized");
+        boolean initialize() throws RepositoryException {
+            try {
+                initLock.writeLock().acquire();
+            } catch (InterruptedException e) {
+                throw new RepositoryException("Unable to aquire write lock.", e);
             }
+            try {
+                if (initialized) {
+                    return false;
+                }
 
-            log.info("initializing workspace '" + getName() + "'...");
+                log.info("initializing workspace '" + getName() + "'...");
 
-            FileSystemConfig fsConfig = config.getFileSystemConfig();
-            fs = fsConfig.createFileSystem();
+                FileSystemConfig fsConfig = config.getFileSystemConfig();
+                fs = fsConfig.createFileSystem();
 
-            persistMgr = createPersistenceManager(new File(config.getHomeDir()),
-                    fs,
-                    config.getPersistenceManagerConfig(),
-                    rootNodeId,
-                    nsReg,
-                    ntReg);
+                persistMgr = createPersistenceManager(new File(config.getHomeDir()),
+                        fs,
+                        config.getPersistenceManagerConfig(),
+                        rootNodeId,
+                        nsReg,
+                        ntReg);
 
-            // create item state manager
-            try {
-                itemStateMgr =
-                        new SharedItemStateManager(persistMgr, rootNodeId, ntReg, true);
+                // create item state manager
                 try {
-                    itemStateMgr.addVirtualItemStateProvider(
-                            vMgr.getVirtualItemStateProvider());
-                    itemStateMgr.addVirtualItemStateProvider(
-                            virtNTMgr.getVirtualItemStateProvider());
-                } catch (Exception e) {
-                    log.error("Unable to add vmgr: " + e.toString(), e);
+                    itemStateMgr =
+                            new SharedItemStateManager(persistMgr, rootNodeId, ntReg, true);
+                    try {
+                        itemStateMgr.addVirtualItemStateProvider(
+                                vMgr.getVirtualItemStateProvider());
+                        itemStateMgr.addVirtualItemStateProvider(
+                                virtNTMgr.getVirtualItemStateProvider());
+                    } catch (Exception e) {
+                        log.error("Unable to add vmgr: " + e.toString(), e);
+                    }
+                } catch (ItemStateException ise) {
+                    String msg = "failed to instantiate shared item state manager";
+                    log.debug(msg);
+                    throw new RepositoryException(msg, ise);
                 }
-            } catch (ItemStateException ise) {
-                String msg = "failed to instantiate shared item state manager";
-                log.debug(msg);
-                throw new RepositoryException(msg, ise);
-            }
 
-            obsMgrFactory = new ObservationManagerFactory();
+                obsMgrFactory = new ObservationManagerFactory();
 
-            // register the observation factory of that workspace
-            delegatingDispatcher.addDispatcher(obsMgrFactory);
+                // register the observation factory of that workspace
+                delegatingDispatcher.addDispatcher(obsMgrFactory);
 
-            initialized = true;
+                idleTimestamp = 0;
+                initialized = true;
 
-            log.info("workspace '" + getName() + "' initialized");
+                log.info("workspace '" + getName() + "' initialized");
+                return true;
+            } finally {
+                initLock.writeLock().release();
+            }
         }
 
         /**
-         * Disposes all objects this <code>WorkspaceInfo</code> is holding.
+         * disposes this workspaceinfo if it has been idle for more than
+         * <code>maxIdleTime</code> milliseconds.
+         *
+         * @param maxIdleTime
          */
-        synchronized void dispose() {
-            if (!initialized) {
-                throw new IllegalStateException("not initialized");
+        void disposeIfIdle(long maxIdleTime) {
+            try {
+                initLock.readLock().acquire();
+            } catch (InterruptedException e) {
+                return;
+            }
+            try {
+                if (!initialized) {
+                    return;
+                }
+                long currentTS = System.currentTimeMillis();
+                if (getIdleTimestamp() == 0) {
+                    // set idle timestamp
+                    idleTimestamp = currentTS;
+                } else {
+                    if ((currentTS - getIdleTimestamp()) > maxIdleTime) {
+                        // temporarily shutdown workspace
+                        log.info("disposing workspace '" + getName() + "' that is idle for " + (currentTS - idleTimestamp));
+                        dispose();
+                    }
+                }
+            } finally {
+                initLock.readLock().release();
             }
+        }
 
-            log.info("shutting down workspace '" + getName() + "'...");
+        /**
+         * Disposes all objects this <code>WorkspaceInfo</code> is holding.
+         */
+        void dispose() {
+            try {
+                initLock.writeLock().acquire();
+            } catch (InterruptedException e) {
+                throw new IllegalStateException("Unable to aquire write lock.");
+            }
 
-            // deregister the observation factory of that workspace
-            delegatingDispatcher.removeDispatcher(obsMgrFactory);
+            try {
+                if (!initialized) {
+                    return;
+                }
 
-            // dispose observation manager factory
-            obsMgrFactory.dispose();
-            obsMgrFactory = null;
+                log.info("shutting down workspace '" + getName() + "'...");
 
-            // shutdown search managers
-            if (searchMgr != null) {
-                searchMgr.close();
-                searchMgr = null;
-            }
+                // deregister the observation factory of that workspace
+                delegatingDispatcher.removeDispatcher(obsMgrFactory);
 
-            // close system session
-            if (systemSession != null) {
-                systemSession.removeListener(RepositoryImpl.this);
-                systemSession.logout();
-                systemSession = null;
-            }
+                // dispose observation manager factory
+                obsMgrFactory.dispose();
+                obsMgrFactory = null;
+
+                // shutdown search managers
+                if (searchMgr != null) {
+                    searchMgr.close();
+                    searchMgr = null;
+                }
 
-            // dispose shared item state manager
-            itemStateMgr.dispose();
-            itemStateMgr = null;
+                // close system session
+                if (systemSession != null) {
+                    systemSession.removeListener(RepositoryImpl.this);
+                    systemSession.logout();
+                    systemSession = null;
+                }
 
-            // close persistence manager
-            try {
-                persistMgr.close();
-            } catch (Exception e) {
-                log.error("error while closing persistence manager of workspace "
-                        + config.getName(), e);
-            }
-            persistMgr = null;
+                // dispose shared item state manager
+                itemStateMgr.dispose();
+                itemStateMgr = null;
 
-            // close lock manager
-            if (lockMgr != null) {
-                lockMgr.close();
-                lockMgr = null;
-            }
+                // close persistence manager
+                try {
+                    persistMgr.close();
+                } catch (Exception e) {
+                    log.error("error while closing persistence manager of workspace "
+                            + config.getName(), e);
+                }
+                persistMgr = null;
 
-            // close workspace file system
-            try {
-                fs.close();
-            } catch (FileSystemException fse) {
-                log.error("error while closing file system of workspace "
-                        + config.getName(), fse);
-            }
-            fs = null;
+                // close lock manager
+                if (lockMgr != null) {
+                    lockMgr.close();
+                    lockMgr = null;
+                }
 
-            // reset idle timestamp
-            idleTimestamp = 0;
+                // close workspace file system
+                try {
+                    fs.close();
+                } catch (FileSystemException fse) {
+                    log.error("error while closing file system of workspace "
+                            + config.getName(), fse);
+                }
+                fs = null;
 
-            initialized = false;
+                // reset idle timestamp
+                idleTimestamp = 0;
+                initialized = false;
 
-            log.info("workspace '" + getName() + "' has been shutdown");
+                log.info("workspace '" + getName() + "' has been shutdown");
+            } finally {
+                initLock.writeLock().release();
+            }
         }
 
         /**
@@ -1652,13 +1740,14 @@
      * The workspace janitor thread that will shutdown workspaces that have
      * been idle for a certain amount of time.
      */
-    private class WorkspaceJanitor extends Thread {
+    private class WorkspaceJanitor implements Runnable {
 
         /**
          * amount of time in mmilliseconds before an idle workspace is
          * automatically shutdown.
          */
         private long maxIdleTime;
+
         /**
          * interval in mmilliseconds between checks for idle workspaces.
          */
@@ -1672,9 +1761,6 @@
          *                    workspace is automatically shutdown.
          */
         WorkspaceJanitor(long maxIdleTime) {
-            super("WorkspaceJanitor");
-            setPriority(Thread.MIN_PRIORITY);
-            setDaemon(true);
             this.maxIdleTime = maxIdleTime;
             // compute check interval as 10% of maxIdleTime
             checkInterval = (long) (0.1 * maxIdleTime);
@@ -1693,49 +1779,41 @@
          * </ol>
          */
         public void run() {
-            while (!disposed) {
-                try {
-                    Thread.sleep(checkInterval);
-                } catch (InterruptedException e) {
-                    /* ignore */
-                }
-
+            while (true) {
                 synchronized (RepositoryImpl.this) {
+                    try {
+                        RepositoryImpl.this.wait(checkInterval);
+                    } catch (InterruptedException e) {
+                        // ignore
+                    }
                     if (disposed) {
                         return;
                     }
-                    // get names of workspaces
-                    Set wspNames = new HashSet(wspInfos.keySet());
-                    // remove default workspace (will never be shutdown when idle)
-                    wspNames.remove(repConfig.getDefaultWorkspaceName());
+                }
+                // get names of workspaces
+                Set wspNames;
+                synchronized (wspInfos) {
+                    wspNames = new HashSet(wspInfos.keySet());
+                }
+                // remove default workspace (will never be shutdown when idle)
+                wspNames.remove(repConfig.getDefaultWorkspaceName());
+
+                synchronized (activeSessions) {
                     // remove workspaces with active sessions
                     for (Iterator it = activeSessions.values().iterator(); it.hasNext();) {
                         SessionImpl ses = (SessionImpl) it.next();
                         wspNames.remove(ses.getWorkspace().getName());
                     }
-                    // remove uninitialized workspaces
-                    for (Iterator it = wspInfos.values().iterator(); it.hasNext();) {
-                        WorkspaceInfo wspInfo = (WorkspaceInfo) it.next();
-                        if (!wspInfo.isInitialized()) {
-                            wspNames.remove(wspInfo.getName());
-                        }
-                    }
+                }
 
-                    // remaining names denote workspaces which are currently idle
-                    for (Iterator it = wspNames.iterator(); it.hasNext();) {
-                        WorkspaceInfo wspInfo = (WorkspaceInfo) wspInfos.get(it.next());
-                        long currentTS = System.currentTimeMillis();
-                        long idleTS = wspInfo.getIdleTimestamp();
-                        if (idleTS == 0) {
-                            // set idle timestamp
-                            wspInfo.setIdleTimestamp(currentTS);
-                        } else {
-                            if ((currentTS - idleTS) > maxIdleTime) {
-                                // temporarily shutdown workspace
-                                wspInfo.dispose();
-                            }
-                        }
+                // remaining names denote workspaces which currently have not
+                // active sessions
+                for (Iterator it = wspNames.iterator(); it.hasNext();) {
+                    WorkspaceInfo wspInfo;
+                    synchronized (wspInfos) {
+                        wspInfo = (WorkspaceInfo) wspInfos.get(it.next());
                     }
+                    wspInfo.disposeIfIdle(maxIdleTime);
                 }
             }
         }