Read UsageListeners from config in brooklyn.properties
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/7ee06ba3 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/7ee06ba3 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/7ee06ba3 Branch: refs/heads/master Commit: 7ee06ba3c67a6d5ea1cb494b75aabb20b00c13f0 Parents: e315409 Author: Aled Sage <[email protected]> Authored: Fri Nov 7 19:44:34 2014 +0000 Committer: Aled Sage <[email protected]> Committed: Mon Nov 10 11:39:53 2014 +0000 ---------------------------------------------------------------------- .../management/internal/LocalUsageManager.java | 29 +++++ .../management/internal/UsageManager.java | 11 ++ .../management/usage/UsageListenerTest.java | 108 +++++++++++++++++++ 3 files changed, 148 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7ee06ba3/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 1bb4a8e..476ea3e 100644 --- a/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java +++ b/core/src/main/java/brooklyn/management/internal/LocalUsageManager.java @@ -20,6 +20,7 @@ package brooklyn.management.internal; import static com.google.common.base.Preconditions.checkNotNull; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -41,8 +42,12 @@ import brooklyn.location.basic.LocationInternal; import brooklyn.management.usage.ApplicationUsage; import brooklyn.management.usage.LocationUsage; import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.flags.TypeCoercions; +import brooklyn.util.javalang.Reflections; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -59,6 +64,23 @@ public class LocalUsageManager implements UsageManager { private static final Logger log = LoggerFactory.getLogger(LocalUsageManager.class); + // 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) { + // 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(); + } else { + throw new IllegalStateException("Failed to create UsageListener from class name '"+input+"' using no-arg constructor"); + } + } + }); + } + @VisibleForTesting public static final String APPLICATION_USAGE_KEY = "usage-application"; @@ -77,6 +99,13 @@ public class LocalUsageManager implements UsageManager { public LocalUsageManager(LocalManagementContext managementContext) { this.managementContext = checkNotNull(managementContext, "managementContext"); + + Collection<UsageListener> listeners = managementContext.getBrooklynProperties().getConfig(UsageManager.USAGE_LISTENERS); + if (listeners != null) { + for (UsageListener listener : listeners) { + addUsageListener(listener); + } + } } @Override http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7ee06ba3/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 35e602f..56a5ace 100644 --- a/core/src/main/java/brooklyn/management/internal/UsageManager.java +++ b/core/src/main/java/brooklyn/management/internal/UsageManager.java @@ -18,10 +18,13 @@ */ package brooklyn.management.internal; +import java.util.List; import java.util.Map; import java.util.Set; +import brooklyn.config.ConfigKey; import brooklyn.entity.Application; +import brooklyn.entity.basic.ConfigKeys; import brooklyn.entity.basic.Lifecycle; import brooklyn.location.Location; import brooklyn.management.usage.ApplicationUsage; @@ -31,10 +34,18 @@ import brooklyn.management.usage.LocationUsage.LocationEvent; import com.google.common.annotations.Beta; import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableList; +import com.google.common.reflect.TypeToken; @Beta public interface UsageManager { + @SuppressWarnings("serial") + public static final ConfigKey<List<UsageListener>> USAGE_LISTENERS = ConfigKeys.newConfigKey( + new TypeToken<List<UsageListener>>() {}, + "brooklyn.usageManager.listeners", "Optional usage listeners (i.e. for metering)", + ImmutableList.<UsageListener>of()); + public interface UsageListener { public static final UsageListener NOOP = new UsageListener() { @Override public void onApplicationEvent(String applicationId, String applicationName, String entityType, http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7ee06ba3/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 new file mode 100644 index 0000000..0cc27c7 --- /dev/null +++ b/software/base/src/test/java/brooklyn/management/usage/UsageListenerTest.java @@ -0,0 +1,108 @@ +/* + * 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 static org.testng.Assert.assertTrue; + +import java.util.List; + +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.config.BrooklynProperties; +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; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +public class UsageListenerTest { + + // Also see {Application|Location}UsageTrackingTest for listener functionality + + private static final Logger LOG = LoggerFactory.getLogger(ApplicationUsageTrackingTest.class); + + protected TestApplication app; + protected ManagementContextInternal mgmt; + + protected boolean shouldSkipOnBoxBaseDirResolution() { + return true; + } + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + RecordingStaticUsageListener.clearInstances(); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + try { + if (mgmt != null) Entities.destroyAll(mgmt); + } catch (Throwable t) { + LOG.error("Caught exception in tearDown method", t); + } finally { + mgmt = null; + RecordingStaticUsageListener.clearInstances(); + } + } + + @Test + public void testAddUsageListenerViaProperties() throws Exception { + BrooklynProperties brooklynProperties = BrooklynProperties.Factory.newEmpty(); + brooklynProperties.put(UsageManager.USAGE_LISTENERS, RecordingStaticUsageListener.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 = RecordingStaticUsageListener.getInstance().getApplicationEvents(); + assertTrue(events.size() > 0, "events="+events); // expect some events + }}); + } + + public static class RecordingStaticUsageListener extends RecordingUsageListener implements UsageListener { + private static final List<RecordingStaticUsageListener> STATIC_INSTANCES = Lists.newCopyOnWriteArrayList(); + + public static RecordingStaticUsageListener getInstance() { + return Iterables.getOnlyElement(STATIC_INSTANCES); + } + + public static void clearInstances() { + STATIC_INSTANCES.clear(); + } + + public RecordingStaticUsageListener() { + // 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); + } + } +}
