/*
* 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.mgmt.rebind;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import java.util.Map;
import org.apache.brooklyn.api.entity.EntityLocal;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.entity.Group;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoManifest;
import org.apache.brooklyn.api.policy.Policy;
import org.apache.brooklyn.api.policy.PolicySpec;
import org.apache.brooklyn.api.sensor.EnricherSpec;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.enricher.AbstractEnricher;
import org.apache.brooklyn.core.entity.Entities;
import org.apache.brooklyn.core.location.Locations;
import org.apache.brooklyn.core.mgmt.rebind.RebindEnricherTest.MyEnricher;
import org.apache.brooklyn.core.policy.AbstractPolicy;
import org.apache.brooklyn.core.test.entity.TestApplication;
import org.apache.brooklyn.core.test.entity.TestEntity;
import org.apache.brooklyn.entity.group.BasicGroup;
import org.apache.brooklyn.test.Asserts;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.collections.MutableSet;
import org.apache.brooklyn.util.core.flags.SetFromFlag;
import org.testng.annotations.Test;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
public class RebindPolicyTest extends RebindTestFixtureWithApp {
/*
* FIXME Need to decide what to do about policy mementos and restoring.
* Lots of places register anonymous inner class policies.
* (e.g. AbstractController registering a AbstractMembershipTrackingPolicy)
* Also, the entity constructor often re-creates the policy.
*
* See RebindManagerImpl.CheckpointingChangeListener.onChanged(Entity) and
* MementosGenerator.newEntityMementoBuilder()
*/
@Test
public void testRestoresSimplePolicyFromConstructor() throws Exception {
MyPolicy origPolicy = new MyPolicy(MutableMap.of("myfield", "myFieldVal", "myconfigkey", "myConfigVal"));
origApp.policies().add(origPolicy);
runRestoresSimplePolicy();
}
@Test
public void testRestoresDeprecatedPolicyFromConstructorWithoutNoArgs() throws Exception {
MyPolicyWithoutNoArgConstructor origPolicy = new MyPolicyWithoutNoArgConstructor(MutableMap.of("myfield", "myFieldVal", "myconfigkey", "myConfigVal"));
origApp.policies().add(origPolicy);
runRestoresSimplePolicy();
}
@Test
public void testRestoresSimplePolicyFromPolicySpec() throws Exception {
origApp.policies().add(PolicySpec.create(MyPolicy.class)
.configure("myfield", "myFieldVal")
.configure(MyPolicy.MY_CONFIG, "myConfigVal"));
runRestoresSimplePolicy();
}
protected void runRestoresSimplePolicy() throws Exception {
MyPolicy origPolicy = (MyPolicy) Iterables.getOnlyElement(origApp.policies());
assertTrue(origPolicy.isRunning());
assertTrue(origPolicy.initCalled);
assertFalse(origPolicy.rebindCalled);
newApp = rebind();
MyPolicy newPolicy = (MyPolicy) Iterables.getOnlyElement(newApp.policies());
assertEquals(newPolicy.myfield, "myFieldVal");
assertEquals(newPolicy.getConfig(MyPolicy.MY_CONFIG), "myConfigVal");
assertTrue(newPolicy.isRunning());
assertFalse(newPolicy.initCalled);
assertTrue(newPolicy.rebindCalled);
}
@Test
public void testRestoresConfig() throws Exception {
origApp.policies().add(PolicySpec.create(MyPolicy.class)
.displayName("My Policy")
.uniqueTag("tagU")
.tag("tag1").tag("tag2")
.configure(MyPolicy.MY_CONFIG_WITH_SETFROMFLAG_NO_SHORT_NAME, "myVal for with setFromFlag noShortName")
.configure(MyPolicy.MY_CONFIG_WITH_SETFROMFLAG_WITH_SHORT_NAME, "myVal for setFromFlag withShortName")
.configure(MyPolicy.MY_CONFIG_WITHOUT_SETFROMFLAG, "myVal for witout setFromFlag"));
newApp = rebind();
MyPolicy newPolicy = (MyPolicy) Iterables.getOnlyElement(newApp.policies());
assertEquals(newPolicy.getDisplayName(), "My Policy");
assertEquals(newPolicy.getUniqueTag(), "tagU");
assertEquals(newPolicy.tags().getTags(), MutableSet.of("tagU", "tag1", "tag2"));
assertEquals(newPolicy.getConfig(MyPolicy.MY_CONFIG_WITH_SETFROMFLAG_NO_SHORT_NAME), "myVal for with setFromFlag noShortName");
assertEquals(newPolicy.getConfig(MyPolicy.MY_CONFIG_WITH_SETFROMFLAG_WITH_SHORT_NAME), "myVal for setFromFlag withShortName");
assertEquals(newPolicy.getConfig(MyPolicy.MY_CONFIG_WITHOUT_SETFROMFLAG), "myVal for witout setFromFlag");
}
@Test
public void testExpungesOnEntityUnmanaged() throws Exception {
Location loc = origManagementContext.getLocationRegistry().resolve("localhost");
TestEntity entity = origApp.createAndManageChild(EntitySpec.create(TestEntity.class));
MyPolicy policy = entity.policies().add(PolicySpec.create(MyPolicy.class));
MyEnricher enricher = entity.enrichers().add(EnricherSpec.create(MyEnricher.class));
RebindTestUtils.waitForPersisted(origApp);
Entities.unmanage(entity);
Locations.unmanage(loc);
RebindTestUtils.waitForPersisted(origApp);
BrooklynMementoManifest manifest = loadMementoManifest();
assertFalse(manifest.getEntityIdToManifest().containsKey(entity.getId()));
assertFalse(manifest.getPolicyIdToType().containsKey(policy.getId()));
assertFalse(manifest.getEnricherIdToType().containsKey(enricher.getId()));
assertFalse(manifest.getLocationIdToType().containsKey(loc.getId()));
}
@Test
public void testExpungesOnPolicyRemoved() throws Exception {
TestEntity entity = origApp.createAndManageChild(EntitySpec.create(TestEntity.class));
MyPolicy policy = entity.policies().add(PolicySpec.create(MyPolicy.class));
MyEnricher enricher = entity.enrichers().add(EnricherSpec.create(MyEnricher.class));
RebindTestUtils.waitForPersisted(origApp);
entity.policies().remove(policy);
entity.enrichers().remove(enricher);
RebindTestUtils.waitForPersisted(origApp);
BrooklynMementoManifest manifest = loadMementoManifest();
assertFalse(manifest.getPolicyIdToType().containsKey(policy.getId()));
assertFalse(manifest.getEnricherIdToType().containsKey(enricher.getId()));
}
@Test
public void testReboundConfigDoesNotContainId() throws Exception {
MyPolicy policy = origApp.policies().add(PolicySpec.create(MyPolicy.class));
newApp = rebind();
MyPolicy newPolicy = (MyPolicy) Iterables.getOnlyElement(newApp.policies());
assertNull(newPolicy.getConfig(ConfigKeys.newStringConfigKey("id")));
assertEquals(newPolicy.getId(), policy.getId());
}
@Test
public void testReconfigurePolicyPersistsChange() throws Exception {
MyPolicyReconfigurable policy = origApp.policies().add(PolicySpec.create(MyPolicyReconfigurable.class)
.configure(MyPolicyReconfigurable.MY_CONFIG, "oldval"));
policy.config().set(MyPolicyReconfigurable.MY_CONFIG, "newval");
newApp = rebind();
MyPolicyReconfigurable newPolicy = (MyPolicyReconfigurable) Iterables.getOnlyElement(newApp.policies());
assertEquals(newPolicy.getConfig(MyPolicyReconfigurable.MY_CONFIG), "newval");
}
@Test
public void testIsRebinding() throws Exception {
origApp.policies().add(PolicySpec.create(PolicyChecksIsRebinding.class));
newApp = rebind();
PolicyChecksIsRebinding newPolicy = (PolicyChecksIsRebinding) Iterables.getOnlyElement(newApp.policies());
assertTrue(newPolicy.isRebindingValWhenRebinding());
assertFalse(newPolicy.isRebinding());
}
@Test
public void testPolicyTags() throws Exception {
Policy origPolicy = origApp.policies().add(PolicySpec.create(MyPolicy.class));
origPolicy.tags().addTag("foo");
origPolicy.tags().addTag(origApp);
newApp = rebind();
Policy newPolicy = Iterables.getOnlyElement(newApp.policies());
Asserts.assertEqualsIgnoringOrder(newPolicy.tags().getTags(), ImmutableSet.of("foo", newApp));
}
// Previously, policy+enricher was added to entity as part of entity.reconstitute, so other entities might not
// have been initialised and the relationships not set. If a policy immediately looked at entity's children or
// at another entity, then it might find those entities' state uninitialised.
//
// Longer term, we may want to force policies+enrichers to be inactive until the entity really is managed.
// However, currently some policies inject an onEvent message during their `setEntity` method (because
// their subscription does not give them the current value - only changed values. Doing that sudo-onEvent is
// a bad idea because even on normal startup the entity might still be in its init method, so we shouldn't be
// kicking off actions at that point.
@Test
public void testPolicyAddedWhenEntityRelationshipsSet() throws Exception {
BasicGroup origGroup = origApp.createAndManageChild(EntitySpec.create(BasicGroup.class));
TestEntity origEntity = origApp.createAndManageChild(EntitySpec.create(TestEntity.class));
origGroup.addMember(origEntity);
EnricherChecksEntityHierarchy origEnricher = origApp.enrichers().add(EnricherSpec.create(EnricherChecksEntityHierarchy.class));
PolicyChecksEntityHierarchy origPolicy = origApp.policies().add(PolicySpec.create(PolicyChecksEntityHierarchy.class));
assertTrue(origEnricher.success);
assertTrue(origPolicy.success);
newApp = (TestApplication) rebind();
EnricherChecksEntityHierarchy newEnricher = (EnricherChecksEntityHierarchy) Iterables.getOnlyElement(newApp.enrichers());
PolicyChecksEntityHierarchy newPolicy = (PolicyChecksEntityHierarchy) Iterables.getOnlyElement(newApp.policies());
assertTrue(newEnricher.success);
assertTrue(newPolicy.success);
}
public static class PolicyChecksEntityHierarchy extends AbstractPolicy {
transient volatile boolean success;
@Override
public void setEntity(EntityLocal entity) {
super.setEntity(entity);
assertTrue(entity instanceof TestApplication);
assertEquals(entity.getChildren().size(), 2);
assertEquals(((Group)Iterables.find(entity.getChildren(), Predicates.instanceOf(Group.class))).getMembers().size(), 1);
success = true;
}
}
public static class EnricherChecksEntityHierarchy extends AbstractEnricher {
transient volatile boolean success;
@Override
public void setEntity(EntityLocal entity) {
super.setEntity(entity);
assertTrue(entity instanceof TestApplication);
assertEquals(entity.getChildren().size(), 2);
assertEquals(((Group)Iterables.find(entity.getChildren(), Predicates.instanceOf(Group.class))).getMembers().size(), 1);
success = true;
}
}
public static class PolicyChecksIsRebinding extends AbstractPolicy {
boolean isRebindingValWhenRebinding;
public boolean isRebindingValWhenRebinding() {
return isRebindingValWhenRebinding;
}
@Override public boolean isRebinding() {
return super.isRebinding();
}
@Override public void rebind() {
super.rebind();
isRebindingValWhenRebinding = isRebinding();
}
}
public static class MyPolicy extends AbstractPolicy {
public static final ConfigKey<String> MY_CONFIG = ConfigKeys.newStringConfigKey("myconfigkey");
@SetFromFlag
public static final ConfigKey<String> MY_CONFIG_WITH_SETFROMFLAG_NO_SHORT_NAME = ConfigKeys.newStringConfigKey("myconfig.withSetfromflag.noShortName");
@SetFromFlag("myConfigWithSetFromFlagWithShortName")
public static final ConfigKey<String> MY_CONFIG_WITH_SETFROMFLAG_WITH_SHORT_NAME = ConfigKeys.newStringConfigKey("myconfig.withSetfromflag.withShortName");
public static final ConfigKey<String> MY_CONFIG_WITHOUT_SETFROMFLAG = ConfigKeys.newStringConfigKey("myconfig.withoutSetfromflag");
@SetFromFlag
String myfield;
@SuppressWarnings("unused")
private final Object dummy = new Object(); // so not serializable
public volatile boolean initCalled;
public volatile boolean rebindCalled;
public MyPolicy() {
}
public MyPolicy(Map<?,?> flags) {
super(flags);
}
@Override
public void init() {
super.init();
initCalled = true;
}
@Override
public void rebind() {
super.rebind();
rebindCalled = true;
}
}
public static class MyPolicyWithoutNoArgConstructor extends MyPolicy {
public MyPolicyWithoutNoArgConstructor(Map<?,?> flags) {
super(flags);
}
}
public static class MyPolicyReconfigurable extends AbstractPolicy {
public static final ConfigKey<String> MY_CONFIG = ConfigKeys.newStringConfigKey("myconfig");
public MyPolicyReconfigurable() {
super();
}
@Override
protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) {
if (MY_CONFIG.equals(key)) {
// we'd do here whatever reconfig meant; caller will set actual new val
} else {
super.doReconfigureConfig(key, val);
}
}
}
}