Merge branch '1.1.x'
This commit is contained in:
commit
1be12ee945
|
|
@ -142,7 +142,7 @@ Their purpose is to load resources (`.class` files etc.) from nested jar files o
|
|||
files in directories (as opposed to explicitly on the classpath). In the case of the
|
||||
`[Jar|War]Launcher` the nested paths are fixed (`+lib/*.jar+` and `+lib-provided/*.jar+` for
|
||||
the war case) so you just add extra jars in those locations if you want more. The
|
||||
`PropertiesLauncher` looks in `lib/` by default, but you can add additional locations by
|
||||
`PropertiesLauncher` looks in `lib/` in your application archive by default, but you can add additional locations by
|
||||
setting an environment variable `LOADER_PATH` or `loader.path` in `application.properties`
|
||||
(comma-separated list of directories or archives).
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@
|
|||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Used to provide a signed jar -->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
|
|
@ -57,6 +62,7 @@
|
|||
<addTestClassPath>true</addTestClassPath>
|
||||
<skipInvocation>${skipTests}</skipInvocation>
|
||||
<streamLogs>true</streamLogs>
|
||||
<parallelThreads>4</parallelThreads>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.boot.launcher.it</groupId>
|
||||
<artifactId>executable-props</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>@project.groupId@</groupId>
|
||||
<artifactId>@project.artifactId@</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<type>jar</type>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<outputDirectory>${project.build.directory}/assembly</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/assembly/lib</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/jar-with-dependencies.xml</descriptor>
|
||||
</descriptors>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>org.springframework.boot.loader.PropertiesLauncher</mainClass>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Start-Class>org.springframework.boot.load.it.props.EmbeddedJarStarter</Start-Class>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>jar-with-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>4.1.4.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<assembly
|
||||
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
|
||||
<id>full</id>
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<useProjectArtifact/>
|
||||
<includes>
|
||||
<include>${project.groupId}:${project.artifactId}</include>
|
||||
</includes>
|
||||
<unpack>true</unpack>
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/assembly</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2012-2013 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.load.it.props;
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
/**
|
||||
* Main class to start the embedded server.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public final class EmbeddedJarStarter {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
|
||||
context.getBean(SpringConfiguration.class).run(args);
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2012-2013 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.load.it.props;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
|
||||
/**
|
||||
* Spring configuration.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
public class SpringConfiguration {
|
||||
|
||||
private String message = "Jar";
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws IOException {
|
||||
String value = PropertiesLoaderUtils.loadAllProperties("application.properties").getProperty("message");
|
||||
if (value!=null) {
|
||||
this.message = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void run(String... args) {
|
||||
System.err.println("Hello Embedded " + this.message + "!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
message: World
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
def jarfile = './target/executable-props-0.0.1.BUILD-SNAPSHOT-full.jar'
|
||||
|
||||
new File("${basedir}/application.properties").delete()
|
||||
|
||||
String exec(String command) {
|
||||
def proc = command.execute([], basedir)
|
||||
proc.waitFor()
|
||||
proc.err.text
|
||||
}
|
||||
|
||||
String out = exec("java -jar ${jarfile}")
|
||||
assert out.contains('Hello Embedded World!'),
|
||||
'Using -jar my.jar should use the application.properties from the jar\n' + out
|
||||
|
||||
out = exec("java -cp ${jarfile} org.springframework.boot.loader.PropertiesLauncher")
|
||||
assert out.contains('Hello Embedded World!'),
|
||||
'Using -cp my.jar with PropertiesLauncher should use the application.properties from the jar\n' + out
|
||||
|
||||
new File("${basedir}/application.properties").withWriter { it -> it << "message: Foo" }
|
||||
out = exec("java -jar ${jarfile}")
|
||||
assert out.contains('Hello Embedded World!'),
|
||||
'Should use the application.properties from the jar in preference to local filesystem\n' + out
|
||||
|
||||
out = exec("java -Dloader.path=.,lib -jar ${jarfile}")
|
||||
assert out.contains('Hello Embedded Foo!'),
|
||||
'With loader.path=.,lib should use the application.properties from the local filesystem\n' + out
|
||||
|
||||
new File("${basedir}/target/application.properties").withWriter { it -> it << "message: Spam" }
|
||||
|
||||
out = exec("java -Dloader.path=target,.,lib -jar ${jarfile}")
|
||||
assert out.contains('Hello Embedded Spam!'),
|
||||
'With loader.path=target,.,lib should use the application.properties from the target directory\n' + out
|
||||
|
|
@ -124,7 +124,7 @@ public class PropertiesLauncher extends Launcher {
|
|||
*/
|
||||
public static final String SET_SYSTEM_PROPERTIES = "loader.system";
|
||||
|
||||
private static final List<String> DEFAULT_PATHS = Arrays.asList("lib/");
|
||||
private static final List<String> DEFAULT_PATHS = Arrays.asList();
|
||||
|
||||
private static final Pattern WORD_SEPARATOR = Pattern.compile("\\W+");
|
||||
|
||||
|
|
@ -136,6 +136,8 @@ public class PropertiesLauncher extends Launcher {
|
|||
|
||||
private final Properties properties = new Properties();
|
||||
|
||||
private Archive parent;
|
||||
|
||||
public PropertiesLauncher() {
|
||||
if (!isDebug()) {
|
||||
this.logger.setLevel(Level.SEVERE);
|
||||
|
|
@ -144,6 +146,7 @@ public class PropertiesLauncher extends Launcher {
|
|||
this.home = getHomeDirectory();
|
||||
initializeProperties(this.home);
|
||||
initializePaths();
|
||||
this.parent = createArchive();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
|
|
@ -314,15 +317,12 @@ public class PropertiesLauncher extends Launcher {
|
|||
path = cleanupPath(path);
|
||||
// Empty path (i.e. the archive itself if running from a JAR) is always added
|
||||
// to the classpath so no need for it to be explicitly listed
|
||||
if (!(path.equals(".") || path.equals(""))) {
|
||||
if (!path.equals("")) {
|
||||
paths.add(path);
|
||||
}
|
||||
}
|
||||
if (paths.isEmpty()) {
|
||||
// On the other hand, we don't want a completely empty path. If the app is
|
||||
// running from an archive (java -jar) then this will make sure the archive
|
||||
// itself is included at the very least.
|
||||
paths.add(".");
|
||||
paths.add("lib");
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
|
@ -449,6 +449,8 @@ public class PropertiesLauncher extends Launcher {
|
|||
}
|
||||
}
|
||||
addParentClassLoaderEntries(lib);
|
||||
// Entries are reversed when added to the actual classpath
|
||||
Collections.reverse(lib);
|
||||
return lib;
|
||||
}
|
||||
|
||||
|
|
@ -493,41 +495,84 @@ public class PropertiesLauncher extends Launcher {
|
|||
}
|
||||
|
||||
private Archive getNestedArchive(final String root) throws Exception {
|
||||
Archive parent = createArchive();
|
||||
if (root.startsWith("/") || parent.getUrl().equals(this.home.toURI().toURL())) {
|
||||
if (root.startsWith("/")
|
||||
|| this.parent.getUrl().equals(this.home.toURI().toURL())) {
|
||||
// If home dir is same as parent archive, no need to add it twice.
|
||||
return null;
|
||||
}
|
||||
EntryFilter filter = new PrefixMatchingArchiveFilter(root);
|
||||
if (parent.getNestedArchives(filter).isEmpty()) {
|
||||
if (this.parent.getNestedArchives(filter).isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// If there are more archives nested in this subdirectory (root) then create a new
|
||||
// virtual archive for them, and have it added to the classpath
|
||||
return new FilteredArchive(parent, filter);
|
||||
return new FilteredArchive(this.parent, filter);
|
||||
}
|
||||
|
||||
private void addParentClassLoaderEntries(List<Archive> lib) throws IOException,
|
||||
URISyntaxException {
|
||||
ClassLoader parentClassLoader = getClass().getClassLoader();
|
||||
List<Archive> urls = new ArrayList<Archive>();
|
||||
for (URL url : getURLs(parentClassLoader)) {
|
||||
if (url.toString().endsWith(".jar") || url.toString().endsWith(".zip")) {
|
||||
lib.add(0, new JarFileArchive(new File(url.toURI())));
|
||||
urls.add(new JarFileArchive(new File(url.toURI())));
|
||||
}
|
||||
else if (url.toString().endsWith("/*")) {
|
||||
String name = url.getFile();
|
||||
File dir = new File(name.substring(0, name.length() - 1));
|
||||
if (dir.exists()) {
|
||||
lib.add(0,
|
||||
new ExplodedArchive(new File(name.substring(0,
|
||||
name.length() - 1)), false));
|
||||
urls.add(new ExplodedArchive(new File(name.substring(0,
|
||||
name.length() - 1)), false));
|
||||
}
|
||||
}
|
||||
else {
|
||||
String filename = URLDecoder.decode(url.getFile(), "UTF-8");
|
||||
lib.add(0, new ExplodedArchive(new File(filename)));
|
||||
urls.add(new ExplodedArchive(new File(filename)));
|
||||
}
|
||||
}
|
||||
// The parent archive might have a "lib/" directory, meaning we are running from
|
||||
// an executable JAR. We add nested entries from there with low priority (i.e. at
|
||||
// end).
|
||||
addNestedArchivesFromParent(urls);
|
||||
for (Archive archive : urls) {
|
||||
// But only add them if they are not already included
|
||||
if (findArchive(lib, archive) < 0) {
|
||||
lib.add(archive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addNestedArchivesFromParent(List<Archive> urls) {
|
||||
int index = findArchive(urls, this.parent);
|
||||
if (index >= 0) {
|
||||
try {
|
||||
Archive nested = getNestedArchive("lib/");
|
||||
if (nested != null) {
|
||||
List<Archive> extra = new ArrayList<Archive>(
|
||||
nested.getNestedArchives(new ArchiveEntryFilter()));
|
||||
urls.addAll(index + 1, extra);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int findArchive(List<Archive> urls, Archive archive) {
|
||||
// Do not rely on Archive to have an equals() method. Look for the archive by
|
||||
// matching strings.
|
||||
if (archive == null) {
|
||||
return -1;
|
||||
}
|
||||
int i = 0;
|
||||
for (Archive url : urls) {
|
||||
if (url.toString().equals(archive.toString())) {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private URL[] getURLs(ClassLoader classLoader) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue