Repository: incubator-brooklyn Updated Branches: refs/heads/master 4c8840e17 -> b5404f52a
UsageListener: improve API - Deprecates old interface - Adds new UsageListener interface that is more flexible and more backwards compatible. - Marks new interface as @Beta - Adds tests for new UsageListener, and refactors old tests for old-style UsageListener. - Adds Entities.proxy(Entity) Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/ac237688 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/ac237688 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/ac237688 Branch: refs/heads/master Commit: ac2376889ce474c91539512f4d93dfde41775f30 Parents: 7b5c754 Author: Aled Sage <[email protected]> Authored: Wed Mar 25 14:58:48 2015 +0000 Committer: Aled Sage <[email protected]> Committed: Thu Mar 26 10:03:35 2015 +0000 ---------------------------------------------------------------------- .../java/brooklyn/entity/basic/Entities.java | 17 ++- .../management/internal/LocalEntityManager.java | 2 +- .../management/internal/LocalUsageManager.java | 121 ++++++++++++++++--- .../internal/NonDeploymentUsageManager.java | 16 ++- .../management/internal/UsageListener.java | 103 ++++++++++++++++ .../management/internal/UsageManager.java | 59 ++++++++- .../usage/ApplicationUsageTrackingTest.java | 47 ++++++- .../usage/LocationUsageTrackingTest.java | 41 ++++++- .../usage/RecordingLegacyUsageListener.java | 70 +++++++++++ .../usage/RecordingUsageListener.java | 13 +- .../management/usage/UsageListenerTest.java | 39 +++++- 11 files changed, 485 insertions(+), 43 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/core/src/main/java/brooklyn/entity/basic/Entities.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/basic/Entities.java b/core/src/main/java/brooklyn/entity/basic/Entities.java index 48ba2d0..ac91c3a 100644 --- a/core/src/main/java/brooklyn/entity/basic/Entities.java +++ b/core/src/main/java/brooklyn/entity/basic/Entities.java @@ -46,7 +46,6 @@ import brooklyn.basic.BrooklynObjectInternal; import brooklyn.config.BrooklynProperties; import brooklyn.config.ConfigKey; import brooklyn.config.ConfigKey.HasConfigKey; -import brooklyn.enricher.basic.AbstractEnricher; import brooklyn.entity.Application; import brooklyn.entity.Effector; import brooklyn.entity.Entity; @@ -55,6 +54,7 @@ import brooklyn.entity.drivers.EntityDriver; import brooklyn.entity.drivers.downloads.DownloadResolver; import brooklyn.entity.effector.Effectors; import brooklyn.entity.proxying.EntityProxyImpl; +import brooklyn.entity.proxying.EntitySpec; import brooklyn.entity.trait.Startable; import brooklyn.entity.trait.StartableMethods; import brooklyn.event.AttributeSensor; @@ -77,7 +77,6 @@ import brooklyn.management.internal.ManagementContextInternal; import brooklyn.management.internal.NonDeploymentManagementContext; import brooklyn.policy.Enricher; import brooklyn.policy.Policy; -import brooklyn.policy.basic.AbstractPolicy; import brooklyn.util.ResourceUtils; import brooklyn.util.collections.MutableMap; import brooklyn.util.config.ConfigBag; @@ -816,6 +815,20 @@ public class Entities { return (AbstractEntity) ((EntityProxyImpl)Proxy.getInvocationHandler(e)).getDelegate(); } + /** + * Returns the proxy form (if available) of the entity. If already a proxy, returns unmodified. + * + * If null is passed in, then null is returned. + * + * For legacy entities (that did not use {@link EntitySpec} or YAML for creation), the + * proxy may not be avilable; in which case the concrete class passed in will be returned. + */ + @Beta + @SuppressWarnings("unchecked") + public static <T extends Entity> T proxy(T e) { + return (e == null) ? null : e instanceof Proxy ? e : (T) ((AbstractEntity)e).getProxyIfAvailable(); + } + /** * Brings this entity under management only if its ancestor is managed. * <p> http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/core/src/main/java/brooklyn/management/internal/LocalEntityManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/management/internal/LocalEntityManager.java b/core/src/main/java/brooklyn/management/internal/LocalEntityManager.java index 67c0a39..ac8446e 100644 --- a/core/src/main/java/brooklyn/management/internal/LocalEntityManager.java +++ b/core/src/main/java/brooklyn/management/internal/LocalEntityManager.java @@ -211,7 +211,7 @@ public class LocalEntityManager implements EntityManagerInternal { Iterable<Entity> result = Iterables.filter(allentities, predicate); return ImmutableSet.copyOf(Iterables.transform(result, new Function<Entity, Entity>() { @Override public Entity apply(Entity input) { - return (input == null) ? null : input instanceof Proxy ? input : ((AbstractEntity)input).getProxyIfAvailable(); + return Entities.proxy(input); }})); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java b/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java index e63d462..02fef4a 100644 --- a/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java +++ b/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java @@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory; import brooklyn.entity.Application; import brooklyn.entity.Entity; +import brooklyn.entity.basic.Entities; import brooklyn.entity.basic.EntityInternal; import brooklyn.entity.basic.Lifecycle; import brooklyn.internal.storage.BrooklynStorage; @@ -73,16 +74,77 @@ public class LocalUsageManager implements UsageManager { private static final Logger log = LoggerFactory.getLogger(LocalUsageManager.class); + private static class ApplicationMetadataImpl implements brooklyn.management.internal.UsageListener.ApplicationMetadata { + private final Application app; + private String applicationId; + private String applicationName; + private String entityType; + private String catalogItemId; + private Map<String, String> metadata; + + ApplicationMetadataImpl(Application app) { + this.app = checkNotNull(app, "app"); + applicationId = app.getId(); + applicationName = app.getDisplayName(); + entityType = app.getEntityType().getName(); + catalogItemId = app.getCatalogItemId(); + metadata = ((EntityInternal)app).toMetadataRecord(); + } + @Override public Application getApplication() { + return app; + } + @Override public String getApplicationId() { + return applicationId; + } + @Override public String getApplicationName() { + return applicationName; + } + @Override public String getEntityType() { + return entityType; + } + @Override public String getCatalogItemId() { + return catalogItemId; + } + @Override public Map<String, String> getMetadata() { + return metadata; + } + } + + private static class LocationMetadataImpl implements brooklyn.management.internal.UsageListener.LocationMetadata { + private final Location loc; + private String locationId; + private Map<String, String> metadata; + + LocationMetadataImpl(Location loc) { + this.loc = checkNotNull(loc, "loc"); + locationId = loc.getId(); + metadata = ((LocationInternal)loc).toMetadataRecord(); + } + @Override public Location getLocation() { + return loc; + } + @Override public String getLocationId() { + return locationId; + } + @Override public Map<String, String> getMetadata() { + return metadata; + } + } + // Register a coercion from String->UsageListener, so that USAGE_LISTENERS defined in brooklyn.properties // will be instantiated, given their class names. static { - TypeCoercions.registerAdapter(String.class, UsageListener.class, new Function<String, UsageListener>() { - @Override public UsageListener apply(String input) { + TypeCoercions.registerAdapter(String.class, brooklyn.management.internal.UsageListener.class, new Function<String, brooklyn.management.internal.UsageListener>() { + @Override public brooklyn.management.internal.UsageListener apply(String input) { // TODO Want to use classLoader = mgmt.getCatalog().getRootClassLoader(); ClassLoader classLoader = LocalUsageManager.class.getClassLoader(); Optional<Object> result = Reflections.invokeConstructorWithArgs(classLoader, input); if (result.isPresent()) { - return (UsageListener) result.get(); + if (result.get() instanceof brooklyn.management.internal.UsageManager.UsageListener) { + return new brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter((brooklyn.management.internal.UsageManager.UsageListener) result.get()); + } else { + return (brooklyn.management.internal.UsageListener) result.get(); + } } else { throw new IllegalStateException("Failed to create UsageListener from class name '"+input+"' using no-arg constructor"); } @@ -100,7 +162,7 @@ public class LocalUsageManager implements UsageManager { private final Object mutex = new Object(); - private final List<UsageListener> listeners = Lists.newCopyOnWriteArrayList(); + private final List<brooklyn.management.internal.UsageListener> listeners = Lists.newCopyOnWriteArrayList(); private final AtomicInteger listenerQueueSize = new AtomicInteger(); @@ -111,13 +173,23 @@ public class LocalUsageManager implements UsageManager { public LocalUsageManager(LocalManagementContext managementContext) { this.managementContext = checkNotNull(managementContext, "managementContext"); - Collection<UsageListener> listeners = managementContext.getBrooklynProperties().getConfig(UsageManager.USAGE_LISTENERS); + // TODO Once brooklyn.management.internal.UsageManager.UsageListener is deleted, restore this + // to normal generics! + Collection<?> listeners = managementContext.getBrooklynProperties().getConfig(UsageManager.USAGE_LISTENERS); if (listeners != null) { - for (UsageListener listener : listeners) { + for (Object listener : listeners) { if (listener instanceof ManagementContextInjectable) { ((ManagementContextInjectable)listener).injectManagementContext(managementContext); } - addUsageListener(listener); + if (listener instanceof brooklyn.management.internal.UsageManager.UsageListener) { + addUsageListener((brooklyn.management.internal.UsageManager.UsageListener)listener); + } else if (listener instanceof brooklyn.management.internal.UsageListener) { + addUsageListener((brooklyn.management.internal.UsageListener)listener); + } else if (listener == null) { + throw new NullPointerException("null listener in config "+UsageManager.USAGE_LISTENERS); + } else { + throw new ClassCastException("listener "+listener+" of type "+listener.getClass()+" is not of type "+brooklyn.management.internal.UsageListener.class.getName()); + } } } } @@ -129,7 +201,7 @@ public class LocalUsageManager implements UsageManager { log.info("Usage manager waiting for "+listenerQueueSize+" listener events for up to "+timeout); } List<ListenableFuture<?>> futures = Lists.newArrayList(); - for (final UsageListener listener : listeners) { + for (final brooklyn.management.internal.UsageListener listener : listeners) { ListenableFuture<?> future = listenerExecutor.submit(new Runnable() { public void run() { if (listener instanceof Closeable) { @@ -152,8 +224,8 @@ public class LocalUsageManager implements UsageManager { } } - private void execOnListeners(final Function<UsageListener, Void> job) { - for (final UsageListener listener : listeners) { + private void execOnListeners(final Function<brooklyn.management.internal.UsageListener, Void> job) { + for (final brooklyn.management.internal.UsageListener listener : listeners) { listenerQueueSize.incrementAndGet(); listenerExecutor.execute(new Runnable() { public void run() { @@ -182,10 +254,9 @@ public class LocalUsageManager implements UsageManager { usage.addEvent(event); eventMap.put(app.getId(), usage); - execOnListeners(new Function<UsageListener, Void>() { - public Void apply(UsageListener listener) { - listener.onApplicationEvent(app.getId(), app.getDisplayName(), app.getEntityType().getName(), - app.getCatalogItemId(), ((EntityInternal)app).toMetadataRecord(), event); + execOnListeners(new Function<brooklyn.management.internal.UsageListener, Void>() { + public Void apply(brooklyn.management.internal.UsageListener listener) { + listener.onApplicationEvent(new ApplicationMetadataImpl(Entities.proxy(app)), event); return null; } public String toString() { @@ -240,9 +311,9 @@ public class LocalUsageManager implements UsageManager { usage.addEvent(event); usageMap.put(loc.getId(), usage); - execOnListeners(new Function<UsageListener, Void>() { - public Void apply(UsageListener listener) { - listener.onLocationEvent(loc.getId(), ((LocationInternal)loc).toMetadataRecord(), event); + execOnListeners(new Function<brooklyn.management.internal.UsageListener, Void>() { + public Void apply(brooklyn.management.internal.UsageListener listener) { + listener.onLocationEvent(new LocationMetadataImpl(loc), event); return null; } public String toString() { @@ -318,12 +389,24 @@ public class LocalUsageManager implements UsageManager { } @Override - public void addUsageListener(UsageListener listener) { + @Deprecated + public void addUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener) { + addUsageListener(new brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener)); + } + + @Override + @Deprecated + public void removeUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener) { + removeUsageListener(new brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener)); + } + + @Override + public void addUsageListener(brooklyn.management.internal.UsageListener listener) { listeners.add(listener); } @Override - public void removeUsageListener(UsageListener listener) { + public void removeUsageListener(brooklyn.management.internal.UsageListener listener) { listeners.remove(listener); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/core/src/main/java/brooklyn/management/internal/NonDeploymentUsageManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/management/internal/NonDeploymentUsageManager.java b/core/src/main/java/brooklyn/management/internal/NonDeploymentUsageManager.java index f8e6a81..366a25c 100644 --- a/core/src/main/java/brooklyn/management/internal/NonDeploymentUsageManager.java +++ b/core/src/main/java/brooklyn/management/internal/NonDeploymentUsageManager.java @@ -100,7 +100,19 @@ public class NonDeploymentUsageManager implements UsageManager { } @Override - public void addUsageListener(UsageListener listener) { + @Deprecated + public void addUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener) { + addUsageListener(new brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener)); + } + + @Override + @Deprecated + public void removeUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener) { + removeUsageListener(new brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener)); + } + + @Override + public void addUsageListener(brooklyn.management.internal.UsageListener listener) { if (isInitialManagementContextReal()) { initialManagementContext.getUsageManager().addUsageListener(listener); } else { @@ -109,7 +121,7 @@ public class NonDeploymentUsageManager implements UsageManager { } @Override - public void removeUsageListener(UsageListener listener) { + public void removeUsageListener(brooklyn.management.internal.UsageListener listener) { if (isInitialManagementContextReal()) { initialManagementContext.getUsageManager().removeUsageListener(listener); } else { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/core/src/main/java/brooklyn/management/internal/UsageListener.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/management/internal/UsageListener.java b/core/src/main/java/brooklyn/management/internal/UsageListener.java new file mode 100644 index 0000000..efd8a63 --- /dev/null +++ b/core/src/main/java/brooklyn/management/internal/UsageListener.java @@ -0,0 +1,103 @@ +/* + * 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 brooklyn.management.internal; + +import java.util.Map; + +import brooklyn.entity.Application; +import brooklyn.location.Location; +import brooklyn.management.usage.ApplicationUsage.ApplicationEvent; +import brooklyn.management.usage.LocationUsage.LocationEvent; + +import com.google.common.annotations.Beta; + +@Beta +public interface UsageListener { + + /** + * A no-op implementation of {@link UsageListener}, for users to extend. + * + * Users are encouraged to extend this class, which will shield the user + * from the addition of other usage event methods being added. If additional + * methods are added in a future release, a no-op implementation will be + * added to this class. + */ + @Beta + public static class BasicUsageListener implements UsageListener { + @Override + public void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event) { + } + + @Override public void onLocationEvent(LocationMetadata loc, LocationEvent event) { + } + } + + /** + * Users should never implement this interface directly; methods may be added in future releases + * without notice. + */ + @Beta + public interface ApplicationMetadata { + /** + * Access the application directly with caution: by the time the listener fires, + * the application may no longer be managed. + */ + @Beta + Application getApplication(); + + String getApplicationId(); + + String getApplicationName(); + + String getEntityType(); + + String getCatalogItemId(); + + Map<String, String> getMetadata(); + } + + /** + * Users should never implement this interface directly; methods may be added in future releases + * without notice. + */ + @Beta + public interface LocationMetadata { + /** + * Access the location directly with caution: by the time the listener fires, + * the location may no longer be managed. + */ + @Beta + Location getLocation(); + + String getLocationId(); + + Map<String, String> getMetadata(); + } + + public static final UsageListener NOOP = new UsageListener() { + @Override public void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event) {} + @Override public void onLocationEvent(LocationMetadata loc, LocationEvent event) {} + }; + + @Beta + void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event); + + @Beta + void onLocationEvent(LocationMetadata loc, LocationEvent event); +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/core/src/main/java/brooklyn/management/internal/UsageManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/management/internal/UsageManager.java b/core/src/main/java/brooklyn/management/internal/UsageManager.java index 88226c6..063b385 100644 --- a/core/src/main/java/brooklyn/management/internal/UsageManager.java +++ b/core/src/main/java/brooklyn/management/internal/UsageManager.java @@ -18,6 +18,8 @@ */ package brooklyn.management.internal; +import static com.google.common.base.Preconditions.checkNotNull; + import java.util.List; import java.util.Map; import java.util.Set; @@ -34,6 +36,7 @@ import brooklyn.management.usage.LocationUsage.LocationEvent; import brooklyn.util.time.Duration; import com.google.common.annotations.Beta; +import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.reflect.TypeToken; @@ -42,10 +45,10 @@ import com.google.common.reflect.TypeToken; public interface UsageManager { @SuppressWarnings("serial") - public static final ConfigKey<List<UsageListener>> USAGE_LISTENERS = ConfigKeys.newConfigKey( - new TypeToken<List<UsageListener>>() {}, + public static final ConfigKey<List<brooklyn.management.internal.UsageListener>> USAGE_LISTENERS = ConfigKeys.newConfigKey( + new TypeToken<List<brooklyn.management.internal.UsageListener>>() {}, "brooklyn.usageManager.listeners", "Optional usage listeners (i.e. for metering)", - ImmutableList.<UsageListener>of()); + ImmutableList.<brooklyn.management.internal.UsageListener>of()); public static final ConfigKey<Duration> USAGE_LISTENER_TERMINATION_TIMEOUT = ConfigKeys.newConfigKey( Duration.class, @@ -53,6 +56,10 @@ public interface UsageManager { "Timeout on termination, to wait for queue of usage listener events to be processed", Duration.TEN_SECONDS); + /** + * @since 0.7.0 + * @deprecated since 0.7.0; use {@link brooklyn.management.internal.UsageListener}; see {@link UsageListenerAdapter} + */ public interface UsageListener { public static final UsageListener NOOP = new UsageListener() { @Override public void onApplicationEvent(String applicationId, String applicationName, String entityType, @@ -60,6 +67,34 @@ public interface UsageManager { @Override public void onLocationEvent(String locationId, Map<String, String> metadata, LocationEvent event) {} }; + public static class UsageListenerAdapter implements brooklyn.management.internal.UsageListener { + private final UsageListener listener; + + public UsageListenerAdapter(UsageListener listener) { + this.listener = checkNotNull(listener, "listener"); + } + + @Override + public void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event) { + listener.onApplicationEvent(app.getApplicationId(), app.getApplicationName(), app.getEntityType(), app.getCatalogItemId(), app.getMetadata(), event); + } + + @Override + public void onLocationEvent(LocationMetadata loc, LocationEvent event) { + listener.onLocationEvent(loc.getLocationId(), loc.getMetadata(), event); + } + + @Override + public boolean equals(Object obj) { + return (obj instanceof UsageListenerAdapter) && listener.equals(((UsageListenerAdapter)obj).listener); + } + + @Override + public int hashCode() { + return Objects.hashCode(listener); + } + } + void onApplicationEvent(String applicationId, String applicationName, String entityType, String catalogItemId, Map<String, String> metadata, ApplicationEvent event); @@ -101,16 +136,30 @@ public interface UsageManager { Set<ApplicationUsage> getApplicationUsage(Predicate<? super ApplicationUsage> filter); /** + * @since 0.7.0 + * @deprecated since 0.7.0; use {@link #removeUsageListener(brooklyn.management.internal.UsageListener)}; + * see {@link brooklyn.management.internal.UsageManager.UsageListener.UsageListenerAdapter} + */ + void addUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener); + + /** + * @since 0.7.0 + * @deprecated since 0.7.0; use {@link #removeUsageListener(brooklyn.management.internal.UsageListener)} + */ + @Deprecated + void removeUsageListener(brooklyn.management.internal.UsageManager.UsageListener listener); + + /** * Adds the given listener, to be notified on recording of application/location events. * The listener notifications may be asynchronous. * * As of 0.7.0, the listener is not persisted so will be lost on restart/rebind. This * behaviour may change in a subsequent release. */ - void addUsageListener(UsageListener listener); + void addUsageListener(brooklyn.management.internal.UsageListener listener); /** * Removes the given listener. */ - void removeUsageListener(UsageListener listener); + void removeUsageListener(brooklyn.management.internal.UsageListener listener); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/software/base/src/test/java/brooklyn/management/usage/ApplicationUsageTrackingTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/brooklyn/management/usage/ApplicationUsageTrackingTest.java b/software/base/src/test/java/brooklyn/management/usage/ApplicationUsageTrackingTest.java index eab3795..337391f 100644 --- a/software/base/src/test/java/brooklyn/management/usage/ApplicationUsageTrackingTest.java +++ b/software/base/src/test/java/brooklyn/management/usage/ApplicationUsageTrackingTest.java @@ -20,6 +20,7 @@ package brooklyn.management.usage; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.util.List; @@ -35,8 +36,10 @@ import org.testng.annotations.Test; import brooklyn.entity.Application; import brooklyn.entity.basic.Entities; import brooklyn.entity.basic.Lifecycle; +import brooklyn.entity.proxying.EntityProxy; import brooklyn.location.Location; import brooklyn.management.internal.ManagementContextInternal; +import brooklyn.management.internal.UsageListener.ApplicationMetadata; import brooklyn.management.usage.ApplicationUsage.ApplicationEvent; import brooklyn.test.Asserts; import brooklyn.test.entity.LocalManagementContextForTests; @@ -82,8 +85,9 @@ public class ApplicationUsageTrackingTest { } @Test - public void testAddAndRemoveUsageListener() throws Exception { - final RecordingUsageListener listener = new RecordingUsageListener(); + @SuppressWarnings("deprecation") + public void testAddAndRemoveLegacyUsageListener() throws Exception { + final RecordingLegacyUsageListener listener = new RecordingLegacyUsageListener(); mgmt.getUsageManager().addUsageListener(listener); app = TestApplication.Factory.newManagedInstanceForTests(mgmt); @@ -124,6 +128,45 @@ public class ApplicationUsageTrackingTest { } @Test + public void testAddAndRemoveUsageListener() throws Exception { + final RecordingUsageListener listener = new RecordingUsageListener(); + mgmt.getUsageManager().addUsageListener(listener); + + app = TestApplication.Factory.newManagedInstanceForTests(mgmt); + app.setCatalogItemId("testCatalogItem"); + app.start(ImmutableList.<Location>of()); + + Asserts.succeedsEventually(new Runnable() { + @Override public void run() { + List<List<?>> events = listener.getApplicationEvents(); + assertEquals(events.size(), 2, "events="+events); // expect STARTING and RUNNING + ApplicationMetadata appMetadata = (ApplicationMetadata) events.get(0).get(1); + ApplicationEvent appEvent = (ApplicationEvent) events.get(0).get(2); + + assertEquals(appMetadata.getApplication(), app, "events="+events); + assertTrue(appMetadata.getApplication() instanceof EntityProxy, "events="+events); + assertEquals(appMetadata.getApplicationId(), app.getId(), "events="+events); + assertNotNull(appMetadata.getApplicationName(), "events="+events); + assertEquals(appMetadata.getCatalogItemId(), app.getCatalogItemId(), "events="+events); + assertNotNull(appMetadata.getEntityType(), "events="+events); + assertNotNull(appMetadata.getMetadata(), "events="+events); + assertEquals(appEvent.getState(), Lifecycle.STARTING, "events="+events); + }}); + + + // Remove the listener; will get no more notifications + listener.clearEvents(); + mgmt.getUsageManager().removeUsageListener(listener); + + app.start(ImmutableList.<Location>of()); + Asserts.succeedsContinually(new Runnable() { + @Override public void run() { + List<List<?>> events = listener.getLocationEvents(); + assertEquals(events.size(), 0, "events="+events); + }}); + } + + @Test public void testUsageIncludesStartAndStopEvents() { // Start event long preStart = System.currentTimeMillis(); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/software/base/src/test/java/brooklyn/management/usage/LocationUsageTrackingTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/brooklyn/management/usage/LocationUsageTrackingTest.java b/software/base/src/test/java/brooklyn/management/usage/LocationUsageTrackingTest.java index 3ad836d..0343740 100644 --- a/software/base/src/test/java/brooklyn/management/usage/LocationUsageTrackingTest.java +++ b/software/base/src/test/java/brooklyn/management/usage/LocationUsageTrackingTest.java @@ -39,6 +39,7 @@ import brooklyn.location.LocationSpec; import brooklyn.location.NoMachinesAvailableException; import brooklyn.location.basic.LocalhostMachineProvisioningLocation; import brooklyn.location.basic.SshMachineLocation; +import brooklyn.management.internal.UsageListener.LocationMetadata; import brooklyn.management.usage.LocationUsage.LocationEvent; import brooklyn.test.Asserts; import brooklyn.util.time.Time; @@ -66,8 +67,9 @@ public class LocationUsageTrackingTest extends BrooklynAppUnitTestSupport { } @Test - public void testAddAndRemoveUsageListener() throws Exception { - final RecordingUsageListener listener = new RecordingUsageListener(); + @SuppressWarnings("deprecation") + public void testAddAndRemoveLegacyUsageListener() throws Exception { + final RecordingLegacyUsageListener listener = new RecordingLegacyUsageListener(); mgmt.getUsageManager().addUsageListener(listener); app.createAndManageChild(EntitySpec.create(SoftwareProcessEntityTest.MyService.class)); @@ -101,6 +103,41 @@ public class LocationUsageTrackingTest extends BrooklynAppUnitTestSupport { } @Test + public void testAddAndRemoveUsageListener() throws Exception { + final RecordingUsageListener listener = new RecordingUsageListener(); + mgmt.getUsageManager().addUsageListener(listener); + + app.createAndManageChild(EntitySpec.create(SoftwareProcessEntityTest.MyService.class)); + app.start(ImmutableList.of(loc)); + final SshMachineLocation machine = Iterables.getOnlyElement(loc.getAllMachines()); + + Asserts.succeedsEventually(new Runnable() { + @Override public void run() { + List<List<?>> events = listener.getLocationEvents(); + LocationMetadata locMetadata = (LocationMetadata) events.get(0).get(1); + LocationEvent locEvent = (LocationEvent) events.get(0).get(2); + + assertEquals(events.size(), 1, "events="+events); + assertEquals(locMetadata.getLocation(), machine, "events="+events); + assertEquals(locMetadata.getLocationId(), machine.getId(), "events="+events); + assertNotNull(locMetadata.getMetadata(), "events="+events); + assertEquals(locEvent.getApplicationId(), app.getId(), "events="+events); + assertEquals(locEvent.getState(), Lifecycle.CREATED, "events="+events); + }}); + + // Remove the listener; will get no more notifications + listener.clearEvents(); + mgmt.getUsageManager().removeUsageListener(listener); + + app.stop(); + Asserts.succeedsContinually(new Runnable() { + @Override public void run() { + List<List<?>> events = listener.getLocationEvents(); + assertEquals(events.size(), 0, "events="+events); + }}); + } + + @Test public void testUsageIncludesStartAndStopEvents() { SoftwareProcessEntityTest.MyService entity = app.createAndManageChild(EntitySpec.create(SoftwareProcessEntityTest.MyService.class)); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/software/base/src/test/java/brooklyn/management/usage/RecordingLegacyUsageListener.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/brooklyn/management/usage/RecordingLegacyUsageListener.java b/software/base/src/test/java/brooklyn/management/usage/RecordingLegacyUsageListener.java new file mode 100644 index 0000000..c270ac5 --- /dev/null +++ b/software/base/src/test/java/brooklyn/management/usage/RecordingLegacyUsageListener.java @@ -0,0 +1,70 @@ +/* + * 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 brooklyn.management.usage; + +import java.util.List; +import java.util.Map; + +import brooklyn.management.usage.ApplicationUsage.ApplicationEvent; +import brooklyn.management.usage.LocationUsage.LocationEvent; +import brooklyn.util.collections.MutableList; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +@Deprecated +public class RecordingLegacyUsageListener implements brooklyn.management.internal.UsageManager.UsageListener { + + private final List<List<?>> events = Lists.newCopyOnWriteArrayList(); + + @Override + public void onApplicationEvent(String applicationId, String applicationName, String entityType, + String catalogItemId, Map<String, String> metadata, ApplicationEvent event) { + events.add(MutableList.of("application", applicationId, applicationName, entityType, catalogItemId, metadata, event)); + } + + @Override + public void onLocationEvent(String locationId, Map<String, String> metadata, LocationEvent event) { + events.add(MutableList.of("location", locationId, metadata, event)); + } + + public void clearEvents() { + events.clear(); + } + + public List<List<?>> getEvents() { + return ImmutableList.copyOf(events); + } + + public List<List<?>> getLocationEvents() { + List<List<?>> result = Lists.newArrayList(); + for (List<?> event : events) { + if (event.get(0).equals("location")) result.add(event); + } + return ImmutableList.copyOf(result); + } + + public List<List<?>> getApplicationEvents() { + List<List<?>> result = Lists.newArrayList(); + for (List<?> event : events) { + if (event.get(0).equals("application")) result.add(event); + } + return ImmutableList.copyOf(result); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/software/base/src/test/java/brooklyn/management/usage/RecordingUsageListener.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/brooklyn/management/usage/RecordingUsageListener.java b/software/base/src/test/java/brooklyn/management/usage/RecordingUsageListener.java index 97ee7da..bf26c5c 100644 --- a/software/base/src/test/java/brooklyn/management/usage/RecordingUsageListener.java +++ b/software/base/src/test/java/brooklyn/management/usage/RecordingUsageListener.java @@ -19,9 +19,7 @@ package brooklyn.management.usage; import java.util.List; -import java.util.Map; -import brooklyn.management.internal.UsageManager.UsageListener; import brooklyn.management.usage.ApplicationUsage.ApplicationEvent; import brooklyn.management.usage.LocationUsage.LocationEvent; import brooklyn.util.collections.MutableList; @@ -29,19 +27,18 @@ import brooklyn.util.collections.MutableList; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -public class RecordingUsageListener implements UsageListener { +public class RecordingUsageListener implements brooklyn.management.internal.UsageListener { private final List<List<?>> events = Lists.newCopyOnWriteArrayList(); @Override - public void onApplicationEvent(String applicationId, String applicationName, String entityType, - String catalogItemId, Map<String, String> metadata, ApplicationEvent event) { - events.add(MutableList.of("application", applicationId, applicationName, entityType, catalogItemId, metadata, event)); + public void onApplicationEvent(ApplicationMetadata app, ApplicationEvent event) { + events.add(MutableList.of("application", app, event)); } @Override - public void onLocationEvent(String locationId, Map<String, String> metadata, LocationEvent event) { - events.add(MutableList.of("location", locationId, metadata, event)); + public void onLocationEvent(LocationMetadata loc, LocationEvent event) { + events.add(MutableList.of("location", loc, event)); } public void clearEvents() { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ac237688/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java ---------------------------------------------------------------------- diff --git a/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java b/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java index 0cc27c7..612d2b3 100644 --- a/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java +++ b/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java @@ -33,7 +33,6 @@ import brooklyn.entity.basic.Entities; import brooklyn.location.Location; import brooklyn.management.internal.ManagementContextInternal; import brooklyn.management.internal.UsageManager; -import brooklyn.management.internal.UsageManager.UsageListener; import brooklyn.test.Asserts; import brooklyn.test.entity.LocalManagementContextForTests; import brooklyn.test.entity.TestApplication; @@ -57,6 +56,7 @@ public class UsageListenerTest { @BeforeMethod(alwaysRun=true) public void setUp() throws Exception { + RecordingStaticLegacyUsageListener.clearInstances(); RecordingStaticUsageListener.clearInstances(); } @@ -68,11 +68,28 @@ public class UsageListenerTest { LOG.error("Caught exception in tearDown method", t); } finally { mgmt = null; + RecordingStaticLegacyUsageListener.clearInstances(); RecordingStaticUsageListener.clearInstances(); } } @Test + public void testAddLegacyUsageListenerViaProperties() throws Exception { + BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty(); + brooklynProperties.put(UsageManager.USAGE_LISTENERS, RecordingStaticLegacyUsageListener.class.getName()); + mgmt = LocalManagementContextForTests.newInstance(brooklynProperties); + + app = TestApplication.Factory.newManagedInstanceForTests(mgmt); + app.start(ImmutableList.<Location>of()); + + Asserts.succeedsEventually(new Runnable() { + @Override public void run() { + List<List<?>> events = RecordingStaticLegacyUsageListener.getInstance().getApplicationEvents(); + assertTrue(events.size() > 0, "events="+events); // expect some events + }}); + } + + @Test public void testAddUsageListenerViaProperties() throws Exception { BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty(); brooklynProperties.put(UsageManager.USAGE_LISTENERS, RecordingStaticUsageListener.class.getName()); @@ -88,7 +105,25 @@ public class UsageListenerTest { }}); } - public static class RecordingStaticUsageListener extends RecordingUsageListener implements UsageListener { + public static class RecordingStaticLegacyUsageListener extends RecordingLegacyUsageListener implements brooklyn.management.internal.UsageManager.UsageListener { + private static final List<RecordingStaticLegacyUsageListener> STATIC_INSTANCES = Lists.newCopyOnWriteArrayList(); + + public static RecordingStaticLegacyUsageListener getInstance() { + return Iterables.getOnlyElement(STATIC_INSTANCES); + } + + public static void clearInstances() { + STATIC_INSTANCES.clear(); + } + + public RecordingStaticLegacyUsageListener() { + // Bad to leak a ref to this before constructor finished, but we'll live with it because + // it's just test code! + STATIC_INSTANCES.add(this); + } + } + + public static class RecordingStaticUsageListener extends RecordingUsageListener implements brooklyn.management.internal.UsageListener { private static final List<RecordingStaticUsageListener> STATIC_INSTANCES = Lists.newCopyOnWriteArrayList(); public static RecordingStaticUsageListener getInstance() {
