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 <[email protected]>
Authored: Thu Apr 23 11:52:53 2015 +0100
Committer: Alex Heneveld <[email protected]>
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;
     }
     

Reply via email to