/* * 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.sensor.BasicAttributeSensorAndConfigKey.IntegerAttributeSensorAndConfigKey; import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; 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.config().set(MyBaseEntity.SUPER_KEY_1, "s1"); entity.config().set(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.config().set((ConfigKey)key, "thisisnotanint"); } @Test public void testGetConfigOfPredicateTaskReturnsCoercedClosure() throws Exception { MyOtherEntity entity2 = new MyOtherEntity(app); entity2.config().set(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.config().set((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.config().set(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.config().set(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; } } }