http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManager.java new file mode 100644 index 0000000..99b79ec --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalSubscriptionManager.java @@ -0,0 +1,292 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.management.internal; + +import static brooklyn.util.JavaGroovyEquivalents.elvis; +import static brooklyn.util.JavaGroovyEquivalents.groovyTruth; +import static brooklyn.util.JavaGroovyEquivalents.join; +import static brooklyn.util.JavaGroovyEquivalents.mapOf; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.event.Sensor; +import org.apache.brooklyn.api.event.SensorEvent; +import org.apache.brooklyn.api.event.SensorEventListener; +import org.apache.brooklyn.api.management.ExecutionManager; +import org.apache.brooklyn.api.management.SubscriptionHandle; +import org.apache.brooklyn.api.management.SubscriptionManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.entity.basic.Entities; +import brooklyn.util.task.BasicExecutionManager; +import brooklyn.util.task.SingleThreadedScheduler; +import brooklyn.util.text.Identifiers; + +import com.google.common.base.Predicate; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimaps; + +/** + * A {@link SubscriptionManager} that stores subscription details locally. + */ +public class LocalSubscriptionManager extends AbstractSubscriptionManager { + + private static final Logger LOG = LoggerFactory.getLogger(LocalSubscriptionManager.class); + + protected final ExecutionManager em; + + private final String tostring = "SubscriptionContext("+Identifiers.getBase64IdFromValue(System.identityHashCode(this), 5)+")"; + + private final AtomicLong totalEventsPublishedCount = new AtomicLong(); + private final AtomicLong totalEventsDeliveredCount = new AtomicLong(); + + @SuppressWarnings("rawtypes") + protected final ConcurrentMap<String, Subscription> allSubscriptions = new ConcurrentHashMap<String, Subscription>(); + @SuppressWarnings("rawtypes") + protected final ConcurrentMap<Object, Set<Subscription>> subscriptionsBySubscriber = new ConcurrentHashMap<Object, Set<Subscription>>(); + @SuppressWarnings("rawtypes") + protected final ConcurrentMap<Object, Set<Subscription>> subscriptionsByToken = new ConcurrentHashMap<Object, Set<Subscription>>(); + + public LocalSubscriptionManager(ExecutionManager m) { + this.em = m; + } + + public long getNumSubscriptions() { + return allSubscriptions.size(); + } + + public long getTotalEventsPublished() { + return totalEventsPublishedCount.get(); + } + + public long getTotalEventsDelivered() { + return totalEventsDeliveredCount.get(); + } + + @SuppressWarnings("unchecked") + protected synchronized <T> SubscriptionHandle subscribe(Map<String, Object> flags, Subscription<T> s) { + Entity producer = s.producer; + Sensor<T> sensor= s.sensor; + s.subscriber = getSubscriber(flags, s); + if (flags.containsKey("subscriberExecutionManagerTag")) { + s.subscriberExecutionManagerTag = flags.remove("subscriberExecutionManagerTag"); + s.subscriberExecutionManagerTagSupplied = true; + } else { + s.subscriberExecutionManagerTag = + s.subscriber instanceof Entity ? "subscription-delivery-entity-"+((Entity)s.subscriber).getId()+"["+s.subscriber+"]" : + s.subscriber instanceof String ? "subscription-delivery-string["+s.subscriber+"]" : + "subscription-delivery-object["+s.subscriber+"]"; + s.subscriberExecutionManagerTagSupplied = false; + } + s.eventFilter = (Predicate<SensorEvent<T>>) flags.remove("eventFilter"); + s.flags = flags; + + if (LOG.isDebugEnabled()) LOG.debug("Creating subscription {} for {} on {} {} in {}", new Object[] {s.id, s.subscriber, producer, sensor, this}); + allSubscriptions.put(s.id, s); + addToMapOfSets(subscriptionsByToken, makeEntitySensorToken(s.producer, s.sensor), s); + if (s.subscriber!=null) { + addToMapOfSets(subscriptionsBySubscriber, s.subscriber, s); + } + if (!s.subscriberExecutionManagerTagSupplied && s.subscriberExecutionManagerTag!=null) { + ((BasicExecutionManager) em).setTaskSchedulerForTag(s.subscriberExecutionManagerTag, SingleThreadedScheduler.class); + } + return s; + } + + @SuppressWarnings("unchecked") + public Set<SubscriptionHandle> getSubscriptionsForSubscriber(Object subscriber) { + return (Set<SubscriptionHandle>) ((Set<?>) elvis(subscriptionsBySubscriber.get(subscriber), Collections.emptySet())); + } + + public synchronized Set<SubscriptionHandle> getSubscriptionsForEntitySensor(Entity source, Sensor<?> sensor) { + Set<SubscriptionHandle> subscriptions = new LinkedHashSet<SubscriptionHandle>(); + subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(source, sensor)), Collections.emptySet())); + subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(null, sensor)), Collections.emptySet())); + subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(source, null)), Collections.emptySet())); + subscriptions.addAll(elvis(subscriptionsByToken.get(makeEntitySensorToken(null, null)), Collections.emptySet())); + return subscriptions; + } + + /** + * Unsubscribe the given subscription id. + * + * @see #subscribe(Map, Entity, Sensor, SensorEventListener) + */ + @SuppressWarnings("rawtypes") + public synchronized boolean unsubscribe(SubscriptionHandle sh) { + if (!(sh instanceof Subscription)) throw new IllegalArgumentException("Only subscription handles of type Subscription supported: sh="+sh+"; type="+(sh != null ? sh.getClass().getCanonicalName() : null)); + Subscription s = (Subscription) sh; + boolean result = allSubscriptions.remove(s.id) != null; + boolean b2 = removeFromMapOfCollections(subscriptionsByToken, makeEntitySensorToken(s.producer, s.sensor), s); + assert result==b2; + if (s.subscriber!=null) { + boolean b3 = removeFromMapOfCollections(subscriptionsBySubscriber, s.subscriber, s); + assert b3 == b2; + } + + // FIXME ALEX - this seems wrong + ((BasicExecutionManager) em).setTaskSchedulerForTag(s.subscriberExecutionManagerTag, SingleThreadedScheduler.class); + return result; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public <T> void publish(final SensorEvent<T> event) { + // REVIEW 1459 - execution + + // delivery in parallel/background, using execution manager + + // subscriptions, should define SingleThreadedScheduler for any subscriber ID tag + // in order to ensure callbacks are invoked in the order they are submitted + // (recommend exactly one per subscription to prevent deadlock) + // this is done with: + // em.setTaskSchedulerForTag(subscriberId, SingleThreadedScheduler.class); + + //note, generating the notifications must be done in the calling thread to preserve order + //e.g. emit(A); emit(B); should cause onEvent(A); onEvent(B) in that order + if (LOG.isTraceEnabled()) LOG.trace("{} got event {}", this, event); + totalEventsPublishedCount.incrementAndGet(); + + Set<Subscription> subs = (Set<Subscription>) ((Set<?>) getSubscriptionsForEntitySensor(event.getSource(), event.getSensor())); + if (groovyTruth(subs)) { + if (LOG.isTraceEnabled()) LOG.trace("sending {}, {} to {}", new Object[] {event.getSensor().getName(), event, join(subs, ",")}); + for (Subscription s : subs) { + if (s.eventFilter!=null && !s.eventFilter.apply(event)) + continue; + final Subscription sAtClosureCreation = s; + +// Set<Object> tags = MutableSet.of(); +// if (s.subscriberExecutionManagerTag!=null) tags.add(s.subscriberExecutionManagerTag); +// if (event.getSource()!=null) tags.add(BrooklynTaskTags.tagForContextEntity(event.getSource())); +// Map<String, Object> tagsMap = mapOf("tags", (Object)tags); + // use code above, instead of line below, if we want subscription deliveries associated with the entity; + // that will cause them to be cancelled when the entity is unmanaged + // (not sure that is useful, and likely NOT worth the expense, but it might be...) -Alex Oct 2014 + Map<String, Object> tagsMap = mapOf("tag", s.subscriberExecutionManagerTag); + + em.submit(tagsMap, new Runnable() { + @Override + public String toString() { + return "LSM.publish("+event+")"; + } + public void run() { + try { + sAtClosureCreation.listener.onEvent(event); + } catch (Throwable t) { + if (event!=null && event.getSource()!=null && Entities.isNoLongerManaged(event.getSource())) { + LOG.debug("Error processing subscriptions to "+this+", after entity unmanaged: "+t, t); + } else { + LOG.warn("Error processing subscriptions to "+this+": "+t, t); + } + } + }}); + totalEventsDeliveredCount.incrementAndGet(); + } + } + } + + @Override + public String toString() { + return tostring; + } + + /** + * Copied from LanguageUtils.groovy, to remove dependency. + * + * Adds the given value to a collection in the map under the key. + * + * A collection (as {@link LinkedHashMap}) will be created if necessary, + * synchronized on map for map access/change and set for addition there + * + * @return the updated set (instance, not copy) + * + * @deprecated since 0.5; use {@link HashMultimap}, and {@link Multimaps#synchronizedSetMultimap(com.google.common.collect.SetMultimap)} + */ + @Deprecated + private static <K,V> Set<V> addToMapOfSets(Map<K,Set<V>> map, K key, V valueInCollection) { + Set<V> coll; + synchronized (map) { + coll = map.get(key); + if (coll==null) { + coll = new LinkedHashSet<V>(); + map.put(key, coll); + } + if (coll.isEmpty()) { + synchronized (coll) { + coll.add(valueInCollection); + } + //if collection was empty then add to the collection while holding the map lock, to prevent removal + return coll; + } + } + synchronized (coll) { + if (!coll.isEmpty()) { + coll.add(valueInCollection); + return coll; + } + } + //if was empty, recurse, because someone else might be removing the collection + return addToMapOfSets(map, key, valueInCollection); + } + + /** + * Copied from LanguageUtils.groovy, to remove dependency. + * + * Removes the given value from a collection in the map under the key. + * + * @return the updated set (instance, not copy) + * + * @deprecated since 0.5; use {@link ArrayListMultimap} or {@link HashMultimap}, and {@link Multimaps#synchronizedListMultimap(com.google.common.collect.ListMultimap)} etc + */ + @Deprecated + private static <K,V> boolean removeFromMapOfCollections(Map<K,? extends Collection<V>> map, K key, V valueInCollection) { + Collection<V> coll; + synchronized (map) { + coll = map.get(key); + if (coll==null) return false; + } + boolean result; + synchronized (coll) { + result = coll.remove(valueInCollection); + } + if (coll.isEmpty()) { + synchronized (map) { + synchronized (coll) { + if (coll.isEmpty()) { + //only remove from the map if no one is adding to the collection or to the map, and the collection is still in the map + if (map.get(key)==coll) { + map.remove(key); + } + } + } + } + } + return result; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalUsageManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalUsageManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalUsageManager.java new file mode 100644 index 0000000..dc95d11 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocalUsageManager.java @@ -0,0 +1,430 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.management.internal; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.brooklyn.api.entity.Application; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.management.entitlement.EntitlementContext; +import org.apache.brooklyn.core.management.ManagementContextInjectable; +import org.apache.brooklyn.core.management.entitlement.Entitlements; +import org.apache.brooklyn.core.management.usage.ApplicationUsage; +import org.apache.brooklyn.core.management.usage.LocationUsage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.entity.basic.Entities; +import brooklyn.entity.basic.EntityInternal; +import brooklyn.entity.basic.Lifecycle; +import brooklyn.internal.storage.BrooklynStorage; + +import org.apache.brooklyn.location.basic.AbstractLocation; +import org.apache.brooklyn.location.basic.LocationConfigKeys; +import org.apache.brooklyn.location.basic.LocationInternal; + +import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.flags.TypeCoercions; +import brooklyn.util.javalang.Reflections; +import brooklyn.util.time.Duration; + +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; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +public class LocalUsageManager implements UsageManager { + + // TODO Threading model needs revisited. + // Synchronizes on updates to storage; but if two Brooklyn nodes were both writing to the same + // ApplicationUsage or LocationUsage record there'd be a race. That currently won't happen + // (at least for ApplicationUsage?) because the app is mastered in just one node at a time, + // and because location events are just manage/unmanage which should be happening in just + // one place at a time for a given location. + + private static final Logger log = LoggerFactory.getLogger(LocalUsageManager.class); + + private static class ApplicationMetadataImpl implements org.apache.brooklyn.core.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 org.apache.brooklyn.core.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, org.apache.brooklyn.core.management.internal.UsageListener.class, new Function<String, org.apache.brooklyn.core.management.internal.UsageListener>() { + @Override public org.apache.brooklyn.core.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()) { + if (result.get() instanceof org.apache.brooklyn.core.management.internal.UsageManager.UsageListener) { + return new org.apache.brooklyn.core.management.internal.UsageManager.UsageListener.UsageListenerAdapter((org.apache.brooklyn.core.management.internal.UsageManager.UsageListener) result.get()); + } else { + return (org.apache.brooklyn.core.management.internal.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"; + + @VisibleForTesting + public static final String LOCATION_USAGE_KEY = "usage-location"; + + private final LocalManagementContext managementContext; + + private final Object mutex = new Object(); + + private final List<org.apache.brooklyn.core.management.internal.UsageListener> listeners = Lists.newCopyOnWriteArrayList(); + + private final AtomicInteger listenerQueueSize = new AtomicInteger(); + + private ListeningExecutorService listenerExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(new ThreadFactoryBuilder() + .setNameFormat("brooklyn-usagemanager-listener-%d") + .build())); + + public LocalUsageManager(LocalManagementContext managementContext) { + this.managementContext = checkNotNull(managementContext, "managementContext"); + + // TODO Once org.apache.brooklyn.core.management.internal.UsageManager.UsageListener is deleted, restore this + // to normal generics! + Collection<?> listeners = managementContext.getBrooklynProperties().getConfig(UsageManager.USAGE_LISTENERS); + if (listeners != null) { + for (Object listener : listeners) { + if (listener instanceof ManagementContextInjectable) { + ((ManagementContextInjectable)listener).injectManagementContext(managementContext); + } + if (listener instanceof org.apache.brooklyn.core.management.internal.UsageManager.UsageListener) { + addUsageListener((org.apache.brooklyn.core.management.internal.UsageManager.UsageListener)listener); + } else if (listener instanceof org.apache.brooklyn.core.management.internal.UsageListener) { + addUsageListener((org.apache.brooklyn.core.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 "+org.apache.brooklyn.core.management.internal.UsageListener.class.getName()); + } + } + } + } + + public void terminate() { + // Wait for the listeners to finish + close the listeners + Duration timeout = managementContext.getBrooklynProperties().getConfig(UsageManager.USAGE_LISTENER_TERMINATION_TIMEOUT); + if (listenerQueueSize.get() > 0) { + log.info("Usage manager waiting for "+listenerQueueSize+" listener events for up to "+timeout); + } + List<ListenableFuture<?>> futures = Lists.newArrayList(); + for (final org.apache.brooklyn.core.management.internal.UsageListener listener : listeners) { + ListenableFuture<?> future = listenerExecutor.submit(new Runnable() { + public void run() { + if (listener instanceof Closeable) { + try { + ((Closeable)listener).close(); + } catch (IOException e) { + log.warn("Problem closing usage listener "+listener+" (continuing)", e); + } + } + }}); + futures.add(future); + } + try { + Futures.successfulAsList(futures).get(timeout.toMilliseconds(), TimeUnit.MILLISECONDS); + } catch (Exception e) { + Exceptions.propagateIfFatal(e); + log.warn("Problem terminiating usage listeners (continuing)", e); + } finally { + listenerExecutor.shutdownNow(); + } + } + + private void execOnListeners(final Function<org.apache.brooklyn.core.management.internal.UsageListener, Void> job) { + for (final org.apache.brooklyn.core.management.internal.UsageListener listener : listeners) { + listenerQueueSize.incrementAndGet(); + listenerExecutor.execute(new Runnable() { + public void run() { + try { + job.apply(listener); + } catch (RuntimeException e) { + log.error("Problem notifying listener "+listener+" of "+job, e); + Exceptions.propagateIfFatal(e); + } finally { + listenerQueueSize.decrementAndGet(); + } + }}); + } + } + + @Override + public void recordApplicationEvent(final Application app, final Lifecycle state) { + log.debug("Storing application lifecycle usage event: application {} in state {}", new Object[] {app, state}); + ConcurrentMap<String, ApplicationUsage> eventMap = managementContext.getStorage().getMap(APPLICATION_USAGE_KEY); + synchronized (mutex) { + ApplicationUsage usage = eventMap.get(app.getId()); + if (usage == null) { + usage = new ApplicationUsage(app.getId(), app.getDisplayName(), app.getEntityType().getName(), ((EntityInternal)app).toMetadataRecord()); + } + final ApplicationUsage.ApplicationEvent event = new ApplicationUsage.ApplicationEvent(state, getUser()); + usage.addEvent(event); + eventMap.put(app.getId(), usage); + + execOnListeners(new Function<org.apache.brooklyn.core.management.internal.UsageListener, Void>() { + public Void apply(org.apache.brooklyn.core.management.internal.UsageListener listener) { + listener.onApplicationEvent(new ApplicationMetadataImpl(Entities.proxy(app)), event); + return null; + } + public String toString() { + return "applicationEvent("+app+", "+state+")"; + }}); + } + } + + /** + * Adds this location event to the usage record for the given location (creating the usage + * record if one does not already exist). + */ + @Override + public void recordLocationEvent(final Location loc, final Lifecycle state) { + // TODO This approach (i.e. recording events on manage/unmanage would not work for + // locations that are reused. For example, in a FixedListMachineProvisioningLocation + // the ssh machine location is returned to the pool and handed back out again. + // But maybe the solution there is to hand out different instances so that one user + // can't change the config of the SshMachineLocation to subsequently affect the next + // user. + // + // TODO Should perhaps extract the location storage methods into their own class, + // but no strong enough feelings yet... + + checkNotNull(loc, "location"); + if (loc.getConfig(AbstractLocation.TEMPORARY_LOCATION)) { + log.info("Ignoring location lifecycle usage event for {} (state {}), because location is a temporary location", loc, state); + return; + } + checkNotNull(state, "state of location %s", loc); + if (loc.getId() == null) { + log.error("Ignoring location lifecycle usage event for {} (state {}), because location has no id", loc, state); + return; + } + if (managementContext.getStorage() == null) { + log.warn("Cannot store location lifecycle usage event for {} (state {}), because storage not available", loc, state); + return; + } + + Object callerContext = loc.getConfig(LocationConfigKeys.CALLER_CONTEXT); + + if (callerContext != null && callerContext instanceof Entity) { + log.debug("Storing location lifecycle usage event: location {} in state {}; caller context {}", new Object[] {loc, state, callerContext}); + + Entity caller = (Entity) callerContext; + String entityTypeName = caller.getEntityType().getName(); + String appId = caller.getApplicationId(); + + final LocationUsage.LocationEvent event = new LocationUsage.LocationEvent(state, caller.getId(), entityTypeName, appId, getUser()); + + ConcurrentMap<String, LocationUsage> usageMap = managementContext.getStorage().<String, LocationUsage>getMap(LOCATION_USAGE_KEY); + synchronized (mutex) { + LocationUsage usage = usageMap.get(loc.getId()); + if (usage == null) { + usage = new LocationUsage(loc.getId(), ((LocationInternal)loc).toMetadataRecord()); + } + usage.addEvent(event); + usageMap.put(loc.getId(), usage); + + execOnListeners(new Function<org.apache.brooklyn.core.management.internal.UsageListener, Void>() { + public Void apply(org.apache.brooklyn.core.management.internal.UsageListener listener) { + listener.onLocationEvent(new LocationMetadataImpl(loc), event); + return null; + } + public String toString() { + return "locationEvent("+loc+", "+state+")"; + }}); + } + } else { + // normal for high-level locations + log.trace("Not recording location lifecycle usage event for {} in state {}, because no caller context", new Object[] {loc, state}); + } + } + + /** + * Returns the usage info for the location with the given id, or null if unknown. + */ + @Override + public LocationUsage getLocationUsage(String locationId) { + BrooklynStorage storage = managementContext.getStorage(); + + Map<String, LocationUsage> usageMap = storage.getMap(LOCATION_USAGE_KEY); + return usageMap.get(locationId); + } + + /** + * Returns the usage info that matches the given predicate. + * For example, could be used to find locations used within a given time period. + */ + @Override + public Set<LocationUsage> getLocationUsage(Predicate<? super LocationUsage> filter) { + // TODO could do more efficient indexing, to more easily find locations in use during a given period. + // But this is good enough for first-pass. + + Map<String, LocationUsage> usageMap = managementContext.getStorage().getMap(LOCATION_USAGE_KEY); + Set<LocationUsage> result = Sets.newLinkedHashSet(); + + for (LocationUsage usage : usageMap.values()) { + if (filter.apply(usage)) { + result.add(usage); + } + } + return result; + } + + /** + * Returns the usage info for the location with the given id, or null if unknown. + */ + @Override + public ApplicationUsage getApplicationUsage(String appId) { + BrooklynStorage storage = managementContext.getStorage(); + + Map<String, ApplicationUsage> usageMap = storage.getMap(APPLICATION_USAGE_KEY); + return usageMap.get(appId); + } + + /** + * Returns the usage info that matches the given predicate. + * For example, could be used to find applications used within a given time period. + */ + @Override + public Set<ApplicationUsage> getApplicationUsage(Predicate<? super ApplicationUsage> filter) { + // TODO could do more efficient indexing, to more easily find locations in use during a given period. + // But this is good enough for first-pass. + + Map<String, ApplicationUsage> usageMap = managementContext.getStorage().getMap(APPLICATION_USAGE_KEY); + Set<ApplicationUsage> result = Sets.newLinkedHashSet(); + + for (ApplicationUsage usage : usageMap.values()) { + if (filter.apply(usage)) { + result.add(usage); + } + } + return result; + } + + @Override + @Deprecated + public void addUsageListener(org.apache.brooklyn.core.management.internal.UsageManager.UsageListener listener) { + addUsageListener(new org.apache.brooklyn.core.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener)); + } + + @Override + @Deprecated + public void removeUsageListener(org.apache.brooklyn.core.management.internal.UsageManager.UsageListener listener) { + removeUsageListener(new org.apache.brooklyn.core.management.internal.UsageManager.UsageListener.UsageListenerAdapter(listener)); + } + + @Override + public void addUsageListener(org.apache.brooklyn.core.management.internal.UsageListener listener) { + listeners.add(listener); + } + + @Override + public void removeUsageListener(org.apache.brooklyn.core.management.internal.UsageListener listener) { + listeners.remove(listener); + } + + private String getUser() { + EntitlementContext entitlementContext = Entitlements.getEntitlementContext(); + if (entitlementContext != null) { + return entitlementContext.user(); + } + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/LocationManagerInternal.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/LocationManagerInternal.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocationManagerInternal.java new file mode 100644 index 0000000..ba426ec --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/LocationManagerInternal.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.management.internal; + +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.management.LocationManager; + +public interface LocationManagerInternal extends LocationManager, BrooklynObjectManagerInternal<Location> { + + public Iterable<String> getLocationIds(); + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementContextInternal.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementContextInternal.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementContextInternal.java new file mode 100644 index 0000000..97a3d73 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementContextInternal.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.management.internal; + +import java.net.URI; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import org.apache.brooklyn.api.entity.Effector; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.management.ManagementContext; +import org.apache.brooklyn.api.management.Task; +import org.apache.brooklyn.core.management.ha.OsgiManager; + +import brooklyn.catalog.internal.CatalogInitialization; +import brooklyn.config.BrooklynProperties; +import brooklyn.entity.basic.BrooklynTaskTags; +import brooklyn.entity.proxying.InternalEntityFactory; +import brooklyn.entity.proxying.InternalLocationFactory; +import brooklyn.entity.proxying.InternalPolicyFactory; +import brooklyn.internal.storage.BrooklynStorage; +import brooklyn.util.guava.Maybe; +import brooklyn.util.task.TaskTags; + +import com.google.common.annotations.Beta; + +public interface ManagementContextInternal extends ManagementContext { + + public static final String SUB_TASK_TAG = TaskTags.SUB_TASK_TAG; + + public static final String EFFECTOR_TAG = BrooklynTaskTags.EFFECTOR_TAG; + public static final String NON_TRANSIENT_TASK_TAG = BrooklynTaskTags.NON_TRANSIENT_TASK_TAG; + public static final String TRANSIENT_TASK_TAG = BrooklynTaskTags.TRANSIENT_TASK_TAG; + + public static final String EMPTY_CATALOG_URL = "classpath://brooklyn/empty.catalog.bom"; + + ClassLoader getBaseClassLoader(); + + Iterable<URL> getBaseClassPathForScanning(); + + void setBaseClassPathForScanning(Iterable<URL> urls); + + void setManagementNodeUri(URI uri); + + void addEntitySetListener(CollectionChangeListener<Entity> listener); + + void removeEntitySetListener(CollectionChangeListener<Entity> listener); + + void terminate(); + + long getTotalEffectorInvocations(); + + <T> T invokeEffectorMethodSync(final Entity entity, final Effector<T> eff, final Object args) throws ExecutionException; + + <T> Task<T> invokeEffector(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters); + + BrooklynStorage getStorage(); + + BrooklynProperties getBrooklynProperties(); + + AccessManager getAccessManager(); + + UsageManager getUsageManager(); + + /** + * @return The OSGi manager, if available; may be absent if OSGi is not supported, + * e.g. in test contexts (but will be supported in all major contexts). + */ + Maybe<OsgiManager> getOsgiManager(); + + InternalEntityFactory getEntityFactory(); + + InternalLocationFactory getLocationFactory(); + + InternalPolicyFactory getPolicyFactory(); + + /** + * Registers an entity that has been created, but that has not yet begun to be managed. + * <p> + * This differs from the idea of "preManaged" where the entities are in the process of being + * managed, but where management is not yet complete. + */ + // TODO would benefit from better naming! The name has percolated up from LocalEntityManager. + // should we just rename here as register or preManage? + void prePreManage(Entity entity); + + /** + * Registers a location that has been created, but that has not yet begun to be managed. + */ + void prePreManage(Location location); + + /** Object which allows adding, removing, and clearing errors. + * TODO In future this will change to a custom interface with a unique identifier for each error. */ + @Beta + List<Throwable> errors(); + + @Beta + CatalogInitialization getCatalogInitialization(); + + @Beta + void setCatalogInitialization(CatalogInitialization catalogInitialization); + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionInfo.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionInfo.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionInfo.java new file mode 100644 index 0000000..2d944d9 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionInfo.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.management.internal; + +import org.apache.brooklyn.api.management.ManagementContext; + +/** Stores a management transition mode, and the management context. */ +// TODO does this class really pull its weight? +public class ManagementTransitionInfo { + + final ManagementContext mgmtContext; + final ManagementTransitionMode mode; + + public ManagementTransitionInfo(ManagementContext mgmtContext, ManagementTransitionMode mode) { + this.mgmtContext = mgmtContext; + this.mode = mode; + } + + + public ManagementContext getManagementContext() { + return mgmtContext; + } + + public ManagementTransitionMode getMode() { + return mode; + } + + @Override + public String toString() { + return super.toString()+"["+mgmtContext+";"+mode+"]"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionMode.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionMode.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionMode.java new file mode 100644 index 0000000..3adc625 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/ManagementTransitionMode.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.management.internal; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Preconditions; + +/** + * Records details of a management transition, specifically the {@link BrooklynObjectManagementMode} before and after, + * and allows easy checking of various aspects of that. + * <p> + * This helps make code readable and keep correct logic if we expand/change the management modes. + */ +public class ManagementTransitionMode { + + private static final Logger log = LoggerFactory.getLogger(ManagementTransitionMode.class); + + private final BrooklynObjectManagementMode modeBefore, modeAfter; + + private ManagementTransitionMode(BrooklynObjectManagementMode modeBefore, BrooklynObjectManagementMode modeAfter) { + this.modeBefore = modeBefore; + this.modeAfter = modeAfter; + } + + public static ManagementTransitionMode transitioning(BrooklynObjectManagementMode modeBefore, BrooklynObjectManagementMode modeAfter) { + return new ManagementTransitionMode(Preconditions.checkNotNull(modeBefore, "modeBefore"), Preconditions.checkNotNull(modeAfter, "modeAfter")); + } + + @Deprecated /** @deprecated marking places where we aren't sure */ + public static ManagementTransitionMode guessing(BrooklynObjectManagementMode modeBefore, BrooklynObjectManagementMode modeAfter) { + return transitioning(modeBefore, modeAfter); + } + + /** @return the mode this object was previously managed as */ + public BrooklynObjectManagementMode getModeBefore() { + return modeBefore; + } + + /** @return the mode this object is now being managed as */ + public BrooklynObjectManagementMode getModeAfter() { + return modeAfter; + } + + /** This management node was previously not loaded here, + * either it did not exist (and is just being created) or it was in persisted state but + * not loaded at this node. */ + public boolean wasNotLoaded() { + return getModeBefore()==BrooklynObjectManagementMode.NONEXISTENT || getModeBefore()==BrooklynObjectManagementMode.UNMANAGED_PERSISTED; + } + + /** This management node is now not going to be loaded here, either it is being destroyed + * (not known anywhere, not even persisted) or simply forgotten here */ + public boolean isNoLongerLoaded() { + return getModeAfter()==BrooklynObjectManagementMode.NONEXISTENT || getModeAfter()==BrooklynObjectManagementMode.UNMANAGED_PERSISTED; + } + + /** This management node was the master for the given object */ + public boolean wasPrimary() { + return getModeBefore()==BrooklynObjectManagementMode.MANAGED_PRIMARY; + } + + /** This management node is now the master for the given object */ + public boolean isPrimary() { + return getModeAfter()==BrooklynObjectManagementMode.MANAGED_PRIMARY; + } + + /** Object was previously loaded as read-only at this management node; + * active management was occurring elsewhere (or not at all) + */ + public boolean wasReadOnly() { + return getModeBefore()==BrooklynObjectManagementMode.LOADED_READ_ONLY; + } + + /** Object is now being loaded as read-only at this management node; + * expect active management to be occurring elsewhere + */ + public boolean isReadOnly() { + return getModeAfter()==BrooklynObjectManagementMode.LOADED_READ_ONLY; + } + + /** Object is being created: + * previously did not exist (not even in persisted state); + * implies that we are the active manager creating it, + * i.e. {@link #getModeAfter()} should indicate {@link BrooklynObjectManagementMode#MANAGED_PRIMARY}. + * (if we're read-only and the manager has just created it, + * {@link #getModeBefore()} should indicate {@link BrooklynObjectManagementMode#UNMANAGED_PERSISTED}) + */ + public boolean isCreating() { + if (getModeBefore()!=BrooklynObjectManagementMode.NONEXISTENT) + return false; + + if (getModeAfter()==BrooklynObjectManagementMode.LOADED_READ_ONLY) { + log.warn("isCreating set on RO object; highly irregular!"); + } + return true; + } + + /** Object is being destroyed: + * either destroyed elsewhere and we're catching up (in read-only mode), + * or we've been the active manager and are destroying it */ + public boolean isDestroying() { + return getModeAfter()==BrooklynObjectManagementMode.NONEXISTENT; + } + + @Override + public String toString() { + return ManagementTransitionMode.class.getSimpleName()+"["+getModeBefore()+"->"+getModeAfter()+"]"; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentAccessManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentAccessManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentAccessManager.java new file mode 100644 index 0000000..1d94f02 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentAccessManager.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.management.internal; + +import org.apache.brooklyn.api.management.AccessController; + + +public class NonDeploymentAccessManager implements AccessManager { + + private final ManagementContextInternal initialManagementContext; + + public NonDeploymentAccessManager(ManagementContextInternal initialManagementContext) { + this.initialManagementContext = initialManagementContext; + } + + private boolean isInitialManagementContextReal() { + return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext)); + } + + @Override + public AccessController getAccessController() { + if (isInitialManagementContextReal()) { + return initialManagementContext.getAccessManager().getAccessController(); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } + + @Override + public boolean isLocationProvisioningAllowed() { + if (isInitialManagementContextReal()) { + return initialManagementContext.getAccessManager().isLocationProvisioningAllowed(); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } + + @Override + public boolean isLocationManagementAllowed() { + if (isInitialManagementContextReal()) { + return initialManagementContext.getAccessManager().isLocationManagementAllowed(); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } + + @Override + public boolean isEntityManagementAllowed() { + if (isInitialManagementContextReal()) { + return initialManagementContext.getAccessManager().isEntityManagementAllowed(); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } + + @Override + public void setLocationProvisioningAllowed(boolean allowed) { + if (isInitialManagementContextReal()) { + initialManagementContext.getAccessManager().setLocationProvisioningAllowed(allowed); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } + + @Override + public void setLocationManagementAllowed(boolean allowed) { + if (isInitialManagementContextReal()) { + initialManagementContext.getAccessManager().setLocationManagementAllowed(allowed); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } + + @Override + public void setEntityManagementAllowed(boolean allowed) { + if (isInitialManagementContextReal()) { + initialManagementContext.getAccessManager().setEntityManagementAllowed(allowed); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentEntityManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentEntityManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentEntityManager.java new file mode 100644 index 0000000..658bbf7 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentEntityManager.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.management.internal; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.apache.brooklyn.api.entity.Application; +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.proxying.EntitySpec; +import org.apache.brooklyn.api.entity.proxying.EntityTypeRegistry; +import org.apache.brooklyn.api.management.ManagementContext; +import org.apache.brooklyn.api.policy.Enricher; +import org.apache.brooklyn.api.policy.EnricherSpec; +import org.apache.brooklyn.api.policy.Policy; +import org.apache.brooklyn.api.policy.PolicySpec; + +import com.google.common.base.Predicate; + +public class NonDeploymentEntityManager implements EntityManagerInternal { + + private final ManagementContext initialManagementContext; + + public NonDeploymentEntityManager(ManagementContext initialManagementContext) { + this.initialManagementContext = initialManagementContext; + } + + @Override + public EntityTypeRegistry getEntityTypeRegistry() { + if (isInitialManagementContextReal()) { + return initialManagementContext.getEntityManager().getEntityTypeRegistry(); + } else { + throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation."); + } + } + + @Override + public <T extends Entity> T createEntity(EntitySpec<T> spec) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getEntityManager().createEntity(spec); + } else { + throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation."); + } + } + + @Override + public <T extends Entity> T createEntity(Map<?,?> config, Class<T> type) { + return createEntity(EntitySpec.create(type).configure(config)); + } + + @Override + public <T extends Policy> T createPolicy(PolicySpec<T> spec) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getEntityManager().createPolicy(spec); + } else { + throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation."); + } + } + + @Override + public <T extends Enricher> T createEnricher(EnricherSpec<T> spec) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getEntityManager().createEnricher(spec); + } else { + throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation."); + } + } + + @Override + public Collection<Entity> getEntities() { + if (isInitialManagementContextReal()) { + return initialManagementContext.getEntityManager().getEntities(); + } else { + return Collections.emptyList(); + } + } + + @Override + public Collection<Entity> getEntitiesInApplication(Application application) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getEntityManager().getEntitiesInApplication(application); + } else { + return Collections.emptyList(); + } + } + + @Override + public Collection<Entity> findEntities(Predicate<? super Entity> filter) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getEntityManager().findEntities(filter); + } else { + return Collections.emptyList(); + } + } + + @Override + public Collection<Entity> findEntitiesInApplication(Application application, Predicate<? super Entity> filter) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getEntityManager().findEntitiesInApplication(application, filter); + } else { + return Collections.emptyList(); + } + } + + @Override + public Entity getEntity(String id) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getEntityManager().getEntity(id); + } else { + return null; + } + } + + @Override + public Iterable<String> getEntityIds() { + if (isInitialManagementContextReal()) { + return ((EntityManagerInternal)initialManagementContext.getEntityManager()).getEntityIds(); + } else { + return Collections.emptyList(); + } + } + + @Override + public ManagementTransitionMode getLastManagementTransitionMode(String itemId) { + if (isInitialManagementContextReal()) { + return ((EntityManagerInternal)initialManagementContext.getEntityManager()).getLastManagementTransitionMode(itemId); + } else { + return null; + } + } + + @Override + public void setManagementTransitionMode(Entity item, ManagementTransitionMode mode) { + if (isInitialManagementContextReal()) { + ((EntityManagerInternal)initialManagementContext.getEntityManager()).setManagementTransitionMode(item, mode); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } + + @Override + public boolean isManaged(Entity entity) { + return false; + } + + @Override + public void manage(Entity e) { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot manage "+e); + } + + @Override + public void unmanage(Entity e, ManagementTransitionMode info) { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + + @Override + public void manageRebindedRoot(Entity item) { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + + @Override + public void unmanage(Entity e) { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot unmanage "+e); + } + + private boolean isInitialManagementContextReal() { + return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext)); + } + + @Override + public Iterable<Entity> getAllEntitiesInApplication(Application application) { + if (isInitialManagementContextReal()) { + return ((EntityManagerInternal)initialManagementContext.getEntityManager()).getAllEntitiesInApplication(application); + } else { + throw new IllegalStateException("Non-deployment context "+this+" (with no initial management context supplied) is not valid for this operation."); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/6602f694/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentLocationManager.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentLocationManager.java b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentLocationManager.java new file mode 100644 index 0000000..5c81648 --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/core/management/internal/NonDeploymentLocationManager.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.core.management.internal; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.apache.brooklyn.api.location.Location; +import org.apache.brooklyn.api.location.LocationSpec; +import org.apache.brooklyn.api.management.ManagementContext; + +public class NonDeploymentLocationManager implements LocationManagerInternal { + + private final ManagementContext initialManagementContext; + + public NonDeploymentLocationManager(ManagementContext initialManagementContext) { + this.initialManagementContext = initialManagementContext; + } + + @Override + public <T extends Location> T createLocation(LocationSpec<T> spec) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getLocationManager().createLocation(spec); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot create "+spec); + } + } + + @Override + public <T extends Location> T createLocation(Map<?, ?> config, Class<T> type) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getLocationManager().createLocation(config, type); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot create "+type); + } + } + + @Override + public Collection<Location> getLocations() { + if (isInitialManagementContextReal()) { + return initialManagementContext.getLocationManager().getLocations(); + } else { + return Collections.emptyList(); + } + } + + @Override + public Location getLocation(String id) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getLocationManager().getLocation(id); + } else { + return null; + } + } + + @Override + public Iterable<String> getLocationIds() { + if (isInitialManagementContextReal()) { + return ((LocationManagerInternal)initialManagementContext.getLocationManager()).getLocationIds(); + } else { + return Collections.emptyList(); + } + } + + @Override + public boolean isManaged(Location loc) { + return false; + } + + @Override + public void manageRebindedRoot(Location loc) { + if (isInitialManagementContextReal()) { + ((LocationManagerInternal)initialManagementContext.getLocationManager()).manageRebindedRoot(loc); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot manage "+loc); + } + } + + @Override + @Deprecated + public Location manage(Location loc) { + if (isInitialManagementContextReal()) { + return initialManagementContext.getLocationManager().manage(loc); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot manage "+loc); + } + } + + + @Override + public ManagementTransitionMode getLastManagementTransitionMode(String itemId) { + if (isInitialManagementContextReal()) { + return ((LocationManagerInternal)initialManagementContext.getLocationManager()).getLastManagementTransitionMode(itemId); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } + + @Override + public void setManagementTransitionMode(Location item, ManagementTransitionMode mode) { + if (isInitialManagementContextReal()) { + ((LocationManagerInternal)initialManagementContext.getLocationManager()).setManagementTransitionMode(item, mode); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } + + @Override + public void unmanage(Location item, ManagementTransitionMode info) { + if (isInitialManagementContextReal()) { + ((LocationManagerInternal)initialManagementContext.getLocationManager()).unmanage(item, info); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation"); + } + } + + @Override + public void unmanage(Location loc) { + if (isInitialManagementContextReal()) { + initialManagementContext.getLocationManager().unmanage(loc); + } else { + throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot unmanage "+loc); + } + } + + private boolean isInitialManagementContextReal() { + return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext)); + } +}
