Fix EntityManagementSupport race for changeListener - Fixes race where effector called post-exec listener concurrent with app.stop(), which unmanaged the app. Caused exception, and thus test's tearDown to fail.
Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/19938ce8 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/19938ce8 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/19938ce8 Branch: refs/heads/0.5.0 Commit: 19938ce8833b40c7b6ea990fcd77d04de3544cc9 Parents: 64da3fc Author: Aled Sage <[email protected]> Authored: Wed Apr 10 11:08:50 2013 +0100 Committer: Aled Sage <[email protected]> Committed: Wed Apr 10 14:29:57 2013 +0100 ---------------------------------------------------------------------- .../NonDeploymentManagementContext.java | 61 +++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/19938ce8/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java b/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java index aed0dbb..9b915aa 100644 --- a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java +++ b/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java @@ -5,9 +5,12 @@ 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; @@ -17,6 +20,7 @@ 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.location.LocationRegistry; import brooklyn.management.EntityManager; @@ -24,6 +28,8 @@ import brooklyn.management.ExecutionContext; import brooklyn.management.ExecutionManager; 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 { @@ -140,8 +146,16 @@ public class NonDeploymentManagementContext implements ManagementContextInternal @Override public RebindManager getRebindManager() { - checkInitialManagementContextReal(); - return initialManagementContext.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 @@ -265,4 +279,47 @@ public class NonDeploymentManagementContext implements ManagementContextInternal 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."); + } + } }
