diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/app/SpringApplicationLauncher.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/app/SpringApplicationLauncher.java index 10ec0cbbd1..01068993a5 100644 --- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/app/SpringApplicationLauncher.java +++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/app/SpringApplicationLauncher.java @@ -22,21 +22,26 @@ import java.util.HashMap; import java.util.Map; /** - * A launcher for {@code SpringApplication}. Uses reflection to allow the launching code - * to exist in a separate ClassLoader from the application code. + * A launcher for {@code SpringApplication} or a {@code SpringApplication} subclass. The + * class that is used can be configured using the System property + * {@code spring.application.class.name} or the {@code SPRING_APPLICATION_CLASS_NAME} + * environment variable. Uses reflection to allow the launching code to exist in a + * separate ClassLoader from the application code. * * @author Andy Wilkinson * @since 1.2.0 + * @see System#getProperty(String) + * @see System#getenv(String) */ public class SpringApplicationLauncher { - private static final String SPRING_APPLICATION_CLASS = "org.springframework.boot.SpringApplication"; + private static final String DEFAULT_SPRING_APPLICATION_CLASS = "org.springframework.boot.SpringApplication"; private final ClassLoader classLoader; /** - * Creates a new launcher that will use the given {@code classLoader} to load - * {@code SpringApplication}. + * Creates a new launcher that will use the given {@code classLoader} to load the + * configured {@code SpringApplication} class. * @param classLoader the {@code ClassLoader} to use */ public SpringApplicationLauncher(ClassLoader classLoader) { @@ -54,7 +59,8 @@ public class SpringApplicationLauncher { public Object launch(Object[] sources, String[] args) throws Exception { Map defaultProperties = new HashMap(); defaultProperties.put("spring.groovy.template.check-template-location", "false"); - Class applicationClass = this.classLoader.loadClass(SPRING_APPLICATION_CLASS); + Class applicationClass = this.classLoader + .loadClass(getSpringApplicationClassName()); Constructor constructor = applicationClass.getConstructor(Object[].class); Object application = constructor.newInstance((Object) sources); applicationClass.getMethod("setDefaultProperties", Map.class).invoke(application, @@ -63,4 +69,19 @@ public class SpringApplicationLauncher { return method.invoke(application, (Object) args); } + private String getSpringApplicationClassName() { + String className = System.getProperty("spring.application.class.name"); + if (className == null) { + className = getEnvironmentVariable("SPRING_APPLICATION_CLASS_NAME"); + } + if (className == null) { + className = DEFAULT_SPRING_APPLICATION_CLASS; + } + return className; + } + + protected String getEnvironmentVariable(String name) { + return System.getenv(name); + } + } diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/app/SpringApplicationLauncherTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/app/SpringApplicationLauncherTests.java new file mode 100644 index 0000000000..0442111ece --- /dev/null +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/app/SpringApplicationLauncherTests.java @@ -0,0 +1,162 @@ +/* + * Copyright 2012-2014 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.boot.cli.app; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.After; +import org.junit.Test; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link SpringApplicationLauncher} + * + * @author Andy Wilkinson + */ +public class SpringApplicationLauncherTests { + + private Map env = new HashMap(); + + @After + public void cleanUp() { + System.clearProperty("spring.application.class.name"); + } + + @Test + public void defaultLaunch() throws Exception { + assertThat(launch(), contains("org.springframework.boot.SpringApplication")); + } + + @Test + public void launchWithClassConfiguredBySystemProperty() { + System.setProperty("spring.application.class.name", + "system.property.SpringApplication"); + assertThat(launch(), contains("system.property.SpringApplication")); + } + + @Test + public void launchWithClassConfiguredByEnvironmentVariable() { + this.env.put("SPRING_APPLICATION_CLASS_NAME", + "environment.variable.SpringApplication"); + assertThat(launch(), contains("environment.variable.SpringApplication")); + } + + @Test + public void systemPropertyOverridesEnvironmentVariable() { + System.setProperty("spring.application.class.name", + "system.property.SpringApplication"); + this.env.put("SPRING_APPLICATION_CLASS_NAME", + "environment.variable.SpringApplication"); + assertThat(launch(), contains("system.property.SpringApplication")); + + } + + @Test + public void sourcesDefaultPropertiesAndArgsAreUsedToLaunch() throws Exception { + System.setProperty("spring.application.class.name", + TestSpringApplication.class.getName()); + + Object[] sources = new Object[0]; + String[] args = new String[0]; + new SpringApplicationLauncher(getClass().getClassLoader()).launch(sources, args); + + assertTrue(sources == TestSpringApplication.sources); + assertTrue(args == TestSpringApplication.args); + + Map defaultProperties = TestSpringApplication.defaultProperties; + assertThat(defaultProperties.size(), equalTo(1)); + assertThat( + defaultProperties.get("spring.groovy.template.check-template-location"), + equalTo("false")); + } + + private List launch() { + TestClassLoader classLoader = new TestClassLoader(getClass().getClassLoader()); + try { + new TestSpringApplicationLauncher(classLoader).launch(new Object[0], + new String[0]); + } + catch (Exception e) { + // SpringApplication isn't on the classpath, but we can still check that + // the launcher tried to use the right class + } + return classLoader.classes; + } + + private static class TestClassLoader extends ClassLoader { + + private List classes = new ArrayList(); + + public TestClassLoader(ClassLoader parent) { + super(parent); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + this.classes.add(name); + return super.findClass(name); + } + + } + + @SuppressWarnings("unused") + private static class TestSpringApplication { + + private static Object[] sources; + + private static Map defaultProperties; + + private static String[] args; + + public TestSpringApplication(Object[] sources) { + TestSpringApplication.sources = sources; + } + + public void setDefaultProperties(Map defaultProperties) { + TestSpringApplication.defaultProperties = defaultProperties; + } + + public void run(String[] args) { + TestSpringApplication.args = args; + } + } + + private class TestSpringApplicationLauncher extends SpringApplicationLauncher { + + public TestSpringApplicationLauncher(ClassLoader classLoader) { + super(classLoader); + } + + @Override + protected String getEnvironmentVariable(String name) { + String variable = SpringApplicationLauncherTests.this.env.get(name); + if (variable == null) { + variable = super.getEnvironmentVariable(name); + } + return variable; + } + + } + +}