private Collection<CommandOutcome> runBlockingMultiple(Collection<CommandRefWithArgs> cmdRefs) { Collection<CommandOutcome> outcomes = new ArrayList<>(cmdRefs.size()); runNonBlocking(cmdRefs, this::noLogOutcome).forEach(future -> { try { outcomes.add(future.get()); } catch (InterruptedException e) { // when interrupted, throw error rather than return CommandOutcome#failed() // see comment in toOutcomeSupplier() method for details throw new BootiqueException(1, "Interrupted", e); } catch (ExecutionException e) { // we don't expect futures to ever throw errors throw new BootiqueException(1, "Unexpected error", e); } }); return outcomes; }
private CommandOutcome processExceptions(Throwable th, Throwable parentTh) { if (th instanceof BootiqueException) { CommandOutcome originalOutcome = ((BootiqueException) th).getOutcome(); // BootiqueException should be stripped of the exception cause and reported on a single line // TODO: should we still print the stack trace via logger.trace? return CommandOutcome.failed(originalOutcome.getExitCode(), originalOutcome.getMessage()); } String thMessage = th != null ? th.getMessage() : null; String message = thMessage != null ? "Command exception: '" + thMessage + "'." : "Command exception."; return CommandOutcome.failed(1, message, parentTh); }
private void checkCycles(RuntimeModule root, List<RuntimeModule> trace) { trace.add(this); if (root == this) { // Add next level, to make error message more clear. trace.add(this.overriddenBy); throw new BootiqueException(1, "Circular override dependency between DI modules: " + trace.stream().map(rm -> rm.bqModule.getName()).collect(Collectors.joining(" -> "))); } if (overriddenBy != null) { overriddenBy.checkCycles(root, trace); } }
private CommandOutcome processExceptions(Throwable th, Throwable parentTh) { if (th instanceof BootiqueException) { CommandOutcome originalOutcome = ((BootiqueException) th).getOutcome(); // BootiqueException should be stripped of the exception cause and reported on a single line // TODO: should we still print the stack trace via logger.trace? return CommandOutcome.failed(originalOutcome.getExitCode(), originalOutcome.getMessage()); } String thMessage = th != null ? th.getMessage() : null; String message = thMessage != null ? "Command exception: '" + thMessage + "'." : "Command exception."; return CommandOutcome.failed(1, message, parentTh); }
protected URL resolveUrl(String resourceId) { // resourceId can be either a file path or a URL or a classpath: URL if (resourceId.startsWith(CLASSPATH_URL_PREFIX)) { String path = resourceId.substring(CLASSPATH_URL_PREFIX.length()); // classpath URLs must not start with a slash. This does not work // with ClassLoader. if (path.length() > 0 && path.charAt(0) == '/') { throw new RuntimeException(CLASSPATH_URL_PREFIX + " URLs must not start with a slash: " + resourceId); } URL cpUrl = ResourceFactory.class.getClassLoader().getResource(path); if (cpUrl == null) { throw new IllegalArgumentException("Classpath URL not found: " + resourceId); } return cpUrl; } URI uri; try { uri = URI.create(resourceId); } catch (IllegalArgumentException e) { throw new BootiqueException(1, "Invalid config resource url: " + resourceId, e); } try { return uri.isAbsolute() ? uri.toURL() : getCanonicalFile(resourceId).toURI().toURL(); } catch (IOException e) { throw new BootiqueException(1, "Invalid config resource url: " + resourceId, e); } }
@Override public Optional<JsonNode> apply(URL url) { URLConnection connection; try { connection = url.openConnection(); } catch (IOException e) { // The message is dumb. But we don't really expect an exception (as no connection is established here), // and don't have a test case to reproduce. // TODO: If we ever see this condition occur, perhaps we can create a better message? throw new BootiqueException(1, "Can't create connection to config resource: " + url, e); } ParserType type = parserTypeFromHeaders(connection); if (type == null) { type = parserTypeFromExtension(url); } if (type == null) { type = ParserType.YAML; } Function<InputStream, Optional<JsonNode>> parser = parser(type); try (InputStream in = connection.getInputStream()) { return parser.apply(in); } catch (IOException e) { throw new BootiqueException(1, "Config resource is not found or is inaccessible: " + url, e); } }
protected void addCommandNoOverride(Map<String, ManagedCommand> commandMap, ManagedCommand managedCommand) { ManagedCommand existing = addCommand(commandMap, managedCommand); // complain on dupes if (existing != null && existing.getCommand() != managedCommand.getCommand()) { String c1 = existing.getCommand().getClass().getName(); String c2 = managedCommand.getCommand().getClass().getName(); String message = String.format("More than one DI command named '%s'. Conflicting types: %s, %s.", managedCommand.getCommand().getMetadata().getName(), c1, c2); throw new BootiqueException(1, message); } } }
protected String commandName(OptionSet optionSet) { Set<String> matches = new HashSet<>(3); getCommandManager().getAllCommands().forEach((name, mc) -> { if (!mc.isHidden() && !mc.isDefault() && optionSet.has(name) && !optionSet.hasArgument(name)) { matches.add(name); } }); switch (matches.size()) { case 0: // default command should be invoked return null; case 1: return matches.iterator().next(); default: String opts = String.join(", ", matches); String message = String.format("CLI options match multiple commands: %s.", opts); throw new BootiqueException(1, message); } }
private OptionSet parse(String[] args) { try { return getParser().parse(args); } catch (OptionException e) { throw new BootiqueException(1, e.getMessage(), e); } }
void setOverriddenBy(RuntimeModule module) { // no more than one override is allowed if (this.overriddenBy != null) { String message = String.format( "Module %s provided by %s is overridden twice by %s and %s", getModuleName(), getProviderName(), this.overriddenBy.getModuleName(), module.getModuleName()); throw new BootiqueException(1, message); } this.overriddenBy = module; } }
logger.stderr("Daemon runtime started..."); } else { throw new BootiqueException(outcome.getExitCode(), "Daemon failed to start: " + outcome);
private Collection<CommandOutcome> runBlockingMultiple(Collection<CommandRefWithArgs> cmdRefs) { Collection<CommandOutcome> outcomes = new ArrayList<>(cmdRefs.size()); runNonBlocking(cmdRefs, this::noLogOutcome).forEach(future -> { try { outcomes.add(future.get()); } catch (InterruptedException e) { // when interrupted, throw error rather than return CommandOutcome#failed() // see comment in toOutcomeSupplier() method for details throw new BootiqueException(1, "Interrupted", e); } catch (ExecutionException e) { // we don't expect futures to ever throw errors throw new BootiqueException(1, "Unexpected error", e); } }); return outcomes; }
private String getTypeLabel(Class<? extends ManagedDataSourceFactory> factoryType) { // TODO: see TODO in ConfigMetadataCompiler ... at least maybe create a public API for this in Bootique to // avoid parsing annotations inside the modules... JsonTypeName typeName = factoryType.getAnnotation(JsonTypeName.class); if (typeName == null) { throw new BootiqueException(1, "Invalid ManagedDataSourceFactory: " + factoryType.getName() + ". Not annotated with @JsonTypeName."); } return typeName.value(); }
private void checkCycles(RuntimeModule root, List<RuntimeModule> trace) { trace.add(this); if (root == this) { // Add next level, to make error message more clear. trace.add(this.overriddenBy); throw new BootiqueException(1, "Circular override dependency between DI modules: " + trace.stream().map(rm -> rm.bqModule.getName()).collect(Collectors.joining(" -> "))); } if (overriddenBy != null) { overriddenBy.checkCycles(root, trace); } }
protected URL resolveUrl(String resourceId) { // resourceId can be either a file path or a URL or a classpath: URL if (resourceId.startsWith(CLASSPATH_URL_PREFIX)) { String path = resourceId.substring(CLASSPATH_URL_PREFIX.length()); // classpath URLs must not start with a slash. This does not work // with ClassLoader. if (path.length() > 0 && path.charAt(0) == '/') { throw new RuntimeException(CLASSPATH_URL_PREFIX + " URLs must not start with a slash: " + resourceId); } URL cpUrl = ResourceFactory.class.getClassLoader().getResource(path); if (cpUrl == null) { throw new IllegalArgumentException("Classpath URL not found: " + resourceId); } return cpUrl; } URI uri; try { uri = URI.create(resourceId); } catch (IllegalArgumentException e) { throw new BootiqueException(1, "Invalid config resource url: " + resourceId, e); } try { return uri.isAbsolute() ? uri.toURL() : getCanonicalFile(resourceId).toURI().toURL(); } catch (IOException e) { throw new BootiqueException(1, "Invalid config resource url: " + resourceId, e); } }
private Class<? extends ManagedDataSourceFactory> delegateFactoryType(Injector injector) { Key<Set<Class<? extends ManagedDataSourceFactory>>> setKey = Key .get(new TypeLiteral<Set<Class<? extends ManagedDataSourceFactory>>>() { }); Set<Class<? extends ManagedDataSourceFactory>> allFactories = injector.getProvider(setKey).get(); // the resulting set should contain this class plus one or more concrete ManagedDataSourceFactory implementors. // We can guess the default only if there's a single implementor. Set<Class<? extends ManagedDataSourceFactory>> set = leafFactories(allFactories); switch (set.size()) { case 0: throw new BootiqueException(1, "No concrete 'bootique-jdbc' implementation found. " + "You will need to add one (such as 'bootique-jdbc-tomcat', etc.) as an application dependency."); case 1: return set.iterator().next(); default: List<String> labels = new ArrayList<>(set.size()); set.forEach(f -> labels.add(getTypeLabel(f))); throw new BootiqueException(1, "More than one 'bootique-jdbc' implementation is found. There's no single default. " + "As a result each DataSource configuration must provide a 'type' property. Valid 'type' values: " + labels); } } }
@Override public Optional<JsonNode> apply(URL url) { URLConnection connection; try { connection = url.openConnection(); } catch (IOException e) { // The message is dumb. But we don't really expect an exception (as no connection is established here), // and don't have a test case to reproduce. // TODO: If we ever see this condition occur, perhaps we can create a better message? throw new BootiqueException(1, "Can't create connection to config resource: " + url, e); } ParserType type = parserTypeFromHeaders(connection); if (type == null) { type = parserTypeFromExtension(url); } if (type == null) { type = ParserType.YAML; } Function<InputStream, Optional<JsonNode>> parser = parser(type); try (InputStream in = connection.getInputStream()) { return parser.apply(in); } catch (IOException e) { throw new BootiqueException(1, "Config resource is not found or is inaccessible: " + url, e); } }
protected String commandName(OptionSet optionSet) { Set<String> matches = new HashSet<>(3); getCommandManager().getAllCommands().forEach((name, mc) -> { if (!mc.isHidden() && !mc.isDefault() && optionSet.has(name) && !optionSet.hasArgument(name)) { matches.add(name); } }); switch (matches.size()) { case 0: // default command should be invoked return null; case 1: return matches.iterator().next(); default: String opts = matches.stream().collect(Collectors.joining(", ")); String message = String.format("CLI options match multiple commands: %s.", opts); throw new BootiqueException(1, message); } }
private ManagedDataSourceFactory createDataSourceFactory(Injector injector) { Class<? extends ManagedDataSourceFactory> factoryType = delegateFactoryType(injector); JavaType jacksonType = TypeFactory.defaultInstance().constructType(factoryType); ObjectMapper mapper = createObjectMapper(injector); JsonNode nodeWithType = jsonNodeWithType(getTypeLabel(factoryType)); try { return mapper.readValue(new TreeTraversingParser(nodeWithType, mapper), jacksonType); } catch (IOException e) { throw new BootiqueException(1, "Deserialization of JDBC DataSource configuration failed.", e); } }
private OptionSet parse(String[] args) { try { return getParser().parse(args); } catch (OptionException e) { throw new BootiqueException(1, e.getMessage(), e); } }