tidy CLI options, renaming startup-ignore-error flags; and other tidy breaks backwards compatibility in CLI: previously we had `ignoreXxxOnStartup` fields, but now many of these default to true, and airlift offers no way to make them false, so they are called `--startupFailOn...` or `--startupContinueOn...`.
changes the nascent CatalogInitialization so that callbacks get it, and can query settings such as whether to throw 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/21707da8 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/21707da8 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/21707da8 Branch: refs/heads/master Commit: 21707da8e476f4ee530e02f93bfdefd981a9e1e1 Parents: 127150e Author: Alex Heneveld <[email protected]> Authored: Thu Apr 30 11:07:00 2015 +0100 Committer: Alex Heneveld <[email protected]> Committed: Fri May 8 18:22:22 2015 +0100 ---------------------------------------------------------------------- .../catalog/internal/BasicBrooklynCatalog.java | 12 ++- .../catalog/internal/CatalogInitialization.java | 96 ++++++++++++++------ .../brooklyn/entity/rebind/RebindIteration.java | 5 +- .../internal/AbstractManagementContext.java | 3 +- .../entity/rebind/RebindCatalogEntityTest.java | 7 +- usage/cli/src/main/java/brooklyn/cli/Main.java | 94 ++++++++++--------- .../brooklyn/launcher/BrooklynLauncher.java | 21 ++++- .../brooklyn/launcher/BrooklynLauncherTest.java | 4 +- 8 files changed, 157 insertions(+), 85 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java index f1d5a3c..375b29e 100644 --- a/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java +++ b/core/src/main/java/brooklyn/catalog/internal/BasicBrooklynCatalog.java @@ -25,6 +25,7 @@ import io.brooklyn.camp.spi.AssemblyTemplate; import io.brooklyn.camp.spi.instantiate.AssemblyTemplateInstantiator; import io.brooklyn.camp.spi.pdp.DeploymentPlan; +import java.lang.reflect.Method; import java.util.Collection; import java.util.List; import java.util.Map; @@ -346,14 +347,21 @@ public class BasicBrooklynCatalog implements BrooklynCatalog { // revert to legacy mechanism SpecT spec = null; + Method method; + try { + method = Reflections.findMethod(specType, "create", Class.class); + } catch (Exception e) { + Exceptions.propagateIfFatal(e); + throw new IllegalStateException("Unsupported creation of spec type "+specType+"; it must have a public static create(Class) method", e); + } try { if (loadedItem.getJavaType()!=null) { - SpecT specT = (SpecT) Reflections.findMethod(specType, "create", Class.class).invoke(null, loadedItem.loadJavaClass(mgmt)); + SpecT specT = (SpecT) method.invoke(null, loadedItem.loadJavaClass(mgmt)); spec = specT; } } catch (Exception e) { Exceptions.propagateIfFatal(e); - throw new IllegalStateException("Unsupported creation of spec type "+specType+"; it must have a public static create(Class) method", e); + throw new IllegalStateException("Error creating "+specType+" "+loadedItem.getJavaType()+": "+e, e); } if (spec==null) http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java index 15d5d76..69dc877 100644 --- a/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java +++ b/core/src/main/java/brooklyn/catalog/internal/CatalogInitialization.java @@ -30,10 +30,13 @@ import brooklyn.catalog.BrooklynCatalog; import brooklyn.catalog.CatalogItem; import brooklyn.config.BrooklynServerConfig; import brooklyn.management.ManagementContext; +import brooklyn.management.ManagementContextInjectable; import brooklyn.management.internal.ManagementContextInternal; import brooklyn.util.ResourceUtils; import brooklyn.util.collections.MutableList; import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.exceptions.FatalRuntimeException; +import brooklyn.util.exceptions.RuntimeInterruptedException; import brooklyn.util.flags.TypeCoercions; import brooklyn.util.guava.Maybe; import brooklyn.util.net.Urls; @@ -41,10 +44,11 @@ import brooklyn.util.text.Strings; import com.google.common.annotations.Beta; import com.google.common.base.Function; +import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; @Beta -public class CatalogInitialization { +public class CatalogInitialization implements ManagementContextInjectable { /* @@ -74,9 +78,13 @@ public class CatalogInitialization { boolean force; boolean disallowLocal = false; - List<Function<ManagementContext, Void>> callbacks = MutableList.of(); + List<Function<CatalogInitialization, Void>> callbacks = MutableList.of(); AtomicInteger runCount = new AtomicInteger(); + ManagementContext managementContext; + boolean isStartingUp = false; + boolean failOnStartupErrors = false; + public CatalogInitialization(String initialUri, boolean reset, String additionUri, boolean force) { this.initialUri = initialUri; this.reset = reset; @@ -88,7 +96,17 @@ public class CatalogInitialization { this(null, false, null, false); } - public CatalogInitialization addPopulationCallback(Function<ManagementContext, Void> callback) { + public void injectManagementContext(ManagementContext managementContext) { + if (this.managementContext!=null && managementContext!=null && !this.managementContext.equals(managementContext)) + throw new IllegalStateException("Cannot switch management context of "+this+"; from "+this.managementContext+" to "+managementContext); + this.managementContext = managementContext; + } + + public ManagementContext getManagementContext() { + return Preconditions.checkNotNull(managementContext, "management context has not been injected into "+this); + } + + public CatalogInitialization addPopulationCallback(Function<CatalogInitialization, Void> callback) { callbacks.add(callback); return this; } @@ -106,51 +124,51 @@ public class CatalogInitialization { } /** makes or updates the mgmt catalog, based on the settings in this class */ - public void populateCatalog(ManagementContext managementContext, boolean needsInitial, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) { + public void populateCatalog(boolean needsInitial, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) { try { BasicBrooklynCatalog catalog; Maybe<BrooklynCatalog> cm = ((ManagementContextInternal)managementContext).getCatalogIfSet(); if (cm.isAbsent()) { if (hasRun()) { - log.warn("Odd: catalog initialization has run but management context has no catalog; re-creating"); + log.warn("Catalog initialization has already run but management context has no catalog; re-creating"); } catalog = new BasicBrooklynCatalog(managementContext); setCatalog(managementContext, catalog, "Replacing catalog with newly populated catalog", true); } else { if (!hasRun()) { - log.warn("Odd: catalog initialization has not run but management context has a catalog; re-populating"); + log.warn("Catalog initialization has not properly run but management context has a catalog; re-populating, possibly overwriting items installed during earlier access (it may have been an early web request)"); } catalog = (BasicBrooklynCatalog) cm.get(); } - populateCatalog(managementContext, catalog, needsInitial, true, optionalItemsForResettingCatalog); + populateCatalog(catalog, needsInitial, true, optionalItemsForResettingCatalog); } finally { runCount.incrementAndGet(); } } - private void populateCatalog(ManagementContext managementContext, BasicBrooklynCatalog catalog, boolean needsInitial, boolean runCallbacks, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) { - applyCatalogLoadMode(managementContext); + private void populateCatalog(BasicBrooklynCatalog catalog, boolean needsInitial, boolean runCallbacks, Collection<CatalogItem<?, ?>> optionalItemsForResettingCatalog) { + applyCatalogLoadMode(); if (optionalItemsForResettingCatalog!=null) { catalog.reset(optionalItemsForResettingCatalog); } if (needsInitial) { - populateInitial(catalog, managementContext); + populateInitial(catalog); } - populateAdditions(catalog, managementContext); + populateAdditions(catalog); if (runCallbacks) { - populateViaCallbacks(catalog, managementContext); + populateViaCallbacks(catalog); } } private enum PopulateMode { YAML, XML, AUTODETECT } - protected void populateInitial(BasicBrooklynCatalog catalog, ManagementContext managementContext) { + protected void populateInitial(BasicBrooklynCatalog catalog) { if (disallowLocal) { if (!hasRun()) { log.debug("CLI initial catalog not being read with disallow-local mode set."); @@ -166,25 +184,25 @@ public class CatalogInitialization { // B6) go to C1 if (initialUri!=null) { - populateInitialFromUri(catalog, managementContext, initialUri, PopulateMode.AUTODETECT); + populateInitialFromUri(catalog, initialUri, PopulateMode.AUTODETECT); return; } String catalogUrl = managementContext.getConfig().getConfig(BrooklynServerConfig.BROOKLYN_CATALOG_URL); if (Strings.isNonBlank(catalogUrl)) { - populateInitialFromUri(catalog, managementContext, catalogUrl, PopulateMode.AUTODETECT); + populateInitialFromUri(catalog, catalogUrl, PopulateMode.AUTODETECT); return; } catalogUrl = Urls.mergePaths(BrooklynServerConfig.getMgmtBaseDir( managementContext.getConfig() ), "catalog.bom"); if (new File(catalogUrl).exists()) { - populateInitialFromUri(catalog, managementContext, "file:"+catalogUrl, PopulateMode.YAML); + populateInitialFromUri(catalog, "file:"+catalogUrl, PopulateMode.YAML); return; } catalogUrl = Urls.mergePaths(BrooklynServerConfig.getMgmtBaseDir( managementContext.getConfig() ), "catalog.xml"); if (new File(catalogUrl).exists()) { - populateInitialFromUri(catalog, managementContext, "file:"+catalogUrl, PopulateMode.XML); + populateInitialFromUri(catalog, "file:"+catalogUrl, PopulateMode.XML); return; } @@ -195,7 +213,7 @@ public class CatalogInitialization { catalogUrl = "classpath:/brooklyn/default.catalog.bom"; if (new ResourceUtils(this).doesUrlExist(catalogUrl)) { - populateInitialFromUri(catalog, managementContext, catalogUrl, PopulateMode.YAML); + populateInitialFromUri(catalog, catalogUrl, PopulateMode.YAML); return; } @@ -203,7 +221,7 @@ public class CatalogInitialization { return; } - private void populateInitialFromUri(BasicBrooklynCatalog catalog, ManagementContext managementContext, String catalogUrl, PopulateMode mode) { + private void populateInitialFromUri(BasicBrooklynCatalog catalog, String catalogUrl, PopulateMode mode) { log.debug("Loading initial catalog from {}", catalogUrl); Exception problem = null; @@ -260,7 +278,7 @@ public class CatalogInitialization { return problem; } - protected void populateAdditions(BasicBrooklynCatalog catalog, ManagementContext mgmt) { + protected void populateAdditions(BasicBrooklynCatalog catalog) { if (Strings.isNonBlank(additionsUri)) { if (disallowLocal) { if (!hasRun()) { @@ -281,17 +299,18 @@ public class CatalogInitialization { } } - protected void populateViaCallbacks(BasicBrooklynCatalog catalog, ManagementContext managementContext) { - for (Function<ManagementContext, Void> callback: callbacks) - callback.apply(managementContext); + protected void populateViaCallbacks(BasicBrooklynCatalog catalog) { + for (Function<CatalogInitialization, Void> callback: callbacks) + callback.apply(this); } private boolean setFromCatalogLoadMode = false; + /** @deprecated since introduced in 0.7.0, only for legacy compatibility with * {@link CatalogLoadMode} {@link BrooklynServerConfig#CATALOG_LOAD_MODE}, * allowing control of catalog loading from a brooklyn property */ @Deprecated - public void applyCatalogLoadMode(ManagementContext managementContext) { + public void applyCatalogLoadMode() { if (setFromCatalogLoadMode) return; setFromCatalogLoadMode = true; Maybe<Object> clmm = ((ManagementContextInternal)managementContext).getConfig().getConfigRaw(BrooklynServerConfig.CATALOG_LOAD_MODE, false); @@ -314,7 +333,7 @@ public class CatalogInitialization { /** makes the catalog, warning if persistence is on and hasn't run yet * (as the catalog will be subsequently replaced) */ @Beta - public BrooklynCatalog getCatalogPopulatingBestEffort(ManagementContext managementContext) { + public BrooklynCatalog getCatalogPopulatingBestEffort() { Maybe<BrooklynCatalog> cm = ((ManagementContextInternal)managementContext).getCatalogIfSet(); if (cm.isPresent()) return cm.get(); @@ -323,7 +342,7 @@ public class CatalogInitialization { if (oldC==null) { // our catalog was added, so run population // NB: we need the catalog to be saved already so that we can run callbacks - populateCatalog(managementContext, (BasicBrooklynCatalog) managementContext.getCatalog(), true, true, null); + populateCatalog((BasicBrooklynCatalog) managementContext.getCatalog(), true, true, null); } return managementContext.getCatalog(); @@ -351,5 +370,30 @@ public class CatalogInitialization { } return cm.get(); } + + public void setStartingUp(boolean isStartingUp) { + this.isStartingUp = isStartingUp; + } + + public void setFailOnStartupErrors(boolean startupFailOnCatalogErrors) { + this.failOnStartupErrors = startupFailOnCatalogErrors; + } + + public void handleException(Throwable throwable, Object details) { + if (throwable instanceof InterruptedException) + throw new RuntimeInterruptedException((InterruptedException) throwable); + if (throwable instanceof RuntimeInterruptedException) + throw (RuntimeInterruptedException) throwable; + + log.error("Error loading catalog item '"+details+"': "+throwable); + log.debug("Trace for error loading catalog item '"+details+"': "+throwable, throwable); + + // TODO give more detail when adding + ((ManagementContextInternal)getManagementContext()).errors().add(throwable); + + if (isStartingUp && failOnStartupErrors) { + throw new FatalRuntimeException("Unable to load catalog item '"+details+"': "+throwable, throwable); + } + } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java index b5af10c..13d02e8 100644 --- a/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java +++ b/core/src/main/java/brooklyn/entity/rebind/RebindIteration.java @@ -345,7 +345,8 @@ public abstract class RebindIteration { Collection<CatalogItem<?, ?>> catalogItems = rebindContext.getCatalogItems(); CatalogInitialization catInit = ((ManagementContextInternal)managementContext).getCatalogInitialization(); - catInit.applyCatalogLoadMode(managementContext); + catInit.injectManagementContext(managementContext); + catInit.applyCatalogLoadMode(); Collection<CatalogItem<?,?>> itemsForResettingCatalog = null; boolean needsInitialCatalog; if (rebindManager.persistCatalogItemsEnabled) { @@ -390,7 +391,7 @@ public abstract class RebindIteration { } // TODO in read-only mode, perhaps do this less frequently than entities etc ? - catInit.populateCatalog(managementContext, needsInitialCatalog, itemsForResettingCatalog); + catInit.populateCatalog(needsInitialCatalog, itemsForResettingCatalog); } protected void instantiateLocationsAndEntities() { http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/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 de3460b..8ff6dfc 100644 --- a/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java +++ b/core/src/main/java/brooklyn/management/internal/AbstractManagementContext.java @@ -380,7 +380,7 @@ public abstract class AbstractManagementContext implements ManagementContextInte // catalog init is needed; normally this will be done from start sequence, // but if accessed early -- and in tests -- we will load it here // TODO log if in launcher mode - return getCatalogInitialization().getCatalogPopulatingBestEffort(this); + return getCatalogInitialization().getCatalogPopulatingBestEffort(); } /** @@ -460,6 +460,7 @@ public abstract class AbstractManagementContext implements ManagementContextInte @Override public synchronized void setCatalogInitialization(CatalogInitialization catalogInitialization) { + if (catalogInitialization!=null) catalogInitialization.injectManagementContext(this); this.catalogInitialization = catalogInitialization; } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java index 9c94697..212212f 100644 --- a/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java +++ b/core/src/test/java/brooklyn/entity/rebind/RebindCatalogEntityTest.java @@ -31,6 +31,7 @@ import org.slf4j.LoggerFactory; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import brooklyn.catalog.internal.CatalogInitialization; import brooklyn.entity.Application; import brooklyn.entity.basic.AbstractApplication; import brooklyn.entity.basic.ApplicationBuilder; @@ -133,10 +134,10 @@ public class RebindCatalogEntityTest extends RebindTestFixture<StartableApplicat // ucl.close is only introduced in java 1.7 if (ucl instanceof Closeable) ((Closeable)ucl).close(); - newManagementContext.getCatalogInitialization().addPopulationCallback(new Function<ManagementContext, Void>() { + newManagementContext.getCatalogInitialization().addPopulationCallback(new Function<CatalogInitialization, Void>() { @Override - public Void apply(ManagementContext input) { - input.getCatalog().addItem(appClazz); + public Void apply(CatalogInitialization input) { + input.getManagementContext().getCatalog().addItem(appClazz); return null; } }); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/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 7dab5aa..ad6aeb1 100644 --- a/usage/cli/src/main/java/brooklyn/cli/Main.java +++ b/usage/cli/src/main/java/brooklyn/cli/Main.java @@ -75,7 +75,6 @@ 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; @@ -192,11 +191,11 @@ public class Main extends AbstractMain { public static class LaunchCommand extends BrooklynCommandCollectingArgs { @Option(name = { "--localBrooklynProperties" }, title = "local brooklyn.properties file", - description = "local brooklyn.properties file, specific to this launch (appending to and overriding global properties)") + description = "Load the given properties file, specific to this launch (appending to and overriding global properties)") public String localBrooklynProperties; @Option(name = { "--noGlobalBrooklynProperties" }, title = "do not use any global brooklyn.properties file found", - description = "do not use the default global brooklyn.properties file found") + description = "Do not use the default global brooklyn.properties file found") public boolean noGlobalBrooklynProperties = false; @Option(name = { "-a", "--app" }, title = "application class or file", @@ -223,7 +222,7 @@ public class Main extends AbstractMain { + "if nothing is yet persisted in the catalog (or if it is reset)") public String catalogInitial; - @Option(name = { "--catalogReset" }, title = "clear catalog", + @Option(name = { "--catalogReset" }, description = "Specifies that any catalog items which have been persisted should be cleared") public boolean catalogReset; @@ -231,22 +230,22 @@ public class Main extends AbstractMain { description = "Specifies a catalog.bom to be added to the catalog") public String catalogAdd; - @Option(name = { "--catalogForce" }, title = "force catalog addition", + @Option(name = { "--catalogForce" }, description = "Specifies that catalog items added via the CLI should be forcibly added, " + "replacing any identical versions already registered (use with care!)") public boolean catalogForce; @Option(name = { "-p", "--port" }, title = "port number", - description = "Specifies the port to be used by the Brooklyn Management Console; " + description = "Use this port for the brooklyn management web console and REST API; " + "default is 8081+ for http, 8443+ for https.") public String port; @Option(name = { "--https" }, - description = "Specifies that the server should start on https.") + description = "Launch the web console on https") public boolean useHttps = false; @Option(name = { "-nc", "--noConsole" }, - description = "Whether to start the web console") + description = "Do not start the web console or REST API") public boolean noConsole = false; @Option(name = { "-b", "--bindAddress" }, @@ -258,26 +257,32 @@ public class Main extends AbstractMain { public String publicAddress = null; @Option(name = { "--noConsoleSecurity" }, - description = "Whether to disable security for the web console with no security (i.e. no authentication required)") + description = "Whether to disable authentication and security filters for the web console (for use when debugging on a secure network or bound to localhost)") public Boolean noConsoleSecurity = false; - @Option(name = { "--ignoreWebStartupErrors" }, - 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 = { "--startupContinueOnWebErrors" }, + description = "Continue on web subsystem failures during startup " + + "(default is to abort if the web API fails to start, as management access is not normally possible)") + public boolean startupContinueOnWebErrors = false; + + @Option(name = { "--startupFailOnPersistenceErrors" }, + description = "Fail on persistence/HA subsystem failures during startup " + + "(default is to continue, so errors can be viewed via the API)") + public boolean startupFailOnPersistenceErrors = false; - @Option(name = { "--ignorePersistenceStartupErrors" }, - 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 = { "--startupFailOnCatalogErrors" }, + description = "Fail on catalog subsystem failures during startup " + + "(default is to continue, so errors can be viewed via the API)") + public boolean startupFailOnCatalogErrors = false; - @Option(name = { "--ignoreManagedAppsStartupErrors" }, - description = "Ignore failures starting managed applications passed on the command line on startup " - + "(default is true, so errors can be viewed via the API)") - public boolean ignoreAppErrors = true; + @Option(name = { "--startupFailOnManagedAppsErrors" }, + description = "Fail startup on errors deploying of managed apps specified via the command line " + + "(default is to continue, so errors can be viewed via the API)") + public boolean startupFailOnManagedAppsErrors = false; @Beta @Option(name = { "--startBrooklynNode" }, - description = "Whether to start a BrooklynNode entity representing this Brooklyn instance (default false)") + description = "Start a BrooklynNode entity representing this Brooklyn instance") public boolean startBrooklynNode = false; // Note in some cases, you can get java.util.concurrent.RejectedExecutionException @@ -285,7 +290,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 (default false)") + description = "Shutdown immediately on user text entry after startup (useful for debugging and demos)") public boolean stopOnKeyPress = false; final static String STOP_WHICH_APPS_ON_SHUTDOWN = "--stopOnShutdown"; @@ -406,13 +411,21 @@ public class Main extends AbstractMain { launcher = createLauncher(); CatalogInitialization catInit = new CatalogInitialization(catalogInitial, catalogReset, catalogAdd, catalogForce); - catInit.addPopulationCallback(new Function<ManagementContext,Void>() { + catInit.addPopulationCallback(new Function<CatalogInitialization,Void>() { @Override - public Void apply(ManagementContext mgmt) { - populateCatalog(mgmt.getCatalog()); + public Void apply(CatalogInitialization catInit) { + try { + populateCatalog(catInit.getManagementContext().getCatalog()); + } catch (Throwable e) { + catInit.handleException(e, "overridden main class populate catalog"); + } + + // Force load of catalog (so web console is up to date) + confirmCatalog(catInit); return null; } }); + catInit.setFailOnStartupErrors(startupFailOnCatalogErrors); launcher.catalogInitialization(catInit); launcher.persistMode(persistMode); @@ -557,9 +570,10 @@ public class Main extends AbstractMain { BrooklynLauncher launcher; launcher = BrooklynLauncher.newInstance(); launcher.localBrooklynPropertiesFile(localBrooklynProperties) - .ignorePersistenceErrors(ignorePersistenceErrors) - .ignoreWebErrors(ignoreWebErrors) - .ignoreAppErrors(ignoreAppErrors) + .ignorePersistenceErrors(!startupFailOnPersistenceErrors) + .ignoreCatalogErrors(!startupFailOnCatalogErrors) + .ignoreWebErrors(startupContinueOnWebErrors) + .ignoreAppErrors(!startupFailOnManagedAppsErrors) .locations(Strings.isBlank(locations) ? ImmutableList.<String>of() : JavaStringEscapes.unwrapJsonishListIfPossible(locations)); launcher.webconsole(!noConsole); @@ -597,17 +611,15 @@ public class Main extends AbstractMain { return launcher; } - /** method intended for subclassing, to add items to the catalog */ + /** method intended for subclassing, to add custom items to the catalog */ protected void populateCatalog(BrooklynCatalog catalog) { - // Force load of catalog (so web console is up to date) - confirmCatalog(catalog); - // nothing else added here } - protected void confirmCatalog(BrooklynCatalog catalog) { + protected void confirmCatalog(CatalogInitialization catInit) { // Force load of catalog (so web console is up to date) Stopwatch time = Stopwatch.createStarted(); + BrooklynCatalog catalog = catInit.getManagementContext().getCatalog(); Iterable<CatalogItem<Object, Object>> items = catalog.getCatalogItems(); for (CatalogItem<Object, Object> item: items) { try { @@ -617,14 +629,7 @@ public class Main extends AbstractMain { } 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); + catInit.handleException(throwable, item); } } log.debug("Catalog (size "+Iterables.size(items)+") confirmed in "+Duration.of(time)); @@ -777,9 +782,10 @@ public class Main extends AbstractMain { .add("bindAddress", bindAddress) .add("noConsole", noConsole) .add("noConsoleSecurity", noConsoleSecurity) - .add("ignorePersistenceErrors", ignorePersistenceErrors) - .add("ignoreWebErrors", ignoreWebErrors) - .add("ignoreAppErrors", ignoreAppErrors) + .add("startupFailOnPersistenceErrors", startupFailOnPersistenceErrors) + .add("startupFailsOnCatalogErrors", startupFailOnCatalogErrors) + .add("startupContinueOnWebErrors", startupContinueOnWebErrors) + .add("startupFailOnManagedAppsErrors", startupFailOnManagedAppsErrors) .add("stopWhichAppsOnShutdown", stopWhichAppsOnShutdown) .add("noShutdownOnExit", noShutdownOnExit) .add("stopOnKeyPress", stopOnKeyPress) http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/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 4d3b1be..71f53a5 100644 --- a/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java +++ b/usage/launcher/src/main/java/brooklyn/launcher/BrooklynLauncher.java @@ -152,6 +152,7 @@ public class BrooklynLauncher { private boolean ignoreWebErrors = false; private boolean ignorePersistenceErrors = true; + private boolean ignoreCatalogErrors = true; private boolean ignoreAppErrors = true; private StopWhichAppsOnShutdown stopWhichAppsOnShutdown = StopWhichAppsOnShutdown.THESE_IF_NOT_PERSISTED; @@ -401,6 +402,11 @@ public class BrooklynLauncher { return this; } + public BrooklynLauncher ignoreCatalogErrors(boolean ignoreCatalogErrors) { + this.ignoreCatalogErrors = ignoreCatalogErrors; + return this; + } + public BrooklynLauncher ignoreWebErrors(boolean ignoreWebErrors) { this.ignoreWebErrors = ignoreWebErrors; return this; @@ -562,6 +568,10 @@ public class BrooklynLauncher { // Create the management context initManagementContext(); + // Inform catalog initialization that it is starting up + CatalogInitialization catInit = ((ManagementContextInternal)managementContext).getCatalogInitialization(); + catInit.setStartingUp(true); + // Start webapps as soon as mgmt context available -- can use them to detect progress of other processes if (startWebApps) { try { @@ -586,15 +596,16 @@ public class BrooklynLauncher { } try { - // run cat init now if it hasn't yet been run - CatalogInitialization catInit = ((ManagementContextInternal)managementContext).getCatalogInitialization(); + // run cat init now if it hasn't yet been run; + // will also run if there was an ignored error in catalog above, allowing it to fail startup here if requested if (catInit!=null && !catInit.hasRun()) { LOG.debug("Loading catalog as part of launcher (persistence did not run it)"); - catInit.populateCatalog(managementContext, true, null); + catInit.populateCatalog(true, null); } } catch (Exception e) { - handleSubsystemStartupError(true, "initial catalog", e); + handleSubsystemStartupError(ignoreCatalogErrors, "initial catalog", e); } + catInit.setStartingUp(false); // Create the locations. Must happen after persistence is started in case the // management context's catalog is loaded from persisted state. (Location @@ -612,7 +623,7 @@ public class BrooklynLauncher { try { startBrooklynNode(); } catch (Exception e) { - handleSubsystemStartupError(true, "brooklyn node / self entity", e); + handleSubsystemStartupError(ignoreAppErrors, "brooklyn node / self entity", e); } } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/21707da8/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 b4f963e..0886390 100644 --- a/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java +++ b/usage/launcher/src/test/java/brooklyn/launcher/BrooklynLauncherTest.java @@ -317,9 +317,9 @@ public class BrooklynLauncherTest { @Test // takes a bit of time because starts webapp, but also tests rest api so useful public void testErrorsCaughtByApiAndRestApiWorks() throws Exception { launcher = newLauncherForTests(true) - .catalogInitialization(new CatalogInitialization(null, false, null, false).addPopulationCallback(new Function<ManagementContext, Void>() { + .catalogInitialization(new CatalogInitialization(null, false, null, false).addPopulationCallback(new Function<CatalogInitialization, Void>() { @Override - public Void apply(ManagementContext input) { + public Void apply(CatalogInitialization input) { throw new RuntimeException("deliberate-exception-for-testing"); } }))
