http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/core/effector/EffectorSayHiTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/EffectorSayHiTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/EffectorSayHiTest.java new file mode 100644 index 0000000..76dcdba --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/effector/EffectorSayHiTest.java @@ -0,0 +1,173 @@ +/* + * 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 static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +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.EntitySpec; +import org.apache.brooklyn.api.entity.ImplementedBy; +import org.apache.brooklyn.api.mgmt.ExecutionContext; +import org.apache.brooklyn.api.mgmt.Task; +import org.apache.brooklyn.core.annotation.EffectorParam; +import org.apache.brooklyn.core.effector.MethodEffector; +import org.apache.brooklyn.core.entity.AbstractEntity; +import org.apache.brooklyn.core.entity.trait.Startable; +import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; +import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.task.BasicTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +/** + * Test the operation of the {@link Effector} implementations. + * + * TODO clarify test purpose + */ +public class EffectorSayHiTest extends BrooklynAppUnitTestSupport { + + //TODO test edge/error conditions + //(missing parameters, wrong number of params, etc) + + private static final Logger log = LoggerFactory.getLogger(EffectorSayHiTest.class); + + private MyEntity e; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + e = app.createAndManageChild(EntitySpec.create(MyEntity.class)); + } + + @Test + public void testFindEffectorMetaData() { + assertEquals("sayHi1", e.SAY_HI_1.getName()); + assertEquals("says hello", e.SAY_HI_1.getDescription()); + + assertEquals(ImmutableList.of("name", "greeting"), getParameterNames(e.SAY_HI_1)); + assertEquals(MutableMap.of("name", null, "greeting", "what to say"), getParameterDescriptions(e.SAY_HI_1)); + } + + @Test + public void testFindTraitEffectors() { + assertEquals(ImmutableList.of("locations"), getParameterNames(Startable.START)); + } + + @Test + public void testInvokeEffectors1() throws Exception { + assertEquals("hi Bob", e.sayHi1("Bob", "hi")); + + assertEquals("hi Bob", e.SAY_HI_1.call(e, ImmutableMap.of("name", "Bob", "greeting", "hi")) ); + assertEquals("hi Bob", e.invoke(e.SAY_HI_1, ImmutableMap.of("name", "Bob", "greeting", "hi")).get() ); + + // and with default greeting param value + assertEquals("hi Bob", e.SAY_HI_1.call(e, ImmutableMap.of("name", "Bob", "greeting", "hi")) ); + assertEquals("hello Bob", e.invoke(e.SAY_HI_1, ImmutableMap.of("name", "Bob")).get() ); + } + + @Test + public void testCanRetrieveTaskForEffector() { + e.sayHi1("Bob", "hi"); + + Set<Task<?>> tasks = mgmt.getExecutionManager().getTasksWithAllTags(ImmutableList.of( + BrooklynTaskTags.tagForContextEntity(e),ManagementContextInternal.EFFECTOR_TAG)); + assertEquals(tasks.size(), 1); + assertTrue(tasks.iterator().next().getDescription().contains("sayHi1")); + } + + @Test + public void testDelegatedNestedEffectorNotRepresentedAsTask() { + e.delegateSayHi1("Bob", "hi"); + + Set<Task<?>> tasks = mgmt.getExecutionManager().getTasksWithAllTags(ImmutableList.of( + BrooklynTaskTags.tagForContextEntity(e),ManagementContextInternal.EFFECTOR_TAG)); + assertEquals(tasks.size(), 1); + assertTrue(tasks.iterator().next().getDescription().contains("delegateSayHi1")); + assertFalse(tasks.iterator().next().getDescription().contains("sayHi1")); + } + + @Test + public void testCanExcludeNonEffectorTasks() throws Exception { + ExecutionContext executionContext = mgmt.getExecutionContext(e); + executionContext.submit(new BasicTask<Void>(new Runnable() { public void run() {} })); + + Set<Task<?>> effectTasks = mgmt.getExecutionManager().getTasksWithAllTags(ImmutableList.of( + BrooklynTaskTags.tagForContextEntity(e),ManagementContextInternal.EFFECTOR_TAG)); + assertEquals(effectTasks.size(), 0); + } + + public interface CanSayHi { + static MethodEffector<String> SAY_HI_1 = new MethodEffector<String>(CanSayHi.class, "sayHi1"); + static MethodEffector<String> DELEGATE_SAY_HI_1 = new MethodEffector<String>(CanSayHi.class, "delegateSayHi1"); + + @org.apache.brooklyn.core.annotation.Effector(description="says hello") + public String sayHi1( + @EffectorParam(name="name") String name, + @EffectorParam(name="greeting", defaultValue="hello", description="what to say") String greeting); + + @org.apache.brooklyn.core.annotation.Effector(description="delegate says hello") + public String delegateSayHi1( + @EffectorParam(name="name") String name, + @EffectorParam(name="greeting") String greeting); + } + + @ImplementedBy(MyEntityImpl.class) + public interface MyEntity extends Entity, CanSayHi { + } + + public static class MyEntityImpl extends AbstractEntity implements MyEntity { + @Override + public String sayHi1(String name, String greeting) { + return greeting+" "+name; + } + @Override + public String delegateSayHi1(String name, String greeting) { + return sayHi1(name, greeting); + } + } + + private List<String> getParameterNames(Effector<?> effector) { + return ImmutableList.copyOf(getParameterDescriptions(effector).keySet()); + } + + private Map<String, String> getParameterDescriptions(Effector<?> effector) { + Map<String,String> result = Maps.newLinkedHashMap(); + for (ParameterType<?> parameter : effector.getParameters()) { + result.put(parameter.getName(), parameter.getDescription()); + } + return result; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/core/effector/EffectorTaskTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/EffectorTaskTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/EffectorTaskTest.java new file mode 100644 index 0000000..3e79aa9 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/effector/EffectorTaskTest.java @@ -0,0 +1,437 @@ +/* + * 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.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.brooklyn.api.effector.Effector; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.mgmt.HasTaskChildren; +import org.apache.brooklyn.api.mgmt.Task; +import org.apache.brooklyn.core.effector.EffectorBody; +import org.apache.brooklyn.core.effector.EffectorTasks; +import org.apache.brooklyn.core.effector.EffectorWithBody; +import org.apache.brooklyn.core.effector.Effectors; +import org.apache.brooklyn.core.effector.EffectorTasks.EffectorTaskFactory; +import org.apache.brooklyn.core.entity.AbstractEntity; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.EntityInternal; +import org.apache.brooklyn.core.entity.trait.Startable; +import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.util.collections.MutableMap; +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.exceptions.Exceptions; +import org.testng.Assert; +import org.testng.annotations.Test; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; + +public class EffectorTaskTest extends BrooklynAppUnitTestSupport { + + // ----------- syntax 1 -- effector with body in a class + + public static final Effector<Integer> DOUBLE_1 = Effectors.effector(Integer.class, "double") + .description("doubles the given number") + .parameter(Integer.class, "numberToDouble") + .impl(new EffectorBody<Integer>() { + @Override + public Integer call(ConfigBag parameters) { + // do a sanity check + Assert.assertNotNull(entity()); + + // finally double the input + return 2*(Integer)parameters.getStringKey("numberToDouble"); + } + }) + .build(); + + public static class DoublingEntity extends AbstractEntity { + public static final Effector<Integer> DOUBLE = EffectorTaskTest.DOUBLE_1; + } + + @Test + public void testSyntaxOneDouble1() throws Exception { + // just use "dynamic" support of effector + Assert.assertEquals(app.invoke(DOUBLE_1, MutableMap.of("numberToDouble", 3)).get(), (Integer)6); + } + + @Test + public void testSyntaxOneTaggedCorrectly() throws Exception { + Task<Integer> t = app.invoke(DOUBLE_1, MutableMap.of("numberToDouble", 3)); + t.get(); + checkTags(t, app, DOUBLE_1, false); + } + + @Test + // also assert it works when the effector is defined on an entity + public void testSimpleEffectorOnEntity() throws Exception { + Entity doubler = app.createAndManageChild(EntitySpec.create(Entity.class, DoublingEntity.class)); + + Assert.assertEquals(doubler.invoke(DOUBLE_1, MutableMap.of("numberToDouble", 3)).get(), (Integer)6); + } + + @Test + // also assert it works when an abstract effector name is passed in to the entity + public void testSimpleEffectorNameMatching() throws Exception { + Entity doubler = app.createAndManageChild(EntitySpec.create(Entity.class, DoublingEntity.class)); + + Assert.assertEquals(doubler.invoke(Effectors.effector(Integer.class, "double").buildAbstract(), MutableMap.of("numberToDouble", 3)).get(), (Integer)6); + } + + + // ----------- syntax 2 -- effector with body built with fluent API + + public static EffectorTaskFactory<Integer> times(final EffectorTaskFactory<Integer> x, final int y) { + return new EffectorTaskFactory<Integer>() { + @Override + public Task<Integer> newTask(final Entity entity, final Effector<Integer> effector, final ConfigBag parameters) { + return TaskBuilder.<Integer>builder().name("times").body(new Callable<Integer>() { public Integer call() { + return DynamicTasks.get( x.newTask(entity, effector, parameters) )*y; + } }).build(); + } + }; + } + + public static final Effector<Integer> DOUBLE_2 = Effectors.effector(Integer.class, "double") + .description("doubles the given number") + .parameter(Integer.class, "numberToDouble") + .impl(times(EffectorTasks.parameter(Integer.class, "numberToDouble"), 2)) + .build(); + + @Test + public void testSyntaxTwoDouble2() throws Exception { + Assert.assertEquals(app.invoke(DOUBLE_2, MutableMap.of("numberToDouble", 3)).get(), (Integer)6); + } + + @Test + public void testEffectorImplTaggedCorrectly() throws Exception { + Task<Integer> t = app.invoke(DOUBLE_2, MutableMap.of("numberToDouble", 3)); + t.get(); + checkTags(t, app, DOUBLE_2, true); + } + + public static final Effector<Integer> DOUBLE_CALL_ABSTRACT = Effectors.effector(Integer.class, "double_call") + .description("doubles the given number") + .parameter(Integer.class, "numberToDouble") + .buildAbstract(); + public static final Effector<Integer> DOUBLE_CALL = Effectors.effector(DOUBLE_CALL_ABSTRACT) + .impl(new EffectorBody<Integer>() { + @Override + public Integer call(ConfigBag parameters) { + final Entity parent = entity(); + final Entity child = Iterables.getOnlyElement(entity().getChildren()); + + final Effector<Integer> DOUBLE_CHECK_ABSTRACT = Effectors.effector(Integer.class, "double_check") + .description("doubles the given number and checks tags, assuming double exists as an effector here") + .parameter(Integer.class, "numberToDouble") + .buildAbstract(); + Effector<Integer> DOUBLE_CHECK = Effectors.effector(DOUBLE_CHECK_ABSTRACT) + .impl(new EffectorBody<Integer>() { + @Override + public Integer call(ConfigBag parameters) { + Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(Tasks.current(), child, null, false)); + Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(Tasks.current(), child, DOUBLE_CHECK_ABSTRACT, false)); + Assert.assertFalse(BrooklynTaskTags.isInEffectorTask(Tasks.current(), child, DOUBLE_1, false)); + Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(Tasks.current(), parent, null, true)); + Assert.assertFalse(BrooklynTaskTags.isInEffectorTask(Tasks.current(), parent, null, false)); + Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(Tasks.current(), parent, DOUBLE_CALL_ABSTRACT, true)); + Assert.assertFalse(BrooklynTaskTags.isInEffectorTask(Tasks.current(), parent, DOUBLE_1, true)); + + return entity().invoke(DOUBLE_1, parameters.getAllConfig()).getUnchecked(); + } + }).build(); + + return child.invoke(DOUBLE_CHECK, parameters.getAllConfig()).getUnchecked(); + } + }).build(); + + + @Test + // also assert it works when the effector is defined on an entity + public void testNestedEffectorTag() throws Exception { + app.createAndManageChild(EntitySpec.create(Entity.class, DoublingEntity.class)); + Assert.assertEquals(app.invoke(DOUBLE_CALL, MutableMap.of("numberToDouble", 3)).get(), (Integer)6); + } + + + private void checkTags(Task<Integer> t, Entity entity, Effector<?> eff, boolean shouldHaveChild) { + Assert.assertEquals(BrooklynTaskTags.getContextEntity(t), app); + Assert.assertTrue(t.getTags().contains(BrooklynTaskTags.EFFECTOR_TAG), "missing effector tag; had: "+t.getTags()); + Assert.assertTrue(t.getDescription().contains(eff.getName()), "description missing effector name: "+t.getDescription()); + Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(t, entity, eff, false)); + Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(t, null, null, false)); + Assert.assertFalse(BrooklynTaskTags.isInEffectorTask(t, entity, Startable.START, false)); + + if (shouldHaveChild) { + Task<?> subtask = ((HasTaskChildren)t).getChildren().iterator().next(); + Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(subtask, entity, eff, false)); + Assert.assertTrue(BrooklynTaskTags.isInEffectorTask(subtask, null, null, false)); + } + } + + // TEST parameter task missing + + // ----------------- syntax for more complex -- an effector using subtasks + + public static Task<Integer> add(final int x, final int y) { + return TaskBuilder.<Integer>builder().name("add").body(new Callable<Integer>() { public Integer call() { return x+y; } }).build(); + } + + public static Task<Integer> add(final Task<Integer> x, final int y) { + return TaskBuilder.<Integer>builder().name("add").body(new Callable<Integer>() { public Integer call() { return DynamicTasks.get(x)+y; } }).build(); + } + + public static Task<Integer> addBasic(final Task<Integer> x, final int y) { + return TaskBuilder.<Integer>builder().name("add (not dynamic)").dynamic(false).body(new Callable<Integer>() { public Integer call() { + Preconditions.checkState(x.isSubmitted()); + return x.getUnchecked()+y; + } }).build(); + } + + public static Task<Integer> times(final int x, final int y) { + return TaskBuilder.<Integer>builder().name("times").body(new Callable<Integer>() { public Integer call() { return x*y; } }).build(); + } + + public static Task<Integer> times(final Task<Integer> x, final int y) { + return TaskBuilder.<Integer>builder().name("times").body(new Callable<Integer>() { public Integer call() { return DynamicTasks.get(x)*y; } }).build(); + } + + public static final Effector<Integer> TWO_X_PLUS_ONE = Effectors.effector(Integer.class, "twoXPlusOne") + .description("doubles the given number and adds one") + .parameter(Integer.class, "numberToStartWith") + .impl(new EffectorBody<Integer>() { + public Integer call(ConfigBag parameters) { + int input = (Integer)parameters.getStringKey("numberToStartWith"); + queue( add(times(input, 2), 1) ); + return last(Integer.class); + } + }) + .build(); + + public static final Effector<Integer> TWO_X_PLUS_ONE_BASIC = Effectors.effector(Integer.class, "twoXPlusOne_Basic") + .description("doubles the given number and adds one, as a basic task") + .parameter(Integer.class, "numberToStartWith") + .impl(new EffectorBody<Integer>() { + public Integer call(ConfigBag parameters) { + int input = (Integer)parameters.getStringKey("numberToStartWith"); + // note the subtasks must be queued explicitly with a basic task + // (but with the DynamicSequentialTask they can be resolved by the task itself; see above) + Task<Integer> product = queue(times(input, 2)); + queue( addBasic(product, 1) ); + return last(Integer.class); + } + }) + .build(); + + // TODO a chaining style approach + + public static class Txp1Entity extends AbstractEntity { + public static final Effector<Integer> TWO_X_P_1 = EffectorTaskTest.TWO_X_PLUS_ONE; + } + + /** the composed effector should allow us to inspect its children */ + @Test + public void testComposedEffector() throws Exception { + Entity txp1 = app.createAndManageChild(EntitySpec.create(Entity.class, Txp1Entity.class)); + + Task<Integer> e = txp1.invoke(TWO_X_PLUS_ONE, MutableMap.of("numberToStartWith", 3)); + Assert.assertTrue(e instanceof DynamicSequentialTask); + Assert.assertEquals(e.get(), (Integer)7); + Assert.assertEquals( Iterables.size( ((HasTaskChildren)e).getChildren() ), 1); + Task<?> child = ((HasTaskChildren)e).getChildren().iterator().next(); + Assert.assertEquals( Iterables.size( ((HasTaskChildren)child).getChildren() ), 1); + } + + /** the composed effector should allow us to inspect its children */ + @Test + public void testComposedEffectorBasic() throws Exception { + Entity txp1 = app.createAndManageChild(EntitySpec.create(Entity.class, Txp1Entity.class)); + + Task<Integer> e = txp1.invoke(TWO_X_PLUS_ONE_BASIC, MutableMap.of("numberToStartWith", 3)); + Assert.assertTrue(e instanceof DynamicSequentialTask); + Assert.assertEquals(e.get(), (Integer)7); + Assert.assertEquals( Iterables.size( ((HasTaskChildren)e).getChildren() ), 2); + } + + // --------- defining + + @Test + public void testEffectorWithBodyWorksEvenIfNotOnEntity() throws Exception { + Entity doubler = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + Assert.assertEquals(doubler.invoke(DOUBLE_1, MutableMap.of("numberToDouble", 3)).get(), (Integer)6); + } + + public static final Effector<Integer> DOUBLE_BODYLESS = Effectors.effector(Integer.class, "double") + .description("doubles the given number") + .parameter(Integer.class, "numberToDouble") + .buildAbstract(); + + @Test + public void testEffectorWithoutBodyFails() throws Exception { + Entity doubler = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + boolean failed = false; + try { + doubler.invoke(DOUBLE_BODYLESS, MutableMap.of("numberToDouble", 3)); + } catch (Exception e) { + failed = true; + } + if (!failed) Assert.fail("doubling should have failed because it had no body"); + } + + @Test + public void testEffectorBodyAdded() throws Exception { + EntityInternal doubler = (EntityInternal) app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + // not yet present + Assert.assertNull( doubler.getEffector("double") ); + + // add it + doubler.getMutableEntityType().addEffector(DOUBLE_BODYLESS, new EffectorBody<Integer>() { + @Override + public Integer call(ConfigBag parameters) { + int input = (Integer)parameters.getStringKey("numberToDouble"); + return queue(times(input, 2)).getUnchecked(); + } + }); + // now it is present + Assert.assertNotNull( doubler.getEffector("double") ); + + Assert.assertEquals(doubler.invoke(DOUBLE_BODYLESS, MutableMap.of("numberToDouble", 3)).get(), (Integer)6); + } + + @Test + public void testEffectorBodyAddedImplicitlyButBodylessSignatureInvoked() throws Exception { + EntityInternal doubler = (EntityInternal) app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + // add it + doubler.getMutableEntityType().addEffector(DOUBLE_1); + + // invoke it, but using something with equivalent name (and signature -- though only name is used currently) + // ensures that the call picks up the body by looking in the actual entity + Assert.assertEquals(doubler.invoke(DOUBLE_BODYLESS, MutableMap.of("numberToDouble", 3)).get(), (Integer)6); + } + + @Test(dependsOnMethods={"testEffectorBodyAdded"}) + public void testEntityNotPermanentlyChanged() throws Exception { + EntityInternal doubler = (EntityInternal) app.createAndManageChild(EntitySpec.create(TestEntity.class)); + // ensures that independent creations of the class previously modified do not have this effector + Assert.assertNull( doubler.getEffector("double") ); + } + + // --- overriding by using statics --------- + + public static class BadDoublingEntity extends DoublingEntity { + public static final Effector<Integer> DOUBLE = Effectors.effector(DoublingEntity.DOUBLE). + impl( ((EffectorWithBody<Integer>)TWO_X_PLUS_ONE).getBody() ).build(); + } + + @Test + // also assert it works when the entity is defined on an entity + public void testOverriddenEffectorOnEntity() throws Exception { + Entity doubler = app.createAndManageChild(EntitySpec.create(Entity.class, BadDoublingEntity.class)); + + Assert.assertEquals(doubler.invoke(DoublingEntity.DOUBLE, MutableMap.of("numberToDouble", 3, "numberToStartWith", 3)).get(), (Integer)7); + } + + public static final Effector<Void> DUMMY = Effectors.effector(Void.class, "dummy") + .impl(new EffectorBody<Void>() { + @Override + public Void call(ConfigBag parameters) { + return null; + } + }) + .build(); + + public static final Effector<Void> STALL = Effectors.effector(Void.class, "stall") + .parameter(AtomicBoolean.class, "lock") + .impl(new EffectorBody<Void>() { + @Override + public Void call(ConfigBag parameters) { + AtomicBoolean lock = (AtomicBoolean)parameters.getStringKey("lock"); + synchronized(lock) { + if (!lock.get()) { + try { + lock.wait(); + } catch (InterruptedException e) { + Exceptions.propagate(e); + } + } + } + return null; + } + }) + .build(); + + public static final Effector<Void> CONTEXT = Effectors.effector(Void.class, "stall_caller") + .parameter(AtomicBoolean.class, "lock") + .impl(new EffectorBody<Void>() { + @Override + public Void call(ConfigBag parameters) { + Entity child = Iterables.getOnlyElement(entity().getChildren()); + AtomicBoolean lock = new AtomicBoolean(); + Task<Void> dummyTask = null; + + try { + // Queue a (DST secondary) task which waits until notified, so that tasks queued later will get blocked + queue(Effectors.invocation(entity(), STALL, ImmutableMap.of("lock", lock))); + + // Start a new task - submitted directly to child's ExecutionContext, as well as added as a + // DST secondary of the current effector. + dummyTask = child.invoke(DUMMY, ImmutableMap.<String, Object>of()); + dummyTask.getUnchecked(); + + // Execution completed in the child's ExecutionContext, but still queued as a secondary. + // Destroy the child entity so that no subsequent tasks can be executed in its context. + Entities.destroy(child); + } finally { + // Let STALL complete + synchronized(lock) { + lock.set(true); + lock.notifyAll(); + } + // At this point DUMMY will be unblocked and the DST will try to execute it as a secondary. + // Submission will be ignored because DUMMY already executed. + // If it's not ignored then submission will fail because entity is already unmanaged. + } + return null; + } + }) + .build(); + + + @Test + public void testNestedEffectorExecutedAsSecondaryTask() throws Exception { + app.createAndManageChild(EntitySpec.create(TestEntity.class)); + Task<Void> effTask = app.invoke(CONTEXT, ImmutableMap.<String, Object>of()); + effTask.get(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/core/effector/ssh/SshEffectorTasksTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/effector/ssh/SshEffectorTasksTest.java b/core/src/test/java/org/apache/brooklyn/core/effector/ssh/SshEffectorTasksTest.java new file mode 100644 index 0000000..fce676a --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/effector/ssh/SshEffectorTasksTest.java @@ -0,0 +1,265 @@ +/* + * 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.ssh; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.api.mgmt.ManagementContext; +import org.apache.brooklyn.api.mgmt.TaskAdaptable; +import org.apache.brooklyn.api.mgmt.TaskFactory; +import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks; +import org.apache.brooklyn.core.effector.ssh.SshEffectorTasksTest; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.test.entity.TestApplication; +import org.apache.brooklyn.util.core.task.ssh.SshFetchTaskWrapper; +import org.apache.brooklyn.util.core.task.ssh.SshPutTaskWrapper; +import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; +import org.apache.brooklyn.util.exceptions.PropagatedRuntimeException; +import org.apache.brooklyn.util.net.Urls; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.apache.brooklyn.location.localhost.LocalhostMachineProvisioningLocation; +import org.apache.brooklyn.location.ssh.SshMachineLocation; + +import com.google.common.io.Files; + +public class SshEffectorTasksTest { + + private static final Logger log = LoggerFactory.getLogger(SshEffectorTasksTest.class); + + TestApplication app; + ManagementContext mgmt; + SshMachineLocation host; + File tempDir; + + boolean failureExpected; + + @BeforeMethod(alwaysRun=true) + public void setup() throws Exception { + app = TestApplication.Factory.newManagedInstanceForTests(); + mgmt = app.getManagementContext(); + + LocalhostMachineProvisioningLocation lhc = mgmt.getLocationManager().createLocation(LocationSpec.create(LocalhostMachineProvisioningLocation.class)); + host = lhc.obtain(); + app.start(Arrays.asList(host)); + clearExpectedFailure(); + tempDir = Files.createTempDir(); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (mgmt != null) Entities.destroyAll(mgmt); + mgmt = null; + FileUtils.deleteDirectory(tempDir); + checkExpectedFailure(); + } + + protected void checkExpectedFailure() { + if (failureExpected) { + clearExpectedFailure(); + Assert.fail("Test should have thrown an exception but it did not."); + } + } + + protected void clearExpectedFailure() { + failureExpected = false; + } + + protected void setExpectingFailure() { + failureExpected = true; + } + + public <T extends TaskAdaptable<?>> T submit(final TaskFactory<T> taskFactory) { + return Entities.submit(app, taskFactory); + } + + // ------------------- basic ssh + + @Test(groups="Integration") + public void testSshEchoHello() { + ProcessTaskWrapper<Integer> t = submit(SshEffectorTasks.ssh("sleep 1 ; echo hello world")); + Assert.assertFalse(t.isDone()); + Assert.assertEquals(t.get(), (Integer)0); + Assert.assertEquals(t.getTask().getUnchecked(), (Integer)0); + Assert.assertEquals(t.getStdout().trim(), "hello world"); + } + + @Test(groups="Integration") + public void testSshPut() throws IOException { + String fn = Urls.mergePaths(tempDir.getPath(), "f1"); + SshPutTaskWrapper t = submit(SshEffectorTasks.put(fn).contents("hello world")); + t.block(); + Assert.assertEquals(FileUtils.readFileToString(new File(fn)), "hello world"); + // and make sure this doesn't throw + Assert.assertTrue(t.isDone()); + Assert.assertTrue(t.isSuccessful()); + Assert.assertEquals(t.get(), null); + Assert.assertEquals(t.getExitCode(), (Integer)0); + } + + @Test(groups="Integration") + public void testSshFetch() throws IOException { + String fn = Urls.mergePaths(tempDir.getPath(), "f2"); + FileUtils.write(new File(fn), "hello fetched world"); + + SshFetchTaskWrapper t = submit(SshEffectorTasks.fetch(fn)); + t.block(); + + Assert.assertTrue(t.isDone()); + Assert.assertEquals(t.get(), "hello fetched world"); + } + + // ----------------- pid stuff + + @Test(groups="Integration") + public void testNonRunningPid() { + ProcessTaskWrapper<Integer> t = submit(SshEffectorTasks.codePidRunning(99999)); + Assert.assertNotEquals(t.getTask().getUnchecked(), (Integer)0); + Assert.assertNotEquals(t.getExitCode(), (Integer)0); + ProcessTaskWrapper<Boolean> t2 = submit(SshEffectorTasks.isPidRunning(99999)); + Assert.assertFalse(t2.getTask().getUnchecked()); + } + + @Test(groups="Integration") + public void testNonRunningPidRequired() { + ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidRunning(99999)); + setExpectingFailure(); + try { + t.getTask().getUnchecked(); + } catch (Exception e) { + log.info("The error if required PID is not found is: "+e); + clearExpectedFailure(); + Assert.assertTrue(e.toString().contains("Process with PID"), "Expected nice clue in error but got: "+e); + } + checkExpectedFailure(); + } + + public static Integer getMyPid() { + try { + java.lang.management.RuntimeMXBean runtime = + java.lang.management.ManagementFactory.getRuntimeMXBean(); + java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm"); + jvm.setAccessible(true); +// sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime); + Object mgmt = jvm.get(runtime); + java.lang.reflect.Method pid_method = + mgmt.getClass().getDeclaredMethod("getProcessId"); + pid_method.setAccessible(true); + + return (Integer) pid_method.invoke(mgmt); + } catch (Exception e) { + throw new PropagatedRuntimeException("Test depends on (fragile) getMyPid method which does not work here", e); + } + } + + @Test(groups="Integration") + public void testRunningPid() { + ProcessTaskWrapper<Integer> t = submit(SshEffectorTasks.codePidRunning(getMyPid())); + Assert.assertEquals(t.getTask().getUnchecked(), (Integer)0); + ProcessTaskWrapper<Boolean> t2 = submit(SshEffectorTasks.isPidRunning(getMyPid())); + Assert.assertTrue(t2.getTask().getUnchecked()); + } + + @Test(groups="Integration") + public void testRunningPidFromFile() throws IOException { + File f = File.createTempFile("testBrooklynPid", ".pid"); + Files.write( (""+getMyPid()).getBytes(), f ); + ProcessTaskWrapper<Integer> t = submit(SshEffectorTasks.codePidFromFileRunning(f.getPath())); + Assert.assertEquals(t.getTask().getUnchecked(), (Integer)0); + ProcessTaskWrapper<Boolean> t2 = submit(SshEffectorTasks.isPidFromFileRunning(f.getPath())); + Assert.assertTrue(t2.getTask().getUnchecked()); + } + + @Test(groups="Integration") + public void testRequirePidFromFileOnFailure() throws IOException { + File f = File.createTempFile("testBrooklynPid", ".pid"); + Files.write( "99999".getBytes(), f ); + ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidFromFileRunning(f.getPath())); + + setExpectingFailure(); + try { + t.getTask().getUnchecked(); + } catch (Exception e) { + log.info("The error if required PID is not found is: "+e); + clearExpectedFailure(); + Assert.assertTrue(e.toString().contains("Process with PID"), "Expected nice clue in error but got: "+e); + Assert.assertEquals(t.getExitCode(), (Integer)1); + } + checkExpectedFailure(); + } + + @Test(groups="Integration") + public void testRequirePidFromFileOnFailureNoSuchFile() throws IOException { + ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidFromFileRunning("/path/does/not/exist/SADVQW")); + + setExpectingFailure(); + try { + t.getTask().getUnchecked(); + } catch (Exception e) { + log.info("The error if required PID is not found is: "+e); + clearExpectedFailure(); + Assert.assertTrue(e.toString().contains("Process with PID"), "Expected nice clue in error but got: "+e); + Assert.assertEquals(t.getExitCode(), (Integer)1); + } + checkExpectedFailure(); + } + + @Test(groups="Integration") + public void testRequirePidFromFileOnFailureTooManyFiles() throws IOException { + ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidFromFileRunning("/*")); + + setExpectingFailure(); + try { + t.getTask().getUnchecked(); + } catch (Exception e) { + log.info("The error if required PID is not found is: "+e); + clearExpectedFailure(); + Assert.assertTrue(e.toString().contains("Process with PID"), "Expected nice clue in error but got: "+e); + Assert.assertEquals(t.getExitCode(), (Integer)2); + } + checkExpectedFailure(); + } + + @Test(groups="Integration") + public void testRequirePidFromFileOnSuccess() throws IOException { + File f = File.createTempFile("testBrooklynPid", ".pid"); + Files.write( (""+getMyPid()).getBytes(), f ); + ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidFromFileRunning(f.getPath())); + + t.getTask().getUnchecked(); + } + + @Test(groups="Integration") + public void testRequirePidFromFileOnSuccessAcceptsWildcards() throws IOException { + File f = File.createTempFile("testBrooklynPid", ".pid"); + Files.write( (""+getMyPid()).getBytes(), f ); + ProcessTaskWrapper<?> t = submit(SshEffectorTasks.requirePidFromFileRunning(f.getPath()+"*")); + + t.getTask().getUnchecked(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/core/entity/DynamicEntityTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/DynamicEntityTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/DynamicEntityTest.java index 885eb45..fb9276c 100644 --- a/core/src/test/java/org/apache/brooklyn/core/entity/DynamicEntityTest.java +++ b/core/src/test/java/org/apache/brooklyn/core/entity/DynamicEntityTest.java @@ -24,10 +24,10 @@ import static org.testng.Assert.assertFalse; import org.apache.brooklyn.api.entity.EntityInitializer; import org.apache.brooklyn.api.entity.EntityLocal; import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.core.effector.EffectorTaskTest; 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.effector.core.EffectorTaskTest; import org.apache.brooklyn.entity.stock.BasicEntity; import org.apache.brooklyn.util.collections.MutableMap; import org.testng.annotations.Test; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/core/entity/EntityTypeTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/EntityTypeTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/EntityTypeTest.java index c01c5c9..cead05a 100644 --- a/core/src/test/java/org/apache/brooklyn/core/entity/EntityTypeTest.java +++ b/core/src/test/java/org/apache/brooklyn/core/entity/EntityTypeTest.java @@ -47,13 +47,13 @@ import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.api.sensor.AttributeSensor; import org.apache.brooklyn.api.sensor.Sensor; +import org.apache.brooklyn.core.effector.MethodEffector; import org.apache.brooklyn.core.entity.AbstractEntity; 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.core.test.entity.TestEntityImpl; -import org.apache.brooklyn.effector.core.MethodEffector; import org.apache.brooklyn.sensor.core.BasicSensorEvent; import org.apache.brooklyn.sensor.core.Sensors; import org.apache.brooklyn.test.Asserts; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/core/entity/hello/HelloEntity.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/hello/HelloEntity.java b/core/src/test/java/org/apache/brooklyn/core/entity/hello/HelloEntity.java index 9d14868..a217514 100644 --- a/core/src/test/java/org/apache/brooklyn/core/entity/hello/HelloEntity.java +++ b/core/src/test/java/org/apache/brooklyn/core/entity/hello/HelloEntity.java @@ -25,7 +25,7 @@ import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.annotation.Effector; import org.apache.brooklyn.core.annotation.EffectorParam; import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.effector.core.MethodEffector; +import org.apache.brooklyn.core.effector.MethodEffector; import org.apache.brooklyn.entity.group.AbstractGroup; import org.apache.brooklyn.sensor.core.BasicSensor; import org.apache.brooklyn.sensor.core.Sensors; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiVersionMoreEntityTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiVersionMoreEntityTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiVersionMoreEntityTest.java index f648419..1d50609 100644 --- a/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiVersionMoreEntityTest.java +++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/osgi/OsgiVersionMoreEntityTest.java @@ -42,6 +42,7 @@ import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder; import org.apache.brooklyn.core.catalog.internal.CatalogItemDtoAbstract; import org.apache.brooklyn.core.catalog.internal.CatalogTestUtils; import org.apache.brooklyn.core.catalog.internal.CatalogUtils; +import org.apache.brooklyn.core.effector.Effectors; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContext; import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; @@ -50,7 +51,6 @@ import org.apache.brooklyn.core.objs.proxy.InternalEntityFactory; import org.apache.brooklyn.core.objs.proxy.InternalPolicyFactory; import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; import org.apache.brooklyn.core.test.entity.TestApplication; -import org.apache.brooklyn.effector.core.Effectors; import org.apache.brooklyn.test.support.TestResourceUnavailableException; import org.apache.brooklyn.util.core.osgi.Osgis; import org.apache.brooklyn.util.guava.Maybe; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindEntityDynamicTypeInfoTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindEntityDynamicTypeInfoTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindEntityDynamicTypeInfoTest.java index c25dd0e..3b9bbfb 100644 --- a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindEntityDynamicTypeInfoTest.java +++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindEntityDynamicTypeInfoTest.java @@ -29,10 +29,10 @@ import org.apache.brooklyn.api.effector.Effector; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.effector.EffectorBody; +import org.apache.brooklyn.core.effector.Effectors; import org.apache.brooklyn.core.test.entity.TestApplication; import org.apache.brooklyn.core.test.entity.TestEntity; -import org.apache.brooklyn.effector.core.EffectorBody; -import org.apache.brooklyn.effector.core.Effectors; import org.apache.brooklyn.util.core.config.ConfigBag; import org.apache.brooklyn.util.stream.Streams; import org.slf4j.Logger; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntity.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntity.java b/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntity.java index 2106f18..8c185a0 100644 --- a/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntity.java +++ b/core/src/test/java/org/apache/brooklyn/core/test/entity/TestEntity.java @@ -36,11 +36,11 @@ import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.config.ListConfigKey; import org.apache.brooklyn.core.config.MapConfigKey; import org.apache.brooklyn.core.config.SetConfigKey; +import org.apache.brooklyn.core.effector.MethodEffector; import org.apache.brooklyn.core.entity.Attributes; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; import org.apache.brooklyn.core.entity.trait.Startable; -import org.apache.brooklyn.effector.core.MethodEffector; import org.apache.brooklyn.sensor.core.BasicNotificationSensor; import org.apache.brooklyn.sensor.core.Sensors; import org.apache.brooklyn.util.collections.MutableMap; http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/effector/core/EffectorBasicTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/effector/core/EffectorBasicTest.java b/core/src/test/java/org/apache/brooklyn/effector/core/EffectorBasicTest.java deleted file mode 100644 index 786fe69..0000000 --- a/core/src/test/java/org/apache/brooklyn/effector/core/EffectorBasicTest.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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.effector.core; - -import java.util.List; -import java.util.concurrent.Callable; - -import org.apache.brooklyn.api.entity.EntityLocal; -import org.apache.brooklyn.api.entity.EntitySpec; -import org.apache.brooklyn.api.mgmt.HasTaskChildren; -import org.apache.brooklyn.api.mgmt.Task; -import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.entity.trait.FailingEntity; -import org.apache.brooklyn.core.entity.trait.Startable; -import org.apache.brooklyn.core.location.SimulatedLocation; -import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; -import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; -import org.apache.brooklyn.core.test.entity.TestEntity; -import org.apache.brooklyn.test.TestUtils; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.core.task.Tasks; -import org.apache.brooklyn.util.exceptions.Exceptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.Assert; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableList; - -public class EffectorBasicTest extends BrooklynAppUnitTestSupport { - - private static final Logger log = LoggerFactory.getLogger(EffectorBasicTest.class); - - // NB: more tests of effectors in EffectorSayHiTest and EffectorConcatenateTest - // as well as EntityConfigMapUsageTest and others - - private List<SimulatedLocation> locs; - - @BeforeMethod(alwaysRun=true) - @Override - public void setUp() throws Exception { - super.setUp(); - locs = ImmutableList.of(new SimulatedLocation()); - } - - @Test - public void testInvokeEffectorStart() { - app.start(locs); - TestUtils.assertSetsEqual(locs, app.getLocations()); - // TODO above does not get registered as a task - } - - @Test - public void testInvokeEffectorStartWithMap() { - app.invoke(Startable.START, MutableMap.of("locations", locs)).getUnchecked(); - TestUtils.assertSetsEqual(locs, app.getLocations()); - } - - @Test - public void testInvokeEffectorStartWithArgs() { - Entities.invokeEffectorWithArgs((EntityLocal)app, app, Startable.START, locs).getUnchecked(); - TestUtils.assertSetsEqual(locs, app.getLocations()); - } - - @Test - public void testInvokeEffectorStartWithTwoEntities() { - TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); - TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class)); - app.start(locs); - TestUtils.assertSetsEqual(locs, app.getLocations()); - TestUtils.assertSetsEqual(locs, entity.getLocations()); - TestUtils.assertSetsEqual(locs, entity2.getLocations()); - } - - @Test - public void testInvokeEffectorTaskHasTag() { - Task<Void> starting = app.invoke(Startable.START, MutableMap.of("locations", locs)); -// log.info("TAGS: "+starting.getTags()); - Assert.assertTrue(starting.getTags().contains(ManagementContextInternal.EFFECTOR_TAG)); - } - - // check various failure situations - - private FailingEntity createFailingEntity() { - FailingEntity entity = app.createAndManageChild(EntitySpec.create(FailingEntity.class) - .configure(FailingEntity.FAIL_ON_START, true)); - return entity; - } - - // uncaught failures are propagates - - @Test - public void testInvokeEffectorStartFailing_Method() { - FailingEntity entity = createFailingEntity(); - assertStartMethodFails(entity); - } - - @Test - public void testInvokeEffectorStartFailing_EntityInvoke() { - FailingEntity entity = createFailingEntity(); - assertTaskFails( entity.invoke(Startable.START, MutableMap.of("locations", locs)) ); - } - - @Test - public void testInvokeEffectorStartFailing_EntitiesInvoke() { - FailingEntity entity = createFailingEntity(); - - assertTaskFails( Entities.invokeEffectorWithArgs(entity, entity, Startable.START, locs) ); - } - - // caught failures are NOT propagated! - - @Test - public void testInvokeEffectorStartFailing_MethodInDynamicTask() { - Task<Void> task = app.getExecutionContext().submit(Tasks.<Void>builder().dynamic(true).body(new Callable<Void>() { - @Override public Void call() throws Exception { - testInvokeEffectorStartFailing_Method(); - return null; - } - }).build()); - - assertTaskSucceeds(task); - assertTaskHasFailedChild(task); - } - - @Test - public void testInvokeEffectorStartFailing_MethodInTask() { - Task<Void> task = app.getExecutionContext().submit(Tasks.<Void>builder().dynamic(false).body(new Callable<Void>() { - @Override public Void call() throws Exception { - testInvokeEffectorStartFailing_Method(); - return null; - } - }).build()); - - assertTaskSucceeds(task); - } - - private void assertTaskSucceeds(Task<Void> task) { - task.getUnchecked(); - Assert.assertFalse(task.isError()); - } - - private void assertTaskHasFailedChild(Task<Void> task) { - Assert.assertTrue(Tasks.failed( ((HasTaskChildren)task).getChildren() ).iterator().hasNext()); - } - - private void assertStartMethodFails(FailingEntity entity) { - try { - entity.start(locs); - Assert.fail("Should have failed"); - } catch (Exception e) { - // expected - } - } - - protected void assertTaskFails(Task<?> t) { - try { - t.get(); - Assert.fail("Should have failed"); - } catch (Exception e) { - Exceptions.propagateIfFatal(e); - // expected - } - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/effector/core/EffectorConcatenateTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/effector/core/EffectorConcatenateTest.java b/core/src/test/java/org/apache/brooklyn/effector/core/EffectorConcatenateTest.java deleted file mode 100644 index 9372e41..0000000 --- a/core/src/test/java/org/apache/brooklyn/effector/core/EffectorConcatenateTest.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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.effector.core; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.fail; - -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.mgmt.ExecutionManager; -import org.apache.brooklyn.api.mgmt.Task; -import org.apache.brooklyn.core.annotation.Effector; -import org.apache.brooklyn.core.annotation.EffectorParam; -import org.apache.brooklyn.core.entity.AbstractEntity; -import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; -import org.apache.brooklyn.core.test.entity.TestApplication; -import org.apache.brooklyn.core.test.entity.TestApplicationImpl; -import org.apache.brooklyn.effector.core.MethodEffector; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.core.task.BasicExecutionContext; -import org.apache.brooklyn.util.core.task.Tasks; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import com.google.common.base.Predicate; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; - -public class EffectorConcatenateTest { - - - private static final Logger log = LoggerFactory.getLogger(EffectorConcatenateTest.class); - private static final long TIMEOUT = 10*1000; - - public static class MyEntityImpl extends AbstractEntity { - - public static MethodEffector<String> CONCATENATE = new MethodEffector<String>(MyEntityImpl.class, "concatenate"); - public static MethodEffector<Void> WAIT_A_BIT = new MethodEffector<Void>(MyEntityImpl.class, "waitabit"); - public static MethodEffector<Void> SPAWN_CHILD = new MethodEffector<Void>(MyEntityImpl.class, "spawnchild"); - - public MyEntityImpl() { - super(); - } - public MyEntityImpl(Entity parent) { - super(parent); - } - - /** The "current task" representing the effector currently executing */ - AtomicReference<Task<?>> waitingTask = new AtomicReference<Task<?>>(); - - /** latch is .countDown'ed by the effector at the beginning of the "waiting" point */ - CountDownLatch nowWaitingLatch = new CountDownLatch(1); - - /** latch is await'ed on by the effector when it is in the "waiting" point */ - CountDownLatch continueFromWaitingLatch = new CountDownLatch(1); - - @Effector(description="sample effector concatenating strings") - public String concatenate(@EffectorParam(name="first", description="first argument") String first, - @EffectorParam(name="second", description="2nd arg") String second) throws Exception { - return first+second; - } - - @Effector(description="sample effector doing some waiting") - public void waitabit() throws Exception { - waitingTask.set(Tasks.current()); - - Tasks.setExtraStatusDetails("waitabit extra status details"); - - Tasks.withBlockingDetails("waitabit.blocking", new Callable<Void>() { - public Void call() throws Exception { - nowWaitingLatch.countDown(); - if (!continueFromWaitingLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { - fail("took too long to be told to continue"); - } - return null; - }}); - } - - @Effector(description="sample effector that spawns a child task that waits a bit") - public void spawnchild() throws Exception { - // spawn a child, then wait - BasicExecutionContext.getCurrentExecutionContext().submit( - MutableMap.of("displayName", "SpawnedChildName"), - new Callable<Void>() { - public Void call() throws Exception { - log.info("beginning spawned child response "+Tasks.current()+", with tags "+Tasks.current().getTags()); - Tasks.setBlockingDetails("spawned child blocking details"); - nowWaitingLatch.countDown(); - if (!continueFromWaitingLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { - fail("took too long to be told to continue"); - } - return null; - }}); - } - } - - private TestApplication app; - private MyEntityImpl e; - - @BeforeMethod(alwaysRun=true) - public void setUp() { - app = new TestApplicationImpl(); - e = new MyEntityImpl(app); - Entities.startManagement(app); - } - - @AfterMethod(alwaysRun=true) - public void tearDown() { - if (app != null) Entities.destroyAll(app.getManagementContext()); - } - - @Test - public void testCanInvokeEffector() throws Exception { - // invocation map syntax - Task<String> task = e.invoke(MyEntityImpl.CONCATENATE, ImmutableMap.of("first", "a", "second", "b")); - assertEquals(task.get(TIMEOUT, TimeUnit.MILLISECONDS), "ab"); - - // method syntax - assertEquals("xy", e.concatenate("x", "y")); - } - - @Test - public void testReportsTaskDetails() throws Exception { - final AtomicReference<String> result = new AtomicReference<String>(); - - Thread bg = new Thread(new Runnable() { - public void run() { - try { - // Expect "wait a bit" to tell us it's blocking - if (!e.nowWaitingLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { - result.set("took too long for waitabit to be waiting"); - return; - } - - // Expect "wait a bit" to have retrieved and set its task - try { - Task<?> t = e.waitingTask.get(); - String status = t.getStatusDetail(true); - log.info("waitabit task says:\n"+status); - if (!status.contains("waitabit extra status details")) { - result.set("Status not in expected format: doesn't contain extra status details phrase 'My extra status details'\n"+status); - return; - } - if (!status.startsWith("waitabit.blocking")) { - result.set("Status not in expected format: doesn't start with blocking details 'waitabit.blocking'\n"+status); - return; - } - } finally { - e.continueFromWaitingLatch.countDown(); - } - } catch (Throwable t) { - log.warn("Failure: "+t, t); - result.set("Failure: "+t); - } - }}); - bg.start(); - - e.invoke(MyEntityImpl.WAIT_A_BIT, ImmutableMap.<String,Object>of()) - .get(TIMEOUT, TimeUnit.MILLISECONDS); - - bg.join(TIMEOUT*2); - assertFalse(bg.isAlive()); - - String problem = result.get(); - if (problem!=null) fail(problem); - } - - @Test - public void testReportsSpawnedTaskDetails() throws Exception { - final AtomicReference<String> result = new AtomicReference<String>(); - - Thread bg = new Thread(new Runnable() { - public void run() { - try { - // Expect "spawned child" to tell us it's blocking - if (!e.nowWaitingLatch.await(TIMEOUT, TimeUnit.MILLISECONDS)) { - result.set("took too long for spawnchild's sub-task to be waiting"); - return; - } - - // Expect spawned task to be have been tagged with entity - ExecutionManager em = e.getManagementContext().getExecutionManager(); - Task<?> subtask = Iterables.find(BrooklynTaskTags.getTasksInEntityContext(em, e), new Predicate<Task<?>>() { - public boolean apply(Task<?> input) { - return "SpawnedChildName".equals(input.getDisplayName()); - } - }); - - // Expect spawned task to haev correct "blocking details" - try { - String status = subtask.getStatusDetail(true); - log.info("subtask task says:\n"+status); - if (!status.contains("spawned child blocking details")) { - result.set("Status not in expected format: doesn't contain blocking details phrase 'spawned child blocking details'\n"+status); - return; - } - } finally { - e.continueFromWaitingLatch.countDown(); - } - } catch (Throwable t) { - log.warn("Failure: "+t, t); - result.set("Failure: "+t); - } - }}); - bg.start(); - - e.invoke(MyEntityImpl.SPAWN_CHILD, ImmutableMap.<String,Object>of()) - .get(TIMEOUT, TimeUnit.MILLISECONDS); - - bg.join(TIMEOUT*2); - assertFalse(bg.isAlive()); - - String problem = result.get(); - if (problem!=null) fail(problem); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/effector/core/EffectorMetadataTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/effector/core/EffectorMetadataTest.java b/core/src/test/java/org/apache/brooklyn/effector/core/EffectorMetadataTest.java deleted file mode 100644 index b8b6bf4..0000000 --- a/core/src/test/java/org/apache/brooklyn/effector/core/EffectorMetadataTest.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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.effector.core; - -import static org.testng.Assert.assertEquals; - -import java.util.Collection; -import java.util.List; - -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.EntitySpec; -import org.apache.brooklyn.api.entity.ImplementedBy; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.core.annotation.EffectorParam; -import org.apache.brooklyn.core.entity.AbstractEntity; -import org.apache.brooklyn.core.entity.trait.Startable; -import org.apache.brooklyn.core.mgmt.internal.EffectorUtils; -import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; -import org.apache.brooklyn.effector.core.BasicParameterType; -import org.apache.brooklyn.effector.core.Effectors; -import org.apache.brooklyn.effector.core.MethodEffector; -import org.testng.Assert; -import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Test; - -import com.google.common.collect.ImmutableList; - -/** - * Test the operation of the {@link Effector} implementations. - * - * TODO clarify test purpose - */ -public class EffectorMetadataTest extends BrooklynAppUnitTestSupport { - - private MyAnnotatedEntity e1; - private MyOverridingEntity e2; - - @BeforeMethod(alwaysRun=true) - @Override - public void setUp() throws Exception { - super.setUp(); - e1 = app.createAndManageChild(EntitySpec.create(MyAnnotatedEntity.class)); - e2 = app.createAndManageChild(EntitySpec.create(MyOverridingEntity.class)); - } - - @Test - public void testEffectorMetaDataFromAnnotationsWithConstant() { - Effector<?> effector = EffectorUtils.findEffectorDeclared(e1, "effWithNewAnnotation").get(); - Assert.assertTrue(Effectors.sameSignature(effector, MyAnnotatedEntity.EFF_WITH_NEW_ANNOTATION)); - assertEquals(effector.getName(), "effWithNewAnnotation"); - assertEquals(effector.getDescription(), "my effector description"); - assertEquals(effector.getReturnType(), String.class); - assertParametersEqual( - effector.getParameters(), - ImmutableList.<ParameterType<?>>of( - new BasicParameterType<String>("param1", String.class, "my param description", "my default val"))); - } - - @Test - public void testEffectorMetaDataFromAnnotationsWithoutConstant() { - Effector<?> effector = EffectorUtils.findEffectorDeclared(e1, "effWithAnnotationButNoConstant").get(); - assertEquals(effector.getName(), "effWithAnnotationButNoConstant"); - assertEquals(effector.getDescription(), "my effector description"); - assertEquals(effector.getReturnType(), String.class); - assertParametersEqual( - effector.getParameters(), - ImmutableList.<ParameterType<?>>of( - new BasicParameterType<String>("param1", String.class, "my param description", "my default val"))); - } - - @SuppressWarnings("rawtypes") - @Test - public void testEffectorMetaDataFromOverriddenMethod() { - // Overridden with new annotations - Effector<?> startEffector = EffectorUtils.findEffectorDeclared(e2, "start").get(); - assertEquals(startEffector.getName(), "start"); - assertEquals(startEffector.getDescription(), "My overridden start description"); - assertEquals(startEffector.getReturnType(), void.class); - assertParametersEqual( - startEffector.getParameters(), - ImmutableList.<ParameterType<?>>of( - new BasicParameterType<Collection>("locations", Collection.class, "my overridden param description", null))); - } - - private void assertParametersEqual(List<ParameterType<?>> actuals, List<ParameterType<?>> expecteds) { - assertEquals(actuals.size(), expecteds.size(), "actual="+actuals); - for (int i = 0; i < actuals.size(); i++) { - ParameterType<?> actual = actuals.get(i); - ParameterType<?> expected = expecteds.get(i); - assertParameterEqual(actual, expected); - } - } - - private void assertParameterEqual(ParameterType<?> actual, ParameterType<?> expected) { - assertEquals(actual.getName(), expected.getName(), "actual="+actual); - assertEquals(actual.getDescription(), expected.getDescription(), "actual="+actual); - assertEquals(actual.getParameterClass(), expected.getParameterClass(), "actual="+actual); - assertEquals(actual.getParameterClassName(), expected.getParameterClassName(), "actual="+actual); - } - - @ImplementedBy(MyAnnotatedEntityImpl.class) - public interface MyAnnotatedEntity extends Entity { - static MethodEffector<String> EFF_WITH_NEW_ANNOTATION = new MethodEffector<String>(MyAnnotatedEntity.class, "effWithNewAnnotation"); - - @org.apache.brooklyn.core.annotation.Effector(description="my effector description") - public String effWithNewAnnotation( - @EffectorParam(name="param1", defaultValue="my default val", description="my param description") String param1); - - @org.apache.brooklyn.core.annotation.Effector(description="my effector description") - public String effWithAnnotationButNoConstant( - @EffectorParam(name="param1", defaultValue="my default val", description="my param description") String param1); - } - - public static class MyAnnotatedEntityImpl extends AbstractEntity implements MyAnnotatedEntity { - @Override - public String effWithNewAnnotation(String param1) { - return param1; - } - - @Override - public String effWithAnnotationButNoConstant(String param1) { - return param1; - } - } - - @ImplementedBy(MyOverridingEntityImpl.class) - public interface MyOverridingEntity extends Entity, Startable { - org.apache.brooklyn.api.effector.Effector<Void> START = Effectors.effector(Startable.START) - .description("My overridden start description") - .parameter(Collection.class, "locations", "my overridden param description") - .build(); - } - - public static class MyOverridingEntityImpl extends AbstractEntity implements MyOverridingEntity { - - @Override - public void restart() { - } - - @Override - public void start(Collection<? extends Location> locations2) { - } - - @Override - public void stop() { - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/8dbb0e4b/core/src/test/java/org/apache/brooklyn/effector/core/EffectorSayHiGroovyTest.groovy ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/effector/core/EffectorSayHiGroovyTest.groovy b/core/src/test/java/org/apache/brooklyn/effector/core/EffectorSayHiGroovyTest.groovy deleted file mode 100644 index ec5c442..0000000 --- a/core/src/test/java/org/apache/brooklyn/effector/core/EffectorSayHiGroovyTest.groovy +++ /dev/null @@ -1,179 +0,0 @@ -/* - * 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.effector.core - -import static org.testng.Assert.* - -import org.apache.brooklyn.api.effector.Effector -import org.apache.brooklyn.api.entity.Entity -import org.apache.brooklyn.api.entity.EntitySpec -import org.apache.brooklyn.api.entity.ImplementedBy -import org.apache.brooklyn.api.mgmt.ManagementContext -import org.apache.brooklyn.api.mgmt.Task -import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; -import org.apache.brooklyn.core.mgmt.internal.EffectorUtils -import org.apache.brooklyn.core.test.entity.TestApplication -import org.apache.brooklyn.core.annotation.EffectorParam -import org.apache.brooklyn.core.entity.AbstractEntity -import org.apache.brooklyn.core.entity.Entities -import org.apache.brooklyn.core.entity.trait.Startable -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.testng.annotations.AfterMethod -import org.testng.annotations.BeforeMethod -import org.testng.annotations.Test - -/** - * Test the operation of the {@link Effector} implementations. - * - * TODO clarify test purpose - */ -public class EffectorSayHiGroovyTest { - private static final Logger log = LoggerFactory.getLogger(EffectorSayHiTest.class); - - private TestApplication app; - private MyEntity e; - - @BeforeMethod(alwaysRun=true) - public void setUp() { - app = TestApplication.Factory.newManagedInstanceForTests(); - e = app.createAndManageChild(EntitySpec.create(MyEntity.class)); - } - - @AfterMethod(alwaysRun=true) - public void tearDown() { - if (app != null) Entities.destroyAll(app.getManagementContext()); - } - - @Test - public void testFindEffectors() { - assertEquals("sayHi1", e.SAY_HI_1.getName()); - assertEquals(["name", "greeting"], e.SAY_HI_1.getParameters()[0..1]*.getName()); - assertEquals("says hello", e.SAY_HI_1.getDescription()); - - assertEquals("sayHi1", e.SAY_HI_1_ALT.getName()); - assertEquals(["name", "greeting"], e.SAY_HI_1_ALT.getParameters()[0..1]*.getName()); - assertEquals("says hello", e.SAY_HI_1_ALT.getDescription()); - - assertEquals("sayHi2", e.SAY_HI_2.getName()); - assertEquals(["name", "greeting"], e.SAY_HI_2.getParameters()[0..1]*.getName()); - assertEquals("says hello", e.SAY_HI_2.getDescription()); - } - - @Test - public void testFindTraitEffectors() { - assertEquals("locations", Startable.START.getParameters()[0].getName()); - } - - @Test - public void testInvokeEffectorMethod1BypassInterception() { - String name = "sayHi1" - def args = ["Bob", "hello"] as Object[] - - //try the alt syntax recommended from web - def metaMethod = e.metaClass.getMetaMethod(name, args) - if (metaMethod==null) - throw new IllegalArgumentException("Invalid arguments (no method found) for method $name: "+args); - assertEquals("hello Bob", metaMethod.invoke(e, args)) - } - - @Test - public void testInvokeEffectorMethod2BypassInterception() { - String name = "sayHi2" - def args = ["Bob", "hello"] as Object[] - assertEquals("hello Bob", e.metaClass.invokeMethod(e, name, args)) - } - - @Test - public void testInvokeEffectors1() { - assertEquals("hi Bob", e.sayHi1("Bob", "hi")) - - assertEquals("hello Bob", e.SAY_HI_1.call(e, [name:"Bob"]) ) - assertEquals("hello Bob", e.invoke(e.SAY_HI_1, [name:"Bob"]).get() ); - - assertEquals("hello Bob", e.SAY_HI_1_ALT.call(e, [name:"Bob"]) ) - } - - @Test - public void testInvokeEffectors2() { - assertEquals("hi Bob", e.sayHi2("Bob", "hi")) - - assertEquals("hello Bob", e.SAY_HI_2.call(e, [name:"Bob"]) ) - assertEquals("hello Bob", e.invoke(e.SAY_HI_2, [name:"Bob"]).get() ); - - } - - @Test - public void testCanRetrieveTaskForEffector() { - e.sayHi2("Bob", "hi") - - ManagementContext managementContext = e.getManagementContext() - - Set<Task> tasks = managementContext.getExecutionManager().getTasksWithAllTags([ - BrooklynTaskTags.tagForContextEntity(e),"EFFECTOR"]) - assertEquals(tasks.size(), 1) - assertTrue(tasks.iterator().next().getDescription().contains("sayHi2")) - } -} -public interface CanSayHi { - //prefer following simple groovy syntax - static Effector<String> SAY_HI_1 = new MethodEffector<String>(CanSayHi.&sayHi1); - //slightly longer-winded pojo also supported - static Effector<String> SAY_HI_1_ALT = new MethodEffector<String>(CanSayHi.class, "sayHi1"); - - @org.apache.brooklyn.core.annotation.Effector(description="says hello") - public String sayHi1( - @EffectorParam(name="name") String name, - @EffectorParam(name="greeting", defaultValue="hello", description="what to say") String greeting); - - //finally there is a way to provide a class/closure if needed or preferred for some odd reason - static Effector<String> SAY_HI_2 = - - //groovy 1.8.2 balks at runtime during getCallSiteArray (bug 5122) if we use anonymous inner class -// new ExplicitEffector<CanSayHi,String>( -// "sayHi2", String.class, [ -// [ "name", String.class, "person to say hi to" ] as BasicParameterType<String>, -// [ "greeting", String.class, "what to say as greeting", "hello" ] as BasicParameterType<String> -// ], -// "says hello to a person") { -// public String invokeEffector(CanSayHi e, Map m) { -// e.sayHi2(m) -// } -// }; - //following is a workaround, not greatly enamoured of it... but MethodEffector is generally preferred anyway - ExplicitEffector.create("sayHi2", String.class, [ - new BasicParameterType<String>("name", String.class, "person to say hi to"), - new BasicParameterType<String>("greeting", String.class, "what to say as greeting", "hello") - ], - "says hello", { e, m -> - def args = EffectorUtils.prepareArgsForEffector(SAY_HI_2, m); - e.sayHi2(args[0], args[1]) }) - - public String sayHi2(String name, String greeting); - -} - -@ImplementedBy(MyEntityImpl.class) -public interface MyEntity extends Entity, CanSayHi { -} - -public class MyEntityImpl extends AbstractEntity implements MyEntity { - public String sayHi1(String name, String greeting) { "$greeting $name" } - public String sayHi2(String name, String greeting) { "$greeting $name" } -}
