Use the full Environment for module args
Rename to ModuleLaunchRequest Add documentation for the format Move request construction Document launcher order Fix empty args case Update docs
This commit is contained in:
committed by
Mark Fisher
parent
cf470b6d6f
commit
74d85cd9d2
@@ -23,10 +23,11 @@ From the `spring-cloud-stream/spring-cloud-stream-module-launcher` directory:
|
||||
|
||||
````
|
||||
java -Dmodules=org.springframework.cloud.stream.module:time-source:1.0.0.BUILD-SNAPSHOT -Dspring.cloud.stream.bindings.output=ticktock -jar target/spring-cloud-stream-module-launcher-1.0.0.BUILD-SNAPSHOT.jar
|
||||
java -Dmodules=org.springframework.cloud.stream.module:log-sink:1.0.0.BUILD-SNAPSHOT -Dserver.port=8081 -Dspring.cloud.stream.bindings.input=ticktock -jar target/spring-cloud-stream-module-launcher-1.0.0.BUILD-SNAPSHOT.jar
|
||||
java -Dmodules=org.springframework.cloud.stream.module:log-sink:1.0.0.BUILD-SNAPSHOT -Dargs.0.server.port=8081 -Dspring.cloud.stream.bindings.input=ticktock -jar target/spring-cloud-stream-module-launcher-1.0.0.BUILD-SNAPSHOT.jar
|
||||
````
|
||||
|
||||
Note that `server.port` needs to be specified explicitly for the log sink module as the time source module already uses the default port `8080`.
|
||||
The module launcher is able to launch several modules, hence the `args.0.` prefix.
|
||||
The binding property is set to use the same name `ticktock` for both the output/input bindings of source/sink modules so that the log sink receives messages from the time source.
|
||||
|
||||
The time messages will be emitted every second. The console for the log module will display each:
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed 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 org.springframework.cloud.stream.module.launcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Encapsulates a reference to a module (as maven coordinates) and a set of "arguments" that must be passed to it.
|
||||
* Those arguments will eventually be passed to the module by the launcher, using any way appropriate.
|
||||
*
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class ModuleLaunchRequest {
|
||||
|
||||
private final String module;
|
||||
|
||||
private final Map<String, String> arguments;
|
||||
|
||||
public ModuleLaunchRequest(String module, Map<String, String> arguments) {
|
||||
this.module = module;
|
||||
this.arguments = arguments != null ? new HashMap<>(arguments) : new HashMap<String, String>();
|
||||
}
|
||||
|
||||
public String getModule() {
|
||||
return module;
|
||||
}
|
||||
|
||||
public Map<String, String> getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
public void addArgument(String name, String value) {
|
||||
this.arguments.put(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s with arguments %s", module, arguments);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,9 @@ package org.springframework.cloud.stream.module.launcher;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -36,6 +38,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author Marius Bogoevici
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
public class ModuleLauncher {
|
||||
|
||||
@@ -59,28 +62,36 @@ public class ModuleLauncher {
|
||||
/**
|
||||
* Launches one or more modules, with the corresponding arguments, if any.
|
||||
*
|
||||
* Modules must be passed in "natural" left to right order.
|
||||
*
|
||||
* The format of each module must conform to the <a href="http://www.eclipse.org/aether">Aether</a> convention:
|
||||
* <code><groupId>:<artifactId>[:<extension>[:<classifier>]]:<version></code>
|
||||
*
|
||||
* To pass arguments to a module, prefix with the module name and a dot. The arg name will be de-qualified and passed along.
|
||||
* For example: <code>--org.springframework.cloud.stream.module:time-source:1.0.0.BUILD-SNAPSHOT.bar=123</code> becomes <code>--bar=123</code> and is only passed to the 'org.springframework.cloud.stream.module:time-source:1.0.0.BUILD-SNAPSHOT' module.
|
||||
*
|
||||
* @param modules a list of modules
|
||||
* @param args a list of arguments, prefixed with the module name
|
||||
* @param moduleLaunchRequests a list of modules with their (unqualified) arguments
|
||||
*/
|
||||
public void launch(String[] modules, String[] args) {
|
||||
for (String module : modules) {
|
||||
List<String> moduleArgs = new ArrayList<>();
|
||||
for (String arg : args) {
|
||||
if (arg.startsWith("--" + module + ".")) {
|
||||
moduleArgs.add("--" + arg.substring(module.length() + 3));
|
||||
}
|
||||
}
|
||||
moduleArgs.add("--spring.jmx.default-domain=" + module.replace("/", ".").replace(":", "."));
|
||||
launchModule(module, moduleArgs.toArray(new String[moduleArgs.size()]));
|
||||
public void launch(List<ModuleLaunchRequest> moduleLaunchRequests) {
|
||||
List<ModuleLaunchRequest> reversed = new ArrayList<>(moduleLaunchRequests);
|
||||
Collections.reverse(reversed);
|
||||
for (ModuleLaunchRequest moduleLaunchRequest : reversed) {
|
||||
String module = moduleLaunchRequest.getModule();
|
||||
moduleLaunchRequest.addArgument("spring.jmx.default-domain", module.replace("/", ".").replace(":", "."));
|
||||
launchModule(module, toArgArray(moduleLaunchRequest.getArguments()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a set of semantic program arguments to "command line program arguments" that is, to the
|
||||
* {@literal --foo=bar} form.
|
||||
*/
|
||||
private String[] toArgArray(Map<String, String> args) {
|
||||
String[] result = new String[args.size()];
|
||||
int i = 0;
|
||||
for (Map.Entry<String, String> kv : args.entrySet()) {
|
||||
result[i++] = String.format("--%s=%s", kv.getKey(), kv.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void launchModule(String module, String[] args) {
|
||||
try {
|
||||
Resource resource = resolveModule(module);
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
* or "MODULES" environment variable as a comma-delimited list, with the arguments
|
||||
* provided at launch.
|
||||
*
|
||||
* @see ModuleLauncher#launch(String[], String[]) for module and argument structure and
|
||||
* @see ModuleLauncherProperties for module and argument structure and
|
||||
* format
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
*
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ModuleResolverProperties.class)
|
||||
|
||||
@@ -16,24 +16,50 @@
|
||||
|
||||
package org.springframework.cloud.stream.module.launcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.validation.constraints.AssertFalse;
|
||||
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Configuration properties for {@link ModuleLauncher}.
|
||||
*
|
||||
* <p>Expects the following keys (resolved from the {@link Environment}, so this could take many forms _ System
|
||||
* properties, environment variables, program arguments, <i>etc.</i> _):<ul>
|
||||
* <li>{@literal modules = <list>}: an ordered list of maven coordinates of modules to launch</li>
|
||||
* <li>{@literal args[<index>][<key>] = <value>}: key/value pairs that will become module arguments,
|
||||
* where {@literal <index>} is the 0-based index of the module in the list above</li>
|
||||
* </ul>
|
||||
*
|
||||
* As an example, this is how one would launch {@literal time --fixedDelay=4 | log} canonical example:
|
||||
* <pre>
|
||||
* modules = org.springframework.cloud.modules:time-source:1.0.0-SNAPSHOT,org.springframework.cloud.modules:log-sink:1.0.0-SNAPSHOT
|
||||
* args.0.fixedDelay=4
|
||||
* </pre>
|
||||
* </p>
|
||||
*
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author Marius Bogoevici
|
||||
* @author Eric Bottard
|
||||
*/
|
||||
@ConfigurationProperties
|
||||
public class ModuleLauncherProperties {
|
||||
|
||||
/**
|
||||
* Array of modules that need to be launched.
|
||||
* Array of coordinates for modules that need to be launched.
|
||||
*/
|
||||
private String[] modules;
|
||||
|
||||
/**
|
||||
* Map of arguments, keyed by the 0-based index in the {@kink #modules array}.
|
||||
*/
|
||||
private Map<Integer, Map<String, String>> args = new HashMap<>();
|
||||
|
||||
public void setModules(String[] modules) {
|
||||
this.modules = modules;
|
||||
}
|
||||
@@ -43,4 +69,12 @@ public class ModuleLauncherProperties {
|
||||
return modules;
|
||||
}
|
||||
|
||||
public void setArgs(Map<Integer, Map<String, String>> args) {
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public Map<Integer, Map<String, String>> getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,15 +16,18 @@
|
||||
|
||||
package org.springframework.cloud.stream.module.launcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring boot {@link ApplicationRunner} that triggers {@link ModuleLauncher} to launch the modules.
|
||||
@@ -34,7 +37,7 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
@Component
|
||||
@EnableConfigurationProperties(ModuleLauncherProperties.class)
|
||||
public class ModuleLauncherRunner implements ApplicationRunner {
|
||||
public class ModuleLauncherRunner implements CommandLineRunner {
|
||||
|
||||
private final static Log log = LogFactory.getLog(ModuleLauncherRunner.class);
|
||||
|
||||
@@ -45,15 +48,27 @@ public class ModuleLauncherRunner implements ApplicationRunner {
|
||||
private ModuleLauncher moduleLauncher;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments applicationArguments) throws Exception {
|
||||
String[] launchedModules = moduleLauncherProperties.getModules();
|
||||
public void run(String... args) throws Exception {
|
||||
List<ModuleLaunchRequest> launchRequests = toModuleLaunchRequests(moduleLauncherProperties);
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Launching: "
|
||||
+ StringUtils.arrayToCommaDelimitedString(launchedModules)
|
||||
+ " with arguments: "
|
||||
+ StringUtils.arrayToCommaDelimitedString(applicationArguments
|
||||
.getSourceArgs()));
|
||||
StringBuilder sb = new StringBuilder("Launching\n");
|
||||
for (ModuleLaunchRequest moduleLaunchRequest : launchRequests) {
|
||||
sb.append('\t').append(moduleLaunchRequest).append('\n');
|
||||
}
|
||||
log.info(sb.toString());
|
||||
}
|
||||
this.moduleLauncher.launch(launchedModules, applicationArguments.getSourceArgs());
|
||||
this.moduleLauncher.launch(launchRequests);
|
||||
}
|
||||
|
||||
private List<ModuleLaunchRequest> toModuleLaunchRequests(ModuleLauncherProperties moduleLauncherProperties) {
|
||||
List<ModuleLaunchRequest> requests = new ArrayList<>();
|
||||
String[] modules = moduleLauncherProperties.getModules();
|
||||
Map<Integer, Map<String, String>> arguments = moduleLauncherProperties.getArgs();
|
||||
for (int i = 0; i < modules.length; i++) {
|
||||
ModuleLaunchRequest moduleLaunchRequest = new ModuleLaunchRequest(modules[i], arguments.get(i));
|
||||
requests.add(moduleLaunchRequest);
|
||||
}
|
||||
return requests;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user