brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rich...@apache.org
Subject [07/10] addresses review comments, cleans up quite a few things. most importantly:
Date Tue, 10 Jun 2014 00:15:28 GMT
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
index 09ff37e..5762268 100644
--- a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
+++ b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
@@ -4,6 +4,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 
 import java.io.IOException;
+import java.net.URI;
 import java.util.concurrent.Callable;
 import java.util.concurrent.TimeUnit;
 
@@ -170,25 +171,28 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
         switch (startMode) {
         case AUTO:
             // don't care; let's start and see if we promote ourselves
-            doPollTask();
+            publishAndCheck(true);
             if (nodeState == ManagementNodeState.STANDBY) {
-                LOG.info("Management node (with high availability mode 'auto') started as standby");
+                String masterNodeId = getManagementPlaneSyncState().getMasterNodeId();
+                ManagementNodeSyncRecord masterNodeDetails = getManagementPlaneSyncState().getManagementNodes().get(masterNodeId);
+                LOG.info("Management node started as HA STANDBY autodetected, master is "+masterNodeId+
+                    (masterNodeDetails==null || masterNodeDetails.getUri()==null ? " (no further info)" : " at "+masterNodeDetails.getUri()));
             } else {
-                LOG.info("Management node (with high availability mode 'auto') started as master");
+                LOG.info("Management node started as HA MASTER autodetected");
             }
             break;
         case MASTER:
             if (existingMaster == null) {
                 promoteToMaster();
-                LOG.info("Management node (with high availability mode 'master') started as master");
+                LOG.info("Management node started as HA MASTER explicitly");
             } else {
                 throw new IllegalStateException("Master already exists; cannot start as master ("+existingMaster.toVerboseString()+")");
             }
             break;
         case STANDBY:
             if (existingMaster != null) {
-                doPollTask();
-                LOG.info("Management node (with high availability mode 'standby') started; status "+nodeState);
+                publishAndCheck(true);
+                LOG.info("Management node started as HA STANDBY explicitly, status "+nodeState);
             } else {
                 throw new IllegalStateException("No existing master; cannot start as standby");
             }
@@ -233,7 +237,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
         final Runnable job = new Runnable() {
             @Override public void run() {
                 try {
-                    doPollTask();
+                    publishAndCheck(false);
                 } catch (Exception e) {
                     if (running) {
                         LOG.error("Problem in HA-poller: "+e, e);
@@ -256,9 +260,10 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
         pollingTask = managementContext.getExecutionManager().submit(task);
     }
     
-    protected synchronized void doPollTask() {
+    /** invoked manually when initializing, and periodically thereafter */
+    protected synchronized void publishAndCheck(boolean initializing) {
         publishHealth();
-        checkMaster();
+        checkMaster(initializing);
     }
     
     protected synchronized void publishHealth() {
@@ -344,7 +349,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
      * Looks up the state of all nodes in the management plane, and checks if the master is still ok.
      * If it's not then determines which node should be promoted to master. If it is ourself, then promotes.
      */
-    protected void checkMaster() {
+    protected void checkMaster(boolean initializin) {
         long now = currentTimeMillis();
         ManagementPlaneSyncRecord memento = loadManagementPlaneSyncRecord(false);
         
@@ -377,16 +382,22 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
         // Need to choose a new master
         ManagementNodeSyncRecord newMasterRecord = masterChooser.choose(memento, heartbeatTimeout, ownNodeId, now);
         String newMasterNodeId = (newMasterRecord == null) ? null : newMasterRecord.getNodeId();
+        URI newMasterNodeUri = (newMasterRecord == null) ? null : newMasterRecord.getUri();
         boolean newMasterIsSelf = ownNodeId.equals(newMasterNodeId);
         
-        LOG.warn("Management node master-promotion required: newMaster={}; oldMaster={}; plane={}, self={}; heartbeatTimeout={}", 
-                new Object[] {
-                        (newMasterNodeId == null ? "<none>" : newMasterNodeId),
-                        (masterNodeMemento == null ? masterNodeId+" (no memento)": masterNodeMemento.toVerboseString()),
-                        memento,
-                        ownNodeMemento.toVerboseString(), 
-                        heartbeatTimeout
-                });
+        LOG.debug("Management node master-promotion required: newMaster={}; oldMaster={}; plane={}, self={}; heartbeatTimeout={}", 
+            new Object[] {
+            (newMasterRecord == null ? "<none>" : newMasterRecord.toVerboseString()),
+            (masterNodeMemento == null ? masterNodeId+" (no memento)": masterNodeMemento.toVerboseString()),
+            memento,
+            ownNodeMemento.toVerboseString(), 
+            heartbeatTimeout
+        });
+        if (!initializin) {
+            LOG.warn("HA subsystem detected change of master from "+masterNodeId+" to "
+                + (newMasterNodeId == null ? "<unknown>" : 
+                    newMasterNodeId + (newMasterNodeUri!=null ? " "+newMasterNodeUri : "")));
+        }
 
         // New master is ourself: promote
         if (newMasterIsSelf) {
@@ -413,7 +424,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
         try {
             managementContext.getRebindManager().rebind();
         } catch (Exception e) {
-            LOG.info("Problem during rebind when promoting node to master; demoting to failed and rethrowing): "+e);
+            LOG.info("Problem during rebind when promoting node to master; demoting to failed and rethrowing: "+e);
             nodeState = ManagementNodeState.FAILED;
             publishDemotionFromMasterOnFailure();
             throw Exceptions.propagate(e);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java b/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java
index cfccd70..8969d03 100644
--- a/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/management/ha/ManagementPlaneSyncRecordPersisterToObjectStore.java
@@ -13,8 +13,9 @@ import org.slf4j.LoggerFactory;
 
 import brooklyn.entity.rebind.persister.MementoSerializer;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
-import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessor;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessorWithLock;
 import brooklyn.entity.rebind.persister.RetryingMementoSerializer;
+import brooklyn.entity.rebind.persister.StoreObjectAccessorLocking;
 import brooklyn.entity.rebind.persister.XmlMementoSerializer;
 import brooklyn.entity.rebind.plane.dto.ManagementPlaneSyncRecordImpl;
 import brooklyn.management.ManagementContext;
@@ -64,10 +65,10 @@ public class ManagementPlaneSyncRecordPersisterToObjectStore implements Manageme
     public static final String NODES_SUB_PATH = "nodes";
 
     // TODO Leak if we go through lots of managers; but tiny!
-    private final ConcurrentMap<String, StoreObjectAccessor> nodeWriters = Maps.newConcurrentMap();
+    private final ConcurrentMap<String, StoreObjectAccessorWithLock> nodeWriters = Maps.newConcurrentMap();
 
-    private final StoreObjectAccessor masterWriter;
-    private final StoreObjectAccessor changeLogWriter;
+    private final StoreObjectAccessorWithLock masterWriter;
+    private final StoreObjectAccessorWithLock changeLogWriter;
 
     private final MementoSerializer<Object> serializer;
 
@@ -91,25 +92,25 @@ public class ManagementPlaneSyncRecordPersisterToObjectStore implements Manageme
 
         objectStore.createSubPath(NODES_SUB_PATH);
 
-        masterWriter = objectStore.newAccessor("/master");
-        changeLogWriter = objectStore.newAccessor("/change.log");
+        masterWriter = new StoreObjectAccessorLocking(objectStore.newAccessor("/master"));
+        changeLogWriter = new StoreObjectAccessorLocking(objectStore.newAccessor("/change.log"));
 
-        LOG.info("ManagementPlaneMemento-persister will use store "+objectStore);
+        LOG.debug("ManagementPlaneMemento-persister will use store "+objectStore);
     }
 
     @Override
     public void stop() {
         running = false;
         try {
-            for (StoreObjectAccessor writer : nodeWriters.values()) {
+            for (StoreObjectAccessorWithLock writer : nodeWriters.values()) {
                 try {
-                    writer.waitForWriteCompleted(SHUTDOWN_TIMEOUT);
+                    writer.waitForCurrentWrites(SHUTDOWN_TIMEOUT);
                 } catch (TimeoutException e) {
                     LOG.warn("Timeout during shutdown, waiting for write of "+writer+"; continuing");
                 }
             }
             try {
-                masterWriter.waitForWriteCompleted(SHUTDOWN_TIMEOUT);
+                masterWriter.waitForCurrentWrites(SHUTDOWN_TIMEOUT);
             } catch (TimeoutException e) {
                 LOG.warn("Timeout during shutdown, waiting for write of "+masterWriter+"; continuing");
             }
@@ -133,7 +134,7 @@ public class ManagementPlaneSyncRecordPersisterToObjectStore implements Manageme
 
         // Be careful about order: if the master-file says nodeX then nodeX's file must have an up-to-date timestamp.
         // Therefore read master file first, followed by the other node-files.
-        String masterNodeId = masterWriter.exists() ? masterWriter.read() : null;
+        String masterNodeId = masterWriter.get();
         if (masterNodeId == null) {
             LOG.warn("No entity-memento deserialized from file "+masterWriter+"; ignoring and continuing");
         } else {
@@ -141,22 +142,25 @@ public class ManagementPlaneSyncRecordPersisterToObjectStore implements Manageme
         }
 
         // Load node-files
-        List<String> nodeContents = objectStore.listContentsWithSubPath(NODES_SUB_PATH);
-        LOG.info("Loading nodes from {}; {} nodes.",
-                new Object[]{objectStore.getSummaryName(), nodeContents.size()});
-
-        for (String nodeContent : nodeContents) {
-            PersistenceObjectStore.StoreObjectAccessor objectAccessor = objectStore.newAccessor(nodeContent);
-            ManagementNodeSyncRecord memento = (ManagementNodeSyncRecord) serializer.fromString(objectAccessor.read());
+        List<String> nodeFiles = objectStore.listContentsWithSubPath(NODES_SUB_PATH);
+        LOG.trace("Loading nodes from {}; {} nodes.",
+                new Object[]{objectStore.getSummaryName(), nodeFiles.size()});
+
+        for (String nodeFile : nodeFiles) {
+            PersistenceObjectStore.StoreObjectAccessor objectAccessor = objectStore.newAccessor(nodeFile);
+            String nodeContents = objectAccessor.get();
+            ManagementNodeSyncRecord memento = nodeContents==null ? null : (ManagementNodeSyncRecord) serializer.fromString(nodeContents);
             if (memento == null) {
-                LOG.warn("No manager-memento deserialized from " + nodeContent + " (possibly just stopped?); ignoring" +
+                LOG.warn("No manager-memento deserialized from " + nodeFile + " (possibly just stopped?); ignoring" +
                         " and continuing");
             } else {
                 builder.node(memento);
             }
         }
 
-        if (LOG.isDebugEnabled()) LOG.debug("Loaded management-plane memento; took {}", Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)));
+        if (LOG.isDebugEnabled()) LOG.trace("Loaded management-plane memento; {} nodes, took {}",
+            nodeFiles.size(),
+            Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)));
         return builder.build();
     }
     
@@ -189,15 +193,15 @@ public class ManagementPlaneSyncRecordPersisterToObjectStore implements Manageme
     }
 
     private void persistMaster(String nodeId) {
-        masterWriter.writeAsync(nodeId);
+        masterWriter.put(nodeId);
         try {
-            masterWriter.waitForWriteCompleted(SYNC_WRITE_TIMEOUT);
+            masterWriter.waitForCurrentWrites(SYNC_WRITE_TIMEOUT);
         } catch (Exception e) {
             throw Exceptions.propagate(e);
         }
         changeLogWriter.append(Time.makeDateString() + ": set master to " + nodeId + "\n");
         try {
-            changeLogWriter.waitForWriteCompleted(SYNC_WRITE_TIMEOUT);
+            changeLogWriter.waitForCurrentWrites(SYNC_WRITE_TIMEOUT);
         } catch (Exception e) {
             throw Exceptions.propagate(e);
         }
@@ -206,18 +210,18 @@ public class ManagementPlaneSyncRecordPersisterToObjectStore implements Manageme
     @Override
     @VisibleForTesting
     public void waitForWritesCompleted(Duration timeout) throws InterruptedException, TimeoutException {
-        for (StoreObjectAccessor writer : nodeWriters.values()) {
-            writer.waitForWriteCompleted(timeout);
+        for (StoreObjectAccessorWithLock writer : nodeWriters.values()) {
+            writer.waitForCurrentWrites(timeout);
         }
-        masterWriter.waitForWriteCompleted(timeout);
+        masterWriter.waitForCurrentWrites(timeout);
     }
 
     private void persist(ManagementNodeSyncRecord node) {
-        StoreObjectAccessor writer = getOrCreateNodeWriter(node.getNodeId());
+        StoreObjectAccessorWithLock writer = getOrCreateNodeWriter(node.getNodeId());
         boolean fileExists = writer.exists();
-        writer.writeAsync(serializer.toString(node));
+        writer.put(serializer.toString(node));
         try {
-            writer.waitForWriteCompleted(SYNC_WRITE_TIMEOUT);
+            writer.waitForCurrentWrites(SYNC_WRITE_TIMEOUT);
         } catch (Exception e) {
             throw Exceptions.propagate(e);
         }
@@ -230,14 +234,15 @@ public class ManagementPlaneSyncRecordPersisterToObjectStore implements Manageme
     }
     
     private void deleteNode(String nodeId) {
-        getOrCreateNodeWriter(nodeId).deleteAsync();
+        getOrCreateNodeWriter(nodeId).delete();
         changeLogWriter.append(Time.makeDateString()+": deleted node "+nodeId+"\n");
     }
 
-    private StoreObjectAccessor getOrCreateNodeWriter(String nodeId) {
-        PersistenceObjectStore.StoreObjectAccessor writer = nodeWriters.get(nodeId);
+    private StoreObjectAccessorWithLock getOrCreateNodeWriter(String nodeId) {
+        PersistenceObjectStore.StoreObjectAccessorWithLock writer = nodeWriters.get(nodeId);
         if (writer == null) {
-            nodeWriters.putIfAbsent(nodeId, objectStore.newAccessor(NODES_SUB_PATH+"/"+nodeId));
+            nodeWriters.putIfAbsent(nodeId, 
+                new StoreObjectAccessorLocking(objectStore.newAccessor(NODES_SUB_PATH+"/"+nodeId)));
             writer = nodeWriters.get(nodeId);
         }
         return writer;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/main/resources/brooklyn-catalog-empty.xml
----------------------------------------------------------------------
diff --git a/core/src/main/resources/brooklyn-catalog-empty.xml b/core/src/main/resources/brooklyn-catalog-empty.xml
new file mode 100644
index 0000000..d2b0dcb
--- /dev/null
+++ b/core/src/main/resources/brooklyn-catalog-empty.xml
@@ -0,0 +1 @@
+<catalog/>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/entity/group/DynamicMultiGroupTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/group/DynamicMultiGroupTest.java b/core/src/test/java/brooklyn/entity/group/DynamicMultiGroupTest.java
index b217001..4be4d00 100644
--- a/core/src/test/java/brooklyn/entity/group/DynamicMultiGroupTest.java
+++ b/core/src/test/java/brooklyn/entity/group/DynamicMultiGroupTest.java
@@ -24,6 +24,7 @@ import brooklyn.event.SensorEventListener;
 import brooklyn.event.basic.Sensors;
 import brooklyn.location.basic.SimulatedLocation;
 import brooklyn.test.Asserts;
+import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.test.entity.TestApplication;
 import brooklyn.test.entity.TestEntity;
 
@@ -39,7 +40,7 @@ public class DynamicMultiGroupTest {
 
     @BeforeMethod(alwaysRun = true)
     public void setUp() {
-        app = ApplicationBuilder.newManagedApp(TestApplication.class);
+        app = ApplicationBuilder.newManagedApp(TestApplication.class, new LocalManagementContextForTests());
         app.start(ImmutableList.of(new SimulatedLocation()));
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java b/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
index 28d68fa..4df0995 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindTestUtils.java
@@ -19,6 +19,7 @@ import brooklyn.entity.rebind.Dumpers.Pointer;
 import brooklyn.entity.rebind.dto.MementosGenerators;
 import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
 import brooklyn.entity.rebind.persister.FileBasedObjectStore;
+import brooklyn.entity.rebind.persister.PersistMode;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
 import brooklyn.entity.trait.Identifiable;
 import brooklyn.location.Location;
@@ -148,7 +149,7 @@ public class RebindTestUtils {
                 unstarted = new LocalManagementContextForTests();
             }
             
-            objectStore.prepareForUse(unstarted, null);
+            objectStore.prepareForUse(unstarted, PersistMode.AUTO);
             BrooklynMementoPersisterToObjectStore newPersister = new BrooklynMementoPersisterToObjectStore(objectStore, classLoader);
             ((RebindManagerImpl) unstarted.getRebindManager()).setPeriodicPersistPeriod(persistPeriod);
             unstarted.getRebindManager().setPersister(newPersister);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java b/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java
index 3653ff3..9947e34 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java
@@ -92,7 +92,7 @@ public abstract class BrooklynMementoPersisterTestFixture {
     }
     
     @Test
-    public void testCheckPointAndLoadMementoUsingFileBasedObjectStore() throws IOException, TimeoutException, InterruptedException {
+    public void testCheckPointAndLoadMemento() throws IOException, TimeoutException, InterruptedException {
         BrooklynMemento reloadedMemento = loadMemento();
         
         assertNotNull(reloadedMemento);
@@ -103,8 +103,8 @@ public abstract class BrooklynMementoPersisterTestFixture {
 //        assertEquals(Iterables.getOnlyElement(reloadedMemento.getEnricherIds()), enricher.getId());
     }
 
-    @Test(dependsOnMethods = "testCheckPointAndLoadMementoUsingFileBasedObjectStore")
-    public void testDeltaAndLoadMementoUsingFileBasedObjectStore() throws TimeoutException, InterruptedException, IOException {
+    @Test
+    public void testDeleteAndLoadMemento() throws TimeoutException, InterruptedException, IOException {
         Entities.destroy(entity);
 
         BrooklynMemento reloadedMemento = loadMemento();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/entity/rebind/persister/FileBasedStoreObjectAccessorWriterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/FileBasedStoreObjectAccessorWriterTest.java b/core/src/test/java/brooklyn/entity/rebind/persister/FileBasedStoreObjectAccessorWriterTest.java
index 7843867..5d6f9a4 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/FileBasedStoreObjectAccessorWriterTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/FileBasedStoreObjectAccessorWriterTest.java
@@ -5,15 +5,15 @@ import java.io.IOException;
 
 import org.testng.annotations.Test;
 
-import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessor;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessorWithLock;
 
 @Test
 public class FileBasedStoreObjectAccessorWriterTest extends PersistenceStoreObjectAccessorWriterTestFixture {
 
-    protected StoreObjectAccessor newPersistenceStoreObjectAccessor() throws IOException {
+    protected StoreObjectAccessorWithLock newPersistenceStoreObjectAccessor() throws IOException {
         File file = File.createTempFile("objectAccessorWriterTest", ".txt");
         file.deleteOnExit();
-        return new FileBasedStoreObjectAccessor(file, executor, ".tmp");
+        return new StoreObjectAccessorLocking(new FileBasedStoreObjectAccessor(file, ".tmp"));
     }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/entity/rebind/persister/InMemoryObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/InMemoryObjectStore.java b/core/src/test/java/brooklyn/entity/rebind/persister/InMemoryObjectStore.java
new file mode 100644
index 0000000..aea0754
--- /dev/null
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/InMemoryObjectStore.java
@@ -0,0 +1,119 @@
+package brooklyn.entity.rebind.persister;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.management.ManagementContext;
+import brooklyn.util.collections.MutableList;
+import brooklyn.util.collections.MutableMap;
+
+import com.google.common.base.Objects;
+
+public class InMemoryObjectStore implements PersistenceObjectStore {
+
+    private static final Logger log = LoggerFactory.getLogger(InMemoryObjectStore.class);
+
+    Map<String,String> filesByName = MutableMap.<String,String>of();
+    boolean prepared = false;
+    
+    public InMemoryObjectStore() {
+        log.info("Using memory-based objectStore");
+    }
+    
+    @Override
+    public String getSummaryName() {
+        return "in-memory (test) persistence store";
+    }
+    
+    @Override
+    public void createSubPath(String subPath) {
+        if (!prepared) throw new IllegalStateException("prepare method not yet invoked: "+this);
+    }
+
+    @Override
+    public StoreObjectAccessor newAccessor(final String path) {
+        return new StoreObjectAccessorLocking(new SingleThreadedInMemoryStoreObjectAccessor(filesByName, path));
+    }
+    
+    public static class SingleThreadedInMemoryStoreObjectAccessor implements StoreObjectAccessor {
+        private final Map<String, String> map;
+        private final String key;
+
+        public SingleThreadedInMemoryStoreObjectAccessor(Map<String,String> map, String key) {
+            this.map = map;
+            this.key = key;
+        }
+        @Override
+        public String get() {
+            synchronized (map) {
+                return map.get(key);
+            }
+        }
+        @Override
+        public boolean exists() {
+            synchronized (map) {
+                return map.containsKey(key);
+            }
+        }
+        @Override
+        public void put(String val) {
+            synchronized (map) {
+                map.put(key, val);
+            }
+        }
+        @Override
+        public void append(String val) {
+            synchronized (map) {
+                String val2 = get();
+                if (val2==null) val2 = val;
+                else val2 = val2 + val;
+
+                map.put(key, val);
+            }
+        }
+        @Override
+        public void delete() {
+            synchronized (map) {
+                map.remove(key);
+            }
+        }
+    }
+
+    @Override
+    public List<String> listContentsWithSubPath(final String parentSubPath) {
+        synchronized (filesByName) {
+            List<String> result = MutableList.of();
+            for (String file: filesByName.keySet())
+                if (file.startsWith(parentSubPath))
+                    result.add(file);
+            return result;
+        }
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public String toString() {
+        return Objects.toStringHelper(this).add("size", filesByName.size()).toString();
+    }
+
+    @Override
+    public void prepareForUse(ManagementContext mgmt, @Nullable PersistMode persistMode) {
+        prepared = true;
+    }
+
+    @Override
+    public void deleteCompletely() {
+        synchronized (filesByName) {
+            filesByName.clear();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/entity/rebind/persister/InMemoryStoreObjectAccessorWriterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/InMemoryStoreObjectAccessorWriterTest.java b/core/src/test/java/brooklyn/entity/rebind/persister/InMemoryStoreObjectAccessorWriterTest.java
index c8899b3..86c8c91 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/InMemoryStoreObjectAccessorWriterTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/InMemoryStoreObjectAccessorWriterTest.java
@@ -4,13 +4,13 @@ import java.io.IOException;
 
 import org.testng.annotations.Test;
 
-import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessor;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessorWithLock;
 
 @Test
 public class InMemoryStoreObjectAccessorWriterTest extends PersistenceStoreObjectAccessorWriterTestFixture {
 
-    protected StoreObjectAccessor newPersistenceStoreObjectAccessor() throws IOException {
-        return new InMemoryObjectStore().newAccessor("foo");
+    protected StoreObjectAccessorWithLock newPersistenceStoreObjectAccessor() throws IOException {
+        return new StoreObjectAccessorLocking(new InMemoryObjectStore().newAccessor("foo"));
     }
     
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/entity/rebind/persister/ListeningObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/ListeningObjectStore.java b/core/src/test/java/brooklyn/entity/rebind/persister/ListeningObjectStore.java
index 82037de..37e3cbb 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/ListeningObjectStore.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/ListeningObjectStore.java
@@ -125,12 +125,6 @@ public class ListeningObjectStore implements PersistenceObjectStore {
         return result;
     }
 
-    public void backupContents(String sourceSubPath, String targetSubPathForBackups) {
-        listener.recordQueryOut("backing up "+sourceSubPath+" to "+targetSubPathForBackups, 
-            1+sourceSubPath.length()+targetSubPathForBackups.length());
-        delegate.backupContents(sourceSubPath, targetSubPathForBackups);
-    }
-
     public void close() {
         delegate.close();
     }
@@ -156,30 +150,24 @@ public class ListeningObjectStore implements PersistenceObjectStore {
         public boolean exists() {
             return delegate.exists();
         }
-        public void writeAsync(String val) {
+        public void put(String val) {
             listener.recordDataOut("writing "+path, val.length());
-            delegate.writeAsync(val);
+            delegate.put(val);
         }
         public void append(String s) {
             listener.recordDataOut("appending "+path, s.length());
             delegate.append(s);
         }
-        public void deleteAsync() {
+        public void delete() {
             listener.recordQueryOut("deleting "+path, path.length());
-            delegate.deleteAsync();
+            delegate.delete();
         }
-        public String read() {
+        public String get() {
             listener.recordQueryOut("requesting "+path, path.length());
-            String result = delegate.read();
+            String result = delegate.get();
             listener.recordDataIn("reading "+path, result.length());
             return result;
         }
-        public void waitForWriteCompleted(Duration timeout) throws InterruptedException, TimeoutException {
-            delegate.waitForWriteCompleted(timeout);
-        }
-        
-        
-        
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/entity/rebind/persister/PersistenceStoreObjectAccessorWriterTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/PersistenceStoreObjectAccessorWriterTestFixture.java b/core/src/test/java/brooklyn/entity/rebind/persister/PersistenceStoreObjectAccessorWriterTestFixture.java
index ad36ed1..06d98b5 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/PersistenceStoreObjectAccessorWriterTestFixture.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/PersistenceStoreObjectAccessorWriterTestFixture.java
@@ -10,7 +10,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
-import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessor;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessorWithLock;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.time.Duration;
 
@@ -22,7 +22,7 @@ public abstract class PersistenceStoreObjectAccessorWriterTestFixture {
     private static final Duration TIMEOUT = Duration.TEN_SECONDS;
     
     protected ListeningExecutorService executor;
-    protected StoreObjectAccessor accessor;
+    protected StoreObjectAccessorWithLock accessor;
     
     @BeforeMethod(alwaysRun=true)
     public void setUp() throws Exception {
@@ -30,34 +30,34 @@ public abstract class PersistenceStoreObjectAccessorWriterTestFixture {
         accessor = newPersistenceStoreObjectAccessor();
     }
 
-    protected abstract StoreObjectAccessor newPersistenceStoreObjectAccessor() throws IOException;
+    protected abstract StoreObjectAccessorWithLock newPersistenceStoreObjectAccessor() throws IOException;
     
     @AfterMethod(alwaysRun=true)
     public void tearDown() throws Exception {
         if (accessor != null) {
-            accessor.deleteAsync();
-            accessor.waitForWriteCompleted(Duration.TEN_SECONDS);
+            accessor.delete();
+            accessor.waitForCurrentWrites(Duration.TEN_SECONDS);
         }
         if (executor != null) executor.shutdownNow();
     }
 
     @Test
     public void testWritesFile() throws Exception {
-        accessor.writeAsync("abc");
-        accessor.waitForWriteCompleted(TIMEOUT);
+        accessor.put("abc");
+        accessor.waitForCurrentWrites(TIMEOUT);
 
-        assertEquals(accessor.read(), "abc");
+        assertEquals(accessor.get(), "abc");
     }
     
     @Test
     public void testWriteBacklogThenDeleteWillLeaveFileDeleted() throws Exception {
         String big = makeBigString(biggishSize());
         
-        accessor.writeAsync(big);
-        accessor.writeAsync(big);
-        accessor.deleteAsync();
+        accessor.put(big);
+        accessor.put(big);
+        accessor.delete();
         
-        accessor.waitForWriteCompleted(TIMEOUT);
+        accessor.waitForCurrentWrites(TIMEOUT);
         assertFalse(accessor.exists());
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java b/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
index 33e1de9..6b0450d 100644
--- a/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
+++ b/core/src/test/java/brooklyn/management/ha/HighAvailabilityManagerTestFixture.java
@@ -29,6 +29,7 @@ import com.google.common.base.Ticker;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 
+@Test
 public abstract class HighAvailabilityManagerTestFixture {
 
     @SuppressWarnings("unused")
@@ -79,13 +80,12 @@ public abstract class HighAvailabilityManagerTestFixture {
     public void tearDown() throws Exception {
         if (manager != null) manager.stop();
         if (managementContext != null) Entities.destroyAll(managementContext);
-        if (objectStore!=null) objectStore.deleteCompletely();
+        if (objectStore != null) objectStore.deleteCompletely();
     }
     
     // Can get a log.error about our management node's heartbeat being out of date. Caused by
     // poller first writing a heartbeat record, and then the clock being incremented. But the
     // next poll fixes it.
-    @Test
     public void testPromotes() throws Exception {
         persister.delta(ManagementPlaneSyncRecordDeltaImpl.builder()
                 .node(newManagerMemento(ownNodeId, ManagementNodeState.STANDBY, tickerCurrentMillis()))
@@ -122,7 +122,6 @@ public abstract class HighAvailabilityManagerTestFixture {
             }});
     }
 
-    @Test
     public void testGetManagementPlaneStatus() throws Exception {
         // with the name zzzzz the mgr created here should never be promoted by the alphabetical strategy!
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/test/entity/LocalManagementContextForTests.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/test/entity/LocalManagementContextForTests.java b/core/src/test/java/brooklyn/test/entity/LocalManagementContextForTests.java
index 5f670ff..751517b 100644
--- a/core/src/test/java/brooklyn/test/entity/LocalManagementContextForTests.java
+++ b/core/src/test/java/brooklyn/test/entity/LocalManagementContextForTests.java
@@ -1,9 +1,7 @@
 package brooklyn.test.entity;
 
-import brooklyn.catalog.internal.BasicBrooklynCatalog;
-import brooklyn.catalog.internal.CatalogClasspathDo.CatalogScanningModes;
-import brooklyn.catalog.internal.CatalogDtoUtils;
 import brooklyn.config.BrooklynProperties;
+import brooklyn.management.internal.AbstractManagementContext;
 import brooklyn.management.internal.LocalManagementContext;
 
 /** management context which forces an empty catalog to prevent scanning / interacting with local filesystem.
@@ -12,10 +10,14 @@ import brooklyn.management.internal.LocalManagementContext;
 public class LocalManagementContextForTests extends LocalManagementContext {
 
     public LocalManagementContextForTests(BrooklynProperties brooklynProperties) {
-        super(brooklynProperties);
-        catalog = new BasicBrooklynCatalog(this, CatalogDtoUtils.newDefaultLocalScanningDto(CatalogScanningModes.NONE));
+        super(setEmptyCatalogAsDefault(brooklynProperties));
     }
     
+    public static BrooklynProperties setEmptyCatalogAsDefault(BrooklynProperties brooklynProperties) {
+        brooklynProperties.putIfAbsent(AbstractManagementContext.BROOKLYN_CATALOG_URL, "classpath://brooklyn-catalog-empty.xml");
+        return brooklynProperties;
+    }
+
     public LocalManagementContextForTests() {
         this(BrooklynProperties.Factory.newEmpty());
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/core/src/test/java/brooklyn/util/file/ArchiveBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/util/file/ArchiveBuilderTest.java b/core/src/test/java/brooklyn/util/file/ArchiveBuilderTest.java
index e90fe60..012ff11 100644
--- a/core/src/test/java/brooklyn/util/file/ArchiveBuilderTest.java
+++ b/core/src/test/java/brooklyn/util/file/ArchiveBuilderTest.java
@@ -13,7 +13,7 @@ import java.util.zip.ZipInputStream;
 
 import javax.annotation.Nullable;
 
-import org.testng.annotations.BeforeTest;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import brooklyn.util.os.Os;
@@ -40,7 +40,7 @@ public class ArchiveBuilderTest {
                 }
             };
 
-    @BeforeTest
+    @BeforeClass
     public void createTmpDirAndFiles() throws IOException {
         parentDir = new File(Os.tmp(), Identifiers.makeRandomId(4));
         Os.deleteOnExitRecursively(parentDir);
@@ -50,7 +50,7 @@ public class ArchiveBuilderTest {
         Files.write("123456", new File(tmpDir, "data02.txt"), Charsets.US_ASCII);
         Files.write("qqqqqq", new File(tmpDir, "data03.txt"), Charsets.US_ASCII);
     }
-
+    
     @Test
     public void testCreateZipFromDir() throws Exception {
         File archive = ArchiveBuilder.zip().addDir(tmpDir).create();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/locations/jclouds/src/main/java/brooklyn/entity/rebind/persister/jclouds/JcloudsBlobStoreBasedObjectStore.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/entity/rebind/persister/jclouds/JcloudsBlobStoreBasedObjectStore.java b/locations/jclouds/src/main/java/brooklyn/entity/rebind/persister/jclouds/JcloudsBlobStoreBasedObjectStore.java
index e6b1d91..8a04bbe 100644
--- a/locations/jclouds/src/main/java/brooklyn/entity/rebind/persister/jclouds/JcloudsBlobStoreBasedObjectStore.java
+++ b/locations/jclouds/src/main/java/brooklyn/entity/rebind/persister/jclouds/JcloudsBlobStoreBasedObjectStore.java
@@ -13,12 +13,14 @@ import org.jclouds.blobstore.options.ListContainerOptions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.rebind.persister.PersistMode;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
 import brooklyn.location.basic.LocationConfigKeys;
 import brooklyn.location.cloud.CloudLocationConfig;
 import brooklyn.location.jclouds.JcloudsLocation;
 import brooklyn.management.ManagementContext;
+import brooklyn.util.exceptions.FatalConfigurationRuntimeException;
 
 import com.google.common.base.Function;
 import com.google.common.base.Objects;
@@ -30,7 +32,6 @@ import com.google.common.collect.FluentIterable;
  */
 public class JcloudsBlobStoreBasedObjectStore implements PersistenceObjectStore {
 
-    @SuppressWarnings("unused")
     private static final Logger log = LoggerFactory.getLogger(JcloudsBlobStoreBasedObjectStore.class);
 
     private final String containerName;
@@ -90,7 +91,9 @@ public class JcloudsBlobStoreBasedObjectStore implements PersistenceObjectStore
     @Override
     public void createSubPath(String subPath) {
         checkPrepared();
-        context.getBlobStore().createDirectory(getContainerName(), subPath);
+        // not needed, and buggy on softlayer w swift w jclouds 1.7.2:
+        // throws a "not found" if we're creating an empty directory from scratch
+//        context.getBlobStore().createDirectory(getContainerName(), subPath);
     }
 
     protected void checkPrepared() {
@@ -133,11 +136,6 @@ public class JcloudsBlobStoreBasedObjectStore implements PersistenceObjectStore
     }
 
     @Override
-    public void backupContents(String parentSubPath, String backupSubPath) {
-        // TODO for now assume object store deals with backups
-    }
-
-    @Override
     public void close() {
         if (context!=null)
             context.close();
@@ -156,10 +154,33 @@ public class JcloudsBlobStoreBasedObjectStore implements PersistenceObjectStore
         if (this.mgmt!=null && !this.mgmt.equals(mgmt))
             throw new IllegalStateException("Cannot change mgmt context of "+this);
         this.mgmt = mgmt;
+
+        getBlobStoreContext();
         
-        // TODO to prepare for persistMode, see backupContents, and how FileBasedObjectStore does it.
+        if (persistMode==null || persistMode==PersistMode.DISABLED) {
+            log.warn("Should not be using "+this+" when persistMode is "+persistMode);
+            return;
+        }
         
-        getBlobStoreContext();
+        Boolean backups = mgmt.getConfig().getConfig(BrooklynServerConfig.PERSISTENCE_BACKUPS_REQUIRED);
+        if (backups==null) backups = false;
+        if (backups) {
+            throw new FatalConfigurationRuntimeException("Backups not supported for object store ("+this+")");
+        }
+        
+        switch (persistMode) {
+        case CLEAN:
+            deleteCompletely();
+            break;
+        case REBIND:
+            // no op - could confirm it exists (?)
+            break;
+        case AUTO:
+            // again, no op
+            break;
+        default:
+            throw new FatalConfigurationRuntimeException("Unexpected persist mode "+persistMode+"; modified during initialization?!");
+        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/locations/jclouds/src/main/java/brooklyn/entity/rebind/persister/jclouds/JcloudsStoreObjectAccessor.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/main/java/brooklyn/entity/rebind/persister/jclouds/JcloudsStoreObjectAccessor.java b/locations/jclouds/src/main/java/brooklyn/entity/rebind/persister/jclouds/JcloudsStoreObjectAccessor.java
index ada99ff..bba5ec1 100644
--- a/locations/jclouds/src/main/java/brooklyn/entity/rebind/persister/jclouds/JcloudsStoreObjectAccessor.java
+++ b/locations/jclouds/src/main/java/brooklyn/entity/rebind/persister/jclouds/JcloudsStoreObjectAccessor.java
@@ -1,7 +1,6 @@
 package brooklyn.entity.rebind.persister.jclouds;
 
 import java.io.IOException;
-import java.util.concurrent.TimeoutException;
 
 import org.apache.commons.io.Charsets;
 import org.jclouds.blobstore.BlobStore;
@@ -10,7 +9,6 @@ import org.jclouds.util.Strings2;
 
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
 import brooklyn.util.exceptions.Exceptions;
-import brooklyn.util.time.Duration;
 
 import com.google.common.base.Throwables;
 import com.google.common.io.ByteSource;
@@ -36,10 +34,11 @@ public class JcloudsStoreObjectAccessor implements PersistenceObjectStore.StoreO
     }
 
     @Override
-    public void writeAsync(String val) {
+    public void put(String val) {
         if (val==null) val = "";
         
         blobStore.createContainerInLocation(null, containerName);
+        // seems not needed, at least not w SoftLayer
 //        blobStore.createDirectory(containerName, directoryName);
         ByteSource payload = ByteSource.wrap(val.getBytes(Charsets.UTF_8));
         Blob blob;
@@ -54,26 +53,23 @@ public class JcloudsStoreObjectAccessor implements PersistenceObjectStore.StoreO
     }
 
     @Override
-    public void append(String s) {
-
+    public void append(String val) {
+        String val0 = get();
+        if (val0==null) val0="";
+        if (val==null) val="";
+        put(val0+val);
     }
 
     @Override
-    public void deleteAsync() {
+    public void delete() {
         blobStore.removeBlob(containerName, blobName);
     }
 
     @Override
-    public void waitForWriteCompleted(Duration timeout) throws InterruptedException, TimeoutException {
-
-    }
-
-    @Override
-    public String read() {
+    public String get() {
         try {
             Blob blob = blobStore.getBlob(containerName, blobName);
-            if (blob==null)
-                throw new IllegalStateException("Error reading blobstore "+containerName+" "+blobName+": no such blob");
+            if (blob==null) return null;
             return Strings2.toString(blob.getPayload());
         } catch (IOException e) {
             Exceptions.propagateIfFatal(e);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BlobStoreTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BlobStoreTest.java b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BlobStoreTest.java
index f892de7..ef95fb8 100644
--- a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BlobStoreTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BlobStoreTest.java
@@ -11,8 +11,8 @@ import org.jclouds.blobstore.domain.PageSet;
 import org.jclouds.blobstore.domain.StorageMetadata;
 import org.jclouds.blobstore.options.ListContainerOptions;
 import org.junit.Assert;
-import org.testng.annotations.AfterTest;
-import org.testng.annotations.BeforeTest;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
@@ -30,7 +30,17 @@ import com.google.common.base.Preconditions;
 @Test(groups="Integration")
 public class BlobStoreTest {
 
-    public static final String PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC = "named:softlayer-objectstore-amsterdam-1";
+    /**
+     * Integration tests as written require a location defined as follows:
+     * 
+     * <code>
+     * brooklyn.location.named.brooklyn-jclouds-objstore-test-1==jclouds:swift:https://ams01.objectstorage.softlayer.net/auth/v1.0
+     * brooklyn.location.named.brooklyn-jclouds-objstore-test-1.identity=IBMOS1234-5:yourname
+     * brooklyn.location.named.brooklyn-jclouds-objstore-test-1.credential=0123abcd.......
+     * </code>
+     */
+    public static final String PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC = "named:brooklyn-jclouds-objstore-test-1";
+    
     public static final String CONTAINER_PREFIX = "brooklyn-persistence-test";
     
     private String locationSpec = PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC;
@@ -62,14 +72,14 @@ public class BlobStoreTest {
         return context;
     }
     
-    @BeforeTest(alwaysRun=true)
+    @BeforeMethod(alwaysRun=true)
     public void setup() {
         testContainerName = CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(8);
         mgmt = new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault());
         getBlobStoreContext();
     }
     
-    @AfterTest(alwaysRun=true)
+    @AfterMethod(alwaysRun=true)
     public void teardown() {
         Entities.destroyAll(mgmt);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BrooklynMementoPersisterJcloudsObjectStoreTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BrooklynMementoPersisterJcloudsObjectStoreTest.java b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BrooklynMementoPersisterJcloudsObjectStoreTest.java
index 47cc309..bd3e425 100644
--- a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BrooklynMementoPersisterJcloudsObjectStoreTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/BrooklynMementoPersisterJcloudsObjectStoreTest.java
@@ -1,6 +1,10 @@
 package brooklyn.entity.rebind.persister.jclouds;
 
 
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
@@ -16,6 +20,9 @@ import brooklyn.util.time.Duration;
 @Test(groups="Integration")
 public class BrooklynMementoPersisterJcloudsObjectStoreTest extends BrooklynMementoPersisterTestFixture {
 
+    @Override @BeforeMethod
+    public void setUp() throws Exception { super.setUp(); }
+    
     protected LocalManagementContext newPersistingManagementContext() {
         objectStore = new JcloudsBlobStoreBasedObjectStore(
             BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC, BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4));
@@ -25,4 +32,16 @@ public class BrooklynMementoPersisterJcloudsObjectStoreTest extends BrooklynMeme
             .buildStarted();
     }
     
+    @Test(groups="Integration")
+    @Override
+    public void testCheckPointAndLoadMemento() throws IOException, TimeoutException, InterruptedException {
+        super.testCheckPointAndLoadMemento();
+    }
+    
+    @Test(groups="Integration")
+    @Override
+    public void testDeleteAndLoadMemento() throws TimeoutException, InterruptedException, IOException {
+        super.testDeleteAndLoadMemento();
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
index d9146c7..17c5adc 100644
--- a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/HighAvailabilityManagerJcloudsObjectStoreTest.java
@@ -1,5 +1,6 @@
 package brooklyn.entity.rebind.persister.jclouds;
 
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
@@ -16,14 +17,35 @@ public class HighAvailabilityManagerJcloudsObjectStoreTest extends HighAvailabil
         return new LocalManagementContextForTests(BrooklynProperties.Factory.newDefault());
     }
 
+    @Override @BeforeMethod
+    public void setUp() throws Exception { super.setUp(); }
+    
     protected PersistenceObjectStore newPersistenceObjectStore() {
         return new JcloudsBlobStoreBasedObjectStore(
             BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC, BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4));
     }
-    
+
     @Test(groups="Integration", invocationCount=5) //run fewer times w softlayer... 
     public void testGetManagementPlaneStatusManyTimes() throws Exception {
         testGetManagementPlaneStatus();
     }
     
+    @Test(groups="Integration")
+    @Override
+    public void testDoesNotPromoteIfMasterTimeoutNotExpired() throws Exception {
+        super.testDoesNotPromoteIfMasterTimeoutNotExpired();
+    }
+    
+    @Test(groups="Integration")
+    @Override
+    public void testGetManagementPlaneStatus() throws Exception {
+        super.testGetManagementPlaneStatus();
+    }
+    
+    @Test(groups="Integration")
+    @Override
+    public void testPromotes() throws Exception {
+        super.testPromotes();
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/JcloudsObjectStoreAccessorWriterTest.java
----------------------------------------------------------------------
diff --git a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/JcloudsObjectStoreAccessorWriterTest.java b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/JcloudsObjectStoreAccessorWriterTest.java
index b1bfbcf..b33f7ae 100644
--- a/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/JcloudsObjectStoreAccessorWriterTest.java
+++ b/locations/jclouds/src/test/java/brooklyn/entity/rebind/persister/jclouds/JcloudsObjectStoreAccessorWriterTest.java
@@ -8,9 +8,10 @@ import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.basic.Entities;
-import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessor;
 import brooklyn.entity.rebind.persister.PersistMode;
+import brooklyn.entity.rebind.persister.PersistenceObjectStore.StoreObjectAccessorWithLock;
 import brooklyn.entity.rebind.persister.PersistenceStoreObjectAccessorWriterTestFixture;
+import brooklyn.entity.rebind.persister.StoreObjectAccessorLocking;
 import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.util.text.Identifiers;
 
@@ -20,7 +21,7 @@ public class JcloudsObjectStoreAccessorWriterTest extends PersistenceStoreObject
     private JcloudsBlobStoreBasedObjectStore store;
     private LocalManagementContextForTests mgmt;
 
-    @Override @BeforeMethod(alwaysRun=true)
+    @Override @BeforeMethod
     public void setUp() throws Exception {
         store = new JcloudsBlobStoreBasedObjectStore(
             BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC, BlobStoreTest.CONTAINER_PREFIX+"-"+Identifiers.makeRandomId(4));
@@ -32,11 +33,11 @@ public class JcloudsObjectStoreAccessorWriterTest extends PersistenceStoreObject
     public void tearDown() throws Exception {
         super.tearDown();
         if (mgmt!=null) Entities.destroyAll(mgmt);
-        store.deleteCompletely();
+        if (store!=null) store.deleteCompletely();
     }
     
-    protected StoreObjectAccessor newPersistenceStoreObjectAccessor() throws IOException {
-        return store.newAccessor("sample-file-"+Identifiers.makeRandomId(4));
+    protected StoreObjectAccessorWithLock newPersistenceStoreObjectAccessor() throws IOException {
+        return new StoreObjectAccessorLocking(store.newAccessor("sample-file-"+Identifiers.makeRandomId(4)));
     }
 
     protected int biggishSize() {
@@ -44,4 +45,16 @@ public class JcloudsObjectStoreAccessorWriterTest extends PersistenceStoreObject
         return 10000;
     }
     
+    @Test(groups="Integration")
+    @Override
+    public void testWriteBacklogThenDeleteWillLeaveFileDeleted() throws Exception {
+        super.testWriteBacklogThenDeleteWillLeaveFileDeleted();
+    }
+    
+    @Test(groups="Integration")
+    @Override
+    public void testWritesFile() throws Exception {
+        super.testWritesFile();
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/usage/cli/src/main/java/brooklyn/cli/Main.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/Main.java b/usage/cli/src/main/java/brooklyn/cli/Main.java
index 1a482e0..1ad8abf 100644
--- a/usage/cli/src/main/java/brooklyn/cli/Main.java
+++ b/usage/cli/src/main/java/brooklyn/cli/Main.java
@@ -331,10 +331,8 @@ public class Main {
                 launchApp(launcher, utils, loader);
     
                 launcher.persistMode(persistMode);
-                if (persistMode != PersistMode.DISABLED && Strings.isNonBlank(persistenceDir)) {
-                    launcher.persistenceDir(persistenceDir);
-                    launcher.persistenceLocation(persistenceLocation);
-                }
+                launcher.persistenceDir(persistenceDir);
+                launcher.persistenceLocation(persistenceLocation);
                 
                 launcher.highAvailabilityMode(highAvailabilityMode);
 
@@ -664,7 +662,7 @@ public class Main {
             System.err.println(getUsageInfo(parser)); // display cli help
             System.exit(PARSE_ERROR);
         } catch (FatalConfigurationRuntimeException e) {
-            log.error("Configuration error: " + e.getMessage(), e);
+            log.error(e.getMessage(), e.getCause());
             System.err.println("Configuration error: " + e.getMessage());
             System.exit(CONFIGURATION_ERROR);
         } catch (Exception e) { // unexpected error during command execution

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/usage/launcher/pom.xml
----------------------------------------------------------------------
diff --git a/usage/launcher/pom.xml b/usage/launcher/pom.xml
index ad25ea1..9fbe572 100644
--- a/usage/launcher/pom.xml
+++ b/usage/launcher/pom.xml
@@ -113,6 +113,13 @@
             <artifactId>brooklyn-locations-jclouds</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>io.brooklyn</groupId>
+            <artifactId>brooklyn-locations-jclouds</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
         
     </dependencies>
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
index e261015..1f4086e 100644
--- a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
+++ b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
@@ -9,7 +9,6 @@ import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator;
 
 import java.io.Closeable;
 import java.io.File;
-import java.io.IOException;
 import java.io.StringReader;
 import java.net.InetAddress;
 import java.util.ArrayList;
@@ -18,6 +17,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeoutException;
 
+import javax.annotation.Nullable;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -58,8 +59,8 @@ import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.FatalConfigurationRuntimeException;
 import brooklyn.util.exceptions.RuntimeInterruptedException;
 import brooklyn.util.net.Networking;
-import brooklyn.util.os.Os;
 import brooklyn.util.stream.Streams;
+import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 
@@ -110,7 +111,7 @@ public class BrooklynLauncher {
     private boolean shutdownOnExit = true;
     private PersistMode persistMode = PersistMode.DISABLED;
     private HighAvailabilityMode highAvailabilityMode = HighAvailabilityMode.DISABLED;
-    private File persistenceDir;
+    private String persistenceDir;
     private String persistenceLocation;
     private Duration persistPeriod = Duration.ONE_SECOND;
     private Duration haHeartbeatTimeout = Duration.THIRTY_SECONDS;
@@ -212,8 +213,8 @@ public class BrooklynLauncher {
         return this;
     }
 
-    public BrooklynLauncher persistenceLocation(String persistenceLocationSpec) {
-        persistenceLocation = checkNotNull(persistenceLocationSpec, "persistenceLocationSpec");
+    public BrooklynLauncher persistenceLocation(@Nullable String persistenceLocationSpec) {
+        persistenceLocation = persistenceLocationSpec;
         return this;
     }
 
@@ -341,16 +342,16 @@ public class BrooklynLauncher {
         return this;
     }
     
-    public BrooklynLauncher persistenceDir(String persistenceDir) {
-        if (persistenceDir==null) return persistenceDir((File)null);
-        return persistenceDir(new File(persistenceDir));
-    }
-
-    public BrooklynLauncher persistenceDir(File persistenceDir) {
+    public BrooklynLauncher persistenceDir(@Nullable String persistenceDir) {
         this.persistenceDir = persistenceDir;
         return this;
     }
 
+    public BrooklynLauncher persistenceDir(@Nullable File persistenceDir) {
+        if (persistenceDir==null) return persistenceDir((String)null);
+        return persistenceDir(persistenceDir.getAbsolutePath());
+    }
+
     public BrooklynLauncher persistPeriod(Duration persistPeriod) {
         this.persistPeriod = persistPeriod;
         return this;
@@ -407,12 +408,13 @@ public class BrooklynLauncher {
                 .getCampPlatform();
         // TODO start CAMP rest _server_ in the below (at /camp) ?
         
+        initPersistence();
+        
         // Start the web-console
         if (startWebApps) {
             startWebApps();
         }
         
-        initPersistence();
         createApps();
         startApps();
         
@@ -455,20 +457,27 @@ public class BrooklynLauncher {
         if (persistMode == PersistMode.DISABLED) {
             LOG.info("Persistence disabled");
         } else {
-            if (persistenceDir == null) {
-                persistenceDir = new File( BrooklynServerConfig.getPersistenceDir(brooklynProperties) );
+            if (persistenceLocation == null) {
+                persistenceLocation = brooklynProperties.getConfig(BrooklynServerConfig.PERSISTENCE_LOCATION_SPEC);
             }
+            
+            persistenceDir = BrooklynServerConfig.resolvePersistencePath(persistenceDir, brooklynProperties, persistenceLocation);
 
-            if (persistenceLocation == null) {
-                objectStore = new FileBasedObjectStore(persistenceDir);
+            if (Strings.isBlank(persistenceLocation)) {
+                objectStore = new FileBasedObjectStore(new File(persistenceDir));
             } else {
-                String persistenceContainer;
-                if (persistenceDir.getAbsolutePath().endsWith(Os.mergePaths("brooklyn-persisted-state", "data")))
-                    persistenceContainer = "brooklyn-persisted-state";
-                else persistenceContainer = persistenceDir.getName();
-                objectStore = new JcloudsBlobStoreBasedObjectStore(persistenceLocation, persistenceContainer);
+                objectStore = new JcloudsBlobStoreBasedObjectStore(persistenceLocation, persistenceDir);
+            }
+            try {
+                objectStore.prepareForUse(managementContext, persistMode);
+            } catch (FatalConfigurationRuntimeException e) {
+                throw e;
+            } catch (Exception e) {
+                Exceptions.propagateIfFatal(e);
+                LOG.debug("Error initializing persistence subsystem (rethrowing): "+e, e);
+                throw new FatalConfigurationRuntimeException("Error initializing persistence subsystem: "+
+                    Exceptions.collapseText(e), e);
             }
-            objectStore.prepareForUse(managementContext, persistMode);
 
             RebindManager rebindManager = managementContext.getRebindManager();
 
@@ -510,10 +519,10 @@ public class BrooklynLauncher {
                         rebinding = true;
                         break;
                     case AUTO:
-                        if (persistenceLocation!=null) {
+                        if (Strings.isNonBlank(persistenceLocation)) {
                             rebinding = true;
-                        } else if (persistenceDir!=null && persistenceDir.exists()) {
-                            String[] files = persistenceDir.list();
+                        } else if (persistenceDir!=null && new File(persistenceDir).exists()) {
+                            String[] files = new File(persistenceDir).list();
                             if (files==null)
                                 throw new FatalConfigurationRuntimeException("Persistence dir "+persistenceDir+" is not a directory.");
                             rebinding = (files.length > 0);
@@ -527,16 +536,22 @@ public class BrooklynLauncher {
                 
                 RebindManager rebindManager = managementContext.getRebindManager();
                 if (rebinding) {
-                    LOG.info("Management node (no high availability) rebinding to entities using "+persistenceDir.getAbsolutePath());
+                    if (Strings.isNonBlank(persistenceLocation))
+                        LOG.info("Management node (no HA) rebinding to entities at "+persistenceLocation+" in "+persistenceDir);
+                    else
+                        LOG.info("Management node (no HA) rebinding to entities on file system in "+persistenceDir);
 
                     ClassLoader classLoader = managementContext.getCatalog().getRootClassLoader();
                     try {
                         rebindManager.rebind(classLoader);
-                    } catch (IOException e) {
-                        throw Exceptions.propagate(e);
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        LOG.debug("Error rebinding to persisted state (rethrowing): "+e, e);
+                        throw new FatalConfigurationRuntimeException("Error rebinding to persisted state: "+
+                            Exceptions.collapseText(e), e);
                     }
                 } else {
-                    LOG.info("Management node (no high availability) starting, no existing entities to rebind in "+persistenceDir.getAbsolutePath());
+                    LOG.debug("Management node (no HA) skipping rebind, no existing entities in "+persistenceDir);
                 }
                 rebindManager.start();
             }
@@ -557,7 +572,7 @@ public class BrooklynLauncher {
                     throw new IllegalStateException("Unexpected high availability mode "+highAvailabilityMode);
             }
             
-            LOG.info("Management node (with high availability) starting");
+            LOG.debug("Management node (with HA) starting");
             HighAvailabilityManager haManager = managementContext.getHighAvailabilityManager();
             haManager.start(startMode);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
index 7df8975..5e9abe4 100644
--- a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
+++ b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
@@ -11,6 +11,7 @@ import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import brooklyn.config.BrooklynProperties;
 import brooklyn.entity.Application;
 import brooklyn.entity.proxying.EntitySpec;
 import brooklyn.entity.rebind.RebindTestUtils;
@@ -20,7 +21,9 @@ import brooklyn.management.ha.HighAvailabilityMode;
 import brooklyn.management.ha.ManagementPlaneSyncRecordPersister;
 import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.test.Asserts;
+import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.test.entity.TestApplication;
+import brooklyn.util.os.Os;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Predicates;
@@ -39,6 +42,7 @@ public class BrooklynLauncherHighAvailabilityTest {
     @BeforeMethod(alwaysRun=true)
     public void setUp() throws Exception {
         persistenceDir = Files.createTempDir();
+        Os.deleteOnExitRecursively(persistenceDir);
     }
 
     @AfterMethod(alwaysRun=true)
@@ -62,6 +66,7 @@ public class BrooklynLauncherHighAvailabilityTest {
     protected void doTestStandbyTakesOver(boolean stopGracefully) throws Exception {
         primary = BrooklynLauncher.newInstance()
                 .webconsole(false)
+                .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
                 .highAvailabilityMode(HighAvailabilityMode.AUTO)
                 .persistMode(PersistMode.AUTO)
                 .persistenceDir(persistenceDir)
@@ -78,6 +83,7 @@ public class BrooklynLauncherHighAvailabilityTest {
         // Secondary will come up as standby
         secondary = BrooklynLauncher.newInstance()
                 .webconsole(false)
+                .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
                 .highAvailabilityMode(HighAvailabilityMode.AUTO)
                 .persistMode(PersistMode.AUTO)
                 .persistenceDir(persistenceDir)
@@ -103,6 +109,7 @@ public class BrooklynLauncherHighAvailabilityTest {
         // Start tertiary (will come up as standby)
         tertiary = BrooklynLauncher.newInstance()
                 .webconsole(false)
+                .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
                 .highAvailabilityMode(HighAvailabilityMode.AUTO)
                 .persistMode(PersistMode.AUTO)
                 .persistenceDir(persistenceDir)
@@ -126,10 +133,10 @@ public class BrooklynLauncherHighAvailabilityTest {
         assertOnlyAppEventually(tertiaryManagementContext, TestApplication.class);
     }
     
-    @Test
     public void testHighAvailabilityMasterModeFailsIfAlreadyHasMaster() throws Exception {
         primary = BrooklynLauncher.newInstance()
                 .webconsole(false)
+                .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
                 .highAvailabilityMode(HighAvailabilityMode.AUTO)
                 .persistMode(PersistMode.AUTO)
                 .persistenceDir(persistenceDir)
@@ -141,6 +148,7 @@ public class BrooklynLauncherHighAvailabilityTest {
             // Secondary will come up as standby
             secondary = BrooklynLauncher.newInstance()
                     .webconsole(false)
+                    .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
                     .highAvailabilityMode(HighAvailabilityMode.MASTER)
                     .persistMode(PersistMode.AUTO)
                     .persistenceDir(persistenceDir)
@@ -157,6 +165,7 @@ public class BrooklynLauncherHighAvailabilityTest {
         try {
             primary = BrooklynLauncher.newInstance()
                     .webconsole(false)
+                    .brooklynProperties(LocalManagementContextForTests.setEmptyCatalogAsDefault(BrooklynProperties.Factory.newEmpty()))
                     .highAvailabilityMode(HighAvailabilityMode.STANDBY)
                     .persistMode(PersistMode.AUTO)
                     .persistenceDir(persistenceDir)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherRebindTestToFiles.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherRebindTestToFiles.java b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherRebindTestToFiles.java
index 569aa57..ddd770b 100644
--- a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherRebindTestToFiles.java
+++ b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherRebindTestToFiles.java
@@ -6,7 +6,6 @@ import java.io.File;
 
 import org.testng.annotations.Test;
 
-import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.proxying.EntitySpec;
 import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
@@ -43,7 +42,7 @@ public class BrooklynLauncherRebindTestToFiles extends BrooklynLauncherRebindTes
     }
 
     protected void checkPersistenceContainerNameIsDefault() {
-        checkPersistenceContainerNameIs(BrooklynServerConfig.getPersistenceDir(BrooklynProperties.Factory.newDefault()));
+        checkPersistenceContainerNameIs(BrooklynServerConfig.DEFAULT_PERSISTENCE_DIR_FOR_FILESYSTEM);
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherRebindToCloudObjectStoreTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherRebindToCloudObjectStoreTest.java b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherRebindToCloudObjectStoreTest.java
index 15ce172..e38da7f 100644
--- a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherRebindToCloudObjectStoreTest.java
+++ b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherRebindToCloudObjectStoreTest.java
@@ -2,11 +2,13 @@ package brooklyn.launcher;
 
 import static org.testng.Assert.assertEquals;
 
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
+import brooklyn.entity.rebind.persister.jclouds.BlobStoreTest;
 import brooklyn.entity.rebind.persister.jclouds.JcloudsBlobStoreBasedObjectStore;
 import brooklyn.management.ManagementContext;
 import brooklyn.test.entity.LocalManagementContextForTests;
@@ -14,13 +16,15 @@ import brooklyn.util.javalang.JavaClassNames;
 import brooklyn.util.text.Identifiers;
 
 @Test(groups="Integration")
-// Jun 2014: Alex has confirmed setting Integration here means that inherited methods are NOT run as part of normal build
 public class BrooklynLauncherRebindToCloudObjectStoreTest extends BrooklynLauncherRebindTestFixture {
 
-    public static final String LOCATION_SPEC = "named:softlayer-objectstore-amsterdam-1";
-    
-    { persistenceLocationSpec = LOCATION_SPEC; }
-    
+    { persistenceLocationSpec = BlobStoreTest.PERSIST_TO_OBJECT_STORE_FOR_TEST_SPEC; }
+
+    @BeforeMethod
+    public void setUp() throws Exception {
+        persistenceDir = newTempPersistenceContainerName();
+    }
+
     @Override
     protected BrooklynLauncher newLauncherBase() {
         return super.newLauncherBase().persistenceLocation(persistenceLocationSpec);
@@ -52,7 +56,52 @@ public class BrooklynLauncherRebindToCloudObjectStoreTest extends BrooklynLaunch
     }
 
     protected void checkPersistenceContainerNameIsDefault() {
-        checkPersistenceContainerNameIs(BrooklynServerConfig.PERSISTENCE_PATH_SEGMENT);
+        checkPersistenceContainerNameIs(BrooklynServerConfig.DEFAULT_PERSISTENCE_CONTAINER_NAME);
     }
-    
+
+    @Override @Test(groups="Integration")
+    public void testRebindsToExistingApp() throws Exception {
+        super.testRebindsToExistingApp();
+    }
+
+    @Override @Test(groups="Integration")
+    public void testRebindCanAddNewApps() throws Exception {
+        super.testRebindCanAddNewApps();
+    }
+
+    @Override @Test(groups="Integration")
+    public void testAutoRebindsToExistingApp() throws Exception {
+        super.testAutoRebindsToExistingApp();
+    }
+
+    @Override @Test(groups="Integration")
+    public void testCleanDoesNotRebindToExistingApp() throws Exception {
+        super.testCleanDoesNotRebindToExistingApp();
+    }
+
+    @Override @Test(groups="Integration")
+    public void testAutoRebindCreatesNewIfEmptyDir() throws Exception {
+        super.testAutoRebindCreatesNewIfEmptyDir();
+    }
+
+    @Override @Test(groups="Integration")
+    public void testRebindRespectsPersistenceDirSetInProperties() throws Exception {
+        super.testRebindRespectsPersistenceDirSetInProperties();
+    }
+
+    @Override @Test(groups="Integration")
+    public void testRebindRespectsDefaultPersistenceDir() throws Exception {
+        super.testRebindRespectsDefaultPersistenceDir();
+    }
+
+    @Override @Test(groups="Integration")
+    public void testPersistenceFailsIfNoDir() throws Exception {
+        super.testPersistenceFailsIfNoDir();
+    }
+
+    @Override @Test(groups="Integration")
+    public void testExplicitRebindFailsIfEmpty() throws Exception {
+        super.testExplicitRebindFailsIfEmpty();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/utils/common/src/main/java/brooklyn/util/exceptions/FatalConfigurationRuntimeException.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/exceptions/FatalConfigurationRuntimeException.java b/utils/common/src/main/java/brooklyn/util/exceptions/FatalConfigurationRuntimeException.java
index 4a57a3e..c73ac71 100644
--- a/utils/common/src/main/java/brooklyn/util/exceptions/FatalConfigurationRuntimeException.java
+++ b/utils/common/src/main/java/brooklyn/util/exceptions/FatalConfigurationRuntimeException.java
@@ -1,6 +1,7 @@
 package brooklyn.util.exceptions;
 
-/** Exception indicating a fatal config error, typically used in CLI routines. */
+/** Exception indicating a fatal config error, typically used in CLI routines.
+ * The message supplied here should be suitable for display in a CLI response (without stack trace / exception class). */
 public class FatalConfigurationRuntimeException extends RuntimeException {
 
     private static final long serialVersionUID = -5361951925760434821L;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/de3fb4be/utils/common/src/main/java/brooklyn/util/text/Strings.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/text/Strings.java b/utils/common/src/main/java/brooklyn/util/text/Strings.java
index 2428546..ab5a4d9 100644
--- a/utils/common/src/main/java/brooklyn/util/text/Strings.java
+++ b/utils/common/src/main/java/brooklyn/util/text/Strings.java
@@ -666,22 +666,47 @@ public class Strings {
         return count==1 ? "" : "s";
     }
     /** as {@link #s(int)} based on size of argument */
-    public static String s(@Nullable Map<?,?> map) {
-        if (map==null) return "s";
-        return s(map.size());
+    public static String s(@Nullable Map<?,?> x) {
+        return s(x==null ? 0 : x.size());
     }
     /** as {@link #s(int)} based on size of argument */
-    public static String s(Iterable<?> iter) {
-        if (iter==null) return "s";
-        return s(iter.iterator());
+    public static String s(Iterable<?> x) {
+        if (x==null) return s(0);
+        return s(x.iterator());
     }
     /** as {@link #s(int)} based on size of argument */
-    public static String s(Iterator<?> iter) {
-        if (iter==null) return "s";
-        if (!iter.hasNext()) return "s";
-        iter.next();
-        if (!iter.hasNext()) return "";
-        return "s";
+    public static String s(Iterator<?> x) {
+        int count = 0;
+        if (x==null || !x.hasNext()) {}
+        else { 
+            x.next(); count++;
+            if (x.hasNext()) count++;
+        }
+        return s(count);
+    }
+
+    /** returns "ies" if the argument is not 1, "y" otherwise; useful when constructing plurals */
+    public static String ies(int count) {
+        return count==1 ? "y" : "ies";
+    }
+    /** as {@link #ies(int)} based on size of argument */
+    public static String ies(@Nullable Map<?,?> x) {
+        return ies(x==null ? 0 : x.size());
+    }
+    /** as {@link #ies(int)} based on size of argument */
+    public static String ies(Iterable<?> x) {
+        if (x==null) return ies(0);
+        return ies(x.iterator());
+    }
+    /** as {@link #ies(int)} based on size of argument */
+    public static String ies(Iterator<?> x) {
+        int count = 0;
+        if (x==null || !x.hasNext()) {}
+        else { 
+            x.next(); count++;
+            if (x.hasNext()) count++;
+        }
+        return ies(count);
     }
 
     /** converts a map of any objects to a map of strings, preserving nulls and invoking toString where needed */


Mime
View raw message