blob: 6918d89f223f3b30bcc418712ee147f287c9dbf7 [file] [log] [blame]
package brooklyn.management.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import brooklyn.catalog.BrooklynCatalog;
import brooklyn.config.StringConfigMap;
import brooklyn.entity.Application;
import brooklyn.entity.Effector;
import brooklyn.entity.Entity;
import brooklyn.entity.basic.AbstractEntity;
import brooklyn.entity.drivers.EntityDriverManager;
import brooklyn.entity.drivers.downloads.DownloadResolverManager;
import brooklyn.entity.rebind.ChangeListener;
import brooklyn.entity.rebind.RebindManager;
import brooklyn.internal.storage.BrooklynStorage;
import brooklyn.location.LocationRegistry;
import brooklyn.management.EntityManager;
import brooklyn.management.ExecutionContext;
import brooklyn.management.ExecutionManager;
import brooklyn.management.LocationManager;
import brooklyn.management.SubscriptionContext;
import brooklyn.management.Task;
import brooklyn.mementos.BrooklynMemento;
import brooklyn.mementos.BrooklynMementoPersister;
import brooklyn.util.task.AbstractExecutionContext;
public class NonDeploymentManagementContext implements ManagementContextInternal {
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 final NonDeploymentExecutionContext executionContext;
private NonDeploymentEntityManager entityManager;
private NonDeploymentLocationManager locationManager;
public NonDeploymentManagementContext(AbstractEntity entity, NonDeploymentManagementContextMode mode) {
this.entity = checkNotNull(entity, "entity");
this.mode = checkNotNull(mode, "mode");
qsm = new QueueingSubscriptionManager();
subscriptionContext = new BasicSubscriptionContext(qsm, entity);
executionContext = new NonDeploymentExecutionContext();
entityManager = new NonDeploymentEntityManager(null);
locationManager = new NonDeploymentLocationManager(null);
}
public void setManagementContext(ManagementContextInternal val) {
this.initialManagementContext = checkNotNull(val, "initialManagementContext");
this.entityManager = new NonDeploymentEntityManager(val);
this.locationManager = new NonDeploymentLocationManager(val);
}
@Override
public String toString() {
return super.toString()+"["+entity+";"+mode+"]";
}
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 EntityManager getEntityManager() {
return entityManager;
}
@Override
public LocationManager getLocationManager() {
return locationManager;
}
@Override
public ExecutionManager getExecutionManager() {
throw new IllegalStateException("Non-deployment context "+this+" is not valid for this operation: executions cannot be performed prior to management");
}
@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);
return subscriptionContext;
}
@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);
return executionContext;
}
// TODO the methods below should delegate to the application?
@Override
public EntityDriverManager getEntityDriverManager() {
checkInitialManagementContextReal();
return initialManagementContext.getEntityDriverManager();
}
@Override
public EntityDriverManager getEntityDriverFactory() {
return getEntityDriverManager();
}
@Override
public DownloadResolverManager getEntityDownloadsManager() {
checkInitialManagementContextReal();
return initialManagementContext.getEntityDownloadsManager();
}
@Override
public StringConfigMap getConfig() {
checkInitialManagementContextReal();
return initialManagementContext.getConfig();
}
@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 LocationRegistry getLocationRegistry() {
checkInitialManagementContextReal();
return initialManagementContext.getLocationRegistry();
}
@Override
public BrooklynCatalog getCatalog() {
checkInitialManagementContextReal();
return initialManagementContext.getCatalog();
}
@Override
public Collection<Entity> getEntities() {
return getEntityManager().getEntities();
}
@Override
public Entity getEntity(String id) {
return getEntityManager().getEntity(id);
}
@Override
public boolean isManaged(Entity entity) {
return getEntityManager().isManaged(entity);
}
@Override
public void manage(Entity e) {
getEntityManager().manage(e);
}
@Override
public void unmanage(Entity e) {
getEntityManager().unmanage(e);
}
@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);
}
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.");
}
}
private class NonDeploymentExecutionContext extends AbstractExecutionContext {
@Override
public Set<Task<?>> getTasks() {
return Collections.emptySet();
}
@Override
public Task<?> getCurrentTask() {
return null;
}
@Override
protected <T> Task<T> submitInternal(@SuppressWarnings("rawtypes") Map properties, Object task) {
throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
}
}
/**
* 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 BrooklynMementoPersister getPersister() {
throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
}
@Override
public List<Application> rebind(BrooklynMemento memento) {
throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
}
@Override
public List<Application> rebind(BrooklynMemento memento, ClassLoader classLoader) {
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(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
}
}
}