From a37d8884dfe5027c57355cd7e6abe74cf6a1df03 Mon Sep 17 00:00:00 2001 From: Sam Corbett Date: Mon, 10 Oct 2016 21:49:29 +0100 Subject: [PATCH 1/2] Delete unnecessary SuppressWarnings annotation in InvokeEffectorOnSensorChange --- .../brooklyn/policy/InvokeEffectorOnSensorChange.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/src/main/java/org/apache/brooklyn/policy/InvokeEffectorOnSensorChange.java b/core/src/main/java/org/apache/brooklyn/policy/InvokeEffectorOnSensorChange.java index 8a81f49e56..ebb6da8427 100644 --- a/core/src/main/java/org/apache/brooklyn/policy/InvokeEffectorOnSensorChange.java +++ b/core/src/main/java/org/apache/brooklyn/policy/InvokeEffectorOnSensorChange.java @@ -18,9 +18,6 @@ */ package org.apache.brooklyn.policy; -import java.util.Map; - -import org.apache.brooklyn.api.effector.Effector; import org.apache.brooklyn.api.entity.EntityLocal; import org.apache.brooklyn.api.sensor.Sensor; import org.apache.brooklyn.api.sensor.SensorEvent; @@ -62,10 +59,9 @@ public void setEntity(EntityLocal entity) { subscriptions().subscribe(entity, (Sensor)sensor, this); } - @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public void onEvent(SensorEvent event) { - entity.invoke((Effector)entity.getEntityType().getEffectorByName( getConfig(EFFECTOR) ).get(), (Map)MutableMap.of()); + entity.invoke(entity.getEntityType().getEffectorByName(getConfig(EFFECTOR)).get(), MutableMap.of()); } } From ebd32a8c1ac1eb6a3daceb0bc5cd248c3014f3de Mon Sep 17 00:00:00 2001 From: Sam Corbett Date: Mon, 10 Oct 2016 21:44:02 +0100 Subject: [PATCH 2/2] ProxyEffector Forwards effector invocations from one entity to another. --- .../camp/brooklyn/ProxyEffectorYamlTest.java | 93 ++++++++++++++++++ .../brooklyn/core/effector/ProxyEffector.java | 96 +++++++++++++++++++ .../core/effector/ProxyEffectorTest.java | 90 +++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ProxyEffectorYamlTest.java create mode 100644 core/src/main/java/org/apache/brooklyn/core/effector/ProxyEffector.java create mode 100644 core/src/test/java/org/apache/brooklyn/core/effector/ProxyEffectorTest.java diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ProxyEffectorYamlTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ProxyEffectorYamlTest.java new file mode 100644 index 0000000000..b7303020fa --- /dev/null +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ProxyEffectorYamlTest.java @@ -0,0 +1,93 @@ +/* + * 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.camp.brooklyn; + +import static org.apache.brooklyn.test.Asserts.assertEquals; + +import java.util.Iterator; + +import org.apache.brooklyn.api.effector.Effector; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.entity.stock.BasicEntity; +import org.apache.brooklyn.test.Asserts; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; + +public class ProxyEffectorYamlTest extends AbstractYamlTest { + + @Test + public void testProxyEffector() throws Exception { + final String effectorName = "proxy-effector"; + Entity app = createAndStartApplication( + "location: localhost", + "services:", + "- id: target", + " type: " + TestEntity.class.getName(), + "- type: " + BasicEntity.class.getName(), + " brooklyn.initializers:", + " - type: org.apache.brooklyn.core.effector.ProxyEffector", + " brooklyn.config:", + " name: " + effectorName, + " targetEntity: $brooklyn:entity(\"target\")", + " targetEffector: identityEffector" + ); + waitForApplicationTasks(app); + + assertEquals(app.getChildren().size(), 2, "expected two elements in " + app.getChildren()); + Entity basicEntity; + Iterator ei = app.getChildren().iterator(); + basicEntity = ei.next(); + if (!BasicEntity.class.isAssignableFrom(basicEntity.getClass())) { + basicEntity = ei.next(); + } + + Effector effector = basicEntity.getEntityType().getEffectorByName(effectorName).get(); + Object result = basicEntity.invoke(effector, ImmutableMap.of("arg", "hello, world")).get(); + assertEquals(((String) result).trim(), "hello, world"); +} + + @Test + public void testThrowsIfTargetDoesNotResolve() throws Exception { + final String effectorName = "proxy-effector"; + Entity app = createAndStartApplication( + "location: localhost", + "services:", + "- type: " + BasicEntity.class.getName(), + " brooklyn.initializers:", + " - type: org.apache.brooklyn.core.effector.ProxyEffector", + " brooklyn.config:", + " name: " + effectorName, + " targetEntity: $brooklyn:entity(\"skhbfskdbf\")", + " targetEffector: identityEffector" + ); + waitForApplicationTasks(app); + Entity basicEntity = app.getChildren().iterator().next(); + Effector effector = basicEntity.getEntityType().getEffectorByName(effectorName).get(); + try { + basicEntity.invoke(effector, ImmutableMap.of()).get(); + Asserts.shouldHaveFailedPreviously("expected exception when invoking effector that does not exist"); + } catch (Exception e) { + Asserts.expectedFailure(e); + } + } + +} diff --git a/core/src/main/java/org/apache/brooklyn/core/effector/ProxyEffector.java b/core/src/main/java/org/apache/brooklyn/core/effector/ProxyEffector.java new file mode 100644 index 0000000000..596cb771af --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/effector/ProxyEffector.java @@ -0,0 +1,96 @@ +/* + * 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.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.internal.EffectorUtils; +import org.apache.brooklyn.util.core.config.ConfigBag; +import org.apache.brooklyn.util.core.task.Tasks; +import org.apache.brooklyn.util.guava.Maybe; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +public class ProxyEffector extends AddEffector { + + private static final Logger LOG = LoggerFactory.getLogger(ProxyEffector.class); + + public static final ConfigKey TARGET_ENTITY = ConfigKeys.newConfigKey(Entity.class, + "targetEntity", "The proxy target"); + + public static final ConfigKey TARGET_EFFECTOR_NAME = ConfigKeys.newStringConfigKey( + "targetEffector", "The effector to invoke on the target entity"); + + public ProxyEffector(ConfigBag params) { + super(newEffectorBuilder(params).build()); + } + + public ProxyEffector(Map params) { + this(ConfigBag.newInstance(params)); + } + + public static EffectorBuilder newEffectorBuilder(ConfigBag params) { + EffectorBuilder eff = AddEffector.newEffectorBuilder(Object.class, params); + eff.impl(new Body(eff.buildAbstract(), params)); + return eff; + } + + protected static class Body extends EffectorBody { + + private final Object target; + private final String effectorName; + + public Body(Effector eff, ConfigBag params) { + // Don't use getConfig(TARGET_ENTITY) because DslComponents can't be + // coerced to entities at this point. + this.target = Preconditions.checkNotNull(params.getAllConfigRaw().get(TARGET_ENTITY.getName()), + "Target entity must be supplied when defining this effector"); + this.effectorName = Preconditions.checkNotNull(params.get(TARGET_EFFECTOR_NAME), "Target effector name must be supplied when defining this effector"); + } + + @Override + public Object call(ConfigBag params) { + Entity target = resolveTarget().get(); + return invokeEffectorNamed(target, effectorName, params); + } + + private Maybe resolveTarget() { + return Tasks.resolving(target, Entity.class) + .context(entity()) + .getMaybe(); + } + + private Object invokeEffectorNamed(Entity target, String effectorName, ConfigBag params) { + LOG.debug("{} forwarding effector invocation on {} to entity={}, effector={}, parameters={}", + new Object[]{this, entity(), target, effectorName, params}); + Effector effector = EffectorUtils.findEffectorDeclared(target, effectorName).get(); + return target.invoke(effector, params.getAllConfig()).getUnchecked(); + } + } + + +} diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/ProxyEffectorTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/ProxyEffectorTest.java new file mode 100644 index 0000000000..a8757969d4 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/effector/ProxyEffectorTest.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 static org.testng.Assert.assertEquals; + +import java.util.NoSuchElementException; + +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.EntityInternal; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.entity.stock.BasicEntity; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.time.Duration; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; + +public class ProxyEffectorTest extends BrooklynAppUnitTestSupport { + + @Test + public void testHappyPath() { + TestEntity a = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + ProxyEffector proxyEffector = new ProxyEffector(ImmutableMap.of( + AddEffector.EFFECTOR_NAME, "proxy-effector", + ProxyEffector.TARGET_ENTITY, a, + ProxyEffector.TARGET_EFFECTOR_NAME, "identityEffector")); + // BasicEntity doesn't have an identityEffector. + EntityInternal b = Entities.deproxy(app.createAndManageChild(EntitySpec.create(BasicEntity.class) + .addInitializer(proxyEffector))); + Object output = b.invoke(b.getEffector("proxy-effector"), ImmutableMap.of("arg", "value")) + .getUnchecked(Duration.ONE_MINUTE); + assertEquals(output, "value"); + } + + @Test + public void testThrowsIfTargetEffectorDoesntExist() { + TestEntity a = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + ProxyEffector proxyEffector = new ProxyEffector(ImmutableMap.of( + AddEffector.EFFECTOR_NAME, "proxy-effector", + ProxyEffector.TARGET_ENTITY, a, + ProxyEffector.TARGET_EFFECTOR_NAME, "kajnfksjdnfkjsdnf")); + EntityInternal b = Entities.deproxy(app.createAndManageChild(EntitySpec.create(BasicEntity.class) + .addInitializer(proxyEffector))); + try { + b.invoke(b.getEffector("proxy-effector"), ImmutableMap.of("arg", "value")) + .getUnchecked(Duration.ONE_MINUTE); + Asserts.shouldHaveFailedPreviously("expected exception when invoking effector that does not exist"); + } catch (Exception e) { + Asserts.expectedFailureOfType(e, NoSuchElementException.class); + } + } + + @Test(expectedExceptions = NullPointerException.class) + public void testThrowsIfEntityUnset() { + new ProxyEffector(MutableMap.of( + AddEffector.EFFECTOR_NAME, "proxy-effector", + ProxyEffector.TARGET_ENTITY, null, + ProxyEffector.TARGET_EFFECTOR_NAME, "kajnfksjdnfkjsdnf")); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testThrowsIfTargetEffectorNameUnset() { + new ProxyEffector(MutableMap.of( + AddEffector.EFFECTOR_NAME, "proxy-effector", + ProxyEffector.TARGET_ENTITY, app, + ProxyEffector.TARGET_EFFECTOR_NAME, null)); + } + +} \ No newline at end of file