Package org.apache.jackrabbit.core

Source Code of org.apache.jackrabbit.core.RepositoryImpl$WorkspaceJanitor

/*
* Copyright 2004-2005 The Apache Software Foundation or its licensors,
*                     as applicable.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.core;

import org.apache.commons.collections.map.ReferenceMap;
import org.apache.jackrabbit.core.config.LoginModuleConfig;
import org.apache.jackrabbit.core.config.PersistenceManagerConfig;
import org.apache.jackrabbit.core.config.RepositoryConfig;
import org.apache.jackrabbit.core.config.VersioningConfig;
import org.apache.jackrabbit.core.config.WorkspaceConfig;
import org.apache.jackrabbit.core.config.FileSystemConfig;
import org.apache.jackrabbit.core.fs.BasedFileSystem;
import org.apache.jackrabbit.core.fs.FileSystem;
import org.apache.jackrabbit.core.fs.FileSystemException;
import org.apache.jackrabbit.core.fs.FileSystemResource;
import org.apache.jackrabbit.core.lock.LockManager;
import org.apache.jackrabbit.core.lock.LockManagerImpl;
import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
import org.apache.jackrabbit.core.nodetype.virtual.VirtualNodeTypeStateManager;
import org.apache.jackrabbit.core.observation.DelegatingObservationDispatcher;
import org.apache.jackrabbit.core.observation.ObservationManagerFactory;
import org.apache.jackrabbit.core.security.AuthContext;
import org.apache.jackrabbit.core.state.ItemStateException;
import org.apache.jackrabbit.core.state.PMContext;
import org.apache.jackrabbit.core.state.PersistenceManager;
import org.apache.jackrabbit.core.state.SharedItemStateManager;
import org.apache.jackrabbit.core.version.VersionManager;
import org.apache.jackrabbit.core.version.VersionManagerImpl;
import org.apache.jackrabbit.name.QName;
import org.apache.jackrabbit.name.NoPrefixDeclaredException;
import org.apache.jackrabbit.uuid.UUID;
import org.apache.log4j.Logger;

import javax.jcr.AccessDeniedException;
import javax.jcr.Credentials;
import javax.jcr.LoginException;
import javax.jcr.NamespaceRegistry;
import javax.jcr.NoSuchWorkspaceException;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
import javax.jcr.observation.ObservationManager;
import javax.security.auth.Subject;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.Arrays;
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;

/**
* A <code>RepositoryImpl</code> ...
*/
public class RepositoryImpl implements Repository, SessionListener,
        EventListener {

    private static Logger log = Logger.getLogger(RepositoryImpl.class);

    /**
     * repository home lock
     */
    private static final String REPOSITORY_LOCK = ".lock";

    /**
     * hardcoded uuid of the repository root node
     */
    private static final String ROOT_NODE_UUID = "cafebabe-cafe-babe-cafe-babecafebabe";

    private static final String SYSTEM_ROOT_NODE_UUID = "deadbeef-cafe-babe-cafe-babecafebabe";
    private static final String VERSION_STORAGE_NODE_UUID = "deadbeef-face-babe-cafe-babecafebabe";
    private static final String NODETYPES_NODE_UUID = "deadbeef-cafe-cafe-cafe-babecafebabe";

    /**
     * the name of the filesystem resource containing the properties of the
     * repository.
     */
    private static final String PROPERTIES_RESOURCE = "rep.properties";

    /**
     * the repository properties.
     */
    private final Properties repProps;

    // names of well-known repository properties
    public static final String STATS_NODE_COUNT_PROPERTY = "jcr.repository.stats.nodes.count";
    public static final String STATS_PROP_COUNT_PROPERTY = "jcr.repository.stats.properties.count";

    private String rootNodeUUID;

    private final NamespaceRegistryImpl nsReg;
    private final NodeTypeRegistry ntReg;
    private final VersionManager vMgr;
    private final VirtualNodeTypeStateManager virtNTMgr;

    /**
     * Search manager for the jcr:system tree. May be <code>null</code> if
     * none is configured.
     */
    private SearchManager systemSearchMgr;

    // configuration of the repository
    protected final RepositoryConfig repConfig;

    // the master filesystem
    private final FileSystem repStore;

    // sub file system where the repository stores meta data such as uuid of root node, etc.
    private final FileSystem metaDataStore;

    /**
     * the delegating observation dispatcher for all workspaces
     */
    private final DelegatingObservationDispatcher delegatingDispatcher =
            new DelegatingObservationDispatcher();

    /**
     * map of workspace names and <code>WorkspaceInfo<code>s.
     */
    private final HashMap wspInfos = new HashMap();

    /**
     * active sessions (weak references)
     */
    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;

    // flag indicating if respository has been shut down
    private boolean disposed = false;

    /**
     * the lock that guards instantiation of multiple repositories.
     */
    private FileLock repLock;

    /**
     * private constructor
     *
     * @param repConfig
     */
    protected RepositoryImpl(RepositoryConfig repConfig) throws RepositoryException {

        log.info("Starting repository...");

        this.repConfig = repConfig;

        acquireRepositoryLock() ;

        // setup file systems
        repStore = repConfig.getFileSystem();
        String fsRootPath = "/meta";
        try {
            if (!repStore.exists(fsRootPath) || !repStore.isFolder(fsRootPath)) {
                repStore.createFolder(fsRootPath);
            }
        } catch (FileSystemException fse) {
            String msg = "failed to create folder for repository meta data";
            log.fatal(msg, fse);
            throw new RepositoryException(msg, fse);
        }
        metaDataStore = new BasedFileSystem(repStore, fsRootPath);

        // init root node uuid
        rootNodeUUID = loadRootNodeUUID(metaDataStore);

        // load repository properties
        repProps = loadRepProps();
        nodesCount = Long.parseLong(repProps.getProperty(STATS_NODE_COUNT_PROPERTY, "0"));
        propsCount = Long.parseLong(repProps.getProperty(STATS_PROP_COUNT_PROPERTY, "0"));

        // create registries
        nsReg = createNamespaceRegistry(new BasedFileSystem(repStore, "/namespaces"));
        ntReg = createNodeTypeRegistry(nsReg, new BasedFileSystem(repStore, "/nodetypes"));

        // init workspace configs
        Iterator iter = repConfig.getWorkspaceConfigs().iterator();
        while (iter.hasNext()) {
            WorkspaceConfig config = (WorkspaceConfig) iter.next();
            WorkspaceInfo info = createWorkspaceInfo(config);
            wspInfos.put(config.getName(), info);
        }

        // init version manager
        vMgr = createVersionManager(repConfig.getVersioningConfig(),
                delegatingDispatcher);

        // init virtual node type manager
        virtNTMgr = new VirtualNodeTypeStateManager(getNodeTypeRegistry(),
                delegatingDispatcher, NODETYPES_NODE_UUID, SYSTEM_ROOT_NODE_UUID);

        // initialize default workspace
        String wspName = repConfig.getDefaultWorkspaceName();
        try {
            initWorkspace((WorkspaceInfo) wspInfos.get(wspName));
        } catch (RepositoryException e) {
            // if default workspace failed to initialize, shutdown again
            log.fatal("Failed to initialize workspace '" + wspName + "'", e);
            log.fatal("Unable to start repository, forcing shutdown...");
            shutdown();
            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");
    }

    /**
     * Creates the version manager.
     *
     * @param vConfig the versioning config
     * @return the newly created version manager
     * @throws RepositoryException if an error occurrs
     */
    protected VersionManager createVersionManager(VersioningConfig vConfig,
                                                  DelegatingObservationDispatcher delegatingDispatcher)
            throws RepositoryException {
        PersistenceManager pm = createPersistenceManager(vConfig.getHomeDir(),
                vConfig.getFileSystem(),
                vConfig.getPersistenceManagerConfig(),
                rootNodeUUID,
                nsReg,
                ntReg);
        return new VersionManagerImpl(pm, ntReg, delegatingDispatcher, VERSION_STORAGE_NODE_UUID, SYSTEM_ROOT_NODE_UUID);
    }

    /**
     * Lock the repository home.
     *
     * @throws RepositoryException if the repository lock can not be acquired
     */
    protected void acquireRepositoryLock() throws RepositoryException {
        File home = new File(this.repConfig.getHomeDir());
        File lock = new File(home, REPOSITORY_LOCK);

        if (lock.exists()) {
            log.warn("Existing lock file at " + lock.getAbsolutePath() +
                    " deteteced. Repository was not shut down properly.");
        } else {
            try {
                lock.createNewFile();
            } catch (IOException e) {
                throw new RepositoryException(
                    "Unable to create lock file at " + lock.getAbsolutePath(), e);
            }
        }
        try {
            repLock = new RandomAccessFile(lock, "rw").getChannel().tryLock();
        } catch (IOException e) {
            throw new RepositoryException(
                "Unable to lock file at " + lock.getAbsolutePath(), e);
        }
        if (repLock == null) {
            throw new RepositoryException(
                    "The repository home at " + home.getAbsolutePath() +
                    " appears to be in use since the file at " +
                    lock.getAbsolutePath() + " is locked by another process.");
        }
    }

    /**
     * Release repository lock
     */
    protected void releaseRepositoryLock() {
        if (repLock != null) {
            try {
                FileChannel channel = repLock.channel();
                repLock.release();
                channel.close();
            } catch (IOException e) {
                // ignore
            }
        }
        repLock = null;

        File home = new File(this.repConfig.getHomeDir());
        File lock = new File(home, REPOSITORY_LOCK);
        if (!lock.delete()) {
            log.error("Unable to release repository lock");
        }
    }

    /**
     * Returns the root node uuid.
     * @param fs
     * @return
     * @throws RepositoryException
     */
    protected String loadRootNodeUUID(FileSystem fs) throws RepositoryException {
        FileSystemResource uuidFile = new FileSystemResource(fs, "rootUUID");
        try {
            if (uuidFile.exists()) {
                try {
                    // load uuid of the repository's root node
                    InputStream in = uuidFile.getInputStream();
/*
                   // uuid is stored in binary format (16 bytes)
                   byte[] bytes = new byte[16];
                   try {
                       in.read(bytes);
                   } finally {
                       try {
                           in.close();
                       } catch (IOException ioe) {
                           // ignore
                       }
                   }
                   rootNodeUUID = new UUID(bytes).toString();            // uuid is stored in binary format (16 bytes)
*/
                    // uuid is stored in text format (36 characters) for better readability
                    char[] chars = new char[36];
                    InputStreamReader reader = new InputStreamReader(in);
                    try {
                        reader.read(chars);
                    } finally {
                        try {
                            reader.close();
                        } catch (IOException ioe) {
                            // ignore
                        }
                    }
                    return new UUID(new String(chars)).toString();
                } catch (Exception e) {
                    String msg = "failed to load persisted repository state";
                    log.debug(msg);
                    throw new RepositoryException(msg, e);
                }
            } else {
                // create new uuid
/*
                UUID rootUUID = UUID.randomUUID();     // version 4 uuid
                rootNodeUUID = rootUUID.toString();
*/
                /**
                 * use hard-coded uuid for root node rather than generating
                 * a different uuid per repository instance; using a
                 * hard-coded uuid makes it easier to copy/move entire
                 * workspaces from one repository instance to another.
                 */
                try {
                    // persist uuid of the repository's root node
                    OutputStream out = uuidFile.getOutputStream();
/*
                    // store uuid in binary format
                    try {
                        out.write(rootUUID.getBytes());
                    } finally {
                        try {
                            out.close();
                        } catch (IOException ioe) {
                            // ignore
                        }
                    }
*/
                    // store uuid in text format for better readability
                    OutputStreamWriter writer = new OutputStreamWriter(out);
                    try {
                        writer.write(ROOT_NODE_UUID);
                    } finally {
                        try {
                            writer.close();
                        } catch (IOException ioe) {
                            // ignore
                        }
                    }
                    return ROOT_NODE_UUID;
                } catch (Exception e) {
                    String msg = "failed to persist repository state";
                    log.debug(msg);
                    throw new RepositoryException(msg, e);
                }
            }
        } catch (FileSystemException fse) {
            String msg = "failed to access repository state";
            log.debug(msg);
            throw new RepositoryException(msg, fse);
        }
    }

    /**
     * Creates the <code>NamespaceRegistry</code> instance.
     *
     * @param fs
     * @return
     * @throws RepositoryException
     */
    protected NamespaceRegistryImpl createNamespaceRegistry(FileSystem fs)
            throws RepositoryException {
        return new NamespaceRegistryImpl(fs);
    }

    /**
     * Creates the <code>NodeTypeRegistry</code> instance.
     *
     * @param fs
     * @return
     * @throws RepositoryException
     */
    protected NodeTypeRegistry createNodeTypeRegistry(NamespaceRegistry nsReg,
                                                      FileSystem fs)
            throws RepositoryException {
        return NodeTypeRegistry.create(nsReg, fs);
    }

    /**
     * Creates a new <code>RepositoryImpl</code> instance.
     * <p/>
     *
     * @param config the configuration of the repository
     * @return a new <code>RepositoryImpl</code> instance
     * @throws RepositoryException If an error occurs
     */
    public static RepositoryImpl create(RepositoryConfig config)
            throws RepositoryException {
        return new RepositoryImpl(config);
    }

    /**
     * Performs a sanity check on this repository instance.
     *
     * @throws IllegalStateException if this repository has been rendered
     *                               invalid for some reason (e.g. if it has
     *                               been shut down)
     */
    protected void sanityCheck() throws IllegalStateException {
        // check repository status
        if (disposed) {
            throw new IllegalStateException("repository instance has been shut down");
        }
    }

    private void initWorkspace(WorkspaceInfo wspInfo) throws RepositoryException {
        // first initialize workspace info
        wspInfo.initialize();
        // get system session and Workspace instance
        SessionImpl sysSession = wspInfo.getSystemSession();
        WorkspaceImpl wsp = (WorkspaceImpl) sysSession.getWorkspace();

        /**
         * todo implement 'System' workspace
         * FIXME
         * - the should be one 'System' workspace per repository
         * - the 'System' workspace should have the /jcr:system node
         * - versions, version history and node types should be reflected in
         *   this system workspace as content under /jcr:system
         * - all other workspaces should be dynamic workspaces based on
         *   this 'read-only' system workspace
         *
         * for now, we just create a /jcr:system node in every workspace
         */
        NodeImpl rootNode = (NodeImpl) sysSession.getRootNode();
        if (!rootNode.hasNode(QName.JCR_SYSTEM)) {
            NodeTypeImpl nt = sysSession.getNodeTypeManager().getNodeType(QName.REP_SYSTEM);
            NodeImpl sysRoot = rootNode.internalAddChildNode(QName.JCR_SYSTEM, nt, SYSTEM_ROOT_NODE_UUID);
            // add version storage
            nt = sysSession.getNodeTypeManager().getNodeType(QName.REP_VERSIONSTORAGE);
            sysRoot.internalAddChildNode(QName.JCR_VERSIONSTORAGE, nt, VERSION_STORAGE_NODE_UUID);
            // add node types
            nt = sysSession.getNodeTypeManager().getNodeType(QName.REP_NODETYPES);
            sysRoot.internalAddChildNode(QName.JCR_NODETYPES, nt, NODETYPES_NODE_UUID);
            rootNode.save();
        }

        // register the repository as event listener for keeping repository statistics
        wsp.getObservationManager().addEventListener(this,
                Event.NODE_ADDED | Event.NODE_REMOVED
                | Event.PROPERTY_ADDED | Event.PROPERTY_REMOVED,
                "/", true, null, null, false);

        // register SearchManager as event listener
        SearchManager searchMgr = wspInfo.getSearchManager();
        if (searchMgr != null) {
            wsp.getObservationManager().addEventListener(searchMgr,
                    Event.NODE_ADDED | Event.NODE_REMOVED
                    | Event.PROPERTY_ADDED | Event.PROPERTY_REMOVED
                    | Event.PROPERTY_CHANGED,
                    "/", true, null, null, false);
        }

        // register the observation factory of that workspace
        delegatingDispatcher.addDispatcher(wspInfo.getObservationManagerFactory());
    }

    /**
     * Returns the system search manager or <code>null</code> if none is
     * configured.
     */
    private SearchManager getSystemSearchManager(String wspName)
            throws RepositoryException {
        if (systemSearchMgr == null) {
            try {
                if (repConfig.getSearchConfig() != null) {
                    SystemSession defSysSession = getSystemSession(wspName);
                    systemSearchMgr = new SystemSearchManager(repConfig.getSearchConfig(),
                            nsReg, ntReg, defSysSession.getItemStateManager(), SYSTEM_ROOT_NODE_UUID);
                    ObservationManager obsMgr = defSysSession.getWorkspace().getObservationManager();
                    obsMgr.addEventListener(systemSearchMgr, Event.NODE_ADDED |
                            Event.NODE_REMOVED | Event.PROPERTY_ADDED |
                            Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED,
                            "/" + QName.JCR_SYSTEM.toJCRName(defSysSession.getNamespaceResolver()),
                            true, null, null, false);
                } else {
                    systemSearchMgr = null;
                }
            } catch (NoPrefixDeclaredException e) {
                throw new RepositoryException(e);
            }
        }
        return systemSearchMgr;
    }

    NamespaceRegistryImpl getNamespaceRegistry() {
        // check sanity of this instance
        sanityCheck();

        return nsReg;
    }

    NodeTypeRegistry getNodeTypeRegistry() {
        // check sanity of this instance
        sanityCheck();

        return ntReg;
    }

    VersionManager getVersionManager() {
        // check sanity of this instance
        sanityCheck();

        return vMgr;
    }

    String getRootNodeUUID() {
        // check sanity of this instance
        sanityCheck();

        return rootNodeUUID;
    }

    /**
     * Returns the names of <i>all</i> workspaces in this repository.
     *
     * @return the names of all workspaces in this repository.
     * @see javax.jcr.Workspace#getAccessibleWorkspaceNames()
     */
    String[] getWorkspaceNames() {
        return (String[]) wspInfos.keySet().toArray(new String[wspInfos.keySet().size()]);
    }

    /**
     * Returns the {@link WorkspaceInfo} for the named workspace.
     *
     * @param workspaceName The name of the workspace whose {@link WorkspaceInfo}
     *                      is to be returned. This must not be <code>null</code>.
     * @return The {@link WorkspaceInfo} for the named workspace. This will
     *         never be <code>null</code>.
     * @throws IllegalStateException    If this repository has already been
     *                                  shut down.
     * @throws NoSuchWorkspaceException If the named workspace does not exist.
     */
    protected WorkspaceInfo getWorkspaceInfo(String workspaceName)
            throws IllegalStateException, NoSuchWorkspaceException {
        // check sanity of this instance
        sanityCheck();

        WorkspaceInfo 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);
                }
            }
        }
        return wspInfo;
    }

    /**
     * Creates a workspace with the given name.
     *
     * @param workspaceName name of the new workspace
     * @throws RepositoryException if a workspace with the given name
     *                             already exists or if another error occurs
     * @see SessionImpl#createWorkspace(String)
     */
    protected synchronized void createWorkspace(String workspaceName)
            throws RepositoryException {
        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);
    }

    SharedItemStateManager getWorkspaceStateManager(String workspaceName)
            throws NoSuchWorkspaceException, RepositoryException {
        // check sanity of this instance
        sanityCheck();

        return getWorkspaceInfo(workspaceName).getItemStateProvider();
    }

    ObservationManagerFactory getObservationManagerFactory(String workspaceName)
            throws NoSuchWorkspaceException {
        // check sanity of this instance
        sanityCheck();

        return getWorkspaceInfo(workspaceName).getObservationManagerFactory();
    }

    /**
     * Returns the {@link SearchManager} for the workspace with name
     * <code>workspaceName</code>.
     *
     * @param workspaceName the name of the workspace.
     * @return the <code>SearchManager</code> for the workspace, or
     *         <code>null</code> if the workspace does not have a
     *         <code>SearchManager</code> configured.
     * @throws NoSuchWorkspaceException if there is no workspace with name
     *                                  <code>workspaceName</code>.
     * @throws RepositoryException      if an error occurs while opening the
     *                                  search index.
     */
    SearchManager getSearchManager(String workspaceName)
            throws NoSuchWorkspaceException, RepositoryException {
        // check sanity of this instance
        sanityCheck();

        return getWorkspaceInfo(workspaceName).getSearchManager();
    }

    /**
     * Returns the {@link LockManager} for the workspace with name
     * <code>workspaceName</code>
     *
     * @param workspaceName workspace name
     * @return <code>LockManager</code> for the workspace
     * @throws NoSuchWorkspaceException if such a workspace does not exist
     * @throws RepositoryException      if some other error occurs
     */
    LockManager getLockManager(String workspaceName) throws
            NoSuchWorkspaceException, RepositoryException {
        // check sanity of this instance
        sanityCheck();

        return getWorkspaceInfo(workspaceName).getLockManager();
    }

    /**
     * Returns the {@link SystemSession} for the workspace with name
     * <code>workspaceName</code>
     *
     * @param workspaceName workspace name
     * @return system session of the specified workspace
     * @throws NoSuchWorkspaceException if such a workspace does not exist
     * @throws RepositoryException      if some other error occurs
     */
    SystemSession getSystemSession(String workspaceName)
            throws NoSuchWorkspaceException, RepositoryException {
        // check sanity of this instance
        sanityCheck();

        return getWorkspaceInfo(workspaceName).getSystemSession();
    }

    /**
     * Creates a new repository session on the specified workspace for the
     * <b><i>authenticated</i></b> subject of the given login context and
     * adds it to the <i>active</i> sessions.
     * <p/>
     * Calls {@link #createSessionInstance(AuthContext, WorkspaceConfig)} to
     * create the actual <code>SessionImpl</code> instance.
     *
    * @param loginContext  login context with authenticated subject
     * @param workspaceName workspace name
     * @return a new session
     * @throws NoSuchWorkspaceException if the specified workspace does not exist
     * @throws AccessDeniedException    if the subject of the given login context
     *                                  is not granted access to the specified
     *                                  workspace
     * @throws RepositoryException      if another error occurs
     */
    protected synchronized 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);
        return ses;
    }

    /**
     * Creates a new repository session on the specified workspace for the given
     * <b><i>authenticated</i></b> subject and adds it to the <i>active</i>
     * sessions.
     * <p/>
     * Calls {@link #createSessionInstance(Subject, WorkspaceConfig)} to
     * create the actual <code>SessionImpl</code> instance.
     *
     * @param subject       authenticated subject
     * @param workspaceName workspace name
     * @return a new session
     * @throws NoSuchWorkspaceException if the specified workspace does not exist
     * @throws AccessDeniedException    if the subject of the given login context
     *                                  is not granted access to the specified
     *                                  workspace
     * @throws RepositoryException      if another error occurs
     */
    protected synchronized 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);
        return ses;
    }

    /**
     * Shuts down this repository.
     */
    public synchronized void shutdown() {
        // check status of this instance
        if (disposed) {
            // there's nothing to do here because the repository has already been shut down
            return;
        }

        log.info("Shutting down repository...");

        // close active user sessions
        while (!activeSessions.isEmpty()) {
           ((Session) activeSessions.values().iterator().next()).logout();
       }

        // shut down workspaces
        for (Iterator it = wspInfos.values().iterator(); it.hasNext();) {
            WorkspaceInfo wspInfo = (WorkspaceInfo) it.next();
            synchronized(wspInfo) {
                if (wspInfo.isInitialized()) {
                    wspInfo.dispose();
                }
            }
        }

        // shutdown system search manager if there is one
        if (systemSearchMgr != null) {
            systemSearchMgr.close();
        }

        try {
            vMgr.close();
        } catch (Exception e) {
            log.error("Error while closing Version Manager.", e);
        }

        // persist repository properties
        try {
            storeRepProps(repProps);
        } catch (RepositoryException e) {
            log.error("failed to persist repository properties", e);
        }

        try {
            // close repository file system
            repStore.close();
        } catch (FileSystemException e) {
            log.error("error while closing repository filesystem", e);
        }

        try {
            // close versioning file system
            repConfig.getVersioningConfig().getFileSystem().close();
        } catch (FileSystemException e) {
            log.error("error while closing versioning filesystem", e);
        }

        // 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");
    }

    /**
     * Returns the configuration of this repository.
     * @return repository configuration
     */
    public RepositoryConfig getConfig() {
        return repConfig;
    }

    /**
     * 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
     * found in the class path and (re)sets the statistics properties, if not
     * present.
     *
     * @param props the properties object to load
     *
     * @throws RepositoryException if the properties can not be loaded
     */
    protected void setDefaultRepositoryProperties(Properties props)
            throws RepositoryException {
        InputStream in = RepositoryImpl.class.getResourceAsStream("repository.properties");
        try {
            props.load(in);
            in.close();

            // set counts
            if (!props.containsKey(STATS_NODE_COUNT_PROPERTY)) {
                props.setProperty(STATS_NODE_COUNT_PROPERTY, Long.toString(nodesCount));
            }
            if (!props.containsKey(STATS_PROP_COUNT_PROPERTY)) {
                props.setProperty(STATS_PROP_COUNT_PROPERTY, Long.toString(propsCount));
            }
        } catch (IOException e) {
            String msg = "Failed to load repository properties: " +e.toString();
            log.error(msg);
            throw new RepositoryException(msg, e);
        }
    }

    /**
     * Loads the repository properties by executing the following steps:
     * <ul>
     * <li> if the {@link #PROPERTIES_RESOURCE} exists in the meta data store,
     * the properties are loaded from that resource.</li>
     * <li> {@link #setDefaultRepositoryProperties(Properties)} is called
     * afterwards in order to initialize/update the repository properties
     * since some default properties might have changed and need updating.</li>
     * <li> finally {@link #storeRepProps(Properties)} is called in order to
     * persist the newly generated properties.</li>
     * </ul>
     *
     * @return the newly loaded/initialized repository properties
     *
     * @throws RepositoryException
     */
    protected Properties loadRepProps() throws RepositoryException {
        FileSystemResource propFile = new FileSystemResource(metaDataStore, PROPERTIES_RESOURCE);
        try {
            Properties props = new Properties();
            if (propFile.exists()) {
                InputStream in = propFile.getInputStream();
                try {
                    props.load(in);
                } finally {
                    in.close();
                }
            }
            // now set the default props
            setDefaultRepositoryProperties(props);

            // and store
            storeRepProps(props);

            return props;

        } catch (Exception e) {
            String msg = "failed to load repository properties";
            log.debug(msg);
            throw new RepositoryException(msg, e);
        }
    }

    /**
     * Stores the properties to a persistent resource in the meta filesytem.
     *
     * @throws RepositoryException
     */
    protected void storeRepProps(Properties props) throws RepositoryException {
        FileSystemResource propFile = new FileSystemResource(metaDataStore, PROPERTIES_RESOURCE);
        try {
            propFile.makeParentDirs();
            OutputStream os = propFile.getOutputStream();
            try {
                props.store(os, null);
            } finally {
                // make sure stream is closed
                os.close();
            }
        } catch (Exception e) {
            String msg = "failed to persist repository properties";
            log.debug(msg);
            throw new RepositoryException(msg, e);
        }
    }

    /**
     * Creates a workspace persistence manager based on the given
     * configuration. The persistence manager is instantiated using
     * information in the given persistence manager configuration and
     * initialized with a persistence manager context containing the other
     * arguments.
     *
     * @return the created workspace persistence manager
     * @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)
            throws RepositoryException {
        try {
            PersistenceManager pm = (PersistenceManager) pmConfig.newInstance();
            pm.init(new PMContext(homeDir, fs, rootNodeUUID, nsReg, ntReg));
            return pm;
        } catch (Exception e) {
            String msg = "Cannot instantiate persistence manager " + pmConfig.getClassName();
            throw new RepositoryException(msg, e);
        }
    }

    //-----------------------------------------------------------< Repository >
    /**
     * {@inheritDoc}
     */
    public Session login(Credentials credentials, String workspaceName)
            throws LoginException, NoSuchWorkspaceException, RepositoryException {
        // check sanity of this instance
        sanityCheck();

        if (workspaceName == null) {
            workspaceName = repConfig.getDefaultWorkspaceName();
        }

        // check if workspace exists (will throw NoSuchWorkspaceException if not)
        getWorkspaceInfo(workspaceName);

        if (credentials == null) {
            // null credentials, obtain the identity of the already-authenticated
            // subject from access control context
            AccessControlContext acc = AccessController.getContext();
            Subject subject;
            try {
                subject = Subject.getSubject(acc);
            } catch (SecurityException se) {
                throw new LoginException(se.getMessage());
            }
            if (subject == null) {
                throw new LoginException("No Subject associated with AccessControlContext");
            }
            // create session
            try {
                return createSession(subject, workspaceName);
            } catch (AccessDeniedException ade) {
                // authenticated subject is not authorized for the specified workspace
                throw new LoginException(ade.getMessage());
            }
        }

        // login either using JAAS or our own LoginModule
        AuthContext authCtx;
        try {
            LoginModuleConfig lmc = repConfig.getLoginModuleConfig();
            if (lmc == null) {
                authCtx = new AuthContext.JAAS(repConfig.getAppName(), credentials);
            } else {
                authCtx = new AuthContext.Local(
                        lmc.getLoginModule(), lmc.getParameters(), credentials);
            }
            authCtx.login();
        } catch (javax.security.auth.login.LoginException le) {
            throw new LoginException(le.getMessage());
        }

        // create session
        try {
            return createSession(authCtx, workspaceName);
        } catch (AccessDeniedException ade) {
            // authenticated subject is not authorized for the specified workspace
            throw new LoginException(ade.getMessage());
        }
    }

    /**
     * {@inheritDoc}
     */
    public Session login(String workspaceName)
            throws LoginException, NoSuchWorkspaceException, RepositoryException {
        return login(null, workspaceName);
    }

    /**
     * {@inheritDoc}
     */
    public Session login() throws LoginException, RepositoryException {
        return login(null, null);
    }

    /**
     * {@inheritDoc}
     */
    public Session login(Credentials credentials)
            throws LoginException, RepositoryException {
        return login(credentials, null);
    }

    /**
     * {@inheritDoc}
     */
    public String getDescriptor(String key) {
        return repProps.getProperty(key);
    }

    /**
     * {@inheritDoc}
     */
    public String[] getDescriptorKeys() {
        String[] keys = (String[]) repProps.keySet().toArray(new String[repProps.keySet().size()]);
        Arrays.sort(keys);
        return keys;
    }

    //------------------------------------------------------< SessionListener >
    /**
     * {@inheritDoc}
     */
    public void loggingOut(SessionImpl session) {
    }

    /**
     * {@inheritDoc}
     */
    public synchronized void loggedOut(SessionImpl session) {
        // remove session from active sessions
        activeSessions.remove(session);
    }

    //--------------------------------------------------------< EventListener >
    /**
     * {@inheritDoc}
     */
    public void onEvent(EventIterator events) {
        // check status of this instance
        if (disposed) {
            // ignore, repository instance has been shut down
            return;
        }

        synchronized (repProps) {
            while (events.hasNext()) {
                Event event = events.nextEvent();
                long type = event.getType();
                if ((type & Event.NODE_ADDED) == Event.NODE_ADDED) {
                    nodesCount++;
                    repProps.setProperty(STATS_NODE_COUNT_PROPERTY, Long.toString(nodesCount));
                }
                if ((type & Event.NODE_REMOVED) == Event.NODE_REMOVED) {
                    nodesCount--;
                    repProps.setProperty(STATS_NODE_COUNT_PROPERTY, Long.toString(nodesCount));
                }
                if ((type & Event.PROPERTY_ADDED) == Event.PROPERTY_ADDED) {
                    propsCount++;
                    repProps.setProperty(STATS_PROP_COUNT_PROPERTY, Long.toString(propsCount));
                }
                if ((type & Event.PROPERTY_REMOVED) == Event.PROPERTY_REMOVED) {
                    propsCount--;
                    repProps.setProperty(STATS_PROP_COUNT_PROPERTY, Long.toString(propsCount));
                }
            }
        }
    }

    //------------------------------------------< overridable factory methods >
    /**
     * Creates an instance of the {@link SessionImpl} class representing a
     * user authenticated by the <code>loginContext</code> instance attached
     * to the workspace configured by the <code>wspConfig</code>.
     *
     * @throws AccessDeniedException if the subject of the given login context
     *                               is not granted access to the specified
     *                               workspace
     * @throws RepositoryException   If any other error occurrs creating the
     *                               session.
     */
    protected SessionImpl createSessionInstance(AuthContext loginContext,
                                                WorkspaceConfig wspConfig)
            throws AccessDeniedException, RepositoryException {

        return new XASessionImpl(this, loginContext, wspConfig);
    }

    /**
     * Creates an instance of the {@link SessionImpl} class representing a
     * user represented by the <code>subject</code> instance attached
     * to the workspace configured by the <code>wspConfig</code>.
     *
     * @throws AccessDeniedException if the subject of the given login context
     *                               is not granted access to the specified
     *                               workspace
     * @throws RepositoryException   If any other error occurrs creating the
     *                               session.
     */
    protected SessionImpl createSessionInstance(Subject subject,
                                                WorkspaceConfig wspConfig)
            throws AccessDeniedException, RepositoryException {

        return new XASessionImpl(this, subject, wspConfig);
    }

    /**
     * Creates a new {@link RepositoryImpl.WorkspaceInfo} instance for
     * <code>wspConfig</code>.
     *
     * @param wspConfig the workspace configuration.
     * @return a new <code>WorkspaceInfo</code> instance.
     */
    protected WorkspaceInfo createWorkspaceInfo(WorkspaceConfig wspConfig) {
        return new WorkspaceInfo(wspConfig);
    }

    //--------------------------------------------------------< inner classes >
    /**
     * <code>WorkspaceInfo</code> holds the objects that are shared
     * among multiple per-session <code>WorkspaceImpl</code> instances
     * representing the same named workspace, i.e. the same physical
     * storage.
     */
    protected class WorkspaceInfo {

        /**
         * workspace configuration (passed in constructor)
         */
        private final WorkspaceConfig config;

        /**
         * file system (instantiated on init)
         */
        private FileSystem fs;

        /**
         * persistence manager (instantiated on init)
         */
        private PersistenceManager persistMgr;

        /**
         * item state provider (instantiated on init)
         */
        private SharedItemStateManager itemStateMgr;

        /**
         * observation manager factory (instantiated on init)
         */
        private ObservationManagerFactory obsMgrFactory;

        /**
         * system session (lazily instantiated)
         */
        private SystemSession systemSession;

        /**
         * search manager (lazily instantiated)
         */
        private SearchManager searchMgr;

        /**
         * lock manager (lazily instantiated)
         */
        private LockManagerImpl lockMgr;

        /**
         * flag indicating whether this instance has been initialized.
         */
        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>.
         *
         * @param config workspace configuration
         */
        protected WorkspaceInfo(WorkspaceConfig config) {
            this.config = config;
            idleTimestamp = 0;
            initialized = false;
        }

        /**
         * Returns the workspace name.
         *
         * @return the workspace name
         */
        String getName() {
            return config.getName();
        }

        /**
         * Returns the workspace configuration.
         *
         * @return the workspace configuration
         */
        public WorkspaceConfig getConfig() {
            return config;
        }

        /**
         * 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>.
         *
         * @return <code>true</code> if this workspace info is initialized.
         */
        synchronized boolean isInitialized() {
            return initialized;
        }

        /**
         * Returns the workspace file system.
         *
         * @return the workspace file system
         */
        synchronized FileSystem getFileSystem() {
            if (!initialized) {
                throw new IllegalStateException("not initialized");
            }

            return fs;
        }

        /**
         * Returns the workspace persistence manager.
         *
         * @return the workspace persistence manager
         * @throws RepositoryException if the persistence manager could not be instantiated/initialized
         */
        synchronized PersistenceManager getPersistenceManager()
                throws RepositoryException {
            if (!initialized) {
                throw new IllegalStateException("not initialized");
            }

            return persistMgr;
        }

        /**
         * Returns the workspace item state provider
         *
         * @return the workspace item state provider
         * @throws RepositoryException if the workspace item state provider
         *                             could not be created
         */
        synchronized SharedItemStateManager getItemStateProvider()
                throws RepositoryException {
            if (!initialized) {
                throw new IllegalStateException("not initialized");
            }

            return itemStateMgr;
        }

        /**
         * Returns the observation manager factory for this workspace
         *
         * @return the observation manager factory for this workspace
         */
        synchronized ObservationManagerFactory getObservationManagerFactory() {
            if (!initialized) {
                throw new IllegalStateException("not initialized");
            }

            return obsMgrFactory;
        }

        /**
         * Returns the search manager for this workspace.
         *
         * @return the search manager for this workspace, or <code>null</code>
         *         if no <code>SearchManager</code>
         * @throws RepositoryException if the search manager could not be created
         */
        synchronized SearchManager getSearchManager() throws RepositoryException {
            if (!initialized) {
                throw new IllegalStateException("not initialized");
            }

            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,
                        rootNodeUUID,
                        getSystemSearchManager(getName()),
                        SYSTEM_ROOT_NODE_UUID);
            }
            return searchMgr;
        }

        /**
         * Returns the lock manager for this workspace.
         *
         * @return the lock manager for this workspace
         * @throws RepositoryException if the lock manager could not be created
         */
        synchronized LockManager getLockManager() throws RepositoryException {
            if (!initialized) {
                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);
            }
            return lockMgr;
        }

        /**
         * Returns the system session for this workspace.
         *
         * @return the system session for this workspace
         * @throws RepositoryException if the system session could not be created
         */
        synchronized SystemSession getSystemSession() throws RepositoryException {
            if (!initialized) {
                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);
            }
            return systemSession;
        }

        /**
         * Initializes this workspace info. The following components are
         * initialized immediately:
         * <ul>
         * <li>file system</li>
         * <li>persistence manager</li>
         * <li>shared item state manager</li>
         * <li>observation manager factory</li>
         * </ul>
         * The following components are initialized lazily (i.e. on demand)
         * in order to save resources and to avoid 'chicken & egg' bootstrap
         * problems:
         * <ul>
         * <li>system session</li>
         * <li>lock manager</li>
         * <li>search manager</li>
         * </ul>
         */
        synchronized void initialize() throws RepositoryException {
            if (initialized) {
                throw new IllegalStateException("already initialized");
            }

            log.info("initializing workspace '" + getName() + "'...");

            FileSystemConfig fsConfig = config.getFileSystemConfig();
            fsConfig.init();
            fs = fsConfig.getFileSystem();

            persistMgr = createPersistenceManager(new File(config.getHomeDir()),
                    fs,
                    config.getPersistenceManagerConfig(),
                    rootNodeUUID,
                    nsReg,
                    ntReg);

            // create item state manager
            try {
                itemStateMgr =
                        new SharedItemStateManager(persistMgr, rootNodeUUID, 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);
            }

            obsMgrFactory = new ObservationManagerFactory();

            initialized = true;

            log.info("workspace '" + getName() + "' initialized");
        }

        /**
         * Disposes all objects this <code>WorkspaceInfo</code> is holding.
         */
        synchronized void dispose() {
            if (!initialized) {
                throw new IllegalStateException("not initialized");
            }

            log.info("shutting down workspace '" + getName() + "'...");

            // dispose observation manager factory
            obsMgrFactory.dispose();
            obsMgrFactory = null;

            // shutdown search managers
            if (searchMgr != null) {
                searchMgr.close();
                searchMgr = null;
            }

            // close system session
            if (systemSession != null) {
                systemSession.removeListener(RepositoryImpl.this);
                systemSession.logout();
                systemSession = null;
            }

            // dispose shared item state manager
            itemStateMgr.dispose();
            itemStateMgr = 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 lock manager
            if (lockMgr != null) {
                lockMgr.close();
                lockMgr = null;
            }

            // close workspace file system
            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();
                            }
                        }
                    }
                }
            }
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.core.RepositoryImpl$WorkspaceJanitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.