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() {

Reply via email to