@Override
  public void attachLifecycleEffectors(Entity entity) {
    if (getPidFile() == null
        && getServiceName() == null
        && getClass().equals(ChefLifecycleEffectorTasks.class)) {
      // warn on incorrect usage
      log.warn(
          "Uses of "
              + getClass()
              + " must define a PID file or a service name (or subclass and override {start,stop} methods as per javadoc) "
              + "in order for check-running and stop to work");
    }

    super.attachLifecycleEffectors(entity);
  }
  @SuppressWarnings({"unchecked", "deprecation"})
  protected void startWithChefSoloAsync() {
    String baseDir =
        MachineLifecycleEffectorTasks.resolveOnBoxDir(
            entity(),
            Machines.findUniqueMachineLocation(entity().getLocations(), SshMachineLocation.class)
                .get());
    String installDir = Urls.mergePaths(baseDir, "installs/chef");

    @SuppressWarnings("rawtypes")
    Map<String, String> cookbooks =
        (Map)
            ConfigBag.newInstance(entity().getConfig(CHEF_COOKBOOK_URLS))
                .putIfAbsent(entity().getConfig(CHEF_COOKBOOKS))
                .getAllConfig();
    if (cookbooks.isEmpty())
      log.warn("No cookbook_urls set for " + entity() + "; launch will likely fail subsequently");
    DynamicTasks.queue(
        ChefSoloTasks.installChef(installDir, false),
        ChefSoloTasks.installCookbooks(installDir, cookbooks, false));

    // TODO chef for and run a prestart recipe if necessary
    // TODO open ports

    String primary = getPrimaryCookbook();

    // put all config under brooklyn/cookbook/config
    Navigator<MutableMap<Object, Object>> attrs = Jsonya.newInstancePrimitive().at("brooklyn");
    if (Strings.isNonBlank(primary)) attrs.at(primary);
    attrs.at("config");
    attrs.put(entity().getAllConfigBag().getAllConfig());
    // and put launch attrs at root
    try {
      attrs
          .root()
          .put(
              (Map<?, ?>)
                  Tasks.resolveDeepValue(
                      entity().getConfig(CHEF_LAUNCH_ATTRIBUTES),
                      Object.class,
                      entity().getExecutionContext()));
    } catch (Exception e) {
      Exceptions.propagate(e);
    }

    Collection<? extends String> runList = entity().getConfig(CHEF_LAUNCH_RUN_LIST);
    if (runList == null) runList = entity().getConfig(CHEF_RUN_LIST);
    if (runList == null) {
      if (Strings.isNonBlank(primary)) runList = ImmutableList.of(primary + "::" + "start");
      else
        throw new IllegalStateException(
            "Require a primary cookbook or a run_list to effect " + "start" + " on " + entity());
    }

    String runDir =
        Urls.mergePaths(
            baseDir,
            "apps/"
                + entity().getApplicationId()
                + "/chef/entities/"
                + entity().getEntityType().getSimpleName()
                + "_"
                + entity().getId());

    DynamicTasks.queue(
        ChefSoloTasks.buildChefFile(
            runDir, installDir, "launch", runList, (Map<String, Object>) attrs.root().get()));

    DynamicTasks.queue(
        ChefSoloTasks.runChef(runDir, "launch", entity().getConfig(CHEF_RUN_CONVERGE_TWICE)));
  }