Relocate projects to spring-boot-project
Move projects to better reflect the way that Spring Boot is released. The following projects are under `spring-boot-project`: - `spring-boot` - `spring-boot-autoconfigure` - `spring-boot-tools` - `spring-boot-starters` - `spring-boot-actuator` - `spring-boot-actuator-autoconfigure` - `spring-boot-test` - `spring-boot-test-autoconfigure` - `spring-boot-devtools` - `spring-boot-cli` - `spring-boot-docs` See gh-9316
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.maven;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
|
||||
import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
|
||||
|
||||
/**
|
||||
* A base mojo filtering the dependencies of the project.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author David Turanski
|
||||
* @since 1.1
|
||||
*/
|
||||
public abstract class AbstractDependencyFilterMojo extends AbstractMojo {
|
||||
|
||||
/**
|
||||
* Collection of artifact definitions to include. The {@link Include} element defines
|
||||
* a {@code groupId} and {@code artifactId} mandatory properties and an optional
|
||||
* {@code classifier} property.
|
||||
* @since 1.2
|
||||
*/
|
||||
@Parameter(property = "spring-boot.includes")
|
||||
private List<Include> includes;
|
||||
|
||||
/**
|
||||
* Collection of artifact definitions to exclude. The {@link Exclude} element defines
|
||||
* a {@code groupId} and {@code artifactId} mandatory properties and an optional
|
||||
* {@code classifier} property.
|
||||
* @since 1.1
|
||||
*/
|
||||
@Parameter(property = "spring-boot.excludes")
|
||||
private List<Exclude> excludes;
|
||||
|
||||
/**
|
||||
* Comma separated list of groupId names to exclude (exact match).
|
||||
* @since 1.1
|
||||
*/
|
||||
@Parameter(property = "spring-boot.excludeGroupIds", defaultValue = "")
|
||||
private String excludeGroupIds;
|
||||
|
||||
/**
|
||||
* Comma separated list of artifact names to exclude (exact match).
|
||||
* @since 1.1
|
||||
*/
|
||||
@Parameter(property = "spring-boot.excludeArtifactIds", defaultValue = "")
|
||||
private String excludeArtifactIds;
|
||||
|
||||
protected void setExcludes(List<Exclude> excludes) {
|
||||
this.excludes = excludes;
|
||||
}
|
||||
|
||||
protected void setIncludes(List<Include> includes) {
|
||||
this.includes = includes;
|
||||
}
|
||||
|
||||
protected void setExcludeGroupIds(String excludeGroupIds) {
|
||||
this.excludeGroupIds = excludeGroupIds;
|
||||
}
|
||||
|
||||
protected void setExcludeArtifactIds(String excludeArtifactIds) {
|
||||
this.excludeArtifactIds = excludeArtifactIds;
|
||||
}
|
||||
|
||||
protected Set<Artifact> filterDependencies(Set<Artifact> dependencies,
|
||||
FilterArtifacts filters) throws MojoExecutionException {
|
||||
try {
|
||||
Set<Artifact> filtered = new LinkedHashSet<>(dependencies);
|
||||
filtered.retainAll(filters.filter(dependencies));
|
||||
return filtered;
|
||||
}
|
||||
catch (ArtifactFilterException ex) {
|
||||
throw new MojoExecutionException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return artifact filters configured for this MOJO.
|
||||
* @param additionalFilters optional additional filters to apply
|
||||
* @return the filters
|
||||
*/
|
||||
protected final FilterArtifacts getFilters(ArtifactsFilter... additionalFilters) {
|
||||
FilterArtifacts filters = new FilterArtifacts();
|
||||
for (ArtifactsFilter additionalFilter : additionalFilters) {
|
||||
filters.addFilter(additionalFilter);
|
||||
}
|
||||
filters.addFilter(
|
||||
new ArtifactIdFilter("", cleanFilterConfig(this.excludeArtifactIds)));
|
||||
filters.addFilter(
|
||||
new MatchingGroupIdFilter(cleanFilterConfig(this.excludeGroupIds)));
|
||||
if (this.includes != null && !this.includes.isEmpty()) {
|
||||
filters.addFilter(new IncludeFilter(this.includes));
|
||||
}
|
||||
if (this.excludes != null && !this.excludes.isEmpty()) {
|
||||
filters.addFilter(new ExcludeFilter(this.excludes));
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
private String cleanFilterConfig(String content) {
|
||||
if (content == null || content.trim().isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder cleaned = new StringBuilder();
|
||||
StringTokenizer tokenizer = new StringTokenizer(content, ",");
|
||||
while (tokenizer.hasMoreElements()) {
|
||||
cleaned.append(tokenizer.nextToken().trim());
|
||||
if (tokenizer.hasMoreElements()) {
|
||||
cleaned.append(",");
|
||||
}
|
||||
}
|
||||
return cleaned.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
* 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.maven;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.model.Resource;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactFeatureFilter;
|
||||
import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
|
||||
|
||||
import org.springframework.boot.loader.tools.FileUtils;
|
||||
import org.springframework.boot.loader.tools.MainClassFinder;
|
||||
|
||||
/**
|
||||
* Base class to run a spring application.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author David Liu
|
||||
* @author Daniel Young
|
||||
* @see RunMojo
|
||||
* @see StartMojo
|
||||
*/
|
||||
public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
|
||||
|
||||
private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";
|
||||
|
||||
/**
|
||||
* The Maven project.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(defaultValue = "${project}", readonly = true, required = true)
|
||||
private MavenProject project;
|
||||
|
||||
/**
|
||||
* Add maven resources to the classpath directly, this allows live in-place editing of
|
||||
* resources. Duplicate resources are removed from {@code target/classes} to prevent
|
||||
* them to appear twice if {@code ClassLoader.getResources()} is called. Please
|
||||
* consider adding {@code spring-boot-devtools} to your project instead as it provides
|
||||
* this feature and many more.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.addResources", defaultValue = "false")
|
||||
private boolean addResources = false;
|
||||
|
||||
/**
|
||||
* Path to agent jar. NOTE: the use of agents means that processes will be started by
|
||||
* forking a new JVM.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.agent")
|
||||
private File[] agent;
|
||||
|
||||
/**
|
||||
* Flag to say that the agent requires -noverify.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.noverify")
|
||||
private boolean noverify = false;
|
||||
|
||||
/**
|
||||
* Current working directory to use for the application. If not specified, basedir
|
||||
* will be used. NOTE: the use of working directory means that processes will be
|
||||
* started by forking a new JVM.
|
||||
* @since 1.5
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.workingDirectory")
|
||||
private File workingDirectory;
|
||||
|
||||
/**
|
||||
* JVM arguments that should be associated with the forked process used to run the
|
||||
* application. On command line, make sure to wrap multiple values between quotes.
|
||||
* NOTE: the use of JVM arguments means that processes will be started by forking a
|
||||
* new JVM.
|
||||
* @since 1.1
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.jvmArguments")
|
||||
private String jvmArguments;
|
||||
|
||||
/**
|
||||
* Arguments that should be passed to the application. On command line use commas to
|
||||
* separate multiple arguments.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.arguments")
|
||||
private String[] arguments;
|
||||
|
||||
/**
|
||||
* The spring profiles to activate. Convenience shortcut of specifying the
|
||||
* 'spring.profiles.active' argument. On command line use commas to separate multiple
|
||||
* profiles.
|
||||
* @since 1.3
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.profiles")
|
||||
private String[] profiles;
|
||||
|
||||
/**
|
||||
* The name of the main class. If not specified the first compiled class found that
|
||||
* contains a 'main' method will be used.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.main-class")
|
||||
private String mainClass;
|
||||
|
||||
/**
|
||||
* Additional folders besides the classes directory that should be added to the
|
||||
* classpath.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.folders")
|
||||
private String[] folders;
|
||||
|
||||
/**
|
||||
* Directory containing the classes and resource files that should be packaged into
|
||||
* the archive.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
|
||||
private File classesDirectory;
|
||||
|
||||
/**
|
||||
* Flag to indicate if the run processes should be forked. {@code fork} is
|
||||
* automatically enabled if an agent, jvmArguments or working directory are specified,
|
||||
* or if devtools is present.
|
||||
* @since 1.2
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.fork")
|
||||
private Boolean fork;
|
||||
|
||||
/**
|
||||
* Flag to include the test classpath when running.
|
||||
* @since 1.3
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.useTestClasspath", defaultValue = "false")
|
||||
private Boolean useTestClasspath;
|
||||
|
||||
/**
|
||||
* Skip the execution.
|
||||
* @since 1.3.2
|
||||
*/
|
||||
@Parameter(property = "spring-boot.run.skip", defaultValue = "false")
|
||||
private boolean skip;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||
if (this.skip) {
|
||||
getLog().debug("skipping run as per configuration.");
|
||||
return;
|
||||
}
|
||||
run(getStartClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if the application process should be forked.
|
||||
* @return {@code true} if the application process should be forked
|
||||
*/
|
||||
protected boolean isFork() {
|
||||
return (Boolean.TRUE.equals(this.fork)
|
||||
|| (this.fork == null && enableForkByDefault()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify if fork should be enabled by default.
|
||||
* @return {@code true} if fork should be enabled by default
|
||||
* @see #logDisabledFork()
|
||||
*/
|
||||
protected boolean enableForkByDefault() {
|
||||
return hasAgent() || hasJvmArgs() || hasWorkingDirectorySet();
|
||||
}
|
||||
|
||||
private boolean hasAgent() {
|
||||
return (this.agent != null && this.agent.length > 0);
|
||||
}
|
||||
|
||||
private boolean hasJvmArgs() {
|
||||
return (this.jvmArguments != null && !this.jvmArguments.isEmpty());
|
||||
}
|
||||
|
||||
private boolean hasWorkingDirectorySet() {
|
||||
return this.workingDirectory != null;
|
||||
}
|
||||
|
||||
private void run(String startClassName)
|
||||
throws MojoExecutionException, MojoFailureException {
|
||||
boolean fork = isFork();
|
||||
this.project.getProperties().setProperty("_spring.boot.fork.enabled",
|
||||
Boolean.toString(fork));
|
||||
if (fork) {
|
||||
doRunWithForkedJvm(startClassName);
|
||||
}
|
||||
else {
|
||||
logDisabledFork();
|
||||
runWithMavenJvm(startClassName, resolveApplicationArguments().asArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a warning indicating that fork mode has been explicitly disabled while some
|
||||
* conditions are present that require to enable it.
|
||||
* @see #enableForkByDefault()
|
||||
*/
|
||||
protected void logDisabledFork() {
|
||||
if (hasAgent()) {
|
||||
getLog().warn("Fork mode disabled, ignoring agent");
|
||||
}
|
||||
if (hasJvmArgs()) {
|
||||
getLog().warn("Fork mode disabled, ignoring JVM argument(s) ["
|
||||
+ this.jvmArguments + "]");
|
||||
}
|
||||
if (hasWorkingDirectorySet()) {
|
||||
getLog().warn("Fork mode disabled, ignoring working directory configuration");
|
||||
}
|
||||
}
|
||||
|
||||
private void doRunWithForkedJvm(String startClassName)
|
||||
throws MojoExecutionException, MojoFailureException {
|
||||
List<String> args = new ArrayList<>();
|
||||
addAgents(args);
|
||||
addJvmArgs(args);
|
||||
addClasspath(args);
|
||||
args.add(startClassName);
|
||||
addArgs(args);
|
||||
runWithForkedJvm(this.workingDirectory, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run with a forked VM, using the specified command line arguments.
|
||||
* @param workingDirectory the working directory of the forked JVM
|
||||
* @param args the arguments (JVM arguments and application arguments)
|
||||
* @throws MojoExecutionException in case of MOJO execution errors
|
||||
* @throws MojoFailureException in case of MOJO failures
|
||||
*/
|
||||
protected abstract void runWithForkedJvm(File workingDirectory, List<String> args)
|
||||
throws MojoExecutionException, MojoFailureException;
|
||||
|
||||
/**
|
||||
* Run with the current VM, using the specified arguments.
|
||||
* @param startClassName the class to run
|
||||
* @param arguments the class arguments
|
||||
* @throws MojoExecutionException in case of MOJO execution errors
|
||||
* @throws MojoFailureException in case of MOJO failures
|
||||
*/
|
||||
protected abstract void runWithMavenJvm(String startClassName, String... arguments)
|
||||
throws MojoExecutionException, MojoFailureException;
|
||||
|
||||
/**
|
||||
* Resolve the application arguments to use.
|
||||
* @return a {@link RunArguments} defining the application arguments
|
||||
*/
|
||||
protected RunArguments resolveApplicationArguments() {
|
||||
RunArguments runArguments = new RunArguments(this.arguments);
|
||||
addActiveProfileArgument(runArguments);
|
||||
return runArguments;
|
||||
}
|
||||
|
||||
private void addArgs(List<String> args) {
|
||||
RunArguments applicationArguments = resolveApplicationArguments();
|
||||
Collections.addAll(args, applicationArguments.asArray());
|
||||
logArguments("Application argument(s): ", this.arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the JVM arguments to use.
|
||||
* @return a {@link RunArguments} defining the JVM arguments
|
||||
*/
|
||||
protected RunArguments resolveJvmArguments() {
|
||||
return new RunArguments(this.jvmArguments);
|
||||
}
|
||||
|
||||
private void addJvmArgs(List<String> args) {
|
||||
RunArguments jvmArguments = resolveJvmArguments();
|
||||
Collections.addAll(args, jvmArguments.asArray());
|
||||
logArguments("JVM argument(s): ", jvmArguments.asArray());
|
||||
}
|
||||
|
||||
private void addAgents(List<String> args) {
|
||||
if (this.agent != null) {
|
||||
getLog().info("Attaching agents: " + Arrays.asList(this.agent));
|
||||
for (File agent : this.agent) {
|
||||
args.add("-javaagent:" + agent);
|
||||
}
|
||||
}
|
||||
if (this.noverify) {
|
||||
args.add("-noverify");
|
||||
}
|
||||
}
|
||||
|
||||
private void addActiveProfileArgument(RunArguments arguments) {
|
||||
if (this.profiles.length > 0) {
|
||||
StringBuilder arg = new StringBuilder("--spring.profiles.active=");
|
||||
for (int i = 0; i < this.profiles.length; i++) {
|
||||
arg.append(this.profiles[i]);
|
||||
if (i < this.profiles.length - 1) {
|
||||
arg.append(",");
|
||||
}
|
||||
}
|
||||
arguments.getArgs().addFirst(arg.toString());
|
||||
logArguments("Active profile(s): ", this.profiles);
|
||||
}
|
||||
}
|
||||
|
||||
private void addClasspath(List<String> args) throws MojoExecutionException {
|
||||
try {
|
||||
StringBuilder classpath = new StringBuilder();
|
||||
for (URL ele : getClassPathUrls()) {
|
||||
classpath = classpath
|
||||
.append((classpath.length() > 0 ? File.pathSeparator : "")
|
||||
+ new File(ele.toURI()));
|
||||
}
|
||||
getLog().debug("Classpath for forked process: " + classpath);
|
||||
args.add("-cp");
|
||||
args.add(classpath.toString());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MojoExecutionException("Could not build classpath", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String getStartClass() throws MojoExecutionException {
|
||||
String mainClass = this.mainClass;
|
||||
if (mainClass == null) {
|
||||
try {
|
||||
mainClass = MainClassFinder.findSingleMainClass(this.classesDirectory,
|
||||
SPRING_BOOT_APPLICATION_CLASS_NAME);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new MojoExecutionException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
if (mainClass == null) {
|
||||
throw new MojoExecutionException("Unable to find a suitable main class, "
|
||||
+ "please add a 'mainClass' property");
|
||||
}
|
||||
return mainClass;
|
||||
}
|
||||
|
||||
protected URL[] getClassPathUrls() throws MojoExecutionException {
|
||||
try {
|
||||
List<URL> urls = new ArrayList<>();
|
||||
addUserDefinedFolders(urls);
|
||||
addResources(urls);
|
||||
addProjectClasses(urls);
|
||||
addDependencies(urls);
|
||||
return urls.toArray(new URL[urls.size()]);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new MojoExecutionException("Unable to build classpath", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void addUserDefinedFolders(List<URL> urls) throws MalformedURLException {
|
||||
if (this.folders != null) {
|
||||
for (String folder : this.folders) {
|
||||
urls.add(new File(folder).toURI().toURL());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addResources(List<URL> urls) throws IOException {
|
||||
if (this.addResources) {
|
||||
for (Resource resource : this.project.getResources()) {
|
||||
File directory = new File(resource.getDirectory());
|
||||
urls.add(directory.toURI().toURL());
|
||||
FileUtils.removeDuplicatesFromOutputDirectory(this.classesDirectory,
|
||||
directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addProjectClasses(List<URL> urls) throws MalformedURLException {
|
||||
urls.add(this.classesDirectory.toURI().toURL());
|
||||
}
|
||||
|
||||
private void addDependencies(List<URL> urls)
|
||||
throws MalformedURLException, MojoExecutionException {
|
||||
FilterArtifacts filters = this.useTestClasspath ? getFilters()
|
||||
: getFilters(new TestArtifactFilter());
|
||||
Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(),
|
||||
filters);
|
||||
for (Artifact artifact : artifacts) {
|
||||
if (artifact.getFile() != null) {
|
||||
urls.add(artifact.getFile().toURI().toURL());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logArguments(String message, String[] args) {
|
||||
StringBuilder sb = new StringBuilder(message);
|
||||
for (String arg : args) {
|
||||
sb.append(arg).append(" ");
|
||||
}
|
||||
getLog().debug(sb.toString().trim());
|
||||
}
|
||||
|
||||
private static class TestArtifactFilter extends AbstractArtifactFeatureFilter {
|
||||
|
||||
TestArtifactFilter() {
|
||||
super("", Artifact.SCOPE_TEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getArtifactFeature(Artifact artifact) {
|
||||
return artifact.getScope();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Isolated {@link ThreadGroup} to capture uncaught exceptions.
|
||||
*/
|
||||
class IsolatedThreadGroup extends ThreadGroup {
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private Throwable exception;
|
||||
|
||||
IsolatedThreadGroup(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
if (!(ex instanceof ThreadDeath)) {
|
||||
synchronized (this.monitor) {
|
||||
this.exception = (this.exception == null ? ex : this.exception);
|
||||
}
|
||||
getLog().warn(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void rethrowUncaughtException() throws MojoExecutionException {
|
||||
synchronized (this.monitor) {
|
||||
if (this.exception != null) {
|
||||
throw new MojoExecutionException(
|
||||
"An exception occurred while running. "
|
||||
+ this.exception.getMessage(),
|
||||
this.exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Runner used to launch the application.
|
||||
*/
|
||||
class LaunchRunner implements Runnable {
|
||||
|
||||
private final String startClassName;
|
||||
|
||||
private final String[] args;
|
||||
|
||||
LaunchRunner(String startClassName, String... args) {
|
||||
this.startClassName = startClassName;
|
||||
this.args = (args != null ? args : new String[] {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Thread thread = Thread.currentThread();
|
||||
ClassLoader classLoader = thread.getContextClassLoader();
|
||||
try {
|
||||
Class<?> startClass = classLoader.loadClass(this.startClassName);
|
||||
Method mainMethod = startClass.getMethod("main", String[].class);
|
||||
if (!mainMethod.isAccessible()) {
|
||||
mainMethod.setAccessible(true);
|
||||
}
|
||||
mainMethod.invoke(null, new Object[] { this.args });
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
Exception wrappedEx = new Exception(
|
||||
"The specified mainClass doesn't contain a "
|
||||
+ "main method with appropriate signature.",
|
||||
ex);
|
||||
thread.getThreadGroup().uncaughtException(thread, wrappedEx);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
thread.getThreadGroup().uncaughtException(thread, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.maven;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.model.Dependency;
|
||||
import org.apache.maven.plugin.logging.Log;
|
||||
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.Library;
|
||||
import org.springframework.boot.loader.tools.LibraryCallback;
|
||||
import org.springframework.boot.loader.tools.LibraryScope;
|
||||
|
||||
/**
|
||||
* {@link Libraries} backed by Maven {@link Artifact}s.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class ArtifactsLibraries implements Libraries {
|
||||
|
||||
private static final Map<String, LibraryScope> scopes;
|
||||
|
||||
static {
|
||||
Map<String, LibraryScope> libraryScopes = new HashMap<>();
|
||||
libraryScopes.put(Artifact.SCOPE_COMPILE, LibraryScope.COMPILE);
|
||||
libraryScopes.put(Artifact.SCOPE_RUNTIME, LibraryScope.RUNTIME);
|
||||
libraryScopes.put(Artifact.SCOPE_PROVIDED, LibraryScope.PROVIDED);
|
||||
libraryScopes.put(Artifact.SCOPE_SYSTEM, LibraryScope.PROVIDED);
|
||||
scopes = Collections.unmodifiableMap(libraryScopes);
|
||||
}
|
||||
|
||||
private final Set<Artifact> artifacts;
|
||||
|
||||
private final Collection<Dependency> unpacks;
|
||||
|
||||
private final Log log;
|
||||
|
||||
public ArtifactsLibraries(Set<Artifact> artifacts, Collection<Dependency> unpacks,
|
||||
Log log) {
|
||||
this.artifacts = artifacts;
|
||||
this.unpacks = unpacks;
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doWithLibraries(LibraryCallback callback) throws IOException {
|
||||
Set<String> duplicates = getDuplicates(this.artifacts);
|
||||
for (Artifact artifact : this.artifacts) {
|
||||
LibraryScope scope = scopes.get(artifact.getScope());
|
||||
if (scope != null && artifact.getFile() != null) {
|
||||
String name = getFileName(artifact);
|
||||
if (duplicates.contains(name)) {
|
||||
this.log.debug("Duplicate found: " + name);
|
||||
name = artifact.getGroupId() + "-" + name;
|
||||
this.log.debug("Renamed to: " + name);
|
||||
}
|
||||
callback.library(new Library(name, artifact.getFile(), scope,
|
||||
isUnpackRequired(artifact)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> getDuplicates(Set<Artifact> artifacts) {
|
||||
Set<String> duplicates = new HashSet<>();
|
||||
Set<String> seen = new HashSet<>();
|
||||
for (Artifact artifact : artifacts) {
|
||||
String fileName = getFileName(artifact);
|
||||
if (artifact.getFile() != null && !seen.add(fileName)) {
|
||||
duplicates.add(fileName);
|
||||
}
|
||||
}
|
||||
return duplicates;
|
||||
}
|
||||
|
||||
private boolean isUnpackRequired(Artifact artifact) {
|
||||
if (this.unpacks != null) {
|
||||
for (Dependency unpack : this.unpacks) {
|
||||
if (artifact.getGroupId().equals(unpack.getGroupId())
|
||||
&& artifact.getArtifactId().equals(unpack.getArtifactId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getFileName(Artifact artifact) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(artifact.getArtifactId()).append("-").append(artifact.getBaseVersion());
|
||||
String classifier = artifact.getClassifier();
|
||||
if (classifier != null) {
|
||||
sb.append("-").append(classifier);
|
||||
}
|
||||
sb.append(".").append(artifact.getArtifactHandler().getExtension());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.maven;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugins.annotations.Component;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import org.sonatype.plexus.build.incremental.BuildContext;
|
||||
|
||||
import org.springframework.boot.loader.tools.BuildPropertiesWriter;
|
||||
import org.springframework.boot.loader.tools.BuildPropertiesWriter.NullAdditionalPropertyValueException;
|
||||
import org.springframework.boot.loader.tools.BuildPropertiesWriter.ProjectDetails;
|
||||
|
||||
/**
|
||||
* Generate a {@code build-info.properties} file based the content of the current
|
||||
* {@link MavenProject}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Mojo(name = "build-info", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, threadSafe = true)
|
||||
public class BuildInfoMojo extends AbstractMojo {
|
||||
|
||||
@Component
|
||||
private BuildContext buildContext;
|
||||
|
||||
/**
|
||||
* The Maven project.
|
||||
*/
|
||||
@Parameter(defaultValue = "${project}", readonly = true, required = true)
|
||||
private MavenProject project;
|
||||
|
||||
/**
|
||||
* The location of the generated build-info.properties.
|
||||
*/
|
||||
@Parameter(defaultValue = "${project.build.outputDirectory}/META-INF/build-info.properties")
|
||||
private File outputFile;
|
||||
|
||||
/**
|
||||
* Additional properties to store in the build-info.properties. Each entry is prefixed
|
||||
* by {@code build.} in the generated build-info.properties.
|
||||
*/
|
||||
@Parameter
|
||||
private Map<String, String> additionalProperties;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||
try {
|
||||
new BuildPropertiesWriter(this.outputFile)
|
||||
.writeBuildProperties(new ProjectDetails(this.project.getGroupId(),
|
||||
this.project.getArtifactId(), this.project.getVersion(),
|
||||
this.project.getName(), this.additionalProperties));
|
||||
this.buildContext.refresh(this.outputFile);
|
||||
}
|
||||
catch (NullAdditionalPropertyValueException ex) {
|
||||
throw new MojoFailureException(
|
||||
"Failed to generate build-info.properties. " + ex.getMessage(), ex);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MojoExecutionException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.maven;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactsFilter;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
|
||||
|
||||
/**
|
||||
* Base class for {@link ArtifactsFilter} based on a {@link FilterableDependency} list.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author David Turanski
|
||||
* @since 1.2
|
||||
*/
|
||||
public abstract class DependencyFilter extends AbstractArtifactsFilter {
|
||||
|
||||
private final List<? extends FilterableDependency> filters;
|
||||
|
||||
/**
|
||||
* Create a new instance with the list of {@link FilterableDependency} instance(s) to
|
||||
* use.
|
||||
* @param dependencies the source dependencies
|
||||
*/
|
||||
public DependencyFilter(List<? extends FilterableDependency> dependencies) {
|
||||
this.filters = dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public Set filter(Set artifacts) throws ArtifactFilterException {
|
||||
Set result = new HashSet();
|
||||
for (Object artifact : artifacts) {
|
||||
if (!filter((Artifact) artifact)) {
|
||||
result.add(artifact);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract boolean filter(Artifact artifact);
|
||||
|
||||
/**
|
||||
* Check if the specified {@link org.apache.maven.artifact.Artifact} matches the
|
||||
* specified {@link org.springframework.boot.maven.FilterableDependency}. Returns
|
||||
* {@code true} if it should be excluded
|
||||
* @param artifact the Maven {@link Artifact}
|
||||
* @param dependency the {@link FilterableDependency}
|
||||
* @return {@code true} if the artifact matches the dependency
|
||||
*/
|
||||
protected final boolean equals(Artifact artifact, FilterableDependency dependency) {
|
||||
if (!dependency.getGroupId().equals(artifact.getGroupId())) {
|
||||
return false;
|
||||
}
|
||||
if (!dependency.getArtifactId().equals(artifact.getArtifactId())) {
|
||||
return false;
|
||||
}
|
||||
return (dependency.getClassifier() == null || artifact.getClassifier() != null
|
||||
&& dependency.getClassifier().equals(artifact.getClassifier()));
|
||||
}
|
||||
|
||||
protected final List<? extends FilterableDependency> getFilters() {
|
||||
return this.filters;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.maven;
|
||||
|
||||
/**
|
||||
* A model for a dependency to exclude.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.1
|
||||
*/
|
||||
public class Exclude extends FilterableDependency {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.maven;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
|
||||
/**
|
||||
* An {DependencyFilter} that filters out any artifact matching an {@link Exclude}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author David Turanski
|
||||
* @since 1.1
|
||||
*/
|
||||
public class ExcludeFilter extends DependencyFilter {
|
||||
|
||||
public ExcludeFilter(Exclude... excludes) {
|
||||
this(Arrays.asList(excludes));
|
||||
}
|
||||
|
||||
public ExcludeFilter(List<Exclude> excludes) {
|
||||
super(excludes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean filter(Artifact artifact) {
|
||||
for (FilterableDependency dependency : getFilters()) {
|
||||
if (equals(artifact, dependency)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.maven;
|
||||
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
|
||||
/**
|
||||
* A model for a dependency to include or exclude.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author David Turanski
|
||||
* @since 1.2
|
||||
*/
|
||||
abstract class FilterableDependency {
|
||||
|
||||
/**
|
||||
* The groupId of the artifact to exclude.
|
||||
*/
|
||||
@Parameter(required = true)
|
||||
private String groupId;
|
||||
|
||||
/**
|
||||
* The artifactId of the artifact to exclude.
|
||||
*/
|
||||
@Parameter(required = true)
|
||||
private String artifactId;
|
||||
|
||||
/**
|
||||
* The classifier of the artifact to exclude.
|
||||
*/
|
||||
@Parameter
|
||||
private String classifier;
|
||||
|
||||
public String getGroupId() {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public String getArtifactId() {
|
||||
return this.artifactId;
|
||||
}
|
||||
|
||||
public void setArtifactId(String artifactId) {
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
|
||||
public String getClassifier() {
|
||||
return this.classifier;
|
||||
}
|
||||
|
||||
public void setClassifier(String classifier) {
|
||||
this.classifier = classifier;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.maven;
|
||||
|
||||
/**
|
||||
* A model for a dependency to include.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @since 1.2
|
||||
*/
|
||||
public class Include extends FilterableDependency {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.maven;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
|
||||
|
||||
/**
|
||||
* An {@link ArtifactsFilter} that filters out any artifact not matching an
|
||||
* {@link Include}.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @since 1.2
|
||||
*/
|
||||
public class IncludeFilter extends DependencyFilter {
|
||||
|
||||
public IncludeFilter(List<Include> includes) {
|
||||
super(includes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean filter(Artifact artifact) {
|
||||
for (FilterableDependency dependency : getFilters()) {
|
||||
if (equals(artifact, dependency)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.maven;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactFeatureFilter;
|
||||
|
||||
/**
|
||||
* An {@link org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter
|
||||
* ArtifactsFilter} that filters by matching groupId.
|
||||
*
|
||||
* Preferred over the
|
||||
* {@link org.apache.maven.shared.artifact.filter.collection.GroupIdFilter} due to that
|
||||
* classes use of {@link String#startsWith} to match on prefix.
|
||||
*
|
||||
* @author Mark Ingram
|
||||
* @since 1.1
|
||||
*/
|
||||
public class MatchingGroupIdFilter extends AbstractArtifactFeatureFilter {
|
||||
|
||||
/**
|
||||
* Create a new instance with the CSV groupId values that should be excluded.
|
||||
* @param exclude the group values to exclude
|
||||
*/
|
||||
public MatchingGroupIdFilter(String exclude) {
|
||||
super("", exclude);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getArtifactFeature(Artifact artifact) {
|
||||
return artifact.getGroupId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.maven;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
|
||||
import org.apache.maven.plugins.shade.relocation.Relocator;
|
||||
import org.apache.maven.plugins.shade.resource.ResourceTransformer;
|
||||
|
||||
/**
|
||||
* Extension for the <a href="http://maven.apache.org/plugins/maven-shade-plugin/">Maven
|
||||
* shade plugin</a> to allow properties files (e.g. {@literal META-INF/spring.factories})
|
||||
* to be merged without losing any information.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class PropertiesMergingResourceTransformer implements ResourceTransformer {
|
||||
|
||||
// Set this in pom configuration with <resource>...</resource>
|
||||
private String resource;
|
||||
|
||||
private final Properties data = new Properties();
|
||||
|
||||
/**
|
||||
* Return the data the properties being merged.
|
||||
* @return the data
|
||||
*/
|
||||
public Properties getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTransformResource(String resource) {
|
||||
if (this.resource != null && this.resource.equalsIgnoreCase(resource)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processResource(String resource, InputStream is,
|
||||
List<Relocator> relocators) throws IOException {
|
||||
Properties properties = new Properties();
|
||||
properties.load(is);
|
||||
is.close();
|
||||
for (Entry<Object, Object> entry : properties.entrySet()) {
|
||||
String name = (String) entry.getKey();
|
||||
String value = (String) entry.getValue();
|
||||
String existing = this.data.getProperty(name);
|
||||
this.data.setProperty(name,
|
||||
existing == null ? value : existing + "," + value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTransformedResource() {
|
||||
return !this.data.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyOutputStream(JarOutputStream os) throws IOException {
|
||||
os.putNextEntry(new JarEntry(this.resource));
|
||||
this.data.store(os, "Merged by PropertiesMergingResourceTransformer");
|
||||
os.flush();
|
||||
this.data.clear();
|
||||
}
|
||||
|
||||
public String getResource() {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
public void setResource(String resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* 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.maven;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.model.Dependency;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugins.annotations.Component;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.maven.plugins.annotations.ResolutionScope;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
import org.apache.maven.project.MavenProjectHelper;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
|
||||
|
||||
import org.springframework.boot.loader.tools.DefaultLaunchScript;
|
||||
import org.springframework.boot.loader.tools.LaunchScript;
|
||||
import org.springframework.boot.loader.tools.Layout;
|
||||
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||
import org.springframework.boot.loader.tools.Layouts;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.Repackager;
|
||||
import org.springframework.boot.loader.tools.Repackager.MainClassTimeoutWarningListener;
|
||||
|
||||
/**
|
||||
* Repackages existing JAR and WAR archives so that they can be executed from the command
|
||||
* line using {@literal java -jar}. With <code>layout=NONE</code> can also be used simply
|
||||
* to package a JAR with nested dependencies (and no main class, so not executable).
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Mojo(name = "repackage", defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)
|
||||
public class RepackageMojo extends AbstractDependencyFilterMojo {
|
||||
|
||||
/**
|
||||
* The Maven project.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(defaultValue = "${project}", readonly = true, required = true)
|
||||
private MavenProject project;
|
||||
|
||||
/**
|
||||
* Maven project helper utils.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Component
|
||||
private MavenProjectHelper projectHelper;
|
||||
|
||||
/**
|
||||
* Directory containing the generated archive.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(defaultValue = "${project.build.directory}", required = true)
|
||||
private File outputDirectory;
|
||||
|
||||
/**
|
||||
* Name of the generated archive.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter(defaultValue = "${project.build.finalName}", required = true)
|
||||
private String finalName;
|
||||
|
||||
/**
|
||||
* Skip the execution.
|
||||
* @since 1.2
|
||||
*/
|
||||
@Parameter(property = "spring-boot.repackage.skip", defaultValue = "false")
|
||||
private boolean skip;
|
||||
|
||||
/**
|
||||
* Classifier to add to the artifact generated. If given, the artifact will be
|
||||
* attached with that classifier and the main artifact will be deployed as the main
|
||||
* artifact. If this is not given (default), it will replace the main artifact and
|
||||
* only the repackaged artifact will be deployed. Attaching the artifact allows to
|
||||
* deploy it alongside to the original one, see <a href=
|
||||
* "http://maven.apache.org/plugins/maven-deploy-plugin/examples/deploying-with-classifiers.html"
|
||||
* > the maven documentation for more details</a>.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter
|
||||
private String classifier;
|
||||
|
||||
/**
|
||||
* Attach the repackaged archive to be installed and deployed.
|
||||
* @since 1.4
|
||||
*/
|
||||
@Parameter(defaultValue = "true")
|
||||
private boolean attach = true;
|
||||
|
||||
/**
|
||||
* The name of the main class. If not specified the first compiled class found that
|
||||
* contains a 'main' method will be used.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter
|
||||
private String mainClass;
|
||||
|
||||
/**
|
||||
* The type of archive (which corresponds to how the dependencies are laid out inside
|
||||
* it). Possible values are JAR, WAR, ZIP, DIR, NONE. Defaults to a guess based on the
|
||||
* archive type.
|
||||
* @since 1.0
|
||||
*/
|
||||
@Parameter
|
||||
private LayoutType layout;
|
||||
|
||||
/**
|
||||
* The layout factory that will be used to create the executable archive if no
|
||||
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
||||
* parties.
|
||||
* @since 1.5
|
||||
*/
|
||||
@Parameter
|
||||
private LayoutFactory layoutFactory;
|
||||
|
||||
/**
|
||||
* A list of the libraries that must be unpacked from fat jars in order to run.
|
||||
* Specify each library as a <code><dependency></code> with a
|
||||
* <code><groupId></code> and a <code><artifactId></code> and they will be
|
||||
* unpacked at runtime.
|
||||
* @since 1.1
|
||||
*/
|
||||
@Parameter
|
||||
private List<Dependency> requiresUnpack;
|
||||
|
||||
/**
|
||||
* Make a fully executable jar for *nix machines by prepending a launch script to the
|
||||
* jar.
|
||||
* <p>
|
||||
* Currently, some tools do not accept this format so you may not always be able to
|
||||
* use this technique. For example, <code>jar -xf</code> may silently fail to extract
|
||||
* a jar or war that has been made fully-executable. It is recommended that you only
|
||||
* enable this option if you intend to execute it directly, rather than running it
|
||||
* with <code>java -jar</code> or deploying it to a servlet container.
|
||||
* @since 1.3
|
||||
*/
|
||||
@Parameter(defaultValue = "false")
|
||||
private boolean executable;
|
||||
|
||||
/**
|
||||
* The embedded launch script to prepend to the front of the jar if it is fully
|
||||
* executable. If not specified the 'Spring Boot' default script will be used.
|
||||
* @since 1.3
|
||||
*/
|
||||
@Parameter
|
||||
private File embeddedLaunchScript;
|
||||
|
||||
/**
|
||||
* Properties that should be expanded in the embedded launch script.
|
||||
* @since 1.3
|
||||
*/
|
||||
@Parameter
|
||||
private Properties embeddedLaunchScriptProperties;
|
||||
|
||||
/**
|
||||
* Exclude Spring Boot devtools from the repackaged archive.
|
||||
* @since 1.3
|
||||
*/
|
||||
@Parameter(defaultValue = "true")
|
||||
private boolean excludeDevtools = true;
|
||||
|
||||
/**
|
||||
* Include system scoped dependencies.
|
||||
* @since 1.4
|
||||
*/
|
||||
@Parameter(defaultValue = "false")
|
||||
public boolean includeSystemScope;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||
if (this.project.getPackaging().equals("pom")) {
|
||||
getLog().debug("repackage goal could not be applied to pom project.");
|
||||
return;
|
||||
}
|
||||
if (this.skip) {
|
||||
getLog().debug("skipping repackaging as per configuration.");
|
||||
return;
|
||||
}
|
||||
repackage();
|
||||
}
|
||||
|
||||
private void repackage() throws MojoExecutionException {
|
||||
File source = this.project.getArtifact().getFile();
|
||||
File target = getTargetFile();
|
||||
Repackager repackager = getRepackager(source);
|
||||
Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(),
|
||||
getFilters(getAdditionalFilters()));
|
||||
Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack,
|
||||
getLog());
|
||||
try {
|
||||
LaunchScript launchScript = getLaunchScript();
|
||||
repackager.repackage(target, libraries, launchScript);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new MojoExecutionException(ex.getMessage(), ex);
|
||||
}
|
||||
updateArtifact(source, target, repackager.getBackupFile());
|
||||
}
|
||||
|
||||
private File getTargetFile() {
|
||||
String classifier = (this.classifier == null ? "" : this.classifier.trim());
|
||||
if (!classifier.isEmpty() && !classifier.startsWith("-")) {
|
||||
classifier = "-" + classifier;
|
||||
}
|
||||
if (!this.outputDirectory.exists()) {
|
||||
this.outputDirectory.mkdirs();
|
||||
}
|
||||
return new File(this.outputDirectory, this.finalName + classifier + "."
|
||||
+ this.project.getArtifact().getArtifactHandler().getExtension());
|
||||
}
|
||||
|
||||
private Repackager getRepackager(File source) {
|
||||
Repackager repackager = new Repackager(source, this.layoutFactory);
|
||||
repackager.addMainClassTimeoutWarningListener(
|
||||
new LoggingMainClassTimeoutWarningListener());
|
||||
repackager.setMainClass(this.mainClass);
|
||||
if (this.layout != null) {
|
||||
getLog().info("Layout: " + this.layout);
|
||||
repackager.setLayout(this.layout.layout());
|
||||
}
|
||||
return repackager;
|
||||
}
|
||||
|
||||
private ArtifactsFilter[] getAdditionalFilters() {
|
||||
List<ArtifactsFilter> filters = new ArrayList<>();
|
||||
if (this.excludeDevtools) {
|
||||
Exclude exclude = new Exclude();
|
||||
exclude.setGroupId("org.springframework.boot");
|
||||
exclude.setArtifactId("spring-boot-devtools");
|
||||
ExcludeFilter filter = new ExcludeFilter(exclude);
|
||||
filters.add(filter);
|
||||
}
|
||||
if (!this.includeSystemScope) {
|
||||
filters.add(new ScopeFilter(null, Artifact.SCOPE_SYSTEM));
|
||||
}
|
||||
return filters.toArray(new ArtifactsFilter[filters.size()]);
|
||||
}
|
||||
|
||||
private LaunchScript getLaunchScript() throws IOException {
|
||||
if (this.executable || this.embeddedLaunchScript != null) {
|
||||
return new DefaultLaunchScript(this.embeddedLaunchScript,
|
||||
buildLaunchScriptProperties());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Properties buildLaunchScriptProperties() {
|
||||
Properties properties = new Properties();
|
||||
if (this.embeddedLaunchScriptProperties != null) {
|
||||
properties.putAll(this.embeddedLaunchScriptProperties);
|
||||
}
|
||||
putIfMissing(properties, "initInfoProvides", this.project.getArtifactId());
|
||||
putIfMissing(properties, "initInfoShortDescription", this.project.getName(),
|
||||
this.project.getArtifactId());
|
||||
putIfMissing(properties, "initInfoDescription",
|
||||
removeLineBreaks(this.project.getDescription()), this.project.getName(),
|
||||
this.project.getArtifactId());
|
||||
return properties;
|
||||
}
|
||||
|
||||
private String removeLineBreaks(String description) {
|
||||
return (description == null ? null : description.replaceAll("\\s+", " "));
|
||||
}
|
||||
|
||||
private void putIfMissing(Properties properties, String key,
|
||||
String... valueCandidates) {
|
||||
if (!properties.containsKey(key)) {
|
||||
for (String candidate : valueCandidates) {
|
||||
if (candidate != null && !candidate.isEmpty()) {
|
||||
properties.put(key, candidate);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateArtifact(File source, File repackaged, File original) {
|
||||
if (this.attach) {
|
||||
attachArtifact(source, repackaged);
|
||||
}
|
||||
else if (source.equals(repackaged)) {
|
||||
this.project.getArtifact().setFile(original);
|
||||
getLog().info("Updating main artifact " + source + " to " + original);
|
||||
}
|
||||
}
|
||||
|
||||
private void attachArtifact(File source, File repackaged) {
|
||||
if (this.classifier != null) {
|
||||
getLog().info("Attaching archive: " + repackaged + ", with classifier: "
|
||||
+ this.classifier);
|
||||
this.projectHelper.attachArtifact(this.project, this.project.getPackaging(),
|
||||
this.classifier, repackaged);
|
||||
}
|
||||
else if (!source.equals(repackaged)) {
|
||||
this.project.getArtifact().setFile(repackaged);
|
||||
getLog().info("Replacing main artifact " + source + " to " + repackaged);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Archive layout types.
|
||||
*/
|
||||
public enum LayoutType {
|
||||
|
||||
/**
|
||||
* Jar Layout.
|
||||
*/
|
||||
JAR(new Layouts.Jar()),
|
||||
|
||||
/**
|
||||
* War Layout.
|
||||
*/
|
||||
WAR(new Layouts.War()),
|
||||
|
||||
/**
|
||||
* Zip Layout.
|
||||
*/
|
||||
ZIP(new Layouts.Expanded()),
|
||||
|
||||
/**
|
||||
* Dir Layout.
|
||||
*/
|
||||
DIR(new Layouts.Expanded()),
|
||||
|
||||
/**
|
||||
* No Layout.
|
||||
*/
|
||||
NONE(new Layouts.None());
|
||||
|
||||
private final Layout layout;
|
||||
|
||||
public Layout layout() {
|
||||
return this.layout;
|
||||
}
|
||||
|
||||
LayoutType(Layout layout) {
|
||||
this.layout = layout;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class LoggingMainClassTimeoutWarningListener
|
||||
implements MainClassTimeoutWarningListener {
|
||||
|
||||
@Override
|
||||
public void handleTimeoutWarning(long duration, String mainMethod) {
|
||||
getLog().warn("Searching for the main-class is taking some time, "
|
||||
+ "consider using the mainClass configuration " + "parameter");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.maven;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.codehaus.plexus.util.cli.CommandLineUtils;
|
||||
|
||||
/**
|
||||
* Parse and expose arguments specified in a single string.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class RunArguments {
|
||||
|
||||
private static final String[] NO_ARGS = {};
|
||||
|
||||
private final LinkedList<String> args = new LinkedList<>();
|
||||
|
||||
RunArguments(String arguments) {
|
||||
this(parseArgs(arguments));
|
||||
}
|
||||
|
||||
RunArguments(String[] args) {
|
||||
if (args != null) {
|
||||
Arrays.stream(args).filter(Objects::nonNull).forEach(this.args::add);
|
||||
}
|
||||
}
|
||||
|
||||
public LinkedList<String> getArgs() {
|
||||
return this.args;
|
||||
}
|
||||
|
||||
public String[] asArray() {
|
||||
return this.args.toArray(new String[this.args.size()]);
|
||||
}
|
||||
|
||||
private static String[] parseArgs(String arguments) {
|
||||
if (arguments == null || arguments.trim().isEmpty()) {
|
||||
return NO_ARGS;
|
||||
}
|
||||
try {
|
||||
arguments = arguments.replace('\n', ' ').replace('\t', ' ');
|
||||
return CommandLineUtils.translateCommandline(arguments);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(
|
||||
"Failed to parse arguments [" + arguments + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2012-2016 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.maven;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugins.annotations.Execute;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
import org.apache.maven.plugins.annotations.ResolutionScope;
|
||||
|
||||
import org.springframework.boot.loader.tools.JavaExecutable;
|
||||
import org.springframework.boot.loader.tools.RunProcess;
|
||||
|
||||
/**
|
||||
* Run an executable archive application.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Mojo(name = "run", requiresProject = true, defaultPhase = LifecyclePhase.VALIDATE, requiresDependencyResolution = ResolutionScope.TEST)
|
||||
@Execute(phase = LifecyclePhase.TEST_COMPILE)
|
||||
public class RunMojo extends AbstractRunMojo {
|
||||
|
||||
private static final int EXIT_CODE_SIGINT = 130;
|
||||
|
||||
private static final String RESTARTER_CLASS_LOCATION = "org/springframework/boot/devtools/restart/Restarter.class";
|
||||
|
||||
/**
|
||||
* Devtools presence flag to avoid checking for it several times per execution.
|
||||
*/
|
||||
private Boolean hasDevtools;
|
||||
|
||||
@Override
|
||||
protected boolean enableForkByDefault() {
|
||||
return super.enableForkByDefault() || hasDevtools();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logDisabledFork() {
|
||||
super.logDisabledFork();
|
||||
if (hasDevtools()) {
|
||||
getLog().warn("Fork mode disabled, devtools will be disabled");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runWithForkedJvm(File workingDirectory, List<String> args)
|
||||
throws MojoExecutionException {
|
||||
try {
|
||||
RunProcess runProcess = new RunProcess(workingDirectory,
|
||||
new JavaExecutable().toString());
|
||||
Runtime.getRuntime()
|
||||
.addShutdownHook(new Thread(new RunProcessKiller(runProcess)));
|
||||
int exitCode = runProcess.run(true, args.toArray(new String[args.size()]));
|
||||
if (exitCode == 0 || exitCode == EXIT_CODE_SIGINT) {
|
||||
return;
|
||||
}
|
||||
throw new MojoExecutionException(
|
||||
"Application finished with exit code: " + exitCode);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MojoExecutionException("Could not exec java", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runWithMavenJvm(String startClassName, String... arguments)
|
||||
throws MojoExecutionException {
|
||||
IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(startClassName);
|
||||
Thread launchThread = new Thread(threadGroup,
|
||||
new LaunchRunner(startClassName, arguments), "main");
|
||||
launchThread.setContextClassLoader(new URLClassLoader(getClassPathUrls()));
|
||||
launchThread.start();
|
||||
join(threadGroup);
|
||||
threadGroup.rethrowUncaughtException();
|
||||
}
|
||||
|
||||
private void join(ThreadGroup threadGroup) {
|
||||
boolean hasNonDaemonThreads;
|
||||
do {
|
||||
hasNonDaemonThreads = false;
|
||||
Thread[] threads = new Thread[threadGroup.activeCount()];
|
||||
threadGroup.enumerate(threads);
|
||||
for (Thread thread : threads) {
|
||||
if (thread != null && !thread.isDaemon()) {
|
||||
try {
|
||||
hasNonDaemonThreads = true;
|
||||
thread.join();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (hasNonDaemonThreads);
|
||||
}
|
||||
|
||||
private boolean hasDevtools() {
|
||||
if (this.hasDevtools == null) {
|
||||
this.hasDevtools = checkForDevtools();
|
||||
}
|
||||
return this.hasDevtools;
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
private boolean checkForDevtools() {
|
||||
try {
|
||||
URL[] urls = getClassPathUrls();
|
||||
URLClassLoader classLoader = new URLClassLoader(urls);
|
||||
return (classLoader.findResource(RESTARTER_CLASS_LOCATION) != null);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RunProcessKiller implements Runnable {
|
||||
|
||||
private final RunProcess runProcess;
|
||||
|
||||
private RunProcessKiller(RunProcess runProcess) {
|
||||
this.runProcess = runProcess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.runProcess.kill();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.maven;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.management.AttributeNotFoundException;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.ReflectionException;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
|
||||
/**
|
||||
* A JMX client for the {@code SpringApplicationAdmin} MBean. Permits to obtain
|
||||
* information about a given Spring application.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class SpringApplicationAdminClient {
|
||||
|
||||
// Note: see SpringApplicationAdminJmxAutoConfiguration
|
||||
static final String DEFAULT_OBJECT_NAME = "org.springframework.boot:type=Admin,name=SpringApplication";
|
||||
|
||||
private final MBeanServerConnection connection;
|
||||
|
||||
private final ObjectName objectName;
|
||||
|
||||
SpringApplicationAdminClient(MBeanServerConnection connection, String jmxName) {
|
||||
this.connection = connection;
|
||||
this.objectName = toObjectName(jmxName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the spring application managed by this instance is ready. Returns
|
||||
* {@code false} if the mbean is not yet deployed so this method should be repeatedly
|
||||
* called until a timeout is reached.
|
||||
* @return {@code true} if the application is ready to service requests
|
||||
* @throws MojoExecutionException if the JMX service could not be contacted
|
||||
*/
|
||||
public boolean isReady() throws MojoExecutionException {
|
||||
try {
|
||||
return (Boolean) this.connection.getAttribute(this.objectName, "Ready");
|
||||
}
|
||||
catch (InstanceNotFoundException ex) {
|
||||
return false; // Instance not available yet
|
||||
}
|
||||
catch (AttributeNotFoundException ex) {
|
||||
throw new IllegalStateException("Unexpected: attribute 'Ready' not available",
|
||||
ex);
|
||||
}
|
||||
catch (ReflectionException ex) {
|
||||
throw new MojoExecutionException("Failed to retrieve Ready attribute",
|
||||
ex.getCause());
|
||||
}
|
||||
catch (MBeanException | IOException ex) {
|
||||
throw new MojoExecutionException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the application managed by this instance.
|
||||
* @throws MojoExecutionException if the JMX service could not be contacted
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws InstanceNotFoundException if the lifecycle mbean cannot be found
|
||||
*/
|
||||
public void stop()
|
||||
throws MojoExecutionException, IOException, InstanceNotFoundException {
|
||||
try {
|
||||
this.connection.invoke(this.objectName, "shutdown", null, null);
|
||||
}
|
||||
catch (ReflectionException ex) {
|
||||
throw new MojoExecutionException("Shutdown failed", ex.getCause());
|
||||
}
|
||||
catch (MBeanException ex) {
|
||||
throw new MojoExecutionException("Could not invoke shutdown operation", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectName toObjectName(String name) {
|
||||
try {
|
||||
return new ObjectName(name);
|
||||
}
|
||||
catch (MalformedObjectNameException ex) {
|
||||
throw new IllegalArgumentException("Invalid jmx name '" + name + "'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a connector for an {@link javax.management.MBeanServer} exposed on the
|
||||
* current machine and the current port. Security should be disabled.
|
||||
* @param port the port on which the mbean server is exposed
|
||||
* @return a connection
|
||||
* @throws IOException if the connection to that server failed
|
||||
*/
|
||||
public static JMXConnector connect(int port) throws IOException {
|
||||
String url = "service:jmx:rmi:///jndi/rmi://127.0.0.1:" + port + "/jmxrmi";
|
||||
JMXServiceURL serviceUrl = new JMXServiceURL(url);
|
||||
return JMXConnectorFactory.connect(serviceUrl, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* 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.maven;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.net.ConnectException;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.ReflectionException;
|
||||
import javax.management.remote.JMXConnector;
|
||||
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.maven.plugins.annotations.ResolutionScope;
|
||||
|
||||
import org.springframework.boot.loader.tools.JavaExecutable;
|
||||
import org.springframework.boot.loader.tools.RunProcess;
|
||||
|
||||
/**
|
||||
* Start a spring application. Contrary to the {@code run} goal, this does not block and
|
||||
* allows other goal to operate on the application. This goal is typically used in
|
||||
* integration test scenario where the application is started before a test suite and
|
||||
* stopped after.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
* @see StopMojo
|
||||
*/
|
||||
@Mojo(name = "start", requiresProject = true, defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST, requiresDependencyResolution = ResolutionScope.TEST)
|
||||
public class StartMojo extends AbstractRunMojo {
|
||||
|
||||
private static final String ENABLE_MBEAN_PROPERTY = "--spring.application.admin.enabled=true";
|
||||
|
||||
private static final String JMX_NAME_PROPERTY_PREFIX = "--spring.application.admin.jmx-name=";
|
||||
|
||||
/**
|
||||
* The JMX name of the automatically deployed MBean managing the lifecycle of the
|
||||
* spring application.
|
||||
*/
|
||||
@Parameter
|
||||
private String jmxName = SpringApplicationAdminClient.DEFAULT_OBJECT_NAME;
|
||||
|
||||
/**
|
||||
* The port to use to expose the platform MBeanServer if the application needs to be
|
||||
* forked.
|
||||
*/
|
||||
@Parameter
|
||||
private int jmxPort = 9001;
|
||||
|
||||
/**
|
||||
* The number of milli-seconds to wait between each attempt to check if the spring
|
||||
* application is ready.
|
||||
*/
|
||||
@Parameter
|
||||
private long wait = 500;
|
||||
|
||||
/**
|
||||
* The maximum number of attempts to check if the spring application is ready.
|
||||
* Combined with the "wait" argument, this gives a global timeout value (30 sec by
|
||||
* default)
|
||||
*/
|
||||
@Parameter
|
||||
private int maxAttempts = 60;
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
@Override
|
||||
protected void runWithForkedJvm(File workingDirectory, List<String> args)
|
||||
throws MojoExecutionException, MojoFailureException {
|
||||
RunProcess runProcess = runProcess(workingDirectory, args);
|
||||
try {
|
||||
waitForSpringApplication();
|
||||
}
|
||||
catch (MojoExecutionException | MojoFailureException ex) {
|
||||
runProcess.kill();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private RunProcess runProcess(File workingDirectory, List<String> args)
|
||||
throws MojoExecutionException {
|
||||
try {
|
||||
RunProcess runProcess = new RunProcess(workingDirectory,
|
||||
new JavaExecutable().toString());
|
||||
runProcess.run(false, args.toArray(new String[args.size()]));
|
||||
return runProcess;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MojoExecutionException("Could not exec java", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RunArguments resolveApplicationArguments() {
|
||||
RunArguments applicationArguments = super.resolveApplicationArguments();
|
||||
applicationArguments.getArgs().addLast(ENABLE_MBEAN_PROPERTY);
|
||||
if (isFork()) {
|
||||
applicationArguments.getArgs()
|
||||
.addLast(JMX_NAME_PROPERTY_PREFIX + this.jmxName);
|
||||
}
|
||||
return applicationArguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RunArguments resolveJvmArguments() {
|
||||
RunArguments jvmArguments = super.resolveJvmArguments();
|
||||
if (isFork()) {
|
||||
List<String> remoteJmxArguments = new ArrayList<>();
|
||||
remoteJmxArguments.add("-Dcom.sun.management.jmxremote");
|
||||
remoteJmxArguments.add("-Dcom.sun.management.jmxremote.port=" + this.jmxPort);
|
||||
remoteJmxArguments.add("-Dcom.sun.management.jmxremote.authenticate=false");
|
||||
remoteJmxArguments.add("-Dcom.sun.management.jmxremote.ssl=false");
|
||||
jvmArguments.getArgs().addAll(remoteJmxArguments);
|
||||
}
|
||||
return jvmArguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runWithMavenJvm(String startClassName, String... arguments)
|
||||
throws MojoExecutionException {
|
||||
IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(startClassName);
|
||||
Thread launchThread = new Thread(threadGroup,
|
||||
new LaunchRunner(startClassName, arguments), startClassName + ".main()");
|
||||
launchThread.setContextClassLoader(new URLClassLoader(getClassPathUrls()));
|
||||
launchThread.start();
|
||||
waitForSpringApplication(this.wait, this.maxAttempts);
|
||||
}
|
||||
|
||||
private void waitForSpringApplication(long wait, int maxAttempts)
|
||||
throws MojoExecutionException {
|
||||
SpringApplicationAdminClient client = new SpringApplicationAdminClient(
|
||||
ManagementFactory.getPlatformMBeanServer(), this.jmxName);
|
||||
getLog().debug("Waiting for spring application to start...");
|
||||
for (int i = 0; i < maxAttempts; i++) {
|
||||
if (client.isReady()) {
|
||||
return;
|
||||
}
|
||||
String message = "Spring application is not ready yet, waiting " + wait
|
||||
+ "ms (attempt " + (i + 1) + ")";
|
||||
getLog().debug(message);
|
||||
synchronized (this.lock) {
|
||||
try {
|
||||
this.lock.wait(wait);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException(
|
||||
"Interrupted while waiting for Spring Boot app to start.");
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new MojoExecutionException(
|
||||
"Spring application did not start before the configured timeout ("
|
||||
+ (wait * maxAttempts) + "ms");
|
||||
}
|
||||
|
||||
private void waitForSpringApplication()
|
||||
throws MojoFailureException, MojoExecutionException {
|
||||
try {
|
||||
if (isFork()) {
|
||||
waitForForkedSpringApplication();
|
||||
}
|
||||
else {
|
||||
doWaitForSpringApplication(ManagementFactory.getPlatformMBeanServer());
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new MojoFailureException("Could not contact Spring Boot application",
|
||||
ex);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MojoExecutionException(
|
||||
"Could not figure out if the application has started", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void waitForForkedSpringApplication()
|
||||
throws IOException, MojoFailureException, MojoExecutionException {
|
||||
try {
|
||||
getLog().debug("Connecting to local MBeanServer at port " + this.jmxPort);
|
||||
try (JMXConnector connector = execute(this.wait, this.maxAttempts,
|
||||
new CreateJmxConnector(this.jmxPort))) {
|
||||
if (connector == null) {
|
||||
throw new MojoExecutionException(
|
||||
"JMX MBean server was not reachable before the configured "
|
||||
+ "timeout (" + (this.wait * this.maxAttempts)
|
||||
+ "ms");
|
||||
}
|
||||
getLog().debug("Connected to local MBeanServer at port " + this.jmxPort);
|
||||
MBeanServerConnection connection = connector.getMBeanServerConnection();
|
||||
doWaitForSpringApplication(connection);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MojoExecutionException(
|
||||
"Failed to connect to MBean server at port " + this.jmxPort, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void doWaitForSpringApplication(MBeanServerConnection connection)
|
||||
throws IOException, MojoExecutionException, MojoFailureException {
|
||||
final SpringApplicationAdminClient client = new SpringApplicationAdminClient(
|
||||
connection, this.jmxName);
|
||||
try {
|
||||
execute(this.wait, this.maxAttempts, () -> (client.isReady() ? true : null));
|
||||
}
|
||||
catch (ReflectionException ex) {
|
||||
throw new MojoExecutionException("Unable to retrieve 'ready' attribute",
|
||||
ex.getCause());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new MojoFailureException("Could not invoke shutdown operation", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a task, retrying it on failure.
|
||||
* @param <T> the result type
|
||||
* @param wait the wait time
|
||||
* @param maxAttempts the maximum number of attempts
|
||||
* @param callback the task to execute (possibly multiple times). The callback should
|
||||
* return {@code null} to indicate that another attempt should be made
|
||||
* @return the result
|
||||
* @throws Exception in case of execution errors
|
||||
*/
|
||||
public <T> T execute(long wait, int maxAttempts, Callable<T> callback)
|
||||
throws Exception {
|
||||
getLog().debug("Waiting for spring application to start...");
|
||||
for (int i = 0; i < maxAttempts; i++) {
|
||||
T result = callback.call();
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
String message = "Spring application is not ready yet, waiting " + wait
|
||||
+ "ms (attempt " + (i + 1) + ")";
|
||||
getLog().debug(message);
|
||||
synchronized (this.lock) {
|
||||
try {
|
||||
this.lock.wait(wait);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException(
|
||||
"Interrupted while waiting for Spring Boot app to start.");
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new MojoExecutionException(
|
||||
"Spring application did not start before the configured " + "timeout ("
|
||||
+ (wait * maxAttempts) + "ms");
|
||||
}
|
||||
|
||||
private class CreateJmxConnector implements Callable<JMXConnector> {
|
||||
|
||||
private final int port;
|
||||
|
||||
CreateJmxConnector(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JMXConnector call() throws Exception {
|
||||
try {
|
||||
return SpringApplicationAdminClient.connect(this.port);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (hasCauseWithType(ex, ConnectException.class)) {
|
||||
String message = "MBean server at port " + this.port
|
||||
+ " is not up yet...";
|
||||
getLog().debug(message);
|
||||
return null;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasCauseWithType(Throwable t, Class<? extends Exception> type) {
|
||||
return type.isAssignableFrom(t.getClass())
|
||||
|| t.getCause() != null && hasCauseWithType(t.getCause(), type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.maven;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.remote.JMXConnector;
|
||||
|
||||
import org.apache.maven.plugin.AbstractMojo;
|
||||
import org.apache.maven.plugin.MojoExecutionException;
|
||||
import org.apache.maven.plugin.MojoFailureException;
|
||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
import org.apache.maven.plugins.annotations.Parameter;
|
||||
import org.apache.maven.project.MavenProject;
|
||||
|
||||
/**
|
||||
* Stop a spring application that has been started by the "start" goal. Typically invoked
|
||||
* once a test suite has completed.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Mojo(name = "stop", requiresProject = true, defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST)
|
||||
public class StopMojo extends AbstractMojo {
|
||||
|
||||
/**
|
||||
* The Maven project.
|
||||
* @since 1.4.1
|
||||
*/
|
||||
@Parameter(defaultValue = "${project}", readonly = true, required = true)
|
||||
private MavenProject project;
|
||||
|
||||
/**
|
||||
* Flag to indicate if process to stop was forked. By default, the value is inherited
|
||||
* from the {@link MavenProject}. If it is set, it must match the value used to
|
||||
* {@link StartMojo start} the process.
|
||||
* @since 1.3
|
||||
*/
|
||||
@Parameter(property = "spring-boot.stop.fork")
|
||||
private Boolean fork;
|
||||
|
||||
/**
|
||||
* The JMX name of the automatically deployed MBean managing the lifecycle of the
|
||||
* application.
|
||||
*/
|
||||
@Parameter
|
||||
private String jmxName = SpringApplicationAdminClient.DEFAULT_OBJECT_NAME;
|
||||
|
||||
/**
|
||||
* The port to use to lookup the platform MBeanServer if the application has been
|
||||
* forked.
|
||||
*/
|
||||
@Parameter
|
||||
private int jmxPort = 9001;
|
||||
|
||||
/**
|
||||
* Skip the execution.
|
||||
* @since 1.3.2
|
||||
*/
|
||||
@Parameter(property = "spring-boot.stop.skip", defaultValue = "false")
|
||||
private boolean skip;
|
||||
|
||||
@Override
|
||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||
if (this.skip) {
|
||||
getLog().debug("skipping stop as per configuration.");
|
||||
return;
|
||||
}
|
||||
getLog().info("Stopping application...");
|
||||
try {
|
||||
if (isForked()) {
|
||||
stopForkedProcess();
|
||||
}
|
||||
else {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// The response won't be received as the server has died - ignoring
|
||||
getLog().debug("Service is not reachable anymore (" + ex.getMessage() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isForked() {
|
||||
if (this.fork != null) {
|
||||
return this.fork;
|
||||
}
|
||||
String property = this.project.getProperties()
|
||||
.getProperty("_spring.boot.fork.enabled");
|
||||
return Boolean.valueOf(property);
|
||||
}
|
||||
|
||||
private void stopForkedProcess()
|
||||
throws IOException, MojoFailureException, MojoExecutionException {
|
||||
try (JMXConnector connector = SpringApplicationAdminClient
|
||||
.connect(this.jmxPort)) {
|
||||
MBeanServerConnection connection = connector.getMBeanServerConnection();
|
||||
doStop(connection);
|
||||
}
|
||||
}
|
||||
|
||||
private void stop() throws IOException, MojoFailureException, MojoExecutionException {
|
||||
doStop(ManagementFactory.getPlatformMBeanServer());
|
||||
}
|
||||
|
||||
private void doStop(MBeanServerConnection connection)
|
||||
throws IOException, MojoExecutionException {
|
||||
try {
|
||||
new SpringApplicationAdminClient(connection, this.jmxName).stop();
|
||||
}
|
||||
catch (InstanceNotFoundException ex) {
|
||||
throw new MojoExecutionException(
|
||||
"Spring application lifecycle JMX bean not found (fork is " + ""
|
||||
+ this.fork + "). Could not stop application gracefully",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<goals>
|
||||
<goal>build-info</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<execute>
|
||||
<runOnIncremental>true</runOnIncremental>
|
||||
<runOnConfiguration>false</runOnConfiguration>
|
||||
</execute>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
Reference in New Issue
Block a user