Minimize and centralize assumptions about build output
Closes gh-15471
This commit is contained in:
@@ -25,7 +25,7 @@ public class DevToolsTestApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
new SpringApplicationBuilder(DevToolsTestApplication.class)
|
||||
.listeners(new WebServerPortFileWriter("target/server.port")).run(args);
|
||||
.listeners(new WebServerPortFileWriter(args[0])).run(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.devtools.tests;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
|
||||
/**
|
||||
* Base class for all {@link ApplicationLauncher} implementations.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
abstract class AbstractApplicationLauncher implements ApplicationLauncher {
|
||||
|
||||
private final Directories directories;
|
||||
|
||||
AbstractApplicationLauncher(Directories directories) {
|
||||
this.directories = directories;
|
||||
}
|
||||
|
||||
protected final void copyApplicationTo(File location) throws IOException {
|
||||
FileSystemUtils.deleteRecursively(location);
|
||||
location.mkdirs();
|
||||
FileSystemUtils.copyRecursively(
|
||||
new File(this.directories.getTestClassesDirectory(), "com"),
|
||||
new File(location, "com"));
|
||||
}
|
||||
|
||||
protected final List<String> getDependencyJarPaths() {
|
||||
return Stream.of(this.directories.getDependenciesDirectory().listFiles())
|
||||
.map(File::getAbsolutePath).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected final Directories getDirectories() {
|
||||
return this.directories;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.devtools.tests;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Launches an application with DevTools.
|
||||
*
|
||||
@@ -23,6 +25,7 @@ package org.springframework.boot.devtools.tests;
|
||||
*/
|
||||
public interface ApplicationLauncher {
|
||||
|
||||
LaunchedApplication launchApplication(JvmLauncher javaLauncher) throws Exception;
|
||||
LaunchedApplication launchApplication(JvmLauncher javaLauncher, File serverPortFile)
|
||||
throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.boot.devtools.tests;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -28,13 +30,16 @@ import net.bytebuddy.dynamic.DynamicType.Builder;
|
||||
import net.bytebuddy.implementation.FixedValue;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||
import org.springframework.boot.testsupport.BuildOutput;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -50,9 +55,15 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
@RunWith(Parameterized.class)
|
||||
public class DevToolsIntegrationTests {
|
||||
|
||||
@ClassRule
|
||||
public static final TemporaryFolder temp = new TemporaryFolder();
|
||||
|
||||
private static final BuildOutput buildOutput = new BuildOutput(
|
||||
DevToolsIntegrationTests.class);
|
||||
|
||||
private LaunchedApplication launchedApplication;
|
||||
|
||||
private final File serverPortFile = new File("target/server.port");
|
||||
private final File serverPortFile;
|
||||
|
||||
private final ApplicationLauncher applicationLauncher;
|
||||
|
||||
@@ -61,14 +72,15 @@ public class DevToolsIntegrationTests {
|
||||
|
||||
public DevToolsIntegrationTests(ApplicationLauncher applicationLauncher) {
|
||||
this.applicationLauncher = applicationLauncher;
|
||||
this.serverPortFile = new File(
|
||||
DevToolsIntegrationTests.buildOutput.getRootLocation(), "server.port");
|
||||
}
|
||||
|
||||
@Before
|
||||
public void launchApplication() throws Exception {
|
||||
this.serverPortFile.delete();
|
||||
System.out.println("Launching " + this.javaLauncher.getClass());
|
||||
this.launchedApplication = this.applicationLauncher
|
||||
.launchApplication(this.javaLauncher);
|
||||
.launchApplication(this.javaLauncher, this.serverPortFile);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -205,13 +217,15 @@ public class DevToolsIntegrationTests {
|
||||
}
|
||||
|
||||
private int awaitServerPort() throws Exception {
|
||||
long end = System.currentTimeMillis() + 40000;
|
||||
Duration timeToWait = Duration.ofSeconds(40);
|
||||
long end = System.currentTimeMillis() + timeToWait.toMillis();
|
||||
System.out.println("Reading server port from '" + this.serverPortFile + "'");
|
||||
while (this.serverPortFile.length() == 0) {
|
||||
System.out.println("Getting server port " + this.serverPortFile.length());
|
||||
if (System.currentTimeMillis() > end) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"server.port file was not written within 30 seconds. "
|
||||
+ "Application output:%n%s%s",
|
||||
"server.port file '" + this.serverPortFile
|
||||
+ "' was not written within " + timeToWait.toMillis()
|
||||
+ "ms. " + "Application output:%n%s%s",
|
||||
FileCopyUtils.copyToString(new FileReader(
|
||||
this.launchedApplication.getStandardOut())),
|
||||
FileCopyUtils.copyToString(new FileReader(
|
||||
@@ -234,10 +248,11 @@ public class DevToolsIntegrationTests {
|
||||
}
|
||||
|
||||
@Parameters(name = "{0}")
|
||||
public static Object[] parameters() {
|
||||
return new Object[] { new Object[] { new LocalApplicationLauncher() },
|
||||
new Object[] { new ExplodedRemoteApplicationLauncher() },
|
||||
new Object[] { new JarFileRemoteApplicationLauncher() } };
|
||||
public static Object[] parameters() throws IOException {
|
||||
Directories directories = new Directories(buildOutput, temp);
|
||||
return new Object[] { new Object[] { new LocalApplicationLauncher(directories) },
|
||||
new Object[] { new ExplodedRemoteApplicationLauncher(directories) },
|
||||
new Object[] { new JarFileRemoteApplicationLauncher(directories) } };
|
||||
}
|
||||
|
||||
private static final class ControllerBuilder {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.devtools.tests;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import org.springframework.boot.testsupport.BuildOutput;
|
||||
|
||||
/**
|
||||
* Various directories used by the {@link ApplicationLauncher ApplicationLaunchers}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class Directories {
|
||||
|
||||
private final BuildOutput buildOutput;
|
||||
|
||||
private final TemporaryFolder temp;
|
||||
|
||||
Directories(BuildOutput buildOutput, TemporaryFolder temp) {
|
||||
this.buildOutput = buildOutput;
|
||||
this.temp = temp;
|
||||
}
|
||||
|
||||
File getTestClassesDirectory() {
|
||||
return this.buildOutput.getTestClassesLocation();
|
||||
}
|
||||
|
||||
File getRemoteAppDirectory() {
|
||||
return new File(this.temp.getRoot(), "remote");
|
||||
}
|
||||
|
||||
File getDependenciesDirectory() {
|
||||
return new File(this.buildOutput.getRootLocation(), "dependencies");
|
||||
}
|
||||
|
||||
File getAppDirectory() {
|
||||
return new File(this.temp.getRoot(), "app");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
@@ -20,7 +20,6 @@ import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -31,18 +30,17 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class ExplodedRemoteApplicationLauncher extends RemoteApplicationLauncher {
|
||||
|
||||
public ExplodedRemoteApplicationLauncher(Directories directories) {
|
||||
super(directories);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String createApplicationClassPath() throws Exception {
|
||||
File appDirectory = new File("target/app");
|
||||
FileSystemUtils.deleteRecursively(appDirectory);
|
||||
appDirectory.mkdirs();
|
||||
FileSystemUtils.copyRecursively(new File("target/test-classes/com"),
|
||||
new File("target/app/com"));
|
||||
File appDirectory = getDirectories().getAppDirectory();
|
||||
copyApplicationTo(appDirectory);
|
||||
List<String> entries = new ArrayList<>();
|
||||
entries.add("target/app");
|
||||
for (File jar : new File("target/dependencies").listFiles()) {
|
||||
entries.add(jar.getAbsolutePath());
|
||||
}
|
||||
entries.add(appDirectory.getAbsolutePath());
|
||||
entries.addAll(getDependencyJarPaths());
|
||||
return StringUtils.collectionToDelimitedString(entries, File.pathSeparator);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2018 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.
|
||||
@@ -27,7 +27,6 @@ import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -39,28 +38,24 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class JarFileRemoteApplicationLauncher extends RemoteApplicationLauncher {
|
||||
|
||||
public JarFileRemoteApplicationLauncher(Directories directories) {
|
||||
super(directories);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String createApplicationClassPath() throws Exception {
|
||||
File appDirectory = new File("target/app");
|
||||
if (appDirectory.isDirectory()
|
||||
&& !FileSystemUtils.deleteRecursively(appDirectory.toPath())) {
|
||||
throw new IllegalStateException(
|
||||
"Failed to delete '" + appDirectory.getAbsolutePath() + "'");
|
||||
}
|
||||
appDirectory.mkdirs();
|
||||
File appDirectory = getDirectories().getAppDirectory();
|
||||
copyApplicationTo(appDirectory);
|
||||
Manifest manifest = new Manifest();
|
||||
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
JarOutputStream output = new JarOutputStream(
|
||||
new FileOutputStream(new File(appDirectory, "app.jar")), manifest);
|
||||
FileSystemUtils.copyRecursively(new File("target/test-classes/com"),
|
||||
new File("target/app/com"));
|
||||
addToJar(output, new File("target/app/"), new File("target/app/"));
|
||||
File appJar = new File(appDirectory, "app.jar");
|
||||
JarOutputStream output = new JarOutputStream(new FileOutputStream(appJar),
|
||||
manifest);
|
||||
addToJar(output, appDirectory, appDirectory);
|
||||
output.close();
|
||||
List<String> entries = new ArrayList<>();
|
||||
entries.add("target/app/app.jar");
|
||||
for (File jar : new File("target/dependencies").listFiles()) {
|
||||
entries.add(jar.getAbsolutePath());
|
||||
}
|
||||
entries.add(appJar.getAbsolutePath());
|
||||
entries.addAll(getDependencyJarPaths());
|
||||
String classpath = StringUtils.collectionToDelimitedString(entries,
|
||||
File.pathSeparator);
|
||||
return classpath;
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import org.springframework.boot.testsupport.BuildOutput;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -39,12 +40,15 @@ class JvmLauncher implements TestRule {
|
||||
|
||||
private static final Pattern NON_ALPHABET_PATTERN = Pattern.compile("[^A-Za-z]+");
|
||||
|
||||
private final BuildOutput buildOutput = new BuildOutput(getClass());
|
||||
|
||||
private File outputDirectory;
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
this.outputDirectory = new File("target/output/" + NON_ALPHABET_PATTERN
|
||||
.matcher(description.getMethodName()).replaceAll(""));
|
||||
this.outputDirectory = new File(this.buildOutput.getRootLocation(),
|
||||
"output/" + NON_ALPHABET_PATTERN.matcher(description.getMethodName())
|
||||
.replaceAll(""));
|
||||
this.outputDirectory.mkdirs();
|
||||
return base;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -29,28 +28,29 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class LocalApplicationLauncher implements ApplicationLauncher {
|
||||
public class LocalApplicationLauncher extends AbstractApplicationLauncher {
|
||||
|
||||
LocalApplicationLauncher(Directories directories) {
|
||||
super(directories);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LaunchedApplication launchApplication(JvmLauncher jvmLauncher)
|
||||
throws Exception {
|
||||
public LaunchedApplication launchApplication(JvmLauncher jvmLauncher,
|
||||
File serverPortFile) throws Exception {
|
||||
LaunchedJvm jvm = jvmLauncher.launch("local", createApplicationClassPath(),
|
||||
"com.example.DevToolsTestApplication", "--server.port=0");
|
||||
return new LaunchedApplication(new File("target/app"), jvm.getStandardOut(),
|
||||
jvm.getStandardError(), jvm.getProcess(), null, null);
|
||||
"com.example.DevToolsTestApplication", serverPortFile.getAbsolutePath(),
|
||||
"--server.port=0");
|
||||
return new LaunchedApplication(getDirectories().getAppDirectory(),
|
||||
jvm.getStandardOut(), jvm.getStandardError(), jvm.getProcess(), null,
|
||||
null);
|
||||
}
|
||||
|
||||
protected String createApplicationClassPath() throws Exception {
|
||||
File appDirectory = new File("target/app");
|
||||
FileSystemUtils.deleteRecursively(appDirectory);
|
||||
appDirectory.mkdirs();
|
||||
FileSystemUtils.copyRecursively(new File("target/test-classes/com"),
|
||||
new File("target/app/com"));
|
||||
File appDirectory = getDirectories().getAppDirectory();
|
||||
copyApplicationTo(appDirectory);
|
||||
List<String> entries = new ArrayList<>();
|
||||
entries.add("target/app");
|
||||
for (File jar : new File("target/dependencies").listFiles()) {
|
||||
entries.add(jar.getAbsolutePath());
|
||||
}
|
||||
entries.add(appDirectory.getAbsolutePath());
|
||||
entries.addAll(getDependencyJarPaths());
|
||||
return StringUtils.collectionToDelimitedString(entries, File.pathSeparator);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.util.function.BiFunction;
|
||||
import org.springframework.boot.devtools.RemoteSpringApplication;
|
||||
import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -34,18 +33,23 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
abstract class RemoteApplicationLauncher implements ApplicationLauncher {
|
||||
abstract class RemoteApplicationLauncher extends AbstractApplicationLauncher {
|
||||
|
||||
RemoteApplicationLauncher(Directories directories) {
|
||||
super(directories);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LaunchedApplication launchApplication(JvmLauncher javaLauncher)
|
||||
throws Exception {
|
||||
public LaunchedApplication launchApplication(JvmLauncher javaLauncher,
|
||||
File serverPortFile) throws Exception {
|
||||
LaunchedJvm applicationJvm = javaLauncher.launch("app",
|
||||
createApplicationClassPath(), "com.example.DevToolsTestApplication",
|
||||
"--server.port=0", "--spring.devtools.remote.secret=secret");
|
||||
int port = awaitServerPort(applicationJvm.getStandardOut());
|
||||
serverPortFile.getAbsolutePath(), "--server.port=0",
|
||||
"--spring.devtools.remote.secret=secret");
|
||||
int port = awaitServerPort(applicationJvm.getStandardOut(), serverPortFile);
|
||||
BiFunction<Integer, File, Process> remoteRestarter = getRemoteRestarter(
|
||||
javaLauncher);
|
||||
return new LaunchedApplication(new File("target/remote"),
|
||||
return new LaunchedApplication(getDirectories().getRemoteAppDirectory(),
|
||||
applicationJvm.getStandardOut(), applicationJvm.getStandardError(),
|
||||
applicationJvm.getProcess(), remoteRestarter.apply(port, null),
|
||||
remoteRestarter);
|
||||
@@ -74,24 +78,18 @@ abstract class RemoteApplicationLauncher implements ApplicationLauncher {
|
||||
|
||||
private String createRemoteSpringApplicationClassPath(File classesDirectory)
|
||||
throws Exception {
|
||||
File remoteAppDirectory = getDirectories().getRemoteAppDirectory();
|
||||
if (classesDirectory == null) {
|
||||
File remoteDirectory = new File("target/remote");
|
||||
FileSystemUtils.deleteRecursively(remoteDirectory);
|
||||
remoteDirectory.mkdirs();
|
||||
FileSystemUtils.copyRecursively(new File("target/test-classes/com"),
|
||||
new File("target/remote/com"));
|
||||
copyApplicationTo(remoteAppDirectory);
|
||||
}
|
||||
List<String> entries = new ArrayList<>();
|
||||
entries.add("target/remote");
|
||||
for (File jar : new File("target/dependencies").listFiles()) {
|
||||
entries.add(jar.getAbsolutePath());
|
||||
}
|
||||
entries.add(remoteAppDirectory.getAbsolutePath());
|
||||
entries.addAll(getDependencyJarPaths());
|
||||
return StringUtils.collectionToDelimitedString(entries, File.pathSeparator);
|
||||
}
|
||||
|
||||
private int awaitServerPort(File standardOut) throws Exception {
|
||||
private int awaitServerPort(File standardOut, File serverPortFile) throws Exception {
|
||||
long end = System.currentTimeMillis() + 30000;
|
||||
File serverPortFile = new File("target/server.port");
|
||||
while (serverPortFile.length() == 0) {
|
||||
if (System.currentTimeMillis() > end) {
|
||||
throw new IllegalStateException(String.format(
|
||||
|
||||
Reference in New Issue
Block a user