You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/08/20 00:53:57 UTC
[09/36] incubator-brooklyn git commit: Rename o.a.b.effector.core to
o.a.b.core.effector
Rename o.a.b.effector.core to o.a.b.core.effector
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/8dbb0e4b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/8dbb0e4b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/8dbb0e4b
Branch: refs/heads/master
Commit: 8dbb0e4b2de44840f23f6cce2862dc41ae9ff307
Parents: 4820fa4
Author: Aled Sage <al...@gmail.com>
Authored: Wed Aug 19 22:39:55 2015 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Aug 19 22:39:55 2015 +0100
----------------------------------------------------------------------
.../core/effector/AbstractEffector.java | 90 ++++
.../core/effector/AddChildrenEffector.java | 117 +++++
.../brooklyn/core/effector/AddEffector.java | 116 +++++
.../brooklyn/core/effector/AddSensor.java | 126 ++++++
.../core/effector/BasicParameterType.java | 116 +++++
.../brooklyn/core/effector/EffectorAndBody.java | 60 +++
.../brooklyn/core/effector/EffectorBase.java | 106 +++++
.../brooklyn/core/effector/EffectorBody.java | 100 +++++
.../brooklyn/core/effector/EffectorTasks.java | 229 ++++++++++
.../core/effector/EffectorWithBody.java | 32 ++
.../brooklyn/core/effector/Effectors.java | 202 +++++++++
.../core/effector/ExplicitEffector.java | 74 ++++
.../brooklyn/core/effector/MethodEffector.java | 180 ++++++++
.../core/effector/ssh/SshEffectorTasks.java | 335 ++++++++++++++
.../apache/brooklyn/core/entity/Entities.java | 2 +-
.../brooklyn/core/entity/EntityDynamicType.java | 14 +-
.../core/entity/trait/MemberReplaceable.java | 2 +-
.../brooklyn/core/entity/trait/Resizable.java | 2 +-
.../brooklyn/core/entity/trait/Startable.java | 6 +-
.../core/entity/trait/StartableMethods.java | 2 +-
.../core/mgmt/EntityManagementUtils.java | 2 +-
.../core/mgmt/internal/EffectorUtils.java | 2 +-
.../mgmt/internal/LocalManagementContext.java | 2 +-
.../core/mgmt/persist/XmlMementoSerializer.java | 8 +-
.../core/objs/proxy/EntityProxyImpl.java | 2 +-
.../effector/core/AbstractEffector.java | 90 ----
.../effector/core/AddChildrenEffector.java | 117 -----
.../brooklyn/effector/core/AddEffector.java | 116 -----
.../brooklyn/effector/core/AddSensor.java | 126 ------
.../effector/core/BasicParameterType.java | 116 -----
.../brooklyn/effector/core/EffectorAndBody.java | 60 ---
.../brooklyn/effector/core/EffectorBase.java | 106 -----
.../brooklyn/effector/core/EffectorBody.java | 100 -----
.../brooklyn/effector/core/EffectorTasks.java | 229 ----------
.../effector/core/EffectorWithBody.java | 32 --
.../brooklyn/effector/core/Effectors.java | 202 ---------
.../effector/core/ExplicitEffector.java | 74 ----
.../brooklyn/effector/core/MethodEffector.java | 180 --------
.../effector/core/ssh/SshEffectorTasks.java | 334 --------------
.../brooklyn/entity/group/DynamicCluster.java | 2 +-
.../entity/group/DynamicClusterImpl.java | 2 +-
.../entity/group/DynamicFabricImpl.java | 2 +-
.../brooklyn/entity/group/DynamicGroup.java | 2 +-
.../entity/group/DynamicRegionsFabric.java | 2 +-
.../entity/group/QuarantineGroupImpl.java | 2 +-
.../brooklyn/sensor/core/HttpRequestSensor.java | 2 +-
.../brooklyn/sensor/core/StaticSensor.java | 2 +-
.../windows/WindowsPerformanceCounterFeed.java | 2 +-
.../brooklyn/util/core/task/ssh/SshTasks.java | 2 +-
.../core/effector/EffectorBasicTest.java | 183 ++++++++
.../core/effector/EffectorConcatenateTest.java | 241 ++++++++++
.../core/effector/EffectorMetadataTest.java | 166 +++++++
.../effector/EffectorSayHiGroovyTest.groovy | 182 ++++++++
.../core/effector/EffectorSayHiTest.java | 173 ++++++++
.../core/effector/EffectorTaskTest.java | 437 +++++++++++++++++++
.../core/effector/ssh/SshEffectorTasksTest.java | 265 +++++++++++
.../brooklyn/core/entity/DynamicEntityTest.java | 2 +-
.../brooklyn/core/entity/EntityTypeTest.java | 2 +-
.../brooklyn/core/entity/hello/HelloEntity.java | 2 +-
.../mgmt/osgi/OsgiVersionMoreEntityTest.java | 2 +-
.../rebind/RebindEntityDynamicTypeInfoTest.java | 4 +-
.../brooklyn/core/test/entity/TestEntity.java | 2 +-
.../effector/core/EffectorBasicTest.java | 183 --------
.../effector/core/EffectorConcatenateTest.java | 241 ----------
.../effector/core/EffectorMetadataTest.java | 166 -------
.../core/EffectorSayHiGroovyTest.groovy | 179 --------
.../effector/core/EffectorSayHiTest.java | 173 --------
.../effector/core/EffectorTaskTest.java | 437 -------------------
.../effector/core/ssh/SshEffectorTasksTest.java | 264 -----------
.../location/ssh/SshMachineLocationTest.java | 6 +-
.../util/core/task/ssh/SshTasksTest.java | 2 +-
.../brooklyn/demo/CumulusRDFApplication.java | 6 +-
.../brooklyn/policy/loadbalancing/Movable.java | 2 +-
.../loadbalancing/MockContainerEntity.java | 2 +-
.../postgresql/PostgreSqlNodeSaltImpl.java | 6 +-
.../entity/salt/SaltLifecycleEffectorTasks.java | 2 +-
.../apache/brooklyn/entity/salt/SaltTasks.java | 5 +-
.../postgresql/PostgreSqlSaltLiveTest.java | 4 +-
.../entity/brooklynnode/BrooklynCluster.java | 2 +-
.../brooklynnode/BrooklynEntityMirrorImpl.java | 2 +-
.../entity/brooklynnode/BrooklynNode.java | 2 +-
.../entity/brooklynnode/BrooklynNodeImpl.java | 4 +-
.../brooklynnode/BrooklynNodeSshDriver.java | 2 +-
.../brooklynnode/RemoteEffectorBuilder.java | 4 +-
.../BrooklynClusterUpgradeEffectorBody.java | 4 +-
.../BrooklynNodeUpgradeEffectorBody.java | 6 +-
.../effector/SelectMasterEffectorBody.java | 4 +-
.../SetHighAvailabilityModeEffectorBody.java | 4 +-
...SetHighAvailabilityPriorityEffectorBody.java | 4 +-
.../entity/chef/ChefLifecycleEffectorTasks.java | 2 +-
.../brooklyn/entity/chef/ChefSoloTasks.java | 2 +-
.../apache/brooklyn/entity/chef/ChefTasks.java | 4 +-
.../entity/chef/KnifeConvergeTaskFactory.java | 2 +-
.../java/JavaSoftwareProcessSshDriver.java | 4 +-
.../entity/java/JmxAttributeSensor.java | 2 +-
.../brooklyn/entity/machine/MachineEntity.java | 2 +-
.../entity/machine/MachineEntityImpl.java | 2 +-
.../entity/machine/pool/ServerPool.java | 2 +-
.../entity/machine/pool/ServerPoolImpl.java | 2 +-
.../base/AbstractSoftwareProcessSshDriver.java | 4 +-
.../MachineLifecycleEffectorTasks.java | 6 +-
.../system_service/InitdServiceInstaller.java | 2 +-
.../system_service/SystemServiceEnricher.java | 2 +-
.../brooklyn/sensor/ssh/SshCommandEffector.java | 12 +-
.../brooklyn/sensor/ssh/SshCommandSensor.java | 2 +-
.../brooklynnode/SelectMasterEffectorTest.java | 2 +-
.../ChefSoloDriverMySqlEntityLiveTest.java | 2 +-
.../mysql/ChefSoloDriverToyMySqlEntity.java | 2 +-
.../software/base/SoftwareEffectorTest.java | 6 +-
.../base/SoftwareProcessEntityTest.java | 2 +-
.../base/SoftwareProcessSubclassTest.java | 4 +-
.../test/mysql/AbstractToyMySqlEntityTest.java | 2 +-
.../mysql/DynamicToyMySqlEntityBuilder.java | 2 +-
.../test/ssh/SshCommandIntegrationTest.java | 2 +-
.../SystemServiceEnricherTest.java | 2 +-
.../entity/database/DatastoreMixins.java | 2 +-
.../database/mariadb/MariaDbNodeImpl.java | 2 +-
.../database/mariadb/MariaDbSshDriver.java | 2 +-
.../entity/database/mysql/MySqlNode.java | 2 +-
.../entity/database/mysql/MySqlNodeImpl.java | 2 +-
.../entity/database/mysql/MySqlSshDriver.java | 2 +-
.../database/postgresql/PostgreSqlNode.java | 2 +-
.../PostgreSqlNodeChefImplFromScratch.java | 6 +-
.../database/postgresql/PostgreSqlNodeImpl.java | 2 +-
.../postgresql/PostgreSqlSshDriver.java | 2 +-
.../database/postgresql/PostgreSqlChefTest.java | 4 +-
.../nosql/cassandra/CassandraDatacenter.java | 4 +-
.../cassandra/CassandraDatacenterImpl.java | 2 +-
.../entity/nosql/cassandra/CassandraFabric.java | 2 +-
.../nosql/cassandra/CassandraNodeImpl.java | 2 +-
.../nosql/cassandra/CassandraNodeSshDriver.java | 2 +-
.../nosql/couchbase/CouchbaseClusterImpl.java | 2 +-
.../entity/nosql/couchbase/CouchbaseNode.java | 4 +-
.../nosql/couchbase/CouchbaseNodeImpl.java | 2 +-
.../nosql/couchbase/CouchbaseNodeSshDriver.java | 2 +-
.../entity/nosql/mongodb/MongoDBClient.java | 2 +-
.../brooklyn/entity/nosql/riak/RiakNode.java | 2 +-
.../entity/nosql/riak/RiakNodeSshDriver.java | 2 +-
.../entity/osgi/karaf/KarafContainer.java | 2 +-
.../entity/osgi/karaf/KarafContainerTest.java | 2 +-
.../brooklyn/entity/proxy/LoadBalancer.java | 2 +-
.../entity/proxy/nginx/NginxController.java | 2 +-
.../brooklyn/entity/proxy/nginx/UrlMapping.java | 2 +-
.../entity/webapp/DynamicWebAppClusterImpl.java | 2 +-
.../entity/webapp/JavaWebAppService.java | 2 +-
.../spi/dsl/BrooklynDslDeferredSupplier.java | 2 +-
.../camp/brooklyn/EntitiesYamlTest.java | 2 +-
.../TestSensorAndEffectorInitializer.java | 4 +-
.../brooklyn/VanillaBashNetcatYamlTest.java | 2 +-
.../brooklyn/test/lite/CampYamlLiteTest.java | 4 +-
.../qa/load/SimulatedMySqlNodeImpl.java | 2 +-
.../testing/mocks/RestMockSimpleEntity.java | 2 +-
.../test/osgi/entities/more/MoreEntity.java | 2 +-
.../test/osgi/entities/more/MoreEntityImpl.java | 2 +-
.../test/osgi/entities/more/MoreEntity.java | 2 +-
.../test/osgi/entities/more/MoreEntityImpl.java | 2 +-
.../test/osgi/entities/more/MoreEntity.java | 2 +-
.../test/osgi/entities/more/MoreEntityImpl.java | 2 +-
158 files changed, 3694 insertions(+), 3690 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/AbstractEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/AbstractEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/AbstractEffector.java
new file mode 100644
index 0000000..4acd0e0
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/AbstractEffector.java
@@ -0,0 +1,90 @@
+/*
+ * 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.effector;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.effector.ParameterType;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.core.effector.EffectorTasks.EffectorTaskFactory;
+import org.apache.brooklyn.core.mgmt.internal.EffectorUtils;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.task.DynamicSequentialTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * The abstract {@link Effector} implementation.
+ *
+ * The concrete subclass (often anonymous) will supply the {@link #call(Entity, Map)} implementation,
+ * and the fields in the constructor.
+ */
+public abstract class AbstractEffector<T> extends EffectorBase<T> implements EffectorWithBody<T> {
+
+ private static final long serialVersionUID = 1832435915652457843L;
+
+ @SuppressWarnings("unused")
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractEffector.class);
+
+ public AbstractEffector(String name, Class<T> returnType, List<ParameterType<?>> parameters, String description) {
+ super(name, returnType, parameters, description);
+ }
+
+ public abstract T call(Entity entity, @SuppressWarnings("rawtypes") Map parameters);
+
+ /** Convenience for named-parameter syntax (needs map in first argument) */
+ public T call(Entity entity) { return call(ImmutableMap.of(), entity); }
+
+ /** Convenience for named-parameter syntax (needs map in first argument) */
+ public T call(@SuppressWarnings("rawtypes") Map parameters, Entity entity) { return call(entity, parameters); }
+
+ /** @deprecated since 0.7.0 use {@link #getFlagsForTaskInvocationAt(Entity, Effector, ConfigBag)} */ @Deprecated
+ protected final Map<Object,Object> getFlagsForTaskInvocationAt(Entity entity) {
+ return getFlagsForTaskInvocationAt(entity, this, null);
+ }
+ /** subclasses may override to add additional flags, but they should include the flags returned here
+ * unless there is very good reason not to */
+ protected Map<Object,Object> getFlagsForTaskInvocationAt(Entity entity, Effector<T> effector, ConfigBag parameters) {
+ return EffectorUtils.getTaskFlagsForEffectorInvocation(entity, effector, parameters);
+ }
+
+ /** not meant for overriding; subclasses should override the abstract {@link #call(Entity, Map)} method in this class */
+ @Override
+ public final EffectorTaskFactory<T> getBody() {
+ return new EffectorTaskFactory<T>() {
+ @Override
+ public Task<T> newTask(final Entity entity, final Effector<T> effector, final ConfigBag parameters) {
+ return new DynamicSequentialTask<T>(
+ getFlagsForTaskInvocationAt(entity, AbstractEffector.this, parameters),
+ new Callable<T>() {
+ @Override public T call() {
+ return AbstractEffector.this.call(parameters.getAllConfig(), entity);
+ }
+ });
+ }
+ };
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/AddChildrenEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/AddChildrenEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/AddChildrenEffector.java
new file mode 100644
index 0000000..f2730ca
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/AddChildrenEffector.java
@@ -0,0 +1,117 @@
+/*
+ * 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.effector;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.effector.Effectors.EffectorBuilder;
+import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
+import org.apache.brooklyn.core.mgmt.EntityManagementUtils.CreationResult;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.gson.Gson;
+
+/** Entity initializer which defines an effector which adds a child blueprint to an entity.
+ * <p>
+ * One of the config keys {@link #BLUEPRINT_YAML} (containing a YAML blueprint (map or string))
+ * or {@link #BLUEPRINT_TYPE} (containing a string referring to a catalog type) should be supplied, but not both.
+ * Parameters defined here are supplied as config during the entity creation.
+ *
+ * @since 0.7.0 */
+@Beta
+public class AddChildrenEffector extends AddEffector {
+
+ private static final Logger log = LoggerFactory.getLogger(AddChildrenEffector.class);
+
+ public static final ConfigKey<Object> BLUEPRINT_YAML = ConfigKeys.newConfigKey(Object.class, "blueprint_yaml");
+ public static final ConfigKey<String> BLUEPRINT_TYPE = ConfigKeys.newStringConfigKey("blueprint_type");
+ public static final ConfigKey<Boolean> AUTO_START = ConfigKeys.newBooleanConfigKey("auto_start");
+
+ public AddChildrenEffector(ConfigBag params) {
+ super(newEffectorBuilder(params).build());
+ }
+
+ public AddChildrenEffector(Map<String,String> params) {
+ this(ConfigBag.newInstance(params));
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static EffectorBuilder<List<String>> newEffectorBuilder(ConfigBag params) {
+ EffectorBuilder<List<String>> eff = (EffectorBuilder) AddEffector.newEffectorBuilder(List.class, params);
+ eff.impl(new Body(eff.buildAbstract(), params));
+ return eff;
+ }
+
+ protected static class Body extends EffectorBody<List<String>> {
+
+ private final Effector<?> effector;
+ private final String blueprintBase;
+ private final Boolean autostart;
+
+ public Body(Effector<?> eff, ConfigBag params) {
+ this.effector = eff;
+ String newBlueprint = null;
+ Object yaml = params.get(BLUEPRINT_YAML);
+ if (yaml instanceof Map) {
+ newBlueprint = new Gson().toJson(yaml);
+ } else if (yaml instanceof String) {
+ newBlueprint = (String) yaml;
+ } else if (yaml!=null) {
+ throw new IllegalArgumentException(this+" requires map or string in "+BLUEPRINT_YAML+"; not "+yaml.getClass()+" ("+yaml+")");
+ }
+ String blueprintType = params.get(BLUEPRINT_TYPE);
+ if (blueprintType!=null) {
+ if (newBlueprint!=null) {
+ throw new IllegalArgumentException(this+" cannot take both "+BLUEPRINT_TYPE+" and "+BLUEPRINT_YAML);
+ }
+ newBlueprint = "services: [ { type: "+blueprintType+" } ]";
+ }
+ if (newBlueprint==null) {
+ throw new IllegalArgumentException(this+" requires either "+BLUEPRINT_TYPE+" or "+BLUEPRINT_YAML);
+ }
+ blueprintBase = newBlueprint;
+ autostart = params.get(AUTO_START);
+ }
+
+ @Override
+ public List<String> call(ConfigBag params) {
+ params = getMergedParams(effector, params);
+
+ String blueprint = blueprintBase;
+ if (!params.isEmpty()) {
+ blueprint = blueprint+"\n"+"brooklyn.config: "+
+ new Gson().toJson(params.getAllConfig());
+ }
+
+ log.debug(this+" adding children to "+entity()+":\n"+blueprint);
+ CreationResult<List<Entity>, List<String>> result = EntityManagementUtils.addChildren(entity(), blueprint, autostart);
+ log.debug(this+" added children to "+entity()+": "+result.get());
+ return result.task().getUnchecked();
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/AddEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/AddEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/AddEffector.java
new file mode 100644
index 0000000..9590bcf
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/AddEffector.java
@@ -0,0 +1,116 @@
+/*
+ * 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.effector;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.effector.ParameterType;
+import org.apache.brooklyn.api.entity.EntityInitializer;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.config.MapConfigKey;
+import org.apache.brooklyn.core.effector.Effectors.EffectorBuilder;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.text.Strings;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+/**
+ * Entity initializer which adds an effector to an entity.
+ * <p>
+ * This instance provides a {@link #newEffectorBuilder(Class, ConfigBag)}
+ * which returns an abstract (body-less) effector defining:
+ * <li> the name from {@link #EFFECTOR_NAME};
+ * <li> the description from {@link #EFFECTOR_DESCRIPTION}
+ * <li> the parameters from {@link #EFFECTOR_PARAMETER_DEFS}
+ * <p>
+ * Callers should pass the effector to instantiate into the constructor.
+ * Often subclasses will supply a constructor which takes a ConfigBag of parameters,
+ * and a custom {@link #newEffectorBuilder(Class, ConfigBag)} which adds the body
+ * before passing to this class.
+ * <p>
+ * Note that the parameters passed to the call method in the body of the effector implementation
+ * are only those supplied by a user at runtime; in order to merge with default
+ * values, use {@link #getMergedParams(Effector, ConfigBag)}.
+ *
+ * @since 0.7.0 */
+@Beta
+public class AddEffector implements EntityInitializer {
+
+ public static final ConfigKey<String> EFFECTOR_NAME = ConfigKeys.newStringConfigKey("name");
+ public static final ConfigKey<String> EFFECTOR_DESCRIPTION = ConfigKeys.newStringConfigKey("description");
+
+ public static final ConfigKey<Map<String,Object>> EFFECTOR_PARAMETER_DEFS = new MapConfigKey<Object>(Object.class, "parameters");
+
+ final Effector<?> effector;
+
+ public AddEffector(Effector<?> effector) {
+ this.effector = Preconditions.checkNotNull(effector, "effector");
+ }
+
+ @Override
+ public void apply(EntityLocal entity) {
+ ((EntityInternal)entity).getMutableEntityType().addEffector(effector);
+ }
+
+ public static <T> EffectorBuilder<T> newEffectorBuilder(Class<T> type, ConfigBag params) {
+ String name = Preconditions.checkNotNull(params.get(EFFECTOR_NAME), "name must be supplied when defining an effector: %s", params);
+ EffectorBuilder<T> eff = Effectors.effector(type, name);
+ eff.description(params.get(EFFECTOR_DESCRIPTION));
+
+ Map<String, Object> paramDefs = params.get(EFFECTOR_PARAMETER_DEFS);
+ if (paramDefs!=null) {
+ for (Map.Entry<String, Object> paramDef: paramDefs.entrySet()){
+ if (paramDef!=null) {
+ String paramName = paramDef.getKey();
+ Object value = paramDef.getValue();
+ if (value==null) value = Collections.emptyMap();
+ if (!(value instanceof Map)) {
+ if (value instanceof CharSequence && Strings.isBlank((CharSequence) value))
+ value = Collections.emptyMap();
+ }
+ if (!(value instanceof Map))
+ throw new IllegalArgumentException("Illegal argument of type "+value.getClass()+" value '"+value+"' supplied as parameter definition "
+ + "'"+paramName);
+ eff.parameter(ConfigKeys.DynamicKeys.newNamedInstance(paramName, (Map<?, ?>) value));
+ }
+ }
+ }
+
+ return eff;
+ }
+
+ /** returns a ConfigBag containing the merger of the supplied parameters with default values on the effector-defined parameters */
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public static ConfigBag getMergedParams(Effector<?> eff, ConfigBag params) {
+ ConfigBag result = ConfigBag.newInstanceCopying(params);
+ for (ParameterType<?> param: eff.getParameters()) {
+ ConfigKey key = Effectors.asConfigKey(param);
+ if (!result.containsKey(key))
+ result.configure(key, params.get(key));
+ }
+ return result;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..3c08831
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/AddSensor.java
@@ -0,0 +1,126 @@
+/*
+ * 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.effector;
+
+import java.util.Map;
+
+import org.apache.brooklyn.api.entity.EntityInitializer;
+import org.apache.brooklyn.api.entity.EntityLocal;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.sensor.core.Sensors;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.javalang.Boxing;
+import org.apache.brooklyn.util.time.Duration;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+/**
+ * Creates a new {@link AttributeSensor} on an entity.
+ * <p>
+ * The configuration can include the sensor {@code name}, {@code period} and {@code targetType}.
+ * For the targetType, currently this only supports classes on the initial classpath, not those in
+ * OSGi bundles added at runtime.
+ *
+ * @since 0.7.0
+ */
+@Beta
+public class AddSensor<T> implements EntityInitializer {
+
+ public static final ConfigKey<String> SENSOR_NAME = ConfigKeys.newStringConfigKey("name", "The name of the sensor to create");
+ public static final ConfigKey<Duration> 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<String> 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<T> sensor;
+
+ public AddSensor(Map<String, String> params) {
+ this(ConfigBag.newInstance(params));
+ }
+
+ public AddSensor(final ConfigBag params) {
+ this.name = Preconditions.checkNotNull(params.get(SENSOR_NAME), "Name must be supplied when defining a sensor");
+ this.period = params.get(SENSOR_PERIOD);
+ this.type = params.get(SENSOR_TYPE);
+ this.sensor = newSensor();
+ }
+
+ @Override
+ public void apply(EntityLocal entity) {
+ ((EntityInternal) entity).getMutableEntityType().addSensor(sensor);
+ }
+
+ private AttributeSensor<T> newSensor() {
+ String className = getFullClassName(type);
+ Class<T> clazz = getType(className);
+ return Sensors.newSensor(clazz, name);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Class<T> getType(String className) {
+ try {
+ // TODO use OSGi loader (low priority however); also ensure that allows primitives
+ Maybe<Class<?>> primitive = Boxing.getPrimitiveType(className);
+ if (primitive.isPresent()) return (Class<T>) primitive.get();
+ return (Class<T>) Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ if (!className.contains(".")) {
+ // could be assuming "java.lang" package; try again with that
+ try {
+ return (Class<T>) Class.forName("java.lang."+className);
+ } catch (ClassNotFoundException e2) {
+ throw new IllegalArgumentException("Invalid target type for sensor "+name+": " + className+" (also tried java.lang."+className+")");
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid target type for sensor "+name+": " + className);
+ }
+ }
+ }
+
+ protected String getFullClassName(String className) {
+ if (className.equalsIgnoreCase("string")) {
+ return "java.lang.String";
+ } else if (className.equalsIgnoreCase("int") || className.equalsIgnoreCase("integer")) {
+ return "java.lang.Integer";
+ } else if (className.equalsIgnoreCase("long")) {
+ return "java.lang.Long";
+ } else if (className.equalsIgnoreCase("float")) {
+ return "java.lang.Float";
+ } else if (className.equalsIgnoreCase("double")) {
+ return "java.lang.Double";
+ } else if (className.equalsIgnoreCase("bool") || className.equalsIgnoreCase("boolean")) {
+ return "java.lang.Boolean";
+ } else if (className.equalsIgnoreCase("byte")) {
+ return "java.lang.Byte";
+ } else if (className.equalsIgnoreCase("char") || className.equalsIgnoreCase("character")) {
+ return "java.lang.Character";
+ } else if (className.equalsIgnoreCase("object")) {
+ return "java.lang.Object";
+ } else {
+ return className;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/BasicParameterType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/BasicParameterType.java b/core/src/main/java/org/apache/brooklyn/core/effector/BasicParameterType.java
new file mode 100644
index 0000000..eb0417f
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/BasicParameterType.java
@@ -0,0 +1,116 @@
+/*
+ * 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.effector;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.brooklyn.api.effector.ParameterType;
+
+import com.google.common.base.Objects;
+
+public class BasicParameterType<T> implements ParameterType<T> {
+ private static final long serialVersionUID = -5521879180483663919L;
+
+ private String name;
+ private Class<T> type;
+ private String description;
+ private Boolean hasDefaultValue = null;
+ private T defaultValue = null;
+
+ public BasicParameterType() {
+ this(Collections.emptyMap());
+ }
+
+ @SuppressWarnings("unchecked")
+ public BasicParameterType(Map<?, ?> arguments) {
+ if (arguments.containsKey("name")) name = (String) arguments.get("name");
+ if (arguments.containsKey("type")) type = (Class<T>) arguments.get("type");
+ if (arguments.containsKey("description")) description = (String) arguments.get("description");
+ if (arguments.containsKey("defaultValue")) defaultValue = (T) arguments.get("defaultValue");
+ }
+
+ public BasicParameterType(String name, Class<T> type) {
+ this(name, type, null, null, false);
+ }
+
+ public BasicParameterType(String name, Class<T> type, String description) {
+ this(name, type, description, null, false);
+ }
+
+ public BasicParameterType(String name, Class<T> type, String description, T defaultValue) {
+ this(name, type, description, defaultValue, true);
+ }
+
+ public BasicParameterType(String name, Class<T> type, String description, T defaultValue, boolean hasDefaultValue) {
+ this.name = name;
+ this.type = type;
+ this.description = description;
+ this.defaultValue = defaultValue;
+ if (defaultValue!=null && !defaultValue.getClass().equals(Object.class)) {
+ // if default value is null (or is an Object, which is ambiguous on resolution to to rebind),
+ // don't bother to set this as it creates noise in the persistence files
+ this.hasDefaultValue = hasDefaultValue;
+ }
+ }
+
+ @Override
+ public String getName() { return name; }
+
+ @Override
+ public Class<T> getParameterClass() { return type; }
+
+ @Override
+ public String getParameterClassName() { return type.getCanonicalName(); }
+
+ @Override
+ public String getDescription() { return description; }
+
+ @Override
+ public T getDefaultValue() {
+ return hasDefaultValue() ? defaultValue : null;
+ }
+
+ public boolean hasDefaultValue() {
+ // a new Object() was previously used to indicate no default value, but that doesn't work well across serialization boundaries!
+ return hasDefaultValue!=null ? hasDefaultValue : defaultValue!=null && !defaultValue.getClass().equals(Object.class);
+ }
+
+ @Override
+ public String toString() {
+ return Objects.toStringHelper(this).omitNullValues()
+ .add("name", name).add("description", description).add("type", getParameterClassName())
+ .add("defaultValue", defaultValue)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name, description, type, defaultValue);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof ParameterType) &&
+ Objects.equal(name, ((ParameterType<?>)obj).getName()) &&
+ Objects.equal(description, ((ParameterType<?>)obj).getDescription()) &&
+ Objects.equal(type, ((ParameterType<?>)obj).getParameterClass()) &&
+ Objects.equal(defaultValue, ((ParameterType<?>)obj).getDefaultValue());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorAndBody.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/EffectorAndBody.java b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorAndBody.java
new file mode 100644
index 0000000..49e85b8
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorAndBody.java
@@ -0,0 +1,60 @@
+/*
+ * 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.effector;
+
+import java.util.List;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.effector.ParameterType;
+import org.apache.brooklyn.core.effector.EffectorTasks.EffectorTaskFactory;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+
+@Beta // added in 0.6.0
+public class EffectorAndBody<T> extends EffectorBase<T> implements EffectorWithBody<T> {
+
+ private static final long serialVersionUID = -6023389678748222968L;
+ private final EffectorTaskFactory<T> body;
+
+ public EffectorAndBody(Effector<T> original, EffectorTaskFactory<T> body) {
+ this(original.getName(), original.getReturnType(), original.getParameters(), original.getDescription(), body);
+ }
+
+ public EffectorAndBody(String name, Class<T> returnType, List<ParameterType<?>> parameters, String description, EffectorTaskFactory<T> body) {
+ super(name, returnType, parameters, description);
+ this.body = body;
+ }
+
+ @Override
+ public EffectorTaskFactory<T> getBody() {
+ return body;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(super.hashCode(), getBody());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return super.equals(other) && Objects.equal(getBody(), ((EffectorAndBody<?>)other).getBody());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorBase.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/EffectorBase.java b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorBase.java
new file mode 100644
index 0000000..68132c4
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorBase.java
@@ -0,0 +1,106 @@
+/*
+ * 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.effector;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.effector.ParameterType;
+import org.apache.brooklyn.core.effector.EffectorTasks.EffectorTaskFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+
+/** concrete implementation of Effector interface,
+ * but not (at this level of the hirarchy) defining an implementation
+ * (see {@link EffectorTaskFactory} and {@link EffectorWithBody}) */
+public class EffectorBase<T> implements Effector<T> {
+
+ @SuppressWarnings("unused")
+ private static final Logger log = LoggerFactory.getLogger(EffectorBase.class);
+
+ private static final long serialVersionUID = -4153962199078384835L;
+
+ private final String name;
+ private final Class<T> returnType;
+ private final List<ParameterType<?>> parameters;
+ private final String description;
+
+ public EffectorBase(String name, Class<T> returnType, List<ParameterType<?>> parameters, String description) {
+ this.name = name;
+ this.returnType = returnType;
+ this.parameters = new ArrayList<ParameterType<?>>(parameters);
+ this.description = description;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Class<T> getReturnType() {
+ return returnType;
+ }
+
+ @Override
+ public String getReturnTypeName() {
+ return returnType.getCanonicalName();
+ }
+
+ @Override
+ public List<ParameterType<?>> getParameters() {
+ return parameters;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public String toString() {
+ List<String> parameterNames = new ArrayList<String>(parameters.size());
+ for (ParameterType<?> parameter: parameters) {
+ String parameterName = (parameter.getName() != null) ? parameter.getName() : "<unknown>";
+ parameterNames.add(parameterName);
+ }
+ return name+"["+Joiner.on(",").join(parameterNames)+"]";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name, returnType, parameters, description);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof EffectorBase)) return false;
+ if (!(other.getClass().equals(getClass()))) return false;
+ if (!Objects.equal(hashCode(), other.hashCode())) return false;
+ return Objects.equal(getName(), ((EffectorBase<?>)other).getName()) &&
+ Objects.equal(getReturnType(), ((EffectorBase<?>)other).getReturnType()) &&
+ Objects.equal(getParameters(), ((EffectorBase<?>)other).getParameters()) &&
+ Objects.equal(getDescription(), ((EffectorBase<?>)other).getDescription());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorBody.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/EffectorBody.java b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorBody.java
new file mode 100644
index 0000000..b1643ba
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorBody.java
@@ -0,0 +1,100 @@
+/*
+ * 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.effector;
+
+import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.api.mgmt.TaskAdaptable;
+import org.apache.brooklyn.api.mgmt.TaskFactory;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.core.task.DynamicSequentialTask;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.core.task.Tasks;
+
+import com.google.common.annotations.Beta;
+
+/** Typical implementations override {@link #main(ConfigBag)} to do the work of the effector
+ * <p>
+ * See also {@link EffectorTasks}: possibly this will be deleted in preference for an approach based on {@link EffectorTasks}.
+ *
+ * @since 0.6.0
+ **/
+@Beta
+public abstract class EffectorBody<T> {
+ /** Does the work of the effector, either in place, or (better) by building up
+ * subtasks, which can by added using {@link DynamicTasks} methods
+ * (and various convenience methods which do that automatically; see subclasses of EffectorBody
+ * for more info on usage; or see {@link DynamicSequentialTask} for details of the threading model
+ * by which added tasks are placed in a secondary thread)
+ * <p>
+ * The associated entity can be accessed through the {@link #entity()} method.
+ */
+ public abstract T call(ConfigBag parameters);
+
+ // NB: we could also support an 'init' method which is done at creation,
+ // as a place where implementers can describe the structure of the task before it executes
+ // (and init gets invoked in EffectorBodyTaskFactory.newTask _before_ the task is submitted and main is called)
+
+
+ // ---- convenience method(s) for implementers of main -- see subclasses and *Tasks statics for more
+
+ protected EntityInternal entity() {
+ return (EntityInternal) BrooklynTaskTags.getTargetOrContextEntity(Tasks.current());
+ }
+
+ protected <V extends TaskAdaptable<?>> V queue(V task) {
+ return DynamicTasks.queue(task);
+ }
+
+ protected <V extends TaskAdaptable<?>> void queue(V task1, V task2, V ...tasks) {
+ DynamicTasks.queue(task1);
+ DynamicTasks.queue(task2);
+ for (V task: tasks)
+ DynamicTasks.queue(task);
+ }
+
+ protected <V extends TaskFactory<?>> void queue(V task1, V task2, V ...tasks) {
+ DynamicTasks.queue(task1.newTask());
+ DynamicTasks.queue(task2.newTask());
+ for (V task: tasks)
+ DynamicTasks.queue(task.newTask());
+ }
+
+ protected <U extends TaskAdaptable<?>> U queue(TaskFactory<U> task) {
+ return DynamicTasks.queue(task.newTask());
+ }
+
+ /** see {@link DynamicTasks#waitForLast()} */
+ protected Task<?> waitForLast() {
+ return DynamicTasks.waitForLast();
+ }
+
+ /** Returns the result of the last task queued in this context, coerced to the given type */
+ protected <V> V last(Class<V> type) {
+ Task<?> last = waitForLast();
+ if (last==null)
+ throw new IllegalStateException("No last task available (in "+DynamicTasks.getTaskQueuingContext()+")");
+ if (!Tasks.isQueuedOrSubmitted(last))
+ throw new IllegalStateException("Last task "+last+" has not been queued or submitted; will not block on its result");
+
+ return TypeCoercions.coerce(last.getUnchecked(), type);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorTasks.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/EffectorTasks.java b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorTasks.java
new file mode 100644
index 0000000..39eb79b
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorTasks.java
@@ -0,0 +1,229 @@
+/*
+ * 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.effector;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.effector.ParameterType;
+import org.apache.brooklyn.api.entity.Entity;
+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.location.Machines;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.core.mgmt.internal.EffectorUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.location.winrm.WinRmMachineLocation;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.task.DynamicSequentialTask;
+import org.apache.brooklyn.util.core.task.DynamicTasks;
+import org.apache.brooklyn.util.core.task.TaskBuilder;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.javalang.Reflections;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+/**
+ * Miscellaneous tasks which are useful in effectors.
+ * @since 0.6.0
+ */
+@Beta
+public class EffectorTasks {
+
+ @SuppressWarnings("unused")
+ private static final Logger log = LoggerFactory.getLogger(EffectorTasks.class);
+
+ public interface EffectorTaskFactory<T> {
+ public abstract TaskAdaptable<T> newTask(Entity entity, Effector<T> effector, ConfigBag parameters);
+ }
+
+ /** wrapper for {@link EffectorBody} which simply runs that body on each invocation;
+ * the body must be thread safe and ideally stateless */
+ public static class EffectorBodyTaskFactory<T> implements EffectorTaskFactory<T> {
+ private final EffectorBody<T> effectorBody;
+ public EffectorBodyTaskFactory(EffectorBody<T> effectorBody) {
+ this.effectorBody = effectorBody;
+ }
+
+ @Override
+ public Task<T> newTask(final Entity entity, final org.apache.brooklyn.api.effector.Effector<T> effector, final ConfigBag parameters) {
+ final AtomicReference<DynamicSequentialTask<T>> dst = new AtomicReference<DynamicSequentialTask<T>>();
+
+ dst.set(new DynamicSequentialTask<T>(
+ getFlagsForTaskInvocationAt(entity, effector, parameters),
+ new Callable<T>() {
+ @Override
+ public T call() throws Exception {
+ try {
+ DynamicTasks.setTaskQueueingContext(dst.get());
+ return effectorBody.call(parameters);
+ } finally {
+ DynamicTasks.removeTaskQueueingContext();
+ }
+ }
+ }) {
+ @Override
+ public void handleException(Throwable throwable) throws Exception {
+ EffectorUtils.handleEffectorException(entity, effector, throwable);
+ }
+ });
+ return dst.get();
+ };
+
+ /** @deprecated since 0.7.0 use {@link #getFlagsForTaskInvocationAt(Entity, Effector, ConfigBag)} */ @Deprecated
+ protected final Map<Object,Object> getFlagsForTaskInvocationAt(Entity entity, Effector<?> effector) {
+ return getFlagsForTaskInvocationAt(entity, effector, null);
+ }
+ /** subclasses may override to add additional flags, but they should include the flags returned here
+ * unless there is very good reason not to; default impl returns a MutableMap */
+ protected Map<Object,Object> getFlagsForTaskInvocationAt(Entity entity, Effector<?> effector, ConfigBag parameters) {
+ return EffectorUtils.getTaskFlagsForEffectorInvocation(entity, effector, parameters);
+ }
+ }
+
+ /** wrapper for {@link EffectorTaskFactory} which ensures effector task tags are applied to it if needed
+ * (wrapping in a task if needed); without this, {@link EffectorBody}-based effectors get it by
+ * virtue of the call to {@link #getFlagsForTaskInvocationAt(Entity,Effector,ConfigBag)} therein
+ * but {@link EffectorTaskFactory}-based effectors generate a task without the right tags
+ * to be able to tell using {@link BrooklynTaskTags} the effector-context of the task
+ * <p>
+ * this gets applied automatically so marked as package-private */
+ static class EffectorMarkingTaskFactory<T> implements EffectorTaskFactory<T> {
+ private final EffectorTaskFactory<T> effectorTaskFactory;
+ public EffectorMarkingTaskFactory(EffectorTaskFactory<T> effectorTaskFactory) {
+ this.effectorTaskFactory = effectorTaskFactory;
+ }
+
+ @Override
+ public Task<T> newTask(final Entity entity, final org.apache.brooklyn.api.effector.Effector<T> effector, final ConfigBag parameters) {
+ if (effectorTaskFactory instanceof EffectorBodyTaskFactory)
+ return effectorTaskFactory.newTask(entity, effector, parameters).asTask();
+ // if we're in an effector context for this effector already, then also pass through
+ if (BrooklynTaskTags.isInEffectorTask(Tasks.current(), entity, effector, false))
+ return effectorTaskFactory.newTask(entity, effector, parameters).asTask();
+ // otherwise, create the task inside an appropriate effector body so tags, name, etc are set correctly
+ return new EffectorBodyTaskFactory<T>(new EffectorBody<T>() {
+ @Override
+ public T call(ConfigBag parameters) {
+ TaskAdaptable<T> t = DynamicTasks.queue(effectorTaskFactory.newTask(entity, effector, parameters));
+ return t.asTask().getUnchecked();
+ }
+ }).newTask(entity, effector, parameters);
+ }
+ }
+
+ public static <T> ConfigKey<T> asConfigKey(ParameterType<T> t) {
+ return ConfigKeys.newConfigKey(t.getParameterClass(), t.getName());
+ }
+
+ public static <T> ParameterTask<T> parameter(ParameterType<T> t) {
+ return new ParameterTask<T>(asConfigKey(t)).
+ name("parameter "+t);
+ }
+ public static <T> ParameterTask<T> parameter(Class<T> type, String name) {
+ return new ParameterTask<T>(ConfigKeys.newConfigKey(type, name)).
+ name("parameter "+name+" ("+type+")");
+ }
+ public static <T> ParameterTask<T> parameter(final ConfigKey<T> p) {
+ return new ParameterTask<T>(p);
+ }
+ public static class ParameterTask<T> implements EffectorTaskFactory<T> {
+ final ConfigKey<T> p;
+ private TaskBuilder<T> builder;
+ public ParameterTask(ConfigKey<T> p) {
+ this.p = p;
+ this.builder = Tasks.<T>builder().name("parameter "+p);
+ }
+ public ParameterTask<T> name(String taskName) {
+ builder.name(taskName);
+ return this;
+ }
+ @Override
+ public Task<T> newTask(Entity entity, Effector<T> effector, final ConfigBag parameters) {
+ return builder.body(new Callable<T>() {
+ @Override
+ public T call() throws Exception {
+ return parameters.get(p);
+ }
+
+ }).build();
+ }
+
+ }
+
+ public static <T> EffectorTaskFactory<T> of(final Task<T> task) {
+ return new EffectorTaskFactory<T>() {
+ @Override
+ public Task<T> newTask(Entity entity, Effector<T> effector, ConfigBag parameters) {
+ return task;
+ }
+ };
+ }
+
+ /** Finds the entity where this task is running
+ * @throws NullPointerException if there is none (no task, or no context entity for that task) */
+ public static Entity findEntity() {
+ return Preconditions.checkNotNull(BrooklynTaskTags.getTargetOrContextEntity(Tasks.current()),
+ "This must be executed in a task whose execution context has a target or context entity " +
+ "(i.e. it must be run from within an effector)");
+ }
+
+ /** Finds the entity where this task is running, casted to the given Entity subtype
+ * @throws NullPointerException if there is none
+ * @throws IllegalArgumentException if it is not of the indicated type */
+ public static <T extends Entity> T findEntity(Class<T> type) {
+ Entity t = findEntity();
+ return Reflections.cast(t, type);
+ }
+
+ /** Finds a unique {@link SshMachineLocation} attached to the entity
+ * where this task is running
+ * @throws NullPointerException if {@link #findEntity()} fails
+ * @throws IllegalStateException if call to {@link #getSshMachine(Entity)} fails */
+ public static SshMachineLocation findSshMachine() {
+ return getSshMachine(findEntity());
+ }
+
+ /** Finds a unique {@link SshMachineLocation} attached to the supplied entity
+ * @throws IllegalStateException if there is not a unique such {@link SshMachineLocation} */
+ public static SshMachineLocation getSshMachine(Entity entity) {
+ try {
+ return Machines.findUniqueSshMachineLocation(entity.getLocations()).get();
+ } catch (Exception e) {
+ throw new IllegalStateException("Entity "+entity+" (in "+Tasks.current()+") requires a single SshMachineLocation, but has "+entity.getLocations(), e);
+ }
+ }
+
+ /** Finds a unique {@link WinRmMachineLocation} attached to the supplied entity
+ * @throws IllegalStateException if there is not a unique such {@link WinRmMachineLocation} */
+ public static WinRmMachineLocation getWinRmMachine(Entity entity) {
+ try {
+ return Machines.findUniqueWinRmMachineLocation(entity.getLocations()).get();
+ } catch (Exception e) {
+ throw new IllegalStateException("Entity "+entity+" (in "+Tasks.current()+") requires a single WinRmMachineLocation, but has "+entity.getLocations(), e);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorWithBody.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/EffectorWithBody.java b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorWithBody.java
new file mode 100644
index 0000000..67dba14
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/EffectorWithBody.java
@@ -0,0 +1,32 @@
+/*
+ * 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.effector;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.core.effector.EffectorTasks.EffectorTaskFactory;
+
+import com.google.common.annotations.Beta;
+
+@Beta // added in 0.6.0
+public interface EffectorWithBody<T> extends Effector<T> {
+
+ /** returns the body of the effector, i.e. a factory which can generate tasks which can run */
+ public EffectorTaskFactory<T> getBody();
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java b/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
new file mode 100644
index 0000000..63ea52d
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/Effectors.java
@@ -0,0 +1,202 @@
+/*
+ * 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.effector;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.effector.ParameterType;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.EntityLocal;
+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.EffectorTasks.EffectorBodyTaskFactory;
+import org.apache.brooklyn.core.effector.EffectorTasks.EffectorMarkingTaskFactory;
+import org.apache.brooklyn.core.effector.EffectorTasks.EffectorTaskFactory;
+import org.apache.brooklyn.core.entity.Entities;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.config.ConfigBag;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.text.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class Effectors {
+
+ private static final Logger log = LoggerFactory.getLogger(Effectors.class);
+
+ public static class EffectorBuilder<T> {
+ private Class<T> returnType;
+ private String effectorName;
+ private String description;
+ private Map<String,ParameterType<?>> parameters = new LinkedHashMap<String,ParameterType<?>>();
+ private EffectorTaskFactory<T> impl;
+
+ private EffectorBuilder(Class<T> returnType, String effectorName) {
+ this.returnType = returnType;
+ this.effectorName = effectorName;
+ }
+ public EffectorBuilder<T> description(String description) {
+ this.description = description;
+ return this;
+ }
+ public EffectorBuilder<T> parameter(Class<?> paramType, String paramName) {
+ return parameter(paramType, paramName, null, null);
+ }
+ public EffectorBuilder<T> parameter(Class<?> paramType, String paramName, String paramDescription) {
+ return parameter(paramType, paramName, paramDescription, null);
+ }
+ public <V> EffectorBuilder<T> parameter(Class<V> paramType, String paramName, String paramDescription, V defaultValue) {
+ return parameter(new BasicParameterType<V>(paramName, paramType, paramDescription, defaultValue));
+ }
+ public <V> EffectorBuilder<T> parameter(ConfigKey<V> key) {
+ return parameter(asParameterType(key));
+ }
+ public EffectorBuilder<T> parameter(ParameterType<?> p) {
+ // allow redeclaring, e.g. for the case where we are overriding an existing effector
+ parameters.put(p.getName(), p);
+ return this;
+ }
+ public EffectorBuilder<T> impl(EffectorTaskFactory<T> taskFactory) {
+ this.impl = new EffectorMarkingTaskFactory<T>(taskFactory);
+ return this;
+ }
+ public EffectorBuilder<T> impl(EffectorBody<T> effectorBody) {
+ this.impl = new EffectorBodyTaskFactory<T>(effectorBody);
+ return this;
+ }
+ /** returns the effector, with an implementation (required); @see {@link #buildAbstract()} */
+ public Effector<T> build() {
+ Preconditions.checkNotNull(impl, "Cannot create effector %s with no impl (did you forget impl? or did you mean to buildAbstract?)", effectorName);
+ return new EffectorAndBody<T>(effectorName, returnType, ImmutableList.copyOf(parameters.values()), description, impl);
+ }
+
+ /** returns an abstract effector, where the body will be defined later/elsewhere
+ * (impl must not be set) */
+ public Effector<T> buildAbstract() {
+ Preconditions.checkArgument(impl==null, "Cannot create abstract effector {} as an impl is defined", effectorName);
+ return new EffectorBase<T>(effectorName, returnType, ImmutableList.copyOf(parameters.values()), description);
+ }
+ }
+
+ /** creates a new effector builder with the given name and return type */
+ public static <T> EffectorBuilder<T> effector(Class<T> returnType, String effectorName) {
+ return new EffectorBuilder<T>(returnType, effectorName);
+ }
+
+ /** creates a new effector builder to _override_ the given effector */
+ public static <T> EffectorBuilder<T> effector(Effector<T> base) {
+ EffectorBuilder<T> builder = new EffectorBuilder<T>(base.getReturnType(), base.getName());
+ for (ParameterType<?> p: base.getParameters())
+ builder.parameter(p);
+ builder.description(base.getDescription());
+ if (base instanceof EffectorWithBody)
+ builder.impl(((EffectorWithBody<T>) base).getBody());
+ return builder;
+ }
+
+ /** as {@link #invocation(Entity, Effector, Map)} but convenience for passing a {@link ConfigBag} */
+ public static <T> TaskAdaptable<T> invocation(Entity entity, Effector<T> eff, ConfigBag parameters) {
+ return invocation(entity, eff, parameters==null ? ImmutableMap.of() : parameters.getAllConfig());
+ }
+
+ /** returns an unsubmitted task which invokes the given effector; use {@link Entities#invokeEffector(EntityLocal, Entity, Effector, Map)} for a submitted variant */
+ public static <T> TaskAdaptable<T> invocation(Entity entity, Effector<T> eff, @Nullable Map<?,?> parameters) {
+ @SuppressWarnings("unchecked")
+ Effector<T> eff2 = (Effector<T>) ((EntityInternal)entity).getEffector(eff.getName());
+ if (log.isTraceEnabled()) {
+ Object eff1Body = (eff instanceof EffectorWithBody<?> ? ((EffectorWithBody<?>) eff).getBody() : "bodyless");
+ String message = String.format("Invoking %s/%s on entity %s", eff, eff1Body, entity);
+ if (eff != eff2) {
+ Object eff2Body = (eff2 instanceof EffectorWithBody<?> ? ((EffectorWithBody<?>) eff2).getBody() : "bodyless");
+ message += String.format(" (actually %s/%s)", eff2, eff2Body);
+ }
+ log.trace(message);
+ }
+ if (eff2 != null) {
+ if (eff2 != eff) {
+ if (eff2 instanceof EffectorWithBody) {
+ log.debug("Replacing invocation of {} on {} with {} which is the impl defined at that entity", new Object[] { eff, entity, eff2 });
+ return ((EffectorWithBody<T>)eff2).getBody().newTask(entity, eff2, ConfigBag.newInstance().putAll(parameters));
+ } else {
+ log.warn("Effector {} defined on {} has no body; invoking caller-supplied {} instead", new Object[] { eff2, entity, eff });
+ }
+ }
+ } else {
+ log.debug("Effector {} does not exist on {}; attempting to invoke anyway", new Object[] { eff, entity });
+ }
+
+ if (eff instanceof EffectorWithBody) {
+ return ((EffectorWithBody<T>)eff).getBody().newTask(entity, eff, ConfigBag.newInstance().putAll(parameters));
+ }
+
+ throw new UnsupportedOperationException("No implementation registered for effector "+eff+" on "+entity);
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static <V> ParameterType<V> asParameterType(ConfigKey<V> key) {
+ return key.hasDefaultValue()
+ ? new BasicParameterType<V>(key.getName(), (Class)key.getType(), key.getDescription(), key.getDefaultValue())
+ : new BasicParameterType<V>(key.getName(), (Class)key.getType(), key.getDescription());
+ }
+
+ public static <V> ConfigKey<V> asConfigKey(ParameterType<V> paramType) {
+ return ConfigKeys.newConfigKey(paramType.getParameterClass(), paramType.getName(), paramType.getDescription(), paramType.getDefaultValue());
+ }
+
+ /** returns an unsubmitted task which will invoke the given effector on the given entities;
+ * return type is Task<List<T>> (but haven't put in the blood sweat toil and tears to make the generics work) */
+ public static TaskAdaptable<List<?>> invocation(Effector<?> eff, Map<?,?> params, Iterable<? extends Entity> entities) {
+ List<TaskAdaptable<?>> tasks = new ArrayList<TaskAdaptable<?>>();
+ for (Entity e: entities) tasks.add(invocation(e, eff, params));
+ return Tasks.parallel("invoking "+eff+" on "+tasks.size()+" node"+(Strings.s(tasks.size())), tasks.toArray(new TaskAdaptable[tasks.size()]));
+ }
+
+ /** returns an unsubmitted task which will invoke the given effector on the given entities
+ * (this form of method is a convenience for {@link #invocation(Effector, Map, Iterable)}) */
+ public static TaskAdaptable<List<?>> invocation(Effector<?> eff, MutableMap<?, ?> params, Entity ...entities) {
+ return invocation(eff, params, Arrays.asList(entities));
+ }
+
+ public static boolean sameSignature(Effector<?> e1, Effector<?> e2) {
+ return Objects.equal(e1.getName(), e2.getName()) &&
+ Objects.equal(e1.getParameters(), e2.getParameters()) &&
+ Objects.equal(e1.getReturnType(), e2.getReturnType());
+ }
+
+ // TODO sameSignatureAndBody
+
+ public static boolean sameInstance(Effector<?> e1, Effector<?> e2) {
+ return e1 == e2;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/ExplicitEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/ExplicitEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/ExplicitEffector.java
new file mode 100644
index 0000000..65c1f0c
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/ExplicitEffector.java
@@ -0,0 +1,74 @@
+/*
+ * 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.effector;
+
+import groovy.lang.Closure;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.effector.ParameterType;
+import org.apache.brooklyn.api.entity.Entity;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+
+public abstract class ExplicitEffector<I,T> extends AbstractEffector<T> {
+ public ExplicitEffector(String name, Class<T> type, String description) {
+ this(name, type, ImmutableList.<ParameterType<?>>of(), description);
+ }
+ public ExplicitEffector(String name, Class<T> type, List<ParameterType<?>> parameters, String description) {
+ super(name, type, parameters, description);
+ }
+
+ public T call(Entity entity, Map parameters) {
+ return invokeEffector((I) entity, (Map<String,?>)parameters );
+ }
+
+ public abstract T invokeEffector(I trait, Map<String,?> parameters);
+
+ /** convenience to create an effector supplying a closure; annotations are preferred,
+ * and subclass here would be failback, but this is offered as
+ * workaround for bug GROOVY-5122, as discussed in test class CanSayHi
+ */
+ public static <I,T> ExplicitEffector<I,T> create(String name, Class<T> type, List<ParameterType<?>> parameters, String description, Closure body) {
+ return new ExplicitEffectorFromClosure<I,T>(name, type, parameters, description, body);
+ }
+
+ private static class ExplicitEffectorFromClosure<I,T> extends ExplicitEffector<I,T> {
+ private static final long serialVersionUID = -5771188171702382236L;
+ final Closure<T> body;
+ public ExplicitEffectorFromClosure(String name, Class<T> type, List<ParameterType<?>> parameters, String description, Closure<T> body) {
+ super(name, type, parameters, description);
+ this.body = body;
+ }
+ public T invokeEffector(I trait, Map<String,?> parameters) { return body.call(trait, parameters); }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(super.hashCode(), body);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return super.equals(other) && Objects.equal(body, ((ExplicitEffectorFromClosure<?,?>)other).body);
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/main/java/org/apache/brooklyn/core/effector/MethodEffector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/MethodEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/MethodEffector.java
new file mode 100644
index 0000000..ad53adb
--- /dev/null
+++ b/core/src/main/java/org/apache/brooklyn/core/effector/MethodEffector.java
@@ -0,0 +1,180 @@
+/*
+ * 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.effector;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.api.effector.Effector;
+import org.apache.brooklyn.api.effector.ParameterType;
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.core.annotation.EffectorParam;
+import org.apache.brooklyn.core.entity.AbstractEntity;
+import org.apache.brooklyn.core.mgmt.internal.EffectorUtils;
+import org.apache.brooklyn.util.core.flags.TypeCoercions;
+import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.groovy.GroovyJavaMethods;
+import org.codehaus.groovy.runtime.MethodClosure;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Lists;
+
+/** concrete class for providing an Effector implementation that gets its information from annotations on a method;
+ * see Effector*Test for usage example.
+ * <p>
+ * note that the method must be on an interface in order for it to be remoted, with the current implementation.
+ * see comments in {@link #call(Entity, Map)} for more details.
+ */
+public class MethodEffector<T> extends AbstractEffector<T> {
+
+ private static final long serialVersionUID = 6989688364011965968L;
+ private static final Logger log = LoggerFactory.getLogger(MethodEffector.class);
+
+ @SuppressWarnings("rawtypes")
+ public static Effector<?> create(Method m) {
+ return new MethodEffector(m);
+ }
+
+ protected static class AnnotationsOnMethod {
+ final Class<?> clazz;
+ final String name;
+ final String description;
+ final Class<?> returnType;
+ final List<ParameterType<?>> parameters;
+
+ public AnnotationsOnMethod(Class<?> clazz, String methodName) {
+ this(clazz, inferBestMethod(clazz, methodName));
+ }
+
+ public AnnotationsOnMethod(Class<?> clazz, Method method) {
+ this.clazz = clazz;
+ this.name = method.getName();
+ this.returnType = method.getReturnType();
+
+ // Get the description
+ org.apache.brooklyn.core.annotation.Effector effectorAnnotation = method.getAnnotation(org.apache.brooklyn.core.annotation.Effector.class);
+ description = (effectorAnnotation != null) ? effectorAnnotation.description() : null;
+
+ // Get the parameters
+ parameters = Lists.newArrayList();
+ int numParameters = method.getParameterTypes().length;
+ for (int i = 0; i < numParameters; i++) {
+ parameters.add(toParameterType(method, i));
+ }
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ protected static ParameterType<?> toParameterType(Method method, int paramIndex) {
+ Annotation[] anns = method.getParameterAnnotations()[paramIndex];
+ Class<?> type = method.getParameterTypes()[paramIndex];
+ EffectorParam paramAnnotation = findAnnotation(anns, EffectorParam.class);
+
+ // TODO if blank, could do "param"+(i+1); would that be better?
+ // TODO this will now give "" if name is blank, rather than previously null. Is that ok?!
+ String name = (paramAnnotation != null) ? paramAnnotation.name() : null;
+
+ String paramDescription = (paramAnnotation == null || EffectorParam.MAGIC_STRING_MEANING_NULL.equals(paramAnnotation.description())) ? null : paramAnnotation.description();
+ String description = (paramDescription != null) ? paramDescription : null;
+
+ String paramDefaultValue = (paramAnnotation == null || EffectorParam.MAGIC_STRING_MEANING_NULL.equals(paramAnnotation.defaultValue())) ? null : paramAnnotation.defaultValue();
+ Object defaultValue = (paramDefaultValue != null) ? TypeCoercions.coerce(paramDefaultValue, type) : null;
+
+ return new BasicParameterType(name, type, description, defaultValue);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static <T extends Annotation> T findAnnotation(Annotation[] anns, Class<T> type) {
+ for (Annotation ann : anns) {
+ if (type.isInstance(ann)) return (T) ann;
+ }
+ return null;
+ }
+
+ protected static Method inferBestMethod(Class<?> clazz, String methodName) {
+ Method best = null;
+ for (Method it : clazz.getMethods()) {
+ if (it.getName().equals(methodName)) {
+ if (best==null || best.getParameterTypes().length < it.getParameterTypes().length) best=it;
+ }
+ }
+ if (best==null) {
+ throw new IllegalStateException("Cannot find method "+methodName+" on "+clazz.getCanonicalName());
+ }
+ return best;
+ }
+ }
+
+ /** Defines a new effector whose details are supplied as annotations on the given type and method name */
+ public MethodEffector(Class<?> whereEffectorDefined, String methodName) {
+ this(new AnnotationsOnMethod(whereEffectorDefined, methodName), null);
+ }
+
+ public MethodEffector(Method method) {
+ this(new AnnotationsOnMethod(method.getDeclaringClass(), method), null);
+ }
+
+ public MethodEffector(MethodClosure mc) {
+ this(new AnnotationsOnMethod((Class<?>)mc.getDelegate(), mc.getMethod()), null);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected MethodEffector(AnnotationsOnMethod anns, String description) {
+ super(anns.name, (Class<T>)anns.returnType, anns.parameters, GroovyJavaMethods.<String>elvis(description, anns.description));
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public T call(Entity entity, Map parameters) {
+ Object[] parametersArray = EffectorUtils.prepareArgsForEffector(this, parameters);
+ if (entity instanceof AbstractEntity) {
+ return EffectorUtils.invokeMethodEffector(entity, this, parametersArray);
+ } else {
+ // we are dealing with a proxy here
+ // this implementation invokes the method on the proxy
+ // (requiring it to be on the interface)
+ // and letting the proxy deal with the remoting / runAtEntity;
+ // alternatively we could create the task here and pass it to runAtEntity;
+ // the latter may allow us to simplify/remove a lot of the stuff from
+ // EffectorUtils and possibly Effectors and Entities
+
+ // TODO Should really find method with right signature, rather than just the right args.
+ // TODO prepareArgs can miss things out that have "default values"! Code below will probably fail if that happens.
+ Method[] methods = entity.getClass().getMethods();
+ for (Method method : methods) {
+ if (method.getName().equals(getName())) {
+ if (parametersArray.length == method.getParameterTypes().length) {
+ try {
+ return (T) method.invoke(entity, parametersArray);
+ } catch (Exception e) {
+ // exception handled by the proxy invocation (which leads to EffectorUtils.invokeEffectorMethod...)
+ throw Exceptions.propagate(e);
+ }
+ }
+ }
+ }
+ String msg = "Could not find method for effector "+getName()+" with "+parametersArray.length+" parameters on "+entity;
+ log.warn(msg+" (throwing); available methods are: "+Arrays.toString(methods));
+ throw new IllegalStateException(msg);
+ }
+ }
+
+}