/* * 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; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.config.ConfigInheritance; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.config.ConfigKeys; import org.apache.brooklyn.core.entity.AbstractEntity; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.entity.internal.ConfigMapTest.MyOtherEntity; import org.apache.brooklyn.core.sensor.AttributeSensorAndConfigKey; import org.apache.brooklyn.core.sensor.BasicAttributeSensorAndConfigKey.IntegerAttributeSensorAndConfigKey; import org.apache.brooklyn.core.test.entity.TestApplication; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * There is a bug where: * class XI extends SI implements X * class SI implements S * interface X extends Y * config C is declared on S and overwritten at Y */ public class ConfigEntityInheritanceTest { private TestApplication app; @BeforeMethod(alwaysRun=true) public void setUp() { app = TestApplication.Factory.newManagedInstanceForTests(); Entities.startManagement(app); } @AfterMethod(alwaysRun=true) public void tearDown() throws Exception { if (app != null) Entities.destroyAll(app.getManagementContext()); } protected void checkKeys(Entity entity2, Integer value) { Assert.assertEquals(entity2.getConfig(MyOtherEntity.INT_KEY), value); Assert.assertEquals(entity2.getConfig(MyOtherEntity.SENSOR_AND_CONFIG_KEY), value); } @Test public void testConfigKeysIncludesHasConfigKeys() throws Exception { checkKeys(app.addChild(EntitySpec.create(MyOtherEntity.class)), 1); } @Test public void testConfigKeysIncludesHasConfigKeysInheritsOverwritten() throws Exception { checkKeys(app.addChild(EntitySpec.create(MyOtherEntityOverwriting.class)), 2); } @Test public void testConfigKeysIncludesHasConfigKeysInheritsOverwrittenThenInherited() throws Exception { checkKeys(app.addChild(EntitySpec.create(MyOtherEntityOverwritingThenInheriting.class)), 2); } public static class MyOtherEntityOverwriting extends MyOtherEntity { public static final ConfigKey<Integer> INT_KEY = ConfigKeys.newConfigKeyWithDefault(MyOtherEntity.INT_KEY, 2); public static final IntegerAttributeSensorAndConfigKey SENSOR_AND_CONFIG_KEY = new IntegerAttributeSensorAndConfigKey(MyOtherEntity.SENSOR_AND_CONFIG_KEY, 2); } public static class MyOtherEntityOverwritingThenInheriting extends MyOtherEntityOverwriting { } // -------------------- @Test public void testConfigKeysHere() throws Exception { checkKeys(app.addChild(EntitySpec.create(MyEntityHere.class)), 3); } @Test public void testConfigKeysSub() throws Exception { checkKeys(app.addChild(EntitySpec.create(MySubEntityHere.class)), 4); } @Test public void testConfigKeysSubExtended() throws Exception { checkKeys(app.addChild(EntitySpec.create(MySubEntityHere.class)), 4); } @Test public void testConfigKeysSubInheriting() throws Exception { checkKeys(app.addChild(EntitySpec.create(MySubEntityHereInheriting.class)), 4); } @Test public void testConfigKeysHereSubRight() throws Exception { checkKeys(app.addChild(EntitySpec.create(MySubEntityHereLeft.class)), 4); } @Test public void testConfigKeysSubLeft() throws Exception { checkKeys(app.addChild(EntitySpec.create(MySubEntityHereRight.class)), 4); } @Test public void testConfigKeysExtAndImplIntTwoRight() throws Exception { // this mirrors the bug observed in kafka entities; // the right-side interface normally dominates, but not when it is transitive // (although we shouldn't rely on order in any case; // new routines check whether one config key extends another and if so it takes the extending one) checkKeys(app.addChild(EntitySpec.create(MyEntityHereExtendingAndImplementingInterfaceImplementingTwoRight.class)), 4); } public interface MyInterfaceDeclaring { public static final ConfigKey<Integer> INT_KEY = ConfigKeys.newIntegerConfigKey("intKey", "int key", 3); public static final AttributeSensorAndConfigKey<Integer,Integer> SENSOR_AND_CONFIG_KEY = new IntegerAttributeSensorAndConfigKey("sensorConfigKey", "sensor+config key", 3); } public interface MyInterfaceRedeclaringAndInheriting extends MyInterfaceDeclaring { public static final ConfigKey<Integer> INT_KEY = ConfigKeys.newConfigKeyWithDefault(MyInterfaceDeclaring.INT_KEY, 4); public static final IntegerAttributeSensorAndConfigKey SENSOR_AND_CONFIG_KEY = new IntegerAttributeSensorAndConfigKey(MyInterfaceDeclaring.SENSOR_AND_CONFIG_KEY, 4); } public interface MyInterfaceRedeclaring { public static final ConfigKey<Integer> INT_KEY = ConfigKeys.newConfigKeyWithDefault(MyInterfaceDeclaring.INT_KEY, 4); public static final IntegerAttributeSensorAndConfigKey SENSOR_AND_CONFIG_KEY = new IntegerAttributeSensorAndConfigKey(MyInterfaceDeclaring.SENSOR_AND_CONFIG_KEY, 4); } public interface MyInterfaceRedeclaringThenExtending extends MyInterfaceRedeclaring { } public interface MyInterfaceExtendingLeft extends MyInterfaceRedeclaring, MyInterfaceDeclaring { } public interface MyInterfaceExtendingRight extends MyInterfaceDeclaring, MyInterfaceRedeclaring { } public static class MyEntityHere extends AbstractEntity implements MyInterfaceDeclaring { } public static class MySubEntityHere extends MyEntityHere implements MyInterfaceRedeclaring { } public static class MySubEntityHereInheriting extends MyEntityHere implements MyInterfaceRedeclaringAndInheriting { } public static class MySubEntityHereExtended extends MyEntityHere implements MyInterfaceRedeclaringThenExtending { } public static class MySubEntityHereLeft extends MyEntityHere implements MyInterfaceRedeclaring, MyInterfaceDeclaring { } public static class MySubEntityHereRight extends MyEntityHere implements MyInterfaceDeclaring, MyInterfaceRedeclaring { } public static class MyEntityHereExtendingAndImplementingInterfaceImplementingTwoRight extends MyEntityHere implements MyInterfaceExtendingRight { } // -------------------- @Test public void testConfigKeysInheritance() throws Exception { app.config().set(MyEntityWithPartiallyHeritableConfig.HERITABLE, "heritable"); app.config().set(MyEntityWithPartiallyHeritableConfig.UNINHERITABLE, "uninheritable"); app.config().set(MyEntityWithPartiallyHeritableConfig.ALWAYS_HERITABLE, "always_heritable"); Entity child = app.addChild(EntitySpec.create(MyEntityWithPartiallyHeritableConfig.class)); Assert.assertNotNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.HERITABLE)); Assert.assertNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.UNINHERITABLE), null); Assert.assertNotNull(child.getConfig(MyEntityWithPartiallyHeritableConfig.ALWAYS_HERITABLE)); } public static class MyEntityWithPartiallyHeritableConfig extends AbstractEntity { public static final ConfigKey<String> HERITABLE = ConfigKeys.builder(String.class, "herit.default").build(); public static final ConfigKey<String> UNINHERITABLE = ConfigKeys.builder(String.class, "herit.none").inheritance(ConfigInheritance.NONE).build(); // i find a strange joy in words where the prefix "in-" does not mean not, like inflammable public static final ConfigKey<String> ALWAYS_HERITABLE = ConfigKeys.builder(String.class, "herit.always").inheritance(ConfigInheritance.ALWAYS).build(); } }