You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2015/05/07 12:57:16 UTC

[1/7] incubator-brooklyn git commit: adjust cli start sequence so web/rest is avail always, with isUp check on rest api

Repository: incubator-brooklyn
Updated Branches:
  refs/heads/master d0cbcf36c -> d64808206


adjust cli start sequence so web/rest is avail always, with isUp check on rest api

first step towards being able to interactively track the startup sequence and notify on errors


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/20810ac9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/20810ac9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/20810ac9

Branch: refs/heads/master
Commit: 20810ac9e275eadbf5c00c8b2ba07534841e56f5
Parents: 7b97cee
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Apr 23 07:46:32 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Apr 27 08:34:36 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/management/ManagementContext.java  | 17 ++++++++-
 .../internal/AbstractManagementContext.java     |  6 +++
 .../internal/LocalManagementContext.java        |  4 ++
 .../NonDeploymentManagementContext.java         |  8 ++++
 usage/cli/src/main/java/brooklyn/cli/Main.java  | 19 +++++-----
 .../brooklyn/launcher/BrooklynLauncher.java     | 39 ++++++++++++++------
 .../main/java/brooklyn/rest/api/ServerApi.java  | 10 +++++
 .../resources/AbstractBrooklynRestResource.java | 13 +++++--
 .../brooklyn/rest/resources/ServerResource.java | 31 +++++++++++++++-
 .../testing/mocks/ManagementContextMock.java    |  5 +++
 10 files changed, 125 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/20810ac9/api/src/main/java/brooklyn/management/ManagementContext.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/management/ManagementContext.java b/api/src/main/java/brooklyn/management/ManagementContext.java
index b45ecef..12ead70 100644
--- a/api/src/main/java/brooklyn/management/ManagementContext.java
+++ b/api/src/main/java/brooklyn/management/ManagementContext.java
@@ -172,9 +172,24 @@ public interface ManagementContext {
     StringConfigMap getConfig();
     
     /**
-     * Whether this management context is still running, or has been terminated.
+     * Whether the management context has been initialized and not yet terminated.
+     * This does not mean startup is entirely completed. See also {@link #isStartupComplete()}.
      */
+    // TODO should we replace this with isNotYetTerminated() ??
+    // and perhaps introduce isFullyRunning() which does (isStartupComplete() && isRunning()),
+    // and/or move to a MgmtContextStatus subclass where errors can also be stored?
     public boolean isRunning();
+    
+    /**
+     * Whether all startup tasks have completed. Previous to this point the management context is still usable 
+     * (and hence {@link #isRunning()} returns true immediately after construction)
+     * but some subsystems (e.g. persistence, OSGi, webapps, entities started at startup)
+     * may not be available until this returns true.
+     * <p>
+     * Also see {@link #isStartupComplete()}.
+     */
+    @Beta  // see comment on isRunning() as items might move to a status handler
+    public boolean isStartupComplete();
 
     /** Record of configured locations and location resolvers */
     LocationRegistry getLocationRegistry();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/20810ac9/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
index 14fe100..501f4e4 100644
--- a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
@@ -163,6 +163,7 @@ public abstract class AbstractManagementContext implements ManagementContextInte
     private final BrooklynStorage storage;
 
     private volatile boolean running = true;
+    protected boolean startupComplete = false;
 
     protected Maybe<URI> uri = Maybe.absent();
 
@@ -201,6 +202,11 @@ public abstract class AbstractManagementContext implements ManagementContextInte
     public boolean isRunning() {
         return running;
     }
+    
+    @Override
+    public boolean isStartupComplete() {
+        return startupComplete;
+    }
 
     @Override
     public BrooklynStorage getStorage() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/20810ac9/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java b/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
index 202e4a6..39e4dc5 100644
--- a/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/LocalManagementContext.java
@@ -412,4 +412,8 @@ public class LocalManagementContext extends AbstractManagementContext {
     public void removePropertiesReloadListener(PropertiesReloadListener listener) {
         reloadListeners.remove(listener);
     }
+
+    public void noteStartupComplete() {
+        startupComplete = true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/20810ac9/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 0972871..72d83bd 100644
--- a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
@@ -164,6 +164,14 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
         // 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() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/20810ac9/usage/cli/src/main/java/brooklyn/cli/Main.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/Main.java b/usage/cli/src/main/java/brooklyn/cli/Main.java
index e674c1d..b66a6eb 100644
--- a/usage/cli/src/main/java/brooklyn/cli/Main.java
+++ b/usage/cli/src/main/java/brooklyn/cli/Main.java
@@ -82,6 +82,7 @@ import brooklyn.util.text.Strings;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
 import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.collect.ImmutableList;
 
@@ -377,7 +378,15 @@ public class Main extends AbstractMain {
                 }
     
                 launcher = createLauncher();
-    
+
+                launcher.customizeInitialCatalog(new Function<BrooklynLauncher,Void>() {
+                    @Override
+                    public Void apply(BrooklynLauncher launcher) {
+                        populateCatalog(launcher.getServerDetails().getManagementContext().getCatalog());
+                        return null;
+                    }
+                });
+                
                 launcher.persistMode(persistMode);
                 launcher.persistenceDir(persistenceDir);
                 launcher.persistenceLocation(persistenceLocation);
@@ -418,14 +427,6 @@ public class Main extends AbstractMain {
             BrooklynServerDetails server = launcher.getServerDetails();
             ManagementContext ctx = server.getManagementContext();
             
-            try {
-                populateCatalog(launcher.getServerDetails().getManagementContext().getCatalog());
-            } catch (Exception e) {
-                Exceptions.propagateIfFatal(e);
-                // don't fail to start just because catalog is not available
-                log.error("Error populating catalog: "+e, e);
-            }
-
             if (verbose) {
                 Entities.dumpInfo(launcher.getApplications());
             }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/20810ac9/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
index 9cc8ea2..10c9afb 100644
--- a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
+++ b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
@@ -158,6 +158,7 @@ public class BrooklynLauncher {
     private StopWhichAppsOnShutdown stopWhichAppsOnShutdown = StopWhichAppsOnShutdown.THESE_IF_NOT_PERSISTED;
     
     private Function<ManagementContext,Void> customizeManagement = null;
+    private Function<BrooklynLauncher,Void> customizeInitialCatalog = null;
     
     private PersistMode persistMode = PersistMode.DISABLED;
     private HighAvailabilityMode highAvailabilityMode = HighAvailabilityMode.DISABLED;
@@ -420,6 +421,11 @@ public class BrooklynLauncher {
         return this;
     }
 
+    public BrooklynLauncher customizeInitialCatalog(Function<BrooklynLauncher, Void> customizeInitialCatalog) {
+        this.customizeInitialCatalog = customizeInitialCatalog;
+        return this;
+    }
+
     public BrooklynLauncher shutdownOnExit(boolean val) {
         LOG.warn("Call to deprecated `shutdownOnExit`", new Throwable("source of deprecated call"));
         stopWhichAppsOnShutdown = StopWhichAppsOnShutdown.THESE_IF_NOT_PERSISTED;
@@ -555,13 +561,22 @@ public class BrooklynLauncher {
         // Create the management context
         initManagementContext();
 
+        // Start webapps as soon as mgmt context available -- can use them to detect progress of other processes
+        if (startWebApps) {
+            try {
+                startWebApps();
+            } catch (Exception e) {
+                handleSubsystemStartupError(ignoreWebErrors, "core web apps", e);
+            }
+        }
+        
         // Add a CAMP platform
         campPlatform = new BrooklynCampPlatformLauncherNoServer()
                 .useManagementContext(managementContext)
                 .launch()
                 .getCampPlatform();
         // TODO start CAMP rest _server_ in the below (at /camp) ?
-        
+
         try {
             initPersistence();
             startPersistence();
@@ -569,34 +584,34 @@ public class BrooklynLauncher {
             handleSubsystemStartupError(ignorePersistenceErrors, "persistence", e);
         }
 
+        try {
+            if (customizeInitialCatalog!=null)
+                customizeInitialCatalog.apply(this);
+        } catch (Exception e) {
+            handleSubsystemStartupError(true, "initial catalog", e);
+        }
+
         // Create the locations. Must happen after persistence is started in case the
         // management context's catalog is loaded from persisted state. (Location
         // resolution uses the catalog's classpath to scan for resolvers.)
         locations.addAll(managementContext.getLocationRegistry().resolve(locationSpecs));
 
-        // Start the web-console
-        if (startWebApps) {
-            try {
-                startWebApps();
-            } catch (Exception e) {
-                handleSubsystemStartupError(ignoreWebErrors, "web apps", e);
-            }
-        }
-
         try {
             createApps();
             startApps();
         } catch (Exception e) {
-            handleSubsystemStartupError(ignoreAppErrors, "managed apps", e);
+            handleSubsystemStartupError(ignoreAppErrors, "brooklyn autostart apps", e);
         }
 
         if (startBrooklynNode) {
             try {
                 startBrooklynNode();
             } catch (Exception e) {
-                handleSubsystemStartupError(ignoreWebErrors, "web apps", e);
+                handleSubsystemStartupError(true, "brooklyn node / self entity", e);
             }
         }
+        
+        ((LocalManagementContext)managementContext).noteStartupComplete();
 
         return this;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/20810ac9/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
index 624c9db..7ee371f 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
@@ -82,6 +82,16 @@ public interface ServerApi {
     @ApiOperation(value = "Return version identifier information for this Brooklyn instance", responseClass = "String", multiValueResponse = false)
     public VersionSummary getVersion();
 
+    @GET
+    @Path("/up")
+    @ApiOperation(value = "Returns whether this server is up - fully started, and not stopping, though it may have errors")
+    public boolean isUp();
+    
+    @GET
+    @Path("/healthy")
+    @ApiOperation(value = "Returns whether this node is healthy - fully started, not stopping, and no errors")
+    public boolean isHealthy();
+    
     @Deprecated /** @deprecated since 0.7.0 use /ha/node (which returns correct JSON) */
     @GET
     @Path("/status")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/20810ac9/usage/rest-server/src/main/java/brooklyn/rest/resources/AbstractBrooklynRestResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/AbstractBrooklynRestResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/AbstractBrooklynRestResource.java
index 553e31c..0f49caf 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/AbstractBrooklynRestResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/AbstractBrooklynRestResource.java
@@ -32,6 +32,7 @@ import brooklyn.management.ManagementContextInjectable;
 import brooklyn.rest.util.BrooklynRestResourceUtils;
 import brooklyn.rest.util.WebResourceUtils;
 import brooklyn.rest.util.json.BrooklynJacksonJsonProvider;
+import brooklyn.util.guava.Maybe;
 import brooklyn.util.task.Tasks;
 import brooklyn.util.time.Duration;
 
@@ -52,12 +53,16 @@ public abstract class AbstractBrooklynRestResource implements ManagementContextI
     private BrooklynRestResourceUtils brooklynRestResourceUtils;
     private ObjectMapper mapper;
 
-    public synchronized ManagementContext mgmt() {
-        if (managementContext!=null) return managementContext;
+    public ManagementContext mgmt() {
+        return mgmtMaybe().get();
+    }
+    
+    protected synchronized Maybe<ManagementContext> mgmtMaybe() {
+        if (managementContext!=null) return Maybe.of(managementContext);
         managementContext = (ManagementContext) servletContext.getAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT);
-        if (managementContext!=null) return managementContext;
+        if (managementContext!=null) return Maybe.of(managementContext);
         
-        throw new IllegalStateException("ManagementContext not supplied for Brooklyn Jersey Resource "+this);
+        return Maybe.absent("ManagementContext not available for Brooklyn Jersey Resource "+this);
     }
     
     public void injectManagementContext(ManagementContext managementContext) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/20810ac9/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
index e44437f..422e06d 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
@@ -35,6 +35,7 @@ import javax.ws.rs.core.Response;
 
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.basic.ConfigKeys;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -46,6 +47,7 @@ import brooklyn.entity.basic.StartableApplication;
 import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils;
 import brooklyn.entity.rebind.persister.FileBasedObjectStore;
 import brooklyn.entity.rebind.persister.PersistenceObjectStore;
+import brooklyn.management.ManagementContext;
 import brooklyn.management.Task;
 import brooklyn.management.entitlement.EntitlementContext;
 import brooklyn.management.entitlement.Entitlements;
@@ -64,6 +66,7 @@ import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.file.ArchiveBuilder;
 import brooklyn.util.flags.TypeCoercions;
+import brooklyn.util.guava.Maybe;
 import brooklyn.util.os.Os;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.Strings;
@@ -229,6 +232,9 @@ public class ServerResource extends AbstractBrooklynRestResource implements Serv
 
     @Override
     public VersionSummary getVersion() {
+        if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SERVER_STATUS, null))
+            throw WebResourceUtils.unauthorized("User '%s' is not authorized for this operation", Entitlements.getEntitlementContext().user());
+        
         // TODO reconcile this with BrooklynVersion reading from the OSGi manifest
         // @ahgittin / @sjcorbett to decide, is there need for this in addition to the OSGi manifest?
         // TODO as part of this call should we have a strategy for reporting downstream builds in this call?
@@ -257,6 +263,26 @@ public class ServerResource extends AbstractBrooklynRestResource implements Serv
         return new VersionSummary(BrooklynVersion.get(), gitSha1, gitBranch);
     }
 
+    @Override
+    public boolean isUp() {
+        if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SERVER_STATUS, null))
+            throw WebResourceUtils.unauthorized("User '%s' is not authorized for this operation", Entitlements.getEntitlementContext().user());
+
+        Maybe<ManagementContext> mm = mgmtMaybe();
+        if (mm.isAbsent()) return false;
+        ManagementContext m = mm.get();
+        if (!m.isStartupComplete()) return false;
+        if (!m.isRunning()) return false;
+        return true;
+    }
+    
+    @Override
+    public boolean isHealthy() {
+        if (!isUp()) return false;
+        // TODO errors
+        return true;
+    }
+    
     @Deprecated
     @Override
     public String getStatus() {
@@ -282,7 +308,10 @@ public class ServerResource extends AbstractBrooklynRestResource implements Serv
     public ManagementNodeState getHighAvailabilityNodeState() {
         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SERVER_STATUS, null))
             throw WebResourceUtils.unauthorized("User '%s' is not authorized for this operation", Entitlements.getEntitlementContext().user());
-        return mgmt().getHighAvailabilityManager().getNodeState();
+        
+        Maybe<ManagementContext> mm = mgmtMaybe();
+        if (mm.isAbsent()) return ManagementNodeState.INITIALIZING;
+        return mm.get().getHighAvailabilityManager().getNodeState();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/20810ac9/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java b/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
index 90edf1e..d7c461e 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
@@ -135,6 +135,11 @@ public class ManagementContextMock implements ManagementContext {
     }
 
     @Override
+    public boolean isStartupComplete() {
+        throw fail();
+    }
+    
+    @Override
     public LocationRegistry getLocationRegistry() {
         throw fail();
     }


[7/7] incubator-brooklyn git commit: This closes #614

Posted by he...@apache.org.
This closes #614


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/d6480820
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/d6480820
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/d6480820

Branch: refs/heads/master
Commit: d648082060ecb9dd3ab8d3bba54f1fef241c1807
Parents: d0cbcf3 7556c58
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu May 7 11:57:04 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu May 7 11:57:04 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/rebind/RebindManager.java   |   5 +
 .../brooklyn/management/ManagementContext.java  |  17 +-
 .../entity/rebind/RebindManagerImpl.java        |  11 +
 .../ha/HighAvailabilityManagerImpl.java         |   5 +-
 .../internal/AbstractManagementContext.java     |  17 +-
 .../internal/LocalManagementContext.java        |   4 +
 .../internal/ManagementContextInternal.java     |   9 +
 .../NonDeploymentManagementContext.java         |  20 +-
 .../policy/followthesun/FollowTheSunPolicy.java |   8 +-
 .../loadbalancing/LoadBalancingPolicy.java      |  16 +-
 .../entity/brooklynnode/BrooklynNodeImpl.java   |   4 +
 .../BrooklynNodeIntegrationTest.java            |  23 +-
 usage/cli/src/main/java/brooklyn/cli/Main.java  |  68 ++++--
 usage/jsgui/src/main/webapp/assets/js/config.js |  10 +-
 .../jsgui/src/main/webapp/assets/js/model/ha.js |  62 ------
 .../assets/js/model/server-extended-status.js   | 102 +++++++++
 usage/jsgui/src/main/webapp/assets/js/router.js | 217 ++++++++++---------
 .../main/webapp/assets/js/view/ha-summary.js    |  17 +-
 .../src/main/webapp/assets/js/view/home.js      |   4 +-
 .../src/main/webapp/assets/tpl/help/page.html   |   5 +-
 .../webapp/assets/tpl/home/server-caution.html  | 106 +++++++++
 .../assets/tpl/home/server-not-ha-master.html   |  35 ---
 usage/jsgui/src/main/webapp/index.html          |   2 +-
 .../src/test/javascript/specs/model/ha-spec.js  |  41 ----
 .../src/test/javascript/specs/router-spec.js    |   3 -
 .../brooklyn/launcher/BrooklynLauncher.java     |  63 ++++--
 .../BrooklynLauncherHighAvailabilityTest.java   |   2 +
 .../brooklyn/launcher/BrooklynLauncherTest.java | 108 +++++++--
 .../main/java/brooklyn/rest/api/ServerApi.java  |  20 ++
 .../rest/filter/HaHotCheckResourceFilter.java   |  74 +++++--
 .../rest/filter/HaMasterCheckFilter.java        |  58 ++++-
 .../resources/AbstractBrooklynRestResource.java |  13 +-
 .../rest/resources/CatalogResource.java         |  17 +-
 .../brooklyn/rest/resources/ServerResource.java |  55 ++++-
 .../brooklyn/rest/BrooklynRestApiLauncher.java  |   1 +
 .../test/java/brooklyn/rest/HaHotCheckTest.java |  57 ++---
 .../brooklyn/rest/HaMasterCheckFilterTest.java  |   3 +-
 .../rest/testing/BrooklynRestResourceTest.java  |   2 +-
 .../mocks/HighAvailabilityManagerStub.java      | 121 -----------
 .../testing/mocks/ManagementContextMock.java    | 189 ----------------
 40 files changed, 871 insertions(+), 723 deletions(-)
----------------------------------------------------------------------



[5/7] incubator-brooklyn git commit: show caution dialog if shutting down or if server is unresponsive

Posted by he...@apache.org.
show caution dialog if shutting down or if server is unresponsive

and include shutting down message in rest api.
tested that i can stop and restart the server and it nicely cycles through sequence of:
"shutting down", "server unreachable", then "starting up", then restores page.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/7d0f1a0e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/7d0f1a0e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/7d0f1a0e

Branch: refs/heads/master
Commit: 7d0f1a0efeffd0e16bdca35ec73f0d7ebbd956ed
Parents: 65d7ee3
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Apr 27 14:30:26 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Apr 27 14:30:26 2015 +0100

----------------------------------------------------------------------
 .../assets/js/model/server-extended-status.js   | 25 +++++++++++++++++---
 usage/jsgui/src/main/webapp/assets/js/router.js | 18 +++++++++++---
 .../src/main/webapp/assets/tpl/help/page.html   |  5 ++--
 .../webapp/assets/tpl/home/server-caution.html  | 18 +++++++++++---
 .../main/java/brooklyn/rest/api/ServerApi.java  |  7 +++++-
 .../brooklyn/rest/resources/ServerResource.java | 11 +++++++++
 6 files changed, 72 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7d0f1a0e/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js b/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
index 88e1ef2..2f5ccb3 100644
--- a/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
+++ b/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
@@ -22,6 +22,20 @@ define(["backbone", "brooklyn", "view/viewutils"], function (Backbone, Brooklyn,
         callbacks: [],
         loaded: false,
         url: "/v1/server/up/extended",
+        onError: function(thiz,xhr,modelish) {
+            log("ServerExtendedStatus: error contacting Brooklyn server");
+            log(xhr);
+            if (xhr.readyState==0) {
+                // server not contactable
+                this.loaded = false;
+            } else {
+                // server error
+                log(xhr.responseText);
+                // simply set unhealthy
+                this.set("healthy", false);
+            }
+            this.applyCallbacks();
+        },
         whenUp: function(f) {
             var that = this;
             if (this.isUp()) {
@@ -48,6 +62,7 @@ define(["backbone", "brooklyn", "view/viewutils"], function (Backbone, Brooklyn,
         },
 
         isUp: function() { return this.get("up") },
+        isShuttingDown: function() { return this.get("shuttingDown") },
         isHealthy: function() { return this.get("healthy") },
         isMaster: function() {
             ha = this.get("ha") || {};
@@ -70,15 +85,19 @@ define(["backbone", "brooklyn", "view/viewutils"], function (Backbone, Brooklyn,
                 return master.nodeUri;
             }
         },
+        applyCallbacks: function() {
+            var currentCallbacks = this.callbacks;
+            this.callbacks = [];
+            _.invoke(currentCallbacks, "apply");
+        },
     });
 
     var serverExtendedStatus = new ServerExtendedStatus();
     serverExtendedStatus.on("sync", function() {
         serverExtendedStatus.loaded = true;
-        var currentCallbacks = serverExtendedStatus.callbacks;
-        serverExtendedStatus.callbacks = [];
-        _.invoke(currentCallbacks, "apply");
+        serverExtendedStatus.applyCallbacks();
     });
+    serverExtendedStatus.on("error", serverExtendedStatus.onError);
 
     // Will returning the instance rather than the object be confusing?
     // It breaks the pattern used by all the other models.

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7d0f1a0e/usage/jsgui/src/main/webapp/assets/js/router.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/router.js b/usage/jsgui/src/main/webapp/assets/js/router.js
index 4c7689a..8eb9c34 100644
--- a/usage/jsgui/src/main/webapp/assets/js/router.js
+++ b/usage/jsgui/src/main/webapp/assets/js/router.js
@@ -48,15 +48,22 @@ define([
             var state = {
                     loaded: serverStatus.loaded,
                     up: serverStatus.isUp(),
+                    shuttingDown: serverStatus.isShuttingDown(),
                     healthy: serverStatus.isHealthy(),
                     master: serverStatus.isMaster(),
                     masterUri: serverStatus.getMasterUri(),
                 };
-            if (state.loaded && state.up && state.healthy && state.master) return this.renderEmpty();
+            
+            if (state.loaded && state.up && state.healthy && state.master) {
+                // this div shows nothing in normal operation
+                return this.renderEmpty();
+            }
             
             this.warningActive = true;
             this.$el.html(this.template(state));
-                
+            $('#application-content').fadeTo(500,0.1);
+            this.$el.fadeTo(200,1);
+            
             $("#dismiss-standby-warning", this.$el).click(function() {
                 that.carryOnRegardless = true;
                 if (that.redirectPending) {
@@ -91,8 +98,13 @@ define([
             return this;
         },
         renderEmpty: function() {
+            var that = this;
             this.warningActive = false;
-            this.$el.empty();
+            this.$el.fadeTo(200,0.2, function() {
+                if (!that.warningActive)
+                    that.$el.empty();
+            });
+            $('#application-content').fadeTo(200,1);
             return this;
         },
         beforeClose: function() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7d0f1a0e/usage/jsgui/src/main/webapp/assets/tpl/help/page.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/help/page.html b/usage/jsgui/src/main/webapp/assets/tpl/help/page.html
index 7d3ef0a..dc35b17 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/help/page.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/help/page.html
@@ -52,10 +52,11 @@ under the License.
                 url:"/v1/server/shutdown",
                 data: { stopAppsFirst: stopAppsFirst, shutdownTimeout: 0, requestTimeout: 0 },
                 success:function (data) {
-                    $('#help-page').fadeTo(500,0.1);
+                    // unfaded if server restarted, in router.js ServerCautionOverlay
+                    $('#application-content').fadeTo(500,0.1);
                 },
                 error: function(data) {
-                    $('#help-page')
+                    $('#application-content')
                         .fadeTo(200,0.2)
                         .delay(200).fadeTo(200,1)
                         .delay(200).fadeTo(100,0.2)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7d0f1a0e/usage/jsgui/src/main/webapp/assets/tpl/home/server-caution.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/server-caution.html b/usage/jsgui/src/main/webapp/assets/tpl/home/server-caution.html
index 0f767c0..94bd622 100644
--- a/usage/jsgui/src/main/webapp/assets/tpl/home/server-caution.html
+++ b/usage/jsgui/src/main/webapp/assets/tpl/home/server-caution.html
@@ -27,16 +27,28 @@ under the License.
         <p><strong>
             Connecting to server...</strong></p>
 
-            <center><img src="/assets/images/throbber.gif"/></center>        
         <p>
             No response from server (yet). 
-            Please check your network connection, or if you are on a slow connection simply be patient!
+            Please check your network connection and the URL, or if you are on a slow connection simply be patient!
             This dialog will disappear if and when the server responds.</p>
         
         <p><i>
             If you understand the risks, that information may be unavailable and operations may fail, 
             and wish to proceed regardless, click <a id="dismiss-standby-warning">here</a>.</i></p>
 
+<% } else if (shuttingDown) { %>
+        <p><strong>
+            Server shutting down...</strong></p>
+                    
+        <p>
+            The Brooklyn server is shutting down.
+            This dialog will disappear if and when the server is restarted at this address.
+            If in doubt, contact your system administrator.</p>
+        
+        <p><i>
+            If you understand the risks, that information may be unavailable and operations may fail, 
+            and wish to proceed regardless, click <a id="dismiss-standby-warning">here</a>.</i></p>
+
 <% } else if (!up) { %>
             <div style="float: right; position: relative; top: -45px; height: 0; overflow: visible;">
                 <img src="/assets/img/icon-status-starting.gif" width="60"/></div>
@@ -50,7 +62,7 @@ under the License.
             This dialog will disappear when the server is ready.</p>
         
         <p><i>
-            If you understand the risks, that information may be unavailable and operations may fail, 
+            If you understand the risks, that information may be incomplete and operations may fail, 
             and wish to proceed regardless, click <a id="dismiss-standby-warning">here</a>.</i></p>
 
 <% } else if (healthy && !master) { %>

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7d0f1a0e/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
index a1ac60c..d779f68 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
@@ -88,6 +88,11 @@ public interface ServerApi {
     public boolean isUp();
     
     @GET
+    @Path("/shuttingDown")
+    @ApiOperation(value = "Returns whether this server is shutting down")
+    public boolean isShuttingDown();
+    
+    @GET
     @Path("/healthy")
     @ApiOperation(value = "Returns whether this node is healthy - fully started, not stopping, and no errors")
     public boolean isHealthy();
@@ -102,7 +107,7 @@ public interface ServerApi {
 
     @GET
     @Path("/up/extended")
-    @ApiOperation(value = "Returns extended server-up information, a map including up (/up), healthy (/healthy), and ha (/ha/states) (qv)")
+    @ApiOperation(value = "Returns extended server-up information, a map including up (/up), shuttingDown (/shuttingDown), healthy (/healthy), and ha (/ha/states) (qv)")
     public Map<String,Object> getUpExtended();
 
     @GET

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7d0f1a0e/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
index ecefd4c..a9a2225 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
@@ -277,6 +277,16 @@ public class ServerResource extends AbstractBrooklynRestResource implements Serv
     }
     
     @Override
+    public boolean isShuttingDown() {
+        if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SERVER_STATUS, null))
+            throw WebResourceUtils.unauthorized("User '%s' is not authorized for this operation", Entitlements.getEntitlementContext().user());
+        Maybe<ManagementContext> mm = mgmtMaybe();
+        if (mm.isAbsent()) return false;
+        ManagementContext m = mm.get();
+        return (m.isStartupComplete() && !m.isRunning());
+    }
+    
+    @Override
     public boolean isHealthy() {
         if (!isUp()) return false;
         if (!((ManagementContextInternal)mgmt()).errors().isEmpty()) return false;
@@ -287,6 +297,7 @@ public class ServerResource extends AbstractBrooklynRestResource implements Serv
     public Map<String,Object> getUpExtended() {
         return MutableMap.<String,Object>of(
             "up", isUp(),
+            "shuttingDown", isShuttingDown(),
             "healthy", isHealthy(),
             "ha", getHighAvailabilityPlaneStates());
     }


[2/7] incubator-brooklyn git commit: add ability to collect startup errors and check healthy through rest api

Posted by he...@apache.org.
add ability to collect startup errors and check healthy through rest api

changes defaults to be to continue on error, and extends (and optimizes) the BrooklynLauncherTest which checks this


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/ddce439f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/ddce439f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/ddce439f

Branch: refs/heads/master
Commit: ddce439f17143e838291c8f60ab6b69610ed9249
Parents: 20810ac
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Apr 23 11:52:53 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Apr 27 08:34:45 2015 +0100

----------------------------------------------------------------------
 .../internal/AbstractManagementContext.java     |  11 +-
 .../internal/ManagementContextInternal.java     |   9 ++
 .../NonDeploymentManagementContext.java         |   6 ++
 usage/cli/src/main/java/brooklyn/cli/Main.java  |  15 +--
 .../brooklyn/launcher/BrooklynLauncher.java     |  24 +++--
 .../BrooklynLauncherHighAvailabilityTest.java   |   2 +
 .../brooklyn/launcher/BrooklynLauncherTest.java | 108 ++++++++++++++-----
 .../brooklyn/rest/resources/ServerResource.java |   2 +-
 8 files changed, 138 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddce439f/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
index 501f4e4..0f20f6b 100644
--- a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java
@@ -23,6 +23,8 @@ import static java.lang.String.format;
 
 import java.net.URI;
 import java.net.URL;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
@@ -72,14 +74,15 @@ import brooklyn.management.ha.HighAvailabilityManager;
 import brooklyn.management.ha.HighAvailabilityManagerImpl;
 import brooklyn.util.GroovyJavaMethods;
 import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableList;
 import brooklyn.util.collections.MutableMap;
 import brooklyn.util.config.ConfigBag;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.task.BasicExecutionContext;
 import brooklyn.util.task.Tasks;
 
-import com.google.common.base.Objects;
 import com.google.common.base.Function;
+import com.google.common.base.Objects;
 
 public abstract class AbstractManagementContext implements ManagementContextInternal {
     private static final Logger log = LoggerFactory.getLogger(AbstractManagementContext.class);
@@ -164,6 +167,7 @@ public abstract class AbstractManagementContext implements ManagementContextInte
 
     private volatile boolean running = true;
     protected boolean startupComplete = false;
+    protected final List<Throwable> errors = Collections.synchronizedList(MutableList.<Throwable>of()); 
 
     protected Maybe<URI> uri = Maybe.absent();
 
@@ -457,4 +461,9 @@ public abstract class AbstractManagementContext implements ManagementContextInte
         return null;
     }
 
+    @Override
+    public List<Throwable> errors() {
+        return errors;
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddce439f/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java b/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
index b8d334c..87bcc98 100644
--- a/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
+++ b/core/src/main/java/brooklyn/management/internal/ManagementContextInternal.java
@@ -20,6 +20,7 @@ package brooklyn.management.internal;
 
 import java.net.URI;
 import java.net.URL;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
@@ -40,6 +41,8 @@ import brooklyn.management.ha.OsgiManager;
 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;
@@ -104,4 +107,10 @@ public interface ManagementContextInternal extends ManagementContext {
      * 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();
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddce439f/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 72d83bd..7f158d5 100644
--- a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
@@ -441,6 +441,12 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
         return initialManagementContext.lookup(id, type);
     }
 
+    @Override
+    public List<Throwable> errors() {
+        checkInitialManagementContextReal();
+        return initialManagementContext.errors();
+    }
+
     /**
      * For when the initial management context is not "real"; the changeListener is a no-op, but everything else forbidden.
      * 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddce439f/usage/cli/src/main/java/brooklyn/cli/Main.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/Main.java b/usage/cli/src/main/java/brooklyn/cli/Main.java
index b66a6eb..45c6128 100644
--- a/usage/cli/src/main/java/brooklyn/cli/Main.java
+++ b/usage/cli/src/main/java/brooklyn/cli/Main.java
@@ -237,21 +237,22 @@ public class Main extends AbstractMain {
         public Boolean noConsoleSecurity = false;
 
         @Option(name = { "--ignoreWebStartupErrors" },
-            description = "Ignore web subsystem failures on startup (default is to abort if it fails to start)")
+            description = "Ignore web subsystem failures on startup (default is to abort if the web API fails to start, as management is not possible)")
         public boolean ignoreWebErrors = false;
 
         @Option(name = { "--ignorePersistenceStartupErrors" },
-            description = "Ignore persistence/HA subsystem failures on startup (default is to abort if it fails to start)")
-        public boolean ignorePersistenceErrors = false;
+            description = "Ignore persistence/HA subsystem failures on startup "
+                + "(default is true, so errors can be viewed via the API)")
+        public boolean ignorePersistenceErrors = true;
 
         @Option(name = { "--ignoreManagedAppsStartupErrors" },
             description = "Ignore failures starting managed applications passed on the command line on startup "
-                + "(default is to abort if they fail to start)")
-        public boolean ignoreAppErrors = false;
+                + "(default is true, so errors can be viewed via the API)")
+        public boolean ignoreAppErrors = true;
 
         @Beta
         @Option(name = { "--startBrooklynNode" },
-                description = "Whether to start a BrooklynNode entity representing this Brooklyn instance")
+                description = "Whether to start a BrooklynNode entity representing this Brooklyn instance (default false)")
         public boolean startBrooklynNode = false;
 
         // Note in some cases, you can get java.util.concurrent.RejectedExecutionException
@@ -259,7 +260,7 @@ public class Main extends AbstractMain {
         // looks like: {@linktourl https://gist.github.com/47066f72d6f6f79b953e}
         @Beta
         @Option(name = { "-sk", "--stopOnKeyPress" },
-                description = "After startup, shutdown on user text entry")
+                description = "After startup, shutdown on user text entry (default false)")
         public boolean stopOnKeyPress = false;
 
         final static String STOP_WHICH_APPS_ON_SHUTDOWN = "--stopOnShutdown";

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddce439f/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
index 10c9afb..16f4f76 100644
--- a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
+++ b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java
@@ -45,7 +45,6 @@ import brooklyn.catalog.CatalogLoadMode;
 import brooklyn.config.BrooklynProperties;
 import brooklyn.config.BrooklynServerConfig;
 import brooklyn.config.BrooklynServerPaths;
-import brooklyn.config.BrooklynServiceAttributes;
 import brooklyn.config.ConfigKey;
 import brooklyn.config.ConfigPredicates;
 import brooklyn.entity.Application;
@@ -101,6 +100,7 @@ import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
 import brooklyn.util.time.Time;
 
+import com.google.common.annotations.Beta;
 import com.google.common.base.Function;
 import com.google.common.base.Splitter;
 import com.google.common.base.Stopwatch;
@@ -151,9 +151,9 @@ public class BrooklynLauncher {
     private Map<String, ?> webconsoleFlags = Maps.newLinkedHashMap();
     private Boolean skipSecurityFilter = null;
     
-    private boolean ignorePersistenceErrors = false;
     private boolean ignoreWebErrors = false;
-    private boolean ignoreAppErrors = false;
+    private boolean ignorePersistenceErrors = true;
+    private boolean ignoreAppErrors = true;
     
     private StopWhichAppsOnShutdown stopWhichAppsOnShutdown = StopWhichAppsOnShutdown.THESE_IF_NOT_PERSISTED;
     
@@ -296,13 +296,14 @@ public class BrooklynLauncher {
      */
     public BrooklynLauncher brooklynProperties(BrooklynProperties brooklynProperties){
         if (managementContext != null) throw new IllegalStateException("Cannot set brooklynProperties and managementContext");
-        this.brooklynProperties = checkNotNull(brooklynProperties, "brooklynProperties");
+        if (this.brooklynProperties!=null && brooklynProperties!=null && this.brooklynProperties!=brooklynProperties)
+            LOG.warn("Brooklyn properties being reset in "+this+"; set null first if you wish to clear it", new Throwable("Source of brooklyn properties reset"));
+        this.brooklynProperties = brooklynProperties;
         return this;
     }
     
     /**
-     * Specifies an attribute passed to deployed webapps 
-     * (in addition to {@link BrooklynServiceAttributes#BROOKLYN_MANAGEMENT_CONTEXT}
+     * Specifies a property to be added to the brooklyn properties
      */
     public BrooklynLauncher brooklynProperties(String field, Object value) {
         brooklynAdditionalProperties.put(checkNotNull(field, "field"), value);
@@ -421,7 +422,10 @@ public class BrooklynLauncher {
         return this;
     }
 
+    @Beta
     public BrooklynLauncher customizeInitialCatalog(Function<BrooklynLauncher, Void> customizeInitialCatalog) {
+        if (this.customizeInitialCatalog!=null)
+            throw new IllegalStateException("Initial catalog customization already set.");
         this.customizeInitialCatalog = customizeInitialCatalog;
         return this;
     }
@@ -585,6 +589,8 @@ public class BrooklynLauncher {
         }
 
         try {
+            // TODO currently done *after* above to mirror existing usage, 
+            // but where this runs will likely change
             if (customizeInitialCatalog!=null)
                 customizeInitialCatalog.apply(this);
         } catch (Exception e) {
@@ -660,6 +666,10 @@ public class BrooklynLauncher {
                 }
                 managementContext = new LocalManagementContext(builder, brooklynAdditionalProperties);
             } else {
+                if (globalBrooklynPropertiesFile != null)
+                    LOG.warn("Ignoring globalBrooklynPropertiesFile "+globalBrooklynPropertiesFile+" because explicit brooklynProperties supplied");
+                if (localBrooklynPropertiesFile != null)
+                    LOG.warn("Ignoring localBrooklynPropertiesFile "+localBrooklynPropertiesFile+" because explicit brooklynProperties supplied");
                 managementContext = new LocalManagementContext(brooklynProperties, brooklynAdditionalProperties);
             }
             brooklynProperties = ((ManagementContextInternal)managementContext).getBrooklynProperties();
@@ -711,6 +721,8 @@ public class BrooklynLauncher {
         Exceptions.propagateIfFatal(e);
         if (ignoreSuchErrors) {
             LOG.error("Subsystem for "+system+" had startup error (continuing with startup): "+e, e);
+            if (managementContext!=null)
+                ((ManagementContextInternal)managementContext).errors().add(e);
         } else {
             throw Exceptions.propagate(e);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddce439f/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
index f151d92..c20cae8 100644
--- a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
+++ b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherHighAvailabilityTest.java
@@ -209,6 +209,7 @@ public class BrooklynLauncherHighAvailabilityTest {
                     .persistMode(PersistMode.AUTO)
                     .persistenceDir(persistenceDir)
                     .persistPeriod(Duration.millis(10))
+                    .ignorePersistenceErrors(false)
                     .application(EntitySpec.create(TestApplication.class))
                     .start();
             fail();
@@ -227,6 +228,7 @@ public class BrooklynLauncherHighAvailabilityTest {
                     .persistMode(PersistMode.AUTO)
                     .persistenceDir(persistenceDir)
                     .persistPeriod(Duration.millis(10))
+                    .ignorePersistenceErrors(false)
                     .application(EntitySpec.create(TestApplication.class))
                     .start();
             fail();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddce439f/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
----------------------------------------------------------------------
diff --git a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
index 1c20c62..0eba3d7 100644
--- a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
+++ b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java
@@ -25,8 +25,14 @@ import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
 import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
 import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.Properties;
 
+import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.Test;
 
@@ -40,6 +46,7 @@ import brooklyn.location.Location;
 import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
 import brooklyn.management.ManagementContext;
 import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.test.HttpTestUtils;
 import brooklyn.test.entity.LocalManagementContextForTests;
 import brooklyn.test.entity.TestApplication;
@@ -47,13 +54,18 @@ import brooklyn.test.entity.TestApplicationImpl;
 import brooklyn.test.entity.TestEntity;
 import brooklyn.util.exceptions.FatalRuntimeException;
 import brooklyn.util.io.FileUtil;
+import brooklyn.util.net.Urls;
 import brooklyn.util.os.Os;
+import brooklyn.util.text.StringFunctions;
 import brooklyn.util.text.Strings;
 
+import com.google.api.client.util.Preconditions;
 import com.google.common.base.Charsets;
+import com.google.common.base.Function;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
 import com.google.common.io.Files;
 
 public class BrooklynLauncherTest {
@@ -65,12 +77,13 @@ public class BrooklynLauncherTest {
     public void tearDown() throws Exception {
         if (launcher != null) launcher.terminate();
         if (persistenceDir != null) RebindTestUtils.deleteMementoDir(persistenceDir);
+        launcher = null;
     }
     
     // Integration because takes a few seconds to start web-console
     @Test(groups="Integration")
     public void testStartsWebServerOnExpectectedPort() throws Exception {
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(true)
                 .webconsolePort("10000+")
                 .start();
         
@@ -88,7 +101,7 @@ public class BrooklynLauncherTest {
         String dataDirName = ".brooklyn-foo"+Strings.makeRandomId(4);
         String dataDir = "~/"+dataDirName;
 
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(true)
                 .brooklynProperties(BrooklynServerConfig.MGMT_BASE_DIR, dataDir)
                 .start();
         
@@ -101,17 +114,18 @@ public class BrooklynLauncherTest {
     
     @Test
     public void testCanDisableWebServerStartup() throws Exception {
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(true)
                 .webconsole(false)
                 .start();
         
         assertNull(launcher.getServerDetails().getWebServer());
         assertNull(launcher.getServerDetails().getWebServerUrl());
+        Assert.assertTrue( ((ManagementContextInternal)launcher.getServerDetails().getManagementContext()).errors().isEmpty() );
     }
     
     @Test
     public void testStartsAppInstance() throws Exception {
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(true)
                 .webconsole(false)
                 .application(new TestApplicationImpl())
                 .start();
@@ -121,7 +135,7 @@ public class BrooklynLauncherTest {
     
     @Test
     public void testStartsAppFromSpec() throws Exception {
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(true)
                 .webconsole(false)
                 .application(EntitySpec.create(TestApplication.class))
                 .start();
@@ -131,7 +145,7 @@ public class BrooklynLauncherTest {
     
     @Test
     public void testStartsAppFromBuilder() throws Exception {
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(true)
                 .webconsole(false)
                 .application(new ApplicationBuilder(EntitySpec.create(TestApplication.class)) {
                         @Override protected void doBuild() {
@@ -147,7 +161,7 @@ public class BrooklynLauncherTest {
                 "services:\n" +
                 "- serviceType: brooklyn.test.entity.TestEntity\n" +
                 "  name: test-app";
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(true)
                 .webconsole(false)
                 .application(yaml)
                 .start();
@@ -158,9 +172,9 @@ public class BrooklynLauncherTest {
         assertTrue(Iterables.getOnlyElement(app.getChildren()) instanceof TestEntity);
     }
     
-    @Test
+    @Test  // may take 2s initializing location if running this test case alone, but noise if running suite 
     public void testStartsAppInSuppliedLocations() throws Exception {
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(true)
                 .webconsole(false)
                 .location("localhost")
                 .application(new ApplicationBuilder(EntitySpec.create(TestApplication.class)) {
@@ -175,7 +189,7 @@ public class BrooklynLauncherTest {
     @Test
     public void testUsesSuppliedManagementContext() throws Exception {
         LocalManagementContext myManagementContext = LocalManagementContextForTests.newInstance();
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(false)
                 .webconsole(false)
                 .managementContext(myManagementContext)
                 .start();
@@ -185,9 +199,9 @@ public class BrooklynLauncherTest {
     
     @Test
     public void testUsesSuppliedBrooklynProperties() throws Exception {
-        BrooklynProperties props = BrooklynProperties.Factory.newEmpty();
+        BrooklynProperties props = LocalManagementContextForTests.builder(true).buildProperties();
         props.put("mykey", "myval");
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(false)
                 .webconsole(false)
                 .brooklynProperties(props)
                 .start();
@@ -197,7 +211,7 @@ public class BrooklynLauncherTest {
 
     @Test
     public void testUsesSupplementaryBrooklynProperties() throws Exception {
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(true)
                 .webconsole(false)
                 .brooklynProperties("mykey", "myval")
                 .start();
@@ -207,7 +221,7 @@ public class BrooklynLauncherTest {
     
     @Test
     public void testReloadBrooklynPropertiesRestoresProgrammaticProperties() throws Exception {
-        launcher = BrooklynLauncher.newInstance()
+        launcher = newLauncherForTests(true)
                 .webconsole(false)
                 .brooklynProperties("mykey", "myval")
                 .start();
@@ -225,15 +239,15 @@ public class BrooklynLauncherTest {
         FileUtil.setFilePermissionsTo600(globalPropertiesFile);
         try {
             String property = "mykey=myval";
-            Files.write(property, globalPropertiesFile, Charsets.UTF_8);
-            launcher = BrooklynLauncher.newInstance()
+            Files.append(getMinimalLauncherPropertiesString()+property, globalPropertiesFile, Charsets.UTF_8);
+            launcher = newLauncherForTests(false)
                     .webconsole(false)
                     .globalBrooklynPropertiesFile(globalPropertiesFile.getAbsolutePath())
                     .start();
             LocalManagementContext managementContext = (LocalManagementContext)launcher.getServerDetails().getManagementContext();
             assertEquals(managementContext.getConfig().getFirst("mykey"), "myval");
             property = "mykey=newval";
-            Files.write(property, globalPropertiesFile, Charsets.UTF_8);
+            Files.write(getMinimalLauncherPropertiesString()+property, globalPropertiesFile, Charsets.UTF_8);
             managementContext.reloadBrooklynProperties();
             assertEquals(managementContext.getConfig().getFirst("mykey"), "newval");
         } finally {
@@ -246,12 +260,12 @@ public class BrooklynLauncherTest {
         File propsFile = File.createTempFile("testChecksGlobalBrooklynPropertiesPermissionsX00", ".properties");
         propsFile.setReadable(true, false);
         try {
-            launcher = BrooklynLauncher.newInstance()
+            launcher = newLauncherForTests(false)
                     .webconsole(false)
                     .globalBrooklynPropertiesFile(propsFile.getAbsolutePath())
                     .start();
-            
-            assertEquals(launcher.getServerDetails().getManagementContext().getConfig().getFirst("mykey"), "myval");
+
+            Assert.fail("Should have thrown");
         } catch (FatalRuntimeException e) {
             if (!e.toString().contains("Invalid permissions for file")) throw e;
         } finally {
@@ -264,12 +278,12 @@ public class BrooklynLauncherTest {
         File propsFile = File.createTempFile("testChecksLocalBrooklynPropertiesPermissionsX00", ".properties");
         propsFile.setReadable(true, false);
         try {
-            launcher = BrooklynLauncher.newInstance()
+            launcher = newLauncherForTests(false)
                     .webconsole(false)
                     .localBrooklynPropertiesFile(propsFile.getAbsolutePath())
                     .start();
             
-            assertEquals(launcher.getServerDetails().getManagementContext().getConfig().getFirst("mykey"), "myval");
+            Assert.fail("Should have thrown");
         } catch (FatalRuntimeException e) {
             if (!e.toString().contains("Invalid permissions for file")) throw e;
         } finally {
@@ -280,20 +294,66 @@ public class BrooklynLauncherTest {
     @Test(groups="Integration")
     public void testStartsWithBrooklynPropertiesPermissionsX00() throws Exception {
         File globalPropsFile = File.createTempFile("testChecksLocalBrooklynPropertiesPermissionsX00_global", ".properties");
+        Files.write(getMinimalLauncherPropertiesString()+"key_in_global=1", globalPropsFile, Charset.defaultCharset());
         File localPropsFile = File.createTempFile("testChecksLocalBrooklynPropertiesPermissionsX00_local", ".properties");
+        Files.write("key_in_local=2", localPropsFile, Charset.defaultCharset());
         FileUtil.setFilePermissionsTo600(globalPropsFile);
         FileUtil.setFilePermissionsTo600(localPropsFile);
         try {
-            launcher = BrooklynLauncher.newInstance()
+            launcher = newLauncherForTests(false)
                     .webconsole(false)
-                    .localBrooklynPropertiesFile(globalPropsFile.getAbsolutePath())
+                    .localBrooklynPropertiesFile(localPropsFile.getAbsolutePath())
+                    .globalBrooklynPropertiesFile(globalPropsFile.getAbsolutePath())
                     .start();
+            assertEquals(launcher.getServerDetails().getManagementContext().getConfig().getFirst("key_in_global"), "1");
+            assertEquals(launcher.getServerDetails().getManagementContext().getConfig().getFirst("key_in_local"), "2");
         } finally {
             globalPropsFile.delete();
             localPropsFile.delete();
         }
     }
     
+    @Test  // takes a few seconds because starts webapp, but also tests rest api so useful
+    public void testErrorsCaughtByApiAndRestApiWorks() throws Exception {
+        launcher = newLauncherForTests(true)
+                .customizeInitialCatalog(new Function<BrooklynLauncher, Void>() {
+                    @Override
+                    public Void apply(BrooklynLauncher input) {
+                        throw new RuntimeException("deliberate-exception-for-testing");
+                    }
+                })
+                .start();
+        // such an error should be thrown, then caught in this calling thread
+        ManagementContext mgmt = launcher.getServerDetails().getManagementContext();
+        Assert.assertFalse( ((ManagementContextInternal)mgmt).errors().isEmpty() );
+        Assert.assertTrue( ((ManagementContextInternal)mgmt).errors().get(0).toString().contains("deliberate"), ""+((ManagementContextInternal)mgmt).errors() );
+        HttpTestUtils.assertContentMatches(
+            Urls.mergePaths(launcher.getServerDetails().getWebServerUrl(), "v1/server/up"), 
+            "true");
+        HttpTestUtils.assertContentMatches(
+            Urls.mergePaths(launcher.getServerDetails().getWebServerUrl(), "v1/server/healthy"), 
+            "false");
+        // TODO test errors api?
+    }
+
+    private BrooklynLauncher newLauncherForTests(boolean minimal) {
+        Preconditions.checkArgument(launcher==null, "can only be used if no launcher yet");
+        BrooklynLauncher launcher = BrooklynLauncher.newInstance();
+        if (minimal)
+            launcher.brooklynProperties(LocalManagementContextForTests.builder(true).buildProperties());
+        return launcher;
+    }
+
+    private String getMinimalLauncherPropertiesString() throws IOException {
+        BrooklynProperties p1 = LocalManagementContextForTests.builder(true).buildProperties();
+        Properties p = new Properties();
+        p.putAll(Maps.transformValues(p1.asMapWithStringKeys(), StringFunctions.toStringFunction()));
+        Writer w = new StringWriter();
+        p.store(w, "test");
+        w.close();
+        return w.toString()+"\n";
+    }
+
     private void assertOnlyApp(BrooklynLauncher launcher, Class<? extends Application> expectedType) {
         assertEquals(launcher.getApplications().size(), 1, "apps="+launcher.getApplications());
         assertNotNull(Iterables.find(launcher.getApplications(), Predicates.instanceOf(TestApplication.class), null), "apps="+launcher.getApplications());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ddce439f/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
index 422e06d..eac52f6 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
@@ -279,7 +279,7 @@ public class ServerResource extends AbstractBrooklynRestResource implements Serv
     @Override
     public boolean isHealthy() {
         if (!isUp()) return false;
-        // TODO errors
+        if (!((ManagementContextInternal)mgmt()).errors().isEmpty()) return false;
         return true;
     }
     


[4/7] incubator-brooklyn git commit: actually force catalog load by default, and report errors better

Posted by he...@apache.org.
actually force catalog load by default, and report errors better

and remove two incompatible items from the catalog, with comments in them - FollowTheSunPolicy and LoadBalancingPolicy


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/65d7ee3c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/65d7ee3c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/65d7ee3c

Branch: refs/heads/master
Commit: 65d7ee3c15afda7c5ed4507889fb632f1e2d5c8f
Parents: e629602
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Mon Apr 27 12:53:09 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Apr 27 14:29:55 2015 +0100

----------------------------------------------------------------------
 .../policy/followthesun/FollowTheSunPolicy.java |  8 +++--
 .../loadbalancing/LoadBalancingPolicy.java      | 16 ++++++---
 usage/cli/src/main/java/brooklyn/cli/Main.java  | 34 +++++++++++++++++++-
 3 files changed, 49 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/65d7ee3c/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java b/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
index 87881bf..f77eb40 100644
--- a/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
+++ b/policy/src/main/java/brooklyn/policy/followthesun/FollowTheSunPolicy.java
@@ -33,7 +33,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.catalog.Catalog;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.Attributes;
 import brooklyn.entity.basic.EntityLocal;
@@ -53,8 +52,11 @@ import com.google.common.base.Function;
 import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 
-@Catalog(name="Follow the Sun", description="Policy for moving \"work\" around to follow the demand; "
-        + "the work can be any \"Movable\" entity")
+    // removed from catalog because it cannot currently be configured via catalog mechanisms - 
+    // PolicySpec.create fails due to no no-arg constructor
+    // TODO make model and parameters things which can be initialized from config then reinstate in catalog
+//@Catalog(name="Follow the Sun", description="Policy for moving \"work\" around to follow the demand; "
+//        + "the work can be any \"Movable\" entity")
 public class FollowTheSunPolicy extends AbstractPolicy {
 
     private static final Logger LOG = LoggerFactory.getLogger(FollowTheSunPolicy.class);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/65d7ee3c/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
----------------------------------------------------------------------
diff --git a/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java b/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
index 0e36928..8a68461 100644
--- a/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
+++ b/policy/src/main/java/brooklyn/policy/loadbalancing/LoadBalancingPolicy.java
@@ -32,7 +32,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.catalog.Catalog;
 import brooklyn.config.ConfigKey;
 import brooklyn.entity.Entity;
 import brooklyn.entity.basic.EntityInternal;
@@ -67,10 +66,13 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
  * of container resource in the pool respectively. These events may be consumed by a separate policy that is capable
  * of resizing the container pool.
  */
-@Catalog(name="Load Balancer", description="Policy that is attached to a pool of \"containers\", each of which "
-        + "can host one or more migratable \"items\". The policy monitors the workrates of the items and effects "
-        + "migrations in an attempt to ensure that the containers are all sufficiently utilized without any of "
-        + "them being overloaded.")
+    // removed from catalog because it cannot currently be configured via catalog mechanisms
+    // PolicySpec.create fails due to no no-arg constructor
+    // TODO make metric and model things which can be initialized from config then reinstate in catalog
+//@Catalog(name="Load Balancer", description="Policy that is attached to a pool of \"containers\", each of which "
+//        + "can host one or more migratable \"items\". The policy monitors the workrates of the items and effects "
+//        + "migrations in an attempt to ensure that the containers are all sufficiently utilized without any of "
+//        + "them being overloaded.")
 public class LoadBalancingPolicy<NodeType extends Entity, ItemType extends Movable> extends AbstractPolicy {
     
     private static final Logger LOG = LoggerFactory.getLogger(LoadBalancingPolicy.class);
@@ -120,6 +122,10 @@ public class LoadBalancingPolicy<NodeType extends Entity, ItemType extends Movab
         }
     };
 
+    public LoadBalancingPolicy() {
+        this(null, null);
+    }
+    
     public LoadBalancingPolicy(AttributeSensor<? extends Number> metric,
             BalanceablePoolModel<NodeType, ItemType> model) {
         this(MutableMap.of(), metric, model);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/65d7ee3c/usage/cli/src/main/java/brooklyn/cli/Main.java
----------------------------------------------------------------------
diff --git a/usage/cli/src/main/java/brooklyn/cli/Main.java b/usage/cli/src/main/java/brooklyn/cli/Main.java
index 45c6128..d89703e 100644
--- a/usage/cli/src/main/java/brooklyn/cli/Main.java
+++ b/usage/cli/src/main/java/brooklyn/cli/Main.java
@@ -38,7 +38,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.BrooklynVersion;
+import brooklyn.basic.BrooklynTypes;
 import brooklyn.catalog.BrooklynCatalog;
+import brooklyn.catalog.CatalogItem;
 import brooklyn.cli.CloudExplorer.BlobstoreGetBlobCommand;
 import brooklyn.cli.CloudExplorer.BlobstoreListContainerCommand;
 import brooklyn.cli.CloudExplorer.BlobstoreListContainersCommand;
@@ -72,6 +74,7 @@ import brooklyn.util.ResourceUtils;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.exceptions.FatalConfigurationRuntimeException;
 import brooklyn.util.exceptions.FatalRuntimeException;
+import brooklyn.util.exceptions.RuntimeInterruptedException;
 import brooklyn.util.exceptions.UserFacingException;
 import brooklyn.util.guava.Maybe;
 import brooklyn.util.javalang.Enums;
@@ -79,12 +82,15 @@ import brooklyn.util.net.Networking;
 import brooklyn.util.text.Identifiers;
 import brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import brooklyn.util.text.Strings;
+import brooklyn.util.time.Duration;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Stopwatch;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 /**
  * This class is the primary CLI for brooklyn.
@@ -573,11 +579,37 @@ public class Main extends AbstractMain {
         /** method intended for subclassing, to add items to the catalog */
         protected void populateCatalog(BrooklynCatalog catalog) {
             // Force load of catalog (so web console is up to date)
-            catalog.getCatalogItems();
+            confirmCatalog(catalog);
 
             // nothing else added here
         }
 
+        protected void confirmCatalog(BrooklynCatalog catalog) {
+            // Force load of catalog (so web console is up to date)
+            Stopwatch time = Stopwatch.createStarted();
+            Iterable<CatalogItem<Object, Object>> items = catalog.getCatalogItems();
+            for (CatalogItem<Object, Object> item: items) {
+                try {
+                    Object spec = catalog.createSpec(item);
+                    if (spec instanceof EntitySpec) {
+                        BrooklynTypes.getDefinedEntityType(((EntitySpec<?>)spec).getType());
+                    }
+                    log.debug("Catalog loaded spec "+spec+" for item "+item);                      
+                } catch (Throwable throwable) {
+                    // swallow errors, apart from interrupted
+                    if (throwable instanceof InterruptedException)
+                        throw new RuntimeInterruptedException((InterruptedException) throwable);
+                    if (throwable instanceof RuntimeInterruptedException)
+                        throw (RuntimeInterruptedException) throwable;
+
+                    log.error("Error loading catalog item '"+item+"': "+throwable);
+                    log.debug("Trace for error loading catalog item '"+item+"': "+throwable, throwable);
+                }
+            }
+            log.debug("Catalog (size "+Iterables.size(items)+") confirmed in "+Duration.of(time));                      
+            // nothing else added here
+        }
+        
         /** convenience for subclasses to specify that an app should run,
          * throwing the right (caught) error if another app has already been specified */
         protected void setAppToLaunch(String className) {


[3/7] incubator-brooklyn git commit: javascript checks up on start, shows caution

Posted by he...@apache.org.
javascript checks up on start, shows caution

popup now appears while server is starting, or if there are errors, and comes back if server changes to standby.
changed to plug in *before* the routes are evaluated, so we don't load the wrong data.

expands rest api to return several pieces of information to simplify gui checks.

simplifies code in rest filters to block requests not compatible with server state, including checking for upness there.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/e629602b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/e629602b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/e629602b

Branch: refs/heads/master
Commit: e629602b41613f1068c9fa4b319e99dafc5eeeeb
Parents: ddce439
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Apr 23 16:31:30 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Mon Apr 27 08:37:04 2015 +0100

----------------------------------------------------------------------
 .../brooklyn/entity/rebind/RebindManager.java   |   5 +
 .../entity/rebind/RebindManagerImpl.java        |  11 +
 .../ha/HighAvailabilityManagerImpl.java         |   5 +-
 .../NonDeploymentManagementContext.java         |   6 +-
 usage/jsgui/src/main/webapp/assets/js/config.js |  10 +-
 .../jsgui/src/main/webapp/assets/js/model/ha.js |  62 ------
 .../assets/js/model/server-extended-status.js   |  87 ++++++++
 usage/jsgui/src/main/webapp/assets/js/router.js | 205 ++++++++++---------
 .../main/webapp/assets/js/view/ha-summary.js    |  17 +-
 .../src/main/webapp/assets/js/view/home.js      |   4 +-
 .../webapp/assets/tpl/home/server-caution.html  |  94 +++++++++
 .../assets/tpl/home/server-not-ha-master.html   |  35 ----
 usage/jsgui/src/main/webapp/index.html          |   2 +-
 .../src/test/javascript/specs/model/ha-spec.js  |  41 ----
 .../src/test/javascript/specs/router-spec.js    |   3 -
 .../main/java/brooklyn/rest/api/ServerApi.java  |   5 +
 .../rest/filter/HaHotCheckResourceFilter.java   |  74 +++++--
 .../rest/filter/HaMasterCheckFilter.java        |  58 ++++--
 .../rest/filter/ServerStatusCheckFilter.java    | 120 +++++++++++
 .../brooklyn/rest/resources/ServerResource.java |  15 +-
 .../brooklyn/rest/BrooklynRestApiLauncher.java  |   1 +
 .../test/java/brooklyn/rest/HaHotCheckTest.java |  55 ++---
 .../testing/mocks/ManagementContextMock.java    |  18 +-
 23 files changed, 600 insertions(+), 333 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/api/src/main/java/brooklyn/entity/rebind/RebindManager.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/entity/rebind/RebindManager.java b/api/src/main/java/brooklyn/entity/rebind/RebindManager.java
index 556c6bc..501430d 100644
--- a/api/src/main/java/brooklyn/entity/rebind/RebindManager.java
+++ b/api/src/main/java/brooklyn/entity/rebind/RebindManager.java
@@ -125,6 +125,11 @@ public interface RebindManager {
      * setting if the process fails after the clear!) */
     @VisibleForTesting
     public void forcePersistNow(boolean full, @Nullable PersistenceExceptionHandler exceptionHandler);
+    
+    /** Whether the management state has changed to a state where a rebind is needed
+     * but we are still awaiting the first run; 
+     * ie state is master or hot, but list of apps is not yet accurate */
+    public boolean isAwaitingInitialRebind();
 
     /** Metrics about rebind, last success, etc. */
     public Map<String,Object> getMetrics();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
index 5d155cb..a31c1de 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
@@ -132,6 +132,8 @@ public class RebindManagerImpl implements RebindManager {
     private RebindFailureMode loadPolicyFailureMode;
     private QuorumCheck danglingRefsQuorumRequiredHealthy;
     
+    private boolean isAwaitingInitialRebind;
+    
     private PersistenceActivityMetrics rebindMetrics = new PersistenceActivityMetrics();
     private PersistenceActivityMetrics persistMetrics = new PersistenceActivityMetrics();
 
@@ -547,6 +549,7 @@ public class RebindManagerImpl implements RebindManager {
             firstRebindEntityCount = iteration.getRebindContext().getEntities().size();
             firstRebindItemCount = iteration.getRebindContext().getAllBrooklynObjects().size();
         }
+        isAwaitingInitialRebind = false;
 
         return iteration.getApplications();
     }
@@ -578,6 +581,14 @@ public class RebindManagerImpl implements RebindManager {
         return result;
     }
 
+    public boolean isAwaitingInitialRebind() {
+        return isAwaitingInitialRebind;
+    }
+
+    public void setAwaitingInitialRebind(boolean isAwaitingInitialRebind) {
+        this.isAwaitingInitialRebind = isAwaitingInitialRebind;
+    }
+    
     /**
      * Wraps a ChangeListener, to log and never propagate any exceptions that it throws.
      * 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
index ec844a3..b004f6b 100644
--- a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
+++ b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
@@ -43,6 +43,7 @@ import brooklyn.entity.basic.BrooklynTaskTags;
 import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.EntityInternal;
 import brooklyn.entity.rebind.RebindManager;
+import brooklyn.entity.rebind.RebindManagerImpl;
 import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils;
 import brooklyn.entity.rebind.persister.BrooklynPersistenceUtils.CreateBackupMode;
 import brooklyn.entity.rebind.persister.PersistenceActivityMetrics;
@@ -151,7 +152,7 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
     private volatile PersistenceActivityMetrics managementStateWritePersistenceMetrics = new PersistenceActivityMetrics();
     private volatile PersistenceActivityMetrics managementStateReadPersistenceMetrics = new PersistenceActivityMetrics();
     private final long startTimeUtc;
-    
+
     public HighAvailabilityManagerImpl(ManagementContextInternal managementContext) {
         this.managementContext = managementContext;
         startTimeUtc = localTickerUtc.read();
@@ -493,6 +494,8 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
                     nodeStateHistory.remove(nodeStateHistory.size()-1);
                 }
             }
+            ((RebindManagerImpl)managementContext.getRebindManager()).setAwaitingInitialRebind(running &&
+                (ManagementNodeState.isHotProxy(newState) || newState==ManagementNodeState.MASTER));
             this.nodeState = newState;
         }
         

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/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 7f158d5..d5fcf75 100644
--- a/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
+++ b/core/src/main/java/brooklyn/management/internal/NonDeploymentManagementContext.java
@@ -544,7 +544,11 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
         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.");

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/main/webapp/assets/js/config.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/config.js b/usage/jsgui/src/main/webapp/assets/js/config.js
index 918a7ca..4fa477d 100644
--- a/usage/jsgui/src/main/webapp/assets/js/config.js
+++ b/usage/jsgui/src/main/webapp/assets/js/config.js
@@ -76,13 +76,9 @@ require.config({
 
 /*
  * Main application entry point.
- *
- * Inclusion of brooklyn module sets up logging.
  */
 require([
-    "backbone", "brooklyn", "router", "model/ha"
-], function (Backbone, Brooklyn, Router, ha) {
-    ha.autoUpdate();
-    var router = new Router();
-    Backbone.history.start();
+    "router"
+], function (Router) {
+    new Router().startBrooklynGui();
 });

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/main/webapp/assets/js/model/ha.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/model/ha.js b/usage/jsgui/src/main/webapp/assets/js/model/ha.js
deleted file mode 100644
index 8ece989..0000000
--- a/usage/jsgui/src/main/webapp/assets/js/model/ha.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.
-*/
-define(["backbone", "brooklyn", "view/viewutils"], function (Backbone, Brooklyn, ViewUtils) {
-
-    var HAStatus = Backbone.Model.extend({
-        callbacks: [],
-        loaded: false,
-        url: "/v1/server/highAvailability",
-        isMaster: function() {
-            return this.get("masterId") == this.get("ownId");
-        },
-        getMasterUri: function() {
-            // Might be undefined if first fetch hasn't completed
-            var nodes = this.get("nodes") || {};
-            var master = nodes[this.get("masterId")];
-            // defensive, if this happens something more serious has gone wrong!
-            if (!master) {
-                return null;
-            } else {
-                return master.nodeUri;
-            }
-        },
-        onLoad: function(f) {
-            if (this.loaded) {
-                f();
-            } else {
-                this.callbacks.push(f);
-            }
-        },
-        autoUpdate: function() {
-            ViewUtils.fetchModelRepeatedlyWithDelay(this, { doitnow: true });
-        }
-    });
-
-    var haStatus = new HAStatus();
-    haStatus.once("sync", function() {
-        haStatus.loaded = true;
-        _.invoke(haStatus.callbacks, "apply");
-        haStatus.callbacks = undefined;
-    });
-
-    // Will returning the instance rather than the object be confusing?
-    // It breaks the pattern used by all the other models.
-    return haStatus;
-
-});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js b/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
new file mode 100644
index 0000000..88e1ef2
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
@@ -0,0 +1,87 @@
+/*
+ * 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.
+*/
+define(["backbone", "brooklyn", "view/viewutils"], function (Backbone, Brooklyn, ViewUtils) {
+
+    var ServerExtendedStatus = Backbone.Model.extend({
+        callbacks: [],
+        loaded: false,
+        url: "/v1/server/up/extended",
+        whenUp: function(f) {
+            var that = this;
+            if (this.isUp()) {
+                f();
+            } else {
+                this.addCallback(function() { that.whenUp(f); });
+            }
+        },
+        onLoad: function(f) {
+            if (this.loaded) {
+                f();
+            } else {
+                this.addCallback(f);
+            }
+        },
+        addCallback: function(f) {
+            this.callbacks.push(f);
+        },
+        autoUpdate: function() {
+            var that = this;
+            // to debug:
+//            serverExtendedStatus.onLoad(function() { log("loaded server status:"); log(that.attributes); })
+            ViewUtils.fetchModelRepeatedlyWithDelay(this, { doitnow: true });
+        },
+
+        isUp: function() { return this.get("up") },
+        isHealthy: function() { return this.get("healthy") },
+        isMaster: function() {
+            ha = this.get("ha") || {};
+            ownId = ha.ownId;
+            if (!ownId) return null;
+            return ha.masterId == ownId;
+        },
+        getMasterUri: function() {
+            // Might be undefined if first fetch hasn't completed
+            ha = this.get("ha") || {};
+            states = ha.states || {};
+            if (!states) return null;
+            
+            var nodes = this.get("nodes") || {};
+            var master = nodes[this.get("masterId")];
+            // defensive, if this happens something more serious has gone wrong!
+            if (!master) {
+                return null;
+            } else {
+                return master.nodeUri;
+            }
+        },
+    });
+
+    var serverExtendedStatus = new ServerExtendedStatus();
+    serverExtendedStatus.on("sync", function() {
+        serverExtendedStatus.loaded = true;
+        var currentCallbacks = serverExtendedStatus.callbacks;
+        serverExtendedStatus.callbacks = [];
+        _.invoke(currentCallbacks, "apply");
+    });
+
+    // Will returning the instance rather than the object be confusing?
+    // It breaks the pattern used by all the other models.
+    return serverExtendedStatus;
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/main/webapp/assets/js/router.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/router.js b/usage/jsgui/src/main/webapp/assets/js/router.js
index 186dd72..4c7689a 100644
--- a/usage/jsgui/src/main/webapp/assets/js/router.js
+++ b/usage/jsgui/src/main/webapp/assets/js/router.js
@@ -18,46 +18,95 @@
 */
 define([
     "brooklyn", "underscore", "jquery", "backbone",
-    "model/application", "model/app-tree", "model/location", "model/ha",
+    "model/application", "model/app-tree", "model/location", 
+    "model/server-extended-status",
     "view/home", "view/application-explorer", "view/catalog", "view/apidoc", "view/script-groovy",
-    "text!tpl/help/page.html","text!tpl/labs/page.html", "text!tpl/home/server-not-ha-master.html"
+    "text!tpl/help/page.html","text!tpl/labs/page.html", "text!tpl/home/server-caution.html"
 ], function (Brooklyn, _, $, Backbone,
-        Application, AppTree, Location, ha,
-        HomeView, ExplorerView, CatalogView, ApidocView, ScriptGroovyView,
-        HelpHtml, LabsHtml, ServerNotMasterHtml) {
+        Application, AppTree, Location, 
+        serverStatus,
+        HomeView, ExplorerView, CatalogView, ApidocView, ScriptGroovyView, 
+        HelpHtml, LabsHtml, ServerCautionHtml) {
 
-    /**
-     * @returns {jquery.Deferred}
-     *      A promise that resolves when the high availability status has been
-     *      loaded. Actions to be taken on the view after it has loaded should
-     *      be registered with calls to .done()
-     */
-    // Not just defined as a function on Router because the delay if the HA status
-    // hasn't loaded requires a reference to the function, which we lose if we use
-    // 'this.showView'.
-    function showViewImpl(router, selector, view) {
-        // Don't do anything until the HA status has loaded.
-        var promise = $.Deferred()
-            .done(function () {
-                // close the previous view - does binding clean-up and avoids memory leaks
-                if (router.currentView) {
-                    router.currentView.close();
-                }
-                // render the view inside the selector element
-                $(selector).html(view.render().el);
-                router.currentView = view;
-                return view
+    var ServerCautionOverlay = Backbone.View.extend({
+        template: _.template(ServerCautionHtml),
+        scheduledRedirect: false,
+        initialize: function() {
+            var that = this;
+            this.carryOnRegardless = false;
+            _.bindAll(this);
+            serverStatus.addCallback(this.renderAndAddCallback);
+        },
+        renderAndAddCallback: function() {
+            this.renderOnUpdate();
+            serverStatus.addCallback(this.renderAndAddCallback);
+        },
+        renderOnUpdate: function() {
+            var that = this;
+            if (this.carryOnRegardless) return this.renderEmpty();
+            
+            var state = {
+                    loaded: serverStatus.loaded,
+                    up: serverStatus.isUp(),
+                    healthy: serverStatus.isHealthy(),
+                    master: serverStatus.isMaster(),
+                    masterUri: serverStatus.getMasterUri(),
+                };
+            if (state.loaded && state.up && state.healthy && state.master) return this.renderEmpty();
+            
+            this.warningActive = true;
+            this.$el.html(this.template(state));
+                
+            $("#dismiss-standby-warning", this.$el).click(function() {
+                that.carryOnRegardless = true;
+                if (that.redirectPending) {
+                    log("Cancelling redirect, using this non-master instance");
+                    clearTimeout(that.redirectPending);
+                    that.redirectPending = null;
+                }       
+                that.renderOnUpdate();
             });
-        (function isComplete() {
-            if (ha.loaded) {
-                promise.resolve();
-            } else {
-                _.defer(isComplete, 100);
+            
+            if (!state.master && state.masterUri) {
+                if (!this.scheduledRedirect && !this.redirectPending) {
+                    log("Not master; will redirect shortly to: "+state.masterUri);
+                    var destination = state.masterUri + "#" + Backbone.history.fragment;
+                    var time = 10;
+                    this.scheduledRedirect = true;
+                    log("Redirecting to " + destination + " in " + time + " seconds");
+                    this.redirectPending = setTimeout(function () {
+                        // re-check, in case the server's status changed in the wait
+                        if (!serverStatus.isMaster()) {
+                            if (that.redirectPending) {
+                                window.location.href = destination;
+                            } else {
+                                log("Cancelled redirect, using this non-master instance");
+                            }
+                        } else {
+                            log("Cancelled redirect, this instance is now master");
+                        }
+                    }, time * 1000);
+                }
             }
-        })();
-        return promise;
-    }
-
+            return this;
+        },
+        renderEmpty: function() {
+            this.warningActive = false;
+            this.$el.empty();
+            return this;
+        },
+        beforeClose: function() {
+            this.stopListening();
+        },
+        warnIfNotLoaded: function() {
+            if (!this.loaded)
+                this.renderOnUpdate();
+        }
+    });
+    // look for ha-standby-overlay for compatibility with older index.html copies
+    var serverCautionOverlay = new ServerCautionOverlay({ el: $("#server-caution-overlay").length ? $("#server-caution-overlay") : $("#ha-standby-overlay")});
+    serverCautionOverlay.render();
+    
     var Router = Backbone.Router.extend({
         routes:{
             'v1/home/*trail':'homePage',
@@ -75,7 +124,14 @@ define([
         },
 
         showView: function(selector, view) {
-            return showViewImpl(this, selector, view);
+            // close the previous view - does binding clean-up and avoids memory leaks
+            if (this.currentView) {
+                this.currentView.close();
+            }
+            // render the view inside the selector element
+            $(selector).html(view.render().el);
+            this.currentView = view;
+            return view;
         },
 
         defaultRoute: function() {
@@ -94,6 +150,7 @@ define([
                 homeView = new HomeView({
                     collection:that.applications,
                     locations:that.locations,
+                    cautionOverlay:serverCautionOverlay,
                     appRouter:that
                 });
                 veryFirstViewLoad = !that.currentView;
@@ -102,13 +159,10 @@ define([
             this.applications.fetch({success:function () {
                 render();
                 // show add application wizard if none already exist and this is the first page load
-                if ((veryFirstViewLoad && trail=='auto' && that.applications.isEmpty()) ||
-                     (trail=='add_application') ) {
-                    ha.onLoad(function() {
-                        if (ha.isMaster()) {
-                            homeView.createApplication();
-                        }
-                    });
+                if ((veryFirstViewLoad && trail=='auto' && that.applications.isEmpty()) || (trail=='add_application') ) {
+                    if (serverStatus.isMaster()) {
+                        homeView.createApplication();
+                    }
                 }
             }, error: render});
         },
@@ -156,64 +210,17 @@ define([
         labsPage:function () {
             $("#application-content").html(_.template(LabsHtml, {}))
             $(".nav1").removeClass("active")
-        }
-    })
-
-    var HaStandbyOverlay = Backbone.View.extend({
-        template: _.template(ServerNotMasterHtml),
-        scheduledRedirect: false,
-        initialize: function() {
-            this.carryOnRegardless = false;
-            this.listenTo(ha, "change", this.render);
         },
-        render: function() {
-            var that = this;
-            
-            if (!ha.isMaster() && !this.carryOnRegardless) {
-                var masterUri = ha.getMasterUri();
-                this.$el.html(this.template({"masterUri": masterUri}));
-                
-                log("render, redirect = "+this.redirectPending);
-                
-                $("#dismiss-standby-warning", this.$el).click(function() {
-                    that.carryOnRegardless = true;
-                    if (that.redirectPending) {
-                        console.log("Cancelling redirect, using this non-master instance");
-                        clearTimeout(that.redirectPending);
-                        that.redirectPending = null;
-                    }       
-                    that.render();
-                });
-                
-                if (masterUri && !this.scheduledRedirect && !this.redirectPending) {
-                    var destination = masterUri + "#" + Backbone.history.fragment;
-                    var time = 10;
-                    this.scheduledRedirect = true;
-                    console.log("Redirecting to " + destination + " in " + time + " seconds");
-                    this.redirectPending = setTimeout(function () {
-                        // re-check, in case the server's status changed in the wait
-                        if (!ha.isMaster()) {
-                            if (that.redirectPending) {
-                                window.location.href = destination;
-                            } else {
-                                console.log("Cancelled redirect, using this non-master instance");
-                            }
-                        } else {
-                            console.log("Cancelled redirect, this instance is now master");
-                        }
-                    }, time * 1000);
-                }
-            } else {
-                this.$el.empty();
-            }
-            return this;
-        },
-        beforeClose: function() {
-            this.stopListening();
+
+        /** Triggers the Backbone.Router process which drives this GUI through Backbone.history,
+         *  after starting background server health checks and waiting for confirmation of health
+         *  (or user click-through). */
+        startBrooklynGui: function() {
+            serverStatus.whenUp(function() { Backbone.history.start(); });
+            serverStatus.autoUpdate();
+            _.delay(serverCautionOverlay.warnIfNotLoaded, 2*1000)
         }
     });
-    new HaStandbyOverlay({ el: $("#ha-standby-overlay") }).render();
-
 
     $.ajax({
         type: "GET",
@@ -221,7 +228,7 @@ define([
         dataType: "text"
     }).done(function (data) {
         if (data != null) {
-            $("#user").html(data);
+            $("#user").html(_.escape(data));
         }
     });
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/main/webapp/assets/js/view/ha-summary.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/ha-summary.js b/usage/jsgui/src/main/webapp/assets/js/view/ha-summary.js
index 62b5c3e..2032d1d 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/ha-summary.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/ha-summary.js
@@ -18,9 +18,9 @@
 */
 define([
     "jquery", "underscore", "backbone", "moment", "view/viewutils",
-    "model/ha",
+    "model/server-extended-status",
     "text!tpl/home/ha-summary.html"
-], function ($, _, Backbone, moment, ViewUtils, ha, HASummaryHtml) {
+], function ($, _, Backbone, moment, ViewUtils, serverStatus, HASummaryHtml) {
 
     var template = _.template(HASummaryHtml);
     var nodeRowTemplate = _.template(
@@ -38,26 +38,27 @@ define([
         initialize: function() {
             _.bindAll(this);
             this.updateTimestampCallback = setInterval(this.updateTimestamps, 1000);
-            this.listenTo(ha, "change", this.renderNodeStatus);
+            this.listenTo(serverStatus, "change", this.renderNodeStatus);
         },
         beforeClose: function() {
             clearInterval(this.updateTimestampCallback);
             this.stopListening();
         },
         updateNow: function() {
-            ha.fetch();
+            serverStatus.fetch();
         },
         render: function() {
             this.$el.html(template());
-            if (ha.loaded) {
+            if (serverStatus.loaded) {
                 this.renderNodeStatus();
             }
             return this;
         },
         renderNodeStatus: function() {
-            var master = ha.get("masterId"),
-                self = ha.get("ownId"),
-                nodes = ha.get("nodes"),
+            var serverHa = serverStatus.get("ha") || {};
+            var master = serverHa.masterId,
+                self = serverHa.ownId,
+                nodes = serverHa.nodes,
                 $target = this.$(".ha-summary-table-body");
             $target.empty();
             // undefined check just in case server returns something odd

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/main/webapp/assets/js/view/home.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/view/home.js b/usage/jsgui/src/main/webapp/assets/js/view/home.js
index de9ce69..ae2ba3a 100644
--- a/usage/jsgui/src/main/webapp/assets/js/view/home.js
+++ b/usage/jsgui/src/main/webapp/assets/js/view/home.js
@@ -139,7 +139,9 @@ define([
                 this._modal.close()
             }
             var that = this;
-            if (!this.options.offline) {
+            if (this.options.offline || (this.options.cautionOverlay && this.options.cautionOverlay.warningActive)) {
+                // don't show wizard
+            } else {
                 var wizard = new AppAddWizard({appRouter:this.options.appRouter})
                 this._modal = wizard
                 this.$(".add-app #modal-container").html(wizard.render().el)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/main/webapp/assets/tpl/home/server-caution.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/server-caution.html b/usage/jsgui/src/main/webapp/assets/tpl/home/server-caution.html
new file mode 100644
index 0000000..0f767c0
--- /dev/null
+++ b/usage/jsgui/src/main/webapp/assets/tpl/home/server-caution.html
@@ -0,0 +1,94 @@
+
+<!--
+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.
+-->
+
+<div class="server-caution-overlay overlay-with-opacity"></div>
+<div class="server-caution-overlay">
+    <div class="overlay-content">
+        <h1 class="lead">Warning!</h1>
+
+<% if (!loaded) { %>
+        <p><strong>
+            Connecting to server...</strong></p>
+
+            <center><img src="/assets/images/throbber.gif"/></center>        
+        <p>
+            No response from server (yet). 
+            Please check your network connection, or if you are on a slow connection simply be patient!
+            This dialog will disappear if and when the server responds.</p>
+        
+        <p><i>
+            If you understand the risks, that information may be unavailable and operations may fail, 
+            and wish to proceed regardless, click <a id="dismiss-standby-warning">here</a>.</i></p>
+
+<% } else if (!up) { %>
+            <div style="float: right; position: relative; top: -45px; height: 0; overflow: visible;">
+                <img src="/assets/img/icon-status-starting.gif" width="60"/></div>
+            
+        <p><strong>
+            Starting up...</strong></p>
+                    
+        <p>
+            The Brooklyn server is starting.
+            It is recommended to wait before proceeding.
+            This dialog will disappear when the server is ready.</p>
+        
+        <p><i>
+            If you understand the risks, that information may be unavailable and operations may fail, 
+            and wish to proceed regardless, click <a id="dismiss-standby-warning">here</a>.</i></p>
+
+<% } else if (healthy && !master) { %>
+        <p><strong>
+            This Brooklyn server is not the high availability master.</strong></p>
+        
+        <p>
+            It is recommended not to use this server directly.</p>
+            
+    <% if (masterUri) { %>
+        <p>Redirecting to the master server at <a href="<%= masterUri %>"><%= masterUri %></a> shortly.</p>
+    <% } else { %>
+        <p>The address of the master Brooklyn server is not currently known.</p>
+    <% } %>
+
+        <p><i>
+            If you understand the risks, that write operations will likely fail, 
+            and wish to proceed regardless, click <a id="dismiss-standby-warning">here</a>.</i></p>
+          
+<% } else { %>
+        <p><strong>
+            This Brooklyn server has errors.</strong></p>
+        
+        <p>
+            Please check with your system administrator.</p>
+
+    <% if (healthy) { /* this page should not be displayed in this case */ %>
+        <p>
+            The misconfiguration may be in Javascript as the server is reporting it is healthy.</p><% } %>
+        
+        <p><i>
+          If you would like to debug the server, click <a id="dismiss-standby-warning">here</a>
+          to dismiss this warning until you reload this page. 
+          (You should reload the page once you believe the errors are fixed
+          to confirm that this dialog does not return.)
+          </i></p>
+
+<% } %>
+
+    </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/main/webapp/assets/tpl/home/server-not-ha-master.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/tpl/home/server-not-ha-master.html b/usage/jsgui/src/main/webapp/assets/tpl/home/server-not-ha-master.html
deleted file mode 100644
index b9b8b73..0000000
--- a/usage/jsgui/src/main/webapp/assets/tpl/home/server-not-ha-master.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
-<!--
-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.
--->
-
-<div class="ha-standby-overlay overlay-with-opacity"></div>
-<div class="ha-standby-overlay">
-    <div class="overlay-content">
-        <h1 class="lead">Warning!</h1>
-        <p><strong>This Brooklyn server is not the high availability master.</strong></p>
-        <p>It is recommended not to use this server directly.</p>
-    <% if (masterUri) { %>
-        <p>Redirecting to the master server at <a href="<%= masterUri %>"><%= masterUri %></a> shortly.</p>
-    <% } else { %>
-        <p>The address of the master Brooklyn server is not currently known.</p>
-    <% } %>
-        <p><i>If you understand the risks, that write operations will likely fail, 
-          and wish to proceed regardless, click <a id="dismiss-standby-warning">here</a>.</i>
-    </div>
-</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/main/webapp/index.html
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/index.html b/usage/jsgui/src/main/webapp/index.html
index 856830e..fda3514 100644
--- a/usage/jsgui/src/main/webapp/index.html
+++ b/usage/jsgui/src/main/webapp/index.html
@@ -67,7 +67,7 @@ under the License.
 
 <div id="main-content">
     <div id="application-content"></div>
-    <div id="ha-standby-overlay"></div>
+    <div id="server-caution-overlay"></div>
 </div>
 
 <!-- load our code with require.js. data-main is replaced with optimised file at build -->

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/test/javascript/specs/model/ha-spec.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/test/javascript/specs/model/ha-spec.js b/usage/jsgui/src/test/javascript/specs/model/ha-spec.js
deleted file mode 100644
index 0e51ef0..0000000
--- a/usage/jsgui/src/test/javascript/specs/model/ha-spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
-*/
-define([
-    "model/ha"
-], function (ha) {
-
-    describe("model/ha", function() {
-
-        beforeEach(function () {
-            ha.url = "fixtures/ha-summary.json";
-            ha.fetch();
-        });
-
-        it("should determine the master's URI", function() {
-            expect(ha.getMasterUri()).toBe("http://10.30.40.50:8081/");
-        });
-
-        it("should determine isMaster", function() {
-            expect(ha.isMaster()).toBe(true);
-            ha.set("masterId", "something else");
-            expect(ha.isMaster()).toBe(false);
-        });
-
-    });
-});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/jsgui/src/test/javascript/specs/router-spec.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/test/javascript/specs/router-spec.js b/usage/jsgui/src/test/javascript/specs/router-spec.js
index bd30eca..0041373 100644
--- a/usage/jsgui/src/test/javascript/specs/router-spec.js
+++ b/usage/jsgui/src/test/javascript/specs/router-spec.js
@@ -20,9 +20,6 @@ define([
     "brooklyn", "router", "model/ha"
 ], function (Brooklyn, Router, ha) {
 
-    ha.url = "fixtures/ha-summary.json";
-    ha.loaded = true;
-
     var View = Backbone.View.extend({
         render:function () {
             this.$el.html("<p>fake view</p>")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
----------------------------------------------------------------------
diff --git a/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java b/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
index 7ee371f..a1ac60c 100644
--- a/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
+++ b/usage/rest-api/src/main/java/brooklyn/rest/api/ServerApi.java
@@ -101,6 +101,11 @@ public interface ServerApi {
     public String getStatus();
 
     @GET
+    @Path("/up/extended")
+    @ApiOperation(value = "Returns extended server-up information, a map including up (/up), healthy (/healthy), and ha (/ha/states) (qv)")
+    public Map<String,Object> getUpExtended();
+
+    @GET
     @Path("/config/{configKey}")
     @ApiOperation(value = "Get the value of the specified config key from brooklyn properties")
     @ApiErrors(value = {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/rest-server/src/main/java/brooklyn/rest/filter/HaHotCheckResourceFilter.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/filter/HaHotCheckResourceFilter.java b/usage/rest-server/src/main/java/brooklyn/rest/filter/HaHotCheckResourceFilter.java
index c219de2..dc2e954 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/filter/HaHotCheckResourceFilter.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/filter/HaHotCheckResourceFilter.java
@@ -29,12 +29,12 @@ import javax.ws.rs.core.Response;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import brooklyn.entity.rebind.RebindManagerImpl.RebindTracker;
 import brooklyn.management.ManagementContext;
 import brooklyn.management.ha.ManagementNodeState;
 import brooklyn.rest.domain.ApiError;
-import brooklyn.util.time.Duration;
+import brooklyn.util.text.Strings;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableSet;
 import com.sun.jersey.api.model.AbstractMethod;
 import com.sun.jersey.spi.container.ContainerRequest;
@@ -43,17 +43,32 @@ import com.sun.jersey.spi.container.ContainerResponseFilter;
 import com.sun.jersey.spi.container.ResourceFilter;
 import com.sun.jersey.spi.container.ResourceFilterFactory;
 
+/** 
+ * Checks that if the method or resource class corresponding to a request
+ * has a {@link HaHotStateRequired} annotation,
+ * that the server is in that state (and up). 
+ * Requests with {@link #SKIP_CHECK_HEADER} set as a header skip this check.
+ * <p>
+ * This follows a different pattern to {@link HaMasterCheckFilter} 
+ * as this needs to know the method being invoked. 
+ */
 public class HaHotCheckResourceFilter implements ResourceFilterFactory {
     
     private static final Logger log = LoggerFactory.getLogger(HaHotCheckResourceFilter.class);
     
     private static final Set<ManagementNodeState> HOT_STATES = ImmutableSet.of(
             ManagementNodeState.MASTER, ManagementNodeState.HOT_STANDBY, ManagementNodeState.HOT_BACKUP);
-    private static final long STATE_CHANGE_SETTLE_OFFSET = Duration.seconds(10).toMilliseconds();
 
     @Context
     private ManagementContext mgmt;
 
+    public HaHotCheckResourceFilter() {}
+    
+    @VisibleForTesting
+    public HaHotCheckResourceFilter(ManagementContext mgmt) {
+        this.mgmt = mgmt;
+    }
+    
     private static class MethodFilter implements ResourceFilter, ContainerRequestFilter {
 
         private AbstractMethod am;
@@ -74,39 +89,54 @@ public class HaHotCheckResourceFilter implements ResourceFilterFactory {
             return null;
         }
 
+        private String lookForProblem(ContainerRequest request) {
+            if (isSkipCheckHeaderSet(request)) 
+                return null;
+            
+            if (!isHaHotStateRequired(request))
+                return null;
+            
+            String problem = HaMasterCheckFilter.lookForProblemIfServerNotRunning(mgmt);
+            if (Strings.isNonBlank(problem)) 
+                return problem;
+            
+            if (!isHaHotStatus())
+                return "server not in required HA hot state";
+            if (isStateNotYetValid())
+                return "server not yet completed loading data for required HA hot state";
+            
+            return null;
+        }
+        
         @Override
         public ContainerRequest filter(ContainerRequest request) {
-            if (!isStateLoaded() && isUnsafe(request)) {
-                log.warn("Disallowed request to standby brooklyn: "+request+"/"+am+" (caller should set '"+HaMasterCheckFilter.SKIP_CHECK_HEADER+"' to force)");
+            String problem = lookForProblem(request);
+            if (Strings.isNonBlank(problem)) {
+                log.warn("Disallowing request as "+problem+": "+request+"/"+am+" (caller should set '"+HaMasterCheckFilter.SKIP_CHECK_HEADER+"' to force)");
                 throw new WebApplicationException(ApiError.builder()
-                    .message("This request is not permitted against a standby Brooklyn server")
+                    .message("This request is only permitted against an active hot Brooklyn server")
                     .errorCode(Response.Status.FORBIDDEN).build().asJsonResponse());
             }
             return request;
         }
 
-        private boolean isStateLoaded() {
-            return isHaHotStatusOrDisabled() && !RebindTracker.isRebinding() && !recentlySwitchedState();
+        // Maybe there should be a separate state to indicate that we have switched state
+        // but still haven't finished rebinding. (Previously there was a time delay and an
+        // isRebinding check, but introducing RebindManager#isAwaitingInitialRebind() seems cleaner.)
+        private boolean isStateNotYetValid() {
+            return mgmt.getRebindManager().isAwaitingInitialRebind();
         }
 
-        // Ideally there will be a separate state to indicate that we switched state
-        // but still haven't finished rebinding. There's a gap between changing the state
-        // and starting rebind so add a time offset just to be sure.
-        private boolean recentlySwitchedState() {
-            long lastStateChange = mgmt.getHighAvailabilityManager().getLastStateChange();
-            if (lastStateChange == -1) return false;
-            return System.currentTimeMillis() - lastStateChange < STATE_CHANGE_SETTLE_OFFSET;
+        private boolean isHaHotStateRequired(ContainerRequest request) {
+            return (am.getAnnotation(HaHotStateRequired.class) != null ||
+                    am.getResource().getAnnotation(HaHotStateRequired.class) != null);
         }
 
-        private boolean isUnsafe(ContainerRequest request) {
-            boolean isOverriden = "true".equalsIgnoreCase(request.getHeaderValue(HaMasterCheckFilter.SKIP_CHECK_HEADER));
-            return !isOverriden &&
-                    (am.getAnnotation(HaHotStateRequired.class) != null ||
-                    am.getResource().getAnnotation(HaHotStateRequired.class) != null);
+        private boolean isSkipCheckHeaderSet(ContainerRequest request) {
+            return "true".equalsIgnoreCase(request.getHeaderValue(HaMasterCheckFilter.SKIP_CHECK_HEADER));
         }
 
-        private boolean isHaHotStatusOrDisabled() {
-            if (!mgmt.getHighAvailabilityManager().isRunning()) return true;
+        private boolean isHaHotStatus() {
             ManagementNodeState state = mgmt.getHighAvailabilityManager().getNodeState();
             return HOT_STATES.contains(state);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/rest-server/src/main/java/brooklyn/rest/filter/HaMasterCheckFilter.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/filter/HaMasterCheckFilter.java b/usage/rest-server/src/main/java/brooklyn/rest/filter/HaMasterCheckFilter.java
index bfb2cf4..9cca507 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/filter/HaMasterCheckFilter.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/filter/HaMasterCheckFilter.java
@@ -40,13 +40,15 @@ import brooklyn.management.ManagementContext;
 import brooklyn.management.ha.ManagementNodeState;
 import brooklyn.rest.domain.ApiError;
 import brooklyn.rest.util.WebResourceUtils;
+import brooklyn.util.text.Strings;
 
 import com.google.common.collect.Sets;
 
 /**
- * Checks that the request is appropriate given the high availability status of the server.
- *
- * @see brooklyn.management.ha.ManagementNodeState
+ * Checks that for requests that want HA master state, the server is up and in that state.
+ * <p>
+ * Post POSTs and PUTs are assumed to need master state, with the exception of shutdown.
+ * Requests with {@link #SKIP_CHECK_HEADER} set as a header skip this check.
  */
 public class HaMasterCheckFilter implements Filter {
 
@@ -64,12 +66,37 @@ public class HaMasterCheckFilter implements Filter {
         mgmt = (ManagementContext) servletContext.getAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT);
     }
 
+    static String lookForProblemIfServerNotRunning(ManagementContext mgmt) {
+        if (mgmt==null) return "no management context available";
+        if (!mgmt.isRunning()) return "server no longer running";
+        if (!mgmt.isStartupComplete()) return "server not in required startup-completed state";
+        return null;
+    }
+    
+    private String lookForProblem(ServletRequest request) {
+        if (isSkipCheckHeaderSet(request)) 
+            return null;
+        
+        if (!isMasterRequiredForRequest(request))
+            return null;
+        
+        String problem = lookForProblemIfServerNotRunning(mgmt);
+        if (Strings.isNonBlank(problem)) 
+            return problem;
+        
+        if (!isMaster()) 
+            return "server not in required HA master state";
+        
+        return null;
+    }
+    
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-        if (!isMaster() && !isRequestAllowedForNonMaster(request)) {
-            log.warn("Disallowed request to non-HA-master brooklyn: "+request+"/"+request.getParameterMap()+" (caller should set '"+SKIP_CHECK_HEADER+"' to force)");
+        String problem = lookForProblem(request);
+        if (problem!=null) {
+            log.warn("Disallowing request as "+problem+": "+request.getParameterMap()+" (caller should set '"+SKIP_CHECK_HEADER+"' to force)");
             WebResourceUtils.applyJsonResponse(servletContext, ApiError.builder()
-                .message("This request is only permitted against a master Brooklyn server")
+                .message("This request is only permitted against an active master Brooklyn server")
                 .errorCode(Response.Status.FORBIDDEN).build().asJsonResponse(), (HttpServletResponse)response);
         } else {
             chain.doFilter(request, response);
@@ -84,23 +111,30 @@ public class HaMasterCheckFilter implements Filter {
         return ManagementNodeState.MASTER.equals(mgmt.getHighAvailabilityManager().getNodeState());
     }
 
-    private boolean isRequestAllowedForNonMaster(ServletRequest request) {
+    private boolean isMasterRequiredForRequest(ServletRequest request) {
         if (request instanceof HttpServletRequest) {
             HttpServletRequest httpRequest = (HttpServletRequest) request;
-            String checkOverridden = httpRequest.getHeader(SKIP_CHECK_HEADER);
-            if ("true".equalsIgnoreCase(checkOverridden)) return true;
             
             String method = httpRequest.getMethod().toUpperCase();
-            if (SAFE_STANDBY_METHODS.contains(method)) return true;
+            // gets usually okay
+            if (SAFE_STANDBY_METHODS.contains(method)) return false;
             
             // explicitly allow calls to shutdown
             // (if stopAllApps is specified, the method itself will fail; but we do not want to consume parameters here, that breaks things!)
             // TODO combine with HaHotCheckResourceFilter and use an annotation HaAnyStateAllowed or similar
-            if ("/v1/server/shutdown".equals(httpRequest.getRequestURI())) return true;
+            if ("/v1/server/shutdown".equals(httpRequest.getRequestURI())) return false;
             
-            return false;
+            // master required for everything else
+            return true;
         }
         // previously non-HttpServletRequests were allowed but I don't think they should be
+        return true;
+    }
+
+    private boolean isSkipCheckHeaderSet(ServletRequest httpRequest) {
+        if (httpRequest instanceof HttpServletRequest)
+            return "true".equalsIgnoreCase(((HttpServletRequest)httpRequest).getHeader(SKIP_CHECK_HEADER));
         return false;
     }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/rest-server/src/main/java/brooklyn/rest/filter/ServerStatusCheckFilter.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/filter/ServerStatusCheckFilter.java b/usage/rest-server/src/main/java/brooklyn/rest/filter/ServerStatusCheckFilter.java
new file mode 100644
index 0000000..d5c30a3
--- /dev/null
+++ b/usage/rest-server/src/main/java/brooklyn/rest/filter/ServerStatusCheckFilter.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package brooklyn.rest.filter;
+
+import java.io.IOException;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.config.BrooklynServiceAttributes;
+import brooklyn.management.ManagementContext;
+import brooklyn.management.ha.ManagementNodeState;
+import brooklyn.rest.domain.ApiError;
+import brooklyn.rest.util.WebResourceUtils;
+
+import com.google.common.collect.Sets;
+
+/**
+ * Checks that the request is appropriate given the high availability status of the server.
+ *
+ * @see brooklyn.management.ha.ManagementNodeState
+ */
+public class ServerStatusCheckFilter implements Filter {
+
+    private static final Logger log = LoggerFactory.getLogger(ServerStatusCheckFilter.class);
+    
+    public static final String SKIP_CHECK_HEADER = "Brooklyn-Allow-Non-Master-Access";
+    private static final Set<String> SAFE_STANDBY_METHODS = Sets.newHashSet("GET", "HEAD");
+
+    protected ServletContext servletContext;
+    protected ManagementContext mgmt;
+
+    @Override
+    public void init(FilterConfig config) throws ServletException {
+        servletContext = config.getServletContext();
+        mgmt = (ManagementContext) servletContext.getAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT);
+    }
+
+    private String lookForProblem(ServletRequest request) {
+        if (isMasterRequiredForRequest(request)) {
+            if (mgmt==null) return "no management context available";
+            if (!mgmt.isRunning()) return "server no longer running";
+            if (!mgmt.isStartupComplete()) return "server not in required startup-completed state";
+            if (!isMaster()) return "server not in required HA master state";
+        }
+        return null;
+    }
+    
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        String problem = lookForProblem(request);
+        if (problem!=null) {
+            log.warn("Disallowing request as "+problem+"/"+request.getParameterMap()+" (caller should set '"+SKIP_CHECK_HEADER+"' to force)");
+            WebResourceUtils.applyJsonResponse(servletContext, ApiError.builder()
+                .message("This request is only permitted against an active master Brooklyn server")
+                .errorCode(Response.Status.FORBIDDEN).build().asJsonResponse(), (HttpServletResponse)response);
+        } else {
+            chain.doFilter(request, response);
+        }
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    private boolean isMaster() {
+        return ManagementNodeState.MASTER.equals(mgmt.getHighAvailabilityManager().getNodeState());
+    }
+
+    private boolean isMasterRequiredForRequest(ServletRequest request) {
+        if (request instanceof HttpServletRequest) {
+            HttpServletRequest httpRequest = (HttpServletRequest) request;
+            String checkOverridden = httpRequest.getHeader(SKIP_CHECK_HEADER);
+            if ("true".equalsIgnoreCase(checkOverridden)) return false;
+            
+            String method = httpRequest.getMethod().toUpperCase();
+            // gets usually okay
+            if (SAFE_STANDBY_METHODS.contains(method)) return false;
+            
+            // explicitly allow calls to shutdown
+            // (if stopAllApps is specified, the method itself will fail; but we do not want to consume parameters here, that breaks things!)
+            // TODO combine with HaHotCheckResourceFilter and use an annotation HaAnyStateAllowed or similar
+            if ("/v1/server/shutdown".equals(httpRequest.getRequestURI())) return false;
+            
+            // master required for everything else
+            return true;
+        }
+        // previously non-HttpServletRequests were allowed but I don't think they should be
+        return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
index eac52f6..ecefd4c 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/ServerResource.java
@@ -33,14 +33,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
-import brooklyn.config.ConfigKey;
-import brooklyn.entity.basic.ConfigKeys;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import brooklyn.BrooklynVersion;
+import brooklyn.config.ConfigKey;
 import brooklyn.entity.Application;
+import brooklyn.entity.basic.ConfigKeys;
 import brooklyn.entity.basic.Entities;
 import brooklyn.entity.basic.EntityLocal;
 import brooklyn.entity.basic.StartableApplication;
@@ -63,6 +62,7 @@ import brooklyn.rest.domain.VersionSummary;
 import brooklyn.rest.transform.HighAvailabilityTransformer;
 import brooklyn.rest.util.WebResourceUtils;
 import brooklyn.util.ResourceUtils;
+import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.file.ArchiveBuilder;
 import brooklyn.util.flags.TypeCoercions;
@@ -283,6 +283,15 @@ public class ServerResource extends AbstractBrooklynRestResource implements Serv
         return true;
     }
     
+    @Override
+    public Map<String,Object> getUpExtended() {
+        return MutableMap.<String,Object>of(
+            "up", isUp(),
+            "healthy", isHealthy(),
+            "ha", getHighAvailabilityPlaneStates());
+    }
+    
+    
     @Deprecated
     @Override
     public String getStatus() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
index 5643614..9941466 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/BrooklynRestApiLauncher.java
@@ -165,6 +165,7 @@ public class BrooklynRestApiLauncher {
         BrooklynCampPlatformLauncherAbstract platform = new BrooklynCampPlatformLauncherNoServer()
                 .useManagementContext(mgmt)
                 .launch();
+        ((LocalManagementContext)mgmt).noteStartupComplete();
         log.debug("started "+platform);
 
         ContextHandler context;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java b/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java
index 58a9e6b..e6ca1d1 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java
@@ -22,20 +22,20 @@ import static org.testng.Assert.assertEquals;
 
 import javax.ws.rs.core.MediaType;
 
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import brooklyn.management.ha.HighAvailabilityManager;
+import brooklyn.management.ha.HighAvailabilityMode;
 import brooklyn.management.ha.ManagementNodeState;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.management.internal.ManagementContextInternal;
 import brooklyn.rest.filter.HaHotCheckResourceFilter;
 import brooklyn.rest.filter.HaMasterCheckFilter;
 import brooklyn.rest.testing.BrooklynRestResourceTest;
-import brooklyn.rest.testing.mocks.ManagementContextMock;
 import brooklyn.rest.util.HaHotStateCheckClassResource;
 import brooklyn.rest.util.HaHotStateCheckResource;
-import brooklyn.rest.util.ManagementContextProvider;
 
 import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.WebResource.Builder;
@@ -43,75 +43,61 @@ import com.sun.jersey.api.core.ResourceConfig;
 
 public class HaHotCheckTest extends BrooklynRestResourceTest {
 
-    private ManagementContextMock mgmtMock;
-
-    @Override
-    @BeforeClass(alwaysRun = true)
-    public void setUp() throws Exception {
-        mgmtMock = new ManagementContextMock();
-        super.setUp();
-    }
-
-    @Override
-    @AfterClass(alwaysRun = true)
-    public void tearDown() throws Exception {
-        super.tearDown();
-    }
-
     @BeforeMethod(alwaysRun = true)
-    public void setUpMethod() {
-        mgmtMock.setState(ManagementNodeState.MASTER);
-    }
+    public void setUp() throws Exception { super.setUp(); }
 
+    @AfterMethod(alwaysRun = false)
+    public void tearDown() throws Exception { super.tearDown(); }
+    
     @Override
     protected void addBrooklynResources() {
-        config.getSingletons().add(new ManagementContextProvider(mgmtMock));
-        config.getProperties().put(ResourceConfig.PROPERTY_RESOURCE_FILTER_FACTORIES, HaHotCheckResourceFilter.class.getName());
+        config.getProperties().put(ResourceConfig.PROPERTY_RESOURCE_FILTER_FACTORIES, 
+            new HaHotCheckResourceFilter(getManagementContext()));
         addResource(new HaHotStateCheckResource());
         addResource(new HaHotStateCheckClassResource());
+        
+        ((LocalManagementContext)getManagementContext()).noteStartupComplete();
     }
 
     @Test
     public void testHaCheck() {
-        HighAvailabilityManager ha = mgmtMock.getHighAvailabilityManager();
+        HighAvailabilityManager ha = getManagementContext().getHighAvailabilityManager();
         assertEquals(ha.getNodeState(), ManagementNodeState.MASTER);
         testResourceFetch("/ha/method/ok", 200);
         testResourceFetch("/ha/method/fail", 200);
         testResourceFetch("/ha/class/fail", 200);
 
-        mgmtMock.setState(ManagementNodeState.STANDBY);
+        getManagementContext().getHighAvailabilityManager().changeMode(HighAvailabilityMode.STANDBY);
         assertEquals(ha.getNodeState(), ManagementNodeState.STANDBY);
 
         testResourceFetch("/ha/method/ok", 200);
         testResourceFetch("/ha/method/fail", 403);
         testResourceFetch("/ha/class/fail", 403);
 
-        //forces isRunning = false
-        mgmtMock.setState(ManagementNodeState.TERMINATED);
+        ((ManagementContextInternal)getManagementContext()).terminate();
         assertEquals(ha.getNodeState(), ManagementNodeState.TERMINATED);
 
         testResourceFetch("/ha/method/ok", 200);
-        testResourceFetch("/ha/method/fail", 200);
-        testResourceFetch("/ha/class/fail", 200);
+        testResourceFetch("/ha/method/fail", 403);
+        testResourceFetch("/ha/class/fail", 403);
     }
 
     @Test
     public void testHaCheckForce() {
-        HighAvailabilityManager ha = mgmtMock.getHighAvailabilityManager();
+        HighAvailabilityManager ha = getManagementContext().getHighAvailabilityManager();
         assertEquals(ha.getNodeState(), ManagementNodeState.MASTER);
         testResourceForcedFetch("/ha/method/ok", 200);
         testResourceForcedFetch("/ha/method/fail", 200);
         testResourceForcedFetch("/ha/class/fail", 200);
 
-        mgmtMock.setState(ManagementNodeState.STANDBY);
+        getManagementContext().getHighAvailabilityManager().changeMode(HighAvailabilityMode.STANDBY);
         assertEquals(ha.getNodeState(), ManagementNodeState.STANDBY);
 
         testResourceForcedFetch("/ha/method/ok", 200);
         testResourceForcedFetch("/ha/method/fail", 200);
         testResourceForcedFetch("/ha/class/fail", 200);
 
-        //forces isRunning = false
-        mgmtMock.setState(ManagementNodeState.TERMINATED);
+        ((ManagementContextInternal)getManagementContext()).terminate();
         assertEquals(ha.getNodeState(), ManagementNodeState.TERMINATED);
 
         testResourceForcedFetch("/ha/method/ok", 200);
@@ -119,6 +105,7 @@ public class HaHotCheckTest extends BrooklynRestResourceTest {
         testResourceForcedFetch("/ha/class/fail", 200);
     }
 
+
     private void testResourceFetch(String resourcePath, int code) {
         testResourceFetch(resourcePath, false, code);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e629602b/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java b/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
index d7c461e..423743f 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
@@ -42,16 +42,26 @@ import brooklyn.management.entitlement.EntitlementManager;
 import brooklyn.management.ha.HighAvailabilityManager;
 import brooklyn.management.ha.ManagementNodeState;
 import brooklyn.util.guava.Maybe;
+import brooklyn.util.javalang.JavaClassNames;
 
 public class ManagementContextMock implements ManagementContext {
+    private Boolean running;
+    private Boolean startupComplete;
     private HighAvailabilityManagerStub haMock = new HighAvailabilityManagerStub();
 
+    public void setRunning(Boolean running) {
+        this.running = running;
+    }
+    public void setStartupComplete(Boolean startupComplete) {
+        this.startupComplete = startupComplete;
+    }
+    
     public void setState(ManagementNodeState state) {
         haMock.setState(state);
     }
 
     private static RuntimeException fail() {
-        throw new UnsupportedOperationException("Mocked method not implemented");
+        throw new UnsupportedOperationException("Mocked method not implemented - "+JavaClassNames.callerNiceClassAndMethod(1));
     }
 
     @Override
@@ -131,12 +141,14 @@ public class ManagementContextMock implements ManagementContext {
 
     @Override
     public boolean isRunning() {
-        throw fail();
+        if (running==null) throw fail();
+        return running;
     }
 
     @Override
     public boolean isStartupComplete() {
-        throw fail();
+        if (startupComplete==null) throw fail();
+        return startupComplete;
     }
     
     @Override


[6/7] incubator-brooklyn git commit: address code review for startup info

Posted by he...@apache.org.
address code review for startup info

JS masterUri fix, HA check cleanups, removing unneeded classes, and catch catalog error


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/7556c582
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/7556c582
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/7556c582

Branch: refs/heads/master
Commit: 7556c582ae253a655eb76abba92b75ac811a77b3
Parents: 7d0f1a0
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Wed May 6 10:48:16 2015 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Wed May 6 10:48:16 2015 +0100

----------------------------------------------------------------------
 .../entity/brooklynnode/BrooklynNodeImpl.java   |   4 +
 .../BrooklynNodeIntegrationTest.java            |  23 ++-
 .../assets/js/model/server-extended-status.js   |  10 +-
 .../rest/filter/ServerStatusCheckFilter.java    | 120 -----------
 .../rest/resources/CatalogResource.java         |  17 +-
 .../test/java/brooklyn/rest/HaHotCheckTest.java |   4 +-
 .../brooklyn/rest/HaMasterCheckFilterTest.java  |   3 +-
 .../rest/testing/BrooklynRestResourceTest.java  |   2 +-
 .../mocks/HighAvailabilityManagerStub.java      | 121 -----------
 .../testing/mocks/ManagementContextMock.java    | 206 -------------------
 10 files changed, 47 insertions(+), 463 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7556c582/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
index d4b4992..9fd34eb 100644
--- a/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
+++ b/software/base/src/main/java/brooklyn/entity/brooklynnode/BrooklynNodeImpl.java
@@ -466,6 +466,10 @@ public class BrooklynNodeImpl extends SoftwareProcessImpl implements BrooklynNod
                     .baseUri(webConsoleUri)
                     .credentialsIfNotNull(getConfig(MANAGEMENT_USER), getConfig(MANAGEMENT_PASSWORD))
                     .poll(new HttpPollConfig<Boolean>(WEB_CONSOLE_ACCESSIBLE)
+                            // TODO `BrooklynNode` shouldn't report `SERVICE_UP` until `/v1/server/healthy` returns true;
+                            // but this should wait until v0.8.0 as we'll need managed nodes to implement `/v1/server/healthy` (added May 2015);
+                            // when that is added BrooklynNodeIntegrationTest.waitForApps can remove the 403 check, it should require 200 always.
+                            // .suburl("/v1/server/healthy")  then check: responseTextEquals("true")
                             .onSuccess(HttpValueFunctions.responseCodeEquals(200))
                             .setOnFailureOrException(false))
                     .poll(new HttpPollConfig<ManagementNodeState>(MANAGEMENT_NODE_STATE)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7556c582/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
index 717eb68..ff7b5fc 100644
--- a/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
+++ b/software/base/src/test/java/brooklyn/entity/brooklynnode/BrooklynNodeIntegrationTest.java
@@ -347,23 +347,31 @@ services:
         log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
+        waitForApps(webConsoleUri.toString());
         String apps = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications");
         List<String> appType = parseJsonList(apps, ImmutableList.of("spec", "type"), String.class);
         assertEquals(appType, ImmutableList.of(BasicApplication.class.getName()));
     }
 
+    protected static void waitForApps(String webConsoleUri) {
+        HttpTestUtils.assertHttpStatusCodeEquals(webConsoleUri+"/v1/applications", 200, 403);
+        HttpTestUtils.assertHttpStatusCodeEventuallyEquals(webConsoleUri+"/v1/applications", 200);
+    }
+
     @Test(groups="Integration")
     public void testStartsAppViaEffector() throws Exception {
         BrooklynNode brooklynNode = app.createAndManageChild(newBrooklynNodeSpecForTest());
         app.start(locs);
         log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
         
+        // note there is also a test for this in DeployApplication
+        final URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
+        waitForApps(webConsoleUri.toString());
+
         final String id = brooklynNode.invoke(BrooklynNode.DEPLOY_BLUEPRINT, ConfigBag.newInstance()
             .configure(DeployBlueprintEffector.BLUEPRINT_TYPE, BasicApplication.class.getName())
             .getAllConfig()).get();
         
-        // note there is also a test for this in DeployApplication
-        final URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
         String apps = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/applications");
         List<String> appType = parseJsonList(apps, ImmutableList.of("spec", "type"), String.class);
         assertEquals(appType, ImmutableList.of(BasicApplication.class.getName()));
@@ -389,6 +397,7 @@ services:
         log.info("started "+app+" containing "+brooklynNode+" for "+JavaClassNames.niceClassAndMethod());
 
         URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
+        waitForApps(webConsoleUri.toString());
 
         // Check that "mynamedloc" has been picked up from the brooklyn.properties
         String locsContent = HttpTestUtils.getContent(webConsoleUri.toString()+"/v1/locations");
@@ -486,6 +495,8 @@ services:
         File pidFile = new File(getDriver(brooklynNode).getPidFile());
         URI webConsoleUri = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI);
 
+        waitForApps(webConsoleUri.toString());
+
         // Stop just the process; will not have unmanaged entity unless machine was being terminated 
         brooklynNode.invoke(BrooklynNode.STOP, ImmutableMap.<String, Object>of(
                 BrooklynNode.StopSoftwareParameters.STOP_MACHINE_MODE.getName(), StopMode.NEVER,
@@ -512,7 +523,9 @@ services:
 
         // Note can't use driver.isRunning to check shutdown; can't invoke scripts on an unmanaged entity
         EntityTestUtils.assertAttributeEquals(brooklynNode, BrooklynNode.SERVICE_UP, false);
-        assertFalse(Entities.isManaged(brooklynNode));
+        // previously we unmanaged the node on stop, but that behaviour has been removed (noticed May 2015)
+        // TODO remove this after a couple of months, for awareness/confirmation
+//        assertFalse(Entities.isManaged(brooklynNode));
         assertFalse(isPidRunning(pidFile), "pid in "+pidFile+" still running");
     }
 
@@ -548,11 +561,13 @@ services:
 
         EntityTestUtils.assertAttributeEqualsEventually(brooklynNode, BrooklynNode.SERVICE_UP, true);
 
+        String baseUrl = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI).toString();
+        waitForApps(baseUrl);
+        
         final String id = brooklynNode.invoke(BrooklynNode.DEPLOY_BLUEPRINT, ConfigBag.newInstance()
                 .configure(DeployBlueprintEffector.BLUEPRINT_TYPE, BasicApplication.class.getName())
                 .getAllConfig()).get();
 
-        String baseUrl = brooklynNode.getAttribute(BrooklynNode.WEB_CONSOLE_URI).toString();
         String entityUrl = Urls.mergePaths(baseUrl, "v1/applications/", id, "entities", id);
         
         Entity mirror = brooklynNode.addChild(EntitySpec.create(BrooklynEntityMirror.class)

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7556c582/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
----------------------------------------------------------------------
diff --git a/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js b/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
index 2f5ccb3..e774294 100644
--- a/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
+++ b/usage/jsgui/src/main/webapp/assets/js/model/server-extended-status.js
@@ -73,13 +73,9 @@ define(["backbone", "brooklyn", "view/viewutils"], function (Backbone, Brooklyn,
         getMasterUri: function() {
             // Might be undefined if first fetch hasn't completed
             ha = this.get("ha") || {};
-            states = ha.states || {};
-            if (!states) return null;
-            
-            var nodes = this.get("nodes") || {};
-            var master = nodes[this.get("masterId")];
-            // defensive, if this happens something more serious has gone wrong!
-            if (!master) {
+            nodes = ha.nodes || {};
+            master = nodes[ha.masterId];
+            if (!master || master.status != "MASTER") {
                 return null;
             } else {
                 return master.nodeUri;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7556c582/usage/rest-server/src/main/java/brooklyn/rest/filter/ServerStatusCheckFilter.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/filter/ServerStatusCheckFilter.java b/usage/rest-server/src/main/java/brooklyn/rest/filter/ServerStatusCheckFilter.java
deleted file mode 100644
index d5c30a3..0000000
--- a/usage/rest-server/src/main/java/brooklyn/rest/filter/ServerStatusCheckFilter.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.rest.filter;
-
-import java.io.IOException;
-import java.util.Set;
-
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.ws.rs.core.Response;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import brooklyn.config.BrooklynServiceAttributes;
-import brooklyn.management.ManagementContext;
-import brooklyn.management.ha.ManagementNodeState;
-import brooklyn.rest.domain.ApiError;
-import brooklyn.rest.util.WebResourceUtils;
-
-import com.google.common.collect.Sets;
-
-/**
- * Checks that the request is appropriate given the high availability status of the server.
- *
- * @see brooklyn.management.ha.ManagementNodeState
- */
-public class ServerStatusCheckFilter implements Filter {
-
-    private static final Logger log = LoggerFactory.getLogger(ServerStatusCheckFilter.class);
-    
-    public static final String SKIP_CHECK_HEADER = "Brooklyn-Allow-Non-Master-Access";
-    private static final Set<String> SAFE_STANDBY_METHODS = Sets.newHashSet("GET", "HEAD");
-
-    protected ServletContext servletContext;
-    protected ManagementContext mgmt;
-
-    @Override
-    public void init(FilterConfig config) throws ServletException {
-        servletContext = config.getServletContext();
-        mgmt = (ManagementContext) servletContext.getAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT);
-    }
-
-    private String lookForProblem(ServletRequest request) {
-        if (isMasterRequiredForRequest(request)) {
-            if (mgmt==null) return "no management context available";
-            if (!mgmt.isRunning()) return "server no longer running";
-            if (!mgmt.isStartupComplete()) return "server not in required startup-completed state";
-            if (!isMaster()) return "server not in required HA master state";
-        }
-        return null;
-    }
-    
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-        String problem = lookForProblem(request);
-        if (problem!=null) {
-            log.warn("Disallowing request as "+problem+"/"+request.getParameterMap()+" (caller should set '"+SKIP_CHECK_HEADER+"' to force)");
-            WebResourceUtils.applyJsonResponse(servletContext, ApiError.builder()
-                .message("This request is only permitted against an active master Brooklyn server")
-                .errorCode(Response.Status.FORBIDDEN).build().asJsonResponse(), (HttpServletResponse)response);
-        } else {
-            chain.doFilter(request, response);
-        }
-    }
-
-    @Override
-    public void destroy() {
-    }
-
-    private boolean isMaster() {
-        return ManagementNodeState.MASTER.equals(mgmt.getHighAvailabilityManager().getNodeState());
-    }
-
-    private boolean isMasterRequiredForRequest(ServletRequest request) {
-        if (request instanceof HttpServletRequest) {
-            HttpServletRequest httpRequest = (HttpServletRequest) request;
-            String checkOverridden = httpRequest.getHeader(SKIP_CHECK_HEADER);
-            if ("true".equalsIgnoreCase(checkOverridden)) return false;
-            
-            String method = httpRequest.getMethod().toUpperCase();
-            // gets usually okay
-            if (SAFE_STANDBY_METHODS.contains(method)) return false;
-            
-            // explicitly allow calls to shutdown
-            // (if stopAllApps is specified, the method itself will fail; but we do not want to consume parameters here, that breaks things!)
-            // TODO combine with HaHotCheckResourceFilter and use an annotation HaAnyStateAllowed or similar
-            if ("/v1/server/shutdown".equals(httpRequest.getRequestURI())) return false;
-            
-            // master required for everything else
-            return true;
-        }
-        // previously non-HttpServletRequests were allowed but I don't think they should be
-        return true;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7556c582/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
index a3eb161..a93f9a0 100644
--- a/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
+++ b/usage/rest-server/src/main/java/brooklyn/rest/resources/CatalogResource.java
@@ -21,6 +21,7 @@ package brooklyn.rest.resources;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
@@ -454,8 +455,20 @@ public class CatalogResource extends AbstractBrooklynRestResource implements Cat
     @SuppressWarnings("unchecked")
     private static <T> List<T> castList(List<? super T> list, Class<T> elementType) {
         List<T> result = Lists.newArrayList();
-        for (Object element : list) {
-            result.add((T) element);
+        Iterator<? super T> li = list.iterator();
+        while (li.hasNext()) {
+            try {
+                result.add((T) li.next());
+            } catch (Throwable throwable) {
+                if (throwable instanceof NoClassDefFoundError) {
+                    // happens if class cannot be loaded for any reason during transformation - don't treat as fatal
+                } else {
+                    Exceptions.propagateIfFatal(throwable);
+                }
+                
+                // item cannot be transformed; we will have logged a warning earlier
+                log.debug("Ignoring invalid catalog item: "+throwable);
+            }
         }
         return result;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7556c582/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java b/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java
index e6ca1d1..7ce6a97 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/HaHotCheckTest.java
@@ -43,10 +43,12 @@ import com.sun.jersey.api.core.ResourceConfig;
 
 public class HaHotCheckTest extends BrooklynRestResourceTest {
 
+    // setup and teardown before/after each method
+    
     @BeforeMethod(alwaysRun = true)
     public void setUp() throws Exception { super.setUp(); }
 
-    @AfterMethod(alwaysRun = false)
+    @AfterMethod(alwaysRun = true)
     public void tearDown() throws Exception { super.tearDown(); }
     
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7556c582/usage/rest-server/src/test/java/brooklyn/rest/HaMasterCheckFilterTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/HaMasterCheckFilterTest.java b/usage/rest-server/src/test/java/brooklyn/rest/HaMasterCheckFilterTest.java
index 671a6b2..27d8d6c 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/HaMasterCheckFilterTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/HaMasterCheckFilterTest.java
@@ -64,6 +64,7 @@ public class HaMasterCheckFilterTest extends BrooklynRestApiLauncherTestFixture
 
     @AfterMethod(alwaysRun=true)
     public void tearDown() throws Exception {
+System.err.println("TEAR DOWN");
         server.stop();
         Entities.destroyAll(writeMgmt);
         Entities.destroyAll(readMgmt);
@@ -151,7 +152,7 @@ public class HaMasterCheckFilterTest extends BrooklynRestApiLauncherTestFixture
 
         writeMgmt = createManagementContext(mementoDir, writeMode);
         appId = createApp(writeMgmt);
-        writeMgmt.getRebindManager().getPersister().waitForWritesCompleted(TIMEOUT);
+        writeMgmt.getRebindManager().waitForPendingComplete(TIMEOUT);
 
         if (readMode == HighAvailabilityMode.DISABLED) {
             //no HA, one node only

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7556c582/usage/rest-server/src/test/java/brooklyn/rest/testing/BrooklynRestResourceTest.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/testing/BrooklynRestResourceTest.java b/usage/rest-server/src/test/java/brooklyn/rest/testing/BrooklynRestResourceTest.java
index 485d5a8..42a622a 100644
--- a/usage/rest-server/src/test/java/brooklyn/rest/testing/BrooklynRestResourceTest.java
+++ b/usage/rest-server/src/test/java/brooklyn/rest/testing/BrooklynRestResourceTest.java
@@ -60,7 +60,7 @@ public abstract class BrooklynRestResourceTest extends BrooklynRestApiTest {
         setUpJersey();
     }
 
-    @AfterClass(alwaysRun = false)
+    @AfterClass(alwaysRun = true)
     public void tearDown() throws Exception {
         tearDownJersey();
         super.tearDown();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7556c582/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/HighAvailabilityManagerStub.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/HighAvailabilityManagerStub.java b/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/HighAvailabilityManagerStub.java
deleted file mode 100644
index 98fb8d5..0000000
--- a/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/HighAvailabilityManagerStub.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.rest.testing.mocks;
-
-import java.util.Map;
-
-import brooklyn.management.ha.HighAvailabilityManager;
-import brooklyn.management.ha.HighAvailabilityMode;
-import brooklyn.management.ha.ManagementNodeState;
-import brooklyn.management.ha.ManagementPlaneSyncRecord;
-import brooklyn.management.ha.ManagementPlaneSyncRecordPersister;
-
-public class HighAvailabilityManagerStub implements HighAvailabilityManager {
-
-    private ManagementNodeState state = ManagementNodeState.MASTER;
-
-    public void setState(ManagementNodeState state) {
-        this.state = state;
-    }
-
-    private static RuntimeException fail() {
-        throw new UnsupportedOperationException("Mocked method not implemented");
-    }
-
-    @Override
-    public boolean isRunning() {
-        return state != ManagementNodeState.TERMINATED;
-    }
-
-    @Override
-    public ManagementNodeState getNodeState() {
-        return state;
-    }
-
-    @Override
-    public long getLastStateChange() {
-        return 0;
-    }
-
-    @Override
-    public HighAvailabilityManager setPersister(ManagementPlaneSyncRecordPersister persister) {
-        throw fail();
-    }
-
-    @Override
-    public void disabled() {
-        throw fail();
-    }
-
-    @Override
-    public void start(HighAvailabilityMode startMode) {
-        throw fail();
-    }
-
-    @Override
-    public void stop() {
-        throw fail();
-    }
-
-    @Override
-    public void changeMode(HighAvailabilityMode mode) {
-        throw fail();
-    }
-
-    @Override
-    public void setPriority(long priority) {
-        throw fail();
-    }
-
-    @Override
-    public long getPriority() {
-        throw fail();
-    }
-
-    @Override
-    public void publishClearNonMaster() {
-        throw fail();
-    }
-
-    @Override
-    public ManagementPlaneSyncRecord getLastManagementPlaneSyncRecord() {
-        throw fail();
-    }
-
-    @Override
-    public ManagementPlaneSyncRecord getManagementPlaneSyncState() {
-        throw fail();
-    }
-
-    @Override
-    public ManagementPlaneSyncRecord loadManagementPlaneSyncRecord(boolean useLocalKnowledgeForThisNode) {
-        throw fail();
-    }
-
-    @Override
-    public ManagementPlaneSyncRecordPersister getPersister() {
-        throw fail();
-    }
-
-    @Override
-    public Map<String, Object> getMetrics() {
-        throw fail();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/7556c582/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
----------------------------------------------------------------------
diff --git a/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java b/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
deleted file mode 100644
index 423743f..0000000
--- a/usage/rest-server/src/test/java/brooklyn/rest/testing/mocks/ManagementContextMock.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package brooklyn.rest.testing.mocks;
-
-import java.net.URI;
-import java.util.Collection;
-
-import brooklyn.basic.BrooklynObject;
-import brooklyn.catalog.BrooklynCatalog;
-import brooklyn.config.StringConfigMap;
-import brooklyn.entity.Application;
-import brooklyn.entity.Entity;
-import brooklyn.entity.drivers.EntityDriverManager;
-import brooklyn.entity.drivers.downloads.DownloadResolverManager;
-import brooklyn.entity.rebind.RebindManager;
-import brooklyn.location.LocationRegistry;
-import brooklyn.management.AccessController;
-import brooklyn.management.EntityManager;
-import brooklyn.management.ExecutionContext;
-import brooklyn.management.ExecutionManager;
-import brooklyn.management.LocationManager;
-import brooklyn.management.ManagementContext;
-import brooklyn.management.SubscriptionContext;
-import brooklyn.management.SubscriptionManager;
-import brooklyn.management.entitlement.EntitlementManager;
-import brooklyn.management.ha.HighAvailabilityManager;
-import brooklyn.management.ha.ManagementNodeState;
-import brooklyn.util.guava.Maybe;
-import brooklyn.util.javalang.JavaClassNames;
-
-public class ManagementContextMock implements ManagementContext {
-    private Boolean running;
-    private Boolean startupComplete;
-    private HighAvailabilityManagerStub haMock = new HighAvailabilityManagerStub();
-
-    public void setRunning(Boolean running) {
-        this.running = running;
-    }
-    public void setStartupComplete(Boolean startupComplete) {
-        this.startupComplete = startupComplete;
-    }
-    
-    public void setState(ManagementNodeState state) {
-        haMock.setState(state);
-    }
-
-    private static RuntimeException fail() {
-        throw new UnsupportedOperationException("Mocked method not implemented - "+JavaClassNames.callerNiceClassAndMethod(1));
-    }
-
-    @Override
-    public HighAvailabilityManager getHighAvailabilityManager() {
-        return haMock;
-    }
-
-    @Override
-    public String getManagementPlaneId() {
-        throw fail();
-    }
-
-    @Override
-    public String getManagementNodeId() {
-        throw fail();
-    }
-
-    @Override
-    public Maybe<URI> getManagementNodeUri() {
-        throw fail();
-    }
-
-    @Override
-    public Collection<Application> getApplications() {
-        throw fail();
-    }
-
-    @Override
-    public EntityManager getEntityManager() {
-        throw fail();
-    }
-
-    @Override
-    public ExecutionManager getExecutionManager() {
-        throw fail();
-    }
-
-    @Override
-    public ExecutionContext getServerExecutionContext() {
-        throw fail();
-    }
-
-    @Override
-    public EntityDriverManager getEntityDriverManager() {
-        throw fail();
-    }
-
-    @Override
-    public DownloadResolverManager getEntityDownloadsManager() {
-        throw fail();
-    }
-
-    @Override
-    public SubscriptionManager getSubscriptionManager() {
-        throw fail();
-    }
-
-    @Override
-    public ExecutionContext getExecutionContext(Entity entity) {
-        throw fail();
-    }
-
-    @Override
-    public SubscriptionContext getSubscriptionContext(Entity entity) {
-        throw fail();
-    }
-
-    @Override
-    public RebindManager getRebindManager() {
-        throw fail();
-    }
-
-    @Override
-    public StringConfigMap getConfig() {
-        throw fail();
-    }
-
-    @Override
-    public boolean isRunning() {
-        if (running==null) throw fail();
-        return running;
-    }
-
-    @Override
-    public boolean isStartupComplete() {
-        if (startupComplete==null) throw fail();
-        return startupComplete;
-    }
-    
-    @Override
-    public LocationRegistry getLocationRegistry() {
-        throw fail();
-    }
-
-    @Override
-    public BrooklynCatalog getCatalog() {
-        throw fail();
-    }
-
-    @Override
-    public LocationManager getLocationManager() {
-        throw fail();
-    }
-
-    @Override
-    public AccessController getAccessController() {
-        throw fail();
-    }
-
-    @Override
-    public void reloadBrooklynProperties() {
-        throw fail();
-
-    }
-
-    @Override
-    public void addPropertiesReloadListener(PropertiesReloadListener listener) {
-        throw fail();
-
-    }
-
-    @Override
-    public void removePropertiesReloadListener(PropertiesReloadListener listener) {
-        throw fail();
-    }
-
-    @Override
-    public EntitlementManager getEntitlementManager() {
-        throw fail();
-    }
-
-    @Override
-    public BrooklynObject lookup(String id) {
-        throw fail();
-    }
-
-    @Override
-    public <T extends BrooklynObject> T lookup(String id, Class<T> type) {
-        throw fail();
-    }
-
-}