|
|
|
@ -14,7 +14,7 @@ |
|
|
|
* limitations under the License. |
|
|
|
* limitations under the License. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
package org.springframework.boot.cli.command.jar; |
|
|
|
package org.springframework.boot.cli.command.archive; |
|
|
|
|
|
|
|
|
|
|
|
import java.io.File; |
|
|
|
import java.io.File; |
|
|
|
import java.io.FileInputStream; |
|
|
|
import java.io.FileInputStream; |
|
|
|
@ -38,10 +38,12 @@ import org.codehaus.groovy.ast.expr.ConstantExpression; |
|
|
|
import org.codehaus.groovy.control.SourceUnit; |
|
|
|
import org.codehaus.groovy.control.SourceUnit; |
|
|
|
import org.codehaus.groovy.transform.ASTTransformation; |
|
|
|
import org.codehaus.groovy.transform.ASTTransformation; |
|
|
|
import org.springframework.boot.cli.app.SpringApplicationLauncher; |
|
|
|
import org.springframework.boot.cli.app.SpringApplicationLauncher; |
|
|
|
|
|
|
|
import org.springframework.boot.cli.archive.PackagedSpringApplicationLauncher; |
|
|
|
import org.springframework.boot.cli.command.Command; |
|
|
|
import org.springframework.boot.cli.command.Command; |
|
|
|
import org.springframework.boot.cli.command.OptionParsingCommand; |
|
|
|
import org.springframework.boot.cli.command.OptionParsingCommand; |
|
|
|
import org.springframework.boot.cli.command.jar.ResourceMatcher.MatchedResource; |
|
|
|
import org.springframework.boot.cli.command.archive.ResourceMatcher.MatchedResource; |
|
|
|
import org.springframework.boot.cli.command.options.CompilerOptionHandler; |
|
|
|
import org.springframework.boot.cli.command.options.CompilerOptionHandler; |
|
|
|
|
|
|
|
import org.springframework.boot.cli.command.options.OptionHandler; |
|
|
|
import org.springframework.boot.cli.command.options.OptionSetGroovyCompilerConfiguration; |
|
|
|
import org.springframework.boot.cli.command.options.OptionSetGroovyCompilerConfiguration; |
|
|
|
import org.springframework.boot.cli.command.options.SourceOptions; |
|
|
|
import org.springframework.boot.cli.command.options.SourceOptions; |
|
|
|
import org.springframework.boot.cli.command.status.ExitStatus; |
|
|
|
import org.springframework.boot.cli.command.status.ExitStatus; |
|
|
|
@ -49,8 +51,8 @@ import org.springframework.boot.cli.compiler.GroovyCompiler; |
|
|
|
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
|
|
|
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration; |
|
|
|
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; |
|
|
|
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory; |
|
|
|
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; |
|
|
|
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; |
|
|
|
import org.springframework.boot.cli.jar.PackagedSpringApplicationLauncher; |
|
|
|
|
|
|
|
import org.springframework.boot.loader.tools.JarWriter; |
|
|
|
import org.springframework.boot.loader.tools.JarWriter; |
|
|
|
|
|
|
|
import org.springframework.boot.loader.tools.Layout; |
|
|
|
import org.springframework.boot.loader.tools.Libraries; |
|
|
|
import org.springframework.boot.loader.tools.Libraries; |
|
|
|
import org.springframework.boot.loader.tools.Library; |
|
|
|
import org.springframework.boot.loader.tools.Library; |
|
|
|
import org.springframework.boot.loader.tools.LibraryCallback; |
|
|
|
import org.springframework.boot.loader.tools.LibraryCallback; |
|
|
|
@ -65,51 +67,66 @@ import joptsimple.OptionSet; |
|
|
|
import joptsimple.OptionSpec; |
|
|
|
import joptsimple.OptionSpec; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* {@link Command} to create a self-contained executable jar file from a CLI application. |
|
|
|
* Abstract {@link Command} to create a self-contained executable archive file from a CLI |
|
|
|
|
|
|
|
* application. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Andy Wilkinson |
|
|
|
* @author Andy Wilkinson |
|
|
|
* @author Phillip Webb |
|
|
|
* @author Phillip Webb |
|
|
|
|
|
|
|
* @author Andrey Stolyarov |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class JarCommand extends OptionParsingCommand { |
|
|
|
abstract class ArchiveCommand extends OptionParsingCommand { |
|
|
|
|
|
|
|
|
|
|
|
public JarCommand() { |
|
|
|
protected ArchiveCommand(String name, String description, |
|
|
|
super("jar", |
|
|
|
OptionHandler optionHandler) { |
|
|
|
"Create a self-contained " |
|
|
|
super(name, description, optionHandler); |
|
|
|
+ "executable jar file from a Spring Groovy script", |
|
|
|
|
|
|
|
new JarOptionHandler()); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public String getUsageHelp() { |
|
|
|
public String getUsageHelp() { |
|
|
|
return "[options] <jar-name> <files>"; |
|
|
|
return "[options] <" + getName() + "-name> <files>"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static final class JarOptionHandler extends CompilerOptionHandler { |
|
|
|
/** |
|
|
|
|
|
|
|
* Abstract base {@link CompilerOptionHandler} for archive commands. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected abstract static class ArchiveOptionHandler extends CompilerOptionHandler { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final String type; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final Layout layout; |
|
|
|
|
|
|
|
|
|
|
|
private OptionSpec<String> includeOption; |
|
|
|
private OptionSpec<String> includeOption; |
|
|
|
|
|
|
|
|
|
|
|
private OptionSpec<String> excludeOption; |
|
|
|
private OptionSpec<String> excludeOption; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ArchiveOptionHandler(String type, Layout layout) { |
|
|
|
|
|
|
|
this.type = type; |
|
|
|
|
|
|
|
this.layout = layout; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
protected void doOptions() { |
|
|
|
protected void doOptions() { |
|
|
|
this.includeOption = option("include", |
|
|
|
this.includeOption = option("include", |
|
|
|
"Pattern applied to directories on the classpath to find files to include in the resulting jar") |
|
|
|
"Pattern applied to directories on the classpath to find files to " |
|
|
|
.withRequiredArg().withValuesSeparatedBy(",").defaultsTo(""); |
|
|
|
+ "include in the resulting ").withRequiredArg() |
|
|
|
|
|
|
|
.withValuesSeparatedBy(",").defaultsTo(""); |
|
|
|
this.excludeOption = option("exclude", |
|
|
|
this.excludeOption = option("exclude", |
|
|
|
"Pattern applied to directories on the classpath to find files to exclude from the resulting jar") |
|
|
|
"Pattern applied to directories on the classpath to find files to " |
|
|
|
.withRequiredArg().withValuesSeparatedBy(",").defaultsTo(""); |
|
|
|
+ "exclude from the resulting " + this.type).withRequiredArg() |
|
|
|
|
|
|
|
.withValuesSeparatedBy(",").defaultsTo(""); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
protected ExitStatus run(OptionSet options) throws Exception { |
|
|
|
protected ExitStatus run(OptionSet options) throws Exception { |
|
|
|
List<?> nonOptionArguments = new ArrayList<Object>( |
|
|
|
List<?> nonOptionArguments = new ArrayList<Object>( |
|
|
|
options.nonOptionArguments()); |
|
|
|
options.nonOptionArguments()); |
|
|
|
Assert.isTrue(nonOptionArguments.size() >= 2, |
|
|
|
Assert.isTrue(nonOptionArguments.size() >= 2, "The name of the resulting " |
|
|
|
"The name of the resulting jar and at least one source file must be specified"); |
|
|
|
+ this.type + " and at least one source file must be specified"); |
|
|
|
|
|
|
|
|
|
|
|
File output = new File((String) nonOptionArguments.remove(0)); |
|
|
|
File output = new File((String) nonOptionArguments.remove(0)); |
|
|
|
Assert.isTrue(output.getName().toLowerCase().endsWith(".jar"), |
|
|
|
Assert.isTrue(output.getName().toLowerCase().endsWith("." + this.type), |
|
|
|
"The output '" + output + "' is not a JAR file."); |
|
|
|
"The output '" + output + "' is not a " + this.type.toUpperCase() |
|
|
|
|
|
|
|
+ " file."); |
|
|
|
deleteIfExists(output); |
|
|
|
deleteIfExists(output); |
|
|
|
|
|
|
|
|
|
|
|
GroovyCompiler compiler = createCompiler(options); |
|
|
|
GroovyCompiler compiler = createCompiler(options); |
|
|
|
@ -196,7 +213,7 @@ public class JarCommand extends OptionParsingCommand { |
|
|
|
List<Library> libraries = new ArrayList<Library>(); |
|
|
|
List<Library> libraries = new ArrayList<Library>(); |
|
|
|
for (URL dependency : dependencies) { |
|
|
|
for (URL dependency : dependencies) { |
|
|
|
File file = new File(dependency.toURI()); |
|
|
|
File file = new File(dependency.toURI()); |
|
|
|
libraries.add(new Library(file, LibraryScope.COMPILE)); |
|
|
|
libraries.add(new Library(file, getLibraryScope(file))); |
|
|
|
} |
|
|
|
} |
|
|
|
return libraries; |
|
|
|
return libraries; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -220,7 +237,7 @@ public class JarCommand extends OptionParsingCommand { |
|
|
|
return builder.toString(); |
|
|
|
return builder.toString(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void addCliClasses(JarWriter writer) throws IOException { |
|
|
|
protected void addCliClasses(JarWriter writer) throws IOException { |
|
|
|
addClass(writer, PackagedSpringApplicationLauncher.class); |
|
|
|
addClass(writer, PackagedSpringApplicationLauncher.class); |
|
|
|
addClass(writer, SpringApplicationLauncher.class); |
|
|
|
addClass(writer, SpringApplicationLauncher.class); |
|
|
|
Resource[] resources = new PathMatchingResourcePatternResolver() |
|
|
|
Resource[] resources = new PathMatchingResourcePatternResolver() |
|
|
|
@ -232,10 +249,19 @@ public class JarCommand extends OptionParsingCommand { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void addClass(JarWriter writer, Class<?> sourceClass) throws IOException { |
|
|
|
protected final void addClass(JarWriter writer, Class<?> sourceClass) |
|
|
|
String name = sourceClass.getName().replace(".", "/") + ".class"; |
|
|
|
throws IOException { |
|
|
|
InputStream stream = sourceClass.getResourceAsStream("/" + name); |
|
|
|
addClass(writer, sourceClass.getClassLoader(), sourceClass.getName()); |
|
|
|
writer.writeEntry(name, stream); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected final void addClass(JarWriter writer, ClassLoader classLoader, |
|
|
|
|
|
|
|
String sourceClass) throws IOException { |
|
|
|
|
|
|
|
if (classLoader == null) { |
|
|
|
|
|
|
|
classLoader = Thread.currentThread().getContextClassLoader(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
String name = sourceClass.replace(".", "/") + ".class"; |
|
|
|
|
|
|
|
InputStream stream = classLoader.getResourceAsStream(name); |
|
|
|
|
|
|
|
writer.writeEntry(this.layout.getClassesLocation() + name, stream); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void addResource(JarWriter writer, Resource resource, String name) |
|
|
|
private void addResource(JarWriter writer, Resource resource, String name) |
|
|
|
@ -259,6 +285,8 @@ public class JarCommand extends OptionParsingCommand { |
|
|
|
return libraries; |
|
|
|
return libraries; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected abstract LibraryScope getLibraryScope(File file); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |