brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [8/8] git commit: This closes 177.
Date Thu, 25 Sep 2014 17:15:09 GMT
This closes 177.

Merge remote-tracking branch 'apache-gh/pr/177' into pr-177-2

Conflicts - quite a few places, where CatalogItem was added in master but not done here.  Have fixed that and other things noted in the pull request.

	core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
	core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/320c1c75
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/320c1c75
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/320c1c75

Branch: refs/heads/master
Commit: 320c1c752a0085f360cd688d0cc8ff8113a9fa65
Parents: 4b02716 d911997
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Thu Sep 25 17:51:08 2014 +0100
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Thu Sep 25 18:07:44 2014 +0100

----------------------------------------------------------------------
 .../rebind/PersistenceExceptionHandler.java     |   2 +
 .../brooklyn/entity/rebind/RebindManager.java   |   3 +-
 .../mementos/BrooklynMementoManifest.java       |   2 +-
 .../mementos/BrooklynMementoRawData.java        | 123 ++++++
 .../rebind/PersistenceExceptionHandlerImpl.java |   6 +
 .../entity/rebind/RebindManagerImpl.java        | 134 +++----
 .../entity/rebind/dto/BrooklynMementoImpl.java  |   4 +-
 .../rebind/dto/BrooklynMementoManifestImpl.java |   2 +-
 .../BrooklynMementoPersisterInMemory.java       |   2 +-
 .../BrooklynMementoPersisterToObjectStore.java  | 396 ++++++++++---------
 .../transformer/BrooklynMementoTransformer.java |  32 ++
 .../rebind/transformer/CompoundTransformer.java | 188 +++++++++
 .../transformer/CompoundTransformerLoader.java  |  86 ++++
 .../rebind/transformer/RawDataTransformer.java  |  30 ++
 .../DeleteOrphanedLocationsTransformer.java     | 125 ++++++
 .../transformer/impl/XsltTransformer.java       |  57 +++
 .../NonDeploymentManagementContext.java         |   4 +-
 .../entity/rebind/transformer/renameClass.xslt  |  35 ++
 .../entity/rebind/transformer/renameField.xslt  |  35 ++
 .../entity/rebind/transformer/renameType.xslt   |  41 ++
 .../BrooklynMementoPersisterTestFixture.java    |  40 +-
 .../CompoundTransformerLoaderTest.java          |  70 ++++
 .../transformer/CompoundTransformerTest.java    | 199 ++++++++++
 .../transformer/impl/XsltTransformerTest.java   | 135 +++++++
 ...nMementoPersisterJcloudsObjectStoreTest.java |  12 +-
 usage/cli/src/main/java/brooklyn/cli/Main.java  |  42 +-
 .../brooklyn/launcher/BrooklynLauncher.java     |  28 +-
 .../BrooklynLauncherRebindTestToFiles.java      |  18 +-
 ...lynLauncherRebindToCloudObjectStoreTest.java |  41 ++
 .../brooklyn/util/javalang/Reflections.java     |   5 +
 30 files changed, 1592 insertions(+), 305 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/api/src/main/java/brooklyn/mementos/BrooklynMementoManifest.java
----------------------------------------------------------------------
diff --cc api/src/main/java/brooklyn/mementos/BrooklynMementoManifest.java
index d3df370,9fbaff3..843e800
--- a/api/src/main/java/brooklyn/mementos/BrooklynMementoManifest.java
+++ b/api/src/main/java/brooklyn/mementos/BrooklynMementoManifest.java
@@@ -36,8 -36,6 +36,8 @@@ public interface BrooklynMementoManifes
  
      public Map<String, String> getEnricherIdToType();
  
-     public Map<String, String> getCatalogIdToType();
++    public Map<String, String> getCatalogItemIdToType();
 +
      public boolean isEmpty();
      
  }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
----------------------------------------------------------------------
diff --cc api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
index 0000000,b010ab8..6638a78
mode 000000,100644..100644
--- a/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
+++ b/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
@@@ -1,0 -1,110 +1,123 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one
+  * or more contributor license agreements.  See the NOTICE file
+  * distributed with this work for additional information
+  * regarding copyright ownership.  The ASF licenses this file
+  * to you under the Apache License, Version 2.0 (the
+  * "License"); you may not use this file except in compliance
+  * with the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing,
+  * software distributed under the License is distributed on an
+  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  * KIND, either express or implied.  See the License for the
+  * specific language governing permissions and limitations
+  * under the License.
+  */
+ package brooklyn.mementos;
+ 
+ import java.util.Collections;
+ import java.util.Map;
+ 
+ import com.google.common.annotations.Beta;
+ import com.google.common.collect.Maps;
+ 
+ /**
+  * Represents the raw persisted data.
+  */
+ @Beta
+ public class BrooklynMementoRawData {
+ 
+     // TODO Should this be on an interface?
+     // The file-based (or object-store based) structure for storing data may well change; is this representation sufficient?
+ 
+     public static Builder builder() {
+         return new Builder();
+     }
+     
+     public static class Builder {
+         protected String brooklynVersion;
+         protected final Map<String, String> entities = Maps.newConcurrentMap();
+         protected final Map<String, String> locations = Maps.newConcurrentMap();
+         protected final Map<String, String> policies = Maps.newConcurrentMap();
+         protected final Map<String, String> enrichers = Maps.newConcurrentMap();
++        protected final Map<String, String> catalogItems = Maps.newConcurrentMap();
+         
+         public Builder brooklynVersion(String val) {
+             brooklynVersion = val; return this;
+         }
+         public Builder entity(String id, String val) {
+             entities.put(id, val); return this;
+         }
+         public Builder entities(Map<String, String> vals) {
+             entities.putAll(vals); return this;
+         }
+         public Builder location(String id, String val) {
+             locations.put(id, val); return this;
+         }
+         public Builder locations(Map<String, String> vals) {
+             locations.putAll(vals); return this;
+         }
+         public Builder policy(String id, String val) {
+             policies.put(id, val); return this;
+         }
+         public Builder policies(Map<String, String> vals) {
+             policies.putAll(vals); return this;
+         }
+         public Builder enricher(String id, String val) {
+             enrichers.put(id, val); return this;
+         }
+         public Builder enrichers(Map<String, String> vals) {
+             enrichers.putAll(vals); return this;
+         }
++        public Builder catalogItem(String id, String val) {
++            catalogItems.put(id, val); return this;
++        }
++        public Builder catalogItems(Map<String, String> vals) {
++            catalogItems.putAll(vals); return this;
++        }
+         public BrooklynMementoRawData build() {
+             return new BrooklynMementoRawData(this);
+         }
+     }
+ 
+     private final Map<String, String> entities;
+     private final Map<String, String> locations;
+     private final Map<String, String> policies;
+     private final Map<String, String> enrichers;
++    private final Map<String, String> catalogItems;
+     
+     private BrooklynMementoRawData(Builder builder) {
+         entities = builder.entities;
+         locations = builder.locations;
+         policies = builder.policies;
+         enrichers = builder.enrichers;
++        catalogItems = builder.catalogItems;
+     }
+ 
+     public Map<String, String> getEntities() {
+         return Collections.unmodifiableMap(entities);
+     }
+ 
+     public Map<String, String> getLocations() {
+         return Collections.unmodifiableMap(locations);
+     }
+ 
+     public Map<String, String> getPolicies() {
+         return Collections.unmodifiableMap(policies);
+     }
+ 
+     public Map<String, String> getEnrichers() {
+         return Collections.unmodifiableMap(enrichers);
+     }
+     
++    public Map<String, String> getCatalogItems() {
++        return Collections.unmodifiableMap(catalogItems);
++    }
++    
+     public boolean isEmpty() {
 -        return entities.isEmpty() && locations.isEmpty() && policies.isEmpty() && enrichers.isEmpty();
++        return entities.isEmpty() && locations.isEmpty() && policies.isEmpty() && enrichers.isEmpty() && catalogItems.isEmpty();
+     }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
index 6bfa400,70c2452..65e0524
--- a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
@@@ -60,7 -55,7 +61,8 @@@ import brooklyn.mementos.BrooklynMement
  import brooklyn.mementos.BrooklynMementoManifest;
  import brooklyn.mementos.BrooklynMementoPersister;
  import brooklyn.mementos.BrooklynMementoPersister.LookupContext;
+ import brooklyn.mementos.BrooklynMementoRawData;
 +import brooklyn.mementos.CatalogItemMemento;
  import brooklyn.mementos.EnricherMemento;
  import brooklyn.mementos.EntityMemento;
  import brooklyn.mementos.LocationMemento;
@@@ -440,24 -422,9 +432,24 @@@ public class RebindManagerImpl implemen
                  LOG.debug("Not rebinding enrichers; feature disabled: {}", memento.getEnricherIds());
              } 
              
 +            // Instantiate catalog items
 +            if (persistCatalogItemsEnabled) {
 +                LOG.debug("RebindManager instantiating catalog items: {}", memento.getCatalogItemIds());
 +                for (CatalogItemMemento catalogItemMemento : memento.getCatalogItemMementos().values()) {
 +                    if (LOG.isDebugEnabled()) LOG.debug("RebindManager instantiating catalog item {}", catalogItemMemento);
 +                    try {
 +                        CatalogItem<?, ?> catalogItem = newCatalogItem(catalogItemMemento, reflections);
 +                        rebindContext.registerCatalogItem(catalogItemMemento.getId(), catalogItem);
 +                    } catch (Exception e) {
 +                        exceptionHandler.onCreateFailed(BrooklynObjectType.CATALOG_ITEM, catalogItemMemento.getId(), catalogItemMemento.getType(), e);
 +                    }
 +                }
 +            } else {
 +                LOG.debug("Not rebinding catalog; feature disabled: {}", memento.getCatalogItemIds());
 +            }
              
              //
-             // PHASE THREE
+             // PHASE FIVE
              //
              
              // Reconstruct locations
@@@ -538,29 -506,9 +530,29 @@@
                  }
              }
  
 -            
 +            // Reconstruct catalog entries
 +            if (persistCatalogItemsEnabled) {
 +                LOG.debug("RebindManager reconstructing catalog items");
 +                for (CatalogItemMemento catalogItemMemento : memento.getCatalogItemMementos().values()) {
 +                    CatalogItem<?, ?> item = rebindContext.getCatalogItem(catalogItemMemento.getId());
 +                    LOG.debug("RebindManager reconstructing catalog item {}", catalogItemMemento);
 +                    if (item == null) {
 +                        exceptionHandler.onNotFound(BrooklynObjectType.CATALOG_ITEM, catalogItemMemento.getId());
 +                    } else {
 +                        try {
 +                            item.getRebindSupport().reconstruct(rebindContext, catalogItemMemento);
 +                            if (item instanceof AbstractBrooklynObject) {
 +                                AbstractBrooklynObject.class.cast(item).setManagementContext(managementContext);
 +                            }
 +                        } catch (Exception e) {
 +                            exceptionHandler.onRebindFailed(BrooklynObjectType.CATALOG_ITEM, item, e);
 +                        }
 +                    }
 +                }
 +            }
 +
              //
-             // PHASE FOUR
+             // PHASE SIX
              //
              
              // Associate policies+enrichers with entities
@@@ -685,9 -601,9 +677,7 @@@
      @VisibleForTesting
      <T extends TreeNode> Map<String, T> sortParentFirst(Map<String, T> nodes) {
          Map<String, T> result = Maps.newLinkedHashMap();
--        for (Map.Entry<String, T> entry : nodes.entrySet()) {
--            String id = entry.getKey();
--            T node = entry.getValue();
++        for (T node : nodes.values()) {
              List<T> tempchain = Lists.newLinkedList();
              
              T nodeinchain = node;
@@@ -703,7 -619,7 +693,7 @@@
      }
  
      private Entity newEntity(String entityId, String entityType, Reflections reflections) {
--        Class<? extends Entity> entityClazz = (Class<? extends Entity>) reflections.loadClass(entityType);
++        Class<? extends Entity> entityClazz = reflections.loadClass(entityType, Entity.class);
          
          if (InternalFactory.isNewStyle(entityClazz)) {
              // Not using entityManager.createEntity(EntitySpec) because don't want init() to be called.
@@@ -748,9 -664,9 +738,9 @@@
       * Constructs a new location, passing to its constructor the location id and all of memento.getFlags().
       */
      private Location newLocation(String locationId, String locationType, Reflections reflections) {
--        Class<? extends Location> locationClazz = (Class<? extends Location>) reflections.loadClass(locationType);
++        Class<? extends Location> locationClazz = reflections.loadClass(locationType, Location.class);
  
--        if (InternalLocationFactory.isNewStyleLocation(managementContext, locationClazz)) {
++        if (InternalFactory.isNewStyle(locationClazz)) {
              // Not using loationManager.createLocation(LocationSpec) because don't want init() to be called
              // TODO Need to rationalise this to move code into methods of InternalLocationFactory.
              //      But note that we'll change all locations to be entities at some point!
@@@ -778,6 -694,6 +768,7 @@@
      /**
       * Constructs a new policy, passing to its constructor the policy id and all of memento.getConfig().
       */
++    @SuppressWarnings("unchecked")
      private Policy newPolicy(PolicyMemento memento, Reflections reflections) {
          String id = memento.getId();
          String policyType = checkNotNull(memento.getType(), "policy type of %s must not be null in memento", id);
@@@ -813,7 -729,7 +804,7 @@@
      private Enricher newEnricher(EnricherMemento memento, Reflections reflections) {
          String id = memento.getId();
          String enricherType = checkNotNull(memento.getType(), "enricher type of %s must not be null in memento", id);
--        Class<? extends Enricher> enricherClazz = (Class<? extends Enricher>) reflections.loadClass(enricherType);
++        Class<? extends Enricher> enricherClazz = reflections.loadClass(enricherType, Enricher.class);
  
          if (InternalFactory.isNewStyle(enricherClazz)) {
              InternalPolicyFactory policyFactory = managementContext.getPolicyFactory();
@@@ -837,13 -753,7 +828,14 @@@
  
              return (Enricher) invokeConstructor(reflections, enricherClazz, new Object[] {flags});
          }
 +    }
  
++    @SuppressWarnings({ "rawtypes" })
 +    private CatalogItem<?, ?> newCatalogItem(CatalogItemMemento memento, Reflections reflections) {
 +        String id = memento.getId();
 +        String itemType = checkNotNull(memento.getType(), "catalog item type of %s must not be null in memento", id);
-         Class<? extends CatalogItem> clazz = (Class<? extends CatalogItem>) reflections.loadClass(itemType);
++        Class<? extends CatalogItem> clazz = reflections.loadClass(itemType, CatalogItem.class);
 +        return invokeConstructor(reflections, clazz, new Object[]{});
      }
  
      private <T> T invokeConstructor(Reflections reflections, Class<T> clazz, Object[]... possibleArgs) {
@@@ -860,20 -770,20 +852,6 @@@
          throw new IllegalStateException("Cannot instantiate instance of type "+clazz+"; expected constructor signature not found");
      }
  
--    private static boolean isNewStylePolicy(Class<?> clazz) {
--        if (!Policy.class.isAssignableFrom(clazz)) {
--            throw new IllegalArgumentException("Class "+clazz+" is not a policy");
--        }
--        return Reflections.hasNoArgConstructor(clazz);
--    }
--    
--    private static boolean isNewStyleEnricher(Class<?> clazz) {
--        if (!Enricher.class.isAssignableFrom(clazz)) {
--            throw new IllegalArgumentException("Class "+clazz+" is not an enricher");
--        }
--        return Reflections.hasNoArgConstructor(clazz);
--    }
--    
      /**
       * Wraps a ChangeListener, to log and never propagate any exceptions that it throws.
       * 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/dto/BrooklynMementoImpl.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/dto/BrooklynMementoManifestImpl.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/entity/rebind/dto/BrooklynMementoManifestImpl.java
index cffff28,ca765f3..e76407f
--- a/core/src/main/java/brooklyn/entity/rebind/dto/BrooklynMementoManifestImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/dto/BrooklynMementoManifestImpl.java
@@@ -114,19 -104,10 +114,19 @@@ public class BrooklynMementoManifestImp
      public Map<String, String> getEnricherIdToType() {
          return Collections.unmodifiableMap(enricherIdToType);
      }
 -    
 +
 +    @Override
-     public Map<String, String> getCatalogIdToType() {
++    public Map<String, String> getCatalogItemIdToType() {
 +        return Collections.unmodifiableMap(catalogItemIdToType);
 +    }
 +
      @Override
      public boolean isEmpty() {
 -        return entityIdToType.isEmpty() && locationIdToType.isEmpty() && policyIdToType.isEmpty() && enricherIdToType.isEmpty();
 +        return entityIdToType.isEmpty() &&
 +                locationIdToType.isEmpty() &&
 +                policyIdToType.isEmpty() &&
 +                enricherIdToType.isEmpty() &&
 +                catalogItemIdToType.isEmpty();
      }
      
  }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
index 6e1f1c0,2613947..e983b01
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
@@@ -146,11 -145,6 +146,11 @@@ public class BrooklynMementoPersisterIn
                          Class<?> clazz = loadClass(manifest.getEnricherIdToType().get(id));
                          return (Enricher) invokeConstructor(clazz, new Object[0], new Object[] {MutableMap.of()});
                      }
 +                    @Override public CatalogItem<?, ?> lookupCatalogItem(String id) {
-                         Class<?> clazz = loadClass(manifest.getCatalogIdToType().get(id));
++                        Class<?> clazz = loadClass(manifest.getCatalogItemIdToType().get(id));
 +                        return (CatalogItem) invokeConstructor(clazz, new Object[0]);
 +                    }
 +
                      private Class<?> loadClass(String name) {
                          try {
                              return classLoader.loadClass(name);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
index 71e6718,e98bfd6..cb87bd4
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@@ -51,7 -50,7 +50,8 @@@ import brooklyn.entity.rebind.persister
  import brooklyn.mementos.BrooklynMemento;
  import brooklyn.mementos.BrooklynMementoManifest;
  import brooklyn.mementos.BrooklynMementoPersister;
+ import brooklyn.mementos.BrooklynMementoRawData;
 +import brooklyn.mementos.CatalogItemMemento;
  import brooklyn.mementos.EnricherMemento;
  import brooklyn.mementos.EntityMemento;
  import brooklyn.mementos.LocationMemento;
@@@ -166,8 -165,143 +167,151 @@@ public class BrooklynMementoPersisterTo
          }
      }
  
+     @Beta
+     public BrooklynMementoRawData loadMementoRawData(final RebindExceptionHandler exceptionHandler) throws IOException {
+         final BrooklynMementoRawData.Builder builder = BrooklynMementoRawData.builder();
+ 
+         Visitor visitor = new Visitor() {
+             @Override
+             public void visit(String contents, BrooklynObjectType type, String subPath) throws Exception {
+                 switch (type) {
+                     case ENTITY:
+                         builder.entity((String) XmlUtil.xpath(contents, "/entity/id"), contents);
+                         break;
+                     case LOCATION:
+                         builder.location((String) XmlUtil.xpath(contents, "/location/id"), contents);
+                         break;
+                     case POLICY:
+                         builder.policy((String) XmlUtil.xpath(contents, "/policy/id"), contents);
+                         break;
+                     case ENRICHER:
+                         builder.enricher((String) XmlUtil.xpath(contents, "/enricher/id"), contents);
+                         break;
++                    case CATALOG_ITEM:
++                        builder.catalogItem((String) XmlUtil.xpath(contents, "/catalogItem/id"), contents);
++                        break;
+                     default:
+                         throw new IllegalStateException("Unexpected brooklyn type: "+type);
+                 }
+             }
+         };
+ 
+         Stopwatch stopwatch = Stopwatch.createStarted();
+ 
+         visitMemento(visitor, exceptionHandler);
+         
+         BrooklynMementoRawData result = builder.build();
+ 
+         if (LOG.isDebugEnabled()) {
+             LOG.debug("Loaded memento manifest; took {}; {} entities, {} locations, {} policies, {} enrichers, from {}", new Object[]{
+                      Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)), result.getEntities().size(), 
+                      result.getLocations().size(), result.getPolicies().size(), result.getEnrichers().size(),
+                      objectStore.getSummaryName() });
+         }
+ 
+         return result;
+     }
+ 
      @Override
      public BrooklynMementoManifest loadMementoManifest(final RebindExceptionHandler exceptionHandler) throws IOException {
+         final BrooklynMementoManifestImpl.Builder builder = BrooklynMementoManifestImpl.builder();
+ 
+         Visitor visitor = new Visitor() {
+             @Override
+             public void visit(String contents, BrooklynObjectType type, String subPath) throws Exception {
+                 switch (type) {
+                     case ENTITY:
+                         String id = (String) XmlUtil.xpath(contents, "/entity/id");
+                         String objType = (String) XmlUtil.xpath(contents, "/entity/type");
+                         builder.entity(id, objType);
+                         break;
+                     case LOCATION:
+                         id = (String) XmlUtil.xpath(contents, "/location/id");
+                         objType = (String) XmlUtil.xpath(contents, "/location/type");
+                         builder.location(id, objType);
+                         break;
+                     case POLICY:
+                         id = (String) XmlUtil.xpath(contents, "/policy/id");
+                         objType = (String) XmlUtil.xpath(contents, "/policy/type");
+                         builder.policy(id, objType);
+                         break;
+                     case ENRICHER:
+                         id = (String) XmlUtil.xpath(contents, "/enricher/id");
+                         objType = (String) XmlUtil.xpath(contents, "/enricher/type");
+                         builder.enricher(id, objType);
+                         break;
++                    case CATALOG_ITEM:
++                        id = (String) XmlUtil.xpath(contents, "/catalogItem/id");
++                        objType = (String) XmlUtil.xpath(contents, "/catalogItem/type");
++                        builder.catalogItem(id, objType);
++                        break;
+                     default:
+                         throw new IllegalStateException("Unexpected brooklyn type: "+type);
+                 }
+             }
+         };
+ 
+         Stopwatch stopwatch = Stopwatch.createStarted();
+ 
+         visitMemento(visitor, exceptionHandler);
+         
+         BrooklynMementoManifest result = builder.build();
+ 
+         if (LOG.isDebugEnabled()) {
 -            LOG.debug("Loaded memento manifest; took {}; {} entities, {} locations, {} policies, {} enrichers, from {}", new Object[]{
++            LOG.debug("Loaded memento manifest; took {}; {} entities, {} locations, {} policies, {} enrichers, {} catalog items, from {}", new Object[]{
+                      Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)), result.getEntityIdToType().size(), 
 -                     result.getLocationIdToType().size(), result.getPolicyIdToType().size(), result.getEnricherIdToType().size(),
++                     result.getLocationIdToType().size(), result.getPolicyIdToType().size(), result.getEnricherIdToType().size(), result.getCatalogItemIdToType().size(),
+                      objectStore.getSummaryName() });
+         }
+ 
+         return result;
+     }
+ 
+     @Override
+     public BrooklynMemento loadMemento(LookupContext lookupContext, final RebindExceptionHandler exceptionHandler) throws IOException {
+         Stopwatch stopwatch = Stopwatch.createStarted();
+ 
+         final BrooklynMementoImpl.Builder builder = BrooklynMementoImpl.builder();
+         
+         Visitor visitor = new Visitor() {
+             @Override
+             public void visit(String contents, BrooklynObjectType type, String subPath) throws Exception {
+                 try {
+                     Memento memento = (Memento) serializer.fromString(contents);
+                     if (memento == null) {
+                         LOG.warn("No "+type.toString().toLowerCase()+"-memento deserialized from " + subPath + "; ignoring and continuing");
+                     } else {
+                         builder.memento(memento);
+                     }
+                 } catch (Exception e) {
+                     exceptionHandler.onLoadMementoFailed(type, "Memento "+subPath, e);
+                 }
+             }
+         };
+ 
+         serializer.setLookupContext(lookupContext);
+         try {
+             visitMemento(visitor, exceptionHandler);
+         } finally {
+             serializer.unsetLookupContext();
+         }
+ 
+         BrooklynMemento result = builder.build();
+         
+         if (LOG.isDebugEnabled()) {
 -            LOG.debug("Loaded memento; took {}; {} entities, {} locations, {} policies, {} enrichers, from {}", new Object[]{
++            LOG.debug("Loaded memento; took {}; {} entities, {} locations, {} policies, {} enrichers, {} catalog items, from {}", new Object[]{
+                       Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)), result.getEntityIds().size(), 
 -                      result.getLocationIds().size(), result.getPolicyIds().size(), result.getEnricherIds().size(),
++                      result.getLocationIds().size(), result.getPolicyIds().size(), result.getEnricherIds().size(), result.getCatalogItemIds().size(),
+                       objectStore.getSummaryName() });
+         }
+         
+         return result;
+     }
+     
+     protected interface Visitor {
+         public void visit(String contents, BrooklynObjectType type, String subPath) throws Exception;
+     }
+     protected void visitMemento(final Visitor visitor, final RebindExceptionHandler exceptionHandler) throws IOException {
          if (!running) {
              throw new IllegalStateException("Persister not running; cannot load memento manifest from " + objectStore.getSummaryName());
          }
@@@ -189,87 -321,42 +333,46 @@@
              throw new IllegalStateException("Failed to list memento files in "+objectStore, e);
          }
  
-         Stopwatch stopwatch = Stopwatch.createStarted();
- 
 -        LOG.debug("Scanning persisted state: {} entities, {} locations, {} policies, {} enrichers, from {}", new Object[]{
 -            entitySubPathList.size(), locationSubPathList.size(), policySubPathList.size(), enricherSubPathList.size(),
 +        LOG.debug("Scanning persisted state: {} entities, {} locations, {} policies, {} enrichers, {} catalog items from {}", new Object[]{
 +            entitySubPathList.size(), locationSubPathList.size(), policySubPathList.size(), enricherSubPathList.size(), catalogSubPathList.size(),
              objectStore.getSummaryName() });
  
-         final BrooklynMementoManifestImpl.Builder builder = BrooklynMementoManifestImpl.builder();
- 
          List<ListenableFuture<?>> futures = Lists.newArrayList();
          
+         class VisitorWrapper implements Runnable {
+             private final String subPath;
+             private final BrooklynObjectType type;
+             public VisitorWrapper(String subPath, BrooklynObjectType type) {
+                 this.subPath = subPath;
+                 this.type = type;
+             }
+             public void run() {
+                 try {
+                     String contents = read(subPath);
+                     visitor.visit(contents, type, subPath);
+                 } catch (Exception e) {
+                     Exceptions.propagateIfFatal(e);
+                     exceptionHandler.onLoadMementoFailed(type, "Memento "+subPath, e);
+                 }
+             }
+         }
+         
          for (final String subPath : entitySubPathList) {
-             futures.add(executor.submit(new Runnable() {
-                 public void run() {
-                     try {
-                         String contents = read(subPath);
-                         String id = (String) XmlUtil.xpath(contents, "/entity/id");
-                         String type = (String) XmlUtil.xpath(contents, "/entity/type");
-                         builder.entity(id, type);
-                     } catch (Exception e) {
-                         Exceptions.propagateIfFatal(e);
-                         exceptionHandler.onLoadMementoFailed(BrooklynObjectType.ENTITY, "Memento "+subPath, e);
-                     }
-                 }}));
+             futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.ENTITY)));
          }
          for (final String subPath : locationSubPathList) {
-             futures.add(executor.submit(new Runnable() {
-                 public void run() {
-                     try {
-                         String contents = read(subPath);
-                         String id = (String) XmlUtil.xpath(contents, "/location/id");
-                         String type = (String) XmlUtil.xpath(contents, "/location/type");
-                         builder.location(id, type);
-                     } catch (Exception e) {
-                         Exceptions.propagateIfFatal(e);
-                         exceptionHandler.onLoadMementoFailed(BrooklynObjectType.LOCATION, "Memento "+subPath, e);
-                     }
-                 }}));
+             futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.LOCATION)));
          }
          for (final String subPath : policySubPathList) {
-             futures.add(executor.submit(new Runnable() {
-                 public void run() {
-                     try {
-                         String contents = read(subPath);
-                         String id = (String) XmlUtil.xpath(contents, "/policy/id");
-                         String type = (String) XmlUtil.xpath(contents, "/policy/type");
-                         builder.policy(id, type);
-                     } catch (Exception e) {
-                         Exceptions.propagateIfFatal(e);
-                         exceptionHandler.onLoadMementoFailed(BrooklynObjectType.POLICY, "Memento "+subPath, e);
-                     }
-                 }}));
+             futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.POLICY)));
          }
          for (final String subPath : enricherSubPathList) {
-             futures.add(executor.submit(new Runnable() {
-                 public void run() {
-                     try {
-                         String contents = read(subPath);
-                         String id = (String) XmlUtil.xpath(contents, "/enricher/id");
-                         String type = (String) XmlUtil.xpath(contents, "/enricher/type");
-                         builder.enricher(id, type);
-                     } catch (Exception e) {
-                         Exceptions.propagateIfFatal(e);
-                         exceptionHandler.onLoadMementoFailed(BrooklynObjectType.ENRICHER, "Memento "+subPath, e);
-                     }
-                 }}));
+             futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.ENRICHER)));
          }
 +        for (final String subPath : catalogSubPathList) {
-             futures.add(executor.submit(new Runnable() {
-                 public void run() {
-                     try {
-                         String contents = read(subPath);
-                         String id = (String) XmlUtil.xpath(contents, "/catalogItem/id");
-                         String type = (String) XmlUtil.xpath(contents, "/catalogItem/type");
-                         builder.catalogItem(id, type);
-                     } catch (Exception e) {
-                         Exceptions.propagateIfFatal(e);
-                         exceptionHandler.onLoadMementoFailed(BrooklynObjectType.CATALOG_ITEM, "Memento "+subPath, e);
-                     }
-                 }}));
++            futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.CATALOG_ITEM)));
 +        }
 +
  
          try {
              // Wait for all, failing fast if any exceptions.
@@@ -299,141 -386,56 +402,60 @@@
                  throw new CompoundRuntimeException("Problem loading mementos", exceptions);
              }
          }
- 
-         BrooklynMementoManifest result = builder.build();
- 
-         if (LOG.isDebugEnabled()) {
-             LOG.debug("Loaded memento manifest; took {}; {} entities, {} locations, {} policies, {} enrichers, {} catalog items, from {}",
-                     new Object[]{
-                      Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)), result.getEntityIdToType().size(), 
-                      result.getLocationIdToType().size(), result.getPolicyIdToType().size(), result.getEnricherIdToType().size(),
-                      result.getCatalogIdToType().size(),
-                      objectStore.getSummaryName() });
-         }
- 
-         if (result.getEntityIdToType().size() != entitySubPathList.size()) {
-             LOG.error("Lost an entity?!");
-         }
-         
-         return result;
      }
  
-     @Override
-     public BrooklynMemento loadMemento(LookupContext lookupContext, final RebindExceptionHandler exceptionHandler) throws IOException {
+     @Beta
+     public void checkpoint(BrooklynMementoRawData newMemento, PersistenceExceptionHandler exceptionHandler) {
          if (!running) {
-             throw new IllegalStateException("Persister not running; cannot load memento from " + objectStore.getSummaryName());
+             if (LOG.isDebugEnabled()) LOG.debug("Ignoring checkpointing entire memento, because not running");
+             return;
          }
-         Stopwatch stopwatch = Stopwatch.createStarted();
- 
-         List<String> entitySubPathList;
-         List<String> locationSubPathList;
-         List<String> policySubPathList;
-         List<String> enricherSubPathList;
-         List<String> catalogItemSubPathList;
+         
          try {
-             entitySubPathList = objectStore.listContentsWithSubPath("entities");
-             locationSubPathList = objectStore.listContentsWithSubPath("locations");
-             policySubPathList = objectStore.listContentsWithSubPath("policies");
-             enricherSubPathList = objectStore.listContentsWithSubPath("enrichers");
-             catalogItemSubPathList = objectStore.listContentsWithSubPath("catalog");
-         } catch (Exception e) {
-             Exceptions.propagateIfFatal(e);
-             exceptionHandler.onLoadMementoFailed(BrooklynObjectType.UNKNOWN, "Failed to list files", e);
-             throw new IllegalStateException("Failed to list memento files in "+objectStore+": "+e, e);
+             lock.writeLock().lockInterruptibly();
+         } catch (InterruptedException e) {
+             throw Exceptions.propagate(e);
          }
 +        
-         LOG.debug("Loading persisted state: {} entities, {} locations, {} policies, {} enrichers, {} catalog items, from {}",
-                 new Object[]{ entitySubPathList.size(), locationSubPathList.size(), policySubPathList.size(),
-                         enricherSubPathList.size(), catalogItemSubPathList.size(), objectStore.getSummaryName() });
- 
-         final BrooklynMementoImpl.Builder builder = BrooklynMementoImpl.builder();
-         serializer.setLookupContext(lookupContext);
-         
-         List<ListenableFuture<?>> futures = Lists.newArrayList();
- 
-         class MementoLoader implements Runnable {
-             private final String subPath;
-             private final BrooklynObjectType type;
-             public MementoLoader(String subPath, BrooklynObjectType type) {
-                 this.subPath = subPath;
-                 this.type = type;
-             }
-             public void run() {
-                 try {
-                     Memento memento = (Memento) serializer.fromString(read(subPath));
-                     if (memento == null) {
-                         LOG.warn("No "+type.toString().toLowerCase()+"-memento deserialized from " + subPath + "; ignoring and continuing");
-                     } else {
-                         builder.memento(memento);
-                     }
-                 } catch (Exception e) {
-                     exceptionHandler.onLoadMementoFailed(type, "Memento "+subPath, e);
-                 }
-             }
-         }
- 
          try {
-             for (final String subPath : entitySubPathList) {
-                 futures.add(executor.submit(new MementoLoader(subPath, BrooklynObjectType.ENTITY)));
+             objectStore.prepareForMasterUse();
+             
+             Stopwatch stopwatch = Stopwatch.createStarted();
+             List<ListenableFuture<?>> futures = Lists.newArrayList();
+             
+             for (Map.Entry<String, String> entry : newMemento.getEntities().entrySet()) {
+                 futures.add(asyncPersist("entities", BrooklynObjectType.ENTITY, entry.getKey(), entry.getValue(), exceptionHandler));
              }
-             for (final String subPath : locationSubPathList) {
-                 futures.add(executor.submit(new MementoLoader(subPath, BrooklynObjectType.LOCATION)));
+             for (Map.Entry<String, String> entry : newMemento.getLocations().entrySet()) {
+                 futures.add(asyncPersist("locations", BrooklynObjectType.LOCATION, entry.getKey(), entry.getValue(), exceptionHandler));
              }
-             for (final String subPath : policySubPathList) {
-                 futures.add(executor.submit(new MementoLoader(subPath, BrooklynObjectType.POLICY)));
+             for (Map.Entry<String, String> entry : newMemento.getPolicies().entrySet()) {
+                 futures.add(asyncPersist("policies", BrooklynObjectType.POLICY, entry.getKey(), entry.getValue(), exceptionHandler));
              }
-             for (final String subPath : enricherSubPathList) {
-                 futures.add(executor.submit(new MementoLoader(subPath, BrooklynObjectType.ENRICHER)));
+             for (Map.Entry<String, String> entry : newMemento.getEnrichers().entrySet()) {
+                 futures.add(asyncPersist("enrichers", BrooklynObjectType.ENRICHER, entry.getKey(), entry.getValue(), exceptionHandler));
              }
-             for (final String subPath : catalogItemSubPathList) {
-                 futures.add(executor.submit(new MementoLoader(subPath, BrooklynObjectType.CATALOG_ITEM)));
 -            
++            for (Map.Entry<String, String> entry : newMemento.getCatalogItems().entrySet()) {
++                futures.add(asyncPersist("catalog", BrooklynObjectType.CATALOG_ITEM, entry.getKey(), entry.getValue(), exceptionHandler));
 +            }
 +
              try {
-                 // Wait for all, failing fast if any exceptions.
+                 // Wait for all the tasks to complete or fail, rather than aborting on the first failure.
+                 // But then propagate failure if any fail. (hence the two calls).
+                 Futures.successfulAsList(futures).get();
                  Futures.allAsList(futures).get();
              } catch (Exception e) {
-                 Exceptions.propagateIfFatal(e);
-                 
-                 List<Exception> exceptions = Lists.newArrayList();
-                 
-                 for (ListenableFuture<?> future : futures) {
-                     if (future.isDone()) {
-                         try {
-                             future.get();
-                         } catch (InterruptedException e2) {
-                             throw Exceptions.propagate(e2);
-                         } catch (ExecutionException e2) {
-                             LOG.warn("Problem loading memento", e2);
-                             exceptions.add(e2);
-                         }
-                         future.cancel(true);
-                     }
-                 }
-                 if (exceptions.isEmpty()) {
-                     throw Exceptions.propagate(e);
-                 } else {
-                     // Normally there should be at least one failure; otherwise all.get() would not have failed.
-                     throw new CompoundRuntimeException("Problem loading mementos", exceptions);
-                 }
++                // TODO is the logging here as good as it was prior to https://github.com/apache/incubator-brooklyn/pull/177/files ?
+                 throw Exceptions.propagate(e);
              }
- 
+             
+             if (LOG.isDebugEnabled()) LOG.debug("Checkpointed entire memento in {}", Time.makeTimeStringRounded(stopwatch));
          } finally {
-             serializer.unsetLookupContext();
-         }
- 
-         BrooklynMemento result = builder.build();
-         
-         if (LOG.isDebugEnabled()) {
-             LOG.debug("Loaded memento; took {}; {} entities, {} locations, {} policies, {} enrichers, {} catalog items, from {}",
-                     new Object[]{Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)),
-                             result.getEntityIds().size(), result.getLocationIds().size(), result.getPolicyIds().size(),
-                             result.getEnricherIds().size(), result.getCatalogItemIds().size(), objectStore.getSummaryName()});
+             lock.writeLock().unlock();
          }
-         
-         return result;
 -
      }
-     
+ 
+ 
      @Override
      public void checkpoint(BrooklynMemento newMemento, PersistenceExceptionHandler exceptionHandler) {
          if (!running) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/transformer/BrooklynMementoTransformer.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/entity/rebind/transformer/BrooklynMementoTransformer.java
index 0000000,0a6fbb8..3e3638b
mode 000000,100644..100644
--- a/core/src/main/java/brooklyn/entity/rebind/transformer/BrooklynMementoTransformer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/transformer/BrooklynMementoTransformer.java
@@@ -1,0 -1,14 +1,32 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
+ package brooklyn.entity.rebind.transformer;
+ 
+ import brooklyn.mementos.BrooklynMemento;
+ 
+ import com.google.common.annotations.Beta;
+ 
+ /**
+  * Transforms the raw data of persisted state (e.g. of an entity).
+  */
+ @Beta
+ public interface BrooklynMementoTransformer {
+ 
+     public BrooklynMemento transform(BrooklynMemento input) throws Exception;
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java
index 0000000,6cac668..f146162
mode 000000,100644..100644
--- a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformer.java
@@@ -1,0 -1,159 +1,188 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
+ package brooklyn.entity.rebind.transformer;
+ 
+ import static com.google.common.base.Preconditions.checkNotNull;
+ 
+ import java.util.Collection;
+ import java.util.Map;
+ 
+ import brooklyn.entity.rebind.BrooklynObjectType;
+ import brooklyn.entity.rebind.RebindExceptionHandler;
+ import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore;
+ import brooklyn.entity.rebind.transformer.impl.XsltTransformer;
 -import brooklyn.entity.rebind.transformer.impl.XsltTransformerTest;
+ import brooklyn.mementos.BrooklynMementoRawData;
+ import brooklyn.util.ResourceUtils;
+ import brooklyn.util.collections.MutableMap;
+ import brooklyn.util.text.TemplateProcessor;
+ 
+ import com.google.api.client.repackaged.com.google.common.annotations.VisibleForTesting;
+ import com.google.common.annotations.Beta;
+ import com.google.common.collect.ArrayListMultimap;
+ import com.google.common.collect.ImmutableMap;
+ import com.google.common.collect.Multimap;
+ 
+ @Beta
+ public class CompoundTransformer {
+     
+     public static final CompoundTransformer NOOP = builder().build();
+     
+     // TODO Does not yet handle BrooklynMementoTransformer, for changing an entire BrooklynMemento.
+     // Need to do refactoring in RebindManager and BrooklynMementoPersisterToObjectStore to convert 
+     // from a BrooklynMementoRawData to a BrooklynMemento.
+     
+     public static Builder builder() {
+         return new Builder();
+     }
+ 
+     public static class Builder {
+         private final Multimap<BrooklynObjectType, RawDataTransformer> rawDataTransformers = ArrayListMultimap.<BrooklynObjectType, RawDataTransformer>create();
+         
+         public Builder rawDataTransformer(RawDataTransformer val) {
+             for (BrooklynObjectType type : BrooklynObjectType.values()) {
+                 rawDataTransformer(type, val);
+             }
+             return this;
+         }
+         public Builder rawDataTransformer(BrooklynObjectType type, RawDataTransformer val) {
+             rawDataTransformers.put(checkNotNull(type, "type"), checkNotNull(val, "val"));
+             return this;
+         }
+         public Builder renameType(String oldVal, String newVal) {
+             // xstream format for inner classes is like <brooklyn.entity.rebind.transformer.CompoundTransformerTest_-OrigType>
+             oldVal = toXstreamClassnameFormat(oldVal);
+             newVal = toXstreamClassnameFormat(newVal);
+             
 -            String xsltTemplate = ResourceUtils.create(XsltTransformerTest.class).getResourceAsString("classpath://brooklyn/entity/rebind/transformer/renameType.xslt");
++            String xsltTemplate = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/entity/rebind/transformer/renameType.xslt");
+             String xslt = TemplateProcessor.processTemplateContents(xsltTemplate, ImmutableMap.of("old_val", oldVal, "new_val", newVal));
+             return xsltTransformer(xslt);
+         }
+         public Builder renameClass(String oldVal, String newVal) {
 -            // xstream format for inner classes is like <brooklyn.entity.rebind.transformer.CompoundTransfor<merTest_-OrigType>
++            // xstream format for inner classes is like <brooklyn.entity.rebind.transformer.CompoundTransformerTest_-OrigType>
+             oldVal = toXstreamClassnameFormat(oldVal);
+             newVal = toXstreamClassnameFormat(newVal);
+             
 -            String xsltTemplate = ResourceUtils.create(XsltTransformerTest.class).getResourceAsString("classpath://brooklyn/entity/rebind/transformer/renameClass.xslt");
++            String xsltTemplate = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/entity/rebind/transformer/renameClass.xslt");
+             String xslt = TemplateProcessor.processTemplateContents(xsltTemplate, ImmutableMap.of("old_val", oldVal, "new_val", newVal));
+             return xsltTransformer(xslt);
+         }
+         public Builder renameField(String clazz, String oldVal, String newVal) {
 -            // xstream format for inner classes is like <brooklyn.entity.rebind.transformer.CompoundTransfor<merTest_-OrigType>
++            // xstream format for inner classes is like <brooklyn.entity.rebind.transformer.CompoundTransformerTest_-OrigType>
+             clazz = toXstreamClassnameFormat(clazz);
+             oldVal = toXstreamClassnameFormat(oldVal);
+             newVal = toXstreamClassnameFormat(newVal);
+             
 -            String xsltTemplate = ResourceUtils.create(XsltTransformerTest.class).getResourceAsString("classpath://brooklyn/entity/rebind/transformer/renameClass.xslt");
++            String xsltTemplate = ResourceUtils.create(this).getResourceAsString("classpath://brooklyn/entity/rebind/transformer/renameField.xslt");
+             String xslt = TemplateProcessor.processTemplateContents(xsltTemplate, ImmutableMap.of("class_name", clazz, "old_val", oldVal, "new_val", newVal));
+             return xsltTransformer(xslt);
+         }
++        /** registers the given XSLT code to be applied to all persisted {@link BrooklynObjectType}s */
+         public Builder xsltTransformer(String xslt) {
+             XsltTransformer xsltTransformer = new XsltTransformer(xslt);
+             for (BrooklynObjectType type : BrooklynObjectType.values()) {
+                 rawDataTransformer(type, xsltTransformer);
+             }
+             return this;
+         }
++        /** registers the given XSLT code to be applied to the given persisted {@link BrooklynObjectType}s */
+         public Builder xsltTransformer(BrooklynObjectType type, String xslt) {
+             XsltTransformer xsltTransformer = new XsltTransformer(xslt);
+             rawDataTransformer(type, xsltTransformer);
+             return this;
+         }
+         private String toXstreamClassnameFormat(String val) {
+             return (val.contains("$")) ? val.replace("$", "_-") : val;
+         }
+         public CompoundTransformer build() {
+             return new CompoundTransformer(this);
+         }
+     }
+ 
+     private final Multimap<BrooklynObjectType, RawDataTransformer> rawDataTransformers;
+     
+     protected CompoundTransformer(Builder builder) {
+         rawDataTransformers = builder.rawDataTransformers;
+     }
+ 
+     public BrooklynMementoRawData transform(BrooklynMementoPersisterToObjectStore reader, RebindExceptionHandler exceptionHandler) throws Exception {
+         BrooklynMementoRawData rawData = reader.loadMementoRawData(exceptionHandler);
+         return transform(rawData);
+     }
+     
+     public BrooklynMementoRawData transform(BrooklynMementoRawData rawData) throws Exception {
+         Map<String, String> entities = MutableMap.copyOf(rawData.getEntities());
+         Map<String, String> locations = MutableMap.copyOf(rawData.getLocations());
+         Map<String, String> policies = MutableMap.copyOf(rawData.getPolicies());
+         Map<String, String> enrichers = MutableMap.copyOf(rawData.getEnrichers());
++        Map<String, String> catalogItems = MutableMap.copyOf(rawData.getCatalogItems());
+ 
++        // TODO @neykov asks whether transformers should be run in registration order,
++        // rather than in type order.  TBD.  (would be an easy change.)
++        // (they're all strings so it shouldn't matter!)
++        
+         for (BrooklynObjectType type : BrooklynObjectType.values()) {
+             Collection<RawDataTransformer> transformers = rawDataTransformers.get(type);
+             for (RawDataTransformer transformer : transformers) {
+                 switch (type) {
+                     case ENTITY:
+                         for (Map.Entry<String, String> entry : entities.entrySet()) {
+                             entry.setValue(transformer.transform(entry.getValue()));
+                         }
+                         break;
+                     case LOCATION:
+                         for (Map.Entry<String, String> entry : locations.entrySet()) {
+                             entry.setValue(transformer.transform(entry.getValue()));
+                         }
+                         break;
+                     case POLICY:
+                         for (Map.Entry<String, String> entry : policies.entrySet()) {
+                             entry.setValue(transformer.transform(entry.getValue()));
+                         }
+                         break;
+                     case ENRICHER:
+                         for (Map.Entry<String, String> entry : enrichers.entrySet()) {
+                             entry.setValue(transformer.transform(entry.getValue()));
+                         }
+                         break;
++                    case CATALOG_ITEM:
++                        for (Map.Entry<String, String> entry : catalogItems.entrySet()) {
++                            entry.setValue(transformer.transform(entry.getValue()));
++                        }
++                        break;
+                     case UNKNOWN:
+                         break; // no-op
+                     default:
+                         throw new IllegalStateException("Unexpected brooklyn object type "+type);
+                     }
+             }
+         }
+         
+         return BrooklynMementoRawData.builder()
+                 .entities(entities)
+                 .locations(locations)
+                 .policies(policies)
+                 .enrichers(enrichers)
+                 .build();
+     }
+     
+     @VisibleForTesting
+     Multimap<BrooklynObjectType, RawDataTransformer> getRawDataTransformers() {
+         return ArrayListMultimap.create(rawDataTransformers);
+     }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java
index 0000000,98ac3b5..9d3cac1
mode 000000,100644..100644
--- a/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java
+++ b/core/src/main/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoader.java
@@@ -1,0 -1,65 +1,86 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
+ package brooklyn.entity.rebind.transformer;
+ 
+ import java.util.Map;
+ 
+ import brooklyn.entity.rebind.transformer.CompoundTransformer.Builder;
 -import brooklyn.entity.rebind.transformer.impl.XsltTransformerTest;
+ import brooklyn.util.ResourceUtils;
+ import brooklyn.util.exceptions.Exceptions;
+ import brooklyn.util.text.TemplateProcessor;
+ import brooklyn.util.yaml.Yamls;
+ 
+ import com.google.common.annotations.Beta;
+ import com.google.common.collect.ImmutableMap;
+ import com.google.common.collect.Iterables;
+ 
+ @Beta
+ public class CompoundTransformerLoader {
+ 
+     // TODO Improve error handing so get nicer errors.
+     // TODO Improve names (e.g. always camel case?)
+     // TODO Pass in classloader for reflectively loading rawDataTransformer?
+     
+     public static CompoundTransformer load(String contents) {
+         CompoundTransformer.Builder builder = CompoundTransformer.builder();
+         Iterable<Object> toplevel = Yamls.parseAll(contents);
++        @SuppressWarnings("unchecked")
+         Map<String, Map<?,?>> rules = (Map<String, Map<?,?>>) ((Map<?,?>)Iterables.getOnlyElement(toplevel));
+         for (Map.Entry<String, Map<?,?>> entry : rules.entrySet()) {
+             addRule(builder, entry.getKey(), entry.getValue());
+         }
+         return builder.build();
+     }
+ 
+     private static void addRule(Builder builder, String name, Map<?,?> args) {
+         if (name.equals("renameClass")) {
+             String oldVal = (String) args.get("old_val");
+             String newVal = (String) args.get("new_val");
+             builder.renameClass(oldVal, newVal);
+         } else if (name.equals("renameType")) {
+             String oldVal = (String) args.get("old_val");
+             String newVal = (String) args.get("new_val");
+             builder.renameType(oldVal, newVal);
+         } else if (name.equals("renameField")) {
+             String clazz = (String) args.get("class_name");
+             String oldVal = (String) args.get("old_val");
+             String newVal = (String) args.get("new_val");
+             builder.renameField(clazz, oldVal, newVal);
+         } else if (name.equals("xslt")) {
+             String url = (String) args.get("url");
++            @SuppressWarnings("unchecked")
+             Map<String,?> substitutions = (Map<String, ?>) args.get("substitutions");
 -            String xsltTemplate = ResourceUtils.create(XsltTransformerTest.class).getResourceAsString(url);
++            String xsltTemplate = ResourceUtils.create(CompoundTransformer.class).getResourceAsString(url);
+             String xslt = TemplateProcessor.processTemplateContents(xsltTemplate, substitutions == null ? ImmutableMap.<String, String>of() : substitutions);
++            // TODO pass XSLT-style parameters instead, maybe?  that's more normal, 
++            // but OTOH freemarker is maybe more powerful, given our other support there
+             builder.xsltTransformer(xslt);
+         } else if (name.equals("rawDataTransformer")) {
+             String type = (String) args.get("type");
+             try {
+                 Class<?> clazz = CompoundTransformerLoader.class.getClassLoader().loadClass(type);
+                 builder.rawDataTransformer((RawDataTransformer) clazz.newInstance());
+             } catch (Exception e) {
+                 throw Exceptions.propagate(e);
+             }
+         } else {
+             throw new IllegalStateException("Unsupported transform '"+name+"' ("+args+")");
+         }
+     }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/transformer/RawDataTransformer.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/entity/rebind/transformer/RawDataTransformer.java
index 0000000,b10cc52..196f8a1
mode 000000,100644..100644
--- a/core/src/main/java/brooklyn/entity/rebind/transformer/RawDataTransformer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/transformer/RawDataTransformer.java
@@@ -1,0 -1,12 +1,30 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
+ package brooklyn.entity.rebind.transformer;
+ 
+ import com.google.common.annotations.Beta;
+ 
+ /**
+  * Transforms the raw data of persisted state (e.g. of an entity).
+  */
+ @Beta
+ public interface RawDataTransformer {
+ 
+     public String transform(String input) throws Exception;
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/transformer/impl/DeleteOrphanedLocationsTransformer.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/entity/rebind/transformer/impl/DeleteOrphanedLocationsTransformer.java
index 0000000,41b2065..4d1c3b1
mode 000000,100644..100644
--- a/core/src/main/java/brooklyn/entity/rebind/transformer/impl/DeleteOrphanedLocationsTransformer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/transformer/impl/DeleteOrphanedLocationsTransformer.java
@@@ -1,0 -1,106 +1,125 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
+ package brooklyn.entity.rebind.transformer.impl;
+ 
+ import java.util.Collection;
+ import java.util.List;
+ import java.util.Set;
+ 
+ import brooklyn.entity.rebind.dto.BrooklynMementoImpl;
+ import brooklyn.entity.rebind.transformer.BrooklynMementoTransformer;
+ import brooklyn.mementos.BrooklynMemento;
+ import brooklyn.mementos.EntityMemento;
+ import brooklyn.mementos.LocationMemento;
+ import brooklyn.util.collections.MutableList;
+ import brooklyn.util.collections.MutableMap;
+ import brooklyn.util.collections.MutableSet;
+ 
+ import com.google.common.annotations.Beta;
+ import com.google.common.collect.Lists;
+ import com.google.common.collect.Sets;
+ 
+ @Beta
+ public class DeleteOrphanedLocationsTransformer implements BrooklynMementoTransformer {
+ 
+     // TODO Work in progress; untested code!
+     
+     public BrooklynMemento transform(BrooklynMemento input) throws Exception {
+         Set<String> referencedLocationIds = findReferencedLocationIds(input);
+         Set<String> unreferencedLocationIds = Sets.newLinkedHashSet();
+         List<String> toCheck = Lists.newLinkedList(input.getLocationIds());
+         
+         while (!toCheck.isEmpty()) {
+             String locationId = toCheck.remove(0);
+             List<String> locationsInHierarchy = MutableList.<String>builder()
+                     .add(locationId)
+                     .addAll(findLocationAncestors(input, locationId))
+                     .addAll(findLocationDescendents(input, locationId))
+                     .build();
+             
+             if (containsAny(referencedLocationIds, locationsInHierarchy)) {
+                 // keep them all
+             } else {
+                 unreferencedLocationIds.addAll(locationsInHierarchy);
+             }
+             toCheck.removeAll(locationsInHierarchy);
+         }
+         
+         // TODO What about brooklyn version?
+         return BrooklynMementoImpl.builder()
+                 .applicationIds(input.getApplicationIds())
+                 .topLevelLocationIds(MutableSet.<String>builder()
+                         .addAll(input.getTopLevelLocationIds())
+                         .removeAll(unreferencedLocationIds)
+                         .build())
+                 .entities(input.getEntityMementos())
+                 .locations(MutableMap.<String, LocationMemento>builder()
+                         .putAll(input.getLocationMementos())
+                         .removeAll(unreferencedLocationIds)
+                         .build())
+                 .policies(input.getPolicyMementos())
 -                .enricheres(input.getEnricherMementos())
++                .enrichers(input.getEnricherMementos())
++                .catalogItems(input.getCatalogItemMementos())
+                 .build();
+     }
+     
+     public boolean containsAny(Collection<?> container, Iterable<?> contenders) {
+         for (Object contender : contenders) {
+             if (container.contains(contender)) return true;
+         }
+         return false;
+     }
+     
+     public Set<String> findReferencedLocationIds(BrooklynMemento input) {
+         Set<String> result = Sets.newLinkedHashSet();
+         
+         for (EntityMemento entity : input.getEntityMementos().values()) {
+             result.addAll(entity.getLocations());
+         }
+         return result;
+     }
+     
+     public Set<String> findLocationAncestors(BrooklynMemento input, String locationId) {
+         Set<String> result = Sets.newLinkedHashSet();
+         
+         String parentId = null;
+         do {
+             LocationMemento memento = input.getLocationMemento(locationId);
+             parentId = memento.getParent();
+             if (parentId != null) result.add(parentId);
+         } while (parentId != null);
+ 
+         return result;
+     }
+     
+     public Set<String> findLocationDescendents(BrooklynMemento input, String locationId) {
+         Set<String> result = Sets.newLinkedHashSet();
+         List<String> tovisit = Lists.newLinkedList();
+         
+         tovisit.add(locationId);
+         while (!tovisit.isEmpty()) {
+             LocationMemento memento = input.getLocationMemento(tovisit.remove(0));
+             List<String> children = memento.getChildren();
+             result.addAll(children);
+             tovisit.addAll(children);
+         };
+ 
+         return result;
+     }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/java/brooklyn/entity/rebind/transformer/impl/XsltTransformer.java
----------------------------------------------------------------------
diff --cc core/src/main/java/brooklyn/entity/rebind/transformer/impl/XsltTransformer.java
index 0000000,344a2a8..feed847
mode 000000,100644..100644
--- a/core/src/main/java/brooklyn/entity/rebind/transformer/impl/XsltTransformer.java
+++ b/core/src/main/java/brooklyn/entity/rebind/transformer/impl/XsltTransformer.java
@@@ -1,0 -1,39 +1,57 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
+ package brooklyn.entity.rebind.transformer.impl;
+ 
+ import java.io.ByteArrayInputStream;
+ import java.io.ByteArrayOutputStream;
+ import java.io.IOException;
+ import java.net.URISyntaxException;
+ 
+ import javax.xml.transform.Source;
+ import javax.xml.transform.Transformer;
+ import javax.xml.transform.TransformerException;
+ import javax.xml.transform.TransformerFactory;
+ import javax.xml.transform.stream.StreamResult;
+ import javax.xml.transform.stream.StreamSource;
+ 
+ import brooklyn.entity.rebind.transformer.RawDataTransformer;
+ 
+ import com.google.common.annotations.Beta;
+ 
+ @Beta
+ public class XsltTransformer implements RawDataTransformer {
+ 
+     private final TransformerFactory factory;
+     private final StreamSource xslt;
+ 
+     public XsltTransformer(String xsltContent) {
+         factory = TransformerFactory.newInstance();
+         xslt = new StreamSource(new ByteArrayInputStream(xsltContent.getBytes()));
+     }
+     
+     public String transform(String input) throws IOException, URISyntaxException, TransformerException {
+         Transformer transformer = factory.newTransformer(xslt);
+         
+         Source text = new StreamSource(new ByteArrayInputStream(input.getBytes()));
+         ByteArrayOutputStream baos = new ByteArrayOutputStream(input.length());
+         transformer.transform(text, new StreamResult(baos));
+         
+         return new String(baos.toByteArray());
+     }
+ }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/resources/brooklyn/entity/rebind/transformer/renameClass.xslt
----------------------------------------------------------------------
diff --cc core/src/main/resources/brooklyn/entity/rebind/transformer/renameClass.xslt
index 0000000,0c1e251..a0800fd
mode 000000,100644..100644
--- a/core/src/main/resources/brooklyn/entity/rebind/transformer/renameClass.xslt
+++ b/core/src/main/resources/brooklyn/entity/rebind/transformer/renameClass.xslt
@@@ -1,0 -1,17 +1,35 @@@
+ <?xml version="1.0" encoding="ISO-8859-1"?>
++<!--
++    Licensed to the Apache Software Foundation (ASF) under one
++    or more contributor license agreements.  See the NOTICE file
++    distributed with this work for additional information
++    regarding copyright ownership.  The ASF licenses this file
++    to you under the Apache License, Version 2.0 (the
++    "License"); you may not use this file except in compliance
++    with the License.  You may obtain a copy of the License at
++    
++     http://www.apache.org/licenses/LICENSE-2.0
++    
++    Unless required by applicable law or agreed to in writing,
++    software distributed under the License is distributed on an
++    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++    KIND, either express or implied.  See the License for the
++    specific language governing permissions and limitations
++    under the License.
++-->
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ 
+   <xsl:output omit-xml-declaration="yes"/>
+   
+   <xsl:template match="node()|@*">
+     <xsl:copy>
+       <xsl:apply-templates select="node()|@*"/>
+     </xsl:copy>
+   </xsl:template>
+ 
+   <xsl:template match="${old_val}">
+     <${new_val}><xsl:apply-templates select="@*|node()" /></${new_val}>
+   </xsl:template>
+ 
+ </xsl:stylesheet>
+ 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/resources/brooklyn/entity/rebind/transformer/renameField.xslt
----------------------------------------------------------------------
diff --cc core/src/main/resources/brooklyn/entity/rebind/transformer/renameField.xslt
index 0000000,3238d87..d4b3354
mode 000000,100644..100644
--- a/core/src/main/resources/brooklyn/entity/rebind/transformer/renameField.xslt
+++ b/core/src/main/resources/brooklyn/entity/rebind/transformer/renameField.xslt
@@@ -1,0 -1,17 +1,35 @@@
+ <?xml version="1.0" encoding="ISO-8859-1"?>
++<!--
++    Licensed to the Apache Software Foundation (ASF) under one
++    or more contributor license agreements.  See the NOTICE file
++    distributed with this work for additional information
++    regarding copyright ownership.  The ASF licenses this file
++    to you under the Apache License, Version 2.0 (the
++    "License"); you may not use this file except in compliance
++    with the License.  You may obtain a copy of the License at
++    
++     http://www.apache.org/licenses/LICENSE-2.0
++    
++    Unless required by applicable law or agreed to in writing,
++    software distributed under the License is distributed on an
++    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++    KIND, either express or implied.  See the License for the
++    specific language governing permissions and limitations
++    under the License.
++-->
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ 
+   <xsl:output omit-xml-declaration="yes"/>
+   
+   <xsl:template match="node()|@*">
+     <xsl:copy>
+       <xsl:apply-templates select="node()|@*"/>
+     </xsl:copy>
+   </xsl:template>
+ 
+   <xsl:template match="${class_name}/${old_val}">
+     <${new_val}><xsl:apply-templates select="@*|node()" /></${new_val}>
+   </xsl:template>
+ 
+ </xsl:stylesheet>
+ 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/main/resources/brooklyn/entity/rebind/transformer/renameType.xslt
----------------------------------------------------------------------
diff --cc core/src/main/resources/brooklyn/entity/rebind/transformer/renameType.xslt
index 0000000,3c605aa..e53fe37
mode 000000,100644..100644
--- a/core/src/main/resources/brooklyn/entity/rebind/transformer/renameType.xslt
+++ b/core/src/main/resources/brooklyn/entity/rebind/transformer/renameType.xslt
@@@ -1,0 -1,23 +1,41 @@@
+ <?xml version="1.0" encoding="ISO-8859-1"?>
++<!--
++    Licensed to the Apache Software Foundation (ASF) under one
++    or more contributor license agreements.  See the NOTICE file
++    distributed with this work for additional information
++    regarding copyright ownership.  The ASF licenses this file
++    to you under the Apache License, Version 2.0 (the
++    "License"); you may not use this file except in compliance
++    with the License.  You may obtain a copy of the License at
++    
++     http://www.apache.org/licenses/LICENSE-2.0
++    
++    Unless required by applicable law or agreed to in writing,
++    software distributed under the License is distributed on an
++    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++    KIND, either express or implied.  See the License for the
++    specific language governing permissions and limitations
++    under the License.
++-->
+ <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ 
+   <xsl:output omit-xml-declaration="yes"/>
+   
+   <xsl:template match="node()|@*">
+     <xsl:copy>
+       <xsl:apply-templates select="node()|@*"/>
+     </xsl:copy>
+   </xsl:template>
+ 
+   <xsl:template match="type">
+     <type>
+       <xsl:copy-of select="@*"/>
+       <xsl:choose>
+         <xsl:when test=".='${old_val}'"><xsl:value-of select="'${new_val}'"/></xsl:when>
+         <xsl:otherwise><xsl:value-of select="." /></xsl:otherwise>
+       </xsl:choose>
+     </type>
+   </xsl:template>
+ 
+ </xsl:stylesheet>
+ 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/320c1c75/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoaderTest.java
----------------------------------------------------------------------
diff --cc core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoaderTest.java
index 0000000,bef4eb6..e473245
mode 000000,100644..100644
--- a/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoaderTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/transformer/CompoundTransformerLoaderTest.java
@@@ -1,0 -1,52 +1,70 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *     http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
+ package brooklyn.entity.rebind.transformer;
+ 
+ import static org.testng.Assert.assertTrue;
+ 
+ import java.util.Collection;
+ 
+ import org.testng.annotations.Test;
+ 
+ import brooklyn.entity.rebind.BrooklynObjectType;
+ import brooklyn.entity.rebind.transformer.impl.XsltTransformer;
+ 
+ import com.google.common.collect.Iterables;
+ 
+ public class CompoundTransformerLoaderTest {
+ 
+     @Test
+     public void testLoadsTransformerFromYaml() throws Exception {
+         String contents =
+                 "renameType:\n"+
+                 "  old_val: myoldname\n"+
+                 "  new_val: mynewname\n"+
+                 "renameClass:\n"+
+                 "  old_val: myoldname\n"+
+                 "  new_val: mynewname\n"+
+                 "renameField:\n"+
+                 "  class_name: myclassname\n"+
+                 "  old_val: myoldname\n"+
+                 "  new_val: mynewname\n"+
+                 "xslt:\n"+
+                 "  url: classpath://brooklyn/entity/rebind/transformer/renameType.xslt\n"+
+                 "  substitutions:\n"+
+                 "    old_val: myoldname\n"+
+                 "    new_val: mynewname\n"+
+                 "rawDataTransformer:\n"+
+                 "  type: "+MyRawDataTransformer.class.getName()+"\n";
+         
+         CompoundTransformer transformer = CompoundTransformerLoader.load(contents);
+         Collection<RawDataTransformer> rawDataTransformers = transformer.getRawDataTransformers().get(BrooklynObjectType.ENTITY);
+         assertTrue(Iterables.get(rawDataTransformers, 0) instanceof XsltTransformer);
+         assertTrue(Iterables.get(rawDataTransformers, 1) instanceof XsltTransformer);
+         assertTrue(Iterables.get(rawDataTransformers, 2) instanceof XsltTransformer);
+         assertTrue(Iterables.get(rawDataTransformers, 3) instanceof XsltTransformer);
+         assertTrue(Iterables.get(rawDataTransformers, 4) instanceof MyRawDataTransformer);
+     }
+     
+     public static class MyRawDataTransformer implements RawDataTransformer {
+         @Override
+         public String transform(String input) throws Exception {
+             return input; // no-op
+         }
+     }
+ }


Mime
View raw message