Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

MODE-1180 Starting the JcrEngine starts repositories in parallel #114

Merged
merged 5 commits into from

2 participants

@rhauch
Owner

When starting the JcrEngine and requesting that the repositories be initialized now performs the initialization in parallel (up to 4 at a time). There is also now an alternative method that accepts a timeout specification, after which the method will return even if the repositories are not all initialized. Note, however, that in such cases, getRepository(String) will block until all repositories are initialized.

A new unit test was added, and all unit and integration tests pass.

rhauch added some commits
@rhauch rhauch MODE-1178 Corrected a few compiler warnings 1154649
@rhauch rhauch MODE-1180 Starting the JcrEngine starts repositories in parallel
When starting the JcrEngine and requesting that the repositories be initialized now performs the initialization in parallel (up to 4 at a time). There is also now an alternative method that accepts a timeout specification, after which the method will return even if the repositories are not all initialized. Note, however, that in such cases, getRepository(String) will block until all repositories are initialized.

A new unit test was added, and all unit and integration tests pass.
992c138
@bcarothers-xx

This is nitpicky, but you might want to add a note that a negative value for this parameter indicates that there is no timeout.

Owner

Very good point.

rhauch added some commits
@rhauch rhauch MODE-1180 Improved JavaDoc for recently-added method
The JavaDoc for the JcrEngine.start(boolean,long,TimeUnit) was improved to detail how to specify "infinite wait".
e180ea8
@rhauch rhauch MODE-1180 Improved the locking mechanism of JcrEngine
The locking within JcrEngine to guarantee concurrent access to the repositories was a bit primitive and simplistic - the initialization of all repositories locked out all access to other repositories.
This new approach allows the initialization to proceed without maintaining the locks. In fact, any operation that uses a JcrRepository will lock the engine only when obtaining the JcrRepositoryHolder. Once the holder is obtained, the getting of the JcrRepository will not require the lock, but may block until the repository is initialized.
d536a62
@rhauch rhauch commented on the diff
...pe-jcr/src/main/java/org/modeshape/jcr/JcrEngine.java
@@ -84,7 +91,7 @@ public class JcrEngine extends ModeShapeEngine implements Repositories {
private static final Logger log = Logger.getLogger(ModeShapeEngine.class);
- private final Map<String, JcrRepository> repositories;
+ private final Map<String, JcrRepositoryHolder> repositories;
@rhauch Owner
rhauch added a note

@bcarothers The JcrEngine is now managing a map of JcrRepositoryHolder instances that basically wrap the JcrRepository (or a Future that may be initializing the repository). The single lock is still used to ensure a single thread operates on the engine's map at any one time, but that lock is there merely to obtain the correct JcrRepositoryHolder instance(s), and the lock is then released. Obtaining the actual JcrRepository (which may block if the repository is still be initialized) is no longer done within the scope of the lock.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...pe-jcr/src/main/java/org/modeshape/jcr/JcrEngine.java
((45 lines not shown))
try {
- getRepository(repositoryName);
- } catch (Throwable t) {
- // Record this in the problems ...
- this.problems.addError(t, JcrI18n.errorStartingRepositoryCheckConfiguration, repositoryName, t.getMessage());
+ repositoriesLock.lock();
@rhauch Owner
rhauch added a note

@bcarothers The repositories lock is held while a holder for each repository is created and put into the map and the callable is submitted into the queue. But because the actual initialization is being done by threads in the ExecutorService, the initialization completes outside of the lock scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...pe-jcr/src/main/java/org/modeshape/jcr/JcrEngine.java
((86 lines not shown))
+ // 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());
+ }
+
+ } else {
+ // Otherwise there's just 0 or 1, so simple to start in serial ...
+ for (String repositoryName : repositoryNames) {
+ try {
+ getRepository(repositoryName);
@rhauch Owner
rhauch added a note

@bcarothers This is all done within the same thread, and this does block; see the comment in the getRepository(String) method for reasons why.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@rhauch
Owner

@bcarothers I think this is a pretty good implementation of what we talked about on IRC.

@rhauch rhauch MODE-1180 Improved the locking mechanism of JcrEngine
Made additional improvements to simplify the logic and to handle cases where one thread calls getRepository(String) but, before that repository is initialized, another thread calls the same method with the same repository name. Before these changes, this would cause the second thread to return a null reference.
ab16049
@bcarothers-xx
Collaborator

I like the approach with the Futures. It looks good to me.

@rhauch rhauch merged commit ab16049 into ModeShape:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 31, 2011
  1. @rhauch
  2. @rhauch

    MODE-1180 Starting the JcrEngine starts repositories in parallel

    rhauch authored
    When starting the JcrEngine and requesting that the repositories be initialized now performs the initialization in parallel (up to 4 at a time). There is also now an alternative method that accepts a timeout specification, after which the method will return even if the repositories are not all initialized. Note, however, that in such cases, getRepository(String) will block until all repositories are initialized.
    
    A new unit test was added, and all unit and integration tests pass.
Commits on Jun 1, 2011
  1. @rhauch

    MODE-1180 Improved JavaDoc for recently-added method

    rhauch authored
    The JavaDoc for the JcrEngine.start(boolean,long,TimeUnit) was improved to detail how to specify "infinite wait".
  2. @rhauch

    MODE-1180 Improved the locking mechanism of JcrEngine

    rhauch authored
    The locking within JcrEngine to guarantee concurrent access to the repositories was a bit primitive and simplistic - the initialization of all repositories locked out all access to other repositories.
    This new approach allows the initialization to proceed without maintaining the locks. In fact, any operation that uses a JcrRepository will lock the engine only when obtaining the JcrRepositoryHolder. Once the holder is obtained, the getting of the JcrRepository will not require the lock, but may block until the repository is initialized.
  3. @rhauch

    MODE-1180 Improved the locking mechanism of JcrEngine

    rhauch authored
    Made additional improvements to simplify the logic and to handle cases where one thread calls getRepository(String) but, before that repository is initialized, another thread calls the same method with the same repository name. Before these changes, this would cause the second thread to return a null reference.
This page is out of date. Refresh to see the latest.
View
2  extensions/modeshape-connector-disk/src/main/java/org/modeshape/connector/disk/DiskSource.java
@@ -344,6 +344,7 @@ public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspa
* @see #getPredefinedWorkspaceNames()
* @see #setCreatingWorkspacesAllowed(boolean)
*/
+ @Override
public boolean isCreatingWorkspacesAllowed() {
return capabilities.supportsCreatingWorkspaces();
}
@@ -455,7 +456,6 @@ public synchronized Reference getReference() {
return ref;
}
-
/**
* {@inheritDoc}
*/
View
3  ...sions/modeshape-connector-disk/src/test/java/org/modeshape/connector/disk/DiskConnectorReadableTest.java
@@ -2,7 +2,6 @@
import java.io.File;
import java.io.IOException;
-import javax.naming.NamingException;
import org.modeshape.common.statistic.Stopwatch;
import org.modeshape.common.util.FileUtil;
import org.modeshape.graph.Graph;
@@ -20,7 +19,7 @@
* @see org.modeshape.graph.connector.test.AbstractConnectorTest#setUpSource()
*/
@Override
- protected RepositorySource setUpSource() throws NamingException {
+ protected RepositorySource setUpSource() {
String[] predefinedWorkspaceNames = new String[] {"aircraft", "cars"};
DiskSource source = new DiskSource();
source.setName("Test Repository");
View
3  ...sions/modeshape-connector-disk/src/test/java/org/modeshape/connector/disk/DiskConnectorWritableTest.java
@@ -2,7 +2,6 @@
import java.io.File;
import java.io.IOException;
-import javax.naming.NamingException;
import org.modeshape.common.util.FileUtil;
import org.modeshape.graph.Graph;
import org.modeshape.graph.connector.RepositorySource;
@@ -19,7 +18,7 @@
* @see org.modeshape.graph.connector.test.AbstractConnectorTest#setUpSource()
*/
@Override
- protected RepositorySource setUpSource() throws NamingException {
+ protected RepositorySource setUpSource() {
String[] predefinedWorkspaceNames = new String[] {"default"};
DiskSource source = new DiskSource();
source.setName("Test Repository");
View
3  ...odeshape-connector-infinispan/src/main/java/org/modeshape/connector/infinispan/BaseInfinispanSource.java
@@ -34,11 +34,11 @@
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory;
-import org.modeshape.common.annotation.ThreadSafe;
import org.infinispan.manager.CacheContainer;
import org.modeshape.common.annotation.Category;
import org.modeshape.common.annotation.Description;
import org.modeshape.common.annotation.Label;
+import org.modeshape.common.annotation.ThreadSafe;
import org.modeshape.common.i18n.I18n;
import org.modeshape.common.util.StringUtil;
import org.modeshape.graph.cache.CachePolicy;
@@ -299,6 +299,7 @@ public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspa
* @see #getPredefinedWorkspaceNames()
* @see #setCreatingWorkspacesAllowed(boolean)
*/
+ @Override
public boolean isCreatingWorkspacesAllowed() {
return capabilities.supportsCreatingWorkspaces();
}
View
1  ...ns/modeshape-connector-jbosscache/src/main/java/org/modeshape/connector/jbosscache/JBossCacheSource.java
@@ -464,6 +464,7 @@ public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspa
* @see #getPredefinedWorkspaceNames()
* @see #setCreatingWorkspacesAllowed(boolean)
*/
+ @Override
@Description( i18n = JBossCacheConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyDescription" )
@Label( i18n = JBossCacheConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyLabel" )
@Category( i18n = JBossCacheConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyCategory" )
View
1  extensions/modeshape-connector-svn/src/main/java/org/modeshape/connector/svn/SvnRepositorySource.java
@@ -267,6 +267,7 @@ public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspa
* @see #getPredefinedWorkspaceNames()
* @see #setCreatingWorkspacesAllowed(boolean)
*/
+ @Override
@Description( i18n = SvnRepositoryConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyDescription" )
@Label( i18n = SvnRepositoryConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyLabel" )
@Category( i18n = SvnRepositoryConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyCategory" )
View
257 modeshape-jcr/src/main/java/org/modeshape/jcr/JcrEngine.java
@@ -32,8 +32,14 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -49,6 +55,7 @@
import org.modeshape.common.util.CheckArg;
import org.modeshape.common.util.IoUtil;
import org.modeshape.common.util.Logger;
+import org.modeshape.common.util.NamedThreadFactory;
import org.modeshape.graph.ExecutionContext;
import org.modeshape.graph.Graph;
import org.modeshape.graph.Location;
@@ -84,9 +91,10 @@
private static final Logger log = Logger.getLogger(ModeShapeEngine.class);
- private final Map<String, JcrRepository> repositories;
+ private final Map<String, JcrRepositoryHolder> repositories;
@rhauch Owner
rhauch added a note

@bcarothers The JcrEngine is now managing a map of JcrRepositoryHolder instances that basically wrap the JcrRepository (or a Future that may be initializing the repository). The single lock is still used to ensure a single thread operates on the engine's map at any one time, but that lock is there merely to obtain the correct JcrRepositoryHolder instance(s), and the lock is then released. Obtaining the actual JcrRepository (which may block if the repository is still be initialized) is no longer done within the scope of the lock.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
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
@@ -96,9 +104,13 @@
JcrEngine( ExecutionContext context,
ModeShapeConfiguration.ConfigurationDefinition configuration ) {
super(context, configuration);
- this.repositories = new HashMap<String, JcrRepository>();
+ 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);
}
/**
@@ -110,35 +122,32 @@
* </p>
*/
void cleanUpLocks() {
- Collection<JcrRepository> repos;
+ Collection<JcrRepositoryHolder> repos = null;
try {
- // Make a copy of the repositories to minimize the time that the lock needs to be held
+ // Make a copy of the repositories to minimize the time that the lock needs to be held.
repositoriesLock.lock();
- repos = new ArrayList<JcrRepository>(repositories.values());
+ repos = new ArrayList<JcrRepositoryHolder>(repositories.values());
} finally {
repositoriesLock.unlock();
}
- for (JcrRepository repository : repos) {
- try {
- repository.getRepositoryLockManager().cleanUpLocks();
- } catch (Throwable t) {
- log.error(t, JcrI18n.errorCleaningUpLocks, repository.getRepositorySourceName());
- }
+ 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 (JcrRepository repository : repositories.values()) {
- repository.close();
+ for (JcrRepositoryHolder holder : repositories.values()) {
+ holder.close();
}
this.repositories.clear();
} finally {
@@ -235,6 +244,10 @@ public void run() {
/**
* 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).
+ * </p>
*
* @param validateRepositoryConfigs true if the configurations of each repository should be validated and each repository
* started/initialized, or false otherwise
@@ -245,15 +258,62 @@ public void run() {
* @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.
+ * </p>
+ *
+ * @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) {
- for (String repositoryName : getRepositoryNames()) {
- try {
- getRepository(repositoryName);
- } catch (Throwable t) {
- // Record this in the problems ...
- this.problems.addError(t, JcrI18n.errorStartingRepositoryCheckConfiguration, repositoryName, t.getMessage());
+ 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());
}
}
}
@@ -290,23 +350,30 @@ public final JcrRepository getRepository( String repositoryName ) throws Reposit
CheckArg.isNotEmpty(repositoryName, "repositoryName");
checkRunning();
+ JcrRepositoryHolder holder = null;
try {
repositoriesLock.lock();
- JcrRepository repository = repositories.get(repositoryName);
- if (repository == null) {
- try {
- repository = doCreateJcrRepository(repositoryName);
- } catch (PathNotFoundException e) {
- // The repository name is not a valid repository ...
- String msg = JcrI18n.repositoryDoesNotExist.text(repositoryName);
- throw new RepositoryException(msg);
- }
- repositories.put(repositoryName, repository);
+ 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();
}
- return repository;
+ 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;
}
/**
@@ -625,8 +692,136 @@ public void shutdownAndAwaitTermination( long timeout,
TimeUnit unit ) throws InterruptedException {
shutdown();
awaitTermination(timeout, unit);
- for (JcrRepository repository : repositories.values()) {
+ 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();
+ }
+ }
+ }
+
}
View
2  modeshape-jcr/src/main/java/org/modeshape/jcr/JcrI18n.java
@@ -61,6 +61,8 @@
public static I18n failedToReadPropertyFromManifest;
public static I18n errorLoadingNodeTypeDefintions;
public static I18n errorStartingRepositoryCheckConfiguration;
+ public static I18n completedStartingRepository;
+ public static I18n startingAllRepositoriesWasInterrupted;
public static I18n unableToFindNodeTypeDefinitionsOnClasspathOrFileOrUrl;
public static I18n unableToFindResourceOnClasspathOrFileOrUrl;
public static I18n unableToImportInitialContent;
View
2  modeshape-jcr/src/main/java/org/modeshape/jcr/RepositoryQueryManager.java
@@ -316,7 +316,7 @@ public void notify( Changes changes ) {
* @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable)
*/
public Thread newThread( Runnable r ) {
- Thread thread = new Thread("modeshape-indexing");
+ Thread thread = new Thread(r, "modeshape-indexing");
thread.setPriority(Thread.NORM_PRIORITY + 3);
return thread;
}
View
2  modeshape-jcr/src/main/resources/org/modeshape/jcr/JcrI18n.properties
@@ -51,6 +51,8 @@ failedToReadPropertiesFromManifest = Error reading manifest properties: {0}
failedToReadPropertyFromManifest = "{0}" property not found in manifest
errorLoadingNodeTypeDefintions = Error loading CND file "{0}": {1}
errorStartingRepositoryCheckConfiguration = Error starting the "{0}" repository (check the configuration): {1}
+startingAllRepositoriesWasInterrupted = The engine was interrupted while starting each repository, and will be left in an unknown state: {0}
+completedStartingRepository = Completed starting the "{0}" repository
unableToFindNodeTypeDefinitionsOnClasspathOrFileOrUrl = Unable to find the node type definition file "{0}" on the classpath, as a relative or absolute file, or resolve as a URL
unableToFindResourceOnClasspathOrFileOrUrl = Unable to find "{0}" on the classpath, as a relative or absolute file, or resolve as a URL
unableToImportInitialContent = Unable to import initial content for repository "{0}" from "{1}". Check that this file exists on the file system or classpath.
View
64 modeshape-jcr/src/test/java/org/modeshape/jcr/JcrEngineTest.java
@@ -27,6 +27,7 @@
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
import java.net.URL;
+import java.util.concurrent.TimeUnit;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Workspace;
@@ -246,6 +247,44 @@ public void shouldAllowCreatingWorkspaces() throws Exception {
jcrSession.logout();
}
+ @FixFor( "MODE-1180" )
+ @Test
+ public void shouldCreateRepositoriesUponStartup() throws Exception {
+ configuration = new JcrConfiguration();
+ configuration.repositorySource("car-source")
+ .usingClass(InMemoryRepositorySource.class)
+ .setDescription("The automobile content")
+ .setProperty("defaultWorkspaceName", "default");
+ configuration.repository("cars")
+ .setSource("car-source")
+ .registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
+ .addNodeTypes(resourceUrl("cars.cnd"))
+ .setInitialContent("src/test/resources/initialWorkspaceContent.xml", "default")
+ .setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
+ configuration.repositorySource("product-source")
+ .usingClass(InMemoryRepositorySource.class)
+ .setDescription("The products content")
+ .setProperty("defaultWorkspaceName", "default");
+ configuration.repository("products")
+ .setSource("product-source")
+ .registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
+ .addNodeTypes(resourceUrl("cars.cnd"))
+ .setInitialContent("src/test/resources/initialWorkspaceContent.xml", "default")
+ .setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
+ engine = configuration.build();
+ assertThat(engine.getProblems().hasErrors(), is(false));
+ engine.start(true, 1L, TimeUnit.NANOSECONDS);
+ repository = engine.getRepository("products");
+ repository = engine.getRepository("cars");
+ session = repository.login();
+
+ assertNodeType("car:Car", false, false, true, false, null, 0, 12, "nt:unstructured", "mix:created");
+
+ // Check that the content is there...
+ assertThat(session.getRootNode().hasNode("Cars"), is(true));
+ assertThat(session.getNode("/Cars"), is(notNullValue()));
+ }
+
@FixFor( "MODE-1119" )
@Test
public void shouldCreateRepositoryConfiguredWithNoInitialContent() throws Exception {
@@ -361,6 +400,31 @@ public void shouldRecordProblemWhenStartingRepositoriesConfiguredWithValidInitia
}
@FixFor( "MODE-1119" )
+ @Test( expected = RepositoryException.class )
+ public void shouldRecordProblemWhenStartingRepositoriesConfiguredWithValidInitialContentPathButEmptyFileAndFailWhenGettingRepository()
+ throws Exception {
+ configuration = new JcrConfiguration();
+ configuration.repositorySource("car-source")
+ .usingClass(InMemoryRepositorySource.class)
+ .setDescription("The automobile content")
+ .setProperty("defaultWorkspaceName", "default");
+ configuration.repository("cars")
+ .setSource("car-source")
+ .registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
+ .addNodeTypes(resourceUrl("cars.cnd"))
+ .setInitialContent("src/test/resources/emptyFile.xml", "default")
+ .setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
+ engine = configuration.build();
+ assertThat(engine.getProblems().hasErrors(), is(false));
+ engine.start(true);
+ assertThat(engine.getProblems().hasErrors(), is(true));
+ assertThat(engine.getProblems().size(), is(1)); // one error
+ assertThat(engine.getProblems().iterator().next().getStatus(), is(Problem.Status.ERROR));
+ // The following will fail ...
+ engine.getRepository("cars");
+ }
+
+ @FixFor( "MODE-1119" )
@Test
public void shouldRecordProblemWhenStartingRepositoriesConfiguredWithIncorrectInitialContentPath() throws Exception {
configuration = new JcrConfiguration();
Something went wrong with that request. Please try again.