Convert thread to an actual application

User can now use spring boot CLI as before or alternatively just
java -jar the deployer app.
This commit is contained in:
Dave Syer
2016-12-15 14:43:48 +00:00
parent e3a0dc8395
commit 7e31e9b36c
7 changed files with 75 additions and 81 deletions

View File

@@ -5,26 +5,7 @@ cache:
language: java language: java
jdk: # TODO: should work with java 1.7 jdk: # TODO: should work with java 1.7
- oraclejdk8 - oraclejdk8
before_install:
- git config user.name "$GIT_NAME"
- git config user.email "$GIT_EMAIL"
- git config credential.helper "store --file=.git/credentials"
- echo "https://$GH_TOKEN:@github.com" > .git/credentials
- gem install asciidoctor
install: install:
- ./mvnw install -P docs -q -U -DskipTests=true -Dmaven.test.redirectTestOutputToFile=true - ./mvnw install -P docs -q -U -DskipTests=true -Dmaven.test.redirectTestOutputToFile=true
- '[ "${MVN_GOAL}" == "deploy" ] && ./docs/src/main/asciidoc/ghpages.sh || echo "Not updating docs"'
script: script:
- './mvnw -s .settings.xml $MVN_GOAL $MVN_PROFILE -nsu -Dmaven.test.redirectTestOutputToFile=true' - './mvnw install -nsu -Dmaven.test.redirectTestOutputToFile=true'
env:
global:
- GIT_NAME="Dave Syer"
- GIT_EMAIL=dsyer@pivotal.io
- CI_DEPLOY_USERNAME=buildmaster
- FEATURE_BRANCH=$(echo ${TRAVIS_BRANCH} | grep -q "^.*/.*$" && echo true || echo false)
- SPRING_CLOUD_BUILD=$(echo ${TRAVIS_REPO_SLUG} | grep -q "^spring-cloud/.*$" && echo true || echo false)
- MVN_GOAL=$([ "${TRAVIS_PULL_REQUEST}" == "false" -a "${TRAVIS_TAG}" == "" -a "${FEATURE_BRANCH}" == "false" -a "${SPRING_CLOUD_BUILD}" == "true" ] && echo deploy || echo install)
- VERSION=$(mvn validate | grep Building | head -1 | sed -e 's/.* //')
- MILESTONE=$(echo ${VERSION} | egrep 'M|RC' && echo true || echo false)
- MVN_PROFILE=$([ "${MILESTONE}" == "true" ] && echo -P milestone)
- secure: "mGJnfRsxSdjAwedXAB8kvC3ckc1TGHS2ym+8dP7TSPhknpM9To+S6THwTmawOpJFq09A/1OlUWeanpGEIHCXB/Oz73ZQ9xqheqFf3mk83xOV0TjIP3AzT2boTNRVFGe+LXOIRgTwg7jULl9TIf6pBUK3s5iPvLYlZhCq1Q6QEIU="

View File

@@ -17,9 +17,9 @@
package org.springframework.cloud.launcher.cli; package org.springframework.cloud.launcher.cli;
import java.io.File; import java.io.File;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -29,7 +29,7 @@ import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.springframework.boot.cli.command.HelpExample; import org.springframework.boot.cli.command.HelpExample;
import org.springframework.boot.cli.command.OptionParsingCommand; import org.springframework.boot.cli.command.OptionParsingCommand;
import org.springframework.boot.cli.command.options.OptionHandler; import org.springframework.boot.cli.command.options.OptionHandler;
@@ -41,7 +41,6 @@ import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext;
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import groovy.lang.GroovyClassLoader;
import joptsimple.OptionSet; import joptsimple.OptionSet;
import joptsimple.OptionSpec; import joptsimple.OptionSpec;
@@ -96,21 +95,17 @@ public class LauncherCommand extends OptionParsingCommand {
@Override @Override
protected synchronized ExitStatus run(OptionSet options) throws Exception { protected synchronized ExitStatus run(OptionSet options) throws Exception {
if (options.has(this.versionOption)) { if (options.has(this.versionOption)) {
System.out.println("Spring Cloud CLI v"+getVersion()); System.out.println("Spring Cloud CLI v" + getVersion());
return ExitStatus.OK; return ExitStatus.OK;
} }
try { try {
URLClassLoader classLoader = populateClassloader(options); URLClassLoader classLoader = populateClassloader(options);
// This is the main class in the deployer archive:
String name = "org.springframework.cloud.launcher.deployer.DeployerThread"; String name = "org.springframework.boot.loader.wrapper.ThinJarWrapper";
Class<?> threadClass = classLoader.loadClass(name); Class<?> threadClass = classLoader.loadClass(name);
URL url = classLoader.getURLs()[0];
Constructor<?> constructor = threadClass.getConstructor(ClassLoader.class, threadClass.getMethod("main", String[].class).invoke(null,
String[].class); new Object[] { getArgs(options, url) });
Thread thread = (Thread) constructor.newInstance(classLoader,
getArgs(options));
thread.start();
thread.join();
} }
catch (Exception e) { catch (Exception e) {
log.error("Error running spring cloud", e); log.error("Error running spring cloud", e);
@@ -120,9 +115,10 @@ public class LauncherCommand extends OptionParsingCommand {
return ExitStatus.OK; return ExitStatus.OK;
} }
private String[] getArgs(OptionSet options) { private String[] getArgs(OptionSet options, URL url) {
List<Object> args = new ArrayList<>(); List<Object> args = new ArrayList<>();
List<String> apps = new ArrayList<>(); List<String> apps = new ArrayList<>();
args.add("--thin.archive=" + url.toString());
int sourceArgCount = 0; int sourceArgCount = 0;
for (Object option : options.nonOptionArguments()) { for (Object option : options.nonOptionArguments()) {
if (option instanceof String) { if (option instanceof String) {
@@ -157,10 +153,6 @@ public class LauncherCommand extends OptionParsingCommand {
throws MalformedURLException { throws MalformedURLException {
DependencyResolutionContext resolutionContext = new DependencyResolutionContext(); DependencyResolutionContext resolutionContext = new DependencyResolutionContext();
GroovyClassLoader loader = new GroovyClassLoader(
Thread.currentThread().getContextClassLoader(),
new CompilerConfiguration());
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory
.createDefaultRepositoryConfiguration(); .createDefaultRepositoryConfiguration();
repositoryConfiguration.add(0, new RepositoryConfiguration("local", repositoryConfiguration.add(0, new RepositoryConfiguration("local",
@@ -170,7 +162,7 @@ public class LauncherCommand extends OptionParsingCommand {
System.setProperty("groovy.grape.report.downloads", "true"); System.setProperty("groovy.grape.report.downloads", "true");
} }
AetherGrapeEngine grapeEngine = AetherGrapeEngineFactory.create(loader, AetherGrapeEngine grapeEngine = AetherGrapeEngineFactory.create(null,
repositoryConfiguration, resolutionContext); repositoryConfiguration, resolutionContext);
HashMap<String, String> dependency = new HashMap<>(); HashMap<String, String> dependency = new HashMap<>();
@@ -178,16 +170,16 @@ public class LauncherCommand extends OptionParsingCommand {
dependency.put("module", "spring-cloud-launcher-deployer"); dependency.put("module", "spring-cloud-launcher-deployer");
dependency.put("version", getVersion()); dependency.put("version", getVersion());
URI[] uris = grapeEngine.resolve(null, dependency); URI[] uris = grapeEngine.resolve(null, dependency);
for (URI uri : uris) { URLClassLoader loader = new URLClassLoader(new URL[] { uris[0].toURL() },
loader.addURL(uri.toURL()); getClass().getClassLoader().getParent());
}
log.debug("resolved URIs " + Arrays.asList(loader.getURLs())); log.debug("resolved URIs " + Arrays.asList(loader.getURLs()));
return loader; return loader;
} }
private String getVersion() { private String getVersion() {
Package pkg = LauncherCommand.class.getPackage(); Package pkg = LauncherCommand.class.getPackage();
String version = (pkg != null ? pkg.getImplementationVersion() : DEFAULT_VERSION); String version = (pkg != null ? pkg.getImplementationVersion()
: DEFAULT_VERSION);
return version != null ? version : DEFAULT_VERSION; return version != null ? version : DEFAULT_VERSION;
} }

View File

@@ -30,7 +30,7 @@ public class LauncherCommandTests {
@Rule @Rule
public OutputCapture output = new OutputCapture(); public OutputCapture output = new OutputCapture();
@Test @Test
public void testCreateClassLoaderAndListDeployables() throws Exception { public void testCreateClassLoaderAndListDeployables() throws Exception {
new LauncherCommand().run("--list"); new LauncherCommand().run("--list");
@@ -39,7 +39,8 @@ public class LauncherCommandTests {
@Test @Test
public void testNonOptionArgsPassedDown() throws Exception { public void testNonOptionArgsPassedDown() throws Exception {
new LauncherCommand().run("--list", "--", "--spring.profiles.active=test"); new LauncherCommand().run("--list", "--", "--spring.profiles.active=test",
"--spring.config.location=file:./src/test/resources/");
assertThat(output.toString(), containsString("foo")); assertThat(output.toString(), containsString("foo"));
} }

View File

@@ -18,6 +18,7 @@
<properties> <properties>
<spring-cloud-deployer.version>1.1.1.RELEASE</spring-cloud-deployer.version> <spring-cloud-deployer.version>1.1.1.RELEASE</spring-cloud-deployer.version>
<spring-cloud-deployer-thin.version>0.0.1.BUILD-SNAPSHOT</spring-cloud-deployer-thin.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@@ -27,6 +28,11 @@
<artifactId>spring-cloud-deployer-local</artifactId> <artifactId>spring-cloud-deployer-local</artifactId>
<version>${spring-cloud-deployer.version}</version> <version>${spring-cloud-deployer.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-deployer-thin</artifactId>
<version>${spring-cloud-deployer-thin.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-deployer-resource-support</artifactId> <artifactId>spring-cloud-deployer-resource-support</artifactId>
@@ -55,4 +61,12 @@
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project> </project>

View File

@@ -16,8 +16,17 @@
package org.springframework.cloud.launcher.deployer; package org.springframework.cloud.launcher.deployer;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.cloud.deployer.spi.app.AppDeployer; import org.springframework.cloud.deployer.spi.app.AppDeployer;
import org.springframework.cloud.deployer.spi.app.AppStatus; import org.springframework.cloud.deployer.spi.app.AppStatus;
@@ -37,14 +46,6 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.springframework.util.StringUtils.collectionToCommaDelimitedString; import static org.springframework.util.StringUtils.collectionToCommaDelimitedString;
/** /**
@@ -102,8 +103,7 @@ public class Deployer {
logger.debug("Deployables {}", properties.getDeployables()); logger.debug("Deployables {}", properties.getDeployables());
for (Deployable deployable : deployables) { for (Deployable deployable : deployables) {
deployInternal(deployer, resourceLoader, deployable, deployInternal(deployer, resourceLoader, deployable, properties, environment);
properties, environment);
} }
for (Deployable deployable : deployables) { for (Deployable deployable : deployables) {
@@ -251,10 +251,11 @@ public class Deployer {
private PropertySource<?> extractPropertySource(String path) { private PropertySource<?> extractPropertySource(String path) {
PropertySource<?> source = null; PropertySource<?> source = null;
Resource resource = new ClassPathResource("config" + path, DeployerThread.class); Resource resource = new ClassPathResource("config" + path,
DeployerApplication.class);
source = loadPropertySource(resource, path); source = loadPropertySource(resource, path);
if (source == null) { if (source == null) {
resource = new ClassPathResource(path, DeployerThread.class); resource = new ClassPathResource(path, DeployerApplication.class);
source = loadPropertySource(resource, path); source = loadPropertySource(resource, path);
} }
if (source == null) { if (source == null) {

View File

@@ -16,8 +16,14 @@
package org.springframework.cloud.launcher.deployer; package org.springframework.cloud.launcher.deployer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.Banner.Mode; import org.springframework.boot.Banner.Mode;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
@@ -27,31 +33,27 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/** /**
* @author Spencer Gibb * @author Spencer Gibb
*/ */
public class DeployerThread extends Thread { public class DeployerApplication {
private static final Logger logger = LoggerFactory.getLogger(DeployerThread.class); private static final Logger logger = LoggerFactory
.getLogger(DeployerApplication.class);
private static final String DEFAULT_VERSION = "1.2.3.BUILD-SNAPSHOT"; private static final String DEFAULT_VERSION = "1.3.0.BUILD-SNAPSHOT";
private String[] args; private String[] args;
public DeployerThread(ClassLoader classLoader, String... args) { public DeployerApplication(String... args) {
super("spring-cloud-launcher");
this.args = args; this.args = args;
setContextClassLoader(classLoader);
setDaemon(true);
} }
@Override public static void main(String[] args) {
public void run() { new DeployerApplication(args).run();
}
void run() {
List<String> list = Arrays.asList(this.args); List<String> list = Arrays.asList(this.args);
if (list.contains("--launcher.list=true")) { if (list.contains("--launcher.list=true")) {
quiet(); quiet();
@@ -64,9 +66,10 @@ public class DeployerThread extends Thread {
private void quiet() { private void quiet() {
try { try {
LogbackLoggingSystem.get(ClassUtils.getDefaultClassLoader()).setLogLevel("ROOT", LogbackLoggingSystem.get(ClassUtils.getDefaultClassLoader())
LogLevel.OFF); .setLogLevel("ROOT", LogLevel.OFF);
} catch (Exception e) { }
catch (Exception e) {
logger.error("Unable to turn of ROOT logger for quiet()", e); logger.error("Unable to turn of ROOT logger for quiet()", e);
} }
} }
@@ -98,7 +101,7 @@ public class DeployerThread extends Thread {
} }
private String getVersion() { private String getVersion() {
Package pkg = DeployerThread.class.getPackage(); Package pkg = DeployerApplication.class.getPackage();
return (pkg != null ? pkg.getImplementationVersion() : DEFAULT_VERSION); return (pkg != null ? pkg.getImplementationVersion() : DEFAULT_VERSION);
} }

View File

@@ -25,26 +25,28 @@ import static org.junit.Assert.assertThat;
/** /**
* @author Spencer Gibb * @author Spencer Gibb
*/ */
public class DeployerThreadTests { public class DeployerApplicationTests {
@Rule @Rule
public OutputCapture output = new OutputCapture(); public OutputCapture output = new OutputCapture();
@Test @Test
public void testCreateClassLoaderAndListDeployables() throws Exception { public void testCreateClassLoaderAndListDeployables() throws Exception {
new DeployerThread(DeployerThread.class.getClassLoader(), "--launcher.list=true").run();; new DeployerApplication("--launcher.list=true").run();
assertThat(output.toString(), containsString("configserver")); assertThat(output.toString(), containsString("configserver"));
} }
@Test @Test
public void testNonOptionArgsPassedDown() throws Exception { public void testNonOptionArgsPassedDown() throws Exception {
new DeployerThread(DeployerThread.class.getClassLoader(), "--launcher.list=true", "--spring.profiles.active=test").run(); new DeployerApplication("--launcher.list=true", "--spring.profiles.active=test")
.run();
assertThat(output.toString(), containsString("foo")); assertThat(output.toString(), containsString("foo"));
} }
@Test @Test
public void testInvalidDeployableFails() throws Exception { public void testInvalidDeployableFails() throws Exception {
new DeployerThread(DeployerThread.class.getClassLoader(), "--launcher.deploy=foo,bar").run(); new DeployerApplication("--launcher.deploy=foo,bar").run();
assertThat(output.toString(), containsString("The following are not valid: 'foo,bar'")); assertThat(output.toString(),
containsString("The following are not valid: 'foo,bar'"));
} }
} }