/*
* 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 static com.google.common.base.Preconditions.checkNotNull;
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.Set;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.core.entity.EntityAutomanagedTest.RecordingCollectionChangeListener.ChangeEvent;
import org.apache.brooklyn.core.entity.EntityAutomanagedTest.RecordingCollectionChangeListener.ChangeType;
import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
import org.apache.brooklyn.core.test.entity.TestApplication;
import org.apache.brooklyn.core.test.entity.TestEntity;
import org.apache.brooklyn.test.Asserts;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
public class EntityAutomanagedTest extends BrooklynAppUnitTestSupport {
// TODO Want to use a RecordingCollectionChangeListener to ensure on auto-manage
// we are notified of the entity being added etc, but compilation fails on command line,
// running `mvn clean install` (using 1.7.0_65, build 24.65-b04)!
// - when written "properly", it complains about the @Override annotation on onItemAdded(Entity item)
// - when that is removed, it complains about the call to mgmt.addEntitySetListener not being compatible
// - when try stripping out generics, it complains that "cannot find symbol" for the declaration of this class
//
// In your IDE (at least in Eclipse), you can uncomment the bits beside the TODO and run it
// with those insertions!
protected RecordingCollectionChangeListener listener;
@BeforeMethod(alwaysRun=true)
public void setUp() throws Exception {
super.setUp();
listener = new RecordingCollectionChangeListener();
// TODO Compiler problems - see comment at top of class
//mgmt.addEntitySetListener((CollectionChangeListener)listener);
}
//////////////////////////////////////
// Variants of addChild(EntitySpec) //
//////////////////////////////////////
@Test
public void testAddedChildSpec() throws Exception {
TestEntity e = app.addChild(EntitySpec.create(TestEntity.class));
assertTrue(Entities.isManaged(e));
listener.assertEventsEqualsEventually(ImmutableList.of(new ChangeEvent(ChangeType.ADDED, e)));
}
@Test
public void testAddedChildHierarchySpec() throws Exception {
TestEntity e = app.addChild(EntitySpec.create(TestEntity.class)
.child(EntitySpec.create(TestEntity.class)
.child(EntitySpec.create(TestEntity.class))));
TestEntity e2 = (TestEntity) Iterables.getOnlyElement(e.getChildren());
TestEntity e3 = (TestEntity) Iterables.getOnlyElement(e2.getChildren());
assertTrue(Entities.isManaged(e));
assertTrue(Entities.isManaged(e2));
assertTrue(Entities.isManaged(e3));
assertEquals(e.getParent(), app);
assertEquals(e2.getParent(), e);
assertEquals(e3.getParent(), e2);
listener.assertEventsEqualsEventually(ImmutableList.of(
new ChangeEvent(ChangeType.ADDED, e),
new ChangeEvent(ChangeType.ADDED, e2),
new ChangeEvent(ChangeType.ADDED, e3)));
}
@Test
public void testNewEntityWithParent() throws Exception {
TestEntity e = app.addChild(EntitySpec.create(TestEntity.class)
.parent(app));
assertTrue(Entities.isManaged(e));
assertEquals(e.getParent(), app);
listener.assertEventsEqualsEventually(ImmutableList.of(new ChangeEvent(ChangeType.ADDED, e)));
}
@Test
public void testNewEntityHierarchyWithParent() throws Exception {
TestEntity e = mgmt.getEntityManager().createEntity(EntitySpec.create(TestEntity.class)
.child(EntitySpec.create(TestEntity.class)
.child(EntitySpec.create(TestEntity.class)))
.parent(app));
TestEntity e2 = (TestEntity) Iterables.getOnlyElement(e.getChildren());
TestEntity e3 = (TestEntity) Iterables.getOnlyElement(e2.getChildren());
assertTrue(Entities.isManaged(e));
assertTrue(Entities.isManaged(e2));
assertTrue(Entities.isManaged(e3));
assertEquals(e.getParent(), app);
assertEquals(e2.getParent(), e);
assertEquals(e3.getParent(), e2);
listener.assertEventsEqualsEventually(ImmutableList.of(
new ChangeEvent(ChangeType.ADDED, e),
new ChangeEvent(ChangeType.ADDED, e2),
new ChangeEvent(ChangeType.ADDED, e3)));
}
@Test
public void testAddingSameChildAgainIsNoop() throws Exception {
TestEntity e = app.addChild(EntitySpec.create(TestEntity.class)
.parent(app));
app.addChild(e);
assertTrue(Entities.isManaged(e));
assertEquals(e.getParent(), app);
listener.assertEventsEqualsEventually(ImmutableList.of(new ChangeEvent(ChangeType.ADDED, e)));
}
//////////////////////////////////////
// Variants of createEntity for app //
//////////////////////////////////////
@Test
public void testNewApp() throws Exception {
TestApplication app2 = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class));
assertTrue(Entities.isManaged(app2));
assertTrue(mgmt.getApplications().contains(app2), "app="+app2+"; apps="+mgmt.getApplications());
app.addChild(app2);
assertTrue(Entities.isManaged(app2));
listener.assertEventsEqualsEventually(ImmutableList.of(new ChangeEvent(ChangeType.ADDED, app2)));
}
////////////////////////////////////////////////////////////////
// Variants of Entities.startManagement and Entities.manage() //
////////////////////////////////////////////////////////////////
@Test
public void testManageIsNoop() throws Exception {
TestEntity child = mgmt.getEntityManager().createEntity(EntitySpec.create(TestEntity.class)
.parent(app));
Entities.manage(child);
assertTrue(Entities.isManaged(child));
listener.assertEventsEqualsEventually(ImmutableList.of(new ChangeEvent(ChangeType.ADDED, child)));
}
@Test
public void testStartManagementIsNoop() throws Exception {
TestApplication app2 = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class));
assertTrue(Entities.isManaged(app2));
Entities.startManagement(app2, mgmt);
assertTrue(Entities.isManaged(app2));
listener.assertEventsEqualsEventually(ImmutableList.of(new ChangeEvent(ChangeType.ADDED, app2)));
}
@Test
public void testStartManagementOfEntityIsNoop() throws Exception {
Entity app2 = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class));
assertTrue(Entities.isManaged(app2));
Entities.startManagement(app2);
assertTrue(Entities.isManaged(app2));
listener.assertEventsEqualsEventually(ImmutableList.of(new ChangeEvent(ChangeType.ADDED, app2)));
}
@Test
public void testStartManagementFailsIfAppDeleted() throws Exception {
TestApplication app2 = mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class));
Entities.unmanage(app2);
try {
Entities.startManagement(app2, mgmt);
fail("Managed deleted app "+app2+" in "+mgmt);
} catch (IllegalStateException e) {
if (!(e.toString().contains("No concrete entity known"))) throw e;
}
}
@Test
public void testManageFailsIfEntityDeleted() throws Exception {
TestEntity child = mgmt.getEntityManager().createEntity(EntitySpec.create(TestEntity.class)
.parent(app));
Entities.unmanage(child);
try {
Entities.manage(child);
fail("Managed deleted entity "+child+" in "+mgmt);
} catch (IllegalArgumentException e) {
if (!(e.toString().contains("Can't manage"))) throw e;
}
}
///////////////////////////////////////////
// Variants of createEntity for non-apps //
///////////////////////////////////////////
// TODO Controversial? Should it be based on reachability from parent? Can entities be (temporarily) top-level?
// But management model is simpler if it becomes managed immediately.
@Test
public void testNewOrphanedEntityIsManaged() throws Exception {
TestEntity e = mgmt.getEntityManager().createEntity(EntitySpec.create(TestEntity.class));
assertTrue(Entities.isManaged(e));
listener.assertEventsEqualsEventually(ImmutableList.of(new ChangeEvent(ChangeType.ADDED, e)));
// Check that orphaned entity doesn't interfere with getApplications
Asserts.assertEqualsIgnoringOrder(mgmt.getApplications(), ImmutableList.of(app));
}
@Test
public void testOrphanedEntityHierarchyIsManaged() throws Exception {
TestEntity e = mgmt.getEntityManager().createEntity(EntitySpec.create(TestEntity.class)
.child(EntitySpec.create(TestEntity.class)
.child(EntitySpec.create(TestEntity.class))));
TestEntity e2 = (TestEntity) Iterables.getOnlyElement(e.getChildren());
TestEntity e3 = (TestEntity) Iterables.getOnlyElement(e2.getChildren());
assertTrue(Entities.isManaged(e));
assertTrue(Entities.isManaged(e2));
assertTrue(Entities.isManaged(e3));
assertEquals(e.getParent(), null);
assertEquals(e2.getParent(), e);
assertEquals(e3.getParent(), e2);
listener.assertEventsEqualsEventually(ImmutableList.of(
new ChangeEvent(ChangeType.ADDED, e),
new ChangeEvent(ChangeType.ADDED, e2),
new ChangeEvent(ChangeType.ADDED, e3)));
}
@Test
public void testNewOrphanedEntityCanBeAddedToChild() throws Exception {
TestEntity e = mgmt.getEntityManager().createEntity(EntitySpec.create(TestEntity.class));
app.addChild(e);
assertTrue(Entities.isManaged(e));
listener.assertEventsEqualsEventually(ImmutableList.of(new ChangeEvent(ChangeType.ADDED, e)));
}
// TODO Compiler problems - see comment at top of class
public static class RecordingCollectionChangeListener { // FIXME implements CollectionChangeListener<Entity> {
public enum ChangeType {
ADDED, REMOVED;
}
public static class ChangeEvent {
public final ChangeType type;
public final Entity entity;
ChangeEvent(ChangeType type, Entity entity) {
this.type = checkNotNull(type, "type");
this.entity = checkNotNull(entity, "entity");
}
@Override
public int hashCode() {
return Objects.hashCode(type, entity);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ChangeEvent)) return false;
ChangeEvent o = (ChangeEvent) obj;
return type.equals(o.type) && entity.equals(o.entity);
}
@Override
public String toString() {
return Objects.toStringHelper(this).add("type", type).add("entity", entity).toString();
}
}
private final List<ChangeEvent> events = Lists.newCopyOnWriteArrayList();
private final Set<Entity> items = Sets.newConcurrentHashSet();
public void assertEventsEqualsEventually(final Iterable<? extends ChangeEvent> expected) {
// TODO Compiler problems - see comment at top of class
// Asserts.succeedsEventually(new Runnable() {
// public void run() {
// assertEquals(events, ImmutableList.copyOf(expected));
// }});
}
public void assertItemsEqualsEventually(final Iterable<? extends Entity> expected) {
// TODO Compiler problems - see comment at top of class
// Asserts.succeedsEventually(new Runnable() {
// public void run() {
// assertEquals(items, ImmutableSet.copyOf(expected));
// }});
}
// TODO Want to include @Override; compiler problems - see comment at top of class
public void onItemAdded(Entity item) {
items.add(item);
events.add(new ChangeEvent(ChangeType.ADDED, item));
}
// TODO Want to include @Override; compiler problems - see comment at top of class
public void onItemRemoved(Entity item) {
items.remove(item);
events.add(new ChangeEvent(ChangeType.REMOVED, item));
}
}
}