@Test
  public void testRestoresConfig() throws Exception {
    origApp.addEnricher(
        EnricherSpec.create(MyEnricher.class)
            .displayName("My Enricher")
            .uniqueTag("tagU")
            .tag("tag1")
            .tag("tag2")
            .configure(
                MyEnricher.MY_CONFIG_WITH_SETFROMFLAG_NO_SHORT_NAME,
                "myVal for with setFromFlag noShortName")
            .configure(
                MyEnricher.MY_CONFIG_WITH_SETFROMFLAG_WITH_SHORT_NAME,
                "myVal for setFromFlag withShortName")
            .configure(MyEnricher.MY_CONFIG_WITHOUT_SETFROMFLAG, "myVal for witout setFromFlag"));

    newApp = (TestApplication) rebind();
    MyEnricher newEnricher = (MyEnricher) Iterables.getOnlyElement(newApp.getEnrichers());

    assertEquals(newEnricher.getDisplayName(), "My Enricher");

    assertEquals(newEnricher.getUniqueTag(), "tagU");
    assertEquals(newEnricher.tags().getTags(), MutableSet.of("tagU", "tag1", "tag2"));

    assertEquals(
        newEnricher.getConfig(MyEnricher.MY_CONFIG_WITH_SETFROMFLAG_NO_SHORT_NAME),
        "myVal for with setFromFlag noShortName");
    assertEquals(
        newEnricher.getConfig(MyEnricher.MY_CONFIG_WITH_SETFROMFLAG_WITH_SHORT_NAME),
        "myVal for setFromFlag withShortName");
    assertEquals(
        newEnricher.getConfig(MyEnricher.MY_CONFIG_WITHOUT_SETFROMFLAG),
        "myVal for witout setFromFlag");
  }
 @Override
 public EntitySpec<? extends Application> createApplicationSpec(String plan) {
   try {
     CampPlatform camp = CampInternalUtils.getCampPlatform(mgmt);
     BrooklynClassLoadingContext loader = JavaBrooklynClassLoadingContext.create(mgmt);
     AssemblyTemplate at = CampInternalUtils.registerDeploymentPlan(plan, loader, camp);
     AssemblyTemplateInstantiator instantiator = CampInternalUtils.getInstantiator(at);
     if (instantiator instanceof AssemblyTemplateSpecInstantiator) {
       return ((AssemblyTemplateSpecInstantiator) instantiator)
           .createApplicationSpec(at, camp, loader, MutableSet.<String>of());
     } else {
       // The unknown instantiator can create the app (Assembly), but not a spec.
       // Currently, all brooklyn plans should produce the above.
       if (at.getPlatformComponentTemplates() == null
           || at.getPlatformComponentTemplates().isEmpty()) {
         if (at.getCustomAttributes().containsKey(BrooklynCampReservedKeys.BROOKLYN_CATALOG))
           throw new IllegalArgumentException(
               "Unrecognized application blueprint format: expected an application, not a brooklyn.catalog");
         throw new PlanNotRecognizedException(
             "Unrecognized application blueprint format: no services defined");
       }
       // map this (expected) error to a nicer message
       throw new PlanNotRecognizedException("Unrecognized application blueprint format");
     }
   } catch (Exception e) {
     // TODO how do we figure out that the plan is not supported vs. invalid to wrap in a
     // PlanNotRecognizedException?
     if (log.isDebugEnabled()) log.debug("Failed to create entity from CAMP spec:\n" + plan, e);
     throw Exceptions.propagate(e);
   }
 }
  @Override
  public String getSeeds() {
    Set<Entity> seeds = getConfig(CassandraNode.INITIAL_SEEDS);
    if (seeds == null) {
      log.warn(
          "No seeds available when requested for " + this,
          new Throwable("source of no Cassandra seeds when requested"));
      return null;
    }
    String snitchName = getConfig(CassandraNode.ENDPOINT_SNITCH_NAME);
    MutableSet<String> seedsHostnames = MutableSet.of();
    for (Entity entity : seeds) {
      // tried removing ourselves if there are other nodes, but that is a BAD idea!
      // blows up with a "java.lang.RuntimeException: No other nodes seen!"

      if (snitchName.equals("Ec2MultiRegionSnitch") || snitchName.contains("MultiCloudSnitch")) {
        // http://www.datastax.com/documentation/cassandra/2.0/mobile/cassandra/architecture/architectureSnitchEC2MultiRegion_c.html
        // says the seeds should be public IPs.
        seedsHostnames.add(entity.getAttribute(CassandraNode.ADDRESS));
      } else {
        String sensorName = getConfig(BROADCAST_ADDRESS_SENSOR);
        if (Strings.isNonBlank(sensorName)) {
          seedsHostnames.add(entity.getAttribute(Sensors.newStringSensor(sensorName)));
        } else {
          Maybe<String> optionalSeedHostname = Machines.findSubnetOrPublicHostname(entity);
          if (optionalSeedHostname.isPresent()) {
            String seedHostname = optionalSeedHostname.get();
            seedsHostnames.add(seedHostname);
          } else {
            log.warn(
                "In node {}, seed hostname missing for {}; not including in seeds list",
                this,
                entity);
          }
        }
      }
    }

    String result = Strings.join(seedsHostnames, ",");
    log.info("Seeds for {}: {}", this, result);
    return result;
  }
  /**
   * Default restart implementation for an entity.
   *
   * <p>Stops processes if possible, then starts the entity again.
   */
  public void restart(ConfigBag parameters) {
    ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPING);

    RestartMachineMode isRestartMachine =
        parameters.get(RestartSoftwareParameters.RESTART_MACHINE_TYPED);
    if (isRestartMachine == null) isRestartMachine = RestartMachineMode.AUTO;
    if (isRestartMachine == RestartMachineMode.AUTO)
      isRestartMachine =
          getDefaultRestartStopsMachine() ? RestartMachineMode.TRUE : RestartMachineMode.FALSE;

    // Calling preStopCustom without a corresponding postStopCustom invocation
    // doesn't look right so use a separate callback pair; Also depending on the arguments
    // stop() could be called which will call the {pre,post}StopCustom on its own.
    DynamicTasks.queue("pre-restart", new PreRestartTask());

    if (isRestartMachine == RestartMachineMode.FALSE) {
      DynamicTasks.queue("stopping (process)", new StopProcessesAtMachineTask());
    } else {
      Map<String, Object> stopMachineFlags = MutableMap.of();
      if (Entitlements.getEntitlementContext() != null) {
        stopMachineFlags.put(
            "tags",
            MutableSet.of(
                BrooklynTaskTags.tagForEntitlement(Entitlements.getEntitlementContext())));
      }
      Task<String> stopTask =
          Tasks.<String>builder()
              .displayName("stopping (machine)")
              .body(new StopMachineTask())
              .flags(stopMachineFlags)
              .build();
      DynamicTasks.queue(stopTask);
    }

    DynamicTasks.queue("starting", new StartInLocationsTask());
    restartChildren(parameters);
    DynamicTasks.queue("post-restart", new PostRestartTask());

    DynamicTasks.waitForLast();
    ServiceStateLogic.setExpectedState(entity(), Lifecycle.RUNNING);
  }
  protected void doStop(ConfigBag parameters, Callable<StopMachineDetails<Integer>> stopTask) {
    preStopConfirmCustom();

    log.info("Stopping {} in {}", entity(), entity().getLocations());

    StopMode stopMachineMode = getStopMachineMode(parameters);
    StopMode stopProcessMode = parameters.get(StopSoftwareParameters.STOP_PROCESS_MODE);

    DynamicTasks.queue("pre-stop", new PreStopCustomTask());

    // BROOKLYN-263:
    // With this change the stop effector will wait for Location to provision so it can terminate
    // the machine, if a provisioning request is in-progress.
    //
    // The ProvisionMachineTask stores transient internal state in PROVISIONING_TASK_STATE and
    // PROVISIONED_MACHINE: it records when the provisioning is running and when done; and it
    // records the final machine. We record the machine in the internal sensor (rather than
    // just relying on getLocations) because the latter is set much later in the start()
    // process.
    //
    // This code is a big improvement (previously there was a several-minute window in some
    // clouds where a call to stop() would leave the machine running).
    //
    // However, there are still races. If the start() code has not yet reached the call to
    // location.obtain() then we won't wait, and the start() call won't know to abort. It's
    // fiddly to get that right, because we need to cope with restart() - so we mustn't leave
    // any state behind that will interfere with subsequent sequential calls to start().
    // There is some attempt to handle it by ProvisionMachineTask checking if the expectedState
    // is stopping/stopped.
    Maybe<MachineLocation> machine = Machines.findUniqueMachineLocation(entity().getLocations());
    ProvisioningTaskState provisioningState =
        entity().sensors().get(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE);

    if (machine.isAbsent() && provisioningState == ProvisioningTaskState.RUNNING) {
      Duration maxWait = entity().config().get(STOP_WAIT_PROVISIONING_TIMEOUT);
      log.info(
          "When stopping {}, waiting for up to {} for the machine to finish provisioning, before terminating it",
          entity(),
          maxWait);
      boolean success =
          Repeater.create("Wait for a machine to appear")
              .until(
                  new Callable<Boolean>() {
                    @Override
                    public Boolean call() throws Exception {
                      ProvisioningTaskState state =
                          entity()
                              .sensors()
                              .get(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE);
                      return (state != ProvisioningTaskState.RUNNING);
                    }
                  })
              .backoffTo(Duration.FIVE_SECONDS)
              .limitTimeTo(maxWait)
              .run();
      if (!success) {
        log.warn(
            "When stopping {}, timed out after {} waiting for the machine to finish provisioning - machine may we left running",
            entity(),
            maxWait);
      }
      machine = Maybe.ofDisallowingNull(entity().sensors().get(INTERNAL_PROVISIONED_MACHINE));
    }
    entity().sensors().remove(AttributesInternal.INTERNAL_PROVISIONING_TASK_STATE);
    entity().sensors().remove(INTERNAL_PROVISIONED_MACHINE);

    Task<List<?>> stoppingProcess = null;
    if (canStop(stopProcessMode, entity())) {
      stoppingProcess =
          Tasks.parallel(
              "stopping",
              Tasks.create("stopping (process)", new StopProcessesAtMachineTask()),
              Tasks.create("stopping (feeds)", new StopFeedsAtMachineTask()));
      DynamicTasks.queue(stoppingProcess);
    }

    Task<StopMachineDetails<Integer>> stoppingMachine = null;
    if (canStop(stopMachineMode, machine.isAbsent())) {
      // Release this machine (even if error trying to stop process - we rethrow that after)
      Map<String, Object> stopMachineFlags = MutableMap.of();
      if (Entitlements.getEntitlementContext() != null) {
        stopMachineFlags.put(
            "tags",
            MutableSet.of(
                BrooklynTaskTags.tagForEntitlement(Entitlements.getEntitlementContext())));
      }
      Task<StopMachineDetails<Integer>> stopMachineTask =
          Tasks.<StopMachineDetails<Integer>>builder()
              .displayName("stopping (machine)")
              .body(stopTask)
              .flags(stopMachineFlags)
              .build();
      stoppingMachine = DynamicTasks.queue(stopMachineTask);

      DynamicTasks.drain(entity().getConfig(STOP_PROCESS_TIMEOUT), false);

      // shutdown the machine if stopping process fails or takes too long
      synchronized (stoppingMachine) {
        // task also used as mutex by DST when it submits it; ensure it only submits once!
        if (!stoppingMachine.isSubmitted()) {
          // force the stoppingMachine task to run by submitting it here
          StringBuilder msg =
              new StringBuilder("Submitting machine stop early in background for ")
                  .append(entity());
          if (stoppingProcess == null) {
            msg.append(". Process stop skipped, pre-stop not finished?");
          } else {
            msg.append(" because process stop has ")
                .append((stoppingProcess.isDone() ? "finished abnormally" : "not finished"));
          }
          log.warn(msg.toString());
          Entities.submit(entity(), stoppingMachine);
        }
      }
    }

    try {
      // This maintains previous behaviour of silently squashing any errors on the stoppingProcess
      // task if the
      // stoppingMachine exits with a nonzero value
      boolean checkStopProcesses =
          (stoppingProcess != null
              && (stoppingMachine == null || stoppingMachine.get().value == 0));

      if (checkStopProcesses) {
        // TODO we should test for destruction above, not merely successful "stop", as things like
        // localhost and ssh won't be destroyed
        DynamicTasks.waitForLast();
        if (machine.isPresent()) {
          // throw early errors *only if* there is a machine and we have not destroyed it
          stoppingProcess.get();
        }
      }
    } catch (Throwable e) {
      ServiceStateLogic.setExpectedState(entity(), Lifecycle.ON_FIRE);
      Exceptions.propagate(e);
    }
    entity().sensors().set(SoftwareProcess.SERVICE_UP, false);
    ServiceStateLogic.setExpectedState(entity(), Lifecycle.STOPPED);

    DynamicTasks.queue("post-stop", new PostStopCustomTask());

    if (log.isDebugEnabled()) log.debug("Stopped software process entity " + entity());
  }