/*
* Copyright 2012-2017 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.loader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.assertj.core.api.Condition;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.loader.archive.Archive;
import org.springframework.boot.loader.archive.ExplodedArchive;
import org.springframework.boot.loader.archive.JarFileArchive;
import org.springframework.core.io.FileSystemResource;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link PropertiesLauncher}.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
public class PropertiesLauncherTests {
@Rule
public InternalOutputCapture output = new InternalOutputCapture();
@Rule
public ExpectedException expected = ExpectedException.none();
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Before
public void setup() throws IOException {
MockitoAnnotations.initMocks(this);
System.setProperty("loader.home",
new File("src/test/resources").getAbsolutePath());
}
@After
public void close() {
System.clearProperty("loader.home");
System.clearProperty("loader.path");
System.clearProperty("loader.main");
System.clearProperty("loader.config.name");
System.clearProperty("loader.config.location");
System.clearProperty("loader.system");
System.clearProperty("loader.classLoader");
}
@Test
public void testDefaultHome() {
System.clearProperty("loader.home");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getHomeDirectory())
.isEqualTo(new File(System.getProperty("user.dir")));
}
@Test
public void testAlternateHome() throws Exception {
System.setProperty("loader.home", "src/test/resources/home");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getHomeDirectory())
.isEqualTo(new File(System.getProperty("loader.home")));
assertThat(launcher.getMainClass()).isEqualTo("demo.HomeApplication");
}
@Test
public void testNonExistentHome() throws Exception {
System.setProperty("loader.home", "src/test/resources/nonexistent");
this.expected.expectMessage("Invalid source folder");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getHomeDirectory())
.isNotEqualTo(new File(System.getProperty("loader.home")));
}
@Test
public void testUserSpecifiedMain() throws Exception {
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getMainClass()).isEqualTo("demo.Application");
assertThat(System.getProperty("loader.main")).isNull();
}
@Test
public void testUserSpecifiedConfigName() throws Exception {
System.setProperty("loader.config.name", "foo");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getMainClass()).isEqualTo("my.Application");
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
.isEqualTo("[etc/]");
}
@Test
public void testRootOfClasspathFirst() throws Exception {
System.setProperty("loader.config.name", "bar");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getMainClass()).isEqualTo("my.BarApplication");
}
@Test
public void testUserSpecifiedDotPath() throws Exception {
System.setProperty("loader.path", ".");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
.isEqualTo("[.]");
}
@Test
public void testUserSpecifiedSlashPath() throws Exception {
System.setProperty("loader.path", "jars/");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
.isEqualTo("[jars/]");
List<Archive> archives = launcher.getClassPathArchives();
assertThat(archives).areExactly(1, endingWith("app.jar!/"));
}
@Test
public void testUserSpecifiedWildcardPath() throws Exception {
System.setProperty("loader.path", "jars/*");
System.setProperty("loader.main", "demo.Application");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
.isEqualTo("[jars/]");
launcher.launch(new String[0]);
waitFor("Hello World");
}
@Test
public void testUserSpecifiedJarPath() throws Exception {
System.setProperty("loader.path", "jars/app.jar");
System.setProperty("loader.main", "demo.Application");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
.isEqualTo("[jars/app.jar]");
launcher.launch(new String[0]);
waitFor("Hello World");
}
@Test
public void testUserSpecifiedRootOfJarPath() throws Exception {
System.setProperty("loader.path",
"jar:file:./src/test/resources/nested-jars/app.jar!/");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
.isEqualTo("[jar:file:./src/test/resources/nested-jars/app.jar!/]");
List<Archive> archives = launcher.getClassPathArchives();
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
assertThat(archives).areExactly(1, endingWith("app.jar!/"));
}
@Test
public void testUserSpecifiedRootOfJarPathWithDot() throws Exception {
System.setProperty("loader.path", "nested-jars/app.jar!/./");
PropertiesLauncher launcher = new PropertiesLauncher();
List<Archive> archives = launcher.getClassPathArchives();
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
assertThat(archives).areExactly(1, endingWith("app.jar!/"));
}
@Test
public void testUserSpecifiedRootOfJarPathWithDotAndJarPrefix() throws Exception {
System.setProperty("loader.path",
"jar:file:./src/test/resources/nested-jars/app.jar!/./");
PropertiesLauncher launcher = new PropertiesLauncher();
List<Archive> archives = launcher.getClassPathArchives();
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
}
@Test
public void testUserSpecifiedJarFileWithNestedArchives() throws Exception {
System.setProperty("loader.path", "nested-jars/app.jar");
System.setProperty("loader.main", "demo.Application");
PropertiesLauncher launcher = new PropertiesLauncher();
List<Archive> archives = launcher.getClassPathArchives();
assertThat(archives).areExactly(1, endingWith("foo.jar!/"));
assertThat(archives).areExactly(1, endingWith("app.jar!/"));
}
@Test
public void testUserSpecifiedDirectoryContainingJarFileWithNestedArchives()
throws Exception {
System.setProperty("loader.path", "nested-jars");
System.setProperty("loader.main", "demo.Application");
PropertiesLauncher launcher = new PropertiesLauncher();
launcher.launch(new String[0]);
waitFor("Hello World");
}
@Test
public void testUserSpecifiedJarPathWithDot() throws Exception {
System.setProperty("loader.path", "./jars/app.jar");
System.setProperty("loader.main", "demo.Application");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
.isEqualTo("[jars/app.jar]");
launcher.launch(new String[0]);
waitFor("Hello World");
}
@Test
public void testUserSpecifiedClassLoader() throws Exception {
System.setProperty("loader.path", "jars/app.jar");
System.setProperty("loader.classLoader", URLClassLoader.class.getName());
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
.isEqualTo("[jars/app.jar]");
launcher.launch(new String[0]);
waitFor("Hello World");
}
@Test
public void testUserSpecifiedClassPathOrder() throws Exception {
System.setProperty("loader.path", "more-jars/app.jar,jars/app.jar");
System.setProperty("loader.classLoader", URLClassLoader.class.getName());
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(ReflectionTestUtils.getField(launcher, "paths").toString())
.isEqualTo("[more-jars/app.jar, jars/app.jar]");
launcher.launch(new String[0]);
waitFor("Hello Other World");
}
@Test
public void testCustomClassLoaderCreation() throws Exception {
System.setProperty("loader.classLoader", TestLoader.class.getName());
PropertiesLauncher launcher = new PropertiesLauncher();
ClassLoader loader = launcher.createClassLoader(archives());
assertThat(loader).isNotNull();
assertThat(loader.getClass().getName()).isEqualTo(TestLoader.class.getName());
}
private List<Archive> archives() throws Exception {
List<Archive> archives = new ArrayList<Archive>();
String path = System.getProperty("java.class.path");
for (String url : path.split(File.pathSeparator)) {
archives.add(archive(url));
}
return archives;
}
private Archive archive(String url) throws IOException {
File file = new FileSystemResource(url).getFile();
if (url.endsWith(".jar")) {
return new JarFileArchive(file);
}
return new ExplodedArchive(file);
}
@Test
public void testUserSpecifiedConfigPathWins() throws Exception {
System.setProperty("loader.config.name", "foo");
System.setProperty("loader.config.location", "classpath:bar.properties");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getMainClass()).isEqualTo("my.BarApplication");
}
@Test
public void testSystemPropertySpecifiedMain() throws Exception {
System.setProperty("loader.main", "foo.Bar");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getMainClass()).isEqualTo("foo.Bar");
}
@Test
public void testSystemPropertiesSet() throws Exception {
System.setProperty("loader.system", "true");
new PropertiesLauncher();
assertThat(System.getProperty("loader.main")).isEqualTo("demo.Application");
}
@Test
public void testArgsEnhanced() throws Exception {
System.setProperty("loader.args", "foo");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(Arrays.asList(launcher.getArgs("bar")).toString())
.isEqualTo("[foo, bar]");
}
@SuppressWarnings("unchecked")
@Test
public void testLoadPathCustomizedUsingManifest() throws Exception {
System.setProperty("loader.home",
this.temporaryFolder.getRoot().getAbsolutePath());
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
manifest.getMainAttributes().putValue("Loader-Path", "/foo.jar, /bar");
File manifestFile = new File(this.temporaryFolder.getRoot(),
"META-INF/MANIFEST.MF");
manifestFile.getParentFile().mkdirs();
manifest.write(new FileOutputStream(manifestFile));
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat((List<String>) ReflectionTestUtils.getField(launcher, "paths"))
.containsExactly("/foo.jar", "/bar/");
}
@Test
public void testManifestWithPlaceholders() throws Exception {
System.setProperty("loader.home", "src/test/resources/placeholders");
PropertiesLauncher launcher = new PropertiesLauncher();
assertThat(launcher.getMainClass()).isEqualTo("demo.FooApplication");
}
private void waitFor(String value) throws Exception {
int count = 0;
boolean timeout = false;
while (!timeout && count < 100) {
count++;
Thread.sleep(50L);
timeout = this.output.toString().contains(value);
}
assertThat(timeout).as("Timed out waiting for (" + value + ")").isTrue();
}
private Condition<Archive> endingWith(final String value) {
return new Condition<Archive>() {
@Override
public boolean matches(Archive archive) {
return archive.toString().endsWith(value);
}
};
}
public static class TestLoader extends URLClassLoader {
public TestLoader(ClassLoader parent) {
super(new URL[0], parent);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return super.findClass(name);
}
}
}