/* * 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.mgmt.internal; import static com.google.common.base.Preconditions.checkNotNull; import java.net.URI; import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.apache.brooklyn.api.catalog.BrooklynCatalog; import org.apache.brooklyn.api.effector.Effector; import org.apache.brooklyn.api.entity.Application; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.drivers.EntityDriverManager; import org.apache.brooklyn.api.entity.drivers.downloads.DownloadResolverManager; import org.apache.brooklyn.api.location.Location; import org.apache.brooklyn.api.location.LocationRegistry; import org.apache.brooklyn.api.mgmt.AccessController; import org.apache.brooklyn.api.mgmt.EntityManager; import org.apache.brooklyn.api.mgmt.ExecutionContext; import org.apache.brooklyn.api.mgmt.ExecutionManager; import org.apache.brooklyn.api.mgmt.LocationManager; import org.apache.brooklyn.api.mgmt.SubscriptionContext; import org.apache.brooklyn.api.mgmt.Task; import org.apache.brooklyn.api.mgmt.entitlement.EntitlementManager; import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityManager; import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode; import org.apache.brooklyn.api.mgmt.ha.ManagementNodeState; import org.apache.brooklyn.api.mgmt.ha.ManagementPlaneSyncRecord; import org.apache.brooklyn.api.mgmt.ha.ManagementPlaneSyncRecordPersister; import org.apache.brooklyn.api.mgmt.rebind.ChangeListener; import org.apache.brooklyn.api.mgmt.rebind.PersistenceExceptionHandler; import org.apache.brooklyn.api.mgmt.rebind.RebindExceptionHandler; import org.apache.brooklyn.api.mgmt.rebind.RebindManager; import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoPersister; import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoRawData; import org.apache.brooklyn.api.objs.BrooklynObject; import org.apache.brooklyn.api.typereg.BrooklynTypeRegistry; import org.apache.brooklyn.config.StringConfigMap; import org.apache.brooklyn.core.catalog.internal.CatalogInitialization; import org.apache.brooklyn.core.entity.AbstractEntity; import org.apache.brooklyn.core.internal.BrooklynProperties; import org.apache.brooklyn.core.internal.storage.BrooklynStorage; import org.apache.brooklyn.core.mgmt.ha.OsgiManager; import org.apache.brooklyn.core.mgmt.usage.UsageManager; import org.apache.brooklyn.core.objs.proxy.InternalEntityFactory; import org.apache.brooklyn.core.objs.proxy.InternalLocationFactory; import org.apache.brooklyn.core.objs.proxy.InternalPolicyFactory; import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.time.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Objects; public class NonDeploymentManagementContext implements ManagementContextInternal { private static final Logger log = LoggerFactory.getLogger(NonDeploymentManagementContext.class); public enum NonDeploymentManagementContextMode { PRE_MANAGEMENT, MANAGEMENT_REBINDING, MANAGEMENT_STARTING, MANAGEMENT_STARTED, MANAGEMENT_STOPPING, MANAGEMENT_STOPPED; public boolean isPreManaged() { return this == PRE_MANAGEMENT || this == MANAGEMENT_REBINDING; } } private final AbstractEntity entity; private NonDeploymentManagementContextMode mode; private ManagementContextInternal initialManagementContext; private final QueueingSubscriptionManager qsm; private final BasicSubscriptionContext subscriptionContext; private NonDeploymentEntityManager entityManager; private NonDeploymentLocationManager locationManager; private NonDeploymentAccessManager accessManager; private NonDeploymentUsageManager usageManager; public NonDeploymentManagementContext(AbstractEntity entity, NonDeploymentManagementContextMode mode) { this.entity = checkNotNull(entity, "entity"); this.mode = checkNotNull(mode, "mode"); qsm = new QueueingSubscriptionManager(); subscriptionContext = new BasicSubscriptionContext(qsm, entity); entityManager = new NonDeploymentEntityManager(null); locationManager = new NonDeploymentLocationManager(null); accessManager = new NonDeploymentAccessManager(null); usageManager = new NonDeploymentUsageManager(null); } @Override public String getManagementPlaneId() { return (initialManagementContext == null) ? null : initialManagementContext.getManagementPlaneId(); } @Override public String getManagementNodeId() { return (initialManagementContext == null) ? null : initialManagementContext.getManagementNodeId(); } @Override public Maybe<URI> getManagementNodeUri() { return (initialManagementContext == null) ? Maybe.<URI>absent() : initialManagementContext.getManagementNodeUri(); } public void setManagementContext(ManagementContextInternal val) { this.initialManagementContext = checkNotNull(val, "initialManagementContext"); this.entityManager = new NonDeploymentEntityManager(val); this.locationManager = new NonDeploymentLocationManager(val); this.accessManager = new NonDeploymentAccessManager(val); this.usageManager = new NonDeploymentUsageManager(val); } @Override public String toString() { return Objects.toStringHelper(this).add("entity", entity.getId()).add("mode", mode).toString(); } public void setMode(NonDeploymentManagementContextMode mode) { this.mode = checkNotNull(mode, "mode"); } public NonDeploymentManagementContextMode getMode() { return mode; } @Override public Collection<Application> getApplications() { return Collections.emptyList(); } @Override public boolean isRunning() { // Assume that the real management context has not been terminated, so always true return true; } @Override public boolean isStartupComplete() { // This mgmt context is only used by items who are not yet fully started. // It's slightly misleading as this does not refer to the main mgmt context. // OTOH it probably won't be used. TBC. -Alex, Apr 2015 return false; } @Override public InternalEntityFactory getEntityFactory() { checkInitialManagementContextReal(); return initialManagementContext.getEntityFactory(); } @Override public InternalLocationFactory getLocationFactory() { checkInitialManagementContextReal(); return initialManagementContext.getLocationFactory(); } @Override public InternalPolicyFactory getPolicyFactory() { checkInitialManagementContextReal(); return initialManagementContext.getPolicyFactory(); } @Override public EntityManager getEntityManager() { return entityManager; } @Override public LocationManager getLocationManager() { return locationManager; } @Override public AccessManager getAccessManager() { return accessManager; } @Override public UsageManager getUsageManager() { return usageManager; } @Override public Maybe<OsgiManager> getOsgiManager() { switch (mode) { case PRE_MANAGEMENT: case MANAGEMENT_STARTING: case MANAGEMENT_STARTED: checkInitialManagementContextReal(); return initialManagementContext.getOsgiManager(); default: return Maybe.absent("Entity " + entity + " is no longer managed; OSGi context no longer available"); } } @Override public AccessController getAccessController() { return getAccessManager().getAccessController(); } @Override public ExecutionManager getExecutionManager() { checkInitialManagementContextReal(); return initialManagementContext.getExecutionManager(); } @Override public QueueingSubscriptionManager getSubscriptionManager() { return qsm; } @Override public synchronized SubscriptionContext getSubscriptionContext(Entity entity) { if (!this.entity.equals(entity)) throw new IllegalStateException("Non-deployment context "+this+" can only use a single Entity: has "+this.entity+", but passed "+entity); if (mode==NonDeploymentManagementContextMode.MANAGEMENT_STOPPED) throw new IllegalStateException("Entity "+entity+" is no longer managed; subscription context not available"); return subscriptionContext; } @Override public synchronized SubscriptionContext getSubscriptionContext(Location loc) { // Should never be called; the NonDeploymentManagementContext is associated with a particular entity, whereas // the #getSubscriptionContext(loc) should only be called in the context of a location. throw new UnsupportedOperationException(); } @Override public ExecutionContext getExecutionContext(Entity entity) { if (!this.entity.equals(entity)) throw new IllegalStateException("Non-deployment context "+this+" can only use a single Entity: has "+this.entity+", but passed "+entity); if (mode==NonDeploymentManagementContextMode.MANAGEMENT_STOPPED) throw new IllegalStateException("Entity "+entity+" is no longer managed; execution context not available"); checkInitialManagementContextReal(); return initialManagementContext.getExecutionContext(entity); } @Override public ExecutionContext getServerExecutionContext() { return initialManagementContext.getServerExecutionContext(); } // TODO the methods below should delegate to the application? @Override public EntityDriverManager getEntityDriverManager() { checkInitialManagementContextReal(); return initialManagementContext.getEntityDriverManager(); } @Override public DownloadResolverManager getEntityDownloadsManager() { checkInitialManagementContextReal(); return initialManagementContext.getEntityDownloadsManager(); } @Override public StringConfigMap getConfig() { checkInitialManagementContextReal(); return initialManagementContext.getConfig(); } @Override public BrooklynProperties getBrooklynProperties() { checkInitialManagementContextReal(); return initialManagementContext.getBrooklynProperties(); } @Override public BrooklynStorage getStorage() { checkInitialManagementContextReal(); return initialManagementContext.getStorage(); } @Override public RebindManager getRebindManager() { // There was a race where EffectorUtils on invoking an effector calls: // mgmtSupport.getEntityChangeListener().onEffectorCompleted(eff); // but where the entity/app may be being unmanaged concurrently (e.g. calling app.stop()). // So now we allow the change-listener to be called. if (isInitialManagementContextReal()) { return initialManagementContext.getRebindManager(); } else { return new NonDeploymentRebindManager(); } } @Override public HighAvailabilityManager getHighAvailabilityManager() { if (isInitialManagementContextReal()) { return initialManagementContext.getHighAvailabilityManager(); } else { return new NonDeploymentHighAvailabilityManager(); } } @Override public LocationRegistry getLocationRegistry() { checkInitialManagementContextReal(); return initialManagementContext.getLocationRegistry(); } @Override public BrooklynCatalog getCatalog() { checkInitialManagementContextReal(); return initialManagementContext.getCatalog(); } @Override public BrooklynTypeRegistry getTypeRegistry() { checkInitialManagementContextReal(); return initialManagementContext.getTypeRegistry(); } @Override public ClassLoader getCatalogClassLoader() { checkInitialManagementContextReal(); return initialManagementContext.getCatalogClassLoader(); } @Override public EntitlementManager getEntitlementManager() { checkInitialManagementContextReal(); return initialManagementContext.getEntitlementManager(); } @Override public <T> T invokeEffectorMethodSync(final Entity entity, final Effector<T> eff, final Object args) throws ExecutionException { throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot invoke effector "+eff+" on entity "+entity); } @Override public <T> Task<T> invokeEffector(final Entity entity, final Effector<T> eff, @SuppressWarnings("rawtypes") final Map parameters) { throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: cannot invoke effector "+eff+" on entity "+entity); } @Override public ClassLoader getBaseClassLoader() { checkInitialManagementContextReal(); return initialManagementContext.getBaseClassLoader(); } @Override public Iterable<URL> getBaseClassPathForScanning() { checkInitialManagementContextReal(); return initialManagementContext.getBaseClassPathForScanning(); } @Override public void addEntitySetListener(CollectionChangeListener<Entity> listener) { checkInitialManagementContextReal(); initialManagementContext.addEntitySetListener(listener); } @Override public void removeEntitySetListener(CollectionChangeListener<Entity> listener) { checkInitialManagementContextReal(); initialManagementContext.removeEntitySetListener(listener); } @Override public void terminate() { if (isInitialManagementContextReal()) { initialManagementContext.terminate(); } else { // no-op; the non-deployment management context has nothing needing terminated } } @Override public long getTotalEffectorInvocations() { if (isInitialManagementContextReal()) { return initialManagementContext.getTotalEffectorInvocations(); } else { return 0; } } @Override public void setBaseClassPathForScanning(Iterable<URL> urls) { checkInitialManagementContextReal(); initialManagementContext.setBaseClassPathForScanning(urls); } @Override public void setManagementNodeUri(URI uri) { checkInitialManagementContextReal(); initialManagementContext.setManagementNodeUri(uri); } @Override public void prePreManage(Entity entity) { // should throw? but in 0.7.0-SNAPSHOT it was no-op log.warn("Ignoring call to prePreManage("+entity+") on "+this); } @Override public void prePreManage(Location location) { // should throw? but in 0.7.0-SNAPSHOT it was no-op log.warn("Ignoring call to prePreManage("+location+") on "+this); } private boolean isInitialManagementContextReal() { return (initialManagementContext != null && !(initialManagementContext instanceof NonDeploymentManagementContext)); } private void checkInitialManagementContextReal() { if (!isInitialManagementContextReal()) { throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation."); } } @Override public void reloadBrooklynProperties() { checkInitialManagementContextReal(); initialManagementContext.reloadBrooklynProperties(); } @Override public void addPropertiesReloadListener(PropertiesReloadListener listener) { checkInitialManagementContextReal(); initialManagementContext.addPropertiesReloadListener(listener); } @Override public void removePropertiesReloadListener(PropertiesReloadListener listener) { checkInitialManagementContextReal(); initialManagementContext.removePropertiesReloadListener(listener); } @Override public BrooklynObject lookup(String id) { checkInitialManagementContextReal(); return initialManagementContext.lookup(id); } @Override public <T extends BrooklynObject> T lookup(String id, Class<T> type) { checkInitialManagementContextReal(); return initialManagementContext.lookup(id, type); } @Override public List<Throwable> errors() { checkInitialManagementContextReal(); return initialManagementContext.errors(); } @Override public CatalogInitialization getCatalogInitialization() { checkInitialManagementContextReal(); return initialManagementContext.getCatalogInitialization(); } @Override public void setCatalogInitialization(CatalogInitialization catalogInitialization) { checkInitialManagementContextReal(); initialManagementContext.setCatalogInitialization(catalogInitialization); } @Override public ExternalConfigSupplierRegistry getExternalConfigProviderRegistry() { checkInitialManagementContextReal(); return initialManagementContext.getExternalConfigProviderRegistry(); } /** * For when the initial management context is not "real"; the changeListener is a no-op, but everything else forbidden. * * @author aled */ private class NonDeploymentRebindManager implements RebindManager { @Override public ChangeListener getChangeListener() { return ChangeListener.NOOP; } @Override public void setPersister(BrooklynMementoPersister persister) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void setPersister(BrooklynMementoPersister persister, PersistenceExceptionHandler exceptionHandler) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public BrooklynMementoPersister getPersister() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public List<Application> rebind() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public List<Application> rebind(ClassLoader classLoader) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public List<Application> rebind(ClassLoader classLoader, RebindExceptionHandler exceptionHandler) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public List<Application> rebind(ClassLoader classLoader, RebindExceptionHandler exceptionHandler, ManagementNodeState mode) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void startPersistence() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void stopPersistence() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void startReadOnly(ManagementNodeState state) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void stopReadOnly() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void start() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void stop() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void waitForPendingComplete(Duration timeout, boolean canTrigger) throws InterruptedException, TimeoutException { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void forcePersistNow() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void forcePersistNow(boolean full, PersistenceExceptionHandler exceptionHandler) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public BrooklynMementoRawData retrieveMementoRawData() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public boolean isAwaitingInitialRebind() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public Map<String, Object> getMetrics() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } } /** * For when the initial management context is not "real". * * @author aled */ private class NonDeploymentHighAvailabilityManager implements HighAvailabilityManager { @Override public ManagementNodeState getNodeState() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public boolean isRunning() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public HighAvailabilityManager setPersister(ManagementPlaneSyncRecordPersister persister) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void disabled() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void start(HighAvailabilityMode startMode) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void stop() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public ManagementPlaneSyncRecordPersister getPersister() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public ManagementPlaneSyncRecord getLastManagementPlaneSyncRecord() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public ManagementPlaneSyncRecord loadManagementPlaneSyncRecord(boolean x) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void changeMode(HighAvailabilityMode startMode) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void setPriority(long priority) { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public long getPriority() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public Map<String, Object> getMetrics() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public void publishClearNonMaster() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } @Override public long getLastStateChange() { throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation."); } } }