/**
  * Dumps out the persisted mementos that are at the given directory.
  *
  * <p>Binds to the persisted state (as a "hot standby") to load the raw data (as strings), and to
  * write out the entity, location, policy, enricher, feed and catalog-item data.
  *
  * @param dir The directory containing the persisted state
  */
 public static void dumpMementoDir(File dir) {
   LocalManagementContextForTests mgmt =
       new LocalManagementContextForTests(BrooklynProperties.Factory.newEmpty());
   FileBasedObjectStore store = null;
   BrooklynMementoPersisterToObjectStore persister = null;
   try {
     store = new FileBasedObjectStore(dir);
     store.injectManagementContext(mgmt);
     store.prepareForSharedUse(PersistMode.AUTO, HighAvailabilityMode.HOT_STANDBY);
     persister =
         new BrooklynMementoPersisterToObjectStore(
             store, BrooklynProperties.Factory.newEmpty(), RebindTestUtils.class.getClassLoader());
     BrooklynMementoRawData data =
         persister.loadMementoRawData(RebindExceptionHandlerImpl.builder().build());
     List<BrooklynObjectType> types =
         ImmutableList.of(
             BrooklynObjectType.ENTITY,
             BrooklynObjectType.LOCATION,
             BrooklynObjectType.POLICY,
             BrooklynObjectType.ENRICHER,
             BrooklynObjectType.FEED,
             BrooklynObjectType.CATALOG_ITEM);
     for (BrooklynObjectType type : types) {
       LOG.info(type + " (" + data.getObjectsOfType(type).keySet() + "):");
       for (Map.Entry<String, String> entry : data.getObjectsOfType(type).entrySet()) {
         LOG.info("\t" + type + " " + entry.getKey() + ": " + entry.getValue());
       }
     }
   } finally {
     if (persister != null) persister.stop(false);
     if (store != null) store.close();
     mgmt.terminate();
   }
 }
 @Override
 public void onManageEntityFailed(Entity entity, Exception e) {
   manageEntityFailures.put(
       entity,
       new IllegalStateException(
           "problem managing entity " + entity.getId() + " (" + entity + ")", e));
   super.onManageEntityFailed(entity, e);
 }
 @Override
 public void onManageLocationFailed(Location location, Exception e) {
   manageLocationFailures.put(
       location,
       new IllegalStateException(
           "problem managing location " + location.getId() + " (" + location + ")", e));
   super.onManageLocationFailed(location, e);
 }
 @Override
 public void onRebindPolicyFailed(Policy policy, Exception e) {
   rebindPolicyFailures.put(
       policy,
       new IllegalStateException(
           "problem rebinding plicy " + policy.getId() + " (" + policy + ")", e));
   super.onRebindPolicyFailed(policy, e);
 }
  @Override
  public BrooklynMementoRawData retrieveMementoRawData() {
    RebindExceptionHandler exceptionHandler =
        RebindExceptionHandlerImpl.builder()
            .danglingRefFailureMode(danglingRefFailureMode)
            .rebindFailureMode(rebindFailureMode)
            .addConfigFailureMode(addConfigFailureMode)
            .addPolicyFailureMode(addPolicyFailureMode)
            .loadPolicyFailureMode(loadPolicyFailureMode)
            .build();

    return loadMementoRawData(exceptionHandler);
  }
  @Override
  public List<Application> rebind(
      ClassLoader classLoaderO,
      RebindExceptionHandler exceptionHandlerO,
      ManagementNodeState modeO) {
    final ClassLoader classLoader =
        classLoaderO != null ? classLoaderO : managementContext.getCatalogClassLoader();
    final RebindExceptionHandler exceptionHandler =
        exceptionHandlerO != null
            ? exceptionHandlerO
            : RebindExceptionHandlerImpl.builder()
                .danglingRefFailureMode(danglingRefFailureMode)
                .danglingRefQuorumRequiredHealthy(danglingRefsQuorumRequiredHealthy)
                .rebindFailureMode(rebindFailureMode)
                .addConfigFailureMode(addConfigFailureMode)
                .addPolicyFailureMode(addPolicyFailureMode)
                .loadPolicyFailureMode(loadPolicyFailureMode)
                .build();
    final ManagementNodeState mode = modeO != null ? modeO : getRebindMode();

    if (mode != ManagementNodeState.MASTER
        && mode != ManagementNodeState.HOT_STANDBY
        && mode != ManagementNodeState.HOT_BACKUP)
      throw new IllegalStateException(
          "Must be either master or hot standby/backup to rebind (mode " + mode + ")");

    ExecutionContext ec = BasicExecutionContext.getCurrentExecutionContext();
    if (ec == null) {
      ec = managementContext.getServerExecutionContext();
      Task<List<Application>> task =
          ec.submit(
              new Callable<List<Application>>() {
                @Override
                public List<Application> call() throws Exception {
                  return rebindImpl(classLoader, exceptionHandler, mode);
                }
              });
      try {
        return task.get();
      } catch (Exception e) {
        throw Exceptions.propagate(e);
      }
    } else {
      return rebindImpl(classLoader, exceptionHandler, mode);
    }
  }
  public void rebindPartialActive(
      CompoundTransformer transformer, Iterator<BrooklynObject> objectsToRebind) {
    final ClassLoader classLoader = managementContext.getCatalogClassLoader();
    // TODO we might want different exception handling for partials;
    // failure at various points should leave proxies in a sensible state,
    // either pointing at old or at new, though this is relatively untested,
    // and some things e.g. policies might not be properly started
    final RebindExceptionHandler exceptionHandler =
        RebindExceptionHandlerImpl.builder()
            .danglingRefFailureMode(danglingRefFailureMode)
            .danglingRefQuorumRequiredHealthy(danglingRefsQuorumRequiredHealthy)
            .rebindFailureMode(rebindFailureMode)
            .addConfigFailureMode(addConfigFailureMode)
            .addPolicyFailureMode(addPolicyFailureMode)
            .loadPolicyFailureMode(loadPolicyFailureMode)
            .build();
    final ManagementNodeState mode = getRebindMode();

    ActivePartialRebindIteration iteration =
        new ActivePartialRebindIteration(
            this,
            mode,
            classLoader,
            exceptionHandler,
            rebindActive,
            readOnlyRebindCount,
            rebindMetrics,
            persistenceStoreAccess);

    iteration.setObjectIterator(
        Iterators.transform(
            objectsToRebind,
            new Function<BrooklynObject, BrooklynObject>() {
              @Override
              public BrooklynObject apply(BrooklynObject obj) {
                // entities must be deproxied
                if (obj instanceof Entity) obj = Entities.deproxy((Entity) obj);
                return obj;
              }
            }));
    if (transformer != null) iteration.applyTransformer(transformer);
    iteration.run();
  }
 @Override
 public void onCreateLocationFailed(String id, String type, Exception e) {
   createLocationFailures.put(
       id, new IllegalStateException("problem creating location " + id + " of type " + type, e));
   super.onCreateLocationFailed(id, type, e);
 }
 @Override
 public void onLoadEnricherMementoFailed(String msg, Exception e) {
   loadMementoFailures.add(new IllegalStateException("problem loading mementos: " + msg, e));
   super.onLoadPolicyMementoFailed(msg, e);
 }
 @Override
 public void onDone() {
   super.onDone();
 }
 @Override
 public void onPolicyNotFound(String id) {
   locationNotFoundFailures.put(id, new IllegalStateException("policy'" + id + "' not found"));
   super.onPolicyNotFound(id);
 }
 @Override
 public void onEntityNotFound(String id) {
   entityNotFoundFailures.put(id, new IllegalStateException("entity '" + id + "' not found"));
   super.onEntityNotFound(id);
 }