http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/hello/LocalEntitiesTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/hello/LocalEntitiesTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/hello/LocalEntitiesTest.java new file mode 100644 index 0000000..0ffa550 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/entity/hello/LocalEntitiesTest.java @@ -0,0 +1,282 @@ +/* + * 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.entity.hello; + +import static org.apache.brooklyn.sensor.core.DependentConfiguration.attributeWhenReady; +import static org.apache.brooklyn.sensor.core.DependentConfiguration.transform; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import groovy.lang.Closure; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.brooklyn.api.entity.EntityLocal; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.mgmt.EntityManager; +import org.apache.brooklyn.api.mgmt.Task; +import org.apache.brooklyn.api.sensor.SensorEvent; +import org.apache.brooklyn.api.sensor.SensorEventListener; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.EntityInternal; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.testng.collections.Lists; +import org.apache.brooklyn.location.core.SimulatedLocation; +import org.apache.brooklyn.test.Asserts; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.time.Time; + +import com.google.common.base.Function; +import com.google.common.base.Predicates; +import com.google.common.base.Stopwatch; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +/** tests effector invocation and a variety of sensor accessors and subscribers */ +public class LocalEntitiesTest extends BrooklynAppUnitTestSupport { + + public static final Logger log = LoggerFactory.getLogger(LocalEntitiesTest.class); + + private SimulatedLocation loc; + private EntityManager entityManager; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + loc = new SimulatedLocation(); + entityManager = mgmt.getEntityManager(); + } + + @Test + public void testEffectorUpdatesAttributeSensor() { + HelloEntity h = app.createAndManageChild(EntitySpec.create(HelloEntity.class)); + app.start(ImmutableList.of(loc)); + + h.setAge(5); + assertEquals((Integer)5, h.getAttribute(HelloEntity.AGE)); + } + + //REVIEW 1459 - new test + //subscriptions get notified in separate thread + @Test + public void testEffectorEmitsAttributeSensor() throws Exception { + final HelloEntity h = app.createAndManageChild(EntitySpec.create(HelloEntity.class)); + app.start(ImmutableList.of(loc)); + + final AtomicReference<SensorEvent<?>> evt = new AtomicReference<SensorEvent<?>>(); + final CountDownLatch latch = new CountDownLatch(1); + + app.getSubscriptionContext().subscribe(h, HelloEntity.AGE, new SensorEventListener<Integer>() { + @Override public void onEvent(SensorEvent<Integer> event) { + evt.set(event); + latch.countDown(); + }}); + + long startTime = System.currentTimeMillis(); + + h.invoke(HelloEntity.SET_AGE, ImmutableMap.of("age", 5)); + assertTrue(latch.await(5000, TimeUnit.MILLISECONDS)); + + // observed intermittent failure 2 May 2012. and 14 Jun "after 2 ms". spurious wakeups. + // code added above to guard against this. (if problem does not recur, remove these comments!) + assertNotNull(evt.get(), "null response after "+(System.currentTimeMillis()-startTime)+" ms"); + assertEquals(HelloEntity.AGE, evt.get().getSensor()); + assertEquals(h, evt.get().getSource()); + assertEquals(5, evt.get().getValue()); + assertTrue(System.currentTimeMillis() - startTime < 5000); //shouldn't have blocked for all 5s + } + + //REVIEW 1459 - new test + @Test + public void testEffectorEmitsTransientSensor() throws Exception { + HelloEntity h = app.createAndManageChild(EntitySpec.create(HelloEntity.class)); + app.start(ImmutableList.of(loc)); + + final AtomicReference<SensorEvent<?>> evt = new AtomicReference<SensorEvent<?>>(); + app.getSubscriptionContext().subscribe(h, HelloEntity.ITS_MY_BIRTHDAY, new SensorEventListener<Object>() { + @Override public void onEvent(SensorEvent<Object> event) { + evt.set(event); + synchronized (evt) { + evt.notifyAll(); + } + }}); + + long startTime = System.currentTimeMillis(); + synchronized (evt) { + h.setAge(5); + evt.wait(5000); + } + assertNotNull(evt.get()); + assertEquals(HelloEntity.ITS_MY_BIRTHDAY, evt.get().getSensor()); + assertEquals(h, evt.get().getSource()); + assertNull(evt.get().getValue()); + assertTrue(System.currentTimeMillis() - startTime < 5000); //shouldn't have blocked for all 5s + } + + @Test + public void testSendMultipleInOrderThenUnsubscribe() throws Exception { + HelloEntity h = app.createAndManageChild(EntitySpec.create(HelloEntity.class)); + app.start(ImmutableList.of(loc)); + + final List<Integer> data = Lists.newArrayList(); + final CountDownLatch latch = new CountDownLatch(5); + + app.getSubscriptionContext().subscribe(h, HelloEntity.AGE, new SensorEventListener<Integer>() { + @Override public void onEvent(SensorEvent<Integer> event) { + data.add(event.getValue()); + Time.sleep((int)(20*Math.random())); + log.info("Thread "+Thread.currentThread()+" notify on subscription received for "+event.getValue()+", data is "+data); + latch.countDown(); + }}); + + Stopwatch stopwatch = Stopwatch.createStarted(); + for (int i = 1; i <= 5; i++) { + h.setAge(i); + } + assertTrue(latch.await(5000, TimeUnit.MILLISECONDS)); + + app.getSubscriptionContext().unsubscribeAll(); + h.setAge(6); + long totalTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); + + // TODO guava util for (1..5) + Asserts.continually(MutableMap.of("timeout", 50), Suppliers.ofInstance(data), Predicates.<Object>equalTo(ImmutableList.of(1,2,3,4,5))); + assertTrue(totalTime < 2000, "totalTime="+totalTime); //shouldn't have blocked for anywhere close to 2s (Aled says TODO: too time sensitive for BuildHive?) + } + + @Test + public void testConfigSetFromAttribute() { + app.setConfig(HelloEntity.MY_NAME, "Bob"); + + HelloEntity dad = app.createAndManageChild(EntitySpec.create(HelloEntity.class)); + HelloEntity son = entityManager.createEntity(EntitySpec.create(HelloEntity.class).parent(dad)); + Entities.manage(son); + + //config is inherited + assertEquals("Bob", app.getConfig(HelloEntity.MY_NAME)); + assertEquals("Bob", dad.getConfig(HelloEntity.MY_NAME)); + assertEquals("Bob", son.getConfig(HelloEntity.MY_NAME)); + + //attributes are not + app.setAttribute(HelloEntity.FAVOURITE_NAME, "Carl"); + assertEquals("Carl", app.getAttribute(HelloEntity.FAVOURITE_NAME)); + assertEquals(null, dad.getAttribute(HelloEntity.FAVOURITE_NAME)); + } + @Test + public void testConfigSetFromAttributeWhenReady() throws Exception { + app.setConfig(HelloEntity.MY_NAME, "Bob"); + + final HelloEntity dad = app.createAndManageChild(EntitySpec.create(HelloEntity.class)); + final HelloEntity son = entityManager.createEntity(EntitySpec.create(HelloEntity.class) + .parent(dad) + .configure(HelloEntity.MY_NAME, attributeWhenReady(dad, HelloEntity.FAVOURITE_NAME + /* third param is closure; defaults to groovy truth (see google), but could be e.g. + , { it!=null && it.length()>0 && it!="Jebediah" } + */ ))); + Entities.manage(son); + + app.start(ImmutableList.of(loc)); + + final Semaphore s1 = new Semaphore(0); + final Object[] sonsConfig = new Object[1]; + Thread t = new Thread(new Runnable() { + public void run() { + log.info("started"); + s1.release(); + log.info("getting config "+sonsConfig[0]); + sonsConfig[0] = son.getConfig(HelloEntity.MY_NAME); + log.info("got config {}", sonsConfig[0]); + s1.release(); + }}); + + log.info("starting"); + long startTime = System.currentTimeMillis(); + t.start(); + log.info("waiting {}", System.identityHashCode(sonsConfig)); + if (!s1.tryAcquire(2, TimeUnit.SECONDS)) fail("race mismatch, missing permits"); + + //thread should be blocking on call to getConfig + assertTrue(t.isAlive()); + assertTrue(System.currentTimeMillis() - startTime < 1500); + synchronized (sonsConfig) { + assertEquals(null, sonsConfig[0]); + for (Task tt : ((EntityInternal)dad).getExecutionContext().getTasks()) { log.info("task at dad: {}, {}", tt, tt.getStatusDetail(false)); } + for (Task tt : ((EntityInternal)son).getExecutionContext().getTasks()) { log.info("task at son: {}, {}", tt, tt.getStatusDetail(false)); } + ((EntityLocal)dad).setAttribute(HelloEntity.FAVOURITE_NAME, "Dan"); + if (!s1.tryAcquire(2, TimeUnit.SECONDS)) fail("race mismatch, missing permits"); + } + log.info("dad: "+dad.getAttribute(HelloEntity.FAVOURITE_NAME)); + log.info("son: "+son.getConfig(HelloEntity.MY_NAME)); + + //shouldn't have blocked for very long at all + assertTrue(System.currentTimeMillis() - startTime < 1500); + //and sons config should now pick up the dad's attribute + assertEquals(sonsConfig[0], "Dan"); + } + + @Test + public void testConfigSetFromAttributeWhenReadyTransformations() { + app.setConfig(HelloEntity.MY_NAME, "Bob"); + + HelloEntity dad = app.createAndManageChild(EntitySpec.create(HelloEntity.class)); + HelloEntity son = entityManager.createEntity(EntitySpec.create(HelloEntity.class) + .parent(dad) + .configure(HelloEntity.MY_NAME, transform(attributeWhenReady(dad, HelloEntity.FAVOURITE_NAME), new Function<String,String>() { + public String apply(String input) { + return input+input.charAt(input.length()-1)+"y"; + }}))); + Entities.manage(son); + + app.start(ImmutableList.of(loc)); + ((EntityLocal)dad).setAttribute(HelloEntity.FAVOURITE_NAME, "Dan"); + assertEquals(son.getConfig(HelloEntity.MY_NAME), "Danny"); + } + + @Test + public void testConfigSetFromAttributeWhenReadyNullTransformations() { + app.setConfig(HelloEntity.MY_NAME, "Bob"); + + HelloEntity dad = app.createAndManageChild(EntitySpec.create(HelloEntity.class)); + // the unnecessary (HelloEntity) cast is required as a work-around to an IntelliJ issue that prevents Brooklyn from launching from the IDE + HelloEntity son = (HelloEntity)entityManager.createEntity(EntitySpec.create(HelloEntity.class) + .parent(dad) + .configure(HelloEntity.MY_NAME, transform(attributeWhenReady(dad, HelloEntity.FAVOURITE_NAME, (Closure)null), new Function<String,String>() { + public String apply(String input) { + return input+input.charAt(input.length()-1)+"y"; + }}))); + Entities.manage(son); + + app.start(ImmutableList.of(loc)); + ((EntityLocal)dad).setAttribute(HelloEntity.FAVOURITE_NAME, "Dan"); + assertEquals(son.getConfig(HelloEntity.MY_NAME), "Danny"); + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/internal/ConfigMapGroovyTest.groovy ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/internal/ConfigMapGroovyTest.groovy b/core/src/test/java/org/apache/brooklyn/core/entity/internal/ConfigMapGroovyTest.groovy new file mode 100644 index 0000000..44e67df --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/entity/internal/ConfigMapGroovyTest.groovy @@ -0,0 +1,61 @@ +/* + * 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.entity.internal; + +import static org.testng.Assert.assertEquals +import static org.testng.Assert.assertTrue + +import org.apache.brooklyn.core.test.entity.TestApplication +import org.apache.brooklyn.core.entity.Entities +import org.apache.brooklyn.core.entity.internal.ConfigMapTest.MyOtherEntity +import org.apache.brooklyn.core.entity.internal.ConfigMapTest.MySubEntity +import org.testng.annotations.AfterMethod +import org.testng.annotations.BeforeMethod +import org.testng.annotations.Test + +public class ConfigMapGroovyTest { + + private TestApplication app; + private MySubEntity entity; + + @BeforeMethod(alwaysRun=true) + public void setUp() { + app = TestApplication.Factory.newManagedInstanceForTests(); + entity = new MySubEntity(app); + Entities.manage(entity); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (app != null) Entities.destroyAll(app.getManagementContext()); + } + + @Test + public void testGetConfigOfTypeClosureReturnsClosure() throws Exception { + MyOtherEntity entity2 = new MyOtherEntity(app); + entity2.setConfig(MyOtherEntity.CLOSURE_KEY, { return "abc" } ); + Entities.manage(entity2); + + Closure configVal = entity2.getConfig(MyOtherEntity.CLOSURE_KEY); + assertTrue(configVal instanceof Closure, "configVal="+configVal); + assertEquals(configVal.call(), "abc"); + } + + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/internal/ConfigMapTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/internal/ConfigMapTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/internal/ConfigMapTest.java new file mode 100644 index 0000000..448ca07 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/entity/internal/ConfigMapTest.java @@ -0,0 +1,298 @@ +/* + * 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.entity.internal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import groovy.lang.Closure; + +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +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.config.ConfigKey; +import org.apache.brooklyn.config.ConfigMap; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.config.ConfigPredicates; +import org.apache.brooklyn.core.entity.AbstractEntity; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.sensor.core.BasicAttributeSensorAndConfigKey.IntegerAttributeSensorAndConfigKey; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.core.task.BasicTask; +import org.apache.brooklyn.util.core.task.DeferredSupplier; +import org.testng.Assert; +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.base.Predicates; +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.MoreExecutors; + +public class ConfigMapTest extends BrooklynAppUnitTestSupport { + + private static final int TIMEOUT_MS = 10*1000; + + private MySubEntity entity; + private ExecutorService executor; + private ExecutionManager executionManager; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + entity = new MySubEntity(app); + Entities.manage(entity); + executor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool()); + executionManager = mgmt.getExecutionManager(); + } + + @AfterMethod(alwaysRun=true) + @Override + public void tearDown() throws Exception { + if (executor != null) executor.shutdownNow(); + super.tearDown(); + } + + @Test + public void testGetConfigKeysReturnsFromSuperAndInterfacesAndSubClass() throws Exception { + assertEquals(entity.getEntityType().getConfigKeys(), ImmutableSet.of( + MySubEntity.SUPER_KEY_1, MySubEntity.SUPER_KEY_2, MySubEntity.SUB_KEY_2, MySubEntity.INTERFACE_KEY_1)); + } + + @Test + public void testConfigKeyDefaultUsesValueInSubClass() throws Exception { + assertEquals(entity.getConfig(MyBaseEntity.SUPER_KEY_1), "overridden superKey1 default"); + } + + @Test + public void testConfigureFromKey() throws Exception { + MySubEntity entity2 = new MySubEntity(MutableMap.of(MySubEntity.SUPER_KEY_1, "changed"), app); + Entities.manage(entity2); + assertEquals(entity2.getConfig(MySubEntity.SUPER_KEY_1), "changed"); + } + + @Test + public void testConfigureFromSuperKey() throws Exception { + MySubEntity entity2 = new MySubEntity(MutableMap.of(MyBaseEntity.SUPER_KEY_1, "changed"), app); + Entities.manage(entity2); + assertEquals(entity2.getConfig(MySubEntity.SUPER_KEY_1), "changed"); + } + + @Test + public void testConfigSubMap() throws Exception { + entity.setConfig(MyBaseEntity.SUPER_KEY_1, "s1"); + entity.setConfig(MySubEntity.SUB_KEY_2, "s2"); + ConfigMap sub = entity.getConfigMap().submap(ConfigPredicates.matchingGlob("sup*")); + Assert.assertEquals(sub.getConfigRaw(MyBaseEntity.SUPER_KEY_1, true).get(), "s1"); + Assert.assertFalse(sub.getConfigRaw(MySubEntity.SUB_KEY_2, true).isPresent()); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void testFailFastOnInvalidConfigKeyCoercion() throws Exception { + MyOtherEntity entity2 = new MyOtherEntity(app); + ConfigKey<Integer> key = MyOtherEntity.INT_KEY; + entity2.setConfig((ConfigKey)key, "thisisnotanint"); + } + + @Test + public void testGetConfigOfPredicateTaskReturnsCoercedClosure() throws Exception { + MyOtherEntity entity2 = new MyOtherEntity(app); + entity2.setConfig(MyOtherEntity.PREDICATE_KEY, Predicates.notNull()); + Entities.manage(entity2); + + Predicate predicate = entity2.getConfig(MyOtherEntity.PREDICATE_KEY); + assertTrue(predicate instanceof Predicate, "predicate="+predicate); + assertTrue(predicate.apply(1)); + assertFalse(predicate.apply(null)); + } + + @Test + public void testGetConfigWithDeferredSupplierReturnsSupplied() throws Exception { + DeferredSupplier<Integer> supplier = new DeferredSupplier<Integer>() { + volatile int next = 0; + public Integer get() { + return next++; + } + }; + + MyOtherEntity entity2 = new MyOtherEntity(app); + entity2.setConfig(MyOtherEntity.INT_KEY, supplier); + Entities.manage(entity2); + + assertEquals(entity2.getConfig(MyOtherEntity.INT_KEY), Integer.valueOf(0)); + assertEquals(entity2.getConfig(MyOtherEntity.INT_KEY), Integer.valueOf(1)); + } + + @Test + public void testGetConfigWithFutureWaitsForResult() throws Exception { + LatchingCallable work = new LatchingCallable("abc"); + Future<String> future = executor.submit(work); + + final MyOtherEntity entity2 = new MyOtherEntity(app); + entity2.setConfig((ConfigKey)MyOtherEntity.STRING_KEY, future); + Entities.manage(entity2); + + Future<String> getConfigFuture = executor.submit(new Callable<String>() { + public String call() { + return entity2.getConfig(MyOtherEntity.STRING_KEY); + }}); + + assertTrue(work.latchCalled.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertFalse(getConfigFuture.isDone()); + + work.latchContinued.countDown(); + assertEquals(getConfigFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS), "abc"); + } + + @Test + public void testGetConfigWithExecutedTaskWaitsForResult() throws Exception { + LatchingCallable work = new LatchingCallable("abc"); + Task<String> task = executionManager.submit(work); + + final MyOtherEntity entity2 = new MyOtherEntity(app); + entity2.setConfig(MyOtherEntity.STRING_KEY, task); + Entities.manage(entity2); + + Future<String> getConfigFuture = executor.submit(new Callable<String>() { + public String call() { + return entity2.getConfig(MyOtherEntity.STRING_KEY); + }}); + + assertTrue(work.latchCalled.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertFalse(getConfigFuture.isDone()); + + work.latchContinued.countDown(); + assertEquals(getConfigFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS), "abc"); + assertEquals(work.callCount.get(), 1); + } + + @Test + public void testGetConfigWithUnexecutedTaskIsExecutedAndWaitsForResult() throws Exception { + LatchingCallable work = new LatchingCallable("abc"); + Task<String> task = new BasicTask<String>(work); + + final MyOtherEntity entity2 = new MyOtherEntity(app); + entity2.setConfig(MyOtherEntity.STRING_KEY, task); + Entities.manage(entity2); + + Future<String> getConfigFuture = executor.submit(new Callable<String>() { + public String call() { + return entity2.getConfig(MyOtherEntity.STRING_KEY); + }}); + + assertTrue(work.latchCalled.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + assertFalse(getConfigFuture.isDone()); + + work.latchContinued.countDown(); + assertEquals(getConfigFuture.get(TIMEOUT_MS, TimeUnit.MILLISECONDS), "abc"); + assertEquals(work.callCount.get(), 1); + } + + public static class MyBaseEntity extends AbstractEntity { + public static final ConfigKey<String> SUPER_KEY_1 = ConfigKeys.newStringConfigKey("superKey1", "superKey1 key", "superKey1 default"); + public static final ConfigKey<String> SUPER_KEY_2 = ConfigKeys.newStringConfigKey("superKey2", "superKey2 key", "superKey2 default"); + + public MyBaseEntity() { + } + public MyBaseEntity(Map flags) { + super(flags); + } + public MyBaseEntity(Map flags, Entity parent) { + super(flags, parent); + } + public MyBaseEntity(Entity parent) { + super(parent); + } + } + + public static class MySubEntity extends MyBaseEntity implements MyInterface { + public static final ConfigKey<String> SUPER_KEY_1 = ConfigKeys.newConfigKeyWithDefault(MyBaseEntity.SUPER_KEY_1, "overridden superKey1 default"); + public static final ConfigKey<String> SUB_KEY_2 = ConfigKeys.newStringConfigKey("subKey2", "subKey2 key", "subKey2 default"); + + MySubEntity() { + } + MySubEntity(Map flags) { + super(flags); + } + MySubEntity(Map flags, Entity parent) { + super(flags, parent); + } + MySubEntity(Entity parent) { + super(parent); + } + } + + public interface MyInterface { + public static final ConfigKey<String> INTERFACE_KEY_1 = ConfigKeys.newStringConfigKey("interfaceKey1", "interface key 1", "interfaceKey1 default"); + } + + public static class MyOtherEntity extends AbstractEntity { + public static final ConfigKey<Integer> INT_KEY = ConfigKeys.newIntegerConfigKey("intKey", "int key", 1); + public static final ConfigKey<String> STRING_KEY = ConfigKeys.newStringConfigKey("stringKey", "string key", null); + public static final ConfigKey<Object> OBJECT_KEY = ConfigKeys.newConfigKey(Object.class, "objectKey", "object key", null); + public static final ConfigKey<Closure> CLOSURE_KEY = ConfigKeys.newConfigKey(Closure.class, "closureKey", "closure key", null); + public static final ConfigKey<Future> FUTURE_KEY = ConfigKeys.newConfigKey(Future.class, "futureKey", "future key", null); + public static final ConfigKey<Task> TASK_KEY = ConfigKeys.newConfigKey(Task.class, "taskKey", "task key", null); + public static final ConfigKey<Predicate> PREDICATE_KEY = ConfigKeys.newConfigKey(Predicate.class, "predicateKey", "predicate key", null); + public static final IntegerAttributeSensorAndConfigKey SENSOR_AND_CONFIG_KEY = new IntegerAttributeSensorAndConfigKey("sensorConfigKey", "sensor+config key", 1); + + public MyOtherEntity() { + } + public MyOtherEntity(Map flags) { + super(flags); + } + public MyOtherEntity(Map flags, Entity parent) { + super(flags, parent); + } + public MyOtherEntity(Entity parent) { + super(parent); + } + } + + static class LatchingCallable<T> implements Callable<T> { + final CountDownLatch latchCalled = new CountDownLatch(1); + final CountDownLatch latchContinued = new CountDownLatch(1); + final AtomicInteger callCount = new AtomicInteger(0); + final T result; + + public LatchingCallable(T result) { + this.result = result; + } + + @Override + public T call() throws Exception { + callCount.incrementAndGet(); + latchCalled.countDown(); + latchContinued.await(); + return result; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/internal/EntityConfigMapUsageLegacyTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/internal/EntityConfigMapUsageLegacyTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/internal/EntityConfigMapUsageLegacyTest.java new file mode 100644 index 0000000..801b3e5 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/entity/internal/EntityConfigMapUsageLegacyTest.java @@ -0,0 +1,292 @@ +/* + * 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.entity.internal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; + +import org.apache.brooklyn.sensor.core.DependentConfiguration; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.time.Time; +import org.testng.annotations.Test; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.entity.Entities; +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.location.core.SimulatedLocation; + +import com.google.common.base.Function; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.Callables; + +/** + * Test that configuration properties are usable and inherited correctly. + * + * Uses legacy mechanism of calling entity constructors. + */ +public class EntityConfigMapUsageLegacyTest extends BrooklynAppUnitTestSupport { + private ConfigKey<Integer> intKey = ConfigKeys.newIntegerConfigKey("bkey", "b key"); + private ConfigKey<String> strKey = ConfigKeys.newStringConfigKey("akey", "a key"); + private ConfigKey<Integer> intKeyWithDefault = ConfigKeys.newIntegerConfigKey("ckey", "c key", 1); + private ConfigKey<String> strKeyWithDefault = ConfigKeys.newStringConfigKey("strKey", "str key", "str key default"); + + @Test + public void testConfigPassedInAtConstructorIsAvailable() throws Exception { + TestEntity entity = new TestEntityImpl(MutableMap.of("config", MutableMap.of(strKey, "aval", intKey, 2)), app); + Entities.manage(entity); + + assertEquals(entity.getConfig(strKey), "aval"); + assertEquals(entity.getConfig(intKey), Integer.valueOf(2)); + } + + @Test + public void testConfigSetToGroovyTruthFalseIsAvailable() throws Exception { + TestEntity entity = new TestEntityImpl(MutableMap.of("config", MutableMap.of(intKeyWithDefault, 0)), app); + Entities.manage(entity); + + assertEquals(entity.getConfig(intKeyWithDefault), (Integer)0); + } + + @Test + public void testInheritedConfigSetToGroovyTruthFalseIsAvailable() throws Exception { + TestEntity parent = new TestEntityImpl(MutableMap.of("config", MutableMap.of(intKeyWithDefault, 0)), app); + TestEntity entity = new TestEntityImpl(parent); + Entities.manage(parent); + + assertEquals(entity.getConfig(intKeyWithDefault), (Integer)0); + } + + @Test + public void testConfigSetToNullIsAvailable() throws Exception { + TestEntity entity = new TestEntityImpl(MutableMap.of("config", MutableMap.of(strKeyWithDefault, null)), app); + Entities.manage(entity); + + assertEquals(entity.getConfig(strKeyWithDefault), null); + } + + @Test + public void testInheritedConfigSetToNullIsAvailable() throws Exception { + TestEntity parent = new TestEntityImpl(MutableMap.of("config", MutableMap.of(strKeyWithDefault, null)), app); + TestEntity entity = new TestEntityImpl(parent); + Entities.manage(parent); + + assertEquals(entity.getConfig(strKeyWithDefault), null); + } + + @Test + public void testConfigCanBeSetOnEntity() throws Exception { + TestEntity entity = new TestEntityImpl(app); + entity.setConfig(strKey, "aval"); + entity.setConfig(intKey, 2); + Entities.manage(entity); + + assertEquals(entity.getConfig(strKey), "aval"); + assertEquals(entity.getConfig(intKey), (Integer)2); + } + + @Test + public void testConfigInheritedFromParent() throws Exception { + TestEntity parent = new TestEntityImpl(MutableMap.of("config", MutableMap.of(strKey, "aval")), app); + parent.setConfig(intKey, 2); + TestEntity entity = new TestEntityImpl(parent); + Entities.manage(parent); + + assertEquals(entity.getConfig(strKey), "aval"); + assertEquals(entity.getConfig(intKey), (Integer)2); + } + + @Test + public void testConfigInConstructorOverridesParentValue() throws Exception { + TestEntity parent = new TestEntityImpl(MutableMap.of("config", MutableMap.of(strKey, "aval")), app); + TestEntity entity = new TestEntityImpl(MutableMap.of("config", MutableMap.of(strKey, "diffval")), parent); + Entities.manage(parent); + + assertEquals("diffval", entity.getConfig(strKey)); + } + + @Test + public void testConfigSetterOverridesParentValue() throws Exception { + TestEntity parent = new TestEntityImpl(MutableMap.of("config", MutableMap.of(strKey, "aval")), app); + TestEntity entity = new TestEntityImpl(parent); + entity.setConfig(strKey, "diffval"); + Entities.manage(parent); + + assertEquals("diffval", entity.getConfig(strKey)); + } + + @Test + public void testConfigSetterOverridesConstructorValue() throws Exception { + TestEntity entity = new TestEntityImpl(MutableMap.of("config", MutableMap.of(strKey, "aval")), app); + entity.setConfig(strKey, "diffval"); + Entities.manage(entity); + + assertEquals("diffval", entity.getConfig(strKey)); + } + + @Test + public void testConfigSetOnParentInheritedByExistingChildrenBeforeStarted() throws Exception { + TestEntity entity = new TestEntityImpl(app); + app.setConfig(strKey,"aval"); + Entities.manage(entity); + + assertEquals("aval", entity.getConfig(strKey)); + } + + @Test + public void testConfigInheritedThroughManyGenerations() throws Exception { + TestEntity e = new TestEntityImpl(app); + TestEntity e2 = new TestEntityImpl(e); + app.setConfig(strKey,"aval"); + Entities.manage(e); + + assertEquals("aval", app.getConfig(strKey)); + assertEquals("aval", e.getConfig(strKey)); + assertEquals("aval", e2.getConfig(strKey)); + } + + @Test(enabled=false) + public void testConfigCannotBeSetAfterApplicationIsStarted() throws Exception { + TestEntity entity = new TestEntityImpl(app); + Entities.manage(entity); + app.start(ImmutableList.of(new SimulatedLocation())); + + try { + app.setConfig(strKey,"aval"); + fail(); + } catch (IllegalStateException e) { + // success + } + + assertEquals(null, entity.getConfig(strKey)); + } + + @Test + public void testConfigReturnsDefaultValueIfNotSet() throws Exception { + TestEntity entity = new TestEntityImpl(app); + Entities.manage(entity); + assertEquals(entity.getConfig(TestEntity.CONF_NAME), "defaultval"); + } + + @Test + public void testGetFutureConfigWhenReady() throws Exception { + TestEntity entity = new TestEntityImpl(app); + entity.setConfig(TestEntity.CONF_NAME, DependentConfiguration.whenDone(Callables.returning("aval"))); + Entities.manage(entity); + app.start(ImmutableList.of(new SimulatedLocation())); + + assertEquals(entity.getConfig(TestEntity.CONF_NAME), "aval"); + } + + @Test + public void testGetFutureConfigBlocksUntilReady() throws Exception { + TestEntity entity = new TestEntityImpl(app); + final CountDownLatch latch = new CountDownLatch(1); + entity.setConfig(TestEntity.CONF_NAME, DependentConfiguration.whenDone(new Callable<String>() { + @Override public String call() throws Exception { + latch.await(); + return "aval"; + }})); + Entities.manage(entity); + app.start(ImmutableList.of(new SimulatedLocation())); + + Thread t = new Thread(new Runnable() { + public void run() { + Time.sleep(10); + latch.countDown(); + }}); + try { + long starttime = System.currentTimeMillis(); + t.start(); + assertEquals(entity.getConfig(TestEntity.CONF_NAME), "aval"); + long endtime = System.currentTimeMillis(); + + assertTrue((endtime - starttime) >= 10, "starttime="+starttime+"; endtime="+endtime); + + } finally { + t.interrupt(); + } + } + + @Test + public void testGetAttributeWhenReadyConfigReturnsWhenSet() throws Exception { + TestEntity entity = new TestEntityImpl(app); + TestEntity entity2 = new TestEntityImpl(app); + entity.setConfig(TestEntity.CONF_NAME, DependentConfiguration.attributeWhenReady(entity2, TestEntity.NAME)); + Entities.manage(entity); + Entities.manage(entity2); + app.start(ImmutableList.of(new SimulatedLocation())); + + entity2.setAttribute(TestEntity.NAME, "aval"); + assertEquals(entity.getConfig(TestEntity.CONF_NAME), "aval"); + } + + @Test + public void testGetAttributeWhenReadyWithPostProcessingConfigReturnsWhenSet() throws Exception { + TestEntity entity = new TestEntityImpl(app); + TestEntity entity2 = new TestEntityImpl(app); + entity.setConfig(TestEntity.CONF_NAME, DependentConfiguration.attributePostProcessedWhenReady(entity2, TestEntity.NAME, Predicates.notNull(), new Function<String,String>() { + @Override public String apply(String input) { + return (input == null) ? null : input+"mysuffix"; + }})); + Entities.manage(entity); + Entities.manage(entity2); + app.start(ImmutableList.of(new SimulatedLocation())); + + entity2.setAttribute(TestEntity.NAME, "aval"); + assertEquals(entity.getConfig(TestEntity.CONF_NAME), "avalmysuffix"); + } + + @Test + public void testGetAttributeWhenReadyConfigBlocksUntilSet() throws Exception { + TestEntity entity = new TestEntityImpl(app); + final TestEntity entity2 = new TestEntityImpl(app); + entity.setConfig(TestEntity.CONF_NAME, DependentConfiguration.attributeWhenReady(entity2, TestEntity.NAME)); + Entities.manage(entity); + Entities.manage(entity2); + app.start(ImmutableList.of(new SimulatedLocation())); + + // previously was just sleep 10, and (endtime-starttime > 10); failed with exactly 10ms + final long sleepTime = 20; + final long earlyReturnGrace = 5; + Thread t = new Thread(new Runnable() { + @Override public void run() { + Time.sleep(sleepTime); + entity2.setAttribute(TestEntity.NAME, "aval"); + }}); + try { + long starttime = System.currentTimeMillis(); + t.start(); + assertEquals(entity.getConfig(TestEntity.CONF_NAME), "aval"); + long endtime = System.currentTimeMillis(); + + assertTrue((endtime - starttime) >= (sleepTime - earlyReturnGrace), "starttime=$starttime; endtime=$endtime"); + + } finally { + t.interrupt(); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/internal/EntityConfigMapUsageTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/internal/EntityConfigMapUsageTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/internal/EntityConfigMapUsageTest.java new file mode 100644 index 0000000..5773e5e --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/entity/internal/EntityConfigMapUsageTest.java @@ -0,0 +1,318 @@ +/* + * 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.entity.internal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; + +import org.apache.brooklyn.api.entity.EntityLocal; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.BasicConfigKey; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.sensor.core.DependentConfiguration; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import org.apache.brooklyn.location.core.SimulatedLocation; + +import com.google.common.base.Function; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.Callables; + +/** + * Test that configuration properties are usable and inherited correctly. + */ +public class EntityConfigMapUsageTest extends BrooklynAppUnitTestSupport { + private static final int EARLY_RETURN_GRACE = 10; + + private BasicConfigKey<Integer> intKey = new BasicConfigKey<Integer>(Integer.class, "bkey", "b key"); + private ConfigKey<String> strKey = new BasicConfigKey<String>(String.class, "akey", "a key"); + private ConfigKey<Integer> intKeyWithDefault = new BasicConfigKey<Integer>(Integer.class, "ckey", "c key", 1); + private ConfigKey<String> strKeyWithDefault = new BasicConfigKey<String>(String.class, "strKey", "str key", "str key default"); + + private List<SimulatedLocation> locs; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + locs = ImmutableList.of(new SimulatedLocation()); + } + + @Test + public void testConfigPassedInAtConstructionIsAvailable() throws Exception { + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(strKey, "aval") + .configure(intKey, 2)); + + assertEquals(entity.getConfig(strKey), "aval"); + assertEquals(entity.getConfig(intKey), (Integer)2); + } + + @Test + public void testConfigSetToGroovyTruthFalseIsAvailable() throws Exception { + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(intKeyWithDefault, 0)); + + assertEquals(entity.getConfig(intKeyWithDefault), (Integer)0); + } + + @Test + public void testInheritedConfigSetToGroovyTruthFalseIsAvailable() throws Exception { + TestEntity parent = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(intKeyWithDefault, 0)); + TestEntity entity = parent.createAndManageChild(EntitySpec.create(TestEntity.class)); + + assertEquals(entity.getConfig(intKeyWithDefault), (Integer)0); + } + + @Test + public void testConfigSetToNullIsAvailable() throws Exception { + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(strKeyWithDefault, (String)null)); + + assertEquals(entity.getConfig(strKeyWithDefault), null); + } + + @Test + public void testInheritedConfigSetToNullIsAvailable() throws Exception { + TestEntity parent = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(strKeyWithDefault, (String)null)); + TestEntity entity = parent.createAndManageChild(EntitySpec.create(TestEntity.class)); + + assertEquals(entity.getConfig(strKeyWithDefault), null); + } + + @Test + public void testInheritedConfigAvailableDeepInHierarchy() throws Exception { + TestEntity parent = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(strKeyWithDefault, "customval")); + TestEntity entity = parent.createAndManageChild(EntitySpec.create(TestEntity.class)); + TestEntity entity2 = entity.createAndManageChild(EntitySpec.create(TestEntity.class)); + TestEntity entity3 = entity2.createAndManageChild(EntitySpec.create(TestEntity.class)); + + assertEquals(entity.getConfig(strKeyWithDefault), "customval"); + assertEquals(entity2.getConfig(strKeyWithDefault), "customval"); + assertEquals(entity3.getConfig(strKeyWithDefault), "customval"); + } + + @Test + public void testConfigCanBeSetOnEntity() throws Exception { + TestEntity entity = app.addChild(EntitySpec.create(TestEntity.class)); + ((EntityLocal)entity).setConfig(strKey, "aval"); + ((EntityLocal)entity).setConfig(intKey, 2); + Entities.manage(entity); + + assertEquals(entity.getConfig(strKey), "aval"); + assertEquals(entity.getConfig(intKey), (Integer)2); + } + + @Test + public void testConfigInheritedFromParent() throws Exception { + TestEntity parent = app.addChild(EntitySpec.create(TestEntity.class) + .configure(strKey, "aval")); + ((EntityLocal)parent).setConfig(intKey, 2); + Entities.manage(parent); + TestEntity entity = parent.createAndManageChild(EntitySpec.create(TestEntity.class)); + + assertEquals(entity.getConfig(strKey), "aval"); + assertEquals(2, entity.getConfig(intKey), (Integer)2); + } + + @Test + public void testConfigAtConstructionOverridesParentValue() throws Exception { + TestEntity parent = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(strKey, "aval")); + TestEntity entity = parent.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(strKey, "diffval")); + + assertEquals(entity.getConfig(strKey), "diffval"); + } + + @Test + public void testConfigSetterOverridesParentValue() throws Exception { + TestEntity parent = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(strKey, "aval")); + TestEntity entity = parent.createAndManageChild(EntitySpec.create(TestEntity.class)); + ((EntityLocal)entity).setConfig(strKey, "diffval"); + + assertEquals(entity.getConfig(strKey), "diffval"); + } + + @Test + public void testConfigSetterOverridesConstructorValue() throws Exception { + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(strKey, "aval")); + ((EntityLocal)entity).setConfig(strKey, "diffval"); + Entities.manage(entity); + + assertEquals(entity.getConfig(strKey), "diffval"); + } + + @Test + public void testConfigSetOnParentInheritedByExistingChildrenBeforeStarted() throws Exception { + TestEntity parent = app.addChild(EntitySpec.create(TestEntity.class)); + TestEntity entity = parent.createChild(EntitySpec.create(TestEntity.class)); + ((EntityLocal)parent).setConfig(strKey,"aval"); + Entities.manage(entity); + + assertEquals(entity.getConfig(strKey), "aval"); + } + + @Test + public void testConfigInheritedThroughManyGenerations() throws Exception { + TestEntity e = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(strKey, "aval")); + TestEntity e2 = e.createAndManageChild(EntitySpec.create(TestEntity.class)); + TestEntity e3 = e2.createAndManageChild(EntitySpec.create(TestEntity.class)); + + assertEquals(e.getConfig(strKey), "aval"); + assertEquals(e2.getConfig(strKey), "aval"); + assertEquals(e3.getConfig(strKey), "aval"); + } + + // This has been relaxed to a warning, with a message saying "may not be supported in future versions" + @Test(enabled=false) + public void testConfigCannotBeSetAfterApplicationIsStarted() throws Exception { + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + app.start(locs); + + try { + ((EntityLocal)app).setConfig(strKey,"aval"); + fail(); + } catch (IllegalStateException e) { + // success + } + + assertEquals(entity.getConfig(strKey), null); + } + + @Test + public void testConfigReturnsDefaultValueIfNotSet() throws Exception { + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + assertEquals(entity.getConfig(TestEntity.CONF_NAME), "defaultval"); + } + + @Test + public void testGetFutureConfigWhenReady() throws Exception { + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(TestEntity.CONF_NAME, DependentConfiguration.whenDone(Callables.returning("aval")))); + app.start(locs); + + assertEquals(entity.getConfig(TestEntity.CONF_NAME), "aval"); + } + + @Test + public void testGetFutureConfigBlocksUntilReady() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(TestEntity.CONF_NAME, DependentConfiguration.whenDone(new Callable<String>() { + public String call() { + try { + latch.await(); return "aval"; + } catch (InterruptedException e) { + throw Exceptions.propagate(e); + } + }}))); + app.start(locs); + + Thread t = new Thread(new Runnable() { + public void run() { + try { + Thread.sleep(10+EARLY_RETURN_GRACE); latch.countDown(); + } catch (InterruptedException e) { + throw Exceptions.propagate(e); + } + }}); + try { + long starttime = System.currentTimeMillis(); + t.start(); + assertEquals(entity.getConfig(TestEntity.CONF_NAME), "aval"); + long endtime = System.currentTimeMillis(); + + assertTrue((endtime - starttime) >= 10, "starttime="+starttime+"; endtime="+endtime); + + } finally { + t.interrupt(); + } + } + + @Test + public void testGetAttributeWhenReadyConfigReturnsWhenSet() throws Exception { + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(TestEntity.CONF_NAME, DependentConfiguration.attributeWhenReady(entity, TestEntity.NAME))); + app.start(locs); + + ((EntityLocal)entity).setAttribute(TestEntity.NAME, "aval"); + assertEquals(entity2.getConfig(TestEntity.CONF_NAME), "aval"); + } + + @Test + public void testGetAttributeWhenReadyWithPostProcessingConfigReturnsWhenSet() throws Exception { + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(TestEntity.CONF_NAME, DependentConfiguration.attributePostProcessedWhenReady(entity, TestEntity.NAME, Predicates.notNull(), new Function<String,String>() { + public String apply(String input) { + return input+"mysuffix"; + }}))); + app.start(locs); + + ((EntityLocal)entity).setAttribute(TestEntity.NAME, "aval"); + assertEquals(entity2.getConfig(TestEntity.CONF_NAME), "avalmysuffix"); + } + + @Test + public void testGetAttributeWhenReadyConfigBlocksUntilSet() throws Exception { + final TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + TestEntity entity2 = app.createAndManageChild(EntitySpec.create(TestEntity.class) + .configure(TestEntity.CONF_NAME, DependentConfiguration.attributeWhenReady(entity, TestEntity.NAME))); + app.start(locs); + + Thread t = new Thread(new Runnable() { + public void run() { + try { + Thread.sleep(10+EARLY_RETURN_GRACE); + ((EntityLocal)entity).setAttribute(TestEntity.NAME, "aval"); + } catch (InterruptedException e) { + throw Exceptions.propagate(e); + } + }}); + try { + long starttime = System.currentTimeMillis(); + t.start(); + assertEquals(entity2.getConfig(TestEntity.CONF_NAME), "aval"); + long endtime = System.currentTimeMillis(); + + assertTrue((endtime - starttime) > 10, "starttime="+starttime+"; endtime="+endtime); + + } finally { + t.interrupt(); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/LifecycleTransitionTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/LifecycleTransitionTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/LifecycleTransitionTest.java new file mode 100644 index 0000000..8b76355 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/LifecycleTransitionTest.java @@ -0,0 +1,51 @@ +/* + * 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.entity.lifecycle; + +import static org.testng.Assert.assertTrue; + +import java.util.Date; + +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle.Transition; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle.TransitionCoalesceFunction; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class LifecycleTransitionTest { + @DataProvider(name = "states") + public Object[][] generateLifecycleStates() { + Object[][] states = new Object[Lifecycle.values().length][]; + int i = 0; + for (Lifecycle state : Lifecycle.values()) { + System.out.println(":" + state); + states[i] = new Object[] {state}; + i++; + } + return states; + } + + @Test(dataProvider="states") + public void testTransitionCoalesce(Lifecycle state) { + Transition t = new Transition(state, new Date()); + String serialized = t.toString(); + Transition t2 = new TransitionCoalesceFunction().apply(serialized); + assertTrue(t.equals(t2), "Deserialized Lifecycle.Transition not equal to original"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogicTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogicTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogicTest.java new file mode 100644 index 0000000..f461e4b --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogicTest.java @@ -0,0 +1,314 @@ +/* + * 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.entity.lifecycle; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.api.sensor.Enricher; +import org.apache.brooklyn.core.entity.Attributes; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.EntityAdjuncts; +import org.apache.brooklyn.core.entity.EntityInternal; +import org.apache.brooklyn.core.entity.lifecycle.Lifecycle; +import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic; +import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers; +import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceNotUpLogic; +import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceProblemsLogic; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.core.test.entity.TestEntityImpl.TestEntityWithoutEnrichers; +import org.apache.brooklyn.entity.group.DynamicCluster; +import org.apache.brooklyn.sensor.core.Sensors; +import org.apache.brooklyn.test.EntityTestUtils; +import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.time.Duration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +@Test +public class ServiceStateLogicTest extends BrooklynAppUnitTestSupport { + + private static final Logger log = LoggerFactory.getLogger(ServiceStateLogicTest.class); + + final static String INDICATOR_KEY_1 = "test-indicator-1"; + final static String INDICATOR_KEY_2 = "test-indicator-2"; + + protected TestEntity entity; + + @Override + protected void setUpApp() { + super.setUpApp(); + entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + } + + + public void testManuallySettingIndicatorsOnEntities() { + // if we set a not up indicator, entity service up should become false + ServiceNotUpLogic.updateNotUpIndicator(entity, INDICATOR_KEY_1, "We're pretending to block service up"); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, false); + + // but state will not change unless we also set either a problem or expected state + assertAttributeEquals(entity, Attributes.SERVICE_STATE_ACTUAL, null); + ServiceProblemsLogic.updateProblemsIndicator(entity, INDICATOR_KEY_1, "We're pretending to block service state also"); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); + + // and if we clear the not up indicator, service up becomes true, but there is a problem, so it shows on-fire + ServiceNotUpLogic.clearNotUpIndicator(entity, INDICATOR_KEY_1); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + + // if we then clear the problem also, state goes to RUNNING + ServiceProblemsLogic.clearProblemsIndicator(entity, INDICATOR_KEY_1); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + + // now add not-up indicator again, and it reverts to up=false, state=stopped + ServiceNotUpLogic.updateNotUpIndicator(entity, INDICATOR_KEY_1, "We're again pretending to block service up"); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, false); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); + + // but if we expect it to be running it will show on fire (because service is not up) + ServiceStateLogic.setExpectedState(entity, Lifecycle.RUNNING); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + + // and if we again clear the not up indicator it will deduce up=true and state=running + ServiceNotUpLogic.clearNotUpIndicator(entity, INDICATOR_KEY_1); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + } + + public void testAppStoppedAndEntityNullBeforeStarting() { + // AbstractApplication has default logic to ensure service is not up if it hasn't been started, + // (this can be removed by updating the problem indicator associated with the SERVICE_STATE_ACTUAL sensor) + assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, false); + // and from that it imputes stopped state + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); + + // TestEntity has no such indicators however + assertAttributeEquals(entity, Attributes.SERVICE_UP, null); + assertAttributeEquals(entity, Attributes.SERVICE_STATE_ACTUAL, null); + } + + public void testAllUpAndRunningAfterStart() { + app.start(ImmutableList.<Location>of()); + + assertAttributeEquals(app, Attributes.SERVICE_UP, true); + assertAttributeEquals(entity, Attributes.SERVICE_UP, true); + // above should be immediate, entity should then derive RUNNING from expected state, and then so should app from children + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + assertAttributeEquals(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + } + + public void testStopsNicelyToo() { + app.start(ImmutableList.<Location>of()); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + + app.stop(); + + assertAttributeEquals(app, Attributes.SERVICE_UP, false); + assertAttributeEquals(entity, Attributes.SERVICE_UP, false); + // above should be immediate, app and entity should then derive STOPPED from the expected state + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); + } + + public void testTwoIndicatorsAreBetterThanOne() { + // if we set a not up indicator, entity service up should become false + ServiceNotUpLogic.updateNotUpIndicator(entity, INDICATOR_KEY_1, "We're pretending to block service up"); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, false); + ServiceNotUpLogic.updateNotUpIndicator(entity, INDICATOR_KEY_2, "We're also pretending to block service up"); + ServiceNotUpLogic.clearNotUpIndicator(entity, INDICATOR_KEY_1); + // clearing one indicator is not sufficient + assertAttributeEquals(entity, Attributes.SERVICE_UP, false); + + // but it does not become true when both are cleared + ServiceNotUpLogic.clearNotUpIndicator(entity, INDICATOR_KEY_2); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true); + } + + @Test(invocationCount=100, groups="Integration") + public void testManuallySettingIndicatorsOnApplicationsManyTimes() throws Exception { + testManuallySettingIndicatorsOnApplications(); + } + + public void testManuallySettingIndicatorsOnApplications() throws Exception { + // indicators on application are more complicated because it is configured with additional indicators from its children + // test a lot of situations, including reconfiguring some of the quorum config + + // to begin with, an entity has not reported anything, so the ComputeServiceIndicatorsFromChildren ignores it + // but the AbstractApplication has emitted a not-up indicator because it has not been started + // both as asserted by this other test: + testAppStoppedAndEntityNullBeforeStarting(); + + // if we clear the not up indicator, the app will show as up, and as running, because it has no reporting children + ServiceNotUpLogic.clearNotUpIndicator(app, Attributes.SERVICE_STATE_ACTUAL); + assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, true); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + + // if we then put a not-up indicator on the TestEntity, it publishes false, but app is still up. State + // won't propagate due to it's SERVICE_STATE_ACTUAL (null) being in IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES + ServiceNotUpLogic.updateNotUpIndicator(entity, INDICATOR_KEY_1, "We're also pretending to block service up"); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, false); + assertAttributeEqualsContinually(app, Attributes.SERVICE_UP, true); + assertAttributeEqualsContinually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + // the entity still has no opinion about its state + assertAttributeEqualsContinually(entity, Attributes.SERVICE_STATE_ACTUAL, null); + + // switching the entity state to one not in IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES will propagate the up state + ServiceStateLogic.setExpectedState(entity, Lifecycle.RUNNING); + assertAttributeEqualsContinually(entity, Attributes.SERVICE_UP, false); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, false); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); + + + // if the entity expects to be stopped, it will report stopped + ServiceStateLogic.setExpectedState(entity, Lifecycle.STOPPED); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); + // and the app will ignore the entity state, so becomes running + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, true); + + // if we clear the not-up indicator, both the entity and the app report service up (with the entity first) + ServiceNotUpLogic.clearNotUpIndicator(entity, INDICATOR_KEY_1); + assertAttributeEqualsEventually(entity, Attributes.SERVICE_UP, true); + // but entity is still stopped because that is what is expected there, and that's okay even if service is apparently up + assertAttributeEqualsEventually(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); + // the app however is running, because the default state quorum check is "all are healthy" + assertAttributeEqualsContinually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + assertAttributeEqualsContinually(app, Attributes.SERVICE_UP, true); + + // if we change the state quorum check for the app to be "all are healthy and at least one running" *then* it shows stopped + // (normally this would be done in `initEnrichers` of course) + Enricher appChildrenBasedEnricher = EntityAdjuncts.tryFindWithUniqueTag(app.getEnrichers(), ComputeServiceIndicatorsFromChildrenAndMembers.DEFAULT_UNIQUE_TAG).get(); + appChildrenBasedEnricher.config().set(ComputeServiceIndicatorsFromChildrenAndMembers.RUNNING_QUORUM_CHECK, QuorumChecks.allAndAtLeastOne()); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + + // if entity is expected running, then it will show running because service is up; this is reflected at app and at entity + ServiceStateLogic.setExpectedState(entity, Lifecycle.RUNNING); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, true); + assertAttributeEquals(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + + // now, when the entity is unmanaged, the app goes on fire because don't have "at least one running" + Entities.unmanage(entity); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + // but UP_QUORUM_CHECK is still the default atLeastOneUnlessEmpty; so serviceUp=true + assertAttributeEqualsContinually(app, Attributes.SERVICE_UP, true); + + // if we change its up-quorum to atLeastOne then state becomes stopped (because there is no expected state; has not been started) + appChildrenBasedEnricher.config().set(ComputeServiceIndicatorsFromChildrenAndMembers.UP_QUORUM_CHECK, QuorumChecks.atLeastOne()); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.STOPPED); + assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, false); + + // if we now start it will successfully start (because unlike entities it does not wait for service up) + // but will remain down and will go on fire + app.start(ImmutableList.<Location>of()); + assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, false); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + + // restoring up-quorum to "atLeastOneUnlessEmpty" causes it to become RUNNING, because happy with empty + appChildrenBasedEnricher.config().set(ComputeServiceIndicatorsFromChildrenAndMembers.UP_QUORUM_CHECK, QuorumChecks.atLeastOneUnlessEmpty()); + assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, true); + // but running-quorum is still allAndAtLeastOne, so remains on-fire + assertAttributeEqualsContinually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + + // now add a child, it's still up and running because null values are ignored by default (i.e. we're still "empty") + entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + assertAttributeEqualsContinually(app, Attributes.SERVICE_UP, true); + assertAttributeEqualsContinually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + + // tell it not to ignore null values for children states, and it will go onfire (but still be service up) + appChildrenBasedEnricher.config().set(ComputeServiceIndicatorsFromChildrenAndMembers.IGNORE_ENTITIES_WITH_THESE_SERVICE_STATES, + ImmutableSet.<Lifecycle>of()); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE); + assertAttributeEquals(app, Attributes.SERVICE_UP, true); + + // tell it not to ignore null values for service up and it will go service down + appChildrenBasedEnricher.config().set(ComputeServiceIndicatorsFromChildrenAndMembers.IGNORE_ENTITIES_WITH_SERVICE_UP_NULL, false); + assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, false); + + // set the entity to RUNNING and the app will be healthy again + ServiceNotUpLogic.clearNotUpIndicator(entity, INDICATOR_KEY_1); + ServiceStateLogic.setExpectedState(entity, Lifecycle.RUNNING); + assertAttributeEqualsEventually(app, Attributes.SERVICE_UP, true); + assertAttributeEqualsEventually(app, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + assertAttributeEquals(entity, Attributes.SERVICE_UP, true); + assertAttributeEquals(entity, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + } + + @Test + public void testQuorumWithStringStates() { + final DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class) + .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(TestEntityWithoutEnrichers.class)) + .configure(DynamicCluster.INITIAL_SIZE, 1)); + + cluster.start(ImmutableList.of(app.newSimulatedLocation())); + EntityTestUtils.assertGroupSizeEqualsEventually(cluster, 1); + + //manually set state to healthy as enrichers are disabled + EntityInternal child = (EntityInternal) cluster.getMembers().iterator().next(); + child.setAttribute(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + child.setAttribute(Attributes.SERVICE_UP, Boolean.TRUE); + + EntityTestUtils.assertAttributeEqualsEventually(cluster, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + + //set untyped service state, the quorum check should be able to handle coercion + AttributeSensor<Object> stateSensor = Sensors.newSensor(Object.class, Attributes.SERVICE_STATE_ACTUAL.getName()); + child.setAttribute(stateSensor, "running"); + + EntityTestUtils.assertAttributeEqualsContinually(cluster, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING); + } + + private static <T> void assertAttributeEqualsEventually(Entity x, AttributeSensor<T> sensor, T value) { + try { + EntityTestUtils.assertAttributeEqualsEventually(ImmutableMap.of("timeout", Duration.seconds(3)), x, sensor, value); + } catch (Throwable e) { + log.warn("Expected "+x+" eventually to have "+sensor+" = "+value+"; instead:"); + Entities.dumpInfo(x); + throw Exceptions.propagate(e); + } + } + private static <T> void assertAttributeEqualsContinually(Entity x, AttributeSensor<T> sensor, T value) { + try { + EntityTestUtils.assertAttributeEqualsContinually(ImmutableMap.of("timeout", Duration.millis(25)), x, sensor, value); + } catch (Throwable e) { + log.warn("Expected "+x+" continually to have "+sensor+" = "+value+"; instead:"); + Entities.dumpInfo(x); + throw Exceptions.propagate(e); + } + } + private static <T> void assertAttributeEquals(Entity x, AttributeSensor<T> sensor, T value) { + try { + EntityTestUtils.assertAttributeEquals(x, sensor, value); + } catch (Throwable e) { + log.warn("Expected "+x+" to have "+sensor+" = "+value+"; instead:"); + Entities.dumpInfo(x); + throw Exceptions.propagate(e); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/c27cf1d0/core/src/test/java/org/apache/brooklyn/core/entity/proxying/ApplicationBuilderOverridingTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/entity/proxying/ApplicationBuilderOverridingTest.java b/core/src/test/java/org/apache/brooklyn/core/entity/proxying/ApplicationBuilderOverridingTest.java new file mode 100644 index 0000000..5a8003d --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/entity/proxying/ApplicationBuilderOverridingTest.java @@ -0,0 +1,221 @@ +/* + * 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.entity.proxying; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.brooklyn.api.entity.Application; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.mgmt.ManagementContext; +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.StartableApplication; +import org.apache.brooklyn.core.entity.factory.ApplicationBuilder; +import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; +import org.apache.brooklyn.core.objs.proxy.EntityProxy; +import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.core.test.entity.TestApplication; +import org.apache.brooklyn.core.test.entity.TestEntity; +import org.apache.brooklyn.entity.stock.BasicApplication; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; + +public class ApplicationBuilderOverridingTest { + + private static final long TIMEOUT_MS = 10*1000; + + private ManagementContext spareManagementContext; + private Application app; + private ExecutorService executor; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + spareManagementContext = new LocalManagementContextForTests(); + executor = Executors.newCachedThreadPool(); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() { + if (app != null) Entities.destroyAll(app.getManagementContext()); + app = null; + if (spareManagementContext != null) Entities.destroyAll(spareManagementContext); + spareManagementContext = null; + } + + @Test + public void testUsesDefaultBasicApplicationClass() { + app = new ApplicationBuilder() { + @Override public void doBuild() {} + }.manage(); + + assertEquals(app.getEntityType().getName(), BasicApplication.class.getCanonicalName()); + assertIsProxy(app); + } + + @Test + public void testUsesSuppliedApplicationClass() { + app = new ApplicationBuilder(EntitySpec.create(TestApplication.class)) { + @Override public void doBuild() {} + }.manage(); + + assertEquals(app.getEntityType().getName(), TestApplication.class.getName()); + } + + @Test + public void testUsesSuppliedManagementContext() { + app = new ApplicationBuilder() { + @Override public void doBuild() {} + }.manage(spareManagementContext); + + assertEquals(app.getManagementContext(), spareManagementContext); + } + + @Test + public void testCreatesChildEntity() { + final AtomicReference<TestEntity> expectedChild = new AtomicReference<TestEntity>(); + app = new ApplicationBuilder() { + @Override public void doBuild() { + expectedChild.set(addChild(EntitySpec.create(TestEntity.class))); + } + }.manage(); + + assertIsProxy(expectedChild.get()); + assertEquals(ImmutableSet.copyOf(app.getChildren()), ImmutableSet.of(expectedChild.get())); + assertEquals(expectedChild.get().getParent(), app); + } + + @Test + public void testAppHierarchyIsManaged() { + app = new ApplicationBuilder() { + @Override public void doBuild() { + Entity entity = addChild(EntitySpec.create(TestEntity.class)); + assertFalse(getManagementContext().getEntityManager().isManaged(entity)); + } + }.manage(); + + assertIsManaged(app); + assertIsManaged(Iterables.get(app.getChildren(), 0)); + } + + @Test(expectedExceptions=IllegalStateException.class) + public void testRentrantCallToManageForbidden() { + ManagementContext secondManagementContext = new LocalManagementContext(); + try { + app = new ApplicationBuilder() { + @Override public void doBuild() { + manage(spareManagementContext); + } + }.manage(secondManagementContext); + } finally { + Entities.destroyAll(secondManagementContext); + } + } + + @Test(expectedExceptions=IllegalStateException.class) + public void testMultipleCallsToManageForbidden() { + ApplicationBuilder appBuilder = new ApplicationBuilder() { + @Override public void doBuild() { + } + }; + app = appBuilder.manage(); + + appBuilder.manage(spareManagementContext); + } + + @Test(expectedExceptions=IllegalStateException.class) + public void testCallToConfigureAfterManageForbidden() { + ApplicationBuilder appBuilder = new ApplicationBuilder() { + @Override public void doBuild() { + } + }; + app = appBuilder.manage(); + appBuilder.configure(ImmutableMap.of()); + } + + @Test(expectedExceptions=IllegalStateException.class) + public void testCallToSetDisplayNameAfterManageForbidden() { + ApplicationBuilder appBuilder = new ApplicationBuilder() { + @Override public void doBuild() { + } + }; + app = appBuilder.manage(spareManagementContext); + appBuilder.appDisplayName("myname"); + } + + @Test + public void testConcurrentCallToManageForbidden() throws Exception { + final CountDownLatch inbuildLatch = new CountDownLatch(1); + final CountDownLatch continueLatch = new CountDownLatch(1); + final ApplicationBuilder builder = new ApplicationBuilder() { + @Override public void doBuild() { + try { + inbuildLatch.countDown(); + continueLatch.await(); + } catch (InterruptedException e) { + throw Exceptions.propagate(e); + } + } + }; + Future<StartableApplication> future = executor.submit(new Callable<StartableApplication>() { + public StartableApplication call() { + return builder.manage(); + } + }); + + inbuildLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); + + try { + app = builder.manage(spareManagementContext); + fail(); + } catch (IllegalStateException e) { + // expected + } + + continueLatch.countDown(); + app = future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + } + + private void assertIsProxy(Entity e) { + assertFalse(e instanceof AbstractEntity, "e="+e+";e.class="+e.getClass()); + assertTrue(e instanceof EntityProxy, "e="+e+";e.class="+e.getClass()); + } + + private void assertIsManaged(Entity e) { + assertTrue(((EntityInternal)e).getManagementSupport().isDeployed(), "e="+e); + } +}
