http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/TestEntityFailingRebind.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/TestEntityFailingRebind.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/TestEntityFailingRebind.java new file mode 100644 index 0000000..974f5d7 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/TestEntityFailingRebind.java @@ -0,0 +1,55 @@ +/* + * 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.management.ha; + +import org.apache.brooklyn.test.entity.TestApplicationImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TestEntityFailingRebind extends TestApplicationImpl { + + private static final Logger LOG = LoggerFactory.getLogger(TestEntityFailingRebind.class); + + public static class RebindException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public RebindException(String message) { + super(message); + } + } + + private static boolean throwOnRebind = true; + + public static void setThrowOnRebind(boolean state) { + throwOnRebind = state; + } + + public static boolean getThrowOnRebind() { + return throwOnRebind; + } + + @Override + public void rebind() { + if (throwOnRebind) { + LOG.warn("Throwing intentional exception to simulate failure of rebinding " + this); + throw new RebindException("Intentional exception thrown when rebinding " + this); + } + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/ha/WarmStandbyTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/management/ha/WarmStandbyTest.java b/core/src/test/java/org/apache/brooklyn/core/management/ha/WarmStandbyTest.java new file mode 100644 index 0000000..8324c16 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/management/ha/WarmStandbyTest.java @@ -0,0 +1,155 @@ +/* + * 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.management.ha; + +import static org.testng.Assert.assertEquals; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.management.ha.HighAvailabilityMode; +import org.apache.brooklyn.api.management.ha.ManagementNodeState; +import org.apache.brooklyn.api.management.ha.ManagementPlaneSyncRecordPersister; +import org.apache.brooklyn.core.management.ha.HighAvailabilityManagerImpl; +import org.apache.brooklyn.core.management.ha.ManagementPlaneSyncRecordPersisterToObjectStore; +import org.apache.brooklyn.core.management.internal.ManagementContextInternal; +import org.apache.brooklyn.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.test.entity.TestApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.basic.Entities; +import brooklyn.entity.rebind.PersistenceExceptionHandlerImpl; +import brooklyn.entity.rebind.persister.BrooklynMementoPersisterToObjectStore; +import brooklyn.entity.rebind.persister.InMemoryObjectStore; +import brooklyn.entity.rebind.persister.ListeningObjectStore; +import brooklyn.entity.rebind.persister.PersistMode; +import brooklyn.entity.rebind.persister.PersistenceObjectStore; +import brooklyn.util.collections.MutableList; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.time.Duration; + +@Test +public class WarmStandbyTest { + + private static final Logger log = LoggerFactory.getLogger(WarmStandbyTest.class); + + private List<HaMgmtNode> nodes = new MutableList<WarmStandbyTest.HaMgmtNode>(); + Map<String,String> sharedBackingStore = MutableMap.of(); + Map<String,Date> sharedBackingStoreDates = MutableMap.of(); + private ClassLoader classLoader = getClass().getClassLoader(); + + public class HaMgmtNode { + // TODO share with HotStandbyTest and SplitBrainTest and a few others (minor differences but worth it ultimately) + + private ManagementContextInternal mgmt; + private String ownNodeId; + private String nodeName; + private ListeningObjectStore objectStore; + private ManagementPlaneSyncRecordPersister persister; + private HighAvailabilityManagerImpl ha; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + nodeName = "node "+nodes.size(); + mgmt = newLocalManagementContext(); + ownNodeId = mgmt.getManagementNodeId(); + objectStore = new ListeningObjectStore(newPersistenceObjectStore()); + objectStore.injectManagementContext(mgmt); + objectStore.prepareForSharedUse(PersistMode.CLEAN, HighAvailabilityMode.DISABLED); + persister = new ManagementPlaneSyncRecordPersisterToObjectStore(mgmt, objectStore, classLoader); + ((ManagementPlaneSyncRecordPersisterToObjectStore)persister).preferRemoteTimestampInMemento(); + BrooklynMementoPersisterToObjectStore persisterObj = new BrooklynMementoPersisterToObjectStore(objectStore, mgmt.getBrooklynProperties(), classLoader); + mgmt.getRebindManager().setPersister(persisterObj, PersistenceExceptionHandlerImpl.builder().build()); + ha = ((HighAvailabilityManagerImpl)mgmt.getHighAvailabilityManager()) + .setPollPeriod(Duration.PRACTICALLY_FOREVER) + .setHeartbeatTimeout(Duration.THIRTY_SECONDS) + .setPersister(persister); + log.info("Created "+nodeName+" "+ownNodeId); + } + + public void tearDown() throws Exception { + if (ha != null) ha.stop(); + if (mgmt != null) Entities.destroyAll(mgmt); + if (objectStore != null) objectStore.deleteCompletely(); + } + + @Override + public String toString() { + return nodeName+" "+ownNodeId; + } + } + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + nodes.clear(); + sharedBackingStore.clear(); + } + + public HaMgmtNode newNode() throws Exception { + HaMgmtNode node = new HaMgmtNode(); + node.setUp(); + nodes.add(node); + return node; + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + for (HaMgmtNode n: nodes) + n.tearDown(); + } + + protected ManagementContextInternal newLocalManagementContext() { + return new LocalManagementContextForTests(); + } + + protected PersistenceObjectStore newPersistenceObjectStore() { + return new InMemoryObjectStore(sharedBackingStore, sharedBackingStoreDates); + } + + // TODO refactor above -- routines above this line are shared among HotStandbyTest and SplitBrainTest + + @Test + public void testWarmStandby() throws Exception { + HaMgmtNode n1 = newNode(); + n1.ha.start(HighAvailabilityMode.AUTO); + assertEquals(n1.ha.getNodeState(), ManagementNodeState.MASTER); + + TestApplication app = TestApplication.Factory.newManagedInstanceForTests(n1.mgmt); + app.start(MutableList.<Location>of()); + + n1.mgmt.getRebindManager().forcePersistNow(false, null); + + HaMgmtNode n2 = newNode(); + n2.ha.start(HighAvailabilityMode.STANDBY); + assertEquals(n2.ha.getNodeState(), ManagementNodeState.STANDBY); + + assertEquals(n2.mgmt.getApplications().size(), 0); + } + + // TODO support forcible demotion, and check that a master forcibly demoted + // to warm standby clears its apps, policies, and locations + + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/internal/AccessManagerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/management/internal/AccessManagerTest.java b/core/src/test/java/org/apache/brooklyn/core/management/internal/AccessManagerTest.java new file mode 100644 index 0000000..891c3a4 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/AccessManagerTest.java @@ -0,0 +1,146 @@ +/* + * 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.management.internal; + +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 org.apache.brooklyn.api.entity.proxying.EntitySpec; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.core.management.internal.LocalManagementContext; +import org.apache.brooklyn.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.test.entity.TestApplication; +import org.apache.brooklyn.test.entity.TestEntity; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.basic.ApplicationBuilder; +import brooklyn.entity.basic.Entities; + +import org.apache.brooklyn.location.basic.SimulatedLocation; + +import brooklyn.util.exceptions.Exceptions; + +import com.google.common.collect.ImmutableSet; + +public class AccessManagerTest { + + private LocalManagementContext managementContext; + private TestApplication app; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + managementContext = new LocalManagementContextForTests(); + app = ApplicationBuilder.newManagedApp(TestApplication.class, managementContext); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (managementContext != null) Entities.destroyAll(managementContext); + app = null; + } + + @Test + public void testEntityManagementAllowed() throws Exception { + // default is allowed + TestEntity e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + // when forbidden, should give error trying to create+manage new entity + managementContext.getAccessManager().setEntityManagementAllowed(false); + try { + app.createAndManageChild(EntitySpec.create(TestEntity.class)); + fail(); + } catch (Exception e) { + // expect it to be forbidden + if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) { + throw e; + } + } + + // when forbidden, should refuse to create new app + try { + ApplicationBuilder.newManagedApp(TestApplication.class, managementContext); + fail(); + } catch (Exception e) { + // expect it to be forbidden + if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) { + throw e; + } + } + + // but when forbidden, still allowed to create locations + managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)); + + // when re-enabled, can create entities again + managementContext.getAccessManager().setEntityManagementAllowed(true); + TestEntity e3 = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + assertEquals(ImmutableSet.copyOf(managementContext.getEntityManager().getEntities()), ImmutableSet.of(app, e1, e3)); + } + + @Test + public void testLocationManagementAllowed() throws Exception { + // default is allowed + Location loc1 = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)); + + // when forbidden, should give error + managementContext.getAccessManager().setLocationManagementAllowed(false); + try { + managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)); + fail(); + } catch (Exception e) { + // expect it to be forbidden + if (Exceptions.getFirstThrowableOfType(e, IllegalStateException.class) == null) { + throw e; + } + } + + // but when forbidden, still allowed to create entity + ApplicationBuilder.newManagedApp(TestApplication.class, managementContext); + + // when re-enabled, can create entities again + managementContext.getAccessManager().setLocationManagementAllowed(true); + Location loc3 = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)); + + assertEquals(ImmutableSet.copyOf(managementContext.getLocationManager().getLocations()), ImmutableSet.of(loc1, loc3)); + } + + @Test + public void testLocationProvisioningAllowed() throws Exception { + SimulatedLocation loc = managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)); + + // default is allowed + assertTrue(managementContext.getAccessController().canProvisionLocation(loc).isAllowed()); + + // when forbidden, should say so + managementContext.getAccessManager().setLocationProvisioningAllowed(false); + assertFalse(managementContext.getAccessController().canProvisionLocation(loc).isAllowed()); + + // but when forbidden, still allowed to create locations + managementContext.getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)); + + // when re-enabled, can create entities again + managementContext.getAccessManager().setLocationProvisioningAllowed(true); + assertTrue(managementContext.getAccessController().canProvisionLocation(loc).isAllowed()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/internal/EntityExecutionManagerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/management/internal/EntityExecutionManagerTest.java b/core/src/test/java/org/apache/brooklyn/core/management/internal/EntityExecutionManagerTest.java new file mode 100644 index 0000000..7f35bba --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/EntityExecutionManagerTest.java @@ -0,0 +1,478 @@ +/* + * 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.management.internal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.Semaphore; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.proxying.EntitySpec; +import org.apache.brooklyn.api.management.ExecutionManager; +import org.apache.brooklyn.api.management.Task; +import org.apache.brooklyn.core.management.internal.BrooklynGarbageCollector; +import org.apache.brooklyn.core.management.internal.LocalManagementContext; +import org.apache.brooklyn.core.management.internal.ManagementContextInternal; +import org.apache.brooklyn.test.entity.LocalManagementContextForTests; +import org.apache.brooklyn.test.entity.TestApplication; +import org.apache.brooklyn.test.entity.TestEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.config.BrooklynProperties; +import brooklyn.entity.basic.ApplicationBuilder; +import brooklyn.entity.basic.BrooklynTaskTags; +import brooklyn.entity.basic.BrooklynTaskTags.WrappedEntity; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.basic.EntityInternal; +import brooklyn.event.basic.BasicAttributeSensor; +import brooklyn.test.Asserts; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.javalang.JavaClassNames; +import brooklyn.util.repeat.Repeater; +import brooklyn.util.task.BasicExecutionManager; +import brooklyn.util.task.ExecutionListener; +import brooklyn.util.task.TaskBuilder; +import brooklyn.util.task.Tasks; +import brooklyn.util.time.Duration; +import brooklyn.util.time.Time; + +import com.google.common.base.Function; +import com.google.common.base.Objects; +import com.google.common.base.Stopwatch; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.Callables; + +/** Includes many tests for {@link BrooklynGarbageCollector} */ +public class EntityExecutionManagerTest { + + private static final Logger LOG = LoggerFactory.getLogger(EntityExecutionManagerTest.class); + + private static final Duration TIMEOUT_MS = Duration.TEN_SECONDS; + + private ManagementContextInternal mgmt; + private TestApplication app; + private TestEntity e; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (app != null) Entities.destroyAll(app.getManagementContext()); + app = null; + if (mgmt != null) Entities.destroyAll(mgmt); + } + + @Test + public void testOnDoneCallback() throws InterruptedException { + mgmt = LocalManagementContextForTests.newInstance(); + ExecutionManager em = mgmt.getExecutionManager(); + BasicExecutionManager bem = (BasicExecutionManager)em; + final Map<Task<?>,Duration> completedTasks = MutableMap.of(); + final Semaphore sema4 = new Semaphore(-1); + bem.addListener(new ExecutionListener() { + @Override + public void onTaskDone(Task<?> task) { + Assert.assertTrue(task.isDone()); + Assert.assertEquals(task.getUnchecked(), "foo"); + completedTasks.put(task, Duration.sinceUtc(task.getEndTimeUtc())); + sema4.release(); + } + }); + Task<String> t1 = em.submit( Tasks.<String>builder().name("t1").dynamic(false).body(Callables.returning("foo")).build() ); + t1.getUnchecked(); + Task<String> t2 = em.submit( Tasks.<String>builder().name("t2").dynamic(false).body(Callables.returning("foo")).build() ); + sema4.acquire(); + Assert.assertEquals(completedTasks.size(), 2, "completed tasks are: "+completedTasks); + completedTasks.get(t1).isShorterThan(Duration.TEN_SECONDS); + completedTasks.get(t2).isShorterThan(Duration.TEN_SECONDS); + } + + protected void forceGc() { + ((LocalManagementContext)app.getManagementContext()).getGarbageCollector().gcIteration(); + } + + protected static Task<?> runEmptyTaskWithNameAndTags(Entity target, String name, Object ...tags) { + TaskBuilder<Object> tb = newEmptyTask(name); + for (Object tag: tags) tb.tag(tag); + Task<?> task = ((EntityInternal)target).getExecutionContext().submit(tb.build()); + task.getUnchecked(); + return task; + } + + protected static TaskBuilder<Object> newEmptyTask(String name) { + return Tasks.builder().name(name).dynamic(false).body(Callables.returning(null)); + } + + protected void assertTaskCountForEntitySoon(final Entity entity, final int expectedCount) { + // Dead task (and initialization task) should have been GC'd on completion. + // However, the GC'ing happens in a listener, executed in a different thread - the task.get() + // doesn't block for it. Therefore can't always guarantee it will be GC'ed by now. + Repeater.create().backoff(Duration.millis(10), 2, Duration.millis(500)).limitTimeTo(Duration.TEN_SECONDS).until(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + forceGc(); + Collection<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(((EntityInternal)entity).getManagementContext().getExecutionManager(), entity); + Assert.assertEquals(tasks.size(), expectedCount, "Tasks were "+tasks); + return true; + } + }).runRequiringTrue(); + } + + @Test + public void testGetTasksAndGcBoringTags() throws Exception { + app = TestApplication.Factory.newManagedInstanceForTests(); + e = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + final Task<?> task = runEmptyTaskWithNameAndTags(e, "should-be-kept", ManagementContextInternal.NON_TRANSIENT_TASK_TAG); + runEmptyTaskWithNameAndTags(e, "should-be-gcd", ManagementContextInternal.TRANSIENT_TASK_TAG); + + assertTaskCountForEntitySoon(e, 1); + Collection<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(app.getManagementContext().getExecutionManager(), e); + assertEquals(tasks, ImmutableList.of(task), "Mismatched tasks, got: "+tasks); + } + + @Test + public void testGcTaskAtNormalTagLimit() throws Exception { + app = TestApplication.Factory.newManagedInstanceForTests(); + e = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + ((BrooklynProperties)app.getManagementContext().getConfig()).put( + BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2); + + for (int count=0; count<5; count++) + runEmptyTaskWithNameAndTags(e, "task"+count, ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag"); + + assertTaskCountForEntitySoon(e, 2); + } + + @Test + public void testGcTaskAtEntityLimit() throws Exception { + app = TestApplication.Factory.newManagedInstanceForTests(); + e = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + ((BrooklynProperties)app.getManagementContext().getConfig()).put( + BrooklynGarbageCollector.MAX_TASKS_PER_ENTITY, 2); + + for (int count=0; count<5; count++) + runEmptyTaskWithNameAndTags(e, "task-e-"+count, ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag"); + for (int count=0; count<5; count++) + runEmptyTaskWithNameAndTags(app, "task-app-"+count, ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag"); + + assertTaskCountForEntitySoon(app, 2); + assertTaskCountForEntitySoon(e, 2); + } + + @Test + public void testGcTaskWithTagAndEntityLimit() throws Exception { + app = TestApplication.Factory.newManagedInstanceForTests(); + e = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + ((BrooklynProperties)app.getManagementContext().getConfig()).put( + BrooklynGarbageCollector.MAX_TASKS_PER_ENTITY, 6); + ((BrooklynProperties)app.getManagementContext().getConfig()).put( + BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2); + + int count=0; + + runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag"); + runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag"); + Time.sleep(Duration.ONE_MILLISECOND); + // should keep the 2 below, because all the other borings get grace, but delete the ones above + runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag"); + runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag"); + + runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag", "another-tag-e"); + runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "boring-tag", "another-tag-e"); + // should keep both the above + + runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag"); + runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag"); + Time.sleep(Duration.ONE_MILLISECOND); + runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag"); + // should keep the below since they have unique tags, but remove one of the e tasks above + runEmptyTaskWithNameAndTags(e, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag", "and-another-tag"); + runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag-app", "another-tag"); + runEmptyTaskWithNameAndTags(app, "task-"+(count++), ManagementContextInternal.NON_TRANSIENT_TASK_TAG, "another-tag-app", "another-tag"); + + assertTaskCountForEntitySoon(e, 6); + assertTaskCountForEntitySoon(app, 3); + + // now with a lowered limit, we should remove one more e + ((BrooklynProperties)app.getManagementContext().getConfig()).put( + BrooklynGarbageCollector.MAX_TASKS_PER_ENTITY, 5); + assertTaskCountForEntitySoon(e, 5); + } + + @Test + public void testGcDynamicTaskAtNormalTagLimit() throws Exception { + app = TestApplication.Factory.newManagedInstanceForTests(); + e = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + ((BrooklynProperties)app.getManagementContext().getConfig()).put( + BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2); + + for (int count=0; count<5; count++) { + TaskBuilder<Object> tb = Tasks.builder().name("task-"+count).dynamic(true).body(new Runnable() { @Override public void run() {}}) + .tag(ManagementContextInternal.NON_TRANSIENT_TASK_TAG).tag("foo"); + ((EntityInternal)e).getExecutionContext().submit(tb.build()).getUnchecked(); + } + + // might need an eventually here, if the internal job completion and GC is done in the background + // (if there are no test failures for a few months, since Sept 2014, then we can remove this comment) + assertTaskCountForEntitySoon(e, 2); + } + + @Test + public void testUnmanagedEntityCanBeGcedEvenIfPreviouslyTagged() throws Exception { + app = TestApplication.Factory.newManagedInstanceForTests(); + e = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + String eId = e.getId(); + + e.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()).get(); + Set<Task<?>> tasks = BrooklynTaskTags.getTasksInEntityContext(app.getManagementContext().getExecutionManager(), e); + Task<?> task = Iterables.get(tasks, 0); + assertTrue(task.getTags().contains(BrooklynTaskTags.tagForContextEntity(e))); + + Set<Object> tags = app.getManagementContext().getExecutionManager().getTaskTags(); + assertTrue(tags.contains(BrooklynTaskTags.tagForContextEntity(e)), "tags="+tags); + + Entities.destroy(e); + forceGc(); + + Set<Object> tags2 = app.getManagementContext().getExecutionManager().getTaskTags(); + for (Object tag : tags2) { + if (tag instanceof Entity && ((Entity)tag).getId().equals(eId)) { + fail("tags contains unmanaged entity "+tag); + } + if ((tag instanceof WrappedEntity) && ((WrappedEntity)tag).entity.getId().equals(eId) + && ((WrappedEntity)tag).wrappingType.equals(BrooklynTaskTags.CONTEXT_ENTITY)) { + fail("tags contains unmanaged entity (wrapped) "+tag); + } + } + return; + } + + @Test(groups="Integration") + public void testSubscriptionAndEffectorTasksGced() throws Exception { + app = TestApplication.Factory.newManagedInstanceForTests(); + BasicExecutionManager em = (BasicExecutionManager) app.getManagementContext().getExecutionManager(); + // allow background enrichers to complete + Time.sleep(Duration.ONE_SECOND); + forceGc(); + List<Task<?>> t1 = em.getAllTasks(); + + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + entity.setAttribute(TestEntity.NAME, "bob"); + entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()).get(); + Entities.destroy(entity); + Time.sleep(Duration.ONE_SECOND); + forceGc(); + List<Task<?>> t2 = em.getAllTasks(); + + Assert.assertEquals(t1.size(), t2.size(), "lists are different:\n"+t1+"\n"+t2+"\n"); + } + + /** + * Invoke effector many times, where each would claim 10MB because it stores the return value. + * If it didn't gc the tasks promptly, it would consume 10GB ram (so would OOME before that). + */ + @Test(groups="Integration") + public void testEffectorTasksGcedSoNoOome() throws Exception { + + BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty(); + brooklynProperties.put(BrooklynGarbageCollector.GC_PERIOD, Duration.ONE_MILLISECOND); + brooklynProperties.put(BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2); + + app = ApplicationBuilder.newManagedApp(TestApplication.class, LocalManagementContextForTests.newInstance(brooklynProperties)); + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + for (int i = 0; i < 1000; i++) { + if (i%100==0) LOG.info(JavaClassNames.niceClassAndMethod()+": iteration "+i); + try { + LOG.debug("testEffectorTasksGced: iteration="+i); + entity.invoke(TestEntity.IDENTITY_EFFECTOR, ImmutableMap.of("arg", new BigObject(10*1000*1000))).get(); + + Time.sleep(Duration.ONE_MILLISECOND); // Give GC thread a chance to run + forceGc(); + } catch (OutOfMemoryError e) { + LOG.warn(JavaClassNames.niceClassAndMethod()+": OOME at iteration="+i); + throw e; + } + } + } + + @Test(groups="Integration") + public void testUnmanagedEntityGcedOnUnmanageEvenIfEffectorInvoked() throws Exception { + app = TestApplication.Factory.newManagedInstanceForTests(); + + BasicAttributeSensor<Object> byteArrayAttrib = new BasicAttributeSensor<Object>(Object.class, "test.byteArray", ""); + + for (int i = 0; i < 1000; i++) { + if (i<100 && i%10==0 || i%100==0) LOG.info(JavaClassNames.niceClassAndMethod()+": iteration "+i); + try { + LOG.debug(JavaClassNames.niceClassAndMethod()+": iteration="+i); + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + entity.setAttribute(byteArrayAttrib, new BigObject(10*1000*1000)); + entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()).get(); + + // we get exceptions because tasks are still trying to publish after deployment; + // this should prevent them +// ((LocalEntityManager)app.getManagementContext().getEntityManager()).stopTasks(entity, Duration.ONE_SECOND); +// Entities.destroy(entity); + + // alternatively if we 'unmanage' instead of destroy, there are usually not errors + // (the errors come from the node transitioning to a 'stopping' state on destroy, + // and publishing lots of info then) + Entities.unmanage(entity); + + forceGc(); + // previously we did an extra GC but it was crazy slow, shouldn't be needed +// System.gc(); System.gc(); + } catch (OutOfMemoryError e) { + LOG.warn(JavaClassNames.niceClassAndMethod()+": OOME at iteration="+i); + ExecutionManager em = app.getManagementContext().getExecutionManager(); + Collection<Task<?>> tasks = ((BasicExecutionManager)em).getAllTasks(); + LOG.info("TASKS count "+tasks.size()+": "+tasks); + throw e; + } + } + } + + @Test(groups={"Integration"}) + public void testEffectorTasksGcedForMaxPerTag() throws Exception { + int maxNumTasks = 2; + BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty(); + brooklynProperties.put(BrooklynGarbageCollector.GC_PERIOD, Duration.ONE_SECOND); + brooklynProperties.put(BrooklynGarbageCollector.MAX_TASKS_PER_TAG, 2); + + app = ApplicationBuilder.newManagedApp(TestApplication.class, LocalManagementContextForTests.newInstance(brooklynProperties)); + final TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + List<Task<?>> tasks = Lists.newArrayList(); + + for (int i = 0; i < (maxNumTasks+1); i++) { + Task<?> task = entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()); + task.get(); + tasks.add(task); + + // TASKS_OLDEST_FIRST_COMPARATOR is based on comparing EndTimeUtc; but two tasks executed in + // rapid succession could finish in same millisecond + // (especially when using System.currentTimeMillis, which can return the same time for several millisconds). + Thread.sleep(10); + } + + // Should initially have all tasks + Set<Task<?>> storedTasks = app.getManagementContext().getExecutionManager().getTasksWithAllTags( + ImmutableList.of(BrooklynTaskTags.tagForContextEntity(entity), ManagementContextInternal.EFFECTOR_TAG)); + assertEquals(storedTasks, ImmutableSet.copyOf(tasks), "storedTasks="+storedTasks+"; expected="+tasks); + + // Then oldest should be GC'ed to leave only maxNumTasks + final List<Task<?>> recentTasks = tasks.subList(tasks.size()-maxNumTasks, tasks.size()); + Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() { + @Override public void run() { + Set<Task<?>> storedTasks2 = app.getManagementContext().getExecutionManager().getTasksWithAllTags( + ImmutableList.of(BrooklynTaskTags.tagForContextEntity(entity), ManagementContextInternal.EFFECTOR_TAG)); + List<String> storedTasks2Str = FluentIterable + .from(storedTasks2) + .transform(new Function<Task<?>, String>() { + @Override public String apply(Task<?> input) { + return taskToVerboseString(input); + }}) + .toList(); + assertEquals(storedTasks2, ImmutableSet.copyOf(recentTasks), "storedTasks="+storedTasks2Str+"; expected="+recentTasks); + }}); + } + + private String taskToVerboseString(Task t) { + return Objects.toStringHelper(t) + .add("id", t.getId()) + .add("displayName", t.getDisplayName()) + .add("submitTime", t.getSubmitTimeUtc()) + .add("startTime", t.getStartTimeUtc()) + .add("endTime", t.getEndTimeUtc()) + .add("status", t.getStatusSummary()) + .add("tags", t.getTags()) + .toString(); + } + + @Test(groups="Integration") + public void testEffectorTasksGcedForAge() throws Exception { + Duration maxTaskAge = Duration.millis(100); + Duration maxOverhead = Duration.millis(250); + Duration earlyReturnGrace = Duration.millis(10); + BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty(); + brooklynProperties.put(BrooklynGarbageCollector.GC_PERIOD, Duration.ONE_MILLISECOND); + brooklynProperties.put(BrooklynGarbageCollector.MAX_TASK_AGE, maxTaskAge); + + app = ApplicationBuilder.newManagedApp(TestApplication.class, LocalManagementContextForTests.newInstance(brooklynProperties)); + final TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + Stopwatch stopwatch = Stopwatch.createStarted(); + Task<?> oldTask = entity.invoke(TestEntity.MY_EFFECTOR, ImmutableMap.<String,Object>of()); + oldTask.get(); + + Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT_MS), new Runnable() { + @Override public void run() { + Set<Task<?>> storedTasks = app.getManagementContext().getExecutionManager().getTasksWithAllTags(ImmutableList.of( + BrooklynTaskTags.tagForTargetEntity(entity), + ManagementContextInternal.EFFECTOR_TAG)); + assertEquals(storedTasks, ImmutableSet.of(), "storedTasks="+storedTasks); + }}); + + Duration timeToGc = Duration.of(stopwatch); + assertTrue(timeToGc.isLongerThan(maxTaskAge.subtract(earlyReturnGrace)), "timeToGc="+timeToGc+"; maxTaskAge="+maxTaskAge); + assertTrue(timeToGc.isShorterThan(maxTaskAge.add(maxOverhead)), "timeToGc="+timeToGc+"; maxTaskAge="+maxTaskAge); + } + + private static class BigObject implements Serializable { + private static final long serialVersionUID = -4021304829674972215L; + private final int sizeBytes; + private final byte[] data; + + BigObject(int sizeBytes) { + this.sizeBytes = sizeBytes; + this.data = new byte[sizeBytes]; + } + + @Override + public String toString() { + return "BigObject["+sizeBytes+"/"+data.length+"]"; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextInstancesTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextInstancesTest.java b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextInstancesTest.java new file mode 100644 index 0000000..973b795 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextInstancesTest.java @@ -0,0 +1,87 @@ +/* + * 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.management.internal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import org.apache.brooklyn.core.management.internal.LocalManagementContext; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSet; + +/** + * Tests the {@link LocalManagementContext#terminateAll()} and {@link LocalManagementContext#getInstances()} behaviour. + * Note this test must NEVER be run in parallel with other tests, as it will terminate the ManagementContext of those + * other tests. + * + * @author pveentjer + */ +public class LocalManagementContextInstancesTest { + + @AfterMethod(alwaysRun = true) + public void tearDown(){ + LocalManagementContext.terminateAll(); + } + + /** WIP group because other threads may be running in background, + * creating management contexts at the same time as us (slim chance, but observed once); + * they shouldn't be, but cleaning that up is another matter! */ + @Test(groups="WIP") + public void testGetInstances(){ + LocalManagementContext.terminateAll(); + LocalManagementContext context1 = new LocalManagementContext(); + LocalManagementContext context2 = new LocalManagementContext(); + LocalManagementContext context3 = new LocalManagementContext(); + + assertEquals(LocalManagementContext.getInstances(), ImmutableSet.of(context1, context2, context3)); + } + + /** WIP group because other threads may be running in background; + * they shouldn't be, but cleaning that up is another matter! */ + @Test + public void terminateAll(){ + LocalManagementContext.terminateAll(); + + LocalManagementContext context1 = new LocalManagementContext(); + LocalManagementContext context2 = new LocalManagementContext(); + + LocalManagementContext.terminateAll(); + + assertTrue(LocalManagementContext.getInstances().isEmpty()); + assertFalse(context1.isRunning()); + assertFalse(context2.isRunning()); + } + + @Test + public void terminateExplicitContext(){ + LocalManagementContext context1 = new LocalManagementContext(); + LocalManagementContext context2 = new LocalManagementContext(); + LocalManagementContext context3 = new LocalManagementContext(); + + context2.terminate(); + + Assert.assertFalse(LocalManagementContext.getInstances().contains(context2)); + Assert.assertTrue(LocalManagementContext.getInstances().contains(context1)); + Assert.assertTrue(LocalManagementContext.getInstances().contains(context3)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextTest.java b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextTest.java new file mode 100644 index 0000000..d88765c --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalManagementContextTest.java @@ -0,0 +1,127 @@ +/* + * 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.management.internal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotEquals; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.management.ManagementContext.PropertiesReloadListener; +import org.apache.brooklyn.core.management.internal.LocalManagementContext; +import org.apache.brooklyn.test.entity.LocalManagementContextForTests; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.config.BrooklynProperties; +import brooklyn.config.BrooklynProperties.Factory.Builder; +import brooklyn.util.os.Os; + +import com.google.common.base.Charsets; +import com.google.common.io.Files; + +public class LocalManagementContextTest { + + private LocalManagementContext context; + private File globalPropertiesFile; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + context = null; + globalPropertiesFile = Os.newTempFile(getClass(), "global.properties"); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (context!=null) context.terminate(); + if (globalPropertiesFile != null) globalPropertiesFile.delete(); + } + + @Test + public void testReloadPropertiesFromBuilder() throws IOException { + String globalPropertiesContents = "brooklyn.location.localhost.displayName=myname"; + Files.write(globalPropertiesContents, globalPropertiesFile, Charsets.UTF_8); + Builder propsBuilder = new BrooklynProperties.Factory.Builder() + .globalPropertiesFile(globalPropertiesFile.getAbsolutePath()); + // no builder support in LocalManagementContextForTests (we are testing that the builder's files are reloaded so we need it here) + context = new LocalManagementContext(propsBuilder); + Location location = context.getLocationRegistry().resolve("localhost"); + assertEquals(location.getDisplayName(), "myname"); + String newGlobalPropertiesContents = "brooklyn.location.localhost.displayName=myname2"; + Files.write(newGlobalPropertiesContents, globalPropertiesFile, Charsets.UTF_8); + context.reloadBrooklynProperties(); + Location location2 = context.getLocationRegistry().resolve("localhost"); + assertEquals(location.getDisplayName(), "myname"); + assertEquals(location2.getDisplayName(), "myname2"); + } + + @Test + public void testReloadPropertiesFromProperties() throws IOException { + String globalPropertiesContents = "brooklyn.location.localhost.displayName=myname"; + Files.write(globalPropertiesContents, globalPropertiesFile, Charsets.UTF_8); + BrooklynProperties brooklynProperties = new BrooklynProperties.Factory.Builder() + .globalPropertiesFile(globalPropertiesFile.getAbsolutePath()) + .build(); + context = LocalManagementContextForTests.builder(true).useProperties(brooklynProperties).build(); + Location location = context.getLocationRegistry().resolve("localhost"); + assertEquals(location.getDisplayName(), "myname"); + String newGlobalPropertiesContents = "brooklyn.location.localhost.displayName=myname2"; + Files.write(newGlobalPropertiesContents, globalPropertiesFile, Charsets.UTF_8); + context.reloadBrooklynProperties(); + Location location2 = context.getLocationRegistry().resolve("localhost"); + assertEquals(location.getDisplayName(), "myname"); + assertEquals(location2.getDisplayName(), "myname"); + } + + @Test + public void testPropertiesModified() throws IOException { + BrooklynProperties properties = BrooklynProperties.Factory.newEmpty(); + properties.put("myname", "myval"); + context = LocalManagementContextForTests.builder(true).useProperties(properties).build(); + assertEquals(context.getBrooklynProperties().get("myname"), "myval"); + properties.put("myname", "newval"); + assertEquals(properties.get("myname"), "newval"); + // TODO: Should changes in the 'properties' collection be reflected in context.getBrooklynProperties()? + assertNotEquals(context.getBrooklynProperties().get("myname"), "newval"); + } + + @Test + public void testAddAndRemoveReloadListener() { + final AtomicInteger reloadedCallbackCount = new AtomicInteger(0); + BrooklynProperties properties = BrooklynProperties.Factory.newEmpty(); + properties.put("myname", "myval"); + context = LocalManagementContextForTests.builder(true).useProperties(properties).build(); + PropertiesReloadListener listener = new PropertiesReloadListener() { + public void reloaded() { + reloadedCallbackCount.incrementAndGet(); + } + }; + assertEquals(reloadedCallbackCount.get(), 0); + context.addPropertiesReloadListener(listener); + context.reloadBrooklynProperties(); + assertEquals(reloadedCallbackCount.get(), 1); + context.removePropertiesReloadListener(listener); + context.reloadBrooklynProperties(); + assertEquals(reloadedCallbackCount.get(), 1); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManagerTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManagerTest.java b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManagerTest.java new file mode 100644 index 0000000..635e562 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManagerTest.java @@ -0,0 +1,181 @@ +/* + * 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.management.internal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.proxying.EntitySpec; +import org.apache.brooklyn.api.event.SensorEvent; +import org.apache.brooklyn.api.event.SensorEventListener; +import org.apache.brooklyn.api.management.SubscriptionHandle; +import org.apache.brooklyn.api.management.SubscriptionManager; +import org.apache.brooklyn.test.entity.TestEntity; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.BrooklynAppUnitTestSupport; +import brooklyn.entity.basic.BasicGroup; +import brooklyn.entity.basic.Entities; + +/** + * testing the {@link SubscriptionManager} and associated classes. + */ +public class LocalSubscriptionManagerTest extends BrooklynAppUnitTestSupport { + + private static final int TIMEOUT_MS = 5000; + + private TestEntity entity; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + } + + private void manage(Entity ...entities) { + for (Entity e: entities) + Entities.manage(e); + } + + @Test + public void testSubscribeToEntityAttributeChange() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + app.subscribe(entity, TestEntity.SEQUENCE, new SensorEventListener<Object>() { + @Override public void onEvent(SensorEvent<Object> event) { + latch.countDown(); + }}); + entity.setSequenceValue(1234); + if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + fail("Timeout waiting for Event on TestEntity listener"); + } + } + + @Test + public void testSubscribeToEntityWithAttributeWildcard() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + app.subscribe(entity, null, new SensorEventListener<Object>() { + @Override public void onEvent(SensorEvent<Object> event) { + latch.countDown(); + }}); + entity.setSequenceValue(1234); + if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + fail("Timeout waiting for Event on TestEntity listener"); + } + } + + @Test + public void testSubscribeToAttributeChangeWithEntityWildcard() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + app.subscribe(null, TestEntity.SEQUENCE, new SensorEventListener<Object>() { + @Override public void onEvent(SensorEvent<Object> event) { + latch.countDown(); + }}); + entity.setSequenceValue(1234); + if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + fail("Timeout waiting for Event on TestEntity listener"); + } + } + + @Test + public void testSubscribeToChildAttributeChange() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + app.subscribeToChildren(app, TestEntity.SEQUENCE, new SensorEventListener<Object>() { + @Override public void onEvent(SensorEvent<Object> event) { + latch.countDown(); + }}); + entity.setSequenceValue(1234); + if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + fail("Timeout waiting for Event on child TestEntity listener"); + } + } + + @Test + public void testSubscribeToMemberAttributeChange() throws Exception { + BasicGroup group = app.createAndManageChild(EntitySpec.create(BasicGroup.class)); + TestEntity member = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + manage(group, member); + + group.addMember(member); + + final List<SensorEvent<Integer>> events = new CopyOnWriteArrayList<SensorEvent<Integer>>(); + final CountDownLatch latch = new CountDownLatch(1); + app.subscribeToMembers(group, TestEntity.SEQUENCE, new SensorEventListener<Integer>() { + @Override public void onEvent(SensorEvent<Integer> event) { + events.add(event); + latch.countDown(); + }}); + member.setAttribute(TestEntity.SEQUENCE, 123); + + if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + fail("Timeout waiting for Event on parent TestEntity listener"); + } + assertEquals(events.size(), 1); + assertEquals(events.get(0).getValue(), (Integer)123); + assertEquals(events.get(0).getSensor(), TestEntity.SEQUENCE); + assertEquals(events.get(0).getSource().getId(), member.getId()); + } + + // Regression test for ConcurrentModificationException in issue #327 + @Test(groups="Integration") + public void testConcurrentSubscribingAndPublishing() throws Exception { + final AtomicReference<Exception> threadException = new AtomicReference<Exception>(); + TestEntity entity = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + + // Repeatedly subscribe and unsubscribe, so listener-set constantly changing while publishing to it. + // First create a stable listener so it is always the same listener-set object. + Thread thread = new Thread() { + public void run() { + try { + SensorEventListener<Object> noopListener = new SensorEventListener<Object>() { + @Override public void onEvent(SensorEvent<Object> event) { + } + }; + app.subscribe(null, TestEntity.SEQUENCE, noopListener); + while (!Thread.currentThread().isInterrupted()) { + SubscriptionHandle handle = app.subscribe(null, TestEntity.SEQUENCE, noopListener); + app.unsubscribe(null, handle); + } + } catch (Exception e) { + threadException.set(e); + } + } + }; + + try { + thread.start(); + for (int i = 0; i < 10000; i++) { + entity.setAttribute(TestEntity.SEQUENCE, i); + } + } finally { + thread.interrupt(); + } + + if (threadException.get() != null) throw threadException.get(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiPathTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiPathTest.java b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiPathTest.java new file mode 100644 index 0000000..8dcbec2 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiPathTest.java @@ -0,0 +1,105 @@ +/* + * 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.management.osgi; + +import java.io.File; +import java.io.IOException; + +import org.apache.brooklyn.core.management.internal.LocalManagementContext; +import org.apache.brooklyn.test.entity.LocalManagementContextForTests; +import org.osgi.framework.BundleException; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +import brooklyn.config.BrooklynProperties; +import brooklyn.config.BrooklynServerConfig; +import brooklyn.config.BrooklynServerPaths; +import brooklyn.entity.basic.Entities; +import brooklyn.util.os.Os; +import brooklyn.util.text.Identifiers; + + +/** + * Tests that OSGi entities load correctly and have the right catalog information set. + * Note further tests done elsewhere using CAMP YAML (referring to methods in this class). + */ +public class OsgiPathTest { + + protected LocalManagementContext mgmt = null; + + @AfterMethod(alwaysRun=true) + public void tearDown() throws BundleException, IOException, InterruptedException { + if (mgmt!=null) Entities.destroyAll(mgmt); + } + + @Test(groups="Integration") // integration only because OSGi takes ~200ms + public void testOsgiPathDefault() { + mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build(); + String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath(); + Assert.assertTrue(path.startsWith(BrooklynServerPaths.getMgmtBaseDir(mgmt)), path); + Assert.assertTrue(path.contains(mgmt.getManagementNodeId()), path); + + assertExistsThenIsCleaned(path); + } + + @Test(groups="Integration") // integration only because OSGi takes ~200ms + public void testOsgiPathCustom() { + BrooklynProperties bp = BrooklynProperties.Factory.newEmpty(); + String randomSeg = "osgi-test-"+Identifiers.makeRandomId(4); + bp.put(BrooklynServerConfig.OSGI_CACHE_DIR, "${brooklyn.os.tmpdir}"+"/"+randomSeg+"/"+"${brooklyn.mgmt.node.id}"); + mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).useProperties(bp).build(); + String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath(); + Os.deleteOnExitRecursivelyAndEmptyParentsUpTo(new File(path), new File(Os.tmp()+"/"+randomSeg)); + + Assert.assertTrue(path.startsWith(Os.tmp()), path); + Assert.assertTrue(path.contains(mgmt.getManagementNodeId()), path); + + assertExistsThenIsCleaned(path); + } + + @Test(groups="Integration") // integration only because OSGi takes ~200ms + public void testOsgiPathCustomWithoutNodeIdNotCleaned() { + BrooklynProperties bp = BrooklynProperties.Factory.newEmpty(); + String randomSeg = "osgi-test-"+Identifiers.makeRandomId(4); + bp.put(BrooklynServerConfig.OSGI_CACHE_DIR, "${brooklyn.os.tmpdir}"+"/"+randomSeg+"/"+"sample"); + mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).useProperties(bp).build(); + String path = BrooklynServerPaths.getOsgiCacheDir(mgmt).getAbsolutePath(); + Os.deleteOnExitRecursivelyAndEmptyParentsUpTo(new File(path), new File(Os.tmp()+"/"+randomSeg)); + + Assert.assertTrue(path.startsWith(Os.tmp()), path); + Assert.assertFalse(path.contains(mgmt.getManagementNodeId()), path); + + assertExistsThenCorrectCleanedBehaviour(path, false); + } + + private void assertExistsThenIsCleaned(String path) { + assertExistsThenCorrectCleanedBehaviour(path, true); + } + private void assertExistsThenCorrectCleanedBehaviour(String path, boolean shouldBeCleanAfterwards) { + Assert.assertTrue(new File(path).exists(), "OSGi cache "+path+" should exist when in use"); + Entities.destroyAll(mgmt); + mgmt = null; + if (shouldBeCleanAfterwards) + Assert.assertFalse(new File(path).exists(), "OSGi cache "+path+" should be cleaned after"); + else + Assert.assertTrue(new File(path).exists(), "OSGi cache "+path+" should NOT be cleaned after"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiStandaloneTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiStandaloneTest.java b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiStandaloneTest.java new file mode 100644 index 0000000..7f1cc48 --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiStandaloneTest.java @@ -0,0 +1,259 @@ +/* + * 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.management.osgi; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarInputStream; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.test.TestResourceUnavailableException; + +import brooklyn.util.exceptions.Exceptions; + +import org.apache.commons.io.FileUtils; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleException; +import org.osgi.framework.FrameworkEvent; +import org.osgi.framework.launch.Framework; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.util.ResourceUtils; +import brooklyn.util.collections.MutableSet; +import brooklyn.util.maven.MavenArtifact; +import brooklyn.util.maven.MavenRetriever; +import brooklyn.util.net.Urls; +import brooklyn.util.os.Os; +import brooklyn.util.osgi.Osgis; +import brooklyn.util.osgi.Osgis.ManifestHelper; +import brooklyn.util.stream.Streams; + +/** + * Tests some assumptions about OSGi behaviour, in standalone mode (not part of brooklyn). + * See {@link OsgiTestResources} for description of test resources. + */ +public class OsgiStandaloneTest { + + private static final Logger log = LoggerFactory.getLogger(OsgiStandaloneTest.class); + + public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = OsgiTestResources.BROOKLYN_OSGI_TEST_A_0_1_0_PATH; + public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = "classpath:"+BROOKLYN_OSGI_TEST_A_0_1_0_PATH; + + public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH; + public static final String BROOKLYN_TEST_OSGI_ENTITIES_URL = "classpath:"+BROOKLYN_TEST_OSGI_ENTITIES_PATH; + public static final String BROOKLYN_TEST_OSGI_ENTITIES_NAME = "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities"; + public static final String BROOKLYN_TEST_OSGI_ENTITIES_VERSION = "0.1.0"; + + protected Framework framework = null; + private File storageTempDir; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + storageTempDir = Os.newTempDir("osgi-standalone"); + framework = Osgis.newFrameworkStarted(storageTempDir.getAbsolutePath(), true, null); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws BundleException, IOException, InterruptedException { + tearDownOsgiFramework(framework, storageTempDir); + } + + public static void tearDownOsgiFramework(Framework framework, File storageTempDir) throws BundleException, InterruptedException, IOException { + if (framework!=null) { + framework.stop(); + Assert.assertEquals(framework.waitForStop(1000).getType(), FrameworkEvent.STOPPED); + framework = null; + } + if (storageTempDir!=null) { + FileUtils.deleteDirectory(storageTempDir); + storageTempDir = null; + } + } + + protected Bundle install(String url) throws BundleException { + try { + return Osgis.install(framework, url); + } catch (Exception e) { + throw new IllegalStateException("test resources not available; may be an IDE issue, so try a mvn rebuild of this project", e); + } + } + + protected Bundle installFromClasspath(String resourceName) throws BundleException { + TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), resourceName); + try { + return Osgis.install(framework, String.format("classpath:%s", resourceName)); + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } + + @Test + public void testInstallBundle() throws Exception { + Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH); + checkMath(bundle, 3, 6); + } + + @Test + public void testBootBundle() throws Exception { + Bundle bundle = installFromClasspath(BROOKLYN_TEST_OSGI_ENTITIES_PATH); + Class<?> bundleCls = bundle.loadClass("brooklyn.osgi.tests.SimpleEntity"); + Assert.assertEquals(Entity.class, bundle.loadClass(Entity.class.getName())); + Assert.assertEquals(Entity.class, bundleCls.getClassLoader().loadClass(Entity.class.getName())); + } + + @Test + public void testDuplicateBundle() throws Exception { + MavenArtifact artifact = new MavenArtifact("org.apache.brooklyn", "brooklyn-api", "jar", "0.8.0-SNAPSHOT"); // BROOKLYN_VERSION + String localUrl = MavenRetriever.localUrl(artifact); + if ("file".equals(Urls.getProtocol(localUrl))) { + helperDuplicateBundle(localUrl); + } else { + log.warn("Skipping test OsgiStandaloneTest.testDuplicateBundle due to " + artifact + " not available in local repo."); + } + } + + @Test(groups="Integration") + public void testRemoteDuplicateBundle() throws Exception { + helperDuplicateBundle(MavenRetriever.hostedUrl(new MavenArtifact("org.apache.brooklyn", "brooklyn-api", "jar", "0.8.0-SNAPSHOT"))); // BROOKLYN_VERSION + } + + public void helperDuplicateBundle(String url) throws Exception { + //The bundle is already installed from the boot path. + //Make sure that we still get the initially loaded + //bundle after trying to install the same version. + Bundle bundle = install(url); + Assert.assertTrue(Osgis.isExtensionBundle(bundle)); + } + + @Test + public void testAMultiplier() throws Exception { + Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH); + checkMath(bundle, 3, 6); + setAMultiplier(bundle, 5); + checkMath(bundle, 3, 15); + } + + /** run two multiplier tests to ensure that irrespective of order the tests run in, + * on a fresh install the multiplier is reset */ + @Test + public void testANOtherMultiple() throws Exception { + Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH); + checkMath(bundle, 3, 6); + setAMultiplier(bundle, 14); + checkMath(bundle, 3, 42); + } + + @Test + public void testGetBundle() throws Exception { + Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH); + setAMultiplier(bundle, 3); + + // can look it up based on the same location string (no other "location identifier" reference string seems to work here, however) + Bundle bundle2 = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH); + checkMath(bundle2, 3, 9); + } + + @Test + public void testUninstallAndReinstallBundle() throws Exception { + Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH); + checkMath(bundle, 3, 6); + setAMultiplier(bundle, 3); + checkMath(bundle, 3, 9); + bundle.uninstall(); + + Bundle bundle2 = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH); + checkMath(bundle2, 3, 6); + } + + protected void checkMath(Bundle bundle, int input, int output) throws Exception { + Assert.assertNotNull(bundle); + Class<?> aClass = bundle.loadClass("brooklyn.test.osgi.TestA"); + Object aInst = aClass.newInstance(); + Object result = aClass.getMethod("times", int.class).invoke(aInst, input); + Assert.assertEquals(result, output); + } + + protected void setAMultiplier(Bundle bundle, int newMultiplier) throws Exception { + Assert.assertNotNull(bundle); + Class<?> aClass = bundle.loadClass("brooklyn.test.osgi.TestA"); + aClass.getField("multiplier").set(null, newMultiplier); + } + + @Test + public void testReadAManifest() throws Exception { + Enumeration<URL> manifests = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF"); + log.info("Bundles and exported packages:"); + MutableSet<String> allPackages = MutableSet.of(); + while (manifests.hasMoreElements()) { + ManifestHelper mf = Osgis.ManifestHelper.forManifestContents(Streams.readFullyString( manifests.nextElement().openStream() )); + List<String> mfPackages = mf.getExportedPackages(); + log.info(" "+mf.getSymbolicNameVersion()+": "+mfPackages); + allPackages.addAll(mfPackages); + } + log.info("Total export package count: "+allPackages.size()); + Assert.assertTrue(allPackages.size()>20, "did not find enough packages"); // probably much larger + Assert.assertTrue(allPackages.contains(Osgis.class.getPackage().getName())); + } + + @Test + public void testReadKnownManifest() throws Exception { + TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_ENTITIES_PATH); + InputStream in = this.getClass().getResourceAsStream(BROOKLYN_TEST_OSGI_ENTITIES_PATH); + JarInputStream jarIn = new JarInputStream(in); + ManifestHelper helper = Osgis.ManifestHelper.forManifest(jarIn.getManifest()); + jarIn.close(); + Assert.assertEquals(helper.getVersion().toString(), "0.1.0"); + Assert.assertTrue(helper.getExportedPackages().contains("brooklyn.osgi.tests")); + } + + @Test + public void testLoadOsgiBundleDependencies() throws Exception { + Bundle bundle = installFromClasspath(BROOKLYN_TEST_OSGI_ENTITIES_PATH); + Assert.assertNotNull(bundle); + Class<?> aClass = bundle.loadClass("brooklyn.osgi.tests.SimpleApplicationImpl"); + Object aInst = aClass.newInstance(); + Assert.assertNotNull(aInst); + } + + @Test + public void testLoadAbsoluteWindowsResourceWithInstalledOSGi() { + //Felix installs an additional URL to the system classloader + //which throws an IllegalArgumentException when passed a + //windows path. See ExtensionManager.java static initializer. + String context = "mycontext"; + String dummyPath = "C:\\dummypath"; + ResourceUtils utils = ResourceUtils.create(this, context); + try { + utils.getResourceFromUrl(dummyPath); + Assert.fail("Non-reachable, should throw an exception for non-existing resource."); + } catch (RuntimeException e) { + Assert.assertTrue(e.getMessage().startsWith("Error getting resource '"+dummyPath+"' for "+context)); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiTestResources.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiTestResources.java b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiTestResources.java new file mode 100644 index 0000000..b44da4d --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/core/management/osgi/OsgiTestResources.java @@ -0,0 +1,74 @@ +/* + * 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.management.osgi; + +/** + * Many OSGi tests require OSGi bundles (of course). Test bundles have been collected here + * for convenience and clarity. Available bundles (on the classpath, with source code + * either embedded or in /src/dependencies) are described by the constants in this class. + * <p> + * Some of these bundles are also used in REST API tests, as that stretches catalog further + * (using CAMP) and that is one area where OSGi is heavily used. + */ +public class OsgiTestResources { + + + /** + * brooklyn-osgi-test-a_0.1.0 - + * defines TestA which has a "times" method and a static multiplier field; + * we set the multiplier to determine when we are sharing versions and when not + */ + public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = "/brooklyn/osgi/brooklyn-osgi-test-a_0.1.0.jar"; + + /** + * brooklyn-test-osgi-entities (v 0.1.0) - + * defines an entity and an application, to confirm it can be read and used by brooklyn + */ + public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = "/brooklyn/osgi/brooklyn-test-osgi-entities.jar"; + public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_APPLICATION = "brooklyn.osgi.tests.SimpleApplication"; + public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY = "brooklyn.osgi.tests.SimpleEntity"; + public static final String BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY = "brooklyn.osgi.tests.SimplePolicy"; + + /** + * brooklyn-test-osgi-more-entities_0.1.0 - + * another bundle with a minimal sayHi effector, used to test versioning and dependencies + * (this one has no dependencies, but see also {@value #BROOKLYN_TEST_MORE_ENTITIES_V2_PATH}) + */ + public static final String BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART = "brooklyn-test-osgi-more-entities"; + public static final String BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FULL = + "org.apache.brooklyn.test.resources.osgi."+BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART; + public static final String BROOKLYN_TEST_MORE_ENTITIES_V1_PATH = "/brooklyn/osgi/" + BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART + "_0.1.0.jar"; + public static final String BROOKLYN_TEST_MORE_ENTITIES_MORE_ENTITY = "brooklyn.osgi.tests.more.MoreEntity"; + + /** + * brooklyn-test-osgi-more-entities_0.2.0 - + * similar to {@link #BROOKLYN_TEST_MORE_ENTITIES_V1_PATH} but saying "HI NAME" rather than "Hi NAME", + * and declaring an explicit dependency on SimplePolicy from {@link #BROOKLYN_TEST_OSGI_ENTITIES_PATH} + */ + public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_PATH = "/brooklyn/osgi/" + BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART + "_0.2.0.jar"; + + /** + * bundle with identical metadata (same symbolic name and version -- hence being an evil twin) + * as {@link #BROOKLYN_TEST_MORE_ENTITIES_V2_PATH}, + * but slightly different behaviour -- saying "HO NAME" -- in order to make sure we can differentiate two two + * at runtime. + */ + public static final String BROOKLYN_TEST_MORE_ENTITIES_V2_EVIL_TWIN_PATH = "/brooklyn/osgi/" + BROOKLYN_TEST_MORE_ENTITIES_SYMBOLIC_NAME_FINAL_PART + "_evil-twin_0.2.0.jar"; + +}
