ProxyEffector Forwards effector invocations from one entity to another.
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/ebd32a8c Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/ebd32a8c Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/ebd32a8c Branch: refs/heads/master Commit: ebd32a8c1ac1eb6a3daceb0bc5cd248c3014f3de Parents: a37d888 Author: Sam Corbett <sam.corb...@cloudsoftcorp.com> Authored: Mon Oct 10 21:44:02 2016 +0100 Committer: Sam Corbett <sam.corb...@cloudsoftcorp.com> Committed: Thu Oct 13 11:42:09 2016 +0100 ---------------------------------------------------------------------- .../camp/brooklyn/ProxyEffectorYamlTest.java | 93 +++++++++++++++++++ .../brooklyn/core/effector/ProxyEffector.java | 96 ++++++++++++++++++++ .../core/effector/ProxyEffectorTest.java | 90 ++++++++++++++++++ 3 files changed, 279 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ebd32a8c/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/ProxyEffectorYamlTest.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 0000000..b730302 --- /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<Entity> 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.<String, Object>of()).get(); + Asserts.shouldHaveFailedPreviously("expected exception when invoking effector that does not exist"); + } catch (Exception e) { + Asserts.expectedFailure(e); + } + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ebd32a8c/core/src/main/java/org/apache/brooklyn/core/effector/ProxyEffector.java ---------------------------------------------------------------------- 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 0000000..596cb77 --- /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<Entity> TARGET_ENTITY = ConfigKeys.newConfigKey(Entity.class, + "targetEntity", "The proxy target"); + + public static final ConfigKey<String> 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<Object> newEffectorBuilder(ConfigBag params) { + EffectorBuilder<Object> eff = AddEffector.newEffectorBuilder(Object.class, params); + eff.impl(new Body(eff.buildAbstract(), params)); + return eff; + } + + protected static class Body extends EffectorBody<Object> { + + 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<Entity> 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(); + } + } + + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/ebd32a8c/core/src/test/java/org/apache/brooklyn/core/effector/ProxyEffectorTest.java ---------------------------------------------------------------------- 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 0000000..a875796 --- /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