### Eclipse Workspace Patch 1.0
#P main
Index: src/test/java/org/geoserver/catalog/impl/CatalogImplTest.java
===================================================================
--- src/test/java/org/geoserver/catalog/impl/CatalogImplTest.java	(revision 15491)
+++ src/test/java/org/geoserver/catalog/impl/CatalogImplTest.java	(working copy)
@@ -5,6 +5,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
 
 import junit.framework.TestCase;
 
@@ -28,6 +29,7 @@
 import org.geoserver.catalog.event.CatalogModifyEvent;
 import org.geoserver.catalog.event.CatalogPostModifyEvent;
 import org.geoserver.catalog.event.CatalogRemoveEvent;
+import org.geoserver.catalog.impl.CatalogThreadSafetyTest.AddAndRemoveStoreRunner;
 
 public class CatalogImplTest extends TestCase {
 
@@ -1265,7 +1267,45 @@
         assertEquals( 2, catalog.getStores(WMSStoreInfo.class).size() );
     }
     
+    private static final int GET_LAYER_BY_ID_WITH_CONCURRENT_ADD_TEST_COUNT = 500;
+    private static final int GET_LAYER_BY_ID_WITH_CONCURRENT_ADD_THREAD_COUNT = 10;
     
+    public void testGetLayerByIdWithConcurrentAdd() throws Exception {
+        addDataStore();
+        addNamespace();
+        
+        LayerInfo layer = catalog.getFactory().createLayer();
+        layer.setResource(ft);
+        layer.setName("LAYER");
+        catalog.add(layer);
+        String id = layer.getId();
+        
+        CountDownLatch ready = new CountDownLatch(GET_LAYER_BY_ID_WITH_CONCURRENT_ADD_THREAD_COUNT + 1);
+        CountDownLatch done = new CountDownLatch(GET_LAYER_BY_ID_WITH_CONCURRENT_ADD_THREAD_COUNT);
+        
+        List<RunnerBase> runners = new ArrayList<RunnerBase>();
+        for (int i = 0; i < GET_LAYER_BY_ID_WITH_CONCURRENT_ADD_THREAD_COUNT; i++) {
+            RunnerBase runner = new LayerAddRunner(ready, done, i);
+            new Thread(runner).start();
+            runners.add(runner);
+        }
+        
+        // note that test thread is ready
+        ready.countDown();
+        // wait for all threads to reach latch in order to maximize likelihood of contention
+        ready.await();
+        
+        for (int i = 0; i < GET_LAYER_BY_ID_WITH_CONCURRENT_ADD_TEST_COUNT ; i++) {
+            catalog.getLayer(id);
+        }
+
+        // make sure worker threads are done
+        done.await();
+        
+        RunnerBase.checkForRunnerExceptions(runners);
+    }
+    
+    
     static class TestListener implements CatalogListener {
 
         public List<CatalogAddEvent> added = new ArrayList();
@@ -1317,4 +1357,26 @@
         }
         
     }
+    
+    class LayerAddRunner extends RunnerBase {
+
+        private int idx;
+        
+        protected LayerAddRunner(CountDownLatch ready, CountDownLatch done, int idx) {
+            super(ready, done);
+            this.idx = idx;
+        }
+
+        protected void runInternal() throws Exception {
+            CatalogFactory factory = catalog.getFactory();
+            for (int i = 0; i < GET_LAYER_BY_ID_WITH_CONCURRENT_ADD_TEST_COUNT; i++) {
+                LayerInfo layer = factory.createLayer();
+                layer.setResource(ft);
+                layer.setName("LAYER-" + i + "-" + idx);
+                catalog.add(layer);
+            }
+        }
+        
+    };
+
 }
Index: src/test/java/org/geoserver/catalog/impl/RunnerBase.java
===================================================================
--- src/test/java/org/geoserver/catalog/impl/RunnerBase.java	(revision 0)
+++ src/test/java/org/geoserver/catalog/impl/RunnerBase.java	(revision 0)
@@ -0,0 +1,69 @@
+package org.geoserver.catalog.impl;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+public abstract class RunnerBase implements Runnable {
+
+    private CountDownLatch ready;
+    private CountDownLatch done;
+    private Exception problem;
+
+    protected RunnerBase(CountDownLatch ready, CountDownLatch done) {
+        this.ready = ready;
+        this.done = done;
+    }
+
+    public void run() {
+        boolean readied = false;
+        try {
+            doBeforeReady();
+            if (ready != null) {
+                ready.countDown();
+                readied = true;
+                ready.await();
+            }
+            runInternal();
+        } catch (Exception e) {
+            if (ready != null && !readied) {
+                try {
+                    ready.countDown();
+                } catch (Exception e2) {
+                    e2.printStackTrace();
+                }
+            }
+            setProblem(e);
+        }
+        if (done != null) {
+            done.countDown();
+        }
+    }
+
+    protected abstract void runInternal() throws Exception;
+
+    protected void doBeforeReady() {
+        ;
+    }
+
+    private synchronized void setProblem(Exception problem) {
+        this.problem = problem;
+    }
+
+    public synchronized Exception getProblem() {
+        return problem;
+    }
+
+    public static void checkForRunnerException(RunnerBase runner) throws Exception {
+        Exception problem = runner.getProblem();
+        if (problem != null) {
+            throw problem;
+        }
+    }
+
+    public static void checkForRunnerExceptions(List<? extends RunnerBase> runners) throws Exception {
+        for (RunnerBase runner : runners) {
+            checkForRunnerException(runner);
+        }
+    }
+
+}
