Merge branch '1.5.x'
This commit is contained in:
commit
b527e71474
|
|
@ -34,8 +34,6 @@ import org.springframework.util.FileCopyUtils;
|
|||
*/
|
||||
abstract class AbstractApplicationLauncher extends ExternalResource {
|
||||
|
||||
private final File serverPortFile = new File("target/server.port");
|
||||
|
||||
private final ApplicationBuilder applicationBuilder;
|
||||
|
||||
private Process process;
|
||||
|
|
@ -62,8 +60,15 @@ abstract class AbstractApplicationLauncher extends ExternalResource {
|
|||
|
||||
protected abstract List<String> getArguments(File archive);
|
||||
|
||||
protected abstract File getWorkingDirectory();
|
||||
|
||||
protected abstract String getDescription(String packaging);
|
||||
|
||||
private Process startApplication() throws Exception {
|
||||
this.serverPortFile.delete();
|
||||
File workingDirectory = getWorkingDirectory();
|
||||
File serverPortFile = workingDirectory == null ? new File("target/server.port")
|
||||
: new File(workingDirectory, "target/server.port");
|
||||
serverPortFile.delete();
|
||||
File archive = this.applicationBuilder.buildApplication();
|
||||
List<String> arguments = new ArrayList<>();
|
||||
arguments.add(System.getProperty("java.home") + "/bin/java");
|
||||
|
|
@ -72,14 +77,17 @@ abstract class AbstractApplicationLauncher extends ExternalResource {
|
|||
arguments.toArray(new String[arguments.size()]));
|
||||
processBuilder.redirectOutput(Redirect.INHERIT);
|
||||
processBuilder.redirectError(Redirect.INHERIT);
|
||||
if (workingDirectory != null) {
|
||||
processBuilder.directory(workingDirectory);
|
||||
}
|
||||
Process process = processBuilder.start();
|
||||
this.httpPort = awaitServerPort(process);
|
||||
this.httpPort = awaitServerPort(process, serverPortFile);
|
||||
return process;
|
||||
}
|
||||
|
||||
private int awaitServerPort(Process process) throws Exception {
|
||||
private int awaitServerPort(Process process, File serverPortFile) throws Exception {
|
||||
long end = System.currentTimeMillis() + 30000;
|
||||
while (this.serverPortFile.length() == 0) {
|
||||
while (serverPortFile.length() == 0) {
|
||||
if (System.currentTimeMillis() > end) {
|
||||
throw new IllegalStateException(
|
||||
"server.port file was not written within 30 seconds");
|
||||
|
|
@ -89,8 +97,8 @@ abstract class AbstractApplicationLauncher extends ExternalResource {
|
|||
}
|
||||
Thread.sleep(100);
|
||||
}
|
||||
return Integer.parseInt(
|
||||
FileCopyUtils.copyToString(new FileReader(this.serverPortFile)));
|
||||
return Integer
|
||||
.parseInt(FileCopyUtils.copyToString(new FileReader(serverPortFile)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,25 +47,33 @@ public abstract class AbstractEmbeddedServletContainerIntegrationTests {
|
|||
|
||||
protected final RestTemplate rest = new RestTemplate();
|
||||
|
||||
public static Object[] parameters(String packaging) {
|
||||
public static Object[] parameters(String packaging,
|
||||
List<Class<? extends AbstractApplicationLauncher>> applicationLaunchers) {
|
||||
List<Object> parameters = new ArrayList<>();
|
||||
parameters.addAll(createParameters(packaging, "jetty"));
|
||||
parameters.addAll(createParameters(packaging, "tomcat"));
|
||||
parameters.addAll(createParameters(packaging, "undertow"));
|
||||
parameters.addAll(createParameters(packaging, "jetty", applicationLaunchers));
|
||||
parameters.addAll(createParameters(packaging, "tomcat", applicationLaunchers));
|
||||
parameters.addAll(createParameters(packaging, "undertow", applicationLaunchers));
|
||||
return parameters.toArray(new Object[parameters.size()]);
|
||||
}
|
||||
|
||||
private static List<Object> createParameters(String packaging, String container,
|
||||
String... versions) {
|
||||
List<Object> parameters = new ArrayList<>();
|
||||
List<Class<? extends AbstractApplicationLauncher>> applicationLaunchers) {
|
||||
List<Object> parameters = new ArrayList<Object>();
|
||||
ApplicationBuilder applicationBuilder = new ApplicationBuilder(temporaryFolder,
|
||||
packaging, container);
|
||||
parameters.add(new Object[] {
|
||||
StringUtils.capitalise(container) + " packaged " + packaging,
|
||||
new PackagedApplicationLauncher(applicationBuilder) });
|
||||
parameters.add(new Object[] {
|
||||
StringUtils.capitalise(container) + " exploded " + packaging,
|
||||
new ExplodedApplicationLauncher(applicationBuilder) });
|
||||
for (Class<? extends AbstractApplicationLauncher> launcherClass : applicationLaunchers) {
|
||||
try {
|
||||
AbstractApplicationLauncher launcher = launcherClass
|
||||
.getDeclaredConstructor(ApplicationBuilder.class)
|
||||
.newInstance(applicationBuilder);
|
||||
String name = StringUtils.capitalise(container) + ": "
|
||||
+ launcher.getDescription(packaging);
|
||||
parameters.add(new Object[] { name, launcher });
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* 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.context.embedded;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link AbstractApplicationLauncher} that launches a Spring Boot application with a
|
||||
* classpath similar to that used when run with Maven or Gradle.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class BootRunApplicationLauncher extends AbstractApplicationLauncher {
|
||||
|
||||
private final File exploded = new File("target/run");
|
||||
|
||||
BootRunApplicationLauncher(ApplicationBuilder applicationBuilder) {
|
||||
super(applicationBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getArguments(File archive) {
|
||||
try {
|
||||
explodeArchive(archive);
|
||||
deleteLauncherClasses();
|
||||
File targetClasses = populateTargetClasses(archive);
|
||||
File dependencies = populateDependencies(archive);
|
||||
populateSrcMainWebapp();
|
||||
List<String> classpath = new ArrayList<String>();
|
||||
classpath.add(targetClasses.getAbsolutePath());
|
||||
for (File dependency : dependencies.listFiles()) {
|
||||
classpath.add(dependency.getAbsolutePath());
|
||||
}
|
||||
return Arrays.asList("-cp",
|
||||
StringUtils.collectionToDelimitedString(classpath,
|
||||
File.pathSeparator),
|
||||
"com.example.ResourceHandlingApplication");
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteLauncherClasses() {
|
||||
FileSystemUtils.deleteRecursively(new File(this.exploded, "org"));
|
||||
}
|
||||
|
||||
private File populateTargetClasses(File archive) {
|
||||
File targetClasses = new File(this.exploded, "target/classes");
|
||||
targetClasses.mkdirs();
|
||||
new File(this.exploded, getClassesPath(archive)).renameTo(targetClasses);
|
||||
return targetClasses;
|
||||
}
|
||||
|
||||
private File populateDependencies(File archive) {
|
||||
File dependencies = new File(this.exploded, "dependencies");
|
||||
dependencies.mkdirs();
|
||||
List<String> libPaths = getLibPaths(archive);
|
||||
for (String libPath : libPaths) {
|
||||
for (File jar : new File(this.exploded, libPath).listFiles()) {
|
||||
jar.renameTo(new File(dependencies, jar.getName()));
|
||||
}
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
private void populateSrcMainWebapp() {
|
||||
File srcMainWebapp = new File(this.exploded, "src/main/webapp");
|
||||
srcMainWebapp.mkdirs();
|
||||
new File(this.exploded, "webapp-resource.txt")
|
||||
.renameTo(new File(srcMainWebapp, "webapp-resource.txt"));
|
||||
}
|
||||
|
||||
private String getClassesPath(File archive) {
|
||||
return archive.getName().endsWith(".jar") ? "BOOT-INF/classes"
|
||||
: "WEB-INF/classes";
|
||||
}
|
||||
|
||||
private List<String> getLibPaths(File archive) {
|
||||
return archive.getName().endsWith(".jar")
|
||||
? Collections.singletonList("BOOT-INF/lib")
|
||||
: Arrays.asList("WEB-INF/lib", "WEB-INF/lib-provided");
|
||||
}
|
||||
|
||||
private void explodeArchive(File archive) throws IOException {
|
||||
FileSystemUtils.deleteRecursively(this.exploded);
|
||||
JarFile jarFile = new JarFile(archive);
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry jarEntry = entries.nextElement();
|
||||
File extracted = new File(this.exploded, jarEntry.getName());
|
||||
if (jarEntry.isDirectory()) {
|
||||
extracted.mkdirs();
|
||||
}
|
||||
else {
|
||||
FileOutputStream extractedOutputStream = new FileOutputStream(extracted);
|
||||
StreamUtils.copy(jarFile.getInputStream(jarEntry), extractedOutputStream);
|
||||
extractedOutputStream.close();
|
||||
}
|
||||
}
|
||||
jarFile.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getWorkingDirectory() {
|
||||
return this.exploded;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDescription(String packaging) {
|
||||
return "build system run " + packaging + " project";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.context.embedded;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for Spring Boot's embedded servlet container support when developing
|
||||
* a jar application.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class EmbeddedServletContainerJarDevelopmentIntegrationTests
|
||||
extends AbstractEmbeddedServletContainerIntegrationTests {
|
||||
|
||||
@Parameters(name = "{0}")
|
||||
public static Object[] parameters() {
|
||||
return AbstractEmbeddedServletContainerIntegrationTests.parameters("jar", Arrays
|
||||
.asList(BootRunApplicationLauncher.class, IdeApplicationLauncher.class));
|
||||
}
|
||||
|
||||
public EmbeddedServletContainerJarDevelopmentIntegrationTests(String name,
|
||||
AbstractApplicationLauncher launcher) {
|
||||
super(name, launcher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaInfResourceFromDependencyIsAvailableViaHttp() throws Exception {
|
||||
ResponseEntity<String> entity = this.rest
|
||||
.getForEntity("/nested-meta-inf-resource.txt", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaInfResourceFromDependencyIsAvailableViaServletContext()
|
||||
throws Exception {
|
||||
ResponseEntity<String> entity = this.rest
|
||||
.getForEntity("/nested-meta-inf-resource.txt", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.context.embedded;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
|
@ -38,7 +40,9 @@ public class EmbeddedServletContainerJarPackagingIntegrationTests
|
|||
|
||||
@Parameters(name = "{0}")
|
||||
public static Object[] parameters() {
|
||||
return AbstractEmbeddedServletContainerIntegrationTests.parameters("jar");
|
||||
return AbstractEmbeddedServletContainerIntegrationTests.parameters("jar",
|
||||
Arrays.asList(PackagedApplicationLauncher.class,
|
||||
ExplodedApplicationLauncher.class));
|
||||
}
|
||||
|
||||
public EmbeddedServletContainerJarPackagingIntegrationTests(String name,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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.context.embedded;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for Spring Boot's embedded servlet container support when developing
|
||||
* a war application.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class EmbeddedServletContainerWarDevelopmentIntegrationTests
|
||||
extends AbstractEmbeddedServletContainerIntegrationTests {
|
||||
|
||||
@Parameters(name = "{0}")
|
||||
public static Object[] parameters() {
|
||||
return AbstractEmbeddedServletContainerIntegrationTests.parameters("war", Arrays
|
||||
.asList(BootRunApplicationLauncher.class, IdeApplicationLauncher.class));
|
||||
}
|
||||
|
||||
public EmbeddedServletContainerWarDevelopmentIntegrationTests(String name,
|
||||
AbstractApplicationLauncher launcher) {
|
||||
super(name, launcher);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaInfResourceFromDependencyIsAvailableViaHttp() throws Exception {
|
||||
ResponseEntity<String> entity = this.rest
|
||||
.getForEntity("/nested-meta-inf-resource.txt", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void metaInfResourceFromDependencyIsAvailableViaServletContext()
|
||||
throws Exception {
|
||||
ResponseEntity<String> entity = this.rest
|
||||
.getForEntity("/nested-meta-inf-resource.txt", String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void webappResourcesAreAvailableViaHttp() throws Exception {
|
||||
ResponseEntity<String> entity = this.rest.getForEntity("/webapp-resource.txt",
|
||||
String.class);
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.context.embedded;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
|
@ -38,7 +40,9 @@ public class EmbeddedServletContainerWarPackagingIntegrationTests
|
|||
|
||||
@Parameters(name = "{0}")
|
||||
public static Object[] parameters() {
|
||||
return AbstractEmbeddedServletContainerIntegrationTests.parameters("war");
|
||||
return AbstractEmbeddedServletContainerIntegrationTests.parameters("war",
|
||||
Arrays.asList(PackagedApplicationLauncher.class,
|
||||
ExplodedApplicationLauncher.class));
|
||||
}
|
||||
|
||||
public EmbeddedServletContainerWarPackagingIntegrationTests(String name,
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ import org.springframework.util.FileSystemUtils;
|
|||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
* {@link AbstractApplicationLauncher} that launches an exploded Spring Boot application
|
||||
* using Spring Boot's Jar or War launcher.
|
||||
* {@link AbstractApplicationLauncher} that launches a Spring Boot application using
|
||||
* {@code JarLauncher} or {@code WarLauncher} and an exploded archive.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
|
|
@ -42,6 +42,16 @@ class ExplodedApplicationLauncher extends AbstractApplicationLauncher {
|
|||
super(applicationBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getWorkingDirectory() {
|
||||
return this.exploded;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDescription(String packaging) {
|
||||
return "exploded " + packaging;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getArguments(File archive) {
|
||||
String mainClass = archive.getName().endsWith(".war")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* 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.context.embedded;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link AbstractApplicationLauncher} that launches a Spring Boot application with a
|
||||
* classpath similar to that used when run in an IDE.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class IdeApplicationLauncher extends AbstractApplicationLauncher {
|
||||
|
||||
private final File exploded = new File("target/ide");
|
||||
|
||||
IdeApplicationLauncher(ApplicationBuilder applicationBuilder) {
|
||||
super(applicationBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getWorkingDirectory() {
|
||||
return this.exploded;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDescription(String packaging) {
|
||||
return "IDE run " + packaging + " project";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getArguments(File archive) {
|
||||
try {
|
||||
explodeArchive(archive, this.exploded);
|
||||
deleteLauncherClasses();
|
||||
File targetClasses = populateTargetClasses(archive);
|
||||
File dependencies = populateDependencies(archive);
|
||||
File resourcesProject = explodedResourcesProject(dependencies);
|
||||
populateSrcMainWebapp();
|
||||
List<String> classpath = new ArrayList<String>();
|
||||
classpath.add(targetClasses.getAbsolutePath());
|
||||
for (File dependency : dependencies.listFiles()) {
|
||||
classpath.add(dependency.getAbsolutePath());
|
||||
}
|
||||
classpath.add(resourcesProject.getAbsolutePath());
|
||||
return Arrays.asList("-cp",
|
||||
StringUtils.collectionToDelimitedString(classpath,
|
||||
File.pathSeparator),
|
||||
"com.example.ResourceHandlingApplication");
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private File populateTargetClasses(File archive) {
|
||||
File targetClasses = new File(this.exploded, "target/classes");
|
||||
targetClasses.mkdirs();
|
||||
new File(this.exploded, getClassesPath(archive)).renameTo(targetClasses);
|
||||
return targetClasses;
|
||||
}
|
||||
|
||||
private File populateDependencies(File archive) {
|
||||
File dependencies = new File(this.exploded, "dependencies");
|
||||
dependencies.mkdirs();
|
||||
List<String> libPaths = getLibPaths(archive);
|
||||
for (String libPath : libPaths) {
|
||||
for (File jar : new File(this.exploded, libPath).listFiles()) {
|
||||
jar.renameTo(new File(dependencies, jar.getName()));
|
||||
}
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
private File explodedResourcesProject(File dependencies) throws IOException {
|
||||
File resourcesProject = new File(this.exploded,
|
||||
"resources-project/target/classes");
|
||||
File resourcesJar = new File(dependencies, "resources-1.0.jar");
|
||||
explodeArchive(resourcesJar, resourcesProject);
|
||||
resourcesJar.delete();
|
||||
return resourcesProject;
|
||||
}
|
||||
|
||||
private void populateSrcMainWebapp() {
|
||||
File srcMainWebapp = new File(this.exploded, "src/main/webapp");
|
||||
srcMainWebapp.mkdirs();
|
||||
new File(this.exploded, "webapp-resource.txt")
|
||||
.renameTo(new File(srcMainWebapp, "webapp-resource.txt"));
|
||||
}
|
||||
|
||||
private void deleteLauncherClasses() {
|
||||
FileSystemUtils.deleteRecursively(new File(this.exploded, "org"));
|
||||
}
|
||||
|
||||
private String getClassesPath(File archive) {
|
||||
return archive.getName().endsWith(".jar") ? "BOOT-INF/classes"
|
||||
: "WEB-INF/classes";
|
||||
}
|
||||
|
||||
private List<String> getLibPaths(File archive) {
|
||||
return archive.getName().endsWith(".jar")
|
||||
? Collections.singletonList("BOOT-INF/lib")
|
||||
: Arrays.asList("WEB-INF/lib", "WEB-INF/lib-provided");
|
||||
}
|
||||
|
||||
private void explodeArchive(File archive, File destination) throws IOException {
|
||||
FileSystemUtils.deleteRecursively(destination);
|
||||
JarFile jarFile = new JarFile(archive);
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry jarEntry = entries.nextElement();
|
||||
File extracted = new File(destination, jarEntry.getName());
|
||||
if (jarEntry.isDirectory()) {
|
||||
extracted.mkdirs();
|
||||
}
|
||||
else {
|
||||
FileOutputStream extractedOutputStream = new FileOutputStream(extracted);
|
||||
StreamUtils.copy(jarFile.getInputStream(jarEntry), extractedOutputStream);
|
||||
extractedOutputStream.close();
|
||||
}
|
||||
}
|
||||
jarFile.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -32,6 +32,16 @@ class PackagedApplicationLauncher extends AbstractApplicationLauncher {
|
|||
super(applicationBuilder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getWorkingDirectory() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDescription(String packaging) {
|
||||
return "packaged " + packaging;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getArguments(File archive) {
|
||||
return Arrays.asList("-jar", archive.getAbsolutePath());
|
||||
|
|
|
|||
|
|
@ -96,15 +96,23 @@ public abstract class AbstractEmbeddedServletContainerFactory
|
|||
if (classLoader instanceof URLClassLoader) {
|
||||
for (URL url : ((URLClassLoader) classLoader).getURLs()) {
|
||||
try {
|
||||
URLConnection connection = url.openConnection();
|
||||
if (connection instanceof JarURLConnection) {
|
||||
JarURLConnection jarConnection = (JarURLConnection) connection;
|
||||
JarFile jar = jarConnection.getJarFile();
|
||||
if (jar.getName().endsWith(".jar")
|
||||
&& jar.getJarEntry("META-INF/resources") != null) {
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
File file = new File(url.getFile());
|
||||
if (file.isDirectory()
|
||||
&& new File(file, "META-INF/resources").isDirectory()) {
|
||||
staticResourceUrls.add(url);
|
||||
}
|
||||
jar.close();
|
||||
else if (isResourcesJar(file)) {
|
||||
staticResourceUrls.add(url);
|
||||
}
|
||||
}
|
||||
else {
|
||||
URLConnection connection = url.openConnection();
|
||||
if (connection instanceof JarURLConnection) {
|
||||
if (isResourcesJar((JarURLConnection) connection)) {
|
||||
staticResourceUrls.add(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
|
|
@ -115,6 +123,34 @@ public abstract class AbstractEmbeddedServletContainerFactory
|
|||
return staticResourceUrls;
|
||||
}
|
||||
|
||||
private boolean isResourcesJar(JarURLConnection connection) {
|
||||
try {
|
||||
return isResourcesJar(connection.getJarFile());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isResourcesJar(File file) {
|
||||
try {
|
||||
return isResourcesJar(new JarFile(file));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isResourcesJar(JarFile jar) throws IOException {
|
||||
try {
|
||||
return jar.getName().endsWith(".jar")
|
||||
&& (jar.getJarEntry("META-INF/resources") != null);
|
||||
}
|
||||
finally {
|
||||
jar.close();
|
||||
}
|
||||
}
|
||||
|
||||
File getExplodedWarFileDocumentRoot(File codeSourceFile) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Code archive: " + codeSourceFile);
|
||||
|
|
|
|||
|
|
@ -392,8 +392,7 @@ public class JettyEmbeddedServletContainerFactory
|
|||
root.isDirectory() ? Resource.newResource(root.getCanonicalFile())
|
||||
: JarResource.newJarResource(Resource.newResource(root)));
|
||||
for (URL resourceJarUrl : this.getUrlsOfJarsWithMetaInfResources()) {
|
||||
Resource resource = Resource
|
||||
.newResource(resourceJarUrl + "META-INF/resources");
|
||||
Resource resource = createResource(resourceJarUrl);
|
||||
// Jetty 9.2 and earlier do not support nested jars. See
|
||||
// https://github.com/eclipse/jetty.project/issues/518
|
||||
if (resource.exists() && resource.isDirectory()) {
|
||||
|
|
@ -408,6 +407,16 @@ public class JettyEmbeddedServletContainerFactory
|
|||
}
|
||||
}
|
||||
|
||||
private Resource createResource(URL url) throws IOException {
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
File file = new File(url.getFile());
|
||||
if (file.isFile()) {
|
||||
return Resource.newResource("jar:" + url + "!/META-INF/resources");
|
||||
}
|
||||
}
|
||||
return Resource.newResource(url + "META-INF/resources");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Jetty's {@code DefaultServlet} to the given {@link WebAppContext}.
|
||||
* @param context the jetty {@link WebAppContext}
|
||||
|
|
|
|||
|
|
@ -840,20 +840,23 @@ public class TomcatEmbeddedServletContainerFactory
|
|||
// A jar file in the file system. Convert to Jar URL.
|
||||
jar = "jar:" + jar + "!/";
|
||||
}
|
||||
addJar(jar);
|
||||
addResourceSet(jar);
|
||||
}
|
||||
else {
|
||||
addResourceSet(url.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addJar(String jar) {
|
||||
private void addResourceSet(String resource) {
|
||||
try {
|
||||
if (isInsideNestedJar(jar)) {
|
||||
if (isInsideNestedJar(resource)) {
|
||||
// It's a nested jar but we now don't want the suffix because Tomcat
|
||||
// is going to try and locate it as a root URL (not the resource
|
||||
// inside it)
|
||||
jar = jar.substring(0, jar.length() - 2);
|
||||
resource = resource.substring(0, resource.length() - 2);
|
||||
}
|
||||
URL url = new URL(jar);
|
||||
URL url = new URL(resource);
|
||||
String path = "/META-INF/resources";
|
||||
this.context.getResources().createWebResourceSet(
|
||||
ResourceSetType.RESOURCE_JAR, "/", url, path);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* 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.context.embedded.tomcat;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import javax.naming.directory.DirContext;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.WebResourceRoot.ResourceSetType;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Abstraction to add resources that works with both Tomcat 8 and 7.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
abstract class TomcatResources {
|
||||
|
||||
private final Context context;
|
||||
|
||||
TomcatResources(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
void addResourceJars(List<URL> resourceJarUrls) {
|
||||
for (URL url : resourceJarUrls) {
|
||||
String file = url.getFile();
|
||||
if (file.endsWith(".jar") || file.endsWith(".jar!/")) {
|
||||
String jar = url.toString();
|
||||
if (!jar.startsWith("jar:")) {
|
||||
// A jar file in the file system. Convert to Jar URL.
|
||||
jar = "jar:" + jar + "!/";
|
||||
}
|
||||
addJar(jar);
|
||||
}
|
||||
else {
|
||||
addDir(file, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final Context getContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to add a JAR to the resources.
|
||||
* @param jar the URL spec for the jar
|
||||
*/
|
||||
protected abstract void addJar(String jar);
|
||||
|
||||
/**
|
||||
* Called to add a dir to the resource.
|
||||
* @param dir the dir
|
||||
* @param url the URL
|
||||
*/
|
||||
protected abstract void addDir(String dir, URL url);
|
||||
|
||||
/**
|
||||
* Return a {@link TomcatResources} instance for the currently running Tomcat version.
|
||||
* @param context the tomcat context
|
||||
* @return a {@link TomcatResources} instance.
|
||||
*/
|
||||
public static TomcatResources get(Context context) {
|
||||
if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage", null)) {
|
||||
return new Tomcat7Resources(context);
|
||||
}
|
||||
return new Tomcat8Resources(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TomcatResources} for Tomcat 7.
|
||||
*/
|
||||
private static class Tomcat7Resources extends TomcatResources {
|
||||
|
||||
private final Method addResourceJarUrlMethod;
|
||||
|
||||
Tomcat7Resources(Context context) {
|
||||
super(context);
|
||||
this.addResourceJarUrlMethod = ReflectionUtils.findMethod(context.getClass(),
|
||||
"addResourceJarUrl", URL.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addJar(String jar) {
|
||||
URL url = getJarUrl(jar);
|
||||
if (url != null) {
|
||||
try {
|
||||
this.addResourceJarUrlMethod.invoke(getContext(), url);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private URL getJarUrl(String jar) {
|
||||
try {
|
||||
return new URL(jar);
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
// Ignore
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDir(String dir, URL url) {
|
||||
if (getContext() instanceof StandardContext) {
|
||||
try {
|
||||
Class<?> fileDirContextClass = Class
|
||||
.forName("org.apache.naming.resources.FileDirContext");
|
||||
Method setDocBaseMethod = ReflectionUtils
|
||||
.findMethod(fileDirContextClass, "setDocBase", String.class);
|
||||
Object fileDirContext = fileDirContextClass.newInstance();
|
||||
setDocBaseMethod.invoke(fileDirContext, dir);
|
||||
Method addResourcesDirContextMethod = ReflectionUtils.findMethod(
|
||||
StandardContext.class, "addResourcesDirContext",
|
||||
DirContext.class);
|
||||
addResourcesDirContextMethod.invoke(getContext(), fileDirContext);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Tomcat 7 reflection failed", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TomcatResources} for Tomcat 8.
|
||||
*/
|
||||
static class Tomcat8Resources extends TomcatResources {
|
||||
|
||||
Tomcat8Resources(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addJar(String jar) {
|
||||
addResourceSet(jar);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDir(String dir, URL url) {
|
||||
addResourceSet(url.toString());
|
||||
}
|
||||
|
||||
private void addResourceSet(String resource) {
|
||||
try {
|
||||
if (isInsideNestedJar(resource)) {
|
||||
// It's a nested jar but we now don't want the suffix because Tomcat
|
||||
// is going to try and locate it as a root URL (not the resource
|
||||
// inside it)
|
||||
resource = resource.substring(0, resource.length() - 2);
|
||||
}
|
||||
URL url = new URL(resource);
|
||||
String path = "/META-INF/resources";
|
||||
getContext().getResources().createWebResourceSet(
|
||||
ResourceSetType.RESOURCE_JAR, "/", url, path);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Ignore (probably not a directory)
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInsideNestedJar(String dir) {
|
||||
return dir.indexOf("!/") < dir.lastIndexOf("!/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ package org.springframework.boot.context.embedded.undertow;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.Charset;
|
||||
|
|
@ -464,11 +465,35 @@ public class UndertowEmbeddedServletContainerFactory
|
|||
|
||||
private ResourceManager getDocumentRootResourceManager() {
|
||||
File root = getCanonicalDocumentRoot();
|
||||
List<URL> metaInfResourceJarUrls = getUrlsOfJarsWithMetaInfResources();
|
||||
List<URL> metaInfResourceUrls = getUrlsOfJarsWithMetaInfResources();
|
||||
List<URL> resourceJarUrls = new ArrayList<URL>();
|
||||
List<ResourceManager> resourceManagers = new ArrayList<ResourceManager>();
|
||||
ResourceManager rootResourceManager = root.isDirectory()
|
||||
? new FileResourceManager(root, 0) : new JarResourceManager(root);
|
||||
return new CompositeResourceManager(rootResourceManager,
|
||||
new MetaInfResourcesResourceManager(metaInfResourceJarUrls));
|
||||
resourceManagers.add(rootResourceManager);
|
||||
for (URL url : metaInfResourceUrls) {
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
File file = new File(url.getFile());
|
||||
if (file.isFile()) {
|
||||
try {
|
||||
resourceJarUrls.add(new URL("jar:" + url + "!/"));
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
resourceManagers.add(new FileResourceManager(
|
||||
new File(file, "META-INF/resources"), 0));
|
||||
}
|
||||
}
|
||||
else {
|
||||
resourceJarUrls.add(url);
|
||||
}
|
||||
}
|
||||
resourceManagers.add(new MetaInfResourcesResourceManager(resourceJarUrls));
|
||||
return new CompositeResourceManager(
|
||||
resourceManagers.toArray(new ResourceManager[resourceManagers.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -618,12 +643,17 @@ public class UndertowEmbeddedServletContainerFactory
|
|||
}
|
||||
|
||||
@Override
|
||||
public Resource getResource(String path) throws IOException {
|
||||
public Resource getResource(String path) {
|
||||
for (URL url : this.metaInfResourceJarUrls) {
|
||||
URL resourceUrl = new URL(url + "META-INF/resources" + path);
|
||||
URLConnection connection = resourceUrl.openConnection();
|
||||
if (connection.getContentLength() >= 0) {
|
||||
return new URLResource(resourceUrl, connection, path);
|
||||
try {
|
||||
URL resourceUrl = new URL(url + "META-INF/resources" + path);
|
||||
URLConnection connection = resourceUrl.openConnection();
|
||||
if (connection.getContentLength() >= 0) {
|
||||
return new URLResource(resourceUrl, connection, path);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue