protected void afterEnd(Map<?, ?> flags, Task<?> task) {
    activeTaskCount.decrementAndGet();
    incompleteTaskCount.decrementAndGet();

    if (log.isTraceEnabled()) log.trace(this + " afterEnd, task: " + task);
    ExecutionUtils.invoke(flags.get("newTaskEndCallback"), task);

    PerThreadCurrentTaskHolder.perThreadCurrentTask.remove();
    ((TaskInternal<?>) task).setEndTimeUtc(System.currentTimeMillis());
    // clear thread _after_ endTime set, so we won't get a null thread when there is no end-time
    if (RENAME_THREADS) {
      String newThreadName = "brooklyn-" + Identifiers.makeRandomId(8);
      task.getThread().setName(newThreadName);
    }
    ((TaskInternal<?>) task).setThread(null);
    synchronized (task) {
      task.notifyAll();
    }

    for (ExecutionListener listener : listeners) {
      try {
        listener.onTaskDone(task);
      } catch (Exception e) {
        log.warn("Error notifying listener " + listener + " of task " + task + " done", e);
      }
    }
  }
  /**
   * will set fields from flags. The unused configuration can be found via the {@linkplain
   * ConfigBag#getUnusedConfig()}. This can be overridden for custom initialization but note the
   * following.
   *
   * <p>if you require fields to be initialized you must do that in this method, with a guard (as in
   * FixedListMachineProvisioningLocation). you must *not* rely on field initializers because they
   * may not run until *after* this method (this method is invoked by the constructor in this class,
   * so initializers in subclasses will not have run when this overridden method is invoked.)
   */
  protected void configure(Map properties) {
    boolean firstTime = (id == null);
    if (firstTime) {
      // pick a random ID if one not set
      id =
          properties.containsKey("id")
              ? (String) properties.get("id")
              : Identifiers.makeRandomId(8);
    }
    configBag.putAll(properties);

    if (properties.containsKey(PARENT_LOCATION.getName())) {
      // need to ensure parent's list of children is also updated
      setParentLocation(configBag.get(PARENT_LOCATION));

      // don't include parentLocation in configBag, as breaks rebind
      configBag.remove(PARENT_LOCATION);
    }

    // NB: flag-setting done here must also be done in BasicLocationRebindSupport
    FlagUtils.setFieldsFromFlagsWithBag(this, properties, configBag, firstTime);

    if (!truth(name) && truth(properties.get("displayName"))) {
      // 'displayName' is a legacy way to refer to a location's name
      // FIXME could this be a GString?
      Preconditions.checkArgument(
          properties.get("displayName") instanceof String,
          "'displayName' property should be a string");
      name = (String) properties.remove("displayName");
    }
  }
/**
 * A basic implementation of the {@link Location} interface.
 *
 * <p>This provides an implementation which works according to the requirements of the interface
 * documentation, and is ready to be extended to make more specialized locations.
 *
 * <p>Override {@link #configure(Map)} to add special initialization logic.
 */
public abstract class AbstractLocation implements LocationInternal, HasHostGeoInfo, Configurable {

  /** @deprecated since 0.7.0 shouldn't be public */
  @Deprecated public static final Logger LOG = LoggerFactory.getLogger(AbstractLocation.class);

  public static final ConfigKey<Location> PARENT_LOCATION =
      new BasicConfigKey<Location>(Location.class, "parentLocation");

  private final AtomicBoolean configured = new AtomicBoolean(false);

  @SetFromFlag(value = "id")
  private String id = Identifiers.makeRandomId(8);

  private Reference<Long> creationTimeUtc = new BasicReference<Long>(System.currentTimeMillis());

  // _not_ set from flag; configured explicitly in configure, because we also need to update the
  // parent's list of children
  private Reference<Location> parent = new BasicReference<Location>();

  // NB: all accesses should be synchronized
  private Set<Location> children = Sets.newLinkedHashSet();

  private Reference<String> name = new BasicReference<String>();
  private boolean displayNameAutoGenerated = true;

  private Reference<HostGeoInfo> hostGeoInfo = new BasicReference<HostGeoInfo>();

  private ConfigBag configBag = new ConfigBag();

  private volatile ManagementContext managementContext;
  private volatile boolean managed;

  private boolean _legacyConstruction;

  private boolean inConstruction;

  private final Map<Class<?>, Object> extensions = Maps.newConcurrentMap();

  private final EntityDynamicType entityType;

  /** Construct a new instance of an AbstractLocation. */
  public AbstractLocation() {
    this(Maps.newLinkedHashMap());
  }

  /**
   * Construct a new instance of an AbstractLocation.
   *
   * <p>The properties map recognizes the following keys:
   *
   * <ul>
   *   <li>name - a name for the location
   *   <li>parentLocation - the parent {@link Location}
   * </ul>
   *
   * Other common properties (retrieved via get/findLocationProperty) include:
   *
   * <ul>
   *   <li>latitude
   *   <li>longitude
   *   <li>displayName
   *   <li>iso3166 - list of iso3166-2 code strings
   *   <li>timeZone
   *   <li>abbreviatedName
   * </ul>
   */
  public AbstractLocation(Map properties) {
    inConstruction = true;
    _legacyConstruction = !InternalLocationFactory.FactoryConstructionTracker.isConstructing();
    if (!_legacyConstruction && properties != null && !properties.isEmpty()) {
      LOG.warn(
          "Forcing use of deprecated old-style location construction for "
              + getClass().getName()
              + " because properties were specified ("
              + properties
              + ")");
      _legacyConstruction = true;
    }

    // When one calls getConfig(key), we want to use the default value specified on *this* location
    // if it overrides the default config. The easiest way to look up all our config keys is to
    // reuse the code for Entity (and this will become identical when locations become first-class
    // entities). See {@link #getConfig(ConfigKey)}
    entityType = new EntityDynamicType((Class) getClass());

    if (_legacyConstruction) {
      LOG.warn(
          "Deprecated use of old-style location construction for "
              + getClass().getName()
              + "; instead use LocationManager().createLocation(spec)");
      if (LOG.isDebugEnabled())
        LOG.debug(
            "Source of use of old-style location construction",
            new Throwable("Source of use of old-style location construction"));

      configure(properties);

      boolean deferConstructionChecks =
          (properties.containsKey("deferConstructionChecks")
              && TypeCoercions.coerce(properties.get("deferConstructionChecks"), Boolean.class));
      if (!deferConstructionChecks) {
        FlagUtils.checkRequiredFields(this);
      }
    }

    inConstruction = false;
  }

  protected void assertNotYetManaged() {
    if (!inConstruction
        && (managementContext != null && managementContext.getLocationManager().isManaged(this))) {
      LOG.warn(
          "Configuration being made to {} after deployment; may not be supported in future versions",
          this);
    }
    // throw new IllegalStateException("Cannot set configuration "+key+" on active location "+this)
  }

  public void setManagementContext(ManagementContextInternal managementContext) {
    this.managementContext = managementContext;
    if (displayNameAutoGenerated && id != null)
      name.set(getClass().getSimpleName() + ":" + id.substring(0, Math.min(id.length(), 4)));

    Location oldParent = parent.get();
    Set<Location> oldChildren = children;
    Map<String, Object> oldConfig = configBag.getAllConfig();
    Long oldCreationTimeUtc = creationTimeUtc.get();
    String oldDisplayName = name.get();
    HostGeoInfo oldHostGeoInfo = hostGeoInfo.get();

    parent = managementContext.getStorage().getReference(id + "-parent");
    children =
        SetFromLiveMap.create(
            managementContext.getStorage().<Location, Boolean>getMap(id + "-children"));
    creationTimeUtc = managementContext.getStorage().getReference(id + "-creationTime");
    hostGeoInfo = managementContext.getStorage().getReference(id + "-hostGeoInfo");
    name = managementContext.getStorage().getReference(id + "-displayName");

    // Only override stored defaults if we have actual values. We might be in setManagementContext
    // because we are reconstituting an existing entity in a new brooklyn management-node (in which
    // case believe what is already in the storage), or we might be in the middle of creating a new
    // entity. Normally for a new entity (using EntitySpec creation approach), this will get called
    // before setting the parent etc. However, for backwards compatibility we still support some
    // things calling the entity's constructor directly.
    if (oldParent != null) parent.set(oldParent);
    if (oldChildren.size() > 0) children.addAll(oldChildren);
    if (creationTimeUtc.isNull()) creationTimeUtc.set(oldCreationTimeUtc);
    if (hostGeoInfo.isNull()) hostGeoInfo.set(oldHostGeoInfo);
    if (name.isNull()) {
      name.set(oldDisplayName);
    } else {
      displayNameAutoGenerated = false;
    }

    configBag =
        ConfigBag.newLiveInstance(
            managementContext.getStorage().<String, Object>getMap(id + "-config"));
    if (oldConfig.size() > 0) {
      configBag.putAll(oldConfig);
    }
  }

  @Override
  public ManagementContext getManagementContext() {
    return managementContext;
  }

  /**
   * Will set fields from flags. The unused configuration can be found via the {@linkplain
   * ConfigBag#getUnusedConfig()}. This can be overridden for custom initialization but note the
   * following.
   *
   * <p>For new-style locations (i.e. not calling constructor directly, this will be invoked
   * automatically by brooklyn-core post-construction).
   *
   * <p>For legacy location use, this will be invoked by the constructor in this class. Therefore if
   * over-riding you must *not* rely on field initializers because they may not run until *after*
   * this method (this method is invoked by the constructor in this class, so initializers in
   * subclasses will not have run when this overridden method is invoked.) If you require fields to
   * be initialized you must do that in this method with a guard (as in
   * FixedListMachineProvisioningLocation).
   */
  public void configure(Map properties) {
    assertNotYetManaged();

    boolean firstTime = !configured.getAndSet(true);

    configBag.putAll(properties);

    if (properties.containsKey(PARENT_LOCATION.getName())) {
      // need to ensure parent's list of children is also updated
      setParent(configBag.get(PARENT_LOCATION));

      // don't include parentLocation in configBag, as breaks rebind
      configBag.remove(PARENT_LOCATION);
    }

    // NB: flag-setting done here must also be done in BasicLocationRebindSupport
    FlagUtils.setFieldsFromFlagsWithBag(this, properties, configBag, firstTime);
    FlagUtils.setAllConfigKeys(this, configBag, false);

    if (properties.containsKey("displayName")) {
      name.set((String) removeIfPossible(properties, "displayName"));
      displayNameAutoGenerated = false;
    } else if (properties.containsKey("name")) {
      name.set((String) removeIfPossible(properties, "name"));
      displayNameAutoGenerated = false;
    } else if (isLegacyConstruction()) {
      name.set(getClass().getSimpleName() + ":" + id.substring(0, Math.min(id.length(), 4)));
      displayNameAutoGenerated = true;
    }

    // TODO Explicitly dealing with iso3166 here because want custom splitter rule comma-separated
    // string.
    // Is there a better way to do it (e.g. more similar to latitude, where configKey+TypeCoercion
    // is enough)?
    if (groovyTruth(properties.get("iso3166"))) {
      Object rawCodes = removeIfPossible(properties, "iso3166");
      Set<String> codes;
      if (rawCodes instanceof CharSequence) {
        codes = ImmutableSet.copyOf(Splitter.on(",").trimResults().split((CharSequence) rawCodes));
      } else {
        codes = TypeCoercions.coerce(rawCodes, Set.class);
      }
      configBag.put(LocationConfigKeys.ISO_3166, codes);
    }
  }

  // TODO ensure no callers rely on 'remove' semantics, and don't remove;
  // or perhaps better use a config bag so we know what is used v unused
  private static Object removeIfPossible(Map map, Object key) {
    try {
      return map.remove(key);
    } catch (Exception e) {
      return map.get(key);
    }
  }

  /**
   * Called by framework (in new-style locations) after configuring, setting parent, etc, but before
   * a reference to this location is shared with other locations.
   *
   * <p>To preserve backwards compatibility for if the location is constructed directly, one can
   * call the code below, but that means it will be called after references to this location have
   * been shared with other entities.
   *
   * <pre>{@code
   * if (isLegacyConstruction()) {
   *     init();
   * }
   * }</pre>
   */
  public void init() {
    // no-op
  }

  /**
   * Called by framework (in new-style locations) on rebind, after configuring, setting parent, etc.
   * Note that a future change to Brooklyn is that {@link #init()} will not be called when
   * rebinding.
   */
  public void rebind() {
    // no-op
  }

  protected boolean isRebinding() {
    return RebindManagerImpl.RebindTracker.isRebinding();
  }

  public boolean isManaged() {
    return managementContext != null && managed;
  }

  public void onManagementStarted() {
    if (displayNameAutoGenerated)
      name.set(getClass().getSimpleName() + ":" + id.substring(0, Math.min(id.length(), 4)));
    this.managed = true;
  }

  public void onManagementStopped() {
    this.managed = false;
    if (managementContext.isRunning()) {
      BrooklynStorage storage = ((ManagementContextInternal) managementContext).getStorage();
      storage.remove(id + "-parent");
      storage.remove(id + "-children");
      storage.remove(id + "-creationTime");
      storage.remove(id + "-hostGeoInfo");
      storage.remove(id + "-displayName");
      storage.remove(id + "-config");
    }
  }

  protected boolean isLegacyConstruction() {
    return _legacyConstruction;
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public String getDisplayName() {
    return name.get();
  }

  protected boolean isDisplayNameAutoGenerated() {
    return displayNameAutoGenerated;
  }

  @Override
  public Location getParent() {
    return parent.get();
  }

  @Override
  public Collection<Location> getChildren() {
    synchronized (children) {
      return ImmutableList.copyOf(children);
    }
  }

  @Override
  public void setParent(Location newParent) {
    if (newParent == this) {
      throw new IllegalArgumentException("Location cannot be its own parent: " + this);
    }
    if (newParent == parent.get()) {
      return; // no-op; already have desired parent
    }

    // TODO Should we support a location changing parent? The resulting unmanage/manage might cause
    // problems.
    if (parent.get() != null) {
      Location oldParent = parent.get();
      parent.set(null);
      ((AbstractLocation) oldParent).removeChild(this); // FIXME Nasty cast
    }
    if (newParent != null) {
      parent.set(newParent);
      ((AbstractLocation) parent.get()).addChild(this); // FIXME Nasty cast
    }
  }

  @Override
  public <T> T getConfig(HasConfigKey<T> key) {
    return getConfig(key.getConfigKey());
  }

  @Override
  public <T> T getConfig(ConfigKey<T> key) {
    if (hasConfig(key, false)) return getLocalConfigBag().get(key);
    if (getParent() != null) return getParent().getConfig(key);

    // In case this entity class has overridden the given key (e.g. to set default), then retrieve
    // this entity's key
    // TODO when locations become entities, the duplication of this compared to
    // EntityConfigMap.getConfig will disappear.
    ConfigKey<T> ownKey = (ConfigKey<T>) elvis(entityType.getConfigKey(key.getName()), key);

    return ownKey.getDefaultValue();
  }

  @Override
  public boolean hasConfig(ConfigKey<?> key, boolean includeInherited) {
    boolean locally = getLocalConfigBag().containsKey(key);
    if (locally) return true;
    if (!includeInherited) return false;
    if (getParent() != null) return getParent().hasConfig(key, true);
    return false;
  }

  @Override
  public Map<String, Object> getAllConfig(boolean includeInherited) {
    ConfigBag bag = (includeInherited ? getAllConfigBag() : getLocalConfigBag());
    return bag.getAllConfig();
  }

  @Override
  public ConfigBag getAllConfigBag() {
    ConfigBag result = ConfigBag.newInstanceExtending(configBag, ImmutableMap.of());
    Location p = getParent();
    if (p != null) result.putIfAbsent(((LocationInternal) p).getAllConfigBag().getAllConfig());
    return result;
  }

  @Override
  public ConfigBag getLocalConfigBag() {
    return configBag;
  }

  /**
   * @deprecated since 0.7; use {@link #getLocalConfigBag()}
   * @since 0.6
   */
  public ConfigBag getRawLocalConfigBag() {
    return getLocalConfigBag();
  }

  @Override
  public <T> T setConfig(ConfigKey<T> key, T value) {
    return configBag.put(key, value);
  }

  /** @since 0.6.0 (?) - use getDisplayName */
  public void setName(String newName) {
    setDisplayName(newName);
    displayNameAutoGenerated = false;
  }

  public void setDisplayName(String newName) {
    name.set(newName);
    displayNameAutoGenerated = false;
  }

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof Location)) {
      return false;
    }

    Location l = (Location) o;
    return getId().equals(l.getId());
  }

  @Override
  public int hashCode() {
    return getId().hashCode();
  }

  @Override
  public boolean containsLocation(Location potentialDescendent) {
    Location loc = potentialDescendent;
    while (loc != null) {
      if (this == loc) return true;
      loc = loc.getParent();
    }
    return false;
  }

  protected <T extends Location> T addChild(LocationSpec<T> spec) {
    T child = managementContext.getLocationManager().createLocation(spec);
    addChild(child);
    return child;
  }

  public void addChild(Location child) {
    // Previously, setParent delegated to addChildLocation and we sometimes ended up with
    // duplicate entries here. Instead this now uses a similar scheme to
    // AbstractLocation.setParent/addChild (with any weaknesses for distribution that such a
    // scheme might have...).
    //
    // We continue to use a list to allow identical-looking locations, but they must be different
    // instances.

    synchronized (children) {
      for (Location contender : children) {
        if (contender == child) {
          // don't re-add; no-op
          return;
        }
      }

      children.add(child);
    }

    if (isManaged()) {
      Locations.manage(child, managementContext);
    } else if (managementContext != null) {
      if (((LocalLocationManager) managementContext.getLocationManager())
              .getLocationEvenIfPreManaged(child.getId())
          == null) {
        ((ManagementContextInternal) managementContext).prePreManage(child);
      }
    }

    children.add(child);
    child.setParent(this);
  }

  protected boolean removeChild(Location child) {
    boolean removed;
    synchronized (children) {
      removed = children.remove(child);
    }
    if (removed) {
      if (child instanceof Closeable) {
        Streams.closeQuietly((Closeable) child);
      }
      child.setParent(null);

      if (isManaged()) {
        managementContext.getLocationManager().unmanage(child);
      }
    }
    return removed;
  }

  /** Default String representation is simplified name of class, together with selected fields. */
  @Override
  public String toString() {
    return string().toString();
  }

  @Override
  public String toVerboseString() {
    return toString();
  }

  /**
   * override this, adding to the returned value, to supply additional fields to include in the
   * toString
   */
  protected ToStringHelper string() {
    return Objects.toStringHelper(getClass()).add("id", id).add("name", name);
  }

  @Override
  public HostGeoInfo getHostGeoInfo() {
    return hostGeoInfo.get();
  }

  public void setHostGeoInfo(HostGeoInfo hostGeoInfo) {
    if (hostGeoInfo != null) {
      this.hostGeoInfo.set(hostGeoInfo);
      setConfig(LocationConfigKeys.LATITUDE, hostGeoInfo.latitude);
      setConfig(LocationConfigKeys.LONGITUDE, hostGeoInfo.longitude);
    }
  }

  @Override
  public RebindSupport<LocationMemento> getRebindSupport() {
    return new BasicLocationRebindSupport(this);
  }

  @Override
  public boolean hasExtension(Class<?> extensionType) {
    return extensions.containsKey(checkNotNull(extensionType, "extensionType"));
  }

  @Override
  @SuppressWarnings("unchecked")
  public <T> T getExtension(Class<T> extensionType) {
    Object extension = extensions.get(checkNotNull(extensionType, "extensionType"));
    if (extension == null) {
      throw new IllegalArgumentException(
          "No extension of type " + extensionType + " registered for location " + this);
    }
    return (T) extension;
  }

  @Override
  public <T> void addExtension(Class<T> extensionType, T extension) {
    checkNotNull(extensionType, "extensionType");
    checkNotNull(extension, "extension");
    checkArgument(
        extensionType.isInstance(extension),
        "extension %s does not implement %s",
        extension,
        extensionType);
    extensions.put(extensionType, extension);
  }

  @Override
  public Map<String, String> toMetadataRecord() {
    ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
    builder.put("id", getId());
    if (getDisplayName() != null) builder.put("displayName", getDisplayName());
    return builder.build();
  }
}
 protected String badContainerName() {
   return "/path/does/not/exist/" + Identifiers.makeRandomId(4);
 }