Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

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

Merged
merged 5 commits into from almost 3 years ago

2 participants

Randall Hauch Brian Carothers
Randall Hauch
Owner
rhauch commented May 31, 2011

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.

added some commits May 31, 2011
Randall Hauch MODE-1178 Corrected a few compiler warnings 1154649
Randall Hauch 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
Brian Carothers

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.

added some commits June 01, 2011
Randall Hauch 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
Randall Hauch 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
Randall Hauch rhauch commented on the diff June 01, 2011
modeshape-jcr/src/main/java/org/modeshape/jcr/JcrEngine.java
@@ -84,7 +91,7 @@ public class JcrEngine extends ModeShapeEngine implements Repositories {
84 91
 
85 92
     private static final Logger log = Logger.getLogger(ModeShapeEngine.class);
86 93
 
87  
-    private final Map<String, JcrRepository> repositories;
  94
+    private final Map<String, JcrRepositoryHolder> repositories;
1
Randall Hauch Owner
rhauch added a note June 01, 2011

@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
modeshape-jcr/src/main/java/org/modeshape/jcr/JcrEngine.java
((45 lines not shown))
251 295
                 try {
252  
-                    getRepository(repositoryName);
253  
-                } catch (Throwable t) {
254  
-                    // Record this in the problems ...
255  
-                    this.problems.addError(t, JcrI18n.errorStartingRepositoryCheckConfiguration, repositoryName, t.getMessage());
  296
+                    repositoriesLock.lock();
1
Randall Hauch Owner
rhauch added a note June 01, 2011

@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
modeshape-jcr/src/main/java/org/modeshape/jcr/JcrEngine.java
((86 lines not shown))
  332
+                // Now wait for the all the startups to complete ...
  333
+                try {
  334
+                    if (timeout < 0L) {
  335
+                        latch.await();
  336
+                    } else {
  337
+                        latch.await(timeout, timeoutUnit);
  338
+                    }
  339
+                } catch (InterruptedException e) {
  340
+                    this.problems.addError(e, JcrI18n.startingAllRepositoriesWasInterrupted, e.getMessage());
  341
+                }
  342
+
  343
+            } else {
  344
+                // Otherwise there's just 0 or 1, so simple to start in serial ...
  345
+                for (String repositoryName : repositoryNames) {
  346
+                    try {
  347
+                        getRepository(repositoryName);
1
Randall Hauch Owner
rhauch added a note June 01, 2011

@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
Randall Hauch
Owner
rhauch commented June 01, 2011

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

Randall Hauch 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
Brian Carothers
Collaborator

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

Randall Hauch rhauch merged commit ab16049 into from June 01, 2011
Randall Hauch rhauch closed this June 01, 2011
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 5 unique commits by 1 author.

May 31, 2011
Randall Hauch MODE-1178 Corrected a few compiler warnings 1154649
Randall Hauch 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
Jun 01, 2011
Randall Hauch 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
Randall Hauch 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
Randall Hauch 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
This page is out of date. Refresh to see the latest.
2  extensions/modeshape-connector-disk/src/main/java/org/modeshape/connector/disk/DiskSource.java
@@ -344,6 +344,7 @@ public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspa
344 344
      * @see #getPredefinedWorkspaceNames()
345 345
      * @see #setCreatingWorkspacesAllowed(boolean)
346 346
      */
  347
+    @Override
347 348
     public boolean isCreatingWorkspacesAllowed() {
348 349
         return capabilities.supportsCreatingWorkspaces();
349 350
     }
@@ -455,7 +456,6 @@ public synchronized Reference getReference() {
455 456
         return ref;
456 457
     }
457 458
 
458  
-
459 459
     /**
460 460
      * {@inheritDoc}
461 461
      */
3  ...sions/modeshape-connector-disk/src/test/java/org/modeshape/connector/disk/DiskConnectorReadableTest.java
@@ -2,7 +2,6 @@
2 2
 
3 3
 import java.io.File;
4 4
 import java.io.IOException;
5  
-import javax.naming.NamingException;
6 5
 import org.modeshape.common.statistic.Stopwatch;
7 6
 import org.modeshape.common.util.FileUtil;
8 7
 import org.modeshape.graph.Graph;
@@ -20,7 +19,7 @@
20 19
      * @see org.modeshape.graph.connector.test.AbstractConnectorTest#setUpSource()
21 20
      */
22 21
     @Override
23  
-    protected RepositorySource setUpSource() throws NamingException {
  22
+    protected RepositorySource setUpSource() {
24 23
         String[] predefinedWorkspaceNames = new String[] {"aircraft", "cars"};
25 24
         DiskSource source = new DiskSource();
26 25
         source.setName("Test Repository");
3  ...sions/modeshape-connector-disk/src/test/java/org/modeshape/connector/disk/DiskConnectorWritableTest.java
@@ -2,7 +2,6 @@
2 2
 
3 3
 import java.io.File;
4 4
 import java.io.IOException;
5  
-import javax.naming.NamingException;
6 5
 import org.modeshape.common.util.FileUtil;
7 6
 import org.modeshape.graph.Graph;
8 7
 import org.modeshape.graph.connector.RepositorySource;
@@ -19,7 +18,7 @@
19 18
      * @see org.modeshape.graph.connector.test.AbstractConnectorTest#setUpSource()
20 19
      */
21 20
     @Override
22  
-    protected RepositorySource setUpSource() throws NamingException {
  21
+    protected RepositorySource setUpSource() {
23 22
         String[] predefinedWorkspaceNames = new String[] {"default"};
24 23
         DiskSource source = new DiskSource();
25 24
         source.setName("Test Repository");
3  ...odeshape-connector-infinispan/src/main/java/org/modeshape/connector/infinispan/BaseInfinispanSource.java
@@ -34,11 +34,11 @@
34 34
 import javax.naming.Reference;
35 35
 import javax.naming.StringRefAddr;
36 36
 import javax.naming.spi.ObjectFactory;
37  
-import org.modeshape.common.annotation.ThreadSafe;
38 37
 import org.infinispan.manager.CacheContainer;
39 38
 import org.modeshape.common.annotation.Category;
40 39
 import org.modeshape.common.annotation.Description;
41 40
 import org.modeshape.common.annotation.Label;
  41
+import org.modeshape.common.annotation.ThreadSafe;
42 42
 import org.modeshape.common.i18n.I18n;
43 43
 import org.modeshape.common.util.StringUtil;
44 44
 import org.modeshape.graph.cache.CachePolicy;
@@ -299,6 +299,7 @@ public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspa
299 299
      * @see #getPredefinedWorkspaceNames()
300 300
      * @see #setCreatingWorkspacesAllowed(boolean)
301 301
      */
  302
+    @Override
302 303
     public boolean isCreatingWorkspacesAllowed() {
303 304
         return capabilities.supportsCreatingWorkspaces();
304 305
     }
1  ...ns/modeshape-connector-jbosscache/src/main/java/org/modeshape/connector/jbosscache/JBossCacheSource.java
@@ -464,6 +464,7 @@ public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspa
464 464
      * @see #getPredefinedWorkspaceNames()
465 465
      * @see #setCreatingWorkspacesAllowed(boolean)
466 466
      */
  467
+    @Override
467 468
     @Description( i18n = JBossCacheConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyDescription" )
468 469
     @Label( i18n = JBossCacheConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyLabel" )
469 470
     @Category( i18n = JBossCacheConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyCategory" )
1  extensions/modeshape-connector-svn/src/main/java/org/modeshape/connector/svn/SvnRepositorySource.java
@@ -267,6 +267,7 @@ public synchronized void setPredefinedWorkspaceNames( String[] predefinedWorkspa
267 267
      * @see #getPredefinedWorkspaceNames()
268 268
      * @see #setCreatingWorkspacesAllowed(boolean)
269 269
      */
  270
+    @Override
270 271
     @Description( i18n = SvnRepositoryConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyDescription" )
271 272
     @Label( i18n = SvnRepositoryConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyLabel" )
272 273
     @Category( i18n = SvnRepositoryConnectorI18n.class, value = "creatingWorkspacesAllowedPropertyCategory" )
257  modeshape-jcr/src/main/java/org/modeshape/jcr/JcrEngine.java
@@ -32,8 +32,14 @@
32 32
 import java.util.HashSet;
33 33
 import java.util.Map;
34 34
 import java.util.Set;
  35
+import java.util.concurrent.Callable;
  36
+import java.util.concurrent.CountDownLatch;
  37
+import java.util.concurrent.ExecutorService;
  38
+import java.util.concurrent.Executors;
  39
+import java.util.concurrent.Future;
35 40
 import java.util.concurrent.ScheduledExecutorService;
36 41
 import java.util.concurrent.ScheduledThreadPoolExecutor;
  42
+import java.util.concurrent.ThreadFactory;
37 43
 import java.util.concurrent.TimeUnit;
38 44
 import java.util.concurrent.locks.Lock;
39 45
 import java.util.concurrent.locks.ReentrantLock;
@@ -49,6 +55,7 @@
49 55
 import org.modeshape.common.util.CheckArg;
50 56
 import org.modeshape.common.util.IoUtil;
51 57
 import org.modeshape.common.util.Logger;
  58
+import org.modeshape.common.util.NamedThreadFactory;
52 59
 import org.modeshape.graph.ExecutionContext;
53 60
 import org.modeshape.graph.Graph;
54 61
 import org.modeshape.graph.Location;
@@ -84,9 +91,10 @@
84 91
 
85 92
     private static final Logger log = Logger.getLogger(ModeShapeEngine.class);
86 93
 
87  
-    private final Map<String, JcrRepository> repositories;
  94
+    private final Map<String, JcrRepositoryHolder> repositories;
88 95
     private final Lock repositoriesLock;
89 96
     private final Map<String, Object> descriptors = new HashMap<String, Object>();
  97
+    private final ExecutorService repositoryStarterService;
90 98
 
91 99
     /**
92 100
      * Provides the ability to schedule lock clean-up
@@ -96,9 +104,13 @@
96 104
     JcrEngine( ExecutionContext context,
97 105
                ModeShapeConfiguration.ConfigurationDefinition configuration ) {
98 106
         super(context, configuration);
99  
-        this.repositories = new HashMap<String, JcrRepository>();
  107
+        this.repositories = new HashMap<String, JcrRepositoryHolder>();
100 108
         this.repositoriesLock = new ReentrantLock();
101 109
         initDescriptors();
  110
+
  111
+        // Create an executor service that we'll use to start the repositories ...
  112
+        ThreadFactory threadFactory = new NamedThreadFactory("modeshape-start-repo");
  113
+        this.repositoryStarterService = Executors.newCachedThreadPool(threadFactory);
102 114
     }
103 115
 
104 116
     /**
@@ -110,35 +122,32 @@
110 122
      * </p>
111 123
      */
112 124
     void cleanUpLocks() {
113  
-        Collection<JcrRepository> repos;
  125
+        Collection<JcrRepositoryHolder> repos = null;
114 126
 
115 127
         try {
116  
-            // Make a copy of the repositories to minimize the time that the lock needs to be held
  128
+            // Make a copy of the repositories to minimize the time that the lock needs to be held.
117 129
             repositoriesLock.lock();
118  
-            repos = new ArrayList<JcrRepository>(repositories.values());
  130
+            repos = new ArrayList<JcrRepositoryHolder>(repositories.values());
119 131
         } finally {
120 132
             repositoriesLock.unlock();
121 133
         }
122 134
 
123  
-        for (JcrRepository repository : repos) {
124  
-            try {
125  
-                repository.getRepositoryLockManager().cleanUpLocks();
126  
-            } catch (Throwable t) {
127  
-                log.error(t, JcrI18n.errorCleaningUpLocks, repository.getRepositorySourceName());
128  
-            }
  135
+        for (JcrRepositoryHolder repository : repos) {
  136
+            repository.cleanUpLocks();
129 137
         }
130 138
     }
131 139
 
132 140
     @Override
133 141
     protected void preShutdown() {
  142
+        repositoryStarterService.shutdown();
134 143
         scheduler.shutdown();
135 144
         super.preShutdown();
136 145
 
137 146
         try {
138 147
             this.repositoriesLock.lock();
139 148
             // Shut down all of the repositories ...
140  
-            for (JcrRepository repository : repositories.values()) {
141  
-                repository.close();
  149
+            for (JcrRepositoryHolder holder : repositories.values()) {
  150
+                holder.close();
142 151
             }
143 152
             this.repositories.clear();
144 153
         } finally {
@@ -235,6 +244,10 @@ public void run() {
235 244
     /**
236 245
      * Start this engine to make it available for use, and optionally start each of the repositories in the configuration. Any
237 246
      * errors starting the repositories will be logged as problems.
  247
+     * <p>
  248
+     * This method starts each repository in parallel, and returns only after all repositories have been started (or failed
  249
+     * startup).
  250
+     * </p>
238 251
      * 
239 252
      * @param validateRepositoryConfigs true if the configurations of each repository should be validated and each repository
240 253
      *        started/initialized, or false otherwise
@@ -245,15 +258,62 @@ public void run() {
245 258
      * @see #shutdown()
246 259
      */
247 260
     public void start( boolean validateRepositoryConfigs ) {
  261
+        start(validateRepositoryConfigs, -1, TimeUnit.SECONDS);
  262
+    }
  263
+
  264
+    /**
  265
+     * Start this engine to make it available for use, and optionally start each of the repositories in the configuration. Any
  266
+     * errors starting the repositories will be logged as problems.
  267
+     * <p>
  268
+     * This method starts each repository in parallel, and returns after the supplied timeout or after all repositories have been
  269
+     * started (or failed startup), whichever comes first.
  270
+     * </p>
  271
+     * 
  272
+     * @param validateRepositoryConfigs true if the configurations of each repository should be validated and each repository
  273
+     *        started/initialized, or false otherwise
  274
+     * @param timeout the maximum time to wait; can be 0 or a positive number, but use a negative number to wait indefinitely
  275
+     *        until all repositories are started (or failed)
  276
+     * @param timeoutUnit the time unit of the {@code timeout} argument; may not be null, but ignored if <code>timeout</code> is
  277
+     *        negative
  278
+     * @throws IllegalStateException if this method is called when already shut down.
  279
+     * @throws JcrConfigurationException if there is an error in the configuration or any of the services that prevents proper
  280
+     *         startup
  281
+     * @see #start()
  282
+     * @see #shutdown()
  283
+     */
  284
+    public void start( boolean validateRepositoryConfigs,
  285
+                       long timeout,
  286
+                       TimeUnit timeoutUnit ) {
248 287
         start();
249 288
         if (validateRepositoryConfigs) {
250  
-            for (String repositoryName : getRepositoryNames()) {
251  
-                try {
252  
-                    getRepository(repositoryName);
253  
-                } catch (Throwable t) {
254  
-                    // Record this in the problems ...
255  
-                    this.problems.addError(t, JcrI18n.errorStartingRepositoryCheckConfiguration, repositoryName, t.getMessage());
  289
+            Set<String> repositoryNames = getRepositoryNames();
  290
+            if (repositoryNames.isEmpty()) return;
  291
+
  292
+            final CountDownLatch latch = new CountDownLatch(repositoryNames.size());
  293
+
  294
+            try {
  295
+                repositoriesLock.lock();
  296
+                // Put in a holder with a future for each repository
  297
+                // (this should proceed quickly, as nothing waits for the initialization) ...
  298
+                for (final String repositoryName : repositoryNames) {
  299
+                    RepositoryInitializer initializer = new RepositoryInitializer(repositoryName, latch);
  300
+                    Future<JcrRepository> future = repositoryStarterService.submit(initializer);
  301
+                    JcrRepositoryHolder holder = new JcrRepositoryHolder(repositoryName, future);
  302
+                    this.repositories.put(repositoryName, holder);
256 303
                 }
  304
+            } finally {
  305
+                repositoriesLock.unlock();
  306
+            }
  307
+
  308
+            // Now wait for the all the startups to complete ...
  309
+            try {
  310
+                if (timeout < 0L) {
  311
+                    latch.await();
  312
+                } else {
  313
+                    latch.await(timeout, timeoutUnit);
  314
+                }
  315
+            } catch (InterruptedException e) {
  316
+                this.problems.addError(e, JcrI18n.startingAllRepositoriesWasInterrupted, e.getMessage());
257 317
             }
258 318
         }
259 319
     }
@@ -290,23 +350,30 @@ public final JcrRepository getRepository( String repositoryName ) throws Reposit
290 350
         CheckArg.isNotEmpty(repositoryName, "repositoryName");
291 351
         checkRunning();
292 352
 
  353
+        JcrRepositoryHolder holder = null;
293 354
         try {
294 355
             repositoriesLock.lock();
295  
-            JcrRepository repository = repositories.get(repositoryName);
296  
-            if (repository == null) {
297  
-                try {
298  
-                    repository = doCreateJcrRepository(repositoryName);
299  
-                } catch (PathNotFoundException e) {
300  
-                    // The repository name is not a valid repository ...
301  
-                    String msg = JcrI18n.repositoryDoesNotExist.text(repositoryName);
302  
-                    throw new RepositoryException(msg);
303  
-                }
304  
-                repositories.put(repositoryName, repository);
  356
+            holder = repositories.get(repositoryName);
  357
+            if (holder != null) {
  358
+                // The repository was already placed in the map and thus initialization has been started
  359
+                // and may be finished. But this call will block until the repository has completed initialization...
  360
+                return holder.getRepository();
305 361
             }
306  
-            return repository;
  362
+            if (!getRepositoryNames().contains(repositoryName)) {
  363
+                // The repository name is not a valid repository ...
  364
+                String msg = JcrI18n.repositoryDoesNotExist.text(repositoryName);
  365
+                throw new RepositoryException(msg);
  366
+            }
  367
+            // Now create the initializer and holder ...
  368
+            RepositoryInitializer initializer = new RepositoryInitializer(repositoryName);
  369
+            Future<JcrRepository> future = repositoryStarterService.submit(initializer);
  370
+            holder = new JcrRepositoryHolder(repositoryName, future);
  371
+            repositories.put(repositoryName, holder);
307 372
         } finally {
308 373
             repositoriesLock.unlock();
309 374
         }
  375
+        JcrRepository repo = holder.getRepository();
  376
+        return repo;
310 377
     }
311 378
 
312 379
     /**
@@ -625,8 +692,136 @@ public void shutdownAndAwaitTermination( long timeout,
625 692
                                              TimeUnit unit ) throws InterruptedException {
626 693
         shutdown();
627 694
         awaitTermination(timeout, unit);
628  
-        for (JcrRepository repository : repositories.values()) {
  695
+        for (JcrRepositoryHolder repository : repositories.values()) {
  696
+            if (repository != null) repository.terminateAllSessions();
  697
+        }
  698
+    }
  699
+
  700
+    protected Logger getLogger() {
  701
+        return log;
  702
+    }
  703
+
  704
+    protected Problems problems() {
  705
+        return problems;
  706
+    }
  707
+
  708
+    protected class JcrRepositoryHolder {
  709
+        private final String repositoryName;
  710
+        private JcrRepository repository;
  711
+        private Future<JcrRepository> future;
  712
+        private Throwable error;
  713
+
  714
+        protected JcrRepositoryHolder( String repositoryName,
  715
+                                       Future<JcrRepository> future ) {
  716
+            this.repositoryName = repositoryName;
  717
+            this.future = future;
  718
+            assert this.future != null;
  719
+        }
  720
+
  721
+        public String getName() {
  722
+            return repositoryName;
  723
+        }
  724
+
  725
+        public synchronized JcrRepository getRepository() throws RepositoryException {
  726
+            if (repository == null) {
  727
+                if (future != null) {
  728
+                    try {
  729
+                        // Otherwise it is still initializing, so wait for it ...
  730
+                        this.repository = future.get();
  731
+                    } catch (Throwable e) {
  732
+                        error = e.getCause();
  733
+                        String msg = JcrI18n.errorStartingRepositoryCheckConfiguration.text(repositoryName, error.getMessage());
  734
+                        throw new RepositoryException(msg, error);
  735
+                    } finally {
  736
+                        this.future = null;
  737
+                    }
  738
+                }
  739
+                if (repository == null) {
  740
+                    // There is no future, but the repository could not be initialized correctly ...
  741
+                    String msg = JcrI18n.errorStartingRepositoryCheckConfiguration.text(repositoryName, error.getMessage());
  742
+                    throw new RepositoryException(msg, error);
  743
+                }
  744
+            }
  745
+            return this.repository;
  746
+        }
  747
+
  748
+        public synchronized void close() {
  749
+            if (future != null) {
  750
+                try {
  751
+                    future.cancel(false);
  752
+                } finally {
  753
+                    future = null;
  754
+                }
  755
+            }
  756
+            if (repository != null) {
  757
+                try {
  758
+                    repository.close();
  759
+                } finally {
  760
+                    repository = null;
  761
+                }
  762
+            }
  763
+        }
  764
+
  765
+        public synchronized void terminateAllSessions() {
  766
+            // only need to do this on repositories that have been used; i.e., not including just-initialized repositories
629 767
             if (repository != null) repository.terminateAllSessions();
630 768
         }
  769
+
  770
+        public synchronized void cleanUpLocks() {
  771
+            // only need to do this on repositories that have been used; i.e., not including just-initialized repositories
  772
+            if (repository != null) {
  773
+                try {
  774
+                    repository.getRepositoryLockManager().cleanUpLocks();
  775
+                } catch (Throwable t) {
  776
+                    getLogger().error(t, JcrI18n.errorCleaningUpLocks, repository.getRepositorySourceName());
  777
+                }
  778
+            }
  779
+        }
  780
+
  781
+        /**
  782
+         * {@inheritDoc}
  783
+         * 
  784
+         * @see java.lang.Object#toString()
  785
+         */
  786
+        @Override
  787
+        public String toString() {
  788
+            return repositoryName;
  789
+        }
631 790
     }
  791
+
  792
+    protected class RepositoryInitializer implements Callable<JcrRepository> {
  793
+        private final String repositoryName;
  794
+        private final CountDownLatch latch;
  795
+
  796
+        protected RepositoryInitializer( String repositoryName ) {
  797
+            this(repositoryName, null);
  798
+        }
  799
+
  800
+        protected RepositoryInitializer( String repositoryName,
  801
+                                         CountDownLatch latch ) {
  802
+            this.repositoryName = repositoryName;
  803
+            this.latch = latch;
  804
+        }
  805
+
  806
+        public JcrRepository call() throws Exception {
  807
+            JcrRepository repository = null;
  808
+            try {
  809
+                repository = doCreateJcrRepository(repositoryName);
  810
+                getLogger().info(JcrI18n.completedStartingRepository, repositoryName);
  811
+                return repository;
  812
+            } catch (RepositoryException t) {
  813
+                // Record this in the problems ...
  814
+                problems().addError(t, JcrI18n.errorStartingRepositoryCheckConfiguration, repositoryName, t.getMessage());
  815
+                throw t;
  816
+            } catch (Throwable t) {
  817
+                // Record this in the problems ...
  818
+                problems().addError(t, JcrI18n.errorStartingRepositoryCheckConfiguration, repositoryName, t.getMessage());
  819
+                String msg = JcrI18n.errorStartingRepositoryCheckConfiguration.text(repositoryName, t.getMessage());
  820
+                throw new RepositoryException(msg, t);
  821
+            } finally {
  822
+                if (latch != null) latch.countDown();
  823
+            }
  824
+        }
  825
+    }
  826
+
632 827
 }
2  modeshape-jcr/src/main/java/org/modeshape/jcr/JcrI18n.java
@@ -61,6 +61,8 @@
61 61
     public static I18n failedToReadPropertyFromManifest;
62 62
     public static I18n errorLoadingNodeTypeDefintions;
63 63
     public static I18n errorStartingRepositoryCheckConfiguration;
  64
+    public static I18n completedStartingRepository;
  65
+    public static I18n startingAllRepositoriesWasInterrupted;
64 66
     public static I18n unableToFindNodeTypeDefinitionsOnClasspathOrFileOrUrl;
65 67
     public static I18n unableToFindResourceOnClasspathOrFileOrUrl;
66 68
     public static I18n unableToImportInitialContent;
2  modeshape-jcr/src/main/java/org/modeshape/jcr/RepositoryQueryManager.java
@@ -316,7 +316,7 @@ public void notify( Changes changes ) {
316 316
                      * @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable)
317 317
                      */
318 318
                     public Thread newThread( Runnable r ) {
319  
-                        Thread thread = new Thread("modeshape-indexing");
  319
+                        Thread thread = new Thread(r, "modeshape-indexing");
320 320
                         thread.setPriority(Thread.NORM_PRIORITY + 3);
321 321
                         return thread;
322 322
                     }
2  modeshape-jcr/src/main/resources/org/modeshape/jcr/JcrI18n.properties
@@ -51,6 +51,8 @@ failedToReadPropertiesFromManifest = Error reading manifest properties: {0}
51 51
 failedToReadPropertyFromManifest = "{0}" property not found in manifest
52 52
 errorLoadingNodeTypeDefintions = Error loading CND file "{0}": {1}
53 53
 errorStartingRepositoryCheckConfiguration = Error starting the "{0}" repository (check the configuration): {1}
  54
+startingAllRepositoriesWasInterrupted = The engine was interrupted while starting each repository, and will be left in an unknown state: {0}
  55
+completedStartingRepository = Completed starting the "{0}" repository
54 56
 unableToFindNodeTypeDefinitionsOnClasspathOrFileOrUrl = Unable to find the node type definition file "{0}" on the classpath, as a relative or absolute file, or resolve as a URL
55 57
 unableToFindResourceOnClasspathOrFileOrUrl = Unable to find "{0}" on the classpath, as a relative or absolute file, or resolve as a URL
56 58
 unableToImportInitialContent = Unable to import initial content for repository "{0}" from "{1}". Check that this file exists on the file system or classpath.
64  modeshape-jcr/src/test/java/org/modeshape/jcr/JcrEngineTest.java
@@ -27,6 +27,7 @@
27 27
 import static org.hamcrest.core.IsNull.notNullValue;
28 28
 import static org.junit.Assert.assertThat;
29 29
 import java.net.URL;
  30
+import java.util.concurrent.TimeUnit;
30 31
 import javax.jcr.RepositoryException;
31 32
 import javax.jcr.Session;
32 33
 import javax.jcr.Workspace;
@@ -246,6 +247,44 @@ public void shouldAllowCreatingWorkspaces() throws Exception {
246 247
         jcrSession.logout();
247 248
     }
248 249
 
  250
+    @FixFor( "MODE-1180" )
  251
+    @Test
  252
+    public void shouldCreateRepositoriesUponStartup() throws Exception {
  253
+        configuration = new JcrConfiguration();
  254
+        configuration.repositorySource("car-source")
  255
+                     .usingClass(InMemoryRepositorySource.class)
  256
+                     .setDescription("The automobile content")
  257
+                     .setProperty("defaultWorkspaceName", "default");
  258
+        configuration.repository("cars")
  259
+                     .setSource("car-source")
  260
+                     .registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
  261
+                     .addNodeTypes(resourceUrl("cars.cnd"))
  262
+                     .setInitialContent("src/test/resources/initialWorkspaceContent.xml", "default")
  263
+                     .setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
  264
+        configuration.repositorySource("product-source")
  265
+                     .usingClass(InMemoryRepositorySource.class)
  266
+                     .setDescription("The products content")
  267
+                     .setProperty("defaultWorkspaceName", "default");
  268
+        configuration.repository("products")
  269
+                     .setSource("product-source")
  270
+                     .registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
  271
+                     .addNodeTypes(resourceUrl("cars.cnd"))
  272
+                     .setInitialContent("src/test/resources/initialWorkspaceContent.xml", "default")
  273
+                     .setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
  274
+        engine = configuration.build();
  275
+        assertThat(engine.getProblems().hasErrors(), is(false));
  276
+        engine.start(true, 1L, TimeUnit.NANOSECONDS);
  277
+        repository = engine.getRepository("products");
  278
+        repository = engine.getRepository("cars");
  279
+        session = repository.login();
  280
+
  281
+        assertNodeType("car:Car", false, false, true, false, null, 0, 12, "nt:unstructured", "mix:created");
  282
+
  283
+        // Check that the content is there...
  284
+        assertThat(session.getRootNode().hasNode("Cars"), is(true));
  285
+        assertThat(session.getNode("/Cars"), is(notNullValue()));
  286
+    }
  287
+
249 288
     @FixFor( "MODE-1119" )
250 289
     @Test
251 290
     public void shouldCreateRepositoryConfiguredWithNoInitialContent() throws Exception {
@@ -361,6 +400,31 @@ public void shouldRecordProblemWhenStartingRepositoriesConfiguredWithValidInitia
361 400
     }
362 401
 
363 402
     @FixFor( "MODE-1119" )
  403
+    @Test( expected = RepositoryException.class )
  404
+    public void shouldRecordProblemWhenStartingRepositoriesConfiguredWithValidInitialContentPathButEmptyFileAndFailWhenGettingRepository()
  405
+        throws Exception {
  406
+        configuration = new JcrConfiguration();
  407
+        configuration.repositorySource("car-source")
  408
+                     .usingClass(InMemoryRepositorySource.class)
  409
+                     .setDescription("The automobile content")
  410
+                     .setProperty("defaultWorkspaceName", "default");
  411
+        configuration.repository("cars")
  412
+                     .setSource("car-source")
  413
+                     .registerNamespace("car", "http://www.modeshape.org/examples/cars/1.0")
  414
+                     .addNodeTypes(resourceUrl("cars.cnd"))
  415
+                     .setInitialContent("src/test/resources/emptyFile.xml", "default")
  416
+                     .setOption(Option.ANONYMOUS_USER_ROLES, ModeShapeRoles.ADMIN);
  417
+        engine = configuration.build();
  418
+        assertThat(engine.getProblems().hasErrors(), is(false));
  419
+        engine.start(true);
  420
+        assertThat(engine.getProblems().hasErrors(), is(true));
  421
+        assertThat(engine.getProblems().size(), is(1)); // one error
  422
+        assertThat(engine.getProblems().iterator().next().getStatus(), is(Problem.Status.ERROR));
  423
+        // The following will fail ...
  424
+        engine.getRepository("cars");
  425
+    }
  426
+
  427
+    @FixFor( "MODE-1119" )
364 428
     @Test
365 429
     public void shouldRecordProblemWhenStartingRepositoriesConfiguredWithIncorrectInitialContentPath() throws Exception {
366 430
         configuration = new JcrConfiguration();
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.