diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java b/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java index d35068f573..2bb27e0ab8 100644 --- a/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java +++ b/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java @@ -47,14 +47,23 @@ @Beta public class AddSensor implements EntityInitializer { - public static final ConfigKey SENSOR_NAME = ConfigKeys.newStringConfigKey("name", "The name of the sensor to create"); - public static final ConfigKey SENSOR_PERIOD = ConfigKeys.newConfigKey(Duration.class, "period", "Period, including units e.g. 1m or 5s or 200ms; default 5 minutes", Duration.FIVE_MINUTES); - public static final ConfigKey SENSOR_TYPE = ConfigKeys.newStringConfigKey("targetType", "Target type for the value; default String", "java.lang.String"); + public static final ConfigKey SENSOR_NAME = ConfigKeys.newStringConfigKey( + "name", "The name of the sensor to create"); + + public static final ConfigKey SENSOR_PERIOD = ConfigKeys.newDurationConfigKey( + "period", + "Period in which the sensor should be updated, including units e.g. 1m, 5s or 200ms; default 5 minutes", + Duration.FIVE_MINUTES); + + public static final ConfigKey SENSOR_TYPE = ConfigKeys.newStringConfigKey( + "targetType", "Target type for the value; default String", + "java.lang.String"); protected final String name; protected final Duration period; protected final String type; protected final AttributeSensor sensor; + protected final ConfigBag params; public AddSensor(Map params) { this(ConfigBag.newInstance(params)); @@ -65,6 +74,7 @@ public AddSensor(final ConfigBag params) { this.period = params.get(SENSOR_PERIOD); this.type = params.get(SENSOR_TYPE); this.sensor = newSensor(); + this.params = params; } @Override diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/ssh/SshCommandEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/ssh/SshCommandEffector.java index b22f717fc1..7f0d9e6066 100644 --- a/core/src/main/java/org/apache/brooklyn/core/effector/ssh/SshCommandEffector.java +++ b/core/src/main/java/org/apache/brooklyn/core/effector/ssh/SshCommandEffector.java @@ -29,6 +29,7 @@ import org.apache.brooklyn.core.effector.Effectors; import org.apache.brooklyn.core.effector.Effectors.EffectorBuilder; import org.apache.brooklyn.core.entity.BrooklynConfigKeys; +import org.apache.brooklyn.core.entity.EntityInitializers; import org.apache.brooklyn.core.sensor.ssh.SshCommandSensor; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.core.config.ConfigBag; @@ -58,45 +59,47 @@ public static EffectorBuilder newEffectorBuilder(ConfigBag params) { protected static class Body extends EffectorBody { private final Effector effector; - private final String command; - private final String executionDir; + private final ConfigBag params; public Body(Effector eff, ConfigBag params) { this.effector = eff; - this.command = Preconditions.checkNotNull(params.get(EFFECTOR_COMMAND), "command must be supplied when defining this effector"); - this.executionDir = params.get(EFFECTOR_EXECUTION_DIR); + Preconditions.checkNotNull(params.getAllConfigRaw().get(EFFECTOR_COMMAND.getName()), "command must be supplied when defining this effector"); + this.params = params; // TODO could take a custom "env" aka effectorShellEnv } @Override public String call(ConfigBag params) { - String command = this.command; - + ConfigBag allConfig = ConfigBag.newInstanceCopying(this.params).putAll(params); + String command = EntityInitializers.resolve(allConfig, EFFECTOR_COMMAND); + String executionDir = EntityInitializers.resolve(allConfig, EFFECTOR_EXECUTION_DIR); + command = SshCommandSensor.makeCommandExecutingInDirectory(command, executionDir, entity()); - + MutableMap env = MutableMap.of(); // first set all declared parameters, including default values - for (ParameterType param: effector.getParameters()) { - env.addIfNotNull(param.getName(), Strings.toString( params.get(Effectors.asConfigKey(param)) )); + for (ParameterType param : effector.getParameters()) { + env.addIfNotNull(param.getName(), Strings.toString(params.get(Effectors.asConfigKey(param)))); } - + // then set things from the entities defined shell environment, if applicable env.putAll(Strings.toStringMap(entity().getConfig(BrooklynConfigKeys.SHELL_ENVIRONMENT), "")); - + // if we wanted to resolve the surrounding environment in real time -- see above // Map paramsResolved = (Map) Tasks.resolveDeepValue(effectorShellEnv, Map.class, entity().getExecutionContext()); - + // finally set the parameters we've been passed; this will repeat declared parameters but to no harm, // it may pick up additional values (could be a flag defining whether this is permitted or not) env.putAll(Strings.toStringMap(params.getAllConfig())); - + SshEffectorTasks.SshEffectorTaskFactory t = SshEffectorTasks.ssh(command) - .requiringZeroAndReturningStdout() - .summary("effector "+effector.getName()) - .environmentVariables(env); + .requiringZeroAndReturningStdout() + .summary("effector " + effector.getName()) + .environmentVariables(env); return queue(t).get(); } - + } - + + } diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/EntityInitializers.java b/core/src/main/java/org/apache/brooklyn/core/entity/EntityInitializers.java index a258007b5b..0f35b7c7aa 100644 --- a/core/src/main/java/org/apache/brooklyn/core/entity/EntityInitializers.java +++ b/core/src/main/java/org/apache/brooklyn/core/entity/EntityInitializers.java @@ -22,6 +22,11 @@ import org.apache.brooklyn.api.entity.EntityInitializer; import org.apache.brooklyn.api.entity.EntityLocal; +import org.apache.brooklyn.api.mgmt.ExecutionContext; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.core.internal.ConfigKeySelfExtracting; +import org.apache.brooklyn.util.core.task.BasicExecutionContext; import com.google.common.collect.ImmutableList; @@ -45,5 +50,30 @@ public void apply(EntityLocal entity) { public static EntityInitializer addingTags(Object... tags) { return new AddTags(tags); } - + + /** + * Resolves key in the + * {@link BasicExecutionContext#getCurrentExecutionContext current execution context}. + * @see #resolve(ConfigBag, ConfigKey, ExecutionContext) + */ + public static T resolve(ConfigBag configBag, ConfigKey key) { + return resolve(configBag, key, BasicExecutionContext.getCurrentExecutionContext()); + } + + /** + * Gets the value for key from configBag. + *

+ * If key is an instance of {@link ConfigKeySelfExtracting} and executionContext is + * not null then its value will be retrieved per the key's implementation of + * {@link ConfigKeySelfExtracting#extractValue extractValue}. Otherwise, the value + * will be retrieved from configBag directly. + */ + public static T resolve(ConfigBag configBag, ConfigKey key, ExecutionContext executionContext) { + if (key instanceof ConfigKeySelfExtracting && executionContext != null) { + ConfigKeySelfExtracting ckse = ((ConfigKeySelfExtracting) key); + return ckse.extractValue(configBag.getAllConfigAsConfigKeyMap(), executionContext); + } + return configBag.get(key); + } + } diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/StaticSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/StaticSensor.java index 18978fac4f..5fa66dded7 100644 --- a/core/src/main/java/org/apache/brooklyn/core/sensor/StaticSensor.java +++ b/core/src/main/java/org/apache/brooklyn/core/sensor/StaticSensor.java @@ -22,7 +22,6 @@ import org.apache.brooklyn.api.entity.EntityLocal; import org.apache.brooklyn.api.mgmt.Task; -import org.apache.brooklyn.api.mgmt.TaskAdaptable; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.effector.AddSensor; @@ -30,7 +29,6 @@ import org.apache.brooklyn.enricher.stock.Propagator; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.core.task.Tasks; -import org.apache.brooklyn.util.core.task.ValueResolver; import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.time.Duration; import org.slf4j.Logger; @@ -47,7 +45,8 @@ * This supports a {@link StaticSensor#SENSOR_PERIOD} * which can be useful if the supplied value is such a function. * However when the source is another sensor, - * consider using {@link Propagator} which listens for changes instead. */ + * consider using {@link Propagator} which listens for changes instead. + */ public class StaticSensor extends AddSensor { private static final Logger log = LoggerFactory.getLogger(StaticSensor.class); @@ -65,18 +64,24 @@ public StaticSensor(ConfigBag params) { timeout = params.get(TIMEOUT); } - @SuppressWarnings("unchecked") @Override public void apply(final EntityLocal entity) { super.apply(entity); class ResolveValue implements Callable> { + @SuppressWarnings("unchecked") @Override public Maybe call() throws Exception { - return Tasks.resolving(value).as((Class)sensor.getType()).timeout(timeout).getMaybe(); + return Tasks.resolving(value) + .as((Class)sensor.getType()) + .timeout(timeout) + .getMaybe(); } } - final Task> resolveValue = Tasks.>builder().displayName("resolving " + value).body(new ResolveValue()).build(); + final Task> resolveValue = Tasks.>builder() + .displayName("resolving " + value) + .body(new ResolveValue()) + .build(); class SetValue implements Callable { @Override diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java index 7b7a908bdf..dced642eb6 100644 --- a/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java +++ b/core/src/main/java/org/apache/brooklyn/core/sensor/password/CreatePasswordSensor.java @@ -24,6 +24,7 @@ import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.effector.AddSensor; +import org.apache.brooklyn.core.entity.EntityInitializers; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.text.Identifiers; @@ -33,23 +34,21 @@ public class CreatePasswordSensor extends AddSensor { public static final ConfigKey ACCEPTABLE_CHARS = ConfigKeys.newStringConfigKey("password.chars", "The characters allowed in password"); - private Integer passwordLength; - private String acceptableChars; - public CreatePasswordSensor(Map params) { this(ConfigBag.newInstance(params)); } public CreatePasswordSensor(ConfigBag params) { super(params); - passwordLength = params.get(PASSWORD_LENGTH); - acceptableChars = params.get(ACCEPTABLE_CHARS); } @Override public void apply(EntityLocal entity) { super.apply(entity); + Integer passwordLength = EntityInitializers.resolve(params, PASSWORD_LENGTH); + String acceptableChars = EntityInitializers.resolve(params, ACCEPTABLE_CHARS); + String password = acceptableChars == null ? Identifiers.makeRandomPassword(passwordLength) : Identifiers.makeRandomPassword(passwordLength, acceptableChars); diff --git a/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java b/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java index 0218a990da..d408a9f75a 100644 --- a/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java +++ b/core/src/main/java/org/apache/brooklyn/core/sensor/ssh/SshCommandSensor.java @@ -26,6 +26,7 @@ import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.effector.AddSensor; import org.apache.brooklyn.core.entity.BrooklynConfigKeys; +import org.apache.brooklyn.core.entity.EntityInitializers; import org.apache.brooklyn.core.sensor.http.HttpRequestSensor; import org.apache.brooklyn.feed.ssh.SshFeed; import org.apache.brooklyn.feed.ssh.SshPollConfig; @@ -33,6 +34,7 @@ import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.core.flags.TypeCoercions; +import org.apache.brooklyn.util.core.task.Tasks; import org.apache.brooklyn.util.os.Os; import org.apache.brooklyn.util.text.Strings; import org.slf4j.Logger; @@ -61,16 +63,9 @@ public final class SshCommandSensor extends AddSensor { + "if not supplied, executes in the entity's run dir (or home dir if no run dir is defined); " + "use '~' to always execute in the home dir, or 'custom-feed/' to execute in a custom-feed dir relative to the run dir"); - protected final String command; - protected final String executionDir; - public SshCommandSensor(final ConfigBag params) { super(params); - - // TODO create a supplier for the command string to support attribute embedding - command = Preconditions.checkNotNull(params.get(SENSOR_COMMAND), "command"); - - executionDir = params.get(SENSOR_EXECUTION_DIR); + Preconditions.checkNotNull(params.getAllConfigRaw().get(SENSOR_COMMAND.getName()), "command"); } @Override @@ -91,6 +86,8 @@ public Map get() { Supplier commandSupplier = new Supplier() { @Override public String get() { + String command = EntityInitializers.resolve(params, SENSOR_COMMAND); + String executionDir = EntityInitializers.resolve(params, SENSOR_EXECUTION_DIR); return makeCommandExecutingInDirectory(command, executionDir, entity); } }; diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/DeferredSupplier.java b/core/src/main/java/org/apache/brooklyn/util/core/task/DeferredSupplier.java index 4a19a74777..6be1941fe7 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/task/DeferredSupplier.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/task/DeferredSupplier.java @@ -24,12 +24,12 @@ * A class that supplies objects of a single type. When used as a ConfigKey value, * the evaluation is deferred until getConfig() is called. The returned value will then * be coerced to the correct type. - * + *

* Subsequent calls to getConfig will result in further calls to deferredProvider.get(), * rather than reusing the result. If you want to reuse the result, consider instead * using a Future. - * - * Note that this functionality replaces the ues of Closure in brooklyn 0.4.0, which + *

+ * Note that this functionality replaces the ues of Closure in brooklyn 0.4.0, which * served the same purpose. */ public interface DeferredSupplier extends Supplier { diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java b/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java index 0f85c3f7b4..6fa6bc41bf 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/task/ValueResolver.java @@ -50,7 +50,7 @@ /** * Resolves a given object, as follows: - *

  • If it is a {@link Tasks} or a {@link DeferredSupplier} then get its contents + *
  • If it is a {@link Task} or a {@link DeferredSupplier} then get its contents *
  • If it's a map and {@link #deep(boolean)} is requested, it applies resolution to contents *
  • It applies coercion *

    diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/java/JmxAttributeSensor.java b/software/base/src/main/java/org/apache/brooklyn/entity/java/JmxAttributeSensor.java index 30829c7c31..3eaaf6fbe3 100644 --- a/software/base/src/main/java/org/apache/brooklyn/entity/java/JmxAttributeSensor.java +++ b/software/base/src/main/java/org/apache/brooklyn/entity/java/JmxAttributeSensor.java @@ -28,6 +28,7 @@ import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.effector.AddSensor; +import org.apache.brooklyn.core.entity.EntityInitializers; import org.apache.brooklyn.core.sensor.DependentConfiguration; import org.apache.brooklyn.core.sensor.http.HttpRequestSensor; import org.apache.brooklyn.core.sensor.ssh.SshCommandSensor; @@ -61,22 +62,8 @@ public final class JmxAttributeSensor extends AddSensor { public static final ConfigKey ATTRIBUTE = ConfigKeys.newStringConfigKey("attribute", "JMX attribute to poll in object"); public static final ConfigKey DEFAULT_VALUE = ConfigKeys.newConfigKey(Object.class, "defaultValue", "Default value for sensor; normally null"); - protected final String objectName; - protected final String attribute; - protected final Object defaultValue; - public JmxAttributeSensor(final ConfigBag params) { super(params); - - objectName = Preconditions.checkNotNull(params.get(OBJECT_NAME), "objectName"); - attribute = Preconditions.checkNotNull(params.get(ATTRIBUTE), "attribute"); - defaultValue = params.get(DEFAULT_VALUE); - - try { - ObjectName.getInstance(objectName); - } catch (MalformedObjectNameException mone) { - throw new IllegalArgumentException("Malformed JMX object name: " + objectName, mone); - } } @Override @@ -88,6 +75,16 @@ public void apply(final EntityLocal entity) { LOG.debug("Submitting task to add JMX sensor {} to {}", name, entity); } + final String objectName = Preconditions.checkNotNull(EntityInitializers.resolve(params, OBJECT_NAME), "objectName"); + final String attribute = Preconditions.checkNotNull(EntityInitializers.resolve(params, ATTRIBUTE), "attribute"); + final Object defaultValue = EntityInitializers.resolve(params, DEFAULT_VALUE); + + try { + ObjectName.getInstance(objectName); + } catch (MalformedObjectNameException mone) { + throw new IllegalArgumentException("Malformed JMX object name: " + objectName, mone); + } + Task jmxPortTask = DependentConfiguration.attributeWhenReady(entity, UsesJmx.JMX_PORT); Task jmxFeedTask = Tasks.builder() .description("Add JMX feed") @@ -100,10 +97,10 @@ public JmxFeed call() throws Exception { .entity(entity) .period(period) .helper(helper) - .pollAttribute(new JmxAttributePollConfig(sensor) + .pollAttribute(new JmxAttributePollConfig<>(sensor) .objectName(objectName) .attributeName(attribute) - .onFailureOrException(Functions.constant((T) defaultValue))) + .onFailureOrException(Functions.constant((T) defaultValue))) .build(); entity.addFeed(feed); return feed;