Add PropertiesLauncher
parent
604b9069b1
commit
f83fd47184
@ -0,0 +1,2 @@
|
|||||||
|
loader.path: target,target/lib,.
|
||||||
|
loader.main: org.springframework.boot.load.it.jar.EmbeddedJarStarter
|
@ -0,0 +1,87 @@
|
|||||||
|
<?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-dir</artifactId>
|
||||||
|
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<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.6</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>unpack</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy</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}/lib</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>copy</id>
|
||||||
|
<phase>prepare-package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>1.2.1</version>
|
||||||
|
<configuration>
|
||||||
|
<executable>java</executable>
|
||||||
|
<arguments>
|
||||||
|
<argument>-cp</argument>
|
||||||
|
<argument>${project.build.directory}/lib/@project.artifactId@-@project.version@.jar</argument>
|
||||||
|
<argument>org.springframework.boot.loader.PropertiesLauncher</argument>
|
||||||
|
</arguments>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-webapp</artifactId>
|
||||||
|
<version>8.1.8.v20121106</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-annotations</artifactId>
|
||||||
|
<version>8.1.8.v20121106</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
<version>3.2.0.RELEASE</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 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.jar;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||||
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main class to start the embedded server.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
public final class EmbeddedJarStarter {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
Server server = new Server(8080);
|
||||||
|
|
||||||
|
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||||
|
context.setContextPath("/");
|
||||||
|
server.setHandler(context);
|
||||||
|
|
||||||
|
AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();
|
||||||
|
webApplicationContext.register(SpringConfiguration.class);
|
||||||
|
DispatcherServlet dispatcherServlet = new DispatcherServlet(webApplicationContext);
|
||||||
|
context.addServlet(new ServletHolder(dispatcherServlet), "/*");
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
server.join();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 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.jar;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple example Spring MVC Controller.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
public class ExampleController {
|
||||||
|
|
||||||
|
@RequestMapping("/")
|
||||||
|
@ResponseBody
|
||||||
|
public String helloWorld() {
|
||||||
|
return "Hello Embedded Jar World!";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 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.jar;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring configuration.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableWebMvc
|
||||||
|
@ComponentScan
|
||||||
|
public class SpringConfiguration {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 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.loader;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.springframework.boot.loader.util.SystemPropertyUtils;
|
||||||
|
import org.springframework.util.ResourceUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* {@link Launcher} for archives with user-configured classpath and main class via a
|
||||||
|
* properties file. This model is often more flexible and more amenable to creating
|
||||||
|
* well-behaved OS-level services than a model based on executable jars.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Looks in various places for a properties file to extract loader settings, defaulting to
|
||||||
|
* <code>application.properties</code> either on the current classpath or in the current
|
||||||
|
* working directory. The name of the properties file can be changed by setting a System
|
||||||
|
* property <code>loader.config.name</code> (e.g. <code>-Dloader.config.name=foo</code>
|
||||||
|
* will look for <code>foo.properties</code>. If that file doesn't exist then tries
|
||||||
|
* <code>loader.config.location</code> (with allowed prefixes <code>classpath:</code> and
|
||||||
|
* <code>file:</code> or any valid URL). Once that file is located turns it into
|
||||||
|
* Properties and extracts optional values (which can also be provided oroverridden as
|
||||||
|
* System properties in case the file doesn't exist):
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li><code>loader.path</code>: a comma-separated list of classpath directories
|
||||||
|
* (containing file resources and/or archives in *.jar or *.zip). Defaults to
|
||||||
|
* <code>lib</code> (i.e. a directory in the current working directory)</li>
|
||||||
|
* <li><code>loader.main</code>: the main method to delegate execution to once the class
|
||||||
|
* loader is set up. No default, but will fall back to looking in a
|
||||||
|
* <code>MANIFEST.MF</code> if there is one.</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*/
|
||||||
|
public class PropertiesLauncher extends Launcher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties key for main class
|
||||||
|
*/
|
||||||
|
public static final String MAIN = "loader.main";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties key for classpath entries (directories possibly containing jars)
|
||||||
|
*/
|
||||||
|
public static final String PATH = "loader.path";
|
||||||
|
|
||||||
|
public static final String HOME = "loader.home";
|
||||||
|
|
||||||
|
public static String CONFIG_NAME = "loader.config.name";
|
||||||
|
|
||||||
|
public static String CONFIG_LOCATION = "loader.config.location";
|
||||||
|
|
||||||
|
private Logger logger = Logger.getLogger(Launcher.class.getName());
|
||||||
|
|
||||||
|
private static final List<String> DEFAULT_PATHS = Arrays.asList("lib/");
|
||||||
|
|
||||||
|
private List<String> paths = new ArrayList<String>(DEFAULT_PATHS);
|
||||||
|
|
||||||
|
private Properties properties = new Properties();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void launch(String[] args) {
|
||||||
|
try {
|
||||||
|
launch(args, new ExplodedArchive(new File(getHomeDirectory())));
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getHomeDirectory() {
|
||||||
|
return SystemPropertyUtils.resolvePlaceholders(System.getProperty(HOME,
|
||||||
|
"${user.dir}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isNestedArchive(Archive.Entry entry) {
|
||||||
|
String name = entry.getName();
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
for (String path : this.paths) {
|
||||||
|
if (path.length() > 0 && name.equals(path)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (String path : this.paths) {
|
||||||
|
if (path.length() > 0 && name.startsWith(path) && isArchive(name)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void postProcessLib(Archive archive, List<Archive> lib) throws Exception {
|
||||||
|
lib.add(0, archive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look in various places for a properties file to extract loader settings. Default to
|
||||||
|
* <code>application.properties</code> either on the current classpath or in the
|
||||||
|
* current working directory.
|
||||||
|
*
|
||||||
|
* @see org.springframework.boot.loader.Launcher#launch(java.lang.String[],
|
||||||
|
* org.springframework.boot.loader.Archive)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void launch(String[] args, Archive archive) throws Exception {
|
||||||
|
initialize();
|
||||||
|
super.launch(args, archive);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initialize() throws Exception {
|
||||||
|
String config = SystemPropertyUtils.resolvePlaceholders(System.getProperty(
|
||||||
|
CONFIG_NAME, "application")) + ".properties";
|
||||||
|
while (config.startsWith("/")) {
|
||||||
|
config = config.substring(1);
|
||||||
|
}
|
||||||
|
this.logger.fine("Trying default location: " + config);
|
||||||
|
InputStream resource = getClass().getResourceAsStream("/" + config);
|
||||||
|
if (resource == null) {
|
||||||
|
|
||||||
|
config = SystemPropertyUtils.resolvePlaceholders(System.getProperty(
|
||||||
|
CONFIG_LOCATION, config));
|
||||||
|
|
||||||
|
if (config.startsWith("classpath:")) {
|
||||||
|
|
||||||
|
config = config.substring("classpath:".length());
|
||||||
|
while (config.startsWith("/")) {
|
||||||
|
config = config.substring(1);
|
||||||
|
}
|
||||||
|
config = "/" + config;
|
||||||
|
this.logger.fine("Trying classpath: " + config);
|
||||||
|
resource = getClass().getResourceAsStream(config);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
if (config.startsWith("file:")) {
|
||||||
|
|
||||||
|
config = config.substring("file:".length());
|
||||||
|
if (config.startsWith("//")) {
|
||||||
|
config = config.substring(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!config.contains(":")) {
|
||||||
|
|
||||||
|
File file = new File(config);
|
||||||
|
this.logger.fine("Trying file: " + config);
|
||||||
|
if (file.canRead()) {
|
||||||
|
resource = new FileInputStream(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
URL url = new URL(config);
|
||||||
|
if (exists(url)) {
|
||||||
|
URLConnection con = url.openConnection();
|
||||||
|
try {
|
||||||
|
resource = con.getInputStream();
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
// Close the HTTP connection (if applicable).
|
||||||
|
if (con instanceof HttpURLConnection) {
|
||||||
|
((HttpURLConnection) con).disconnect();
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource != null) {
|
||||||
|
this.logger.info("Found: " + config);
|
||||||
|
this.properties.load(resource);
|
||||||
|
try {
|
||||||
|
String path = System.getProperty(PATH);
|
||||||
|
if (path == null) {
|
||||||
|
path = this.properties.getProperty(PATH);
|
||||||
|
}
|
||||||
|
if (path != null) {
|
||||||
|
path = SystemPropertyUtils.resolvePlaceholders(path, true);
|
||||||
|
this.paths = new ArrayList<String>(Arrays.asList(path.split(",")));
|
||||||
|
for (int i = 0; i < this.paths.size(); i++) {
|
||||||
|
this.paths.set(i, this.paths.get(i).trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
resource.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.logger.info("Not found: " + config);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < this.paths.size(); i++) {
|
||||||
|
if (this.paths.get(i).startsWith("./")) {
|
||||||
|
this.paths.set(i, this.paths.get(i).substring(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Iterator<String> iter = this.paths.iterator(); iter.hasNext();) {
|
||||||
|
String path = iter.next();
|
||||||
|
if (path.equals(".") || path.equals("")) {
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.logger.info("Nested archive paths: " + this.paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean exists(URL url) throws IOException {
|
||||||
|
|
||||||
|
// Try a URL connection content-length header...
|
||||||
|
URLConnection con = url.openConnection();
|
||||||
|
ResourceUtils.useCachesIfNecessary(con);
|
||||||
|
HttpURLConnection httpCon = (con instanceof HttpURLConnection ? (HttpURLConnection) con
|
||||||
|
: null);
|
||||||
|
if (httpCon != null) {
|
||||||
|
httpCon.setRequestMethod("HEAD");
|
||||||
|
int code = httpCon.getResponseCode();
|
||||||
|
if (code == HttpURLConnection.HTTP_OK) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (con.getContentLength() >= 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (httpCon != null) {
|
||||||
|
// no HTTP OK status, and no content-length header: give up
|
||||||
|
httpCon.disconnect();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getMainClass(Archive archive) throws Exception {
|
||||||
|
if (System.getProperty(MAIN) != null) {
|
||||||
|
return SystemPropertyUtils
|
||||||
|
.resolvePlaceholders(System.getProperty(MAIN), true);
|
||||||
|
}
|
||||||
|
if (this.properties.containsKey(MAIN)) {
|
||||||
|
return SystemPropertyUtils.resolvePlaceholders(
|
||||||
|
this.properties.getProperty(MAIN), true);
|
||||||
|
}
|
||||||
|
return super.getMainClass(archive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new PropertiesLauncher().launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* 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.loader.util;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for working with Strings that have placeholder values in them. A
|
||||||
|
* placeholder takes the form {@code $ name} . Using {@code PropertyPlaceholderHelper}
|
||||||
|
* these placeholders can be substituted for user-supplied values.
|
||||||
|
* <p>
|
||||||
|
* Values for substitution can be supplied using a {@link Properties} instance or using a
|
||||||
|
* {@link PlaceholderResolver}.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Rob Harrop
|
||||||
|
* @since 3.0
|
||||||
|
*/
|
||||||
|
public class PropertyPlaceholderHelper {
|
||||||
|
|
||||||
|
private static final Map<String, String> wellKnownSimplePrefixes = new HashMap<String, String>(
|
||||||
|
4);
|
||||||
|
|
||||||
|
static {
|
||||||
|
wellKnownSimplePrefixes.put("}", "{");
|
||||||
|
wellKnownSimplePrefixes.put("]", "[");
|
||||||
|
wellKnownSimplePrefixes.put(")", "(");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String placeholderPrefix;
|
||||||
|
|
||||||
|
private final String placeholderSuffix;
|
||||||
|
|
||||||
|
private final String simplePrefix;
|
||||||
|
|
||||||
|
private final String valueSeparator;
|
||||||
|
|
||||||
|
private final boolean ignoreUnresolvablePlaceholders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code PropertyPlaceholderHelper} that uses the supplied prefix and
|
||||||
|
* suffix. Unresolvable placeholders are ignored.
|
||||||
|
* @param placeholderPrefix the prefix that denotes the start of a placeholder.
|
||||||
|
* @param placeholderSuffix the suffix that denotes the end of a placeholder.
|
||||||
|
*/
|
||||||
|
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix) {
|
||||||
|
this(placeholderPrefix, placeholderSuffix, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code PropertyPlaceholderHelper} that uses the supplied prefix and
|
||||||
|
* suffix.
|
||||||
|
* @param placeholderPrefix the prefix that denotes the start of a placeholder
|
||||||
|
* @param placeholderSuffix the suffix that denotes the end of a placeholder
|
||||||
|
* @param valueSeparator the separating character between the placeholder variable and
|
||||||
|
* the associated default value, if any
|
||||||
|
* @param ignoreUnresolvablePlaceholders indicates whether unresolvable placeholders
|
||||||
|
* should be ignored ({@code true}) or cause an exception ({@code false}).
|
||||||
|
*/
|
||||||
|
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
|
||||||
|
String valueSeparator, boolean ignoreUnresolvablePlaceholders) {
|
||||||
|
|
||||||
|
Assert.notNull(placeholderPrefix, "placeholderPrefix must not be null");
|
||||||
|
Assert.notNull(placeholderSuffix, "placeholderSuffix must not be null");
|
||||||
|
this.placeholderPrefix = placeholderPrefix;
|
||||||
|
this.placeholderSuffix = placeholderSuffix;
|
||||||
|
String simplePrefixForSuffix = wellKnownSimplePrefixes
|
||||||
|
.get(this.placeholderSuffix);
|
||||||
|
if (simplePrefixForSuffix != null
|
||||||
|
&& this.placeholderPrefix.endsWith(simplePrefixForSuffix)) {
|
||||||
|
this.simplePrefix = simplePrefixForSuffix;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.simplePrefix = this.placeholderPrefix;
|
||||||
|
}
|
||||||
|
this.valueSeparator = valueSeparator;
|
||||||
|
this.ignoreUnresolvablePlaceholders = ignoreUnresolvablePlaceholders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces all placeholders of format <code>${name}</code> with the corresponding
|
||||||
|
* property from the supplied {@link Properties}.
|
||||||
|
* @param value the value containing the placeholders to be replaced.
|
||||||
|
* @param properties the {@code Properties} to use for replacement.
|
||||||
|
* @return the supplied value with placeholders replaced inline.
|
||||||
|
*/
|
||||||
|
public String replacePlaceholders(String value, final Properties properties) {
|
||||||
|
Assert.notNull(properties, "Argument 'properties' must not be null.");
|
||||||
|
return replacePlaceholders(value, new PlaceholderResolver() {
|
||||||
|
@Override
|
||||||
|
public String resolvePlaceholder(String placeholderName) {
|
||||||
|
return properties.getProperty(placeholderName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces all placeholders of format {@code $ name} with the value returned from the
|
||||||
|
* supplied {@link PlaceholderResolver}.
|
||||||
|
* @param value the value containing the placeholders to be replaced.
|
||||||
|
* @param placeholderResolver the {@code PlaceholderResolver} to use for replacement.
|
||||||
|
* @return the supplied value with placeholders replaced inline.
|
||||||
|
*/
|
||||||
|
public String replacePlaceholders(String value,
|
||||||
|
PlaceholderResolver placeholderResolver) {
|
||||||
|
Assert.notNull(value, "Argument 'value' must not be null.");
|
||||||
|
return parseStringValue(value, placeholderResolver, new HashSet<String>());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String parseStringValue(String strVal,
|
||||||
|
PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
|
||||||
|
|
||||||
|
StringBuilder buf = new StringBuilder(strVal);
|
||||||
|
|
||||||
|
int startIndex = strVal.indexOf(this.placeholderPrefix);
|
||||||
|
while (startIndex != -1) {
|
||||||
|
int endIndex = findPlaceholderEndIndex(buf, startIndex);
|
||||||
|
if (endIndex != -1) {
|
||||||
|
String placeholder = buf.substring(
|
||||||
|
startIndex + this.placeholderPrefix.length(), endIndex);
|
||||||
|
String originalPlaceholder = placeholder;
|
||||||
|
if (!visitedPlaceholders.add(originalPlaceholder)) {
|
||||||
|
throw new IllegalArgumentException("Circular placeholder reference '"
|
||||||
|
+ originalPlaceholder + "' in property definitions");
|
||||||
|
}
|
||||||
|
// Recursive invocation, parsing placeholders contained in the placeholder
|
||||||
|
// key.
|
||||||
|
placeholder = parseStringValue(placeholder, placeholderResolver,
|
||||||
|
visitedPlaceholders);
|
||||||
|
// Now obtain the value for the fully resolved key...
|
||||||
|
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
|
||||||
|
if (propVal == null && this.valueSeparator != null) {
|
||||||
|
int separatorIndex = placeholder.indexOf(this.valueSeparator);
|
||||||
|
if (separatorIndex != -1) {
|
||||||
|
String actualPlaceholder = placeholder.substring(0,
|
||||||
|
separatorIndex);
|
||||||
|
String defaultValue = placeholder.substring(separatorIndex
|
||||||
|
+ this.valueSeparator.length());
|
||||||
|
propVal = placeholderResolver
|
||||||
|
.resolvePlaceholder(actualPlaceholder);
|
||||||
|
if (propVal == null) {
|
||||||
|
propVal = defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (propVal != null) {
|
||||||
|
// Recursive invocation, parsing placeholders contained in the
|
||||||
|
// previously resolved placeholder value.
|
||||||
|
propVal = parseStringValue(propVal, placeholderResolver,
|
||||||
|
visitedPlaceholders);
|
||||||
|
buf.replace(startIndex, endIndex + this.placeholderSuffix.length(),
|
||||||
|
propVal);
|
||||||
|
startIndex = buf.indexOf(this.placeholderPrefix,
|
||||||
|
startIndex + propVal.length());
|
||||||
|
}
|
||||||
|
else if (this.ignoreUnresolvablePlaceholders) {
|
||||||
|
// Proceed with unprocessed value.
|
||||||
|
startIndex = buf.indexOf(this.placeholderPrefix, endIndex
|
||||||
|
+ this.placeholderSuffix.length());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Could not resolve placeholder '"
|
||||||
|
+ placeholder + "'" + " in string value \"" + strVal + "\"");
|
||||||
|
}
|
||||||
|
visitedPlaceholders.remove(originalPlaceholder);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
startIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
|
||||||
|
int index = startIndex + this.placeholderPrefix.length();
|
||||||
|
int withinNestedPlaceholder = 0;
|
||||||
|
while (index < buf.length()) {
|
||||||
|
if (substringMatch(buf, index, this.placeholderSuffix)) {
|
||||||
|
if (withinNestedPlaceholder > 0) {
|
||||||
|
withinNestedPlaceholder--;
|
||||||
|
index = index + this.placeholderSuffix.length();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (substringMatch(buf, index, this.simplePrefix)) {
|
||||||
|
withinNestedPlaceholder++;
|
||||||
|
index = index + this.simplePrefix.length();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy interface used to resolve replacement values for placeholders contained in
|
||||||
|
* Strings.
|
||||||
|
* @see PropertyPlaceholderHelper
|
||||||
|
*/
|
||||||
|
public static interface PlaceholderResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the supplied placeholder name into the replacement value.
|
||||||
|
* @param placeholderName the name of the placeholder to resolve
|
||||||
|
* @return the replacement value or {@code null} if no replacement is to be made
|
||||||
|
*/
|
||||||
|
String resolvePlaceholder(String placeholderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean substringMatch(CharSequence str, int index,
|
||||||
|
CharSequence substring) {
|
||||||
|
for (int j = 0; j < substring.length(); j++) {
|
||||||
|
int i = index + j;
|
||||||
|
if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Assert {
|
||||||
|
|
||||||
|
public static void notNull(Object target, String message) {
|
||||||
|
if (target == null) {
|
||||||
|
throw new IllegalStateException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* 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.loader.util;
|
||||||
|
|
||||||
|
import org.springframework.boot.loader.util.PropertyPlaceholderHelper.PlaceholderResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for resolving placeholders in texts. Usually applied to file paths.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* A text may contain {@code $ ...}} placeholders, to be resolved as system properties:
|
||||||
|
* e.g. {@code $ user.dir}}. Default values can be supplied using the ":" separator
|
||||||
|
* between key and value.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Rob Harrop
|
||||||
|
* @author Dave Syer
|
||||||
|
* @since 1.2.5
|
||||||
|
* @see #PLACEHOLDER_PREFIX
|
||||||
|
* @see #PLACEHOLDER_SUFFIX
|
||||||
|
* @see System#getProperty(String)
|
||||||
|
*/
|
||||||
|
public abstract class SystemPropertyUtils {
|
||||||
|
|
||||||
|
/** Prefix for system property placeholders: "${" */
|
||||||
|
public static final String PLACEHOLDER_PREFIX = "${";
|
||||||
|
|
||||||
|
/** Suffix for system property placeholders: "}" */
|
||||||
|
public static final String PLACEHOLDER_SUFFIX = "}";
|
||||||
|
|
||||||
|
/** Value separator for system property placeholders: ":" */
|
||||||
|
public static final String VALUE_SEPARATOR = ":";
|
||||||
|
|
||||||
|
private static final PropertyPlaceholderHelper strictHelper = new PropertyPlaceholderHelper(
|
||||||
|
PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, false);
|
||||||
|
|
||||||
|
private static final PropertyPlaceholderHelper nonStrictHelper = new PropertyPlaceholderHelper(
|
||||||
|
PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, VALUE_SEPARATOR, true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve ${...} placeholders in the given text, replacing them with corresponding
|
||||||
|
* system property values.
|
||||||
|
* @param text the String to resolve
|
||||||
|
* @return the resolved String
|
||||||
|
* @see #PLACEHOLDER_PREFIX
|
||||||
|
* @see #PLACEHOLDER_SUFFIX
|
||||||
|
* @throws IllegalArgumentException if there is an unresolvable placeholder
|
||||||
|
*/
|
||||||
|
public static String resolvePlaceholders(String text) {
|
||||||
|
return resolvePlaceholders(text, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve ${...} placeholders in the given text, replacing them with corresponding
|
||||||
|
* system property values. Unresolvable placeholders with no default value are ignored
|
||||||
|
* and passed through unchanged if the flag is set to true.
|
||||||
|
* @param text the String to resolve
|
||||||
|
* @param ignoreUnresolvablePlaceholders flag to determine is unresolved placeholders
|
||||||
|
* are ignored
|
||||||
|
* @return the resolved String
|
||||||
|
* @see #PLACEHOLDER_PREFIX
|
||||||
|
* @see #PLACEHOLDER_SUFFIX
|
||||||
|
* @throws IllegalArgumentException if there is an unresolvable placeholder and the
|
||||||
|
* flag is false
|
||||||
|
*/
|
||||||
|
public static String resolvePlaceholders(String text,
|
||||||
|
boolean ignoreUnresolvablePlaceholders) {
|
||||||
|
PropertyPlaceholderHelper helper = (ignoreUnresolvablePlaceholders ? nonStrictHelper
|
||||||
|
: strictHelper);
|
||||||
|
return helper.replacePlaceholders(text, new SystemPropertyPlaceholderResolver(
|
||||||
|
text));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SystemPropertyPlaceholderResolver implements PlaceholderResolver {
|
||||||
|
|
||||||
|
private final String text;
|
||||||
|
|
||||||
|
public SystemPropertyPlaceholderResolver(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolvePlaceholder(String placeholderName) {
|
||||||
|
try {
|
||||||
|
String propVal = System.getProperty(placeholderName);
|
||||||
|
if (propVal == null) {
|
||||||
|
// Fall back to searching the system environment.
|
||||||
|
propVal = System.getenv(placeholderName);
|
||||||
|
}
|
||||||
|
return propVal;
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
System.err.println("Could not resolve placeholder '" + placeholderName
|
||||||
|
+ "' in [" + this.text + "] as system property: " + ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue