You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2006/01/18 14:59:53 UTC

svn commit: r370144 - in /incubator/jackrabbit/trunk/jackrabbit/src: main/config/ main/java/org/apache/jackrabbit/core/ main/java/org/apache/jackrabbit/core/config/ test/java/org/apache/jackrabbit/core/config/

Author: stefan
Date: Wed Jan 18 05:59:42 2006
New Revision: 370144

URL: http://svn.apache.org/viewcvs?rev=370144&view=rev
Log:
JCR-305: provide option to automatically dispose idle workspaces

Modified:
    incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/ConfigurationParser.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java
    incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/config.dtd
    incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/config/repository.xml

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml?rev=370144&r1=370143&r2=370144&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/config/repository.xml Wed Jan 18 05:59:42 2006
@@ -80,7 +80,9 @@
     <!--
         the Workspaces element specifies the physical workspaces root directory
         (rootPath attribute), the name of the default workspace
-        (defaultWorkspace attribute) and optionally the workspace configuration
+        (defaultWorkspace attribute), the (optional) maximum amount of time in
+        seconds before an idle workspace is automatically shutdown
+        (maxIdleTime attribute) and the (optional) workspace configuration
         root directory within the virtual repository file system (configRootPath
         attribute).
 
@@ -97,7 +99,8 @@
     <!ATTLIST Workspaces
         rootPath CDATA #REQUIRED
         defaultWorkspace CDATA #REQUIRED
-        configRootPath CDATA #IMPLIED>
+        configRootPath CDATA #IMPLIED
+        maxIdleTime CDATA #IMPLIED>
 
     <!--
         the Workspace element serves as a workspace configuration template;

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java?rev=370144&r1=370143&r2=370144&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/RepositoryImpl.java Wed Jan 18 05:59:42 2006
@@ -72,6 +72,8 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Properties;
+import java.util.Set;
+import java.util.HashSet;
 import java.nio.channels.FileLock;
 import java.nio.channels.FileChannel;
 
@@ -151,6 +153,13 @@
     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;
@@ -170,9 +179,11 @@
      */
     protected RepositoryImpl(RepositoryConfig repConfig) throws RepositoryException {
 
+        log.info("Starting repository...");
+
         this.repConfig = repConfig;
 
-        this.acquireRepositoryLock() ;
+        acquireRepositoryLock() ;
 
         // setup file systems
         repStore = repConfig.getFileSystem();
@@ -212,7 +223,7 @@
         vMgr = createVersionManager(repConfig.getVersioningConfig(),
                 delegatingDispatcher);
 
-        // init virtual nodetype manager
+        // init virtual node type manager
         virtNTMgr = new VirtualNodeTypeStateManager(getNodeTypeRegistry(),
                 delegatingDispatcher, NODETYPES_NODE_UUID, SYSTEM_ROOT_NODE_UUID);
 
@@ -228,12 +239,23 @@
             throw e;
         }
 
+        // amount of time in seconds before an idle workspace is automatically
+        // shut down
+        int maxIdleTime = repConfig.getWorkspaceMaxIdleTime();
+        if (maxIdleTime != 0) {
+            // start workspace janitor thread
+            wspJanitor = new WorkspaceJanitor(maxIdleTime * 1000);
+            wspJanitor.start();
+        }
+
         // after the workspace is initialized we pass a system session to
         // the virtual node type manager
 
         // todo FIXME it seems odd that the *global* virtual node type manager
         // is using a session that is bound to a single specific workspace
         virtNTMgr.setSession(getSystemSession(repConfig.getDefaultWorkspaceName()));
+
+        log.info("Repository started");
     }
 
     /**
@@ -266,7 +288,7 @@
 
         if (lock.exists()) {
             log.warn("Existing lock file at " + lock.getAbsolutePath() +
-                    " deteteced. Repository was not shutdown properly.");
+                    " deteteced. Repository was not shut down properly.");
         } else {
             try {
                 lock.createNewFile();
@@ -486,7 +508,7 @@
             // add version storage
             nt = sysSession.getNodeTypeManager().getNodeType(QName.REP_VERSIONSTORAGE);
             sysRoot.internalAddChildNode(QName.JCR_VERSIONSTORAGE, nt, VERSION_STORAGE_NODE_UUID);
-            // add nodetypes
+            // add node types
             nt = sysSession.getNodeTypeManager().getNodeType(QName.REP_NODETYPES);
             sysRoot.internalAddChildNode(QName.JCR_NODETYPES, nt, NODETYPES_NODE_UUID);
             rootNode.save();
@@ -721,7 +743,7 @@
      *                                  workspace
      * @throws RepositoryException      if another error occurs
      */
-    protected final SessionImpl createSession(AuthContext loginContext,
+    protected synchronized final SessionImpl createSession(AuthContext loginContext,
                               String workspaceName)
             throws NoSuchWorkspaceException, AccessDeniedException,
             RepositoryException {
@@ -729,6 +751,8 @@
         SessionImpl ses = createSessionInstance(loginContext, wspInfo.getConfig());
         ses.addListener(this);
         activeSessions.put(ses, ses);
+        // reset idle timestamp
+        wspInfo.setIdleTimestamp(0);
         return ses;
     }
 
@@ -749,7 +773,7 @@
      *                                  workspace
      * @throws RepositoryException      if another error occurs
      */
-    protected final SessionImpl createSession(Subject subject,
+    protected synchronized final SessionImpl createSession(Subject subject,
                                               String workspaceName)
             throws NoSuchWorkspaceException, AccessDeniedException,
             RepositoryException {
@@ -757,6 +781,8 @@
         SessionImpl ses = createSessionInstance(subject, wspInfo.getConfig());
         ses.addListener(this);
         activeSessions.put(ses, ses);
+        // reset idle timestamp
+        wspInfo.setIdleTimestamp(0);
         return ses;
     }
 
@@ -770,6 +796,8 @@
             return;
         }
 
+        log.info("Shutting down repository...");
+
         // close active user sessions
         while (!activeSessions.isEmpty()) {
            ((Session) activeSessions.values().iterator().next()).logout();
@@ -820,8 +848,15 @@
         // make sure this instance is not used anymore
         disposed = true;
 
+        if (wspJanitor != null) {
+            wspJanitor.interrupt();
+            wspJanitor = null;
+        }
+
         // finally release repository lock
         releaseRepositoryLock();
+
+        log.info("Repository has been shutdown");
     }
 
     /**
@@ -942,8 +977,12 @@
      * @throws RepositoryException if the persistence manager could
      *                             not be instantiated/initialized
      */
-    private static PersistenceManager createPersistenceManager(File homeDir, FileSystem fs, PersistenceManagerConfig pmConfig,
-                                                               String rootNodeUUID, NamespaceRegistry nsReg, NodeTypeRegistry ntReg)
+    private static PersistenceManager createPersistenceManager(File homeDir,
+                                                               FileSystem fs,
+                                                               PersistenceManagerConfig pmConfig,
+                                                               String rootNodeUUID,
+                                                               NamespaceRegistry nsReg,
+                                                               NodeTypeRegistry ntReg)
             throws RepositoryException {
         try {
             PersistenceManager pm = (PersistenceManager) pmConfig.newInstance();
@@ -1066,7 +1105,7 @@
     /**
      * {@inheritDoc}
      */
-    public void loggedOut(SessionImpl session) {
+    public synchronized void loggedOut(SessionImpl session) {
         // remove session from active sessions
         activeSessions.remove(session);
     }
@@ -1209,6 +1248,11 @@
         private boolean initialized;
 
         /**
+         * timestamp when the workspace has been determined being idle
+         */
+        private long idleTimestamp;
+
+        /**
          * Creates a new <code>WorkspaceInfo</code> based on the given
          * <code>config</code>.
          *
@@ -1216,6 +1260,7 @@
          */
         protected WorkspaceInfo(WorkspaceConfig config) {
             this.config = config;
+            idleTimestamp = 0;
             initialized = false;
         }
 
@@ -1238,6 +1283,28 @@
         }
 
         /**
+         * Returns the timestamp when the workspace has become idle or zero
+         * if the workspace is currently not idle.
+         *
+         * @return the timestamp when the workspace has become idle or zero if
+         *         the workspace is not idle.
+         */
+        long getIdleTimestamp() {
+            return idleTimestamp;
+        }
+
+        /**
+         * Sets the timestamp when the workspace has become idle. if
+         * <code>ts == 0</code> the workspace is marked as being currently
+         * active.
+         *
+         * @param ts timestamp when workspace has become idle.
+         */
+        void setIdleTimestamp(long ts) {
+            idleTimestamp = ts;
+        }
+
+        /**
          * Returns <code>true</code> if this workspace info is initialized,
          * otherwise returns <code>false</code>.
          *
@@ -1395,6 +1462,8 @@
                 throw new IllegalStateException("already initialized");
             }
 
+            log.info("initializing workspace '" + getName() + "'...");
+
             FileSystemConfig fsConfig = config.getFileSystemConfig();
             fsConfig.init();
             fs = fsConfig.getFileSystem();
@@ -1427,6 +1496,8 @@
             obsMgrFactory = new ObservationManagerFactory();
 
             initialized = true;
+
+            log.info("workspace '" + getName() + "' initialized");
         }
 
         /**
@@ -1437,6 +1508,8 @@
                 throw new IllegalStateException("not initialized");
             }
 
+            log.info("shutting down workspace '" + getName() + "'...");
+
             // dispose observation manager factory
             obsMgrFactory.dispose();
             obsMgrFactory = null;
@@ -1477,6 +1550,106 @@
             FileSystemConfig fsConfig = config.getFileSystemConfig();
             fsConfig.dispose();
             fs = null;
+
+            // reset idle timestamp
+            idleTimestamp = 0;
+
+            initialized = false;
+
+            log.info("workspace '" + getName() + "' has been shutdown");
+        }
+    }
+
+    /**
+     * The workspace janitor thread that will shutdown workspaces that have
+     * been idle for a certain amount of time.
+     */
+    private class WorkspaceJanitor extends Thread {
+
+        /**
+         * amount of time in mmilliseconds before an idle workspace is
+         * automatically shutdown.
+         */
+        private long maxIdleTime;
+        /**
+         * interval in mmilliseconds between checks for idle workspaces.
+         */
+        private long checkInterval;
+
+        /**
+         * Creates a new <code>WorkspaceJanitor</code> instance responsible for
+         * shutting down idle workspaces.
+         *
+         * @param maxIdleTime amount of time in mmilliseconds before an idle
+         *                    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);
+        }
+
+        /**
+         * {@inheritDoc}
+         * <p/>
+         * Performs the following tasks in a <code>while (true)</code> loop:
+         * <ol>
+         * <li>wait for <code>checkInterval</code> milliseconds</li>
+         * <li>build list of initialized but currently inactive workspaces
+         *     (excluding the default workspace)</li>
+         * <li>shutdown those workspaces that have been idle for at least
+         *     <code>maxIdleTime</code> milliseconds</li>
+         * </ol>
+         */
+        public void run() {
+            while (!disposed) {
+                try {
+                    Thread.sleep(checkInterval);
+                } catch (InterruptedException e) {
+                    /* ignore */
+                }
+
+                synchronized (RepositoryImpl.this) {
+                    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());
+                    // 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();
+                            }
+                        }
+                    }
+                }
+            }
         }
     }
 }

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/ConfigurationParser.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/ConfigurationParser.java?rev=370144&r1=370143&r2=370144&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/ConfigurationParser.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/ConfigurationParser.java Wed Jan 18 05:59:42 2006
@@ -100,6 +100,9 @@
     /** Name of the config root path configuration attribute. */
     public static final String CONFIG_ROOT_PATH_ATTRIBUTE = "configRootPath";
 
+    /** Name of the maximum idle time configuration attribute. */
+    public static final String MAX_IDLE_TIME_ATTRIBUTE = "maxIdleTime";
+
     /** Name of the default workspace configuration attribute. */
     public static final String DEFAULT_WORKSPACE_ATTRIBUTE =
         "defaultWorkspace";
@@ -228,6 +231,9 @@
         String defaultWorkspace = replaceVariables(
                 getAttribute(workspaces, DEFAULT_WORKSPACE_ATTRIBUTE));
 
+        int maxIdleTime = Integer.parseInt(
+                getAttribute(workspaces, MAX_IDLE_TIME_ATTRIBUTE, "0"));
+
         // Workspace configuration template
         Element template = getElement(root, WORKSPACE_ELEMENT);
 
@@ -239,7 +245,7 @@
 
         return new RepositoryConfig(home, appName, amc, lmc, fsc,
                 workspaceDirectory, workspaceConfigDirectory, defaultWorkspace,
-                template, vc, sc, this);
+                maxIdleTime, template, vc, sc, this);
     }
 
     /**

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java?rev=370144&r1=370143&r2=370144&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/RepositoryConfig.java Wed Jan 18 05:59:42 2006
@@ -196,6 +196,12 @@
     private final String workspaceConfigDirectory;
 
     /**
+     * Amount of time in seconds after which an idle workspace is automatically
+     * shutdown.
+     */
+    private final int workspaceMaxIdleTime;
+
+    /**
      * The workspace configuration template. Used in creating new workspace
      * configuration files.
      */
@@ -222,6 +228,7 @@
      * @param fsc file system configuration
      * @param workspaceDirectory workspace root directory
      * @param workspaceConfigDirectory optional workspace configuration directory
+     * @param workspaceMaxIdleTime maximum workspace idle time in seconds
      * @param defaultWorkspace name of the default workspace
      * @param vc versioning configuration
      * @param sc search configuration for system search manager.
@@ -230,8 +237,9 @@
     RepositoryConfig(String home, String name,
             AccessManagerConfig amc, LoginModuleConfig lmc, FileSystemConfig fsc,
             String workspaceDirectory, String workspaceConfigDirectory,
-            String defaultWorkspace, Element template, VersioningConfig vc,
-            SearchConfig sc, ConfigurationParser parser) {
+            String defaultWorkspace, int workspaceMaxIdleTime,
+            Element template, VersioningConfig vc, SearchConfig sc,
+            ConfigurationParser parser) {
         this.workspaces = new HashMap();
         this.home = home;
         this.name = name;
@@ -240,6 +248,7 @@
         this.fsc = fsc;
         this.workspaceDirectory = workspaceDirectory;
         this.workspaceConfigDirectory = workspaceConfigDirectory;
+        this.workspaceMaxIdleTime = workspaceMaxIdleTime;
         this.defaultWorkspace = defaultWorkspace;
         this.template = template;
         this.vc = vc;
@@ -597,6 +606,17 @@
      */
     public String getDefaultWorkspaceName() {
         return defaultWorkspace;
+    }
+
+    /**
+     * Returns the amount of time in seconds after which an idle workspace is
+     * automatically shutdown. If zero then idle workspaces will never be
+     * automatically shutdown.
+     *
+     * @return maximum workspace idle time in seconds
+     */
+    public int getWorkspaceMaxIdleTime() {
+        return workspaceMaxIdleTime;
     }
 
     /**

Modified: incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/config.dtd
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/config.dtd?rev=370144&r1=370143&r2=370144&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/config.dtd (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/config/config.dtd Wed Jan 18 05:59:42 2006
@@ -96,7 +96,9 @@
 <!--
     the Workspaces element specifies the physical workspaces root directory
     (rootPath attribute), the name of the default workspace
-    (defaultWorkspace attribute) and optionally the workspace configuration
+    (defaultWorkspace attribute), the (optional) maximum amount of time in
+    seconds before an idle workspace is automatically shutdown
+    (maxIdleTime attribute) and the (optional) workspace configuration
     root directory within the virtual repository file system (configRootPath
     attribute).
 
@@ -113,7 +115,8 @@
 <!ATTLIST Workspaces
     rootPath CDATA #REQUIRED
     defaultWorkspace CDATA #REQUIRED
-    configRootPath CDATA #IMPLIED>
+    configRootPath CDATA #IMPLIED
+    maxIdleTime CDATA #IMPLIED>
 
 <!--
     the Workspace element serves as a workspace configuration template;

Modified: incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/config/repository.xml
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/config/repository.xml?rev=370144&r1=370143&r2=370144&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/config/repository.xml (original)
+++ incubator/jackrabbit/trunk/jackrabbit/src/test/java/org/apache/jackrabbit/core/config/repository.xml Wed Jan 18 05:59:42 2006
@@ -80,7 +80,9 @@
     <!--
         the Workspaces element specifies the physical workspaces root directory
         (rootPath attribute), the name of the default workspace
-        (defaultWorkspace attribute) and optionally the workspace configuration
+        (defaultWorkspace attribute), the (optional) maximum amount of time in
+        seconds before an idle workspace is automatically shutdown
+        (maxIdleTime attribute) and the (optional) workspace configuration
         root directory within the virtual repository file system (configRootPath
         attribute).
 
@@ -97,7 +99,8 @@
     <!ATTLIST Workspaces
         rootPath CDATA #REQUIRED
         defaultWorkspace CDATA #REQUIRED
-        configRootPath CDATA #IMPLIED>
+        configRootPath CDATA #IMPLIED
+        maxIdleTime CDATA #IMPLIED>
 
     <!--
         the Workspace element serves as a workspace configuration template;