You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/08/19 23:21:04 UTC
[30/62] [abbrv] incubator-brooklyn git commit: rename core’s o.a.b.entity to o.a.b.core.entity
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
new file mode 100644
index 0000000..8e24fd0
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -0,0 +1,1739 @@
+/*
+ * 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 org.apache.brooklyn.core.entity;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.Application;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.entity.EntitySpec;
+import org.apache.brooklyn.api.entity.EntityType;
+import org.apache.brooklyn.api.entity.Group;
+import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.api.mgmt.EntityManager;
+import org.apache.brooklyn.api.mgmt.ExecutionContext;
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.mgmt.SubscriptionContext;
+import org.apache.brooklyn.api.mgmt.SubscriptionHandle;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
+import org.apache.brooklyn.api.mgmt.rebind.mementos.EntityMemento;
+import org.apache.brooklyn.api.objs.EntityAdjunct;
+import org.apache.brooklyn.api.policy.Policy;
+import org.apache.brooklyn.api.policy.PolicySpec;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.api.sensor.Enricher;
+import org.apache.brooklyn.api.sensor.EnricherSpec;
+import org.apache.brooklyn.api.sensor.Feed;
+import org.apache.brooklyn.api.sensor.Sensor;
+import org.apache.brooklyn.api.sensor.SensorEvent;
+import org.apache.brooklyn.api.sensor.SensorEventListener;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
+import org.apache.brooklyn.core.BrooklynFeatureEnablement;
+import org.apache.brooklyn.core.BrooklynLogging;
+import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+import org.apache.brooklyn.core.config.render.RendererHints;
+import org.apache.brooklyn.core.entity.internal.EntityConfigMap;
+import org.apache.brooklyn.core.entity.lifecycle.PolicyDescriptor;
+import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
+import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceNotUpLogic;
+import org.apache.brooklyn.core.internal.BrooklynInitialization;
+import org.apache.brooklyn.core.internal.storage.BrooklynStorage;
+import org.apache.brooklyn.core.internal.storage.Reference;
+import org.apache.brooklyn.core.internal.storage.impl.BasicReference;
+import org.apache.brooklyn.core.mgmt.internal.EffectorUtils;
+import org.apache.brooklyn.core.mgmt.internal.EntityManagementSupport;
+import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal;
+import org.apache.brooklyn.core.mgmt.internal.SubscriptionTracker;
+import org.apache.brooklyn.core.mgmt.rebind.BasicEntityRebindSupport;
+import org.apache.brooklyn.core.objs.AbstractBrooklynObject;
+import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
+import org.apache.brooklyn.core.objs.AbstractEntityAdjunct.AdjunctTagSupport;
+import org.apache.brooklyn.location.core.Locations;
+import org.apache.brooklyn.policy.core.AbstractPolicy;
+import org.apache.brooklyn.sensor.core.AttributeMap;
+import org.apache.brooklyn.sensor.core.AttributeSensorAndConfigKey;
+import org.apache.brooklyn.sensor.core.BasicNotificationSensor;
+import org.apache.brooklyn.sensor.core.Sensors;
+import org.apache.brooklyn.sensor.enricher.AbstractEnricher;
+import org.apache.brooklyn.sensor.feed.AbstractFeed;
+import org.apache.brooklyn.sensor.feed.ConfigToAttributes;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.util.collections.SetFromLiveMap;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.flags.FlagUtils;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.task.DeferredSupplier;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.javalang.Equals;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Default {@link Entity} implementation, which should be extended whenever implementing an entity.
+ * <p>
+ * Provides several common fields ({@link #displayName}, {@link #id}), and supports the core features of
+ * an entity such as configuration keys, attributes, subscriptions and effector invocation.
+ * <p>
+ * If a sub-class is creating other entities, this should be done in an overridden {@link #init()}
+ * method.
+ * <p>
+ * Note that config is typically inherited by children, whereas the fields and attributes are not.
+ * <p>
+ * Sub-classes should have a no-argument constructor. When brooklyn creates an entity, it will:
+ * <ol>
+ * <li>Construct the entity via the no-argument constructor
+ * <li>Call {@link #setDisplayName(String)}
+ * <li>Call {@link #setManagementContext(ManagementContextInternal)}
+ * <li>Call {@link #setProxy(Entity)}; the proxy should be used by everything else when referring
+ * to this entity (except for drivers/policies that are attached to the entity, which can be
+ * given a reference to this entity itself).
+ * <li>Call {@link #configure(Map)} and then {@link #setConfig(ConfigKey, Object)}
+ * <li>Call {@link #init()}
+ * <li>Call {@link #addPolicy(Policy)} (for any policies defined in the {@link EntitySpec})
+ * <li>Call {@link #setParent(Entity)}, if a parent is specified in the {@link EntitySpec}
+ * </ol>
+ * <p>
+ * The legacy (pre 0.5) mechanism for creating entities is for others to call the constructor directly.
+ * This is now deprecated.
+ */
+public abstract class AbstractEntity extends AbstractBrooklynObject implements EntityLocal, EntityInternal {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractEntity.class);
+
+ static { BrooklynInitialization.initAll(); }
+
+ public static final BasicNotificationSensor<Location> LOCATION_ADDED = new BasicNotificationSensor<Location>(
+ Location.class, "entity.location.added", "Location dynamically added to entity");
+ public static final BasicNotificationSensor<Location> LOCATION_REMOVED = new BasicNotificationSensor<Location>(
+ Location.class, "entity.location.removed", "Location dynamically removed from entity");
+
+ public static final BasicNotificationSensor<Sensor> SENSOR_ADDED = new BasicNotificationSensor<Sensor>(Sensor.class,
+ "entity.sensor.added", "Sensor dynamically added to entity");
+ public static final BasicNotificationSensor<Sensor> SENSOR_REMOVED = new BasicNotificationSensor<Sensor>(Sensor.class,
+ "entity.sensor.removed", "Sensor dynamically removed from entity");
+
+ public static final BasicNotificationSensor<String> EFFECTOR_ADDED = new BasicNotificationSensor<String>(String.class,
+ "entity.effector.added", "Effector dynamically added to entity");
+ public static final BasicNotificationSensor<String> EFFECTOR_REMOVED = new BasicNotificationSensor<String>(String.class,
+ "entity.effector.removed", "Effector dynamically removed from entity");
+ public static final BasicNotificationSensor<String> EFFECTOR_CHANGED = new BasicNotificationSensor<String>(String.class,
+ "entity.effector.changed", "Effector dynamically changed on entity");
+
+ public static final BasicNotificationSensor<PolicyDescriptor> POLICY_ADDED = new BasicNotificationSensor<PolicyDescriptor>(PolicyDescriptor.class,
+ "entity.policy.added", "Policy dynamically added to entity");
+ public static final BasicNotificationSensor<PolicyDescriptor> POLICY_REMOVED = new BasicNotificationSensor<PolicyDescriptor>(PolicyDescriptor.class,
+ "entity.policy.removed", "Policy dynamically removed from entity");
+
+ public static final BasicNotificationSensor<Entity> CHILD_ADDED = new BasicNotificationSensor<Entity>(Entity.class,
+ "entity.children.added", "Child dynamically added to entity");
+ public static final BasicNotificationSensor<Entity> CHILD_REMOVED = new BasicNotificationSensor<Entity>(Entity.class,
+ "entity.children.removed", "Child dynamically removed from entity");
+
+ public static final BasicNotificationSensor<Group> GROUP_ADDED = new BasicNotificationSensor<Group>(Group.class,
+ "entity.group.added", "Group dynamically added to entity");
+ public static final BasicNotificationSensor<Group> GROUP_REMOVED = new BasicNotificationSensor<Group>(Group.class,
+ "entity.group.removed", "Group dynamically removed from entity");
+
+ static {
+ RendererHints.register(Entity.class, RendererHints.displayValue(EntityFunctions.displayName()));
+ }
+
+ private boolean displayNameAutoGenerated = true;
+
+ private Entity selfProxy;
+ private volatile Application application;
+
+ // TODO Because some things still don't use EntitySpec (e.g. the EntityFactory stuff for cluster/fabric),
+ // then we need temp vals here. When setManagementContext is called, we'll switch these out for the read-deal;
+ // i.e. for the values backed by storage
+ private Reference<Entity> parent = new BasicReference<Entity>();
+ private Set<Group> groups = Sets.newLinkedHashSet();
+ private Set<Entity> children = Sets.newLinkedHashSet();
+ private Reference<List<Location>> locations = new BasicReference<List<Location>>(ImmutableList.<Location>of()); // dups removed in addLocations
+ private Reference<Long> creationTimeUtc = new BasicReference<Long>(System.currentTimeMillis());
+ private Reference<String> displayName = new BasicReference<String>();
+ private Reference<String> iconUrl = new BasicReference<String>();
+
+ Map<String,Object> presentationAttributes = Maps.newLinkedHashMap();
+ Collection<AbstractPolicy> policies = Lists.newCopyOnWriteArrayList();
+ Collection<AbstractEnricher> enrichers = Lists.newCopyOnWriteArrayList();
+ Collection<Feed> feeds = Lists.newCopyOnWriteArrayList();
+
+ // FIXME we do not currently support changing parents, but to implement a cluster that can shrink we need to support at least
+ // orphaning (i.e. removing ownership). This flag notes if the entity has previously had a parent, and if an attempt is made to
+ // set a new parent an exception will be thrown.
+ boolean previouslyOwned = false;
+
+ /**
+ * Whether we are still being constructed, in which case never warn in "assertNotYetOwned"
+ */
+ private boolean inConstruction = true;
+
+ private final EntityDynamicType entityType;
+
+ protected final EntityManagementSupport managementSupport = new EntityManagementSupport(this);
+
+ private final BasicConfigurationSupport config = new BasicConfigurationSupport();
+
+ /**
+ * The config values of this entity. Updating this map should be done
+ * via getConfig/setConfig.
+ */
+ // TODO Assigning temp value because not everything uses EntitySpec; see setManagementContext()
+ private EntityConfigMap configsInternal = new EntityConfigMap(this, Maps.<ConfigKey<?>, Object>newLinkedHashMap());
+
+ /**
+ * The sensor-attribute values of this entity. Updating this map should be done
+ * via getAttribute/setAttribute; it will automatically emit an attribute-change event.
+ */
+ // TODO Assigning temp value because not everything uses EntitySpec; see setManagementContext()
+ private AttributeMap attributesInternal = new AttributeMap(this, Maps.<Collection<String>, Object>newLinkedHashMap());
+
+ /**
+ * For temporary data, e.g. timestamps etc for calculating real attribute values, such as when
+ * calculating averages over time etc.
+ *
+ * @deprecated since 0.6; use attributes
+ */
+ @Deprecated
+ protected final Map<String,Object> tempWorkings = Maps.newLinkedHashMap();
+
+ protected transient SubscriptionTracker _subscriptionTracker;
+
+ public AbstractEntity() {
+ this(Maps.newLinkedHashMap(), null);
+ }
+
+ /**
+ * @deprecated since 0.5; instead use no-arg constructor with EntityManager().createEntity(spec)
+ */
+ @Deprecated
+ public AbstractEntity(Map flags) {
+ this(flags, null);
+ }
+
+ /**
+ * @deprecated since 0.5; instead use no-arg constructor with EntityManager().createEntity(spec)
+ */
+ @Deprecated
+ public AbstractEntity(Entity parent) {
+ this(Maps.newLinkedHashMap(), parent);
+ }
+
+ // FIXME don't leak this reference in constructor - even to utils
+ /**
+ * @deprecated since 0.5; instead use no-arg constructor with EntityManager().createEntity(spec)
+ */
+ @Deprecated
+ public AbstractEntity(@SuppressWarnings("rawtypes") Map flags, Entity parent) {
+ super(checkConstructorFlags(flags, parent));
+
+ // TODO Don't let `this` reference escape during construction
+ entityType = new EntityDynamicType(this);
+
+ if (isLegacyConstruction()) {
+ AbstractBrooklynObject checkWeGetThis = configure(flags);
+ assert this.equals(checkWeGetThis) : this+" configure method does not return itself; returns "+checkWeGetThis+" instead of "+this;
+
+ boolean deferConstructionChecks = (flags.containsKey("deferConstructionChecks") && TypeCoercions.coerce(flags.get("deferConstructionChecks"), Boolean.class));
+ if (!deferConstructionChecks) {
+ FlagUtils.checkRequiredFields(this);
+ }
+ }
+ }
+
+ private static Map<?,?> checkConstructorFlags(Map flags, Entity parent) {
+ if (flags==null) {
+ throw new IllegalArgumentException("Flags passed to entity must not be null (try no-arguments or empty map)");
+ }
+ if (flags.get("parent") != null && parent != null && flags.get("parent") != parent) {
+ throw new IllegalArgumentException("Multiple parents supplied, "+flags.get("parent")+" and "+parent);
+ }
+ if (flags.get("owner") != null && parent != null && flags.get("owner") != parent) {
+ throw new IllegalArgumentException("Multiple parents supplied with flags.parent, "+flags.get("owner")+" and "+parent);
+ }
+ if (flags.get("parent") != null && flags.get("owner") != null && flags.get("parent") != flags.get("owner")) {
+ throw new IllegalArgumentException("Multiple parents supplied with flags.parent and flags.owner, "+flags.get("parent")+" and "+flags.get("owner"));
+ }
+ if (parent != null) {
+ flags.put("parent", parent);
+ }
+ if (flags.get("owner") != null) {
+ LOG.warn("Use of deprecated \"flags.owner\" instead of \"flags.parent\" for entity");
+ flags.put("parent", flags.get("owner"));
+ flags.remove("owner");
+ }
+ return flags;
+ }
+
+ /**
+ * @deprecated since 0.7.0; only used for legacy brooklyn types where constructor is called directly
+ */
+ @Override
+ @Deprecated
+ public AbstractEntity configure(Map flags) {
+ if (!inConstruction && getManagementSupport().isDeployed()) {
+ LOG.warn("bulk/flag configuration being made to {} after deployment: may not be supported in future versions ({})",
+ new Object[] { this, flags });
+ }
+ // TODO use a config bag instead
+// ConfigBag bag = new ConfigBag().putAll(flags);
+
+ // FIXME Need to set parent with proxy, rather than `this`
+ Entity suppliedParent = (Entity) flags.remove("parent");
+ if (suppliedParent != null) {
+ suppliedParent.addChild(getProxyIfAvailable());
+ }
+
+ Map<ConfigKey,?> suppliedOwnConfig = (Map<ConfigKey, ?>) flags.remove("config");
+ if (suppliedOwnConfig != null) {
+ for (Map.Entry<ConfigKey, ?> entry : suppliedOwnConfig.entrySet()) {
+ setConfigEvenIfOwned(entry.getKey(), entry.getValue());
+ }
+ }
+
+ if (flags.get("displayName") != null) {
+ displayName.set((String) flags.remove("displayName"));
+ displayNameAutoGenerated = false;
+ } else if (flags.get("name") != null) {
+ displayName.set((String) flags.remove("name"));
+ displayNameAutoGenerated = false;
+ } else if (isLegacyConstruction()) {
+ displayName.set(getClass().getSimpleName()+":"+Strings.maxlen(getId(), 4));
+ displayNameAutoGenerated = true;
+ }
+
+ if (flags.get("iconUrl") != null) {
+ iconUrl.set((String) flags.remove("iconUrl"));
+ }
+
+ // allow config keys, and fields, to be set from these flags if they have a SetFromFlag annotation
+ // TODO the default values on flags are not used? (we should remove that support, since ConfigKeys gives a better way)
+ FlagUtils.setFieldsFromFlags(flags, this);
+ flags = FlagUtils.setAllConfigKeys(flags, this, false);
+
+ // finally all config keys specified in map should be set as config
+ // TODO use a config bag and remove the ones set above in the code below
+ for (Iterator<Map.Entry> fi = flags.entrySet().iterator(); fi.hasNext();) {
+ Map.Entry entry = fi.next();
+ Object k = entry.getKey();
+ if (k instanceof HasConfigKey) k = ((HasConfigKey)k).getConfigKey();
+ if (k instanceof ConfigKey) {
+ setConfigEvenIfOwned((ConfigKey)k, entry.getValue());
+ fi.remove();
+ }
+ }
+
+ if (!flags.isEmpty()) {
+ LOG.warn("Unsupported flags when configuring {}; storing: {}", this, flags);
+ configsInternal.addToLocalBag(flags);
+ }
+
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o == this || o == selfProxy) ||
+ (o instanceof Entity && Objects.equal(getId(), ((Entity)o).getId()));
+ }
+
+ /** internal use only */ @Beta
+ public void setProxy(Entity proxy) {
+ if (selfProxy != null)
+ throw new IllegalStateException("Proxy is already set; cannot reset proxy for "+toString());
+ resetProxy(proxy);
+ }
+ /** internal use only */ @Beta
+ public void resetProxy(Entity proxy) {
+ selfProxy = checkNotNull(proxy, "proxy");
+ }
+
+ public Entity getProxy() {
+ return selfProxy;
+ }
+
+ /**
+ * Returns the proxy, or if not available (because using legacy code) then returns the real entity.
+ * This method will be deleted in a future release; it will be kept while deprecated legacy code
+ * still exists that creates entities without setting the proxy.
+ */
+ @Beta
+ public Entity getProxyIfAvailable() {
+ return getProxy()!=null ? getProxy() : this;
+ }
+
+ /**
+ * Sets a config key value, and returns this Entity instance for use in fluent-API style coding.
+ *
+ * @deprecated since 0.7.0; see {@link #config()}, such as {@code config().set(key, value)}
+ */
+ @Deprecated
+ public <T> AbstractEntity configure(ConfigKey<T> key, T value) {
+ setConfig(key, value);
+ return this;
+ }
+
+ /**
+ * @deprecated since 0.7.0; see {@link #config()}, such as {@code config().set(key, value)}
+ */
+ @SuppressWarnings("unchecked")
+ @Deprecated
+ public <T> AbstractEntity configure(ConfigKey<T> key, String value) {
+ config().set((ConfigKey)key, value);
+ return this;
+ }
+
+ /**
+ * @deprecated since 0.7.0; see {@link #config()}, such as {@code config().set(key, value)}
+ */
+ @Deprecated
+ public <T> AbstractEntity configure(HasConfigKey<T> key, T value) {
+ config().set(key, value);
+ return this;
+ }
+
+ /**
+ * @deprecated since 0.7.0; see {@link #config()}, such as {@code config().set(key, value)}
+ */
+ @SuppressWarnings("unchecked")
+ @Deprecated
+ public <T> AbstractEntity configure(HasConfigKey<T> key, String value) {
+ config().set((ConfigKey)key, value);
+ return this;
+ }
+
+ public void setManagementContext(ManagementContextInternal managementContext) {
+ super.setManagementContext(managementContext);
+ getManagementSupport().setManagementContext(managementContext);
+ entityType.setName(getEntityTypeName());
+ if (displayNameAutoGenerated) displayName.set(getEntityType().getSimpleName()+":"+Strings.maxlen(getId(), 4));
+
+ if (BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_USE_BROOKLYN_LIVE_OBJECTS_DATAGRID_STORAGE)) {
+ Entity oldParent = parent.get();
+ Set<Group> oldGroups = groups;
+ Set<Entity> oldChildren = children;
+ List<Location> oldLocations = locations.get();
+ EntityConfigMap oldConfig = configsInternal;
+ AttributeMap oldAttribs = attributesInternal;
+ long oldCreationTimeUtc = creationTimeUtc.get();
+ String oldDisplayName = displayName.get();
+ String oldIconUrl = iconUrl.get();
+
+ parent = managementContext.getStorage().getReference(getId()+"-parent");
+ groups = SetFromLiveMap.create(managementContext.getStorage().<Group,Boolean>getMap(getId()+"-groups"));
+ children = SetFromLiveMap.create(managementContext.getStorage().<Entity,Boolean>getMap(getId()+"-children"));
+ locations = managementContext.getStorage().getNonConcurrentList(getId()+"-locations");
+ creationTimeUtc = managementContext.getStorage().getReference(getId()+"-creationTime");
+ displayName = managementContext.getStorage().getReference(getId()+"-displayName");
+ iconUrl = managementContext.getStorage().getReference(getId()+"-iconUrl");
+
+ // 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 (oldGroups.size() > 0) groups.addAll(oldGroups);
+ if (oldChildren.size() > 0) children.addAll(oldChildren);
+ if (oldLocations.size() > 0) locations.set(ImmutableList.copyOf(oldLocations));
+ if (creationTimeUtc.isNull()) creationTimeUtc.set(oldCreationTimeUtc);
+ if (displayName.isNull()) {
+ displayName.set(oldDisplayName);
+ } else {
+ displayNameAutoGenerated = false;
+ }
+ if (iconUrl.isNull()) iconUrl.set(oldIconUrl);
+
+ configsInternal = new EntityConfigMap(this, managementContext.getStorage().<ConfigKey<?>, Object>getMap(getId()+"-config"));
+ if (oldConfig.getLocalConfig().size() > 0) {
+ configsInternal.setLocalConfig(oldConfig.getLocalConfig());
+ }
+ config().refreshInheritedConfig();
+
+ attributesInternal = new AttributeMap(this, managementContext.getStorage().<Collection<String>, Object>getMap(getId()+"-attributes"));
+ if (oldAttribs.asRawMap().size() > 0) {
+ for (Map.Entry<Collection<String>,Object> entry : oldAttribs.asRawMap().entrySet()) {
+ attributesInternal.update(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ }
+
+ @Override
+ public Map<String, String> toMetadataRecord() {
+ return ImmutableMap.of();
+ }
+
+ @Override
+ public long getCreationTime() {
+ return creationTimeUtc.get();
+ }
+
+ @Override
+ public String getDisplayName() {
+ return displayName.get();
+ }
+
+ @Override
+ public String getIconUrl() {
+ return iconUrl.get();
+ }
+
+ @Override
+ public void setDisplayName(String newDisplayName) {
+ displayName.set(newDisplayName);
+ displayNameAutoGenerated = false;
+ getManagementSupport().getEntityChangeListener().onChanged();
+ }
+
+ /** allows subclasses to set the default display name to use if none is provided */
+ protected void setDefaultDisplayName(String displayNameIfDefault) {
+ if (displayNameAutoGenerated) {
+ displayName.set(displayNameIfDefault);
+ }
+ }
+
+ /**
+ * Gets the entity type name, to be returned by {@code getEntityType().getName()}.
+ * To be called by brooklyn internals only.
+ * Can be overridden to customize the name.
+ */
+ protected String getEntityTypeName() {
+ try {
+ Class<?> typeClazz = getManagementContext().getEntityManager().getEntityTypeRegistry().getEntityTypeOf(getClass());
+ String typeName = typeClazz.getCanonicalName();
+ if (typeName == null) typeName = typeClazz.getName();
+ return typeName;
+ } catch (IllegalArgumentException e) {
+ String typeName = getClass().getCanonicalName();
+ if (typeName == null) typeName = getClass().getName();
+ LOG.debug("Entity type interface not found for entity "+this+"; instead using "+typeName+" as entity type name");
+ return typeName;
+ }
+ }
+
+ /**
+ * Adds this as a child of the given entity; registers with application if necessary.
+ */
+ @Override
+ public AbstractEntity setParent(Entity entity) {
+ if (!parent.isNull()) {
+ // If we are changing to the same parent...
+ if (parent.contains(entity)) return this;
+ // If we have a parent but changing to orphaned...
+ if (entity==null) { clearParent(); return this; }
+
+ // We have a parent and are changing to another parent...
+ throw new UnsupportedOperationException("Cannot change parent of "+this+" from "+parent+" to "+entity+" (parent change not supported)");
+ }
+ // If we have previously had a parent and are trying to change to another one...
+ if (previouslyOwned && entity != null)
+ throw new UnsupportedOperationException("Cannot set a parent of "+this+" because it has previously had a parent");
+ // We don't have a parent, never have and are changing to having a parent...
+
+ //make sure there is no loop
+ if (this.equals(entity)) throw new IllegalStateException("entity "+this+" cannot own itself");
+ //this may be expensive, but preferable to throw before setting the parent!
+ if (Entities.isDescendant(this, entity))
+ throw new IllegalStateException("loop detected trying to set parent of "+this+" as "+entity+", which is already a descendent");
+
+ parent.set(entity);
+ entity.addChild(getProxyIfAvailable());
+ config().refreshInheritedConfig();
+ previouslyOwned = true;
+
+ getApplication();
+
+ return this;
+ }
+
+ @Override
+ public void clearParent() {
+ if (parent.isNull()) return;
+ Entity oldParent = parent.get();
+ parent.clear();
+ if (oldParent != null) {
+ if (!Entities.isNoLongerManaged(oldParent))
+ oldParent.removeChild(getProxyIfAvailable());
+ }
+ }
+
+ /**
+ * Adds the given entity as a child of this parent <em>and</em> sets this entity as the parent of the child;
+ * returns argument passed in, for convenience.
+ * <p>
+ * The child is NOT managed, even if the parent is already managed at this point
+ * (e.g. the child is added *after* the parent's {@link AbstractEntity#init()} is invoked)
+ * and so will need an explicit <code>getEntityManager().manage(childReturnedFromThis)</code> call.
+ * <i>These semantics are currently under review.</i>
+ */
+ @Override
+ public <T extends Entity> T addChild(T child) {
+ checkNotNull(child, "child must not be null (for entity %s)", this);
+ CatalogUtils.setCatalogItemIdOnAddition(this, child);
+
+ boolean changed;
+ synchronized (children) {
+ if (Entities.isAncestor(this, child)) throw new IllegalStateException("loop detected trying to add child "+child+" to "+this+"; it is already an ancestor");
+ child.setParent(getProxyIfAvailable());
+ changed = children.add(child);
+
+ getManagementSupport().getEntityChangeListener().onChildrenChanged();
+ }
+
+ // TODO not holding synchronization lock while notifying risks out-of-order if addChild+removeChild called in rapid succession.
+ // But doing notification in synchronization block may risk deadlock?
+ if (changed) {
+ emit(AbstractEntity.CHILD_ADDED, child);
+ }
+ return child;
+ }
+
+ /**
+ * Creates an entity using the given spec, and adds it as a child of this entity.
+ *
+ * @see #addChild(Entity)
+ * @see EntityManager#createEntity(EntitySpec)
+ *
+ * @throws IllegalArgumentException If {@code spec.getParent()} is set and is different from this entity
+ */
+ @Override
+ public <T extends Entity> T addChild(EntitySpec<T> spec) {
+ if (spec.getParent()==null) {
+ spec = EntitySpec.create(spec).parent(this);
+ }
+ if (!this.equals(spec.getParent())) {
+ throw new IllegalArgumentException("Attempt to create child of "+this+" with entity spec "+spec+
+ " failed because spec has different parent: "+spec.getParent());
+ }
+ return addChild(getEntityManager().createEntity(spec));
+ }
+
+ @Override
+ public boolean removeChild(Entity child) {
+ boolean changed;
+ synchronized (children) {
+ changed = children.remove(child);
+ child.clearParent();
+
+ if (changed) {
+ getManagementSupport().getEntityChangeListener().onChildrenChanged();
+ }
+ }
+
+ if (changed) {
+ emit(AbstractEntity.CHILD_REMOVED, child);
+ }
+ return changed;
+ }
+
+ @Override
+ public void addGroup(Group group) {
+ boolean changed = groups.add(group);
+ getApplication();
+
+ if (changed) {
+ emit(AbstractEntity.GROUP_ADDED, group);
+ }
+ }
+
+ @Override
+ public void removeGroup(Group group) {
+ boolean changed = groups.remove(group);
+ getApplication();
+
+ if (changed) {
+ emit(AbstractEntity.GROUP_REMOVED, group);
+ }
+ }
+
+ @Override
+ public Entity getParent() {
+ return parent.get();
+ }
+
+ @Override
+ public Collection<Entity> getChildren() {
+ return ImmutableList.copyOf(children);
+ }
+
+ @Override
+ public Collection<Group> getGroups() {
+ return ImmutableList.copyOf(groups);
+ }
+
+ /**
+ * Returns the application, looking it up if not yet known (registering if necessary)
+ */
+ @Override
+ public Application getApplication() {
+ if (application != null) return application;
+ Entity parent = getParent();
+ Application app = (parent != null) ? parent.getApplication() : null;
+ if (app != null) {
+ if (getManagementSupport().isFullyManaged())
+ // only do this once fully managed, in case root app becomes parented
+ setApplication(app);
+ }
+ return app;
+ }
+
+ // FIXME Can this really be deleted? Overridden by AbstractApplication; needs careful review
+ /** @deprecated since 0.4.0 should not be needed / leaked outwith brooklyn internals / mgmt support? */
+ protected synchronized void setApplication(Application app) {
+ if (application != null) {
+ if (application.getId() != app.getId()) {
+ throw new IllegalStateException("Cannot change application of entity (attempted for "+this+" from "+getApplication()+" to "+app);
+ }
+ }
+ this.application = app;
+ }
+
+ @Override
+ public String getApplicationId() {
+ Application app = getApplication();
+ return (app == null) ? null : app.getId();
+ }
+
+ @Override
+ public ManagementContext getManagementContext() {
+ // NB Sept 2014 - removed synch keyword above due to deadlock;
+ // it also synchs in ManagementSupport.getManagementContext();
+ // no apparent reason why it was here also
+ return getManagementSupport().getManagementContext();
+ }
+
+ protected EntityManager getEntityManager() {
+ return getManagementContext().getEntityManager();
+ }
+
+ @Override
+ public EntityType getEntityType() {
+ if (entityType==null) return null;
+ return entityType.getSnapshot();
+ }
+
+ @Override
+ public EntityDynamicType getMutableEntityType() {
+ return entityType;
+ }
+
+ @Override
+ public Collection<Location> getLocations() {
+ synchronized (locations) {
+ return ImmutableList.copyOf(locations.get());
+ }
+ }
+
+ @Override
+ public void addLocations(Collection<? extends Location> newLocations) {
+ synchronized (locations) {
+ List<Location> oldLocations = locations.get();
+ Set<Location> truelyNewLocations = Sets.newLinkedHashSet(newLocations);
+ truelyNewLocations.removeAll(oldLocations);
+ if (truelyNewLocations.size() > 0) {
+ locations.set(ImmutableList.<Location>builder().addAll(oldLocations).addAll(truelyNewLocations).build());
+ }
+
+ for (Location loc : truelyNewLocations) {
+ emit(AbstractEntity.LOCATION_ADDED, loc);
+ }
+ }
+
+ if (getManagementSupport().isDeployed()) {
+ for (Location newLocation : newLocations) {
+ // Location is now reachable, so manage it
+ // TODO will not be required in future releases when creating locations always goes through LocationManager.createLocation(LocationSpec).
+ Locations.manage(newLocation, getManagementContext());
+ }
+ }
+ getManagementSupport().getEntityChangeListener().onLocationsChanged();
+ }
+
+ @Override
+ public void removeLocations(Collection<? extends Location> removedLocations) {
+ synchronized (locations) {
+ List<Location> oldLocations = locations.get();
+ Set<Location> trulyRemovedLocations = Sets.intersection(ImmutableSet.copyOf(removedLocations), ImmutableSet.copyOf(oldLocations));
+ locations.set(MutableList.<Location>builder().addAll(oldLocations).removeAll(removedLocations).buildImmutable());
+
+ for (Location loc : trulyRemovedLocations) {
+ emit(AbstractEntity.LOCATION_REMOVED, loc);
+ }
+ }
+
+ // TODO Not calling `Entities.unmanage(removedLocation)` because this location might be shared with other entities.
+ // Relying on abstractLocation.removeChildLocation unmanaging it, but not ideal as top-level locations will stick
+ // around forever, even if not referenced.
+ // Same goes for AbstractEntity#clearLocations().
+
+ getManagementSupport().getEntityChangeListener().onLocationsChanged();
+ }
+
+ @Override
+ public void clearLocations() {
+ synchronized (locations) {
+ locations.set(ImmutableList.<Location>of());
+ }
+ getManagementSupport().getEntityChangeListener().onLocationsChanged();
+ }
+
+ public Location firstLocation() {
+ synchronized (locations) {
+ return Iterables.get(locations.get(), 0);
+ }
+ }
+
+ /**
+ * Should be invoked at end-of-life to clean up the item.
+ */
+ @Override
+ public void destroy() {
+ }
+
+ @Override
+ public <T> T getAttribute(AttributeSensor<T> attribute) {
+ return attributesInternal.getValue(attribute);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T getAttributeByNameParts(List<String> nameParts) {
+ return (T) attributesInternal.getValue(nameParts);
+ }
+
+ static Set<String> WARNED_READ_ONLY_ATTRIBUTES = Collections.synchronizedSet(MutableSet.<String>of());
+
+ @Override
+ public <T> T setAttribute(AttributeSensor<T> attribute, T val) {
+ if (LOG.isTraceEnabled())
+ LOG.trace(""+this+" setAttribute "+attribute+" "+val);
+
+ if (Boolean.TRUE.equals(getManagementSupport().isReadOnlyRaw())) {
+ T oldVal = getAttribute(attribute);
+ if (Equals.approximately(val, oldVal)) {
+ // ignore, probably an enricher resetting values or something on init
+ } else {
+ String message = this+" setting "+attribute+" = "+val+" (was "+oldVal+") in read only mode; will have very little effect";
+ if (!getManagementSupport().isDeployed()) {
+ if (getManagementSupport().wasDeployed()) message += " (no longer deployed)";
+ else message += " (not yet deployed)";
+ }
+ if (WARNED_READ_ONLY_ATTRIBUTES.add(attribute.getName())) {
+ LOG.warn(message + " (future messages for this sensor logged at trace)");
+ } else if (LOG.isTraceEnabled()) {
+ LOG.trace(message);
+ }
+ }
+ }
+ T result = attributesInternal.update(attribute, val);
+ if (result == null) {
+ // could be this is a new sensor
+ entityType.addSensorIfAbsent(attribute);
+ }
+
+ getManagementSupport().getEntityChangeListener().onAttributeChanged(attribute);
+ return result;
+ }
+
+ @Override
+ public <T> T setAttributeWithoutPublishing(AttributeSensor<T> attribute, T val) {
+ if (LOG.isTraceEnabled())
+ LOG.trace(""+this+" setAttributeWithoutPublishing "+attribute+" "+val);
+
+ T result = attributesInternal.updateWithoutPublishing(attribute, val);
+ if (result == null) {
+ // could be this is a new sensor
+ entityType.addSensorIfAbsentWithoutPublishing(attribute);
+ }
+
+ getManagementSupport().getEntityChangeListener().onAttributeChanged(attribute);
+ return result;
+ }
+
+ @Beta
+ @Override
+ public <T> T modifyAttribute(AttributeSensor<T> attribute, Function<? super T, Maybe<T>> modifier) {
+ if (LOG.isTraceEnabled())
+ LOG.trace(""+this+" modifyAttribute "+attribute+" "+modifier);
+
+ if (Boolean.TRUE.equals(getManagementSupport().isReadOnlyRaw())) {
+ String message = this+" modifying "+attribute+" = "+modifier+" in read only mode; will have very little effect";
+ if (!getManagementSupport().isDeployed()) {
+ if (getManagementSupport().wasDeployed()) message += " (no longer deployed)";
+ else message += " (not yet deployed)";
+ }
+ if (WARNED_READ_ONLY_ATTRIBUTES.add(attribute.getName())) {
+ LOG.warn(message + " (future messages for this sensor logged at trace)");
+ } else if (LOG.isTraceEnabled()) {
+ LOG.trace(message);
+ }
+ }
+ T result = attributesInternal.modify(attribute, modifier);
+ if (result == null) {
+ // could be this is a new sensor
+ entityType.addSensorIfAbsent(attribute);
+ }
+
+ // TODO Conditionally set onAttributeChanged, only if was modified
+ getManagementSupport().getEntityChangeListener().onAttributeChanged(attribute);
+ return result;
+ }
+
+ @Override
+ public void removeAttribute(AttributeSensor<?> attribute) {
+ if (LOG.isTraceEnabled())
+ LOG.trace(""+this+" removeAttribute "+attribute);
+
+ attributesInternal.remove(attribute);
+ entityType.removeSensor(attribute);
+ }
+
+ /** sets the value of the given attribute sensor from the config key value herein
+ * if the attribtue sensor is not-set or null
+ * <p>
+ * returns old value
+ * @deprecated on interface since 0.5.0; use {@link ConfigToAttributes#apply(EntityLocal, AttributeSensorAndConfigKey)} */
+ public <T> T setAttribute(AttributeSensorAndConfigKey<?,T> configuredSensor) {
+ T v = getAttribute(configuredSensor);
+ if (v!=null) return v;
+ v = configuredSensor.getAsSensorValue(this);
+ if (v!=null) return setAttribute(configuredSensor, v);
+ return null;
+ }
+
+ @Override
+ public Map<AttributeSensor, Object> getAllAttributes() {
+ Map<AttributeSensor, Object> result = Maps.newLinkedHashMap();
+ Map<String, Object> attribs = attributesInternal.asMap();
+ for (Map.Entry<String,Object> entry : attribs.entrySet()) {
+ AttributeSensor<?> attribKey = (AttributeSensor<?>) entityType.getSensor(entry.getKey());
+ if (attribKey == null) {
+ // Most likely a race: e.g. persister thread calling getAllAttributes; writer thread
+ // has written attribute value and is in process of calling entityType.addSensorIfAbsent(attribute)
+ // Just use a synthetic AttributeSensor, rather than ignoring value.
+ // TODO If it's not a race, then don't log.warn every time!
+ LOG.warn("When retrieving all attributes of {}, no AttributeSensor for attribute {} (creating synthetic)", this, entry.getKey());
+ attribKey = Sensors.newSensor(Object.class, entry.getKey());
+ }
+ result.put(attribKey, entry.getValue());
+ }
+ return result;
+ }
+
+
+ // -------- CONFIGURATION --------------
+
+ @Override
+ @Beta
+ // the concrete type rather than an interface is returned because Groovy subclasses
+ // complain (incorrectly) if we return ConfigurationSupportInternal
+ // TODO revert to ConfigurationSupportInternal when groovy subclasses work without this (eg new groovy version)
+ public BasicConfigurationSupport config() {
+ return config;
+ }
+
+ /**
+ * Direct use of this class is strongly discouraged. It will become private in a future release,
+ * once {@link #config()} is reverted to return {@link ConfigurationSupportInternal} instead of
+ * {@link BasicConfigurationSupport}.
+ */
+ @Beta
+ // TODO revert to private when config() is reverted to return ConfigurationSupportInternal
+ public class BasicConfigurationSupport implements ConfigurationSupportInternal {
+
+ @Override
+ public <T> T get(ConfigKey<T> key) {
+ return configsInternal.getConfig(key);
+ }
+
+ @Override
+ public <T> T get(HasConfigKey<T> key) {
+ return get(key.getConfigKey());
+ }
+
+ @Override
+ public <T> T set(ConfigKey<T> key, T val) {
+ return setConfigInternal(key, val);
+ }
+
+ @Override
+ public <T> T set(HasConfigKey<T> key, T val) {
+ return set(key.getConfigKey(), val);
+ }
+
+ @Override
+ public <T> T set(ConfigKey<T> key, Task<T> val) {
+ return setConfigInternal(key, val);
+ }
+
+ @Override
+ public <T> T set(HasConfigKey<T> key, Task<T> val) {
+ return set(key.getConfigKey(), val);
+ }
+
+ @Override
+ public ConfigBag getBag() {
+ return configsInternal.getAllConfigBag();
+ }
+
+ @Override
+ public ConfigBag getLocalBag() {
+ return configsInternal.getLocalConfigBag();
+ }
+
+ @Override
+ public Maybe<Object> getRaw(ConfigKey<?> key) {
+ return configsInternal.getConfigRaw(key, true);
+ }
+
+ @Override
+ public Maybe<Object> getRaw(HasConfigKey<?> key) {
+ return getRaw(key.getConfigKey());
+ }
+
+ @Override
+ public Maybe<Object> getLocalRaw(ConfigKey<?> key) {
+ return configsInternal.getConfigRaw(key, false);
+ }
+
+ @Override
+ public Maybe<Object> getLocalRaw(HasConfigKey<?> key) {
+ return getLocalRaw(key.getConfigKey());
+ }
+
+ @Override
+ public void addToLocalBag(Map<String, ?> vals) {
+ configsInternal.addToLocalBag(vals);
+ }
+
+ @Override
+ public void removeFromLocalBag(String key) {
+ configsInternal.removeFromLocalBag(key);
+ }
+
+ @Override
+ public void refreshInheritedConfig() {
+ if (getParent() != null) {
+ configsInternal.setInheritedConfig(((EntityInternal)getParent()).getAllConfig(), ((EntityInternal)getParent()).config().getBag());
+ } else {
+ configsInternal.clearInheritedConfig();
+ }
+
+ refreshInheritedConfigOfChildren();
+ }
+
+ @Override
+ public void refreshInheritedConfigOfChildren() {
+ for (Entity it : getChildren()) {
+ ((EntityInternal)it).config().refreshInheritedConfig();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T setConfigInternal(ConfigKey<T> key, Object val) {
+ if (!inConstruction && getManagementSupport().isDeployed()) {
+ // previously we threw, then warned, but it is still quite common;
+ // so long as callers don't expect miracles, it should be fine.
+ // i (Alex) think the way to be stricter about this (if that becomes needed)
+ // would be to introduce a 'mutable' field on config keys
+ LOG.debug("configuration being made to {} after deployment: {} = {}; change may not be visible in other contexts",
+ new Object[] { this, key, val });
+ }
+ T result = (T) configsInternal.setConfig(key, val);
+
+ getManagementSupport().getEntityChangeListener().onConfigChanged(key);
+ return result;
+
+ }
+ }
+
+ @Override
+ public <T> T getConfig(ConfigKey<T> key) {
+ return config().get(key);
+ }
+
+ @Override
+ public <T> T getConfig(HasConfigKey<T> key) {
+ return config().get(key);
+ }
+
+ @Override
+ @Deprecated
+ public <T> T getConfig(HasConfigKey<T> key, T defaultValue) {
+ return configsInternal.getConfig(key, defaultValue);
+ }
+
+ //don't use groovy defaults for defaultValue as that doesn't implement the contract; we need the above
+ @Override
+ @Deprecated
+ public <T> T getConfig(ConfigKey<T> key, T defaultValue) {
+ return configsInternal.getConfig(key, defaultValue);
+ }
+
+ @Override
+ @Deprecated
+ public Maybe<Object> getConfigRaw(ConfigKey<?> key, boolean includeInherited) {
+ return (includeInherited) ? config().getRaw(key) : config().getLocalRaw(key);
+ }
+
+ @Override
+ @Deprecated
+ public Maybe<Object> getConfigRaw(HasConfigKey<?> key, boolean includeInherited) {
+ return (includeInherited) ? config().getRaw(key) : config().getLocalRaw(key);
+ }
+
+ @Override
+ @Deprecated
+ public <T> T setConfig(ConfigKey<T> key, T val) {
+ return config().set(key, val);
+ }
+
+ @Override
+ @Deprecated
+ public <T> T setConfig(ConfigKey<T> key, Task<T> val) {
+ return config().set(key, val);
+ }
+
+ /**
+ * @deprecated since 0.7.0; use {@code config().set(key, task)}, with {@link Task} instead of {@link DeferredSupplier}
+ */
+ @Deprecated
+ public <T> T setConfig(ConfigKey<T> key, DeferredSupplier val) {
+ return config.setConfigInternal(key, val);
+ }
+
+ @Override
+ @Deprecated
+ public <T> T setConfig(HasConfigKey<T> key, T val) {
+ return config().set(key, val);
+ }
+
+ @Override
+ @Deprecated
+ public <T> T setConfig(HasConfigKey<T> key, Task<T> val) {
+ return (T) config().set(key, val);
+ }
+
+ /**
+ * @deprecated since 0.7.0; use {@code config().set(key, task)}, with {@link Task} instead of {@link DeferredSupplier}
+ */
+ @Deprecated
+ public <T> T setConfig(HasConfigKey<T> key, DeferredSupplier val) {
+ return setConfig(key.getConfigKey(), val);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T setConfigEvenIfOwned(ConfigKey<T> key, T val) {
+ return (T) configsInternal.setConfig(key, val);
+ }
+
+ public <T> T setConfigEvenIfOwned(HasConfigKey<T> key, T val) {
+ return setConfigEvenIfOwned(key.getConfigKey(), val);
+ }
+
+ /**
+ * @deprecated since 0.7.0; use {@code if (val != null) config().set(key, val)}
+ */
+ @Deprecated
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected void setConfigIfValNonNull(ConfigKey key, Object val) {
+ if (val != null) config().set(key, val);
+ }
+
+ /**
+ * @deprecated since 0.7.0; use {@code if (val != null) config().set(key, val)}
+ */
+ @Deprecated
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected void setConfigIfValNonNull(HasConfigKey key, Object val) {
+ if (val != null) config().set(key, val);
+ }
+
+ /**
+ * @deprecated since 0.7.0; see {@code config().refreshInheritedConfig()}
+ */
+ @Override
+ @Deprecated
+ public void refreshInheritedConfig() {
+ config().refreshInheritedConfig();
+ }
+
+ /**
+ * @deprecated since 0.7.0; see {@code config().refreshInheritedConfigOfChildren()}
+ */
+ @Deprecated
+ void refreshInheritedConfigOfChildren() {
+ config().refreshInheritedConfigOfChildren();
+ }
+
+ @Override
+ @Deprecated
+ public EntityConfigMap getConfigMap() {
+ return configsInternal;
+ }
+
+ @Override
+ @Deprecated
+ public Map<ConfigKey<?>,Object> getAllConfig() {
+ return configsInternal.getAllConfig();
+ }
+
+ @Beta
+ @Override
+ @Deprecated
+ public ConfigBag getAllConfigBag() {
+ return config().getBag();
+ }
+
+ @Beta
+ @Override
+ @Deprecated
+ public ConfigBag getLocalConfigBag() {
+ return config().getLocalBag();
+ }
+
+
+ // -------- SUBSCRIPTIONS --------------
+
+ /** @see EntityLocal#subscribe */
+ @Override
+ public <T> SubscriptionHandle subscribe(Entity producer, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+ return getSubscriptionTracker().subscribe(producer, sensor, listener);
+ }
+
+ /** @see EntityLocal#subscribeToChildren */
+ @Override
+ public <T> SubscriptionHandle subscribeToChildren(Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+ return getSubscriptionTracker().subscribeToChildren(parent, sensor, listener);
+ }
+
+ /** @see EntityLocal#subscribeToMembers */
+ @Override
+ public <T> SubscriptionHandle subscribeToMembers(Group group, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+ return getSubscriptionTracker().subscribeToMembers(group, sensor, listener);
+ }
+
+ /**
+ * Unsubscribes the given producer.
+ *
+ * @see SubscriptionContext#unsubscribe(SubscriptionHandle)
+ */
+ @Override
+ public boolean unsubscribe(Entity producer) {
+ return getSubscriptionTracker().unsubscribe(producer);
+ }
+
+ /**
+ * Unsubscribes the given handle.
+ *
+ * @see SubscriptionContext#unsubscribe(SubscriptionHandle)
+ */
+ @Override
+ public boolean unsubscribe(Entity producer, SubscriptionHandle handle) {
+ return getSubscriptionTracker().unsubscribe(producer, handle);
+ }
+
+ @Override
+ public synchronized SubscriptionContext getSubscriptionContext() {
+ return getManagementSupport().getSubscriptionContext();
+ }
+
+ protected synchronized SubscriptionTracker getSubscriptionTracker() {
+ if (_subscriptionTracker == null) {
+ _subscriptionTracker = new SubscriptionTracker(getSubscriptionContext());
+ }
+ return _subscriptionTracker;
+ }
+
+ @Override
+ public synchronized ExecutionContext getExecutionContext() {
+ return getManagementSupport().getExecutionContext();
+ }
+
+ /** Default String representation is simplified name of class, together with selected fields. */
+ @Override
+ public String toString() {
+ return toStringHelper().toString();
+ }
+
+ /**
+ * Override this to add to the toString(), e.g. {@code return super.toStringHelper().add("port", port);}
+ *
+ * Cannot be used in combination with overriding the deprecated toStringFieldsToInclude.
+ */
+ protected ToStringHelper toStringHelper() {
+ return Objects.toStringHelper(this).omitNullValues().add("id", getId());
+// make output more concise by suppressing display name
+// .add("name", getDisplayName());
+ }
+
+ // -------- INITIALIZATION --------------
+
+ /**
+ * Default entity initialization, just calls {@link #initEnrichers()}.
+ */
+ public void init() {
+ super.init();
+ initEnrichers();
+ }
+
+ /**
+ * By default, adds enrichers to populate {@link Attributes#SERVICE_UP} and {@link Attributes#SERVICE_STATE_ACTUAL}
+ * based on {@link Attributes#SERVICE_NOT_UP_INDICATORS},
+ * {@link Attributes#SERVICE_STATE_EXPECTED} and {@link Attributes#SERVICE_PROBLEMS}
+ * (doing nothing if these sensors are not used).
+ * <p>
+ * Subclasses may go further and populate the {@link Attributes#SERVICE_NOT_UP_INDICATORS}
+ * and {@link Attributes#SERVICE_PROBLEMS} from children and members or other sources.
+ */
+ // these enrichers do nothing unless Attributes.SERVICE_NOT_UP_INDICATORS are used
+ // and/or SERVICE_STATE_EXPECTED
+ protected void initEnrichers() {
+ addEnricher(ServiceNotUpLogic.newEnricherForServiceUpIfNotUpIndicatorsEmpty());
+ addEnricher(ServiceStateLogic.newEnricherForServiceStateFromProblemsAndUp());
+ }
+
+ // -------- POLICIES --------------------
+
+ @Override
+ public Collection<Policy> getPolicies() {
+ return ImmutableList.<Policy>copyOf(policies);
+ }
+
+ @Override
+ public void addPolicy(Policy policy) {
+ Policy old = findApparentlyEqualAndWarnIfNotSameUniqueTag(policies, policy);
+ if (old!=null) {
+ LOG.debug("Removing "+old+" when adding "+policy+" to "+this);
+ removePolicy(old);
+ }
+
+ CatalogUtils.setCatalogItemIdOnAddition(this, policy);
+ policies.add((AbstractPolicy)policy);
+ ((AbstractPolicy)policy).setEntity(this);
+
+ getManagementSupport().getEntityChangeListener().onPolicyAdded(policy);
+ emit(AbstractEntity.POLICY_ADDED, new PolicyDescriptor(policy));
+ }
+
+ @Override
+ public <T extends Policy> T addPolicy(PolicySpec<T> spec) {
+ T policy = getManagementContext().getEntityManager().createPolicy(spec);
+ addPolicy(policy);
+ return policy;
+ }
+
+ @Override
+ public <T extends Enricher> T addEnricher(EnricherSpec<T> spec) {
+ T enricher = getManagementContext().getEntityManager().createEnricher(spec);
+ addEnricher(enricher);
+ return enricher;
+ }
+
+ @Override
+ public boolean removePolicy(Policy policy) {
+ ((AbstractPolicy)policy).destroy();
+ boolean changed = policies.remove(policy);
+
+ if (changed) {
+ getManagementSupport().getEntityChangeListener().onPolicyRemoved(policy);
+ emit(AbstractEntity.POLICY_REMOVED, new PolicyDescriptor(policy));
+ }
+ return changed;
+ }
+
+ @Override
+ public boolean removeAllPolicies() {
+ boolean changed = false;
+ for (Policy policy : policies) {
+ removePolicy(policy);
+ changed = true;
+ }
+ return changed;
+ }
+
+ @Override
+ public Collection<Enricher> getEnrichers() {
+ return ImmutableList.<Enricher>copyOf(enrichers);
+ }
+
+ @Override
+ public void addEnricher(Enricher enricher) {
+ Enricher old = findApparentlyEqualAndWarnIfNotSameUniqueTag(enrichers, enricher);
+ if (old!=null) {
+ LOG.debug("Removing "+old+" when adding "+enricher+" to "+this);
+ removeEnricher(old);
+ }
+
+ CatalogUtils.setCatalogItemIdOnAddition(this, enricher);
+ enrichers.add((AbstractEnricher) enricher);
+ ((AbstractEnricher)enricher).setEntity(this);
+
+ getManagementSupport().getEntityChangeListener().onEnricherAdded(enricher);
+ // TODO Could add equivalent of AbstractEntity.POLICY_ADDED for enrichers; no use-case for that yet
+ }
+
+ private <T extends EntityAdjunct> T findApparentlyEqualAndWarnIfNotSameUniqueTag(Collection<? extends T> items, T newItem) {
+ T oldItem = findApparentlyEqual(items, newItem, true);
+
+ if (oldItem!=null) {
+ String oldItemTag = oldItem.getUniqueTag();
+ String newItemTag = newItem.getUniqueTag();
+ if (oldItemTag!=null || newItemTag!=null) {
+ if (Objects.equal(oldItemTag, newItemTag)) {
+ // if same tag, return old item for replacing without comment
+ return oldItem;
+ }
+ // if one has a tag bug not the other, and they are apparently equal,
+ // transfer the tag across
+ T tagged = oldItemTag!=null ? oldItem : newItem;
+ T tagless = oldItemTag!=null ? newItem : oldItem;
+ LOG.warn("Apparently equal items "+oldItem+" and "+newItem+"; but one has a unique tag "+tagged.getUniqueTag()+"; applying to the other");
+ ((AdjunctTagSupport)tagless.tags()).setUniqueTag(tagged.getUniqueTag());
+ }
+
+ if (isRebinding()) {
+ LOG.warn("Adding to "+this+", "+newItem+" appears identical to existing "+oldItem+"; will replace. "
+ + "Underlying addition should be modified so it is not added twice during rebind or unique tag should be used to indicate it is identical.");
+ return oldItem;
+ } else {
+ LOG.warn("Adding to "+this+", "+newItem+" appears identical to existing "+oldItem+"; may get removed on rebind. "
+ + "Underlying addition should be modified so it is not added twice.");
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+ private <T extends EntityAdjunct> T findApparentlyEqual(Collection<? extends T> itemsCopy, T newItem, boolean transferUniqueTag) {
+ // TODO workaround for issue where enrichers/feeds/policies can get added multiple times on rebind,
+ // if it's added in onBecomingManager or connectSensors;
+ // the right fix will be more disciplined about how/where these are added;
+ // furthermore unique tags should be preferred;
+ // when they aren't supplied, a reflection equals is done ignoring selected fields,
+ // which is okay but not great ... and if it misses something (e.g. because an 'equals' isn't implemented)
+ // then you can get a new instance on every rebind
+ // (and currently these aren't readily visible, except looking at the counts or in persisted state)
+ Class<?> beforeEntityAdjunct = newItem.getClass();
+ while (beforeEntityAdjunct.getSuperclass()!=null && !beforeEntityAdjunct.getSuperclass().equals(AbstractEntityAdjunct.class))
+ beforeEntityAdjunct = beforeEntityAdjunct.getSuperclass();
+
+ String newItemTag = newItem.getUniqueTag();
+ for (T oldItem: itemsCopy) {
+ String oldItemTag = oldItem.getUniqueTag();
+ if (oldItemTag!=null && newItemTag!=null) {
+ if (oldItemTag.equals(newItemTag)) {
+ return oldItem;
+ } else {
+ continue;
+ }
+ }
+ // either does not have a unique tag, do deep equality
+ if (oldItem.getClass().equals(newItem.getClass())) {
+ if (EqualsBuilder.reflectionEquals(oldItem, newItem, false,
+ // internal admin in 'beforeEntityAdjunct' should be ignored
+ beforeEntityAdjunct,
+ // known fields which shouldn't block equality checks:
+ // from aggregator
+ "transformation",
+ // from averager
+ "values", "timestamps", "lastAverage",
+ // from some feeds
+ "poller",
+ "pollerStateMutex"
+ )) {
+
+ return oldItem;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean removeEnricher(Enricher enricher) {
+ ((AbstractEnricher)enricher).destroy();
+ boolean changed = enrichers.remove(enricher);
+
+ if (changed) {
+ getManagementSupport().getEntityChangeListener().onEnricherRemoved(enricher);
+ }
+ return changed;
+
+ }
+
+ @Override
+ public boolean removeAllEnrichers() {
+ boolean changed = false;
+ for (AbstractEnricher enricher : enrichers) {
+ changed = removeEnricher(enricher) || changed;
+ }
+ return changed;
+ }
+
+ // -------- FEEDS --------------------
+
+ /**
+ * Convenience, which calls {@link EntityInternal#feeds()} and {@link FeedSupport#addFeed(Feed)}.
+ */
+ @Override
+ public <T extends Feed> T addFeed(T feed) {
+ return feeds().addFeed(feed);
+ }
+
+ @Override
+ public FeedSupport feeds() {
+ return new BasicFeedSupport();
+ }
+
+ @Override
+ @Deprecated
+ public FeedSupport getFeedSupport() {
+ return feeds();
+ }
+
+ protected class BasicFeedSupport implements FeedSupport {
+ @Override
+ public Collection<Feed> getFeeds() {
+ return ImmutableList.<Feed>copyOf(feeds);
+ }
+
+ @Override
+ public <T extends Feed> T addFeed(T feed) {
+ Feed old = findApparentlyEqualAndWarnIfNotSameUniqueTag(feeds, feed);
+ if (old != null) {
+ if (old == feed) {
+ if (!BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_FEED_REGISTRATION_PROPERTY)) {
+ LOG.debug("Feed " + feed + " already added, not adding a second time.");
+ } // else expected to be added a second time through addFeed, ignore
+ return feed;
+ } else {
+ // Different feed object with (seemingly) same functionality, remove previous one, will stop it.
+ LOG.debug("Removing "+old+" when adding "+feed+" to "+this);
+ removeFeed(old);
+ }
+ }
+
+ CatalogUtils.setCatalogItemIdOnAddition(AbstractEntity.this, feed);
+ feeds.add(feed);
+ if (!AbstractEntity.this.equals(((AbstractFeed)feed).getEntity()))
+ ((AbstractFeed)feed).setEntity(AbstractEntity.this);
+
+ getManagementContext().getRebindManager().getChangeListener().onManaged(feed);
+ getManagementSupport().getEntityChangeListener().onFeedAdded(feed);
+ // TODO Could add equivalent of AbstractEntity.POLICY_ADDED for feeds; no use-case for that yet
+
+ return feed;
+ }
+
+ @Override
+ public boolean removeFeed(Feed feed) {
+ feed.stop();
+ boolean changed = feeds.remove(feed);
+
+ if (changed) {
+ getManagementContext().getRebindManager().getChangeListener().onUnmanaged(feed);
+ getManagementSupport().getEntityChangeListener().onFeedRemoved(feed);
+ }
+ return changed;
+ }
+
+ @Override
+ public boolean removeAllFeeds() {
+ boolean changed = false;
+ for (Feed feed : feeds) {
+ changed = removeFeed(feed) || changed;
+ }
+ return changed;
+ }
+ }
+
+ // -------- SENSORS --------------------
+
+ @Override
+ public <T> void emit(Sensor<T> sensor, T val) {
+ if (sensor instanceof AttributeSensor) {
+ LOG.warn("Strongly discouraged use of emit with attribute sensor "+sensor+" "+val+"; use setAttribute instead!",
+ new Throwable("location of discouraged attribute "+sensor+" emit"));
+ }
+ if (val instanceof SensorEvent) {
+ LOG.warn("Strongly discouraged use of emit with sensor event as value "+sensor+" "+val+"; value should be unpacked!",
+ new Throwable("location of discouraged event "+sensor+" emit"));
+ }
+ BrooklynLogging.log(LOG, BrooklynLogging.levelDebugOrTraceIfReadOnly(this),
+ "Emitting sensor notification {} value {} on {}", sensor.getName(), val, this);
+ emitInternal(sensor, val);
+ }
+
+ public <T> void emitInternal(Sensor<T> sensor, T val) {
+ if (getManagementSupport().isNoLongerManaged())
+ throw new IllegalStateException("Entity "+this+" is no longer managed, when trying to publish "+sensor+" "+val);
+
+ SubscriptionContext subsContext = getSubscriptionContext();
+ if (subsContext != null) subsContext.publish(sensor.newEvent(getProxyIfAvailable(), val));
+ }
+
+ // -------- EFFECTORS --------------
+
+ /** Convenience for finding named effector in {@link EntityType#getEffectors()} {@link Map}. */
+ public Effector<?> getEffector(String effectorName) {
+ return entityType.getEffector(effectorName);
+ }
+
+ /** Invoke an {@link Effector} directly. */
+ public <T> Task<T> invoke(Effector<T> eff) {
+ return invoke(MutableMap.of(), eff);
+ }
+
+ public <T> Task<T> invoke(Map parameters, Effector<T> eff) {
+ return invoke(eff, parameters);
+ }
+
+ /**
+ * Additional form supplied for when the parameter map needs to be made explicit.
+ *
+ * @see #invoke(Effector)
+ */
+ @Override
+ public <T> Task<T> invoke(Effector<T> eff, Map<String,?> parameters) {
+ return EffectorUtils.invokeEffectorAsync(this, eff, parameters);
+ }
+
+ /**
+ * Invoked by {@link EntityManagementSupport} when this entity is becoming managed (i.e. it has a working
+ * management context, but before the entity is visible to other entities), including during a rebind.
+ */
+ public void onManagementStarting() {
+ if (isLegacyConstruction()) {
+ entityType.setName(getEntityTypeName());
+ if (displayNameAutoGenerated) displayName.set(getEntityType().getSimpleName()+":"+Strings.maxlen(getId(), 4));
+ }
+ }
+
+ /**
+ * Invoked by {@link EntityManagementSupport} when this entity is fully managed and visible to other entities
+ * through the management context.
+ */
+ public void onManagementStarted() {}
+
+ /**
+ * Invoked by {@link ManagementContext} when this entity becomes managed at a particular management node,
+ * including the initial management started and subsequent management node master-change for this entity.
+ * @deprecated since 0.4.0 override EntityManagementSupport.onManagementStarted if customization needed
+ */
+ public void onManagementBecomingMaster() {}
+
+ /**
+ * Invoked by {@link ManagementContext} when this entity becomes mastered at a particular management node,
+ * including the final management end and subsequent management node master-change for this entity.
+ * @deprecated since 0.4.0 override EntityManagementSupport.onManagementStopped if customization needed
+ */
+ public void onManagementNoLongerMaster() {}
+
+ /**
+ * Invoked by {@link EntityManagementSupport} when this entity is fully unmanaged.
+ * <p>
+ * Note that the activies possible here (when unmanaged) are limited,
+ * and that this event may be caused by either a brooklyn node itself being demoted
+ * (so the entity is managed elsewhere) or by a controlled shutdown.
+ */
+ public void onManagementStopped() {
+ if (getManagementContext().isRunning()) {
+ BrooklynStorage storage = ((ManagementContextInternal)getManagementContext()).getStorage();
+ storage.remove(getId()+"-parent");
+ storage.remove(getId()+"-groups");
+ storage.remove(getId()+"-children");
+ storage.remove(getId()+"-locations");
+ storage.remove(getId()+"-creationTime");
+ storage.remove(getId()+"-displayName");
+ storage.remove(getId()+"-config");
+ storage.remove(getId()+"-attributes");
+ }
+ }
+
+ /** For use by management plane, to invalidate all fields (e.g. when an entity is changing to being proxied) */
+ public void invalidateReferences() {
+ // TODO Just rely on GC of this entity instance, to get rid of the children map etc.
+ // Don't clear it, as it's persisted.
+ // TODO move this to EntityMangementSupport,
+ application = null;
+ }
+
+ @Override
+ public EntityManagementSupport getManagementSupport() {
+ return managementSupport;
+ }
+
+ @Override
+ public void requestPersist() {
+ getManagementSupport().getEntityChangeListener().onChanged();
+ }
+
+ /**
+ * As described in {@link EntityInternal#getRebindSupport()}...
+ * Users are strongly discouraged to call or override this method.
+ * It is for internal calls only, relating to persisting/rebinding entities.
+ * This method may change (or be removed) in a future release without notice.
+ */
+ @Override
+ @Beta
+ public RebindSupport<EntityMemento> getRebindSupport() {
+ return new BasicEntityRebindSupport(this);
+ }
+
+ @Override
+ protected void onTagsChanged() {
+ super.onTagsChanged();
+ getManagementSupport().getEntityChangeListener().onTagsChanged();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/main/java/org/apache/brooklyn/core/entity/Attributes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/Attributes.java b/core/src/main/java/org/apache/brooklyn/core/entity/Attributes.java
new file mode 100644
index 0000000..e585e5d
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/Attributes.java
@@ -0,0 +1,169 @@
+/*
+ * 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 org.apache.brooklyn.core.entity;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.mgmt.ManagementContext;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.api.sensor.Sensor;
+import org.apache.brooklyn.core.config.render.RendererHints;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.sensor.core.BasicAttributeSensor;
+import org.apache.brooklyn.sensor.core.BasicAttributeSensorAndConfigKey;
+import org.apache.brooklyn.sensor.core.BasicNotificationSensor;
+import org.apache.brooklyn.sensor.core.PortAttributeSensorAndConfigKey;
+import org.apache.brooklyn.sensor.core.Sensors;
+import org.apache.brooklyn.util.net.UserAndHostAndPort;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.reflect.TypeToken;
+
+/**
+ * This interface should be used to access {@link Sensor} definitions.
+ */
+public interface Attributes {
+
+ BasicNotificationSensor<Void> LOCATION_CHANGED = new BasicNotificationSensor<Void>(
+ Void.class, "entity.locationChanged", "Indicates that an entity's location has been changed");
+
+ // TODO these should switch to being TemplatedStringAttributeSensorAndConfigKey
+ BasicAttributeSensorAndConfigKey<String> DOWNLOAD_URL = new BasicAttributeSensorAndConfigKey<String>(
+ String.class, "download.url", "URL pattern for downloading the installer (will substitute things like ${version} automatically)");
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ BasicAttributeSensorAndConfigKey<Map<String,String>> DOWNLOAD_ADDON_URLS = new BasicAttributeSensorAndConfigKey(
+ Map.class, "download.addon.urls", "URL patterns for downloading named add-ons (will substitute things like ${version} automatically)");
+
+ /*
+ * Port number attributes.
+ */
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ AttributeSensor<List<Integer>> PORT_NUMBERS = new BasicAttributeSensor(
+ List.class, "port.list", "List of port numbers");
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ AttributeSensor<List<Sensor<Integer>>> PORT_SENSORS = new BasicAttributeSensor(
+ List.class, "port.list.sensors", "List of port number attributes");
+
+ PortAttributeSensorAndConfigKey HTTP_PORT = new PortAttributeSensorAndConfigKey(
+ "http.port", "HTTP port", ImmutableList.of(8080,"18080+"));
+
+ PortAttributeSensorAndConfigKey HTTPS_PORT = new PortAttributeSensorAndConfigKey(
+ "https.port", "HTTP port (with SSL/TLS)", ImmutableList.of(8443,"18443+"));
+
+ PortAttributeSensorAndConfigKey SSH_PORT = new PortAttributeSensorAndConfigKey("ssh.port", "SSH port", 22);
+ PortAttributeSensorAndConfigKey SMTP_PORT = new PortAttributeSensorAndConfigKey("smtp.port", "SMTP port", 25);
+ PortAttributeSensorAndConfigKey DNS_PORT = new PortAttributeSensorAndConfigKey("dns.port", "DNS port", 53);
+ PortAttributeSensorAndConfigKey AMQP_PORT = new PortAttributeSensorAndConfigKey("amqp.port", "AMQP port", "5672+");
+
+ /*
+ * Location/connection attributes.
+ */
+
+ AttributeSensor<String> HOSTNAME = Sensors.newStringSensor( "host.name", "Host name");
+ AttributeSensor<String> ADDRESS = Sensors.newStringSensor( "host.address", "Host IP address");
+ AttributeSensor<UserAndHostAndPort> SSH_ADDRESS = Sensors.newSensor(
+ UserAndHostAndPort.class,
+ "host.sshAddress",
+ "user@host:port for ssh'ing (or null if inappropriate)");
+ AttributeSensor<UserAndHostAndPort> WINRM_ADDRESS = Sensors.newSensor(
+ UserAndHostAndPort.class,
+ "host.winrmAddress",
+ "user@host:port for WinRM'ing (or null if inappropriate)");
+ AttributeSensor<String> SUBNET_HOSTNAME = Sensors.newStringSensor( "host.subnet.hostname", "Host name as known internally in " +
+ "the subnet where it is running (if different to host.name)");
+ AttributeSensor<String> SUBNET_ADDRESS = Sensors.newStringSensor( "host.subnet.address", "Host address as known internally in " +
+ "the subnet where it is running (if different to host.name)");
+
+ AttributeSensor<String> HOST_AND_PORT = Sensors.newStringSensor( "hostandport", "host:port" );
+
+ /*
+ * Lifecycle attributes
+ */
+ AttributeSensor<Boolean> SERVICE_UP = Sensors.newBooleanSensor("service.isUp",
+ "Whether the service is active and availability (confirmed and monitored)");
+
+ @SuppressWarnings("serial")
+ AttributeSensor<Map<String,Object>> SERVICE_NOT_UP_INDICATORS = Sensors.newSensor(
+ new TypeToken<Map<String,Object>>() {},
+ "service.notUp.indicators",
+ "A map of namespaced indicators that the service is not up");
+
+ @SuppressWarnings("serial")
+ AttributeSensor<Map<String,Object>> SERVICE_PROBLEMS = Sensors.newSensor(
+ new TypeToken<Map<String,Object>>() {},
+ "service.problems",
+ "A map of namespaced indicators of problems with a service");
+
+ /**
+ * @since 0.8.0
+ */
+ @SuppressWarnings("serial")
+ AttributeSensor<Map<String,Object>> SERVICE_NOT_UP_DIAGNOSTICS = Sensors.newSensor(
+ new TypeToken<Map<String,Object>>() {},
+ "service.notUp.diagnostics",
+ "A map of namespaced diagnostics, from when the service is not up");
+
+ AttributeSensor<Lifecycle> SERVICE_STATE_ACTUAL = Sensors.newSensor(Lifecycle.class,
+ "service.state", "Actual lifecycle state of the service");
+ AttributeSensor<Lifecycle.Transition> SERVICE_STATE_EXPECTED = Sensors.newSensor(Lifecycle.Transition.class,
+ "service.state.expected", "Last controlled change to service state, indicating what the expected state should be");
+
+ /** @deprecated since 0.7.0 use {@link #SERVICE_STATE_ACTUAL} or {@link #SERVICE_STATE_EXPECTED} as appropriate. */
+ @Deprecated
+ AttributeSensor<Lifecycle> SERVICE_STATE = SERVICE_STATE_ACTUAL;
+
+ /*
+ * Other metadata (optional)
+ */
+
+ AttributeSensor<Integer> PID = Sensors.newIntegerSensor("pid", "Process ID for the previously launched instance");
+
+ AttributeSensor<String> LOG_FILE_LOCATION = Sensors.newStringSensor("log.location", "Log file location");
+
+ AttributeSensor<URI> MAIN_URI = MainUri.MAIN_URI;
+
+ // this class is added because the MAIN_URI relies on a static initialization which unfortunately can't be added to an interface.
+ class MainUri {
+ private final static AttributeSensor<URI> MAIN_URI = Sensors.newSensor(URI.class, "main.uri", "Main URI for contacting the service/endpoint offered by this entity");
+
+ static {
+ RendererHints.register(MAIN_URI, RendererHints.namedActionWithUrl());
+ }
+ }
+
+ /*
+ * Brooklyn management attributes (optional)
+ */
+
+ /**
+ * Used by entities registering a {@link ManagementContext.PropertiesReloadListener} to store a persistent
+ * reference to it, for use when unregistering the listener.
+ */
+ @Beta
+ AttributeSensor<ManagementContext.PropertiesReloadListener> PROPERTIES_RELOAD_LISTENER = Sensors.newSensor(
+ ManagementContext.PropertiesReloadListener.class,
+ "brooklyn.management.propertiesReloadListener", "Properties reload listener");
+
+}