@Override
  public void initialize(ServletContext servletContext) {

    String contentPrimaryTypes = getParam(servletContext, INIT_CONTENT_PRIMARY_TYPE_NAMES);
    String resourcePrimaryTypes = getParam(servletContext, INIT_RESOURCE_PRIMARY_TYPES_NAMES);
    String newFolderPrimaryType = getParam(servletContext, INIT_NEW_FOLDER_PRIMARY_TYPE_NAME);
    String newResourcePrimaryType = getParam(servletContext, INIT_NEW_RESOURCE_PRIMARY_TYPE_NAME);
    String newContentPrimaryType = getParam(servletContext, INIT_NEW_CONTENT_PRIMARY_TYPE_NAME);

    logger.debug("DefaultContentMapper initial content primary types = " + contentPrimaryTypes);
    logger.debug("DefaultContentMapper initial file primary types = " + filePrimaryTypes);
    logger.debug("DefaultContentMapper initial new folder primary types = " + newFolderPrimaryType);
    logger.debug(
        "DefaultContentMapper initial new resource primary types = " + newResourcePrimaryType);
    logger.debug(
        "DefaultContentMapper initial new content primary types = " + newContentPrimaryType);

    this.contentPrimaryTypes =
        split(contentPrimaryTypes != null ? contentPrimaryTypes : DEFAULT_CONTENT_PRIMARY_TYPES);
    this.filePrimaryTypes =
        split(resourcePrimaryTypes != null ? resourcePrimaryTypes : DEFAULT_RESOURCE_PRIMARY_TYPES);
    this.newFolderPrimaryType =
        newFolderPrimaryType != null ? newFolderPrimaryType : DEFAULT_NEW_FOLDER_PRIMARY_TYPE;
    this.newResourcePrimaryType =
        newResourcePrimaryType != null ? newResourcePrimaryType : DEFAULT_NEW_RESOURCE_PRIMARY_TYPE;
    this.newContentPrimaryType =
        newContentPrimaryType != null ? newContentPrimaryType : DEFAULT_NEW_CONTENT_PRIMARY_TYPE;
  }
Exemple #2
0
  /**
   * Pings a connector by name.
   *
   * @param connectorName
   * @return RepositorySource - may be <code>null</code>)
   */
  @ManagementOperation(description = "Pings a connector by name", impact = Impact.ReadOnly)
  public boolean pingConnector(String connectorName) {
    if (!isRunning()) return false;

    // Get engine to use for the rest of the method (this is synchronized)
    // ...
    final JcrEngine engine = getEngine();
    assert engine != null;

    boolean success = false;
    String pingDuration = null;
    try {
      RepositoryConnectionPool pool =
          engine.getRepositoryService().getRepositoryLibrary().getConnectionPool(connectorName);
      if (pool != null) {
        Stopwatch sw = new Stopwatch();
        sw.start();
        success = pool.ping();
        sw.stop();
        pingDuration = sw.getTotalDuration().toString();
      }
    } catch (Exception e) {
      Logger.getLogger(getClass())
          .error(e, JBossManagedI18n.errorDeterminingIfConnectionIsAlive, connectorName);
    }
    if (pingDuration == null) pingDuration = new Duration(0L).toString();
    return success;
  }
Exemple #3
0
  /**
   * Get the number of connections currently in use
   *
   * @param connectorName
   * @return boolean
   */
  @ManagementOperation(
      description = "Get the number of connections currently in use",
      impact = Impact.ReadOnly)
  public long getInUseConnections(String connectorName) {
    if (!isRunning()) return 0;

    // Get engine to use for the rest of the method (this is synchronized)
    final JcrEngine engine = getEngine();
    assert engine != null;

    long totalConnectionsInUse = 0;
    try {
      totalConnectionsInUse =
          engine
              .getRepositoryService()
              .getRepositoryLibrary()
              .getConnectionPool(connectorName)
              .getInUseCount();
    } catch (Exception e) {
      Logger.getLogger(getClass())
          .error(e, JBossManagedI18n.errorDeterminingTotalInUseConnections, connectorName);
    }

    return totalConnectionsInUse;
  }
 protected void process(Changes changes) {
   try {
     searchEngine.index(context, changes.getChangeRequests());
   } catch (RuntimeException e) {
     Logger.getLogger(getClass())
         .error(e, JcrI18n.errorUpdatingQueryIndexes, e.getLocalizedMessage());
   }
 }
Exemple #5
0
  /**
   * Obtains the specified managed repository of this engine. This is called by the
   * JNDIManagedRepositories when a JNDI lookup is performed to find a repository.
   *
   * @param repositoryName for the repository to be returned
   * @return a repository or <code>null</code> if repository doesn't exist
   */
  public JcrRepository getRepository(String repositoryName) {
    if (!isRunning()) return null;

    // Get engine to use for the rest of the method (this is synchronized)
    // ...
    final JcrEngine engine = getEngine();
    assert engine != null;

    try {
      return engine.getRepository(repositoryName);
    } catch (RepositoryException e) {
      Logger.getLogger(getClass())
          .error(e, JBossManagedI18n.errorGettingRepositoryFromEngine, repositoryName);
      return null;
    }
  }
 /**
  * Refresh the node types from the stored representation.
  *
  * @return true if there was at least one node type found, or false if there were none
  */
 protected boolean refreshFromSystem() {
   Lock lock = this.namespacesLock.writeLock();
   try {
     lock.lock();
     // Re-read and re-register all of the namespaces ...
     SessionCache systemCache = repository.createSystemSession(context, false);
     SystemContent system = new SystemContent(systemCache);
     Collection<Namespace> namespaces = system.readAllNamespaces();
     if (namespaces.isEmpty()) return false;
     this.cache.clear();
     this.cache.register(namespaces);
   } catch (Throwable e) {
     logger.error(e, JcrI18n.errorRefreshingNodeTypes, repository.name());
   } finally {
     lock.unlock();
   }
   return true;
 }
Exemple #7
0
  /**
   * Get the number of sessions currently active
   *
   * @param repositoryName
   * @return boolean
   */
  @ManagementOperation(
      description = "Get the number of sessions currently active",
      impact = Impact.ReadOnly)
  public long getActiveSessions(String repositoryName) {
    if (!isRunning()) return 0;

    // Get engine to use for the rest of the method (this is synchronized)
    final JcrEngine engine = getEngine();
    assert engine != null;

    int totalActiveSessions = 0;
    try {
      totalActiveSessions = getRepository(repositoryName).getMetrics().getActiveSessionCount();
    } catch (Exception e) {
      Logger.getLogger(getClass())
          .error(e, JBossManagedI18n.errorDeterminingTotalInUseConnections, repositoryName);
    }

    return totalActiveSessions;
  }
/**
 * A {@link NamespaceRegistry} implementation that stores the namespaces in the '/jcr:system' area
 * as individual nodes for each namespace.
 */
@ThreadSafe
public class SystemNamespaceRegistry implements NamespaceRegistry {

  public static final Name URI_PROPERTY_NAME = ModeShapeLexicon.URI;
  public static final Name GENERATED_PROPERTY_NAME = ModeShapeLexicon.GENERATED;

  private final JcrRepository.RunningState repository;
  private final SimpleNamespaceRegistry cache;
  private ExecutionContext context;
  private final ReadWriteLock namespacesLock = new ReentrantReadWriteLock();
  private final Logger logger = Logger.getLogger(getClass());

  SystemNamespaceRegistry(JcrRepository.RunningState repository) {
    this.repository = repository;
    this.cache = new SimpleNamespaceRegistry();
    // Pre-load all of the built-in namespaces ...
    this.cache.register(new ExecutionContext().getNamespaceRegistry().getNamespaces());
  }

  void setContext(ExecutionContext context) {
    this.context = context;
  }

  /**
   * Refresh the node types from the stored representation.
   *
   * @return true if there was at least one node type found, or false if there were none
   */
  protected boolean refreshFromSystem() {
    Lock lock = this.namespacesLock.writeLock();
    try {
      lock.lock();
      // Re-read and re-register all of the namespaces ...
      SessionCache systemCache = repository.createSystemSession(context, false);
      SystemContent system = new SystemContent(systemCache);
      Collection<Namespace> namespaces = system.readAllNamespaces();
      if (namespaces.isEmpty()) return false;
      this.cache.clear();
      this.cache.register(namespaces);
    } catch (Throwable e) {
      logger.error(e, JcrI18n.errorRefreshingNodeTypes, repository.name());
    } finally {
      lock.unlock();
    }
    return true;
  }

  private final SystemContent systemContent(boolean readOnly) {
    SessionCache systemCache = repository.createSystemSession(context, readOnly);
    return new SystemContent(systemCache);
  }

  @Override
  public String getNamespaceForPrefix(String prefix) {
    Lock lock = this.namespacesLock.readLock();
    try {
      lock.lock();
      CheckArg.isNotNull(prefix, "prefix");
      return cache.getNamespaceForPrefix(prefix);
    } finally {
      lock.unlock();
    }
  }

  @Override
  public String getPrefixForNamespaceUri(String namespaceUri, boolean generateIfMissing) {
    CheckArg.isNotNull(namespaceUri, "namespaceUri");
    Lock lock = this.namespacesLock.readLock();
    try {
      lock.lock();
      // Try the cache first ...
      String prefix = cache.getPrefixForNamespaceUri(namespaceUri, false);
      if (prefix == null && generateIfMissing) {
        SystemContent systemContent = systemContent(!generateIfMissing);
        prefix = systemContent.readNamespacePrefix(namespaceUri, generateIfMissing);
        if (prefix != null) {
          systemContent.save();
          cache.register(prefix, namespaceUri);
        }
      }
      return prefix;
    } finally {
      lock.unlock();
    }
  }

  @Override
  public boolean isRegisteredNamespaceUri(String namespaceUri) {
    CheckArg.isNotNull(namespaceUri, "namespaceUri");
    Lock lock = this.namespacesLock.readLock();
    try {
      lock.lock();
      return cache.isRegisteredNamespaceUri(namespaceUri);
    } finally {
      lock.unlock();
    }
  }

  @Override
  public String getDefaultNamespaceUri() {
    return this.getNamespaceForPrefix("");
  }

  @Override
  public void register(Iterable<Namespace> namespaces) {
    final Lock lock = this.namespacesLock.writeLock();
    try {
      lock.lock();
      Map<String, String> urisByPrefix = new HashMap<String, String>();
      for (Namespace namespace : namespaces) {
        urisByPrefix.put(namespace.getPrefix(), namespace.getNamespaceUri());
      }
      register(urisByPrefix);
    } finally {
      lock.unlock();
    }
  }

  /**
   * Register a set of namespaces.
   *
   * @param namespaceUrisByPrefix the map of new namespace URIs by their prefix
   */
  public void register(Map<String, String> namespaceUrisByPrefix) {
    if (namespaceUrisByPrefix == null || namespaceUrisByPrefix.isEmpty()) return;
    final Lock lock = this.namespacesLock.writeLock();
    try {
      lock.lock();
      SystemContent systemContent = systemContent(false);
      systemContent.registerNamespaces(namespaceUrisByPrefix);
      systemContent.save();
      for (Map.Entry<String, String> entry : namespaceUrisByPrefix.entrySet()) {
        String prefix = entry.getKey().trim();
        String uri = entry.getValue().trim();
        if (prefix.length() == 0) continue;
        this.cache.register(prefix, uri);
      }
    } finally {
      lock.unlock();
    }
  }

  @Override
  public String register(String prefix, String namespaceUri) {
    CheckArg.isNotNull(namespaceUri, "namespaceUri");
    namespaceUri = namespaceUri.trim();
    final Lock lock = this.namespacesLock.writeLock();
    try {
      lock.lock();
      // Register it in the cache first ...
      String previousCachedUriForPrefix = this.cache.register(prefix, namespaceUri);
      if (!namespaceUri.equals(previousCachedUriForPrefix)) {
        // And register it in the source ...
        SystemContent systemContent = systemContent(false);
        systemContent.registerNamespaces(Collections.singletonMap(prefix, namespaceUri));
        systemContent.save();
      }
      return previousCachedUriForPrefix;
    } finally {
      lock.unlock();
    }
  }

  @Override
  public boolean unregister(String namespaceUri) {
    CheckArg.isNotNull(namespaceUri, "namespaceUri");
    namespaceUri = namespaceUri.trim();
    final Lock lock = this.namespacesLock.writeLock();
    try {
      lock.lock();
      // Remove it from the cache ...
      boolean found = this.cache.unregister(namespaceUri);
      // Then from the source ...
      SystemContent systemContent = systemContent(false);
      boolean foundPersistent = systemContent.unregisterNamespace(namespaceUri);
      systemContent.save();
      return foundPersistent || found;
    } finally {
      lock.unlock();
    }
  }

  @Override
  public Set<String> getRegisteredNamespaceUris() {
    final Lock lock = this.namespacesLock.readLock();
    try {
      lock.lock();
      // Just return what's in the cache ...
      return cache.getRegisteredNamespaceUris();
    } finally {
      lock.unlock();
    }
  }

  @Override
  public Set<Namespace> getNamespaces() {
    final Lock lock = this.namespacesLock.readLock();
    try {
      lock.lock();
      // Just return what's in the cache ...
      return cache.getNamespaces();
    } finally {
      lock.unlock();
    }
  }

  /**
   * {@inheritDoc}
   *
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    List<Namespace> namespaces = new ArrayList<Namespace>(getNamespaces());
    Collections.sort(namespaces);
    return namespaces.toString();
  }
}
public class DefaultContentMapper implements ContentMapper {

  public static final String INIT_CONTENT_PRIMARY_TYPE_NAMES =
      "org.modeshape.web.jcr.webdav.CONTENT_PRIMARY_TYPE_NAMES";
  public static final String INIT_RESOURCE_PRIMARY_TYPES_NAMES =
      "org.modeshape.web.jcr.webdav.RESOURCE_PRIMARY_TYPE_NAMES";
  public static final String INIT_NEW_FOLDER_PRIMARY_TYPE_NAME =
      "org.modeshape.web.jcr.webdav.NEW_FOLDER_PRIMARY_TYPE_NAME";
  public static final String INIT_NEW_RESOURCE_PRIMARY_TYPE_NAME =
      "org.modeshape.web.jcr.webdav.NEW_RESOURCE_PRIMARY_TYPE_NAME";
  public static final String INIT_NEW_CONTENT_PRIMARY_TYPE_NAME =
      "org.modeshape.web.jcr.webdav.NEW_CONTENT_PRIMARY_TYPE_NAME";

  private static final String CONTENT_NODE_NAME = "jcr:content";
  private static final String DATA_PROP_NAME = "jcr:data";
  private static final String MODIFIED_PROP_NAME = "jcr:lastModified";
  private static final String ENCODING_PROP_NAME = "jcr:encoding";
  private static final String MIME_TYPE_PROP_NAME = "jcr:mimeType";

  private static final String DEFAULT_CONTENT_PRIMARY_TYPES = "nt:resource, mode:resource";
  private static final String DEFAULT_RESOURCE_PRIMARY_TYPES = "nt:file";
  private static final String DEFAULT_NEW_FOLDER_PRIMARY_TYPE = "nt:folder";
  private static final String DEFAULT_NEW_RESOURCE_PRIMARY_TYPE = "nt:file";
  private static final String DEFAULT_NEW_CONTENT_PRIMARY_TYPE = "nt:resource";

  private Collection<String> contentPrimaryTypes;
  private Collection<String> filePrimaryTypes;
  private String newFolderPrimaryType;
  private String newResourcePrimaryType;
  private String newContentPrimaryType;
  private MimeTypeDetector mimeTypeDetector = new ApertureMimeTypeDetector();

  private final Logger logger = Logger.getLogger(getClass());

  @Override
  public void initialize(ServletContext servletContext) {

    String contentPrimaryTypes = getParam(servletContext, INIT_CONTENT_PRIMARY_TYPE_NAMES);
    String resourcePrimaryTypes = getParam(servletContext, INIT_RESOURCE_PRIMARY_TYPES_NAMES);
    String newFolderPrimaryType = getParam(servletContext, INIT_NEW_FOLDER_PRIMARY_TYPE_NAME);
    String newResourcePrimaryType = getParam(servletContext, INIT_NEW_RESOURCE_PRIMARY_TYPE_NAME);
    String newContentPrimaryType = getParam(servletContext, INIT_NEW_CONTENT_PRIMARY_TYPE_NAME);

    logger.debug("DefaultContentMapper initial content primary types = " + contentPrimaryTypes);
    logger.debug("DefaultContentMapper initial file primary types = " + filePrimaryTypes);
    logger.debug("DefaultContentMapper initial new folder primary types = " + newFolderPrimaryType);
    logger.debug(
        "DefaultContentMapper initial new resource primary types = " + newResourcePrimaryType);
    logger.debug(
        "DefaultContentMapper initial new content primary types = " + newContentPrimaryType);

    this.contentPrimaryTypes =
        split(contentPrimaryTypes != null ? contentPrimaryTypes : DEFAULT_CONTENT_PRIMARY_TYPES);
    this.filePrimaryTypes =
        split(resourcePrimaryTypes != null ? resourcePrimaryTypes : DEFAULT_RESOURCE_PRIMARY_TYPES);
    this.newFolderPrimaryType =
        newFolderPrimaryType != null ? newFolderPrimaryType : DEFAULT_NEW_FOLDER_PRIMARY_TYPE;
    this.newResourcePrimaryType =
        newResourcePrimaryType != null ? newResourcePrimaryType : DEFAULT_NEW_RESOURCE_PRIMARY_TYPE;
    this.newContentPrimaryType =
        newContentPrimaryType != null ? newContentPrimaryType : DEFAULT_NEW_CONTENT_PRIMARY_TYPE;
  }

  protected String getParam(ServletContext servletContext, String name) {
    return servletContext.getInitParameter(name);
  }

  /**
   * Returns an unmodifiable set containing the elements passed in to this method
   *
   * @param elements a set of elements; may not be null
   * @return an unmodifiable set containing all of the elements in {@code elements}; never null
   */
  private static final Set<String> setFor(String... elements) {
    Set<String> set = new HashSet<String>(elements.length);
    set.addAll(Arrays.asList(elements));

    return set;
  }

  /**
   * Splits a comma-delimited string into an unmodifiable set containing the substrings between the
   * commas in the source string. The elements in the set will be {@link String#trim() trimmed}.
   *
   * @param commaDelimitedString input string; may not be null, but need not contain any commas
   * @return an unmodifiable set whose elements are the trimmed substrings of the source string;
   *     never null
   */
  private static final Set<String> split(String commaDelimitedString) {
    return setFor(commaDelimitedString.split("\\s*,\\s*"));
  }

  @Override
  public InputStream getResourceContent(Node node) throws RepositoryException {
    if (!node.hasNode(CONTENT_NODE_NAME)) return null;
    return node.getProperty(CONTENT_NODE_NAME + "/" + DATA_PROP_NAME).getBinary().getStream();
  }

  @Override
  public long getResourceLength(Node node) throws RepositoryException, IOException {
    InputStream is = getResourceContent(node);

    long size = 0;
    int bytesRead;
    byte[] buff = new byte[255];

    while (-1 != (bytesRead = is.read(buff, 0, 255))) {
      size += bytesRead;
    }

    return size;
  }

  @Override
  public Date getLastModified(Node node) throws RepositoryException {
    if (!node.hasNode(CONTENT_NODE_NAME)) return null;

    return node.getProperty(CONTENT_NODE_NAME + "/" + MODIFIED_PROP_NAME).getDate().getTime();
  }

  @Override
  public boolean isFolder(Node node) throws RepositoryException {
    return !isFile(node) && !isContent(node);
  }

  /**
   * @param node the node to check
   * @return true if {@code node}'s primary type is one of the types in {@link #filePrimaryTypes};
   *     may not be null
   * @throws RepositoryException if an error occurs checking the node's primary type
   */
  @Override
  public boolean isFile(Node node) throws RepositoryException {
    for (String nodeType : filePrimaryTypes) {
      if (node.isNodeType(nodeType)) return true;
    }

    return false;
  }

  /**
   * @param node the node to check
   * @return true if {@code node}'s primary type is one of the types in {@link
   *     #contentPrimaryTypes}; may not be null
   * @throws RepositoryException if an error occurs checking the node's primary type
   */
  private boolean isContent(Node node) throws RepositoryException {
    for (String nodeType : contentPrimaryTypes) {
      if (node.isNodeType(nodeType)) return true;
    }

    return false;
  }

  @Override
  public void createFile(Node parentNode, String fileName) throws RepositoryException {
    Node resourceNode = parentNode.addNode(fileName, newResourcePrimaryType);

    Node contentNode = resourceNode.addNode(CONTENT_NODE_NAME, newContentPrimaryType);
    contentNode.setProperty(DATA_PROP_NAME, "");
    contentNode.setProperty(MODIFIED_PROP_NAME, Calendar.getInstance());
    contentNode.setProperty(ENCODING_PROP_NAME, "UTF-8");
    contentNode.setProperty(MIME_TYPE_PROP_NAME, "text/plain");
  }

  @Override
  public void createFolder(Node parentNode, String folderName) throws RepositoryException {
    parentNode.addNode(folderName, newFolderPrimaryType);
  }

  @Override
  public long setContent(
      Node parentNode,
      String resourceName,
      InputStream newContent,
      String contentType,
      String characterEncoding)
      throws RepositoryException, IOException {
    Node contentNode;
    if (parentNode.hasNode(CONTENT_NODE_NAME)) {
      contentNode = parentNode.getNode(CONTENT_NODE_NAME);
    } else {
      contentNode = parentNode.addNode(CONTENT_NODE_NAME, newContentPrimaryType);
    }

    // contentNode.setProperty(MIME_TYPE_PROP_NAME, contentType != null ? contentType :
    // "application/octet-stream");
    contentNode.setProperty(
        ENCODING_PROP_NAME, characterEncoding != null ? characterEncoding : "UTF-8");
    Binary binary = parentNode.getSession().getValueFactory().createBinary(newContent);
    contentNode.setProperty(DATA_PROP_NAME, binary);
    contentNode.setProperty(MODIFIED_PROP_NAME, Calendar.getInstance());

    // Copy the content to the property, THEN re-read the content from the Binary value to avoid
    // discaring the first
    // bytes of the stream
    if (contentType == null) {
      contentType = mimeTypeDetector.mimeTypeOf(resourceName, binary.getStream());
    }

    return contentNode.getProperty(DATA_PROP_NAME).getLength();
  }
}
Exemple #10
0
/**
 * The basic component that encapsulates the ModeShape services, including the {@link Repository}
 * instances.
 */
@ThreadSafe
public class JcrEngine extends ModeShapeEngine implements Repositories {

  static final int LOCK_SWEEP_INTERVAL_IN_MILLIS = 30000;
  static final int LOCK_EXTENSION_INTERVAL_IN_MILLIS = LOCK_SWEEP_INTERVAL_IN_MILLIS * 2;

  private static final Logger log = Logger.getLogger(ModeShapeEngine.class);

  private final Map<String, JcrRepositoryHolder> repositories;
  private final Lock repositoriesLock;
  private final Map<String, Object> descriptors = new HashMap<String, Object>();
  private final ExecutorService repositoryStarterService;

  /** Provides the ability to schedule lock clean-up */
  private final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(2);

  JcrEngine(
      ExecutionContext context, ModeShapeConfiguration.ConfigurationDefinition configuration) {
    super(context, configuration);
    this.repositories = new HashMap<String, JcrRepositoryHolder>();
    this.repositoriesLock = new ReentrantLock();
    initDescriptors();

    // Create an executor service that we'll use to start the repositories ...
    ThreadFactory threadFactory = new NamedThreadFactory("modeshape-start-repo");
    this.repositoryStarterService = Executors.newCachedThreadPool(threadFactory);
  }

  /**
   * Clean up session-scoped locks created by session that are no longer active by iterating over
   * the {@link JcrRepository repositories} and calling their {@link
   * RepositoryLockManager#cleanUpLocks() clean-up method}.
   *
   * <p>It should not be possible for a session to be terminated without cleaning up its locks, but
   * this method will help clean-up dangling locks should a session terminate abnormally.
   */
  void cleanUpLocks() {
    Collection<JcrRepositoryHolder> repos = null;

    try {
      // Make a copy of the repositories to minimize the time that the lock needs to be held.
      repositoriesLock.lock();
      repos = new ArrayList<JcrRepositoryHolder>(repositories.values());
    } finally {
      repositoriesLock.unlock();
    }

    for (JcrRepositoryHolder repository : repos) {
      repository.cleanUpLocks();
    }
  }

  @Override
  protected void preShutdown() {
    repositoryStarterService.shutdown();
    scheduler.shutdown();
    super.preShutdown();

    try {
      this.repositoriesLock.lock();
      // Shut down all of the repositories ...
      for (JcrRepositoryHolder holder : repositories.values()) {
        holder.close();
      }
      this.repositories.clear();
    } finally {
      this.repositoriesLock.unlock();
    }
  }

  /**
   * Blocks until the shutdown has completed, or the timeout occurs, or the current thread is
   * interrupted, whichever happens first.
   *
   * @param timeout the maximum time to wait for each component in this engine
   * @param unit the time unit of the timeout argument
   * @return <tt>true</tt> if this service complete shut down and <tt>false</tt> if the timeout
   *     elapsed before it was shut down completely
   * @throws InterruptedException if interrupted while waiting
   */
  @Override
  public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
    if (!scheduler.awaitTermination(timeout, unit)) return false;

    return super.awaitTermination(timeout, unit);
  }

  /**
   * {@inheritDoc}
   *
   * @see org.modeshape.repository.ModeShapeEngine#checkConfiguration(org.modeshape.graph.Subgraph)
   */
  @Override
  protected void checkConfiguration(Subgraph configuration) {
    super.checkConfiguration(configuration);

    // Get the list of sources ...
    Set<String> sourceNames = new HashSet<String>();
    for (Location child : configuration.getNode(ModeShapeLexicon.SOURCES)) {
      String name = child.getPath().getLastSegment().getName().getLocalName();
      sourceNames.add(name);
    }
    // Verify all of the repositories reference valid sources ...
    for (Location child : configuration.getNode(ModeShapeLexicon.REPOSITORIES)) {
      String repositoryName = readable(child.getPath().getLastSegment().getName());
      Node repositoryNode = configuration.getNode(child);
      Property property = repositoryNode.getProperty(ModeShapeLexicon.SOURCE_NAME);
      if (property == null) {
        getProblems()
            .addError(JcrI18n.repositoryReferencesNonExistantSource, repositoryName, "null");
      } else {
        String sourceName = string(property.getFirstValue());
        if (!sourceNames.contains(sourceName)) {
          getProblems()
              .addError(JcrI18n.repositoryReferencesNonExistantSource, repositoryName, sourceName);
        }
      }
    }
  }

  /**
   * Start this engine to make it available for use.
   *
   * @throws IllegalStateException if this method is called when already shut down.
   * @throws JcrConfigurationException if there is an error in the configuration or any of the
   *     services that prevents proper startup
   * @see #start(boolean)
   * @see #shutdown()
   */
  @Override
  public void start() {
    super.start();

    final JcrEngine engine = this;
    Runnable cleanUpTask =
        new Runnable() {

          public void run() {
            engine.cleanUpLocks();
          }
        };
    try {
      scheduler.scheduleAtFixedRate(
          cleanUpTask,
          LOCK_SWEEP_INTERVAL_IN_MILLIS,
          LOCK_SWEEP_INTERVAL_IN_MILLIS,
          TimeUnit.MILLISECONDS);
      checkProblemsOnStartup();
    } catch (RuntimeException e) {
      try {
        super.shutdown();
      } catch (Throwable t) {
        // Don't care about these ...
      }
      throw e;
    }
  }

  /**
   * Start this engine to make it available for use, and optionally start each of the repositories
   * in the configuration. Any errors starting the repositories will be logged as problems.
   *
   * <p>This method starts each repository in parallel, and returns only after all repositories have
   * been started (or failed startup).
   *
   * @param validateRepositoryConfigs true if the configurations of each repository should be
   *     validated and each repository started/initialized, or false otherwise
   * @throws IllegalStateException if this method is called when already shut down.
   * @throws JcrConfigurationException if there is an error in the configuration or any of the
   *     services that prevents proper startup
   * @see #start()
   * @see #shutdown()
   */
  public void start(boolean validateRepositoryConfigs) {
    start(validateRepositoryConfigs, -1, TimeUnit.SECONDS);
  }

  /**
   * Start this engine to make it available for use, and optionally start each of the repositories
   * in the configuration. Any errors starting the repositories will be logged as problems.
   *
   * <p>This method starts each repository in parallel, and returns after the supplied timeout or
   * after all repositories have been started (or failed startup), whichever comes first.
   *
   * @param validateRepositoryConfigs true if the configurations of each repository should be
   *     validated and each repository started/initialized, or false otherwise
   * @param timeout the maximum time to wait; can be 0 or a positive number, but use a negative
   *     number to wait indefinitely until all repositories are started (or failed)
   * @param timeoutUnit the time unit of the {@code timeout} argument; may not be null, but ignored
   *     if <code>timeout</code> is negative
   * @throws IllegalStateException if this method is called when already shut down.
   * @throws JcrConfigurationException if there is an error in the configuration or any of the
   *     services that prevents proper startup
   * @see #start()
   * @see #shutdown()
   */
  public void start(boolean validateRepositoryConfigs, long timeout, TimeUnit timeoutUnit) {
    start();
    if (validateRepositoryConfigs) {
      Set<String> repositoryNames = getRepositoryNames();
      if (repositoryNames.isEmpty()) return;

      final CountDownLatch latch = new CountDownLatch(repositoryNames.size());

      try {
        repositoriesLock.lock();
        // Put in a holder with a future for each repository
        // (this should proceed quickly, as nothing waits for the initialization) ...
        for (final String repositoryName : repositoryNames) {
          RepositoryInitializer initializer = new RepositoryInitializer(repositoryName, latch);
          Future<JcrRepository> future = repositoryStarterService.submit(initializer);
          JcrRepositoryHolder holder = new JcrRepositoryHolder(repositoryName, future);
          this.repositories.put(repositoryName, holder);
        }
      } finally {
        repositoriesLock.unlock();
      }

      // Now wait for the all the startups to complete ...
      try {
        if (timeout < 0L) {
          latch.await();
        } else {
          latch.await(timeout, timeoutUnit);
        }
      } catch (InterruptedException e) {
        this.problems.addError(e, JcrI18n.startingAllRepositoriesWasInterrupted, e.getMessage());
      }
    }
  }

  /**
   * {@inheritDoc}
   *
   * @see org.modeshape.repository.ModeShapeEngine#newConfigurationException(java.lang.String)
   */
  @Override
  protected ModeShapeConfigurationException newConfigurationException(String msg) {
    return new JcrConfigurationException(msg);
  }

  /**
   * Get the version of this engine.
   *
   * @return version
   */
  public String getEngineVersion() {
    return JcrRepository.getBundleProperty(Repository.REP_VERSION_DESC, true);
  }

  /**
   * Get the {@link Repository} implementation for the named repository.
   *
   * @param repositoryName the name of the repository, which corresponds to the name of a configured
   *     {@link RepositorySource}
   * @return the named repository instance
   * @throws IllegalArgumentException if the repository name is null, blank or invalid
   * @throws RepositoryException if there is no repository with the specified name
   * @throws IllegalStateException if this engine was not {@link #start() started}
   */
  public final JcrRepository getRepository(String repositoryName) throws RepositoryException {
    CheckArg.isNotEmpty(repositoryName, "repositoryName");
    checkRunning();

    JcrRepositoryHolder holder = null;
    try {
      repositoriesLock.lock();
      holder = repositories.get(repositoryName);
      if (holder != null) {
        // The repository was already placed in the map and thus initialization has been started
        // and may be finished. But this call will block until the repository has completed
        // initialization...
        return holder.getRepository();
      }
      if (!getRepositoryNames().contains(repositoryName)) {
        // The repository name is not a valid repository ...
        String msg = JcrI18n.repositoryDoesNotExist.text(repositoryName);
        throw new RepositoryException(msg);
      }
      // Now create the initializer and holder ...
      RepositoryInitializer initializer = new RepositoryInitializer(repositoryName);
      Future<JcrRepository> future = repositoryStarterService.submit(initializer);
      holder = new JcrRepositoryHolder(repositoryName, future);
      repositories.put(repositoryName, holder);
    } finally {
      repositoriesLock.unlock();
    }
    JcrRepository repo = holder.getRepository();
    return repo;
  }

  /**
   * Get the names of each of the JCR repositories.
   *
   * @return the immutable names of the repositories that exist at the time this method is called
   */
  public Set<String> getRepositoryNames() {
    checkRunning();
    Set<String> results = new HashSet<String>();
    // Read the names of the JCR repositories from the configuration (not from the Repository
    // objects used so far) ...
    PathFactory pathFactory = getExecutionContext().getValueFactories().getPathFactory();
    Path repositoriesPath =
        pathFactory.create(configuration.getPath(), ModeShapeLexicon.REPOSITORIES);
    Graph configuration = getConfigurationGraph();
    for (Location child : configuration.getChildren().of(repositoriesPath)) {
      Name repositoryName = child.getPath().getLastSegment().getName();
      results.add(readable(repositoryName));
    }
    return Collections.unmodifiableSet(results);
  }

  protected JcrRepository doCreateJcrRepository(String repositoryName)
      throws RepositoryException, PathNotFoundException {
    RepositoryConnectionFactory connectionFactory = getRepositoryConnectionFactory();
    Map<String, String> descriptors = new HashMap<String, String>();
    Map<Option, String> options = new HashMap<Option, String>();

    // Read the subgraph that represents the repository ...
    PathFactory pathFactory = getExecutionContext().getValueFactories().getPathFactory();
    Path repositoriesPath =
        pathFactory.create(configuration.getPath(), ModeShapeLexicon.REPOSITORIES);
    Path repositoryPath = pathFactory.create(repositoriesPath, repositoryName);
    Name repoName =
        getExecutionContext().getValueFactories().getNameFactory().create(repositoryName);
    Graph configuration = null;
    Subgraph subgraph = getConfigurationSubgraph(false);

    // Read the options ...
    Path optionsPath =
        pathFactory.createRelativePath(
            ModeShapeLexicon.REPOSITORIES, repoName, ModeShapeLexicon.OPTIONS);
    Node optionsNode = subgraph.getNode(optionsPath);
    if (optionsNode != null) {
      for (Location optionLocation : optionsNode.getChildren()) {
        Node optionNode = subgraph.getNode(optionLocation);
        Path.Segment segment = optionLocation.getPath().getLastSegment();
        Property valueProperty = optionNode.getProperty(ModeShapeLexicon.VALUE);
        if (valueProperty == null) {
          log.warn(JcrI18n.noOptionValueProvided, segment.getName().getLocalName());
          continue;
        }
        Option option = Option.findOption(segment.getName().getLocalName());
        if (option == null) {
          log.warn(JcrI18n.invalidOptionProvided, segment.getName().getLocalName());
          continue;
        }
        options.put(option, valueProperty.getFirstValue().toString());
      }
    }

    // Disable the derived content removal option if not explicitly set and no sequencers ...
    if (!options.containsKey(Option.REMOVE_DERIVED_CONTENT_WITH_ORIGINAL)
        && getSequencingService().getSequencers().isEmpty()) {
      options.put(Option.REMOVE_DERIVED_CONTENT_WITH_ORIGINAL, Boolean.FALSE.toString());
    }

    // Read the descriptors ...
    Path descriptorsPath =
        pathFactory.createRelativePath(
            ModeShapeLexicon.REPOSITORIES, repoName, ModeShapeLexicon.DESCRIPTORS);
    Node descriptorsNode = subgraph.getNode(descriptorsPath);
    if (descriptorsNode != null) {
      for (Location descriptorLocation : descriptorsNode.getChildren()) {
        Node optionNode = subgraph.getNode(descriptorLocation);
        Path.Segment segment = descriptorLocation.getPath().getLastSegment();
        Property valueProperty = optionNode.getProperty(ModeShapeLexicon.VALUE);
        if (valueProperty == null) continue;
        descriptors.put(segment.getName().getLocalName(), valueProperty.getFirstValue().toString());
      }
    }

    // Read the namespaces ...
    ExecutionContext context = getExecutionContext();
    Path namespacesPath =
        pathFactory.createRelativePath(
            ModeShapeLexicon.REPOSITORIES, repoName, ModeShapeLexicon.NAMESPACES);
    Node namespacesNode = subgraph.getNode(namespacesPath);
    descriptors.put(org.modeshape.jcr.api.Repository.REPOSITORY_NAME, repositoryName);
    if (namespacesNode != null) {
      configuration = getConfigurationGraph();
      GraphNamespaceRegistry registry =
          new GraphNamespaceRegistry(
              configuration,
              namespacesNode.getLocation().getPath(),
              ModeShapeLexicon.URI,
              ModeShapeLexicon.GENERATED);
      context = context.with(registry);
    }

    // Get the name of the source ...
    Path repoPath = pathFactory.createRelativePath(ModeShapeLexicon.REPOSITORIES, repoName);
    Node repoNode = subgraph.getNode(repoPath);
    if (repoNode == null) {
      // There is no repository with the supplied name ...
      throw new PathNotFoundException(
          Location.create(repoPath),
          repositoriesPath,
          JcrI18n.repositoryDoesNotExist.text(readable(repoName)));
    }
    Property property = repoNode.getProperty(ModeShapeLexicon.SOURCE_NAME);
    if (property == null || property.isEmpty()) {
      if (configuration == null) configuration = getConfigurationGraph();
      String readableName = readable(ModeShapeLexicon.SOURCE_NAME);
      String readablePath = readable(subgraph.getLocation());
      String msg =
          JcrI18n.propertyNotFoundOnNode.text(
              readableName, readablePath, configuration.getCurrentWorkspaceName());
      throw new RepositoryException(msg);
    }
    String sourceName =
        context.getValueFactories().getStringFactory().create(property.getFirstValue());

    // Verify the sourc exists ...
    RepositorySource source = getRepositorySource(sourceName);
    if (source == null) {
      throw new RepositoryException(
          JcrI18n.repositoryReferencesNonExistantSource.text(repositoryName, sourceName));
    }

    // Read the initial content ...
    String initialContentForNewWorkspaces = null;
    for (Location initialContentLocation : repoNode.getChildren(ModeShapeLexicon.INITIAL_CONTENT)) {
      Node initialContent = subgraph.getNode(initialContentLocation);
      if (initialContent == null) continue;

      // Determine where to load the initial content from ...
      Property contentReference = initialContent.getProperty(ModeShapeLexicon.CONTENT);
      if (contentReference == null || contentReference.isEmpty()) {
        if (configuration == null) configuration = getConfigurationGraph();
        String readableName = readable(ModeShapeLexicon.CONTENT);
        String readablePath = readable(initialContentLocation);
        String msg =
            JcrI18n.propertyNotFoundOnNode.text(
                readableName, readablePath, configuration.getCurrentWorkspaceName());
        throw new RepositoryException(msg);
      }
      String contentRef = string(contentReference.getFirstValue());

      // Determine which workspaces this should apply to ...
      Property workspaces = initialContent.getProperty(ModeShapeLexicon.WORKSPACES);
      if (workspaces == null || workspaces.isEmpty()) {
        if (configuration == null) configuration = getConfigurationGraph();
        String readableName = readable(ModeShapeLexicon.WORKSPACES);
        String readablePath = readable(initialContentLocation);
        String msg =
            JcrI18n.propertyNotFoundOnNode.text(
                readableName, readablePath, configuration.getCurrentWorkspaceName());
        throw new RepositoryException(msg);
      }

      // Load the initial content into a transient source ...
      XmlFileRepositorySource initialContentSource = new XmlFileRepositorySource();
      initialContentSource.setName("Initial content for " + repositoryName);
      initialContentSource.setContentLocation(contentRef);
      Graph initialContentGraph = Graph.create(initialContentSource, context);
      Graph sourceGraph = Graph.create(sourceName, connectionFactory, context);

      // And initialize the source with the content (if not already there) ...
      for (Object value : workspaces) {
        String workspaceName = string(value);
        if (workspaceName != null && workspaceName.trim().length() != 0) {
          // Load the content into the workspace with this name ...
          sourceGraph.useWorkspace(workspaceName);
          try {
            sourceGraph.merge(initialContentGraph);
          } catch (RuntimeException e) {
            throw new RepositoryException(
                JcrI18n.unableToImportInitialContent.text(readable(repoName), contentRef), e);
          }
        }
      }

      // Determine if this initial content should apply to new workspaces ...
      Property applyToNewWorkspaces =
          initialContent.getProperty(ModeShapeLexicon.APPLY_TO_NEW_WORKSPACES);
      if (applyToNewWorkspaces != null
          && !applyToNewWorkspaces.isEmpty()
          && isTrue(applyToNewWorkspaces.getFirstValue())) {
        initialContentForNewWorkspaces =
            contentRef; // may overwrite the value if seen more than once!
      }
    }

    // Find the capabilities ...
    RepositorySourceCapabilities capabilities = source.getCapabilities();
    // Create the repository ...
    JcrRepository repository =
        new JcrRepository(
            context,
            connectionFactory,
            sourceName,
            getRepositoryService().getRepositoryLibrary(),
            capabilities,
            descriptors,
            options,
            initialContentForNewWorkspaces);

    // Register all the the node types ...
    Path nodeTypesPath =
        pathFactory.createRelativePath(
            ModeShapeLexicon.REPOSITORIES, repoName, JcrLexicon.NODE_TYPES);
    Node nodeTypesNode = subgraph.getNode(nodeTypesPath);
    if (nodeTypesNode != null) {
      boolean needToRefreshSubgraph = false;
      if (configuration == null) configuration = getConfigurationGraph();

      // Expand any references to a CND file
      Property resourceProperty = nodeTypesNode.getProperty(ModeShapeLexicon.RESOURCE);
      if (resourceProperty != null) {
        ClassLoader classLoader = this.context.getClassLoader();
        for (Object resourceValue : resourceProperty) {
          String resources =
              this.context.getValueFactories().getStringFactory().create(resourceValue);

          for (String resource : resources.split("\\s*,\\s*")) {
            Graph.Batch batch = configuration.batch();
            GraphBatchDestination destination = new GraphBatchDestination(batch);

            Path nodeTypesAbsPath = pathFactory.create(repositoryPath, JcrLexicon.NODE_TYPES);
            CndImporter importer = new CndImporter(destination, nodeTypesAbsPath, true, false);
            InputStream is = IoUtil.getResourceAsStream(resource, classLoader, getClass());
            Problems cndProblems = new SimpleProblems();
            if (is == null) {
              String msg =
                  JcrI18n.unableToFindNodeTypeDefinitionsOnClasspathOrFileOrUrl.text(resource);
              throw new RepositoryException(msg);
            }
            try {
              importer.importFrom(is, cndProblems, resource);
              batch.execute();
              needToRefreshSubgraph = true;
            } catch (IOException ioe) {
              String msg = JcrI18n.errorLoadingNodeTypeDefintions.text(resource, ioe.getMessage());
              throw new RepositoryException(msg, ioe);
            }
            if (!cndProblems.isEmpty()) {
              // Add any warnings or information to this engine's list ...
              getProblems().addAll(cndProblems);
              if (cndProblems.hasErrors()) {
                String msg = null;
                Throwable cause = null;
                for (Problem problem : cndProblems) {
                  if (problem.getStatus() == Status.ERROR) {
                    msg = problem.getMessageString();
                    cause = problem.getThrowable();
                    break;
                  }
                }
                throw new RepositoryException(
                    JcrI18n.errorLoadingNodeTypeDefintions.text(resource, msg), cause);
              }
            }
          }
        }
      }

      // Load any namespaces from the configuration into the repository's context ...
      NamespaceRegistry repoRegistry = repository.getExecutionContext().getNamespaceRegistry();
      repoRegistry.register(configuration.getContext().getNamespaceRegistry().getNamespaces());

      // Re-read the subgraph, in case any new nodes were added
      Subgraph nodeTypesSubgraph = subgraph;
      if (needToRefreshSubgraph) {
        nodeTypesSubgraph =
            configuration.getSubgraphOfDepth(4).at(nodeTypesNode.getLocation().getPath());
      }

      repository
          .getRepositoryTypeManager()
          .registerNodeTypes(nodeTypesSubgraph, nodeTypesNode.getLocation(), false);
    }

    return repository;
  }

  protected final String readable(Name name) {
    return name.getString(context.getNamespaceRegistry());
  }

  protected final String readable(Path path) {
    return path.getString(context.getNamespaceRegistry());
  }

  protected final String readable(Location location) {
    return location.getString(context.getNamespaceRegistry());
  }

  protected final String string(Object value) {
    return context.getValueFactories().getStringFactory().create(value);
  }

  protected final boolean isTrue(Object value) {
    return context.getValueFactories().getBooleanFactory().create(value);
  }

  /** @return descriptors */
  public Map<String, Object> initDescriptors() {
    ValueFactories factories = this.getExecutionContext().getValueFactories();
    descriptors.put(Repository.SPEC_NAME_DESC, valueFor(factories, JcrI18n.SPEC_NAME_DESC.text()));
    descriptors.put(Repository.SPEC_VERSION_DESC, valueFor(factories, "2.0"));

    if (!descriptors.containsKey(Repository.REP_NAME_DESC)) {
      descriptors.put(
          Repository.REP_NAME_DESC,
          valueFor(factories, JcrRepository.getBundleProperty(Repository.REP_NAME_DESC, true)));
    }
    if (!descriptors.containsKey(Repository.REP_VENDOR_DESC)) {
      descriptors.put(
          Repository.REP_VENDOR_DESC,
          valueFor(factories, JcrRepository.getBundleProperty(Repository.REP_VENDOR_DESC, true)));
    }
    if (!descriptors.containsKey(Repository.REP_VENDOR_URL_DESC)) {
      descriptors.put(
          Repository.REP_VENDOR_URL_DESC,
          valueFor(
              factories, JcrRepository.getBundleProperty(Repository.REP_VENDOR_URL_DESC, true)));
    }
    if (!descriptors.containsKey(Repository.REP_VERSION_DESC)) {
      descriptors.put(Repository.REP_VERSION_DESC, valueFor(factories, getEngineVersion()));
    }
    return descriptors;
  }

  private static JcrValue valueFor(ValueFactories valueFactories, int type, Object value) {
    return new JcrValue(valueFactories, null, type, value);
  }

  private static JcrValue valueFor(ValueFactories valueFactories, String value) {
    return valueFor(valueFactories, PropertyType.STRING, value);
  }

  /**
   * This method is equivalent to calling {@link #shutdown()} followed by {@link
   * #awaitTermination(long, TimeUnit)}, except that after those methods are called any remaining
   * JCR sessions are terminated automatically. This is useful when shutting down while there are
   * long-running JCR sessions (such as for event listeners).
   *
   * @param timeout the maximum time to wait for each component in this engine
   * @param unit the time unit of the timeout argument
   * @throws InterruptedException if interrupted while waiting
   */
  public void shutdownAndAwaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
    shutdown();
    awaitTermination(timeout, unit);
    for (JcrRepositoryHolder repository : repositories.values()) {
      if (repository != null) repository.terminateAllSessions();
    }
  }

  protected Logger getLogger() {
    return log;
  }

  protected Problems problems() {
    return problems;
  }

  protected class JcrRepositoryHolder {
    private final String repositoryName;
    private JcrRepository repository;
    private Future<JcrRepository> future;
    private Throwable error;

    protected JcrRepositoryHolder(String repositoryName, Future<JcrRepository> future) {
      this.repositoryName = repositoryName;
      this.future = future;
      assert this.future != null;
    }

    public String getName() {
      return repositoryName;
    }

    public synchronized JcrRepository getRepository() throws RepositoryException {
      if (repository == null) {
        if (future != null) {
          try {
            // Otherwise it is still initializing, so wait for it ...
            this.repository = future.get();
          } catch (Throwable e) {
            error = e.getCause();
            String msg =
                JcrI18n.errorStartingRepositoryCheckConfiguration.text(
                    repositoryName, error.getMessage());
            throw new RepositoryException(msg, error);
          } finally {
            this.future = null;
          }
        }
        if (repository == null) {
          // There is no future, but the repository could not be initialized correctly ...
          String msg =
              JcrI18n.errorStartingRepositoryCheckConfiguration.text(
                  repositoryName, error.getMessage());
          throw new RepositoryException(msg, error);
        }
      }
      return this.repository;
    }

    public synchronized void close() {
      if (future != null) {
        try {
          future.cancel(false);
        } finally {
          future = null;
        }
      }
      if (repository != null) {
        try {
          repository.close();
        } finally {
          repository = null;
        }
      }
    }

    public synchronized void terminateAllSessions() {
      // only need to do this on repositories that have been used; i.e., not including
      // just-initialized repositories
      if (repository != null) repository.terminateAllSessions();
    }

    public synchronized void cleanUpLocks() {
      // only need to do this on repositories that have been used; i.e., not including
      // just-initialized repositories
      if (repository != null) {
        try {
          repository.getRepositoryLockManager().cleanUpLocks();
        } catch (Throwable t) {
          getLogger().error(t, JcrI18n.errorCleaningUpLocks, repository.getRepositorySourceName());
        }
      }
    }

    /**
     * {@inheritDoc}
     *
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
      return repositoryName;
    }
  }

  protected class RepositoryInitializer implements Callable<JcrRepository> {
    private final String repositoryName;
    private final CountDownLatch latch;

    protected RepositoryInitializer(String repositoryName) {
      this(repositoryName, null);
    }

    protected RepositoryInitializer(String repositoryName, CountDownLatch latch) {
      this.repositoryName = repositoryName;
      this.latch = latch;
    }

    public JcrRepository call() throws Exception {
      JcrRepository repository = null;
      try {
        repository = doCreateJcrRepository(repositoryName);
        getLogger().info(JcrI18n.completedStartingRepository, repositoryName);
        return repository;
      } catch (RepositoryException t) {
        // Record this in the problems ...
        problems()
            .addError(
                t,
                JcrI18n.errorStartingRepositoryCheckConfiguration,
                repositoryName,
                t.getMessage());
        throw t;
      } catch (Throwable t) {
        // Record this in the problems ...
        problems()
            .addError(
                t,
                JcrI18n.errorStartingRepositoryCheckConfiguration,
                repositoryName,
                t.getMessage());
        String msg =
            JcrI18n.errorStartingRepositoryCheckConfiguration.text(repositoryName, t.getMessage());
        throw new RepositoryException(msg, t);
      } finally {
        if (latch != null) latch.countDown();
      }
    }
  }
}
Exemple #11
0
  protected JcrRepository doCreateJcrRepository(String repositoryName)
      throws RepositoryException, PathNotFoundException {
    RepositoryConnectionFactory connectionFactory = getRepositoryConnectionFactory();
    Map<String, String> descriptors = new HashMap<String, String>();
    Map<Option, String> options = new HashMap<Option, String>();

    // Read the subgraph that represents the repository ...
    PathFactory pathFactory = getExecutionContext().getValueFactories().getPathFactory();
    Path repositoriesPath =
        pathFactory.create(configuration.getPath(), ModeShapeLexicon.REPOSITORIES);
    Path repositoryPath = pathFactory.create(repositoriesPath, repositoryName);
    Name repoName =
        getExecutionContext().getValueFactories().getNameFactory().create(repositoryName);
    Graph configuration = null;
    Subgraph subgraph = getConfigurationSubgraph(false);

    // Read the options ...
    Path optionsPath =
        pathFactory.createRelativePath(
            ModeShapeLexicon.REPOSITORIES, repoName, ModeShapeLexicon.OPTIONS);
    Node optionsNode = subgraph.getNode(optionsPath);
    if (optionsNode != null) {
      for (Location optionLocation : optionsNode.getChildren()) {
        Node optionNode = subgraph.getNode(optionLocation);
        Path.Segment segment = optionLocation.getPath().getLastSegment();
        Property valueProperty = optionNode.getProperty(ModeShapeLexicon.VALUE);
        if (valueProperty == null) {
          log.warn(JcrI18n.noOptionValueProvided, segment.getName().getLocalName());
          continue;
        }
        Option option = Option.findOption(segment.getName().getLocalName());
        if (option == null) {
          log.warn(JcrI18n.invalidOptionProvided, segment.getName().getLocalName());
          continue;
        }
        options.put(option, valueProperty.getFirstValue().toString());
      }
    }

    // Disable the derived content removal option if not explicitly set and no sequencers ...
    if (!options.containsKey(Option.REMOVE_DERIVED_CONTENT_WITH_ORIGINAL)
        && getSequencingService().getSequencers().isEmpty()) {
      options.put(Option.REMOVE_DERIVED_CONTENT_WITH_ORIGINAL, Boolean.FALSE.toString());
    }

    // Read the descriptors ...
    Path descriptorsPath =
        pathFactory.createRelativePath(
            ModeShapeLexicon.REPOSITORIES, repoName, ModeShapeLexicon.DESCRIPTORS);
    Node descriptorsNode = subgraph.getNode(descriptorsPath);
    if (descriptorsNode != null) {
      for (Location descriptorLocation : descriptorsNode.getChildren()) {
        Node optionNode = subgraph.getNode(descriptorLocation);
        Path.Segment segment = descriptorLocation.getPath().getLastSegment();
        Property valueProperty = optionNode.getProperty(ModeShapeLexicon.VALUE);
        if (valueProperty == null) continue;
        descriptors.put(segment.getName().getLocalName(), valueProperty.getFirstValue().toString());
      }
    }

    // Read the namespaces ...
    ExecutionContext context = getExecutionContext();
    Path namespacesPath =
        pathFactory.createRelativePath(
            ModeShapeLexicon.REPOSITORIES, repoName, ModeShapeLexicon.NAMESPACES);
    Node namespacesNode = subgraph.getNode(namespacesPath);
    descriptors.put(org.modeshape.jcr.api.Repository.REPOSITORY_NAME, repositoryName);
    if (namespacesNode != null) {
      configuration = getConfigurationGraph();
      GraphNamespaceRegistry registry =
          new GraphNamespaceRegistry(
              configuration,
              namespacesNode.getLocation().getPath(),
              ModeShapeLexicon.URI,
              ModeShapeLexicon.GENERATED);
      context = context.with(registry);
    }

    // Get the name of the source ...
    Path repoPath = pathFactory.createRelativePath(ModeShapeLexicon.REPOSITORIES, repoName);
    Node repoNode = subgraph.getNode(repoPath);
    if (repoNode == null) {
      // There is no repository with the supplied name ...
      throw new PathNotFoundException(
          Location.create(repoPath),
          repositoriesPath,
          JcrI18n.repositoryDoesNotExist.text(readable(repoName)));
    }
    Property property = repoNode.getProperty(ModeShapeLexicon.SOURCE_NAME);
    if (property == null || property.isEmpty()) {
      if (configuration == null) configuration = getConfigurationGraph();
      String readableName = readable(ModeShapeLexicon.SOURCE_NAME);
      String readablePath = readable(subgraph.getLocation());
      String msg =
          JcrI18n.propertyNotFoundOnNode.text(
              readableName, readablePath, configuration.getCurrentWorkspaceName());
      throw new RepositoryException(msg);
    }
    String sourceName =
        context.getValueFactories().getStringFactory().create(property.getFirstValue());

    // Verify the sourc exists ...
    RepositorySource source = getRepositorySource(sourceName);
    if (source == null) {
      throw new RepositoryException(
          JcrI18n.repositoryReferencesNonExistantSource.text(repositoryName, sourceName));
    }

    // Read the initial content ...
    String initialContentForNewWorkspaces = null;
    for (Location initialContentLocation : repoNode.getChildren(ModeShapeLexicon.INITIAL_CONTENT)) {
      Node initialContent = subgraph.getNode(initialContentLocation);
      if (initialContent == null) continue;

      // Determine where to load the initial content from ...
      Property contentReference = initialContent.getProperty(ModeShapeLexicon.CONTENT);
      if (contentReference == null || contentReference.isEmpty()) {
        if (configuration == null) configuration = getConfigurationGraph();
        String readableName = readable(ModeShapeLexicon.CONTENT);
        String readablePath = readable(initialContentLocation);
        String msg =
            JcrI18n.propertyNotFoundOnNode.text(
                readableName, readablePath, configuration.getCurrentWorkspaceName());
        throw new RepositoryException(msg);
      }
      String contentRef = string(contentReference.getFirstValue());

      // Determine which workspaces this should apply to ...
      Property workspaces = initialContent.getProperty(ModeShapeLexicon.WORKSPACES);
      if (workspaces == null || workspaces.isEmpty()) {
        if (configuration == null) configuration = getConfigurationGraph();
        String readableName = readable(ModeShapeLexicon.WORKSPACES);
        String readablePath = readable(initialContentLocation);
        String msg =
            JcrI18n.propertyNotFoundOnNode.text(
                readableName, readablePath, configuration.getCurrentWorkspaceName());
        throw new RepositoryException(msg);
      }

      // Load the initial content into a transient source ...
      XmlFileRepositorySource initialContentSource = new XmlFileRepositorySource();
      initialContentSource.setName("Initial content for " + repositoryName);
      initialContentSource.setContentLocation(contentRef);
      Graph initialContentGraph = Graph.create(initialContentSource, context);
      Graph sourceGraph = Graph.create(sourceName, connectionFactory, context);

      // And initialize the source with the content (if not already there) ...
      for (Object value : workspaces) {
        String workspaceName = string(value);
        if (workspaceName != null && workspaceName.trim().length() != 0) {
          // Load the content into the workspace with this name ...
          sourceGraph.useWorkspace(workspaceName);
          try {
            sourceGraph.merge(initialContentGraph);
          } catch (RuntimeException e) {
            throw new RepositoryException(
                JcrI18n.unableToImportInitialContent.text(readable(repoName), contentRef), e);
          }
        }
      }

      // Determine if this initial content should apply to new workspaces ...
      Property applyToNewWorkspaces =
          initialContent.getProperty(ModeShapeLexicon.APPLY_TO_NEW_WORKSPACES);
      if (applyToNewWorkspaces != null
          && !applyToNewWorkspaces.isEmpty()
          && isTrue(applyToNewWorkspaces.getFirstValue())) {
        initialContentForNewWorkspaces =
            contentRef; // may overwrite the value if seen more than once!
      }
    }

    // Find the capabilities ...
    RepositorySourceCapabilities capabilities = source.getCapabilities();
    // Create the repository ...
    JcrRepository repository =
        new JcrRepository(
            context,
            connectionFactory,
            sourceName,
            getRepositoryService().getRepositoryLibrary(),
            capabilities,
            descriptors,
            options,
            initialContentForNewWorkspaces);

    // Register all the the node types ...
    Path nodeTypesPath =
        pathFactory.createRelativePath(
            ModeShapeLexicon.REPOSITORIES, repoName, JcrLexicon.NODE_TYPES);
    Node nodeTypesNode = subgraph.getNode(nodeTypesPath);
    if (nodeTypesNode != null) {
      boolean needToRefreshSubgraph = false;
      if (configuration == null) configuration = getConfigurationGraph();

      // Expand any references to a CND file
      Property resourceProperty = nodeTypesNode.getProperty(ModeShapeLexicon.RESOURCE);
      if (resourceProperty != null) {
        ClassLoader classLoader = this.context.getClassLoader();
        for (Object resourceValue : resourceProperty) {
          String resources =
              this.context.getValueFactories().getStringFactory().create(resourceValue);

          for (String resource : resources.split("\\s*,\\s*")) {
            Graph.Batch batch = configuration.batch();
            GraphBatchDestination destination = new GraphBatchDestination(batch);

            Path nodeTypesAbsPath = pathFactory.create(repositoryPath, JcrLexicon.NODE_TYPES);
            CndImporter importer = new CndImporter(destination, nodeTypesAbsPath, true, false);
            InputStream is = IoUtil.getResourceAsStream(resource, classLoader, getClass());
            Problems cndProblems = new SimpleProblems();
            if (is == null) {
              String msg =
                  JcrI18n.unableToFindNodeTypeDefinitionsOnClasspathOrFileOrUrl.text(resource);
              throw new RepositoryException(msg);
            }
            try {
              importer.importFrom(is, cndProblems, resource);
              batch.execute();
              needToRefreshSubgraph = true;
            } catch (IOException ioe) {
              String msg = JcrI18n.errorLoadingNodeTypeDefintions.text(resource, ioe.getMessage());
              throw new RepositoryException(msg, ioe);
            }
            if (!cndProblems.isEmpty()) {
              // Add any warnings or information to this engine's list ...
              getProblems().addAll(cndProblems);
              if (cndProblems.hasErrors()) {
                String msg = null;
                Throwable cause = null;
                for (Problem problem : cndProblems) {
                  if (problem.getStatus() == Status.ERROR) {
                    msg = problem.getMessageString();
                    cause = problem.getThrowable();
                    break;
                  }
                }
                throw new RepositoryException(
                    JcrI18n.errorLoadingNodeTypeDefintions.text(resource, msg), cause);
              }
            }
          }
        }
      }

      // Load any namespaces from the configuration into the repository's context ...
      NamespaceRegistry repoRegistry = repository.getExecutionContext().getNamespaceRegistry();
      repoRegistry.register(configuration.getContext().getNamespaceRegistry().getNamespaces());

      // Re-read the subgraph, in case any new nodes were added
      Subgraph nodeTypesSubgraph = subgraph;
      if (needToRefreshSubgraph) {
        nodeTypesSubgraph =
            configuration.getSubgraphOfDepth(4).at(nodeTypesNode.getLocation().getPath());
      }

      repository
          .getRepositoryTypeManager()
          .registerNodeTypes(nodeTypesSubgraph, nodeTypesNode.getLocation(), false);
    }

    return repository;
  }