Remove rarely used commands from the CLI

Closes gh-32263
This commit is contained in:
Andy Wilkinson
2022-09-08 20:49:03 +01:00
parent e112657e1a
commit 0555dda63d
170 changed files with 68 additions and 12335 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2022 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.
@@ -48,7 +48,7 @@ class CommandLineIT {
Invocation cli = this.cli.invoke("hint");
assertThat(cli.await()).isEqualTo(0);
assertThat(cli.getErrorOutput()).isEmpty();
assertThat(cli.getStandardOutputLines()).hasSize(11);
assertThat(cli.getStandardOutputLines()).hasSize(5);
}
@Test

View File

@@ -1,150 +0,0 @@
/*
* Copyright 2012-2020 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
*
* https://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.cli;
import java.io.File;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.boot.cli.command.archive.JarCommand;
import org.springframework.boot.cli.infrastructure.CommandLineInvoker;
import org.springframework.boot.cli.infrastructure.CommandLineInvoker.Invocation;
import org.springframework.boot.loader.tools.JavaExecutable;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration test for {@link JarCommand}.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
*/
class JarCommandIT {
private static final boolean JAVA_9_OR_LATER = isClassPresent("java.security.cert.URICertStoreParameters");
private CommandLineInvoker cli;
private File tempDir;
@BeforeEach
void setup(@TempDir File tempDir) {
this.cli = new CommandLineInvoker(new File("src/intTest/resources/jar-command"), tempDir);
this.tempDir = tempDir;
}
@Test
void noArguments() throws Exception {
Invocation invocation = this.cli.invoke("jar");
invocation.await();
assertThat(invocation.getStandardOutput()).isEqualTo("");
assertThat(invocation.getErrorOutput())
.contains("The name of the resulting jar and at least one source file must be specified");
}
@Test
void noSources() throws Exception {
Invocation invocation = this.cli.invoke("jar", "test-app.jar");
invocation.await();
assertThat(invocation.getStandardOutput()).isEqualTo("");
assertThat(invocation.getErrorOutput())
.contains("The name of the resulting jar and at least one source file must be specified");
}
@Test
void jarCreationWithGrabResolver() throws Exception {
File jar = new File(this.tempDir, "test-app.jar");
Invocation invocation = this.cli.invoke("run", jar.getAbsolutePath(), "bad.groovy");
invocation.await();
if (!JAVA_9_OR_LATER) {
assertThat(invocation.getErrorOutput()).isEqualTo("");
}
invocation = this.cli.invoke("jar", jar.getAbsolutePath(), "bad.groovy");
invocation.await();
if (!JAVA_9_OR_LATER) {
assertThat(invocation.getErrorOutput()).isEmpty();
}
assertThat(jar).exists();
Process process = new JavaExecutable().processBuilder("-jar", jar.getAbsolutePath()).start();
invocation = new Invocation(process);
invocation.await();
if (!JAVA_9_OR_LATER) {
assertThat(invocation.getErrorOutput()).isEqualTo("");
}
}
@Test
void jarCreation() throws Exception {
File jar = new File(this.tempDir, "test-app.jar");
Invocation invocation = this.cli.invoke("jar", jar.getAbsolutePath(), "jar.groovy");
invocation.await();
if (!JAVA_9_OR_LATER) {
assertThat(invocation.getErrorOutput()).isEmpty();
}
assertThat(jar).exists();
Process process = new JavaExecutable().processBuilder("-jar", jar.getAbsolutePath()).start();
invocation = new Invocation(process);
invocation.await();
if (!JAVA_9_OR_LATER) {
assertThat(invocation.getErrorOutput()).isEqualTo("");
}
assertThat(invocation.getStandardOutput()).contains("Hello World!")
.contains("/BOOT-INF/classes!/public/public.txt").contains("/BOOT-INF/classes!/resources/resource.txt")
.contains("/BOOT-INF/classes!/static/static.txt").contains("/BOOT-INF/classes!/templates/template.txt")
.contains("/BOOT-INF/classes!/root.properties").contains("Goodbye Mama");
}
@Test
void jarCreationWithIncludes() throws Exception {
File jar = new File(this.tempDir, "test-app.jar");
Invocation invocation = this.cli.invoke("jar", jar.getAbsolutePath(), "--include", "-public/**,-resources/**",
"jar.groovy");
invocation.await();
if (!JAVA_9_OR_LATER) {
assertThat(invocation.getErrorOutput()).isEmpty();
}
assertThat(jar).exists();
Process process = new JavaExecutable().processBuilder("-jar", jar.getAbsolutePath()).start();
invocation = new Invocation(process);
invocation.await();
if (!JAVA_9_OR_LATER) {
assertThat(invocation.getErrorOutput()).isEqualTo("");
}
assertThat(invocation.getStandardOutput()).contains("Hello World!").doesNotContain("/public/public.txt")
.doesNotContain("/resources/resource.txt").contains("/static/static.txt")
.contains("/templates/template.txt").contains("Goodbye Mama");
}
private static boolean isClassPresent(String name) {
try {
Class.forName(name);
return true;
}
catch (Exception ex) {
return false;
}
}
}

View File

@@ -1,66 +0,0 @@
/*
* Copyright 2012-2020 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
*
* https://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.cli;
import java.io.File;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.boot.cli.command.archive.WarCommand;
import org.springframework.boot.cli.infrastructure.CommandLineInvoker;
import org.springframework.boot.cli.infrastructure.CommandLineInvoker.Invocation;
import org.springframework.boot.loader.tools.JavaExecutable;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration test for {@link WarCommand}.
*
* @author Andrey Stolyarov
* @author Henri Kerola
*/
class WarCommandIT {
private CommandLineInvoker cli;
private File tempDir;
@BeforeEach
void setup(@TempDir File tempDir) {
this.cli = new CommandLineInvoker(new File("src/intTest/resources/war-command"), tempDir);
this.tempDir = tempDir;
}
@Test
void warCreation() throws Exception {
File war = new File(this.tempDir, "test-app.war");
Invocation invocation = this.cli.invoke("war", war.getAbsolutePath(), "war.groovy");
invocation.await();
assertThat(war.exists()).isTrue();
Process process = new JavaExecutable().processBuilder("-jar", war.getAbsolutePath(), "--server.port=0").start();
invocation = new Invocation(process);
invocation.await();
assertThat(invocation.getOutput()).contains("onStart error");
assertThat(invocation.getOutput()).contains("Tomcat started");
assertThat(invocation.getOutput()).contains("/WEB-INF/lib-provided/tomcat-embed-core");
assertThat(invocation.getOutput()).contains("WEB-INF/classes!/root.properties");
process.destroy();
}
}

View File

@@ -1,6 +0,0 @@
@GrabResolver(name='clojars.org', root='https://clojars.org/repo')
@Grab('redis.embedded:embedded-redis:0.2')
@Component
class EmbeddedRedis {
}

View File

@@ -1,27 +0,0 @@
package org.test
@EnableGroovyTemplates
@Component
class Example implements CommandLineRunner {
@Autowired
private MyService myService
void run(String... args) {
println "Hello ${this.myService.sayWorld()}"
println getClass().getResource('/public/public.txt')
println getClass().getResource('/resources/resource.txt')
println getClass().getResource('/static/static.txt')
println getClass().getResource('/templates/template.txt')
println getClass().getResource('/root.properties')
println template('template.txt', [world:'Mama'])
}
}
@Service
class MyService {
String sayWorld() {
return 'World!'
}
}

View File

@@ -1,10 +0,0 @@
package org.test
@Component
class Example implements CommandLineRunner {
void run(String... args) {
print "Ssshh"
}
}

View File

@@ -1,17 +0,0 @@
package org.test
@RestController
class WarExample implements CommandLineRunner {
@RequestMapping("/")
public String hello() {
return "Hello"
}
void run(String... args) {
println getClass().getResource('/org/apache/tomcat/InstanceManager.class')
println getClass().getResource('/root.properties')
throw new RuntimeException("onStart error")
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2022 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.
@@ -23,15 +23,9 @@ import java.util.List;
import org.springframework.boot.cli.command.Command;
import org.springframework.boot.cli.command.CommandFactory;
import org.springframework.boot.cli.command.archive.JarCommand;
import org.springframework.boot.cli.command.archive.WarCommand;
import org.springframework.boot.cli.command.core.VersionCommand;
import org.springframework.boot.cli.command.encodepassword.EncodePasswordCommand;
import org.springframework.boot.cli.command.grab.GrabCommand;
import org.springframework.boot.cli.command.init.InitCommand;
import org.springframework.boot.cli.command.install.InstallCommand;
import org.springframework.boot.cli.command.install.UninstallCommand;
import org.springframework.boot.cli.command.run.RunCommand;
/**
* Default implementation of {@link CommandFactory}.
@@ -46,12 +40,6 @@ public class DefaultCommandFactory implements CommandFactory {
static {
List<Command> defaultCommands = new ArrayList<>();
defaultCommands.add(new VersionCommand());
defaultCommands.add(new RunCommand());
defaultCommands.add(new GrabCommand());
defaultCommands.add(new JarCommand());
defaultCommands.add(new WarCommand());
defaultCommands.add(new InstallCommand());
defaultCommands.add(new UninstallCommand());
defaultCommands.add(new InitCommand());
defaultCommands.add(new EncodePasswordCommand());
DEFAULT_COMMANDS = Collections.unmodifiableList(defaultCommands);

View File

@@ -1,86 +0,0 @@
/*
* Copyright 2012-2020 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
*
* https://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.cli.app;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* A launcher for {@code SpringApplication} or a {@code SpringApplication} subclass. The
* class that is used can be configured using the System property
* {@code spring.application.class.name} or the {@code SPRING_APPLICATION_CLASS_NAME}
* environment variable. Uses reflection to allow the launching code to exist in a
* separate ClassLoader from the application code.
*
* @author Andy Wilkinson
* @since 1.2.0
* @see System#getProperty(String)
* @see System#getenv(String)
*/
public class SpringApplicationLauncher {
private static final String DEFAULT_SPRING_APPLICATION_CLASS = "org.springframework.boot.SpringApplication";
private final ClassLoader classLoader;
/**
* Creates a new launcher that will use the given {@code classLoader} to load the
* configured {@code SpringApplication} class.
* @param classLoader the {@code ClassLoader} to use
*/
public SpringApplicationLauncher(ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* Launches the application created using the given {@code sources}. The application
* is launched with the given {@code args}.
* @param sources the sources for the application
* @param args the args for the application
* @return the application's {@code ApplicationContext}
* @throws Exception if the launch fails
*/
public Object launch(Class<?>[] sources, String[] args) throws Exception {
Map<String, Object> defaultProperties = new HashMap<>();
defaultProperties.put("spring.groovy.template.check-template-location", "false");
Class<?> applicationClass = Class.forName(getSpringApplicationClassName(), false, this.classLoader);
Constructor<?> constructor = applicationClass.getDeclaredConstructor(Class[].class);
constructor.setAccessible(true);
Object application = constructor.newInstance((Object) sources);
applicationClass.getMethod("setDefaultProperties", Map.class).invoke(application, defaultProperties);
Method method = applicationClass.getMethod("run", String[].class);
return method.invoke(application, (Object) args);
}
private String getSpringApplicationClassName() {
String className = System.getProperty("spring.application.class.name");
if (className == null) {
className = getEnvironmentVariable("SPRING_APPLICATION_CLASS_NAME");
}
if (className == null) {
className = DEFAULT_SPRING_APPLICATION_CLASS;
}
return className;
}
protected String getEnvironmentVariable(String name) {
return System.getenv(name);
}
}

View File

@@ -1,84 +0,0 @@
/*
* Copyright 2012-2021 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
*
* https://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.cli.app;
import java.io.IOException;
import java.io.InputStream;
import java.util.jar.Manifest;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* {@link SpringBootServletInitializer} for CLI packaged WAR files.
*
* @author Phillip Webb
* @since 1.3.0
*/
public class SpringApplicationWebApplicationInitializer extends SpringBootServletInitializer {
/**
* The entry containing the source class.
*/
public static final String SOURCE_ENTRY = "Spring-Application-Source-Classes";
private String[] sources;
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
try {
this.sources = getSources(servletContext);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
super.onStartup(servletContext);
}
private String[] getSources(ServletContext servletContext) throws IOException {
Manifest manifest = getManifest(servletContext);
if (manifest == null) {
throw new IllegalStateException("Unable to read manifest");
}
String sources = manifest.getMainAttributes().getValue(SOURCE_ENTRY);
return sources.split(",");
}
private Manifest getManifest(ServletContext servletContext) throws IOException {
InputStream stream = servletContext.getResourceAsStream("/META-INF/MANIFEST.MF");
return (stream != null) ? new Manifest(stream) : null;
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?>[] sourceClasses = new Class<?>[this.sources.length];
for (int i = 0; i < this.sources.length; i++) {
sourceClasses[i] = Class.forName(this.sources[i], false, classLoader);
}
return builder.sources(sourceClasses).properties("spring.groovy.template.check-template-location=false");
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* Support classes for CLI applications.
*/
package org.springframework.boot.cli.app;

View File

@@ -1,85 +0,0 @@
/*
* Copyright 2012-2020 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
*
* https://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.cli.archive;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.springframework.boot.cli.app.SpringApplicationLauncher;
/**
* A launcher for a CLI application that has been compiled and packaged as a jar file.
*
* @author Andy Wilkinson
* @author Phillip Webb
* @since 1.0.0
*/
public final class PackagedSpringApplicationLauncher {
/**
* The entry containing the source class.
*/
public static final String SOURCE_ENTRY = "Spring-Application-Source-Classes";
/**
* The entry containing the start class.
*/
public static final String START_CLASS_ENTRY = "Start-Class";
private PackagedSpringApplicationLauncher() {
}
private void run(String[] args) throws Exception {
URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
new SpringApplicationLauncher(classLoader).launch(getSources(classLoader), args);
}
private Class<?>[] getSources(URLClassLoader classLoader) throws Exception {
Enumeration<URL> urls = classLoader.getResources("META-INF/MANIFEST.MF");
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Manifest manifest = new Manifest(url.openStream());
if (isCliPackaged(manifest)) {
String sources = manifest.getMainAttributes().getValue(SOURCE_ENTRY);
return loadClasses(classLoader, sources.split(","));
}
}
throw new IllegalStateException("Cannot locate " + SOURCE_ENTRY + " in MANIFEST.MF");
}
private boolean isCliPackaged(Manifest manifest) {
Attributes attributes = manifest.getMainAttributes();
String startClass = attributes.getValue(START_CLASS_ENTRY);
return getClass().getName().equals(startClass);
}
private Class<?>[] loadClasses(ClassLoader classLoader, String[] names) throws ClassNotFoundException {
Class<?>[] classes = new Class<?>[names.length];
for (int i = 0; i < names.length; i++) {
classes[i] = Class.forName(names[i], false, classLoader);
}
return classes;
}
public static void main(String[] args) throws Exception {
new PackagedSpringApplicationLauncher().run(args);
}
}

View File

@@ -1,21 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* Class that are packaged as part of CLI generated JARs.
* @see org.springframework.boot.cli.command.archive.JarCommand
*/
package org.springframework.boot.cli.archive;

View File

@@ -1,317 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.command.archive;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.jar.Manifest;
import groovy.lang.Grab;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.ASTTransformation;
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.OptionParsingCommand;
import org.springframework.boot.cli.command.archive.ResourceMatcher.MatchedResource;
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.SourceOptions;
import org.springframework.boot.cli.command.status.ExitStatus;
import org.springframework.boot.cli.compiler.GroovyCompiler;
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory;
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
import org.springframework.boot.loader.tools.JarWriter;
import org.springframework.boot.loader.tools.Layout;
import org.springframework.boot.loader.tools.Library;
import org.springframework.boot.loader.tools.LibraryScope;
import org.springframework.boot.loader.tools.Repackager;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.Assert;
/**
* Abstract {@link Command} to create a self-contained executable archive file from a CLI
* application.
*
* @author Andy Wilkinson
* @author Phillip Webb
* @author Andrey Stolyarov
* @author Henri Kerola
*/
abstract class ArchiveCommand extends OptionParsingCommand {
protected ArchiveCommand(String name, String description, OptionHandler optionHandler) {
super(name, description, optionHandler);
}
@Override
public String getUsageHelp() {
return "[options] <" + getName() + "-name> <files>";
}
/**
* 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> excludeOption;
public ArchiveOptionHandler(String type, Layout layout) {
this.type = type;
this.layout = layout;
}
protected Layout getLayout() {
return this.layout;
}
@Override
protected void doOptions() {
this.includeOption = option("include",
"Pattern applied to directories on the classpath to find files to include in the resulting ")
.withRequiredArg().withValuesSeparatedBy(",").defaultsTo("");
this.excludeOption = option("exclude", "Pattern applied to directories on the classpath to find files to "
+ "exclude from the resulting " + this.type).withRequiredArg().withValuesSeparatedBy(",")
.defaultsTo("");
}
@Override
protected ExitStatus run(OptionSet options) throws Exception {
List<?> nonOptionArguments = new ArrayList<Object>(options.nonOptionArguments());
Assert.isTrue(nonOptionArguments.size() >= 2,
() -> "The name of the resulting " + this.type + " and at least one source file must be specified");
File output = new File((String) nonOptionArguments.remove(0));
Assert.isTrue(output.getName().toLowerCase(Locale.ENGLISH).endsWith("." + this.type),
() -> "The output '" + output + "' is not a " + this.type.toUpperCase(Locale.ENGLISH) + " file.");
deleteIfExists(output);
GroovyCompiler compiler = createCompiler(options);
List<URL> classpath = getClassPathUrls(compiler);
List<MatchedResource> classpathEntries = findMatchingClasspathEntries(classpath, options);
String[] sources = new SourceOptions(nonOptionArguments).getSourcesArray();
Class<?>[] compiledClasses = compiler.compile(sources);
List<URL> dependencies = getClassPathUrls(compiler);
dependencies.removeAll(classpath);
writeJar(output, compiledClasses, classpathEntries, dependencies);
return ExitStatus.OK;
}
private void deleteIfExists(File file) {
if (file.exists() && !file.delete()) {
throw new IllegalStateException("Failed to delete existing file " + file.getPath());
}
}
private GroovyCompiler createCompiler(OptionSet options) {
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory
.createDefaultRepositoryConfiguration();
GroovyCompilerConfiguration configuration = new OptionSetGroovyCompilerConfiguration(options, this,
repositoryConfiguration);
GroovyCompiler groovyCompiler = new GroovyCompiler(configuration);
groovyCompiler.getAstTransformations().add(0, new GrabAnnotationTransform());
return groovyCompiler;
}
private List<URL> getClassPathUrls(GroovyCompiler compiler) {
return new ArrayList<>(Arrays.asList(compiler.getLoader().getURLs()));
}
private List<MatchedResource> findMatchingClasspathEntries(List<URL> classpath, OptionSet options)
throws IOException {
ResourceMatcher matcher = new ResourceMatcher(options.valuesOf(this.includeOption),
options.valuesOf(this.excludeOption));
List<File> roots = new ArrayList<>();
for (URL classpathEntry : classpath) {
roots.add(new File(URI.create(classpathEntry.toString())));
}
return matcher.find(roots);
}
private void writeJar(File file, Class<?>[] compiledClasses, List<MatchedResource> classpathEntries,
List<URL> dependencies) throws IOException, URISyntaxException {
final List<Library> libraries;
try (JarWriter writer = new JarWriter(file)) {
addManifest(writer, compiledClasses);
addCliClasses(writer);
for (Class<?> compiledClass : compiledClasses) {
addClass(writer, compiledClass);
}
libraries = addClasspathEntries(writer, classpathEntries);
}
libraries.addAll(createLibraries(dependencies));
Repackager repackager = new Repackager(file);
repackager.setMainClass(PackagedSpringApplicationLauncher.class.getName());
repackager.repackage((callback) -> {
for (Library library : libraries) {
callback.library(library);
}
});
}
private List<Library> createLibraries(List<URL> dependencies) throws URISyntaxException {
List<Library> libraries = new ArrayList<>();
for (URL dependency : dependencies) {
File file = new File(dependency.toURI());
libraries.add(new Library(null, file, getLibraryScope(file), null, false, false, true));
}
return libraries;
}
private void addManifest(JarWriter writer, Class<?>[] compiledClasses) throws IOException {
Manifest manifest = new Manifest();
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
manifest.getMainAttributes().putValue(PackagedSpringApplicationLauncher.SOURCE_ENTRY,
commaDelimitedClassNames(compiledClasses));
writer.writeManifest(manifest);
}
private String commaDelimitedClassNames(Class<?>[] classes) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < classes.length; i++) {
if (i != 0) {
builder.append(',');
}
builder.append(classes[i].getName());
}
return builder.toString();
}
protected void addCliClasses(JarWriter writer) throws IOException {
addClass(writer, PackagedSpringApplicationLauncher.class);
addClass(writer, SpringApplicationLauncher.class);
Resource[] resources = new PathMatchingResourcePatternResolver()
.getResources("org/springframework/boot/groovy/**");
for (Resource resource : resources) {
String url = resource.getURL().toString();
addResource(writer, resource, url.substring(url.indexOf("org/springframework/boot/groovy/")));
}
}
protected final void addClass(JarWriter writer, Class<?> sourceClass) throws IOException {
addClass(writer, sourceClass.getClassLoader(), sourceClass.getName());
}
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) throws IOException {
InputStream stream = resource.getInputStream();
writer.writeEntry(name, stream);
}
private List<Library> addClasspathEntries(JarWriter writer, List<MatchedResource> entries) throws IOException {
List<Library> libraries = new ArrayList<>();
for (MatchedResource entry : entries) {
if (entry.isRoot()) {
libraries.add(new Library(null, entry.getFile(), LibraryScope.COMPILE, null, false, false, true));
}
else {
writeClasspathEntry(writer, entry);
}
}
return libraries;
}
protected void writeClasspathEntry(JarWriter writer, MatchedResource entry) throws IOException {
writer.writeEntry(entry.getName(), new FileInputStream(entry.getFile()));
}
protected abstract LibraryScope getLibraryScope(File file);
}
/**
* {@link ASTTransformation} to change {@code @Grab} annotation values.
*/
private static class GrabAnnotationTransform implements ASTTransformation {
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {
for (ASTNode node : nodes) {
if (node instanceof ModuleNode moduleNode) {
visitModule(moduleNode);
}
}
}
private void visitModule(ModuleNode module) {
for (ClassNode classNode : module.getClasses()) {
AnnotationNode annotation = new AnnotationNode(new ClassNode(Grab.class));
annotation.addMember("value", new ConstantExpression("groovy"));
classNode.addAnnotation(annotation);
// We only need to do it at most once
break;
}
// Disable the addition of a static initializer that calls Grape.addResolver
// because all the dependencies are local now
disableGrabResolvers(module.getClasses());
disableGrabResolvers(module.getImports());
}
private void disableGrabResolvers(List<? extends AnnotatedNode> nodes) {
for (AnnotatedNode classNode : nodes) {
List<AnnotationNode> annotations = classNode.getAnnotations();
for (AnnotationNode node : new ArrayList<>(annotations)) {
if (node.getClassNode().getNameWithoutPackage().equals("GrabResolver")) {
node.setMember("initClass", new ConstantExpression(false));
}
}
}
}
}
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.archive;
import java.io.File;
import org.springframework.boot.cli.command.Command;
import org.springframework.boot.loader.tools.Layouts;
import org.springframework.boot.loader.tools.LibraryScope;
/**
* {@link Command} to create a self-contained executable jar file from a CLI application.
*
* @author Andy Wilkinson
* @author Phillip Webb
* @since 1.3.0
*/
public class JarCommand extends ArchiveCommand {
public JarCommand() {
super("jar", "Create a self-contained executable jar file from a Spring Groovy script", new JarOptionHandler());
}
private static final class JarOptionHandler extends ArchiveOptionHandler {
JarOptionHandler() {
super("jar", new Layouts.Jar());
}
@Override
protected LibraryScope getLibraryScope(File file) {
return LibraryScope.COMPILE;
}
}
}

View File

@@ -1,222 +0,0 @@
/*
* Copyright 2012-2020 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
*
* https://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.cli.command.archive;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
/**
* Used to match resources for inclusion in a CLI application's jar file.
*
* @author Andy Wilkinson
*/
class ResourceMatcher {
private static final String[] DEFAULT_INCLUDES = { "public/**", "resources/**", "static/**", "templates/**",
"META-INF/**", "*" };
private static final String[] DEFAULT_EXCLUDES = { ".*", "repository/**", "build/**", "target/**", "**/*.jar",
"**/*.groovy" };
private final AntPathMatcher pathMatcher = new AntPathMatcher();
private final List<String> includes;
private final List<String> excludes;
ResourceMatcher(List<String> includes, List<String> excludes) {
this.includes = getOptions(includes, DEFAULT_INCLUDES);
this.excludes = getOptions(excludes, DEFAULT_EXCLUDES);
}
List<MatchedResource> find(List<File> roots) throws IOException {
List<MatchedResource> matchedResources = new ArrayList<>();
for (File root : roots) {
if (root.isFile()) {
matchedResources.add(new MatchedResource(root));
}
else {
matchedResources.addAll(findInDirectory(root));
}
}
return matchedResources;
}
private List<MatchedResource> findInDirectory(File directory) throws IOException {
List<MatchedResource> matchedResources = new ArrayList<>();
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
new DirectoryResourceLoader(directory));
for (String include : this.includes) {
for (Resource candidate : resolver.getResources(include)) {
File file = candidate.getFile();
if (file.isFile()) {
MatchedResource matchedResource = new MatchedResource(directory, file);
if (!isExcluded(matchedResource)) {
matchedResources.add(matchedResource);
}
}
}
}
return matchedResources;
}
private boolean isExcluded(MatchedResource matchedResource) {
for (String exclude : this.excludes) {
if (this.pathMatcher.match(exclude, matchedResource.getName())) {
return true;
}
}
return false;
}
private List<String> getOptions(List<String> values, String[] defaults) {
Set<String> result = new LinkedHashSet<>();
Set<String> minus = new LinkedHashSet<>();
boolean deltasFound = false;
for (String value : values) {
if (value.startsWith("+")) {
deltasFound = true;
value = value.substring(1);
result.add(value);
}
else if (value.startsWith("-")) {
deltasFound = true;
value = value.substring(1);
minus.add(value);
}
else if (!value.trim().isEmpty()) {
result.add(value);
}
}
for (String value : defaults) {
if (!minus.contains(value) || !deltasFound) {
result.add(value);
}
}
return new ArrayList<>(result);
}
/**
* {@link ResourceLoader} to get load resource from a directory.
*/
private static class DirectoryResourceLoader extends DefaultResourceLoader {
private final File rootDirectory;
DirectoryResourceLoader(File root) throws MalformedURLException {
super(new DirectoryClassLoader(root));
this.rootDirectory = root;
}
@Override
protected Resource getResourceByPath(String path) {
return new FileSystemResource(new File(this.rootDirectory, path));
}
}
/**
* {@link ClassLoader} backed by a directory.
*/
private static class DirectoryClassLoader extends URLClassLoader {
DirectoryClassLoader(File rootDirectory) throws MalformedURLException {
super(new URL[] { rootDirectory.toURI().toURL() });
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
return findResources(name);
}
@Override
public URL getResource(String name) {
return findResource(name);
}
}
/**
* A single matched resource.
*/
public static final class MatchedResource {
private final File file;
private final String name;
private final boolean root;
private MatchedResource(File file) {
this.name = file.getName();
this.file = file;
this.root = this.name.endsWith(".jar");
}
private MatchedResource(File rootDirectory, File file) {
String filePath = file.getAbsolutePath();
String rootDirectoryPath = rootDirectory.getAbsolutePath();
this.name = StringUtils.cleanPath(filePath.substring(rootDirectoryPath.length() + 1));
this.file = file;
this.root = false;
}
private MatchedResource(File resourceFile, String path, boolean root) {
this.file = resourceFile;
this.name = path;
this.root = root;
}
public String getName() {
return this.name;
}
public File getFile() {
return this.file;
}
public boolean isRoot() {
return this.root;
}
@Override
public String toString() {
return this.file.getAbsolutePath();
}
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.archive;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.springframework.boot.cli.command.Command;
import org.springframework.boot.loader.tools.JarWriter;
import org.springframework.boot.loader.tools.Layouts;
import org.springframework.boot.loader.tools.LibraryScope;
/**
* {@link Command} to create a self-contained executable jar file from a CLI application.
*
* @author Andrey Stolyarov
* @author Phillip Webb
* @author Henri Kerola
* @since 1.3.0
*/
public class WarCommand extends ArchiveCommand {
public WarCommand() {
super("war", "Create a self-contained executable war file from a Spring Groovy script", new WarOptionHandler());
}
private static final class WarOptionHandler extends ArchiveOptionHandler {
WarOptionHandler() {
super("war", new Layouts.War());
}
@Override
protected LibraryScope getLibraryScope(File file) {
String fileName = file.getName();
if (fileName.contains("tomcat-embed") || fileName.contains("spring-boot-starter-tomcat")) {
return LibraryScope.PROVIDED;
}
return LibraryScope.COMPILE;
}
@Override
protected void addCliClasses(JarWriter writer) throws IOException {
addClass(writer, null, "org.springframework.boot.cli.app.SpringApplicationWebApplicationInitializer");
super.addCliClasses(writer);
}
@Override
protected void writeClasspathEntry(JarWriter writer, ResourceMatcher.MatchedResource entry) throws IOException {
writer.writeEntry(getLayout().getClassesLocation() + entry.getName(), new FileInputStream(entry.getFile()));
}
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* CLI commands for creating jars and wars.
*/
package org.springframework.boot.cli.command.archive;

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.grab;
import java.util.List;
import joptsimple.OptionSet;
import org.springframework.boot.cli.command.Command;
import org.springframework.boot.cli.command.OptionParsingCommand;
import org.springframework.boot.cli.command.options.CompilerOptionHandler;
import org.springframework.boot.cli.command.options.OptionSetGroovyCompilerConfiguration;
import org.springframework.boot.cli.command.options.SourceOptions;
import org.springframework.boot.cli.command.status.ExitStatus;
import org.springframework.boot.cli.compiler.GroovyCompiler;
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory;
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
/**
* {@link Command} to grab the dependencies of one or more Groovy scripts.
*
* @author Andy Wilkinson
* @since 1.0.0
*/
public class GrabCommand extends OptionParsingCommand {
public GrabCommand() {
super("grab", "Download a spring groovy script's dependencies to ./repository", new GrabOptionHandler());
}
private static final class GrabOptionHandler extends CompilerOptionHandler {
@Override
protected ExitStatus run(OptionSet options) throws Exception {
SourceOptions sourceOptions = new SourceOptions(options);
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory
.createDefaultRepositoryConfiguration();
GroovyCompilerConfiguration configuration = new OptionSetGroovyCompilerConfiguration(options, this,
repositoryConfiguration);
if (System.getProperty("grape.root") == null) {
System.setProperty("grape.root", ".");
}
GroovyCompiler groovyCompiler = new GroovyCompiler(configuration);
groovyCompiler.compile(sourceOptions.getSourcesArray());
return ExitStatus.OK;
}
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* CLI command for grabbing dependencies.
*/
package org.springframework.boot.cli.command.grab;

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.install;
import java.io.File;
import java.util.List;
/**
* Resolve artifact identifiers (typically in the form {@literal group:artifact:version})
* to {@link File}s.
*
* @author Andy Wilkinson
*/
@FunctionalInterface
interface DependencyResolver {
/**
* Resolves the given {@code artifactIdentifiers}, typically in the form
* "group:artifact:version", and their dependencies.
* @param artifactIdentifiers the artifacts to resolve
* @return the {@code File}s for the resolved artifacts
* @throws Exception if dependency resolution fails
*/
List<File> resolve(List<String> artifactIdentifiers) throws Exception;
}

View File

@@ -1,92 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.install;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.codehaus.groovy.control.CompilationFailedException;
import org.springframework.boot.cli.compiler.GroovyCompiler;
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
/**
* A {@code DependencyResolver} implemented using Groovy's {@code @Grab}.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
class GroovyGrabDependencyResolver implements DependencyResolver {
private final GroovyCompilerConfiguration configuration;
GroovyGrabDependencyResolver(GroovyCompilerConfiguration configuration) {
this.configuration = configuration;
}
@Override
public List<File> resolve(List<String> artifactIdentifiers) throws CompilationFailedException, IOException {
GroovyCompiler groovyCompiler = new GroovyCompiler(this.configuration);
List<File> artifactFiles = new ArrayList<>();
if (!artifactIdentifiers.isEmpty()) {
List<URL> initialUrls = getClassPathUrls(groovyCompiler);
groovyCompiler.compile(createSources(artifactIdentifiers));
List<URL> artifactUrls = getClassPathUrls(groovyCompiler);
artifactUrls.removeAll(initialUrls);
for (URL artifactUrl : artifactUrls) {
artifactFiles.add(toFile(artifactUrl));
}
}
return artifactFiles;
}
private List<URL> getClassPathUrls(GroovyCompiler compiler) {
return new ArrayList<>(Arrays.asList(compiler.getLoader().getURLs()));
}
private String createSources(List<String> artifactIdentifiers) throws IOException {
File file = File.createTempFile("SpringCLIDependency", ".groovy");
file.deleteOnExit();
try (OutputStreamWriter stream = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
for (String artifactIdentifier : artifactIdentifiers) {
stream.write("@Grab('" + artifactIdentifier + "')");
}
// Dummy class to force compiler to do grab
stream.write("class Installer {}");
}
// Windows paths get tricky unless you work with URI
return file.getAbsoluteFile().toURI().toString();
}
private File toFile(URL url) {
try {
return new File(url.toURI());
}
catch (URISyntaxException ex) {
return new File(url.getPath());
}
}
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.install;
import java.util.List;
import joptsimple.OptionSet;
import org.springframework.boot.cli.command.Command;
import org.springframework.boot.cli.command.OptionParsingCommand;
import org.springframework.boot.cli.command.options.CompilerOptionHandler;
import org.springframework.boot.cli.command.status.ExitStatus;
import org.springframework.boot.cli.util.Log;
import org.springframework.util.Assert;
/**
* {@link Command} to install additional dependencies into the CLI.
*
* @author Dave Syer
* @author Andy Wilkinson
* @since 1.2.0
*/
public class InstallCommand extends OptionParsingCommand {
public InstallCommand() {
super("install", "Install dependencies to the lib/ext directory", new InstallOptionHandler());
}
@Override
public String getUsageHelp() {
return "[options] <coordinates>";
}
private static final class InstallOptionHandler extends CompilerOptionHandler {
@Override
@SuppressWarnings("unchecked")
protected ExitStatus run(OptionSet options) throws Exception {
List<String> args = (List<String>) options.nonOptionArguments();
Assert.notEmpty(args,
"Please specify at least one dependency, in the form group:artifact:version, to install");
try {
new Installer(options, this).install(args);
}
catch (Exception ex) {
String message = ex.getMessage();
Log.error((message != null) ? message : ex.getClass().toString());
}
return ExitStatus.OK;
}
}
}

View File

@@ -1,155 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.install;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Properties;
import joptsimple.OptionSet;
import org.springframework.boot.cli.command.options.CompilerOptionHandler;
import org.springframework.boot.cli.command.options.OptionSetGroovyCompilerConfiguration;
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory;
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
import org.springframework.boot.cli.util.Log;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.SystemPropertyUtils;
/**
* Shared logic for the {@link InstallCommand} and {@link UninstallCommand}.
*
* @author Andy Wilkinson
*/
class Installer {
private final DependencyResolver dependencyResolver;
private final Properties installCounts;
Installer(OptionSet options, CompilerOptionHandler compilerOptionHandler) throws IOException {
this(new GroovyGrabDependencyResolver(createCompilerConfiguration(options, compilerOptionHandler)));
}
Installer(DependencyResolver resolver) throws IOException {
this.dependencyResolver = resolver;
this.installCounts = loadInstallCounts();
}
private static GroovyCompilerConfiguration createCompilerConfiguration(OptionSet options,
CompilerOptionHandler compilerOptionHandler) {
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory
.createDefaultRepositoryConfiguration();
return new OptionSetGroovyCompilerConfiguration(options, compilerOptionHandler, repositoryConfiguration) {
@Override
public boolean isAutoconfigure() {
return false;
}
};
}
private Properties loadInstallCounts() throws IOException {
Properties properties = new Properties();
File installed = getInstalled();
if (installed.exists()) {
FileReader reader = new FileReader(installed);
properties.load(reader);
reader.close();
}
return properties;
}
private void saveInstallCounts() throws IOException {
try (FileWriter writer = new FileWriter(getInstalled())) {
this.installCounts.store(writer, null);
}
}
void install(List<String> artifactIdentifiers) throws Exception {
File extDirectory = getDefaultExtDirectory();
extDirectory.mkdirs();
Log.info("Installing into: " + extDirectory);
List<File> artifactFiles = this.dependencyResolver.resolve(artifactIdentifiers);
for (File artifactFile : artifactFiles) {
int installCount = getInstallCount(artifactFile);
if (installCount == 0) {
FileCopyUtils.copy(artifactFile, new File(extDirectory, artifactFile.getName()));
}
setInstallCount(artifactFile, installCount + 1);
}
saveInstallCounts();
}
private int getInstallCount(File file) {
String countString = this.installCounts.getProperty(file.getName());
if (countString == null) {
return 0;
}
return Integer.parseInt(countString);
}
private void setInstallCount(File file, int count) {
if (count == 0) {
this.installCounts.remove(file.getName());
}
else {
this.installCounts.setProperty(file.getName(), Integer.toString(count));
}
}
void uninstall(List<String> artifactIdentifiers) throws Exception {
File extDirectory = getDefaultExtDirectory();
Log.info("Uninstalling from: " + extDirectory);
List<File> artifactFiles = this.dependencyResolver.resolve(artifactIdentifiers);
for (File artifactFile : artifactFiles) {
int installCount = getInstallCount(artifactFile);
if (installCount <= 1) {
new File(extDirectory, artifactFile.getName()).delete();
}
setInstallCount(artifactFile, installCount - 1);
}
saveInstallCounts();
}
void uninstallAll() throws Exception {
File extDirectory = getDefaultExtDirectory();
Log.info("Uninstalling from: " + extDirectory);
for (String name : this.installCounts.stringPropertyNames()) {
new File(extDirectory, name).delete();
}
this.installCounts.clear();
saveInstallCounts();
}
private File getDefaultExtDirectory() {
String home = SystemPropertyUtils.resolvePlaceholders("${spring.home:${SPRING_HOME:.}}");
File extDirectory = new File(new File(home, "lib"), "ext");
if (!extDirectory.isDirectory() && !extDirectory.mkdirs()) {
throw new IllegalStateException("Failed to create ext directory " + extDirectory);
}
return extDirectory;
}
private File getInstalled() {
return new File(getDefaultExtDirectory(), ".installed");
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.install;
import java.util.List;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.springframework.boot.cli.command.Command;
import org.springframework.boot.cli.command.OptionParsingCommand;
import org.springframework.boot.cli.command.options.CompilerOptionHandler;
import org.springframework.boot.cli.command.status.ExitStatus;
import org.springframework.boot.cli.util.Log;
/**
* {@link Command} to uninstall dependencies from the CLI's lib/ext directory.
*
* @author Dave Syer
* @author Andy Wilkinson
* @since 1.2.0
*/
public class UninstallCommand extends OptionParsingCommand {
public UninstallCommand() {
super("uninstall", "Uninstall dependencies from the lib/ext directory", new UninstallOptionHandler());
}
@Override
public String getUsageHelp() {
return "[options] <coordinates>";
}
private static class UninstallOptionHandler extends CompilerOptionHandler {
private OptionSpec<Void> allOption;
@Override
protected void doOptions() {
this.allOption = option("all", "Uninstall all");
}
@Override
@SuppressWarnings("unchecked")
protected ExitStatus run(OptionSet options) throws Exception {
List<String> args = (List<String>) options.nonOptionArguments();
try {
if (options.has(this.allOption)) {
if (!args.isEmpty()) {
throw new IllegalArgumentException("Please use --all without specifying any dependencies");
}
new Installer(options, this).uninstallAll();
}
if (args.isEmpty()) {
throw new IllegalArgumentException(
"Please specify at least one dependency, in the form group:artifact:version, to uninstall");
}
new Installer(options, this).uninstall(args);
}
catch (Exception ex) {
String message = ex.getMessage();
Log.error((message != null) ? message : ex.getClass().toString());
}
return ExitStatus.OK;
}
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* CLI commands for installing and uninstalling CLI dependencies.
*/
package org.springframework.boot.cli.command.install;

View File

@@ -1,71 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.options;
import java.util.Arrays;
import joptsimple.OptionSpec;
/**
* An {@link OptionHandler} for commands that result in the compilation of one or more
* Groovy scripts.
*
* @author Andy Wilkinson
* @author Dave Syer
* @since 1.0.0
*/
public class CompilerOptionHandler extends OptionHandler {
private OptionSpec<Void> noGuessImportsOption;
private OptionSpec<Void> noGuessDependenciesOption;
private OptionSpec<Boolean> autoconfigureOption;
private OptionSpec<String> classpathOption;
@Override
protected final void options() {
this.noGuessImportsOption = option("no-guess-imports", "Do not attempt to guess imports");
this.noGuessDependenciesOption = option("no-guess-dependencies", "Do not attempt to guess dependencies");
this.autoconfigureOption = option("autoconfigure", "Add autoconfigure compiler transformations")
.withOptionalArg().ofType(Boolean.class).defaultsTo(true);
this.classpathOption = option(Arrays.asList("classpath", "cp"), "Additional classpath entries")
.withRequiredArg();
doOptions();
}
protected void doOptions() {
}
public OptionSpec<Void> getNoGuessImportsOption() {
return this.noGuessImportsOption;
}
public OptionSpec<Void> getNoGuessDependenciesOption() {
return this.noGuessDependenciesOption;
}
public OptionSpec<String> getClasspathOption() {
return this.classpathOption;
}
public OptionSpec<Boolean> getAutoconfigureOption() {
return this.autoconfigureOption;
}
}

View File

@@ -1,98 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.options;
import java.util.List;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
import org.springframework.boot.cli.compiler.GroovyCompilerScope;
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory;
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
/**
* Simple adapter class to present an {@link OptionSet} as a
* {@link GroovyCompilerConfiguration}.
*
* @author Andy Wilkinson
* @since 1.0.0
*/
public class OptionSetGroovyCompilerConfiguration implements GroovyCompilerConfiguration {
private final OptionSet options;
private final CompilerOptionHandler optionHandler;
private final List<RepositoryConfiguration> repositoryConfiguration;
protected OptionSetGroovyCompilerConfiguration(OptionSet optionSet, CompilerOptionHandler compilerOptionHandler) {
this(optionSet, compilerOptionHandler, RepositoryConfigurationFactory.createDefaultRepositoryConfiguration());
}
public OptionSetGroovyCompilerConfiguration(OptionSet optionSet, CompilerOptionHandler compilerOptionHandler,
List<RepositoryConfiguration> repositoryConfiguration) {
this.options = optionSet;
this.optionHandler = compilerOptionHandler;
this.repositoryConfiguration = repositoryConfiguration;
}
protected OptionSet getOptions() {
return this.options;
}
@Override
public GroovyCompilerScope getScope() {
return GroovyCompilerScope.DEFAULT;
}
@Override
public boolean isGuessImports() {
return !this.options.has(this.optionHandler.getNoGuessImportsOption());
}
@Override
public boolean isGuessDependencies() {
return !this.options.has(this.optionHandler.getNoGuessDependenciesOption());
}
@Override
public boolean isAutoconfigure() {
return this.optionHandler.getAutoconfigureOption().value(this.options);
}
@Override
public String[] getClasspath() {
OptionSpec<String> classpathOption = this.optionHandler.getClasspathOption();
if (this.options.has(classpathOption)) {
return this.options.valueOf(classpathOption).split(":");
}
return DEFAULT_CLASSPATH;
}
@Override
public List<RepositoryConfiguration> getRepositoryConfiguration() {
return this.repositoryConfiguration;
}
@Override
public boolean isQuiet() {
return false;
}
}

View File

@@ -1,143 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.command.options;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import joptsimple.OptionSet;
import org.springframework.boot.cli.util.ResourceUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Extract source file options (anything following '--' in an {@link OptionSet}).
*
* @author Phillip Webb
* @author Dave Syer
* @author Greg Turnquist
* @author Andy Wilkinson
* @since 1.0.0
*/
public class SourceOptions {
private final List<String> sources;
private final List<?> args;
/**
* Create a new {@link SourceOptions} instance.
* @param options the source option set
*/
public SourceOptions(OptionSet options) {
this(options, null);
}
/**
* Create a new {@link SourceOptions} instance.
* @param arguments the source arguments
*/
public SourceOptions(List<?> arguments) {
this(arguments, null);
}
/**
* Create a new {@link SourceOptions} instance. If it is an error to pass options that
* specify non-existent sources, but the default paths are allowed not to exist (the
* paths are tested before use). If default paths are provided and the option set
* contains no source file arguments it is not an error even if none of the default
* paths exist.
* @param optionSet the source option set
* @param classLoader an optional classloader used to try and load files that are not
* found in the local filesystem
*/
public SourceOptions(OptionSet optionSet, ClassLoader classLoader) {
this(optionSet.nonOptionArguments(), classLoader);
}
private SourceOptions(List<?> nonOptionArguments, ClassLoader classLoader) {
List<String> sources = new ArrayList<>();
int sourceArgCount = 0;
for (Object option : nonOptionArguments) {
if (option instanceof String filename) {
if ("--".equals(filename)) {
break;
}
List<String> urls = new ArrayList<>();
File fileCandidate = new File(filename);
if (fileCandidate.isFile()) {
urls.add(fileCandidate.getAbsoluteFile().toURI().toString());
}
else if (!isAbsoluteWindowsFile(fileCandidate)) {
urls.addAll(ResourceUtils.getUrls(filename, classLoader));
}
for (String url : urls) {
if (isSource(url)) {
sources.add(url);
}
}
if (isSource(filename)) {
if (urls.isEmpty()) {
throw new IllegalArgumentException("Can't find " + filename);
}
else {
sourceArgCount++;
}
}
}
}
this.args = Collections.unmodifiableList(nonOptionArguments.subList(sourceArgCount, nonOptionArguments.size()));
Assert.isTrue(!sources.isEmpty(), "Please specify at least one file");
this.sources = Collections.unmodifiableList(sources);
}
private boolean isAbsoluteWindowsFile(File file) {
return isWindows() && file.isAbsolute();
}
private boolean isWindows() {
return File.separatorChar == '\\';
}
private boolean isSource(String name) {
return name.endsWith(".java") || name.endsWith(".groovy");
}
public List<?> getArgs() {
return this.args;
}
public String[] getArgsArray() {
return this.args.stream().map(this::asString).toArray(String[]::new);
}
private String asString(Object arg) {
return (arg != null) ? String.valueOf(arg) : null;
}
public List<String> getSources() {
return this.sources;
}
public String[] getSourcesArray() {
return StringUtils.toStringArray(this.sources);
}
}

View File

@@ -1,159 +0,0 @@
/*
* Copyright 2012-2020 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
*
* https://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.cli.command.run;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.springframework.boot.cli.command.Command;
import org.springframework.boot.cli.command.OptionParsingCommand;
import org.springframework.boot.cli.command.options.CompilerOptionHandler;
import org.springframework.boot.cli.command.options.OptionSetGroovyCompilerConfiguration;
import org.springframework.boot.cli.command.options.SourceOptions;
import org.springframework.boot.cli.command.status.ExitStatus;
import org.springframework.boot.cli.compiler.GroovyCompilerScope;
import org.springframework.boot.cli.compiler.RepositoryConfigurationFactory;
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
/**
* {@link Command} to 'run' a groovy script or scripts.
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
* @since 1.0.0
* @see SpringApplicationRunner
*/
public class RunCommand extends OptionParsingCommand {
public RunCommand() {
super("run", "Run a spring groovy script", new RunOptionHandler());
}
@Override
public String getUsageHelp() {
return "[options] <files> [--] [args]";
}
public void stop() {
if (getHandler() != null) {
((RunOptionHandler) getHandler()).stop();
}
}
private static class RunOptionHandler extends CompilerOptionHandler {
private final Object monitor = new Object();
private OptionSpec<Void> watchOption;
private OptionSpec<Void> verboseOption;
private OptionSpec<Void> quietOption;
private SpringApplicationRunner runner;
@Override
protected void doOptions() {
this.watchOption = option("watch", "Watch the specified file for changes");
this.verboseOption = option(Arrays.asList("verbose", "v"), "Verbose logging of dependency resolution");
this.quietOption = option(Arrays.asList("quiet", "q"), "Quiet logging");
}
void stop() {
synchronized (this.monitor) {
if (this.runner != null) {
this.runner.stop();
}
this.runner = null;
}
}
@Override
protected synchronized ExitStatus run(OptionSet options) throws Exception {
synchronized (this.monitor) {
if (this.runner != null) {
throw new RuntimeException(
"Already running. Please stop the current application before running another (use the 'stop' command).");
}
SourceOptions sourceOptions = new SourceOptions(options);
List<RepositoryConfiguration> repositoryConfiguration = RepositoryConfigurationFactory
.createDefaultRepositoryConfiguration();
repositoryConfiguration.add(0,
new RepositoryConfiguration("local", new File("repository").toURI(), true));
SpringApplicationRunnerConfiguration configuration = new SpringApplicationRunnerConfigurationAdapter(
options, this, repositoryConfiguration);
this.runner = new SpringApplicationRunner(configuration, sourceOptions.getSourcesArray(),
sourceOptions.getArgsArray());
this.runner.compileAndRun();
return ExitStatus.OK;
}
}
/**
* Simple adapter class to present the {@link OptionSet} as a
* {@link SpringApplicationRunnerConfiguration}.
*/
private class SpringApplicationRunnerConfigurationAdapter extends OptionSetGroovyCompilerConfiguration
implements SpringApplicationRunnerConfiguration {
SpringApplicationRunnerConfigurationAdapter(OptionSet options, CompilerOptionHandler optionHandler,
List<RepositoryConfiguration> repositoryConfiguration) {
super(options, optionHandler, repositoryConfiguration);
}
@Override
public GroovyCompilerScope getScope() {
return GroovyCompilerScope.DEFAULT;
}
@Override
public boolean isWatchForFileChanges() {
return getOptions().has(RunOptionHandler.this.watchOption);
}
@Override
public Level getLogLevel() {
if (isQuiet()) {
return Level.OFF;
}
if (getOptions().has(RunOptionHandler.this.verboseOption)) {
return Level.FINEST;
}
return Level.INFO;
}
@Override
public boolean isQuiet() {
return getOptions().has(RunOptionHandler.this.quietOption);
}
}
}
}

View File

@@ -1,270 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.command.run;
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.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.springframework.boot.cli.app.SpringApplicationLauncher;
import org.springframework.boot.cli.compiler.GroovyCompiler;
import org.springframework.boot.cli.util.ResourceUtils;
/**
* Compiles Groovy code running the resulting classes using a {@code SpringApplication}.
* Takes care of threading and class-loading issues and can optionally monitor sources for
* changes.
*
* @author Phillip Webb
* @author Dave Syer
* @since 1.0.0
*/
public class SpringApplicationRunner {
private static int watcherCounter = 0;
private static int runnerCounter = 0;
private final Object monitor = new Object();
private final SpringApplicationRunnerConfiguration configuration;
private final String[] sources;
private final String[] args;
private final GroovyCompiler compiler;
private RunThread runThread;
private FileWatchThread fileWatchThread;
/**
* Create a new {@link SpringApplicationRunner} instance.
* @param configuration the configuration
* @param sources the files to compile/watch
* @param args input arguments
*/
SpringApplicationRunner(SpringApplicationRunnerConfiguration configuration, String[] sources, String... args) {
this.configuration = configuration;
this.sources = sources.clone();
this.args = args.clone();
this.compiler = new GroovyCompiler(configuration);
int level = configuration.getLogLevel().intValue();
if (level <= Level.FINER.intValue()) {
System.setProperty("org.springframework.boot.cli.compiler.grape.ProgressReporter", "detail");
System.setProperty("trace", "true");
}
else if (level <= Level.FINE.intValue()) {
System.setProperty("debug", "true");
}
else if (level == Level.OFF.intValue()) {
System.setProperty("spring.main.banner-mode", "OFF");
System.setProperty("logging.level.ROOT", "OFF");
System.setProperty("org.springframework.boot.cli.compiler.grape.ProgressReporter", "none");
}
}
/**
* Compile and run the application.
* @throws Exception on error
*/
public void compileAndRun() throws Exception {
synchronized (this.monitor) {
try {
stop();
Class<?>[] compiledSources = compile();
monitorForChanges();
// Run in new thread to ensure that the context classloader is set up
this.runThread = new RunThread(compiledSources);
this.runThread.start();
this.runThread.join();
}
catch (Exception ex) {
if (this.fileWatchThread == null) {
throw ex;
}
else {
ex.printStackTrace();
}
}
}
}
public void stop() {
synchronized (this.monitor) {
if (this.runThread != null) {
this.runThread.shutdown();
this.runThread = null;
}
}
}
private Class<?>[] compile() throws IOException {
Class<?>[] compiledSources = this.compiler.compile(this.sources);
if (compiledSources.length == 0) {
throw new RuntimeException("No classes found in '" + Arrays.toString(this.sources) + "'");
}
return compiledSources;
}
private void monitorForChanges() {
if (this.fileWatchThread == null && this.configuration.isWatchForFileChanges()) {
this.fileWatchThread = new FileWatchThread();
this.fileWatchThread.start();
}
}
/**
* Thread used to launch the Spring Application with the correct context classloader.
*/
private class RunThread extends Thread {
private final Object monitor = new Object();
private final Class<?>[] compiledSources;
private Object applicationContext;
/**
* Create a new {@link RunThread} instance.
* @param compiledSources the sources to launch
*/
RunThread(Class<?>... compiledSources) {
super("runner-" + (runnerCounter++));
this.compiledSources = compiledSources;
if (compiledSources.length != 0) {
setContextClassLoader(compiledSources[0].getClassLoader());
}
setDaemon(true);
}
@Override
public void run() {
synchronized (this.monitor) {
try {
this.applicationContext = new SpringApplicationLauncher(getContextClassLoader())
.launch(this.compiledSources, SpringApplicationRunner.this.args);
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* Shutdown the thread, closing any previously opened application context.
*/
void shutdown() {
synchronized (this.monitor) {
if (this.applicationContext != null) {
try {
Method method = this.applicationContext.getClass().getMethod("close");
method.invoke(this.applicationContext);
}
catch (NoSuchMethodException ex) {
// Not an application context that we can close
}
catch (Exception ex) {
ex.printStackTrace();
}
finally {
this.applicationContext = null;
}
}
}
}
}
/**
* Thread to watch for file changes and trigger recompile/reload.
*/
private class FileWatchThread extends Thread {
private long previous;
private List<File> sources;
FileWatchThread() {
super("filewatcher-" + (watcherCounter++));
this.previous = 0;
this.sources = getSourceFiles();
for (File file : this.sources) {
if (file.exists()) {
long current = file.lastModified();
if (current > this.previous) {
this.previous = current;
}
}
}
setDaemon(false);
}
private List<File> getSourceFiles() {
List<File> sources = new ArrayList<>();
for (String source : SpringApplicationRunner.this.sources) {
List<String> paths = ResourceUtils.getUrls(source, SpringApplicationRunner.this.compiler.getLoader());
for (String path : paths) {
try {
URL url = new URL(path);
if ("file".equals(url.getProtocol())) {
sources.add(new File(url.getFile()));
}
}
catch (MalformedURLException ex) {
// Ignore
}
}
}
return sources;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
for (File file : this.sources) {
if (file.exists()) {
long current = file.lastModified();
if (this.previous < current) {
this.previous = current;
compileAndRun();
}
}
}
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
catch (Exception ex) {
// Swallow, will be reported by compileAndRun
}
}
}
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.command.run;
import java.util.logging.Level;
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
/**
* Configuration for the {@link SpringApplicationRunner}.
*
* @author Phillip Webb
* @since 1.0.0
*/
public interface SpringApplicationRunnerConfiguration extends GroovyCompilerConfiguration {
/**
* Returns {@code true} if the source file should be monitored for changes and
* automatically recompiled.
* @return {@code true} if file watching should be performed, otherwise {@code false}
*/
boolean isWatchForFileChanges();
/**
* Returns the logging level to use.
* @return the logging level
*/
Level getLogLevel();
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* Classes for running CLI applications.
*/
package org.springframework.boot.cli.command.run;

View File

@@ -1,125 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ImportNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.ASTTransformation;
/**
* A base class for {@link ASTTransformation AST transformations} that are solely
* interested in {@link AnnotatedNode AnnotatedNodes}.
*
* @author Andy Wilkinson
* @since 1.1.0
*/
public abstract class AnnotatedNodeASTTransformation implements ASTTransformation {
private final Set<String> interestingAnnotationNames;
private final boolean removeAnnotations;
private SourceUnit sourceUnit;
protected AnnotatedNodeASTTransformation(Set<String> interestingAnnotationNames, boolean removeAnnotations) {
this.interestingAnnotationNames = interestingAnnotationNames;
this.removeAnnotations = removeAnnotations;
}
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {
this.sourceUnit = source;
List<AnnotationNode> annotationNodes = new ArrayList<>();
ClassVisitor classVisitor = new ClassVisitor(source, annotationNodes);
for (ASTNode node : nodes) {
if (node instanceof ModuleNode module) {
visitAnnotatedNode(module.getPackage(), annotationNodes);
for (ImportNode importNode : module.getImports()) {
visitAnnotatedNode(importNode, annotationNodes);
}
for (ImportNode importNode : module.getStarImports()) {
visitAnnotatedNode(importNode, annotationNodes);
}
module.getStaticImports()
.forEach((name, importNode) -> visitAnnotatedNode(importNode, annotationNodes));
module.getStaticStarImports()
.forEach((name, importNode) -> visitAnnotatedNode(importNode, annotationNodes));
for (ClassNode classNode : module.getClasses()) {
visitAnnotatedNode(classNode, annotationNodes);
classNode.visitContents(classVisitor);
}
}
}
processAnnotationNodes(annotationNodes);
}
protected SourceUnit getSourceUnit() {
return this.sourceUnit;
}
protected abstract void processAnnotationNodes(List<AnnotationNode> annotationNodes);
private void visitAnnotatedNode(AnnotatedNode annotatedNode, List<AnnotationNode> annotatedNodes) {
if (annotatedNode != null) {
Iterator<AnnotationNode> annotationNodes = annotatedNode.getAnnotations().iterator();
while (annotationNodes.hasNext()) {
AnnotationNode annotationNode = annotationNodes.next();
if (this.interestingAnnotationNames.contains(annotationNode.getClassNode().getName())) {
annotatedNodes.add(annotationNode);
if (this.removeAnnotations) {
annotationNodes.remove();
}
}
}
}
}
private class ClassVisitor extends ClassCodeVisitorSupport {
private final SourceUnit source;
private final List<AnnotationNode> annotationNodes;
ClassVisitor(SourceUnit source, List<AnnotationNode> annotationNodes) {
this.source = source;
this.annotationNodes = annotationNodes;
}
@Override
protected SourceUnit getSourceUnit() {
return this.source;
}
@Override
public void visitAnnotations(AnnotatedNode node) {
visitAnnotatedNode(node, this.annotationNodes);
}
}
}

View File

@@ -1,187 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.springframework.util.PatternMatchUtils;
/**
* General purpose AST utilities.
*
* @author Phillip Webb
* @author Dave Syer
* @author Greg Turnquist
* @since 1.0.0
*/
public abstract class AstUtils {
/**
* Determine if a {@link ClassNode} has one or more of the specified annotations on
* the class or any of its methods. N.B. the type names are not normally fully
* qualified.
* @param node the class to examine
* @param annotations the annotations to look for
* @return {@code true} if at least one of the annotations is found, otherwise
* {@code false}
*/
public static boolean hasAtLeastOneAnnotation(ClassNode node, String... annotations) {
if (hasAtLeastOneAnnotation((AnnotatedNode) node, annotations)) {
return true;
}
for (MethodNode method : node.getMethods()) {
if (hasAtLeastOneAnnotation(method, annotations)) {
return true;
}
}
return false;
}
/**
* Determine if an {@link AnnotatedNode} has one or more of the specified annotations.
* N.B. the annotation type names are not normally fully qualified.
* @param node the node to examine
* @param annotations the annotations to look for
* @return {@code true} if at least one of the annotations is found, otherwise
* {@code false}
*/
public static boolean hasAtLeastOneAnnotation(AnnotatedNode node, String... annotations) {
for (AnnotationNode annotationNode : node.getAnnotations()) {
for (String annotation : annotations) {
if (PatternMatchUtils.simpleMatch(annotation, annotationNode.getClassNode().getName())) {
return true;
}
}
}
return false;
}
/**
* Determine if a {@link ClassNode} has one or more fields of the specified types or
* method returning one or more of the specified types. N.B. the type names are not
* normally fully qualified.
* @param node the class to examine
* @param types the types to look for
* @return {@code true} if at least one of the types is found, otherwise {@code false}
*/
public static boolean hasAtLeastOneFieldOrMethod(ClassNode node, String... types) {
Set<String> typesSet = new HashSet<>(Arrays.asList(types));
for (FieldNode field : node.getFields()) {
if (typesSet.contains(field.getType().getName())) {
return true;
}
}
for (MethodNode method : node.getMethods()) {
if (typesSet.contains(method.getReturnType().getName())) {
return true;
}
}
return false;
}
/**
* Determine if a {@link ClassNode} subclasses any of the specified types N.B. the
* type names are not normally fully qualified.
* @param node the class to examine
* @param types the types that may have been sub-classed
* @return {@code true} if the class subclasses any of the specified types, otherwise
* {@code false}
*/
public static boolean subclasses(ClassNode node, String... types) {
for (String type : types) {
if (node.getSuperClass().getName().equals(type)) {
return true;
}
}
return false;
}
public static boolean hasAtLeastOneInterface(ClassNode classNode, String... types) {
Set<String> typesSet = new HashSet<>(Arrays.asList(types));
for (ClassNode inter : classNode.getInterfaces()) {
if (typesSet.contains(inter.getName())) {
return true;
}
}
return false;
}
/**
* Extract a top-level {@code name} closure from inside this block if there is one,
* optionally removing it from the block at the same time.
* @param block a block statement (class definition)
* @param name the name to look for
* @param remove whether the extracted closure should be removed
* @return a beans Closure if one can be found, null otherwise
*/
public static ClosureExpression getClosure(BlockStatement block, String name, boolean remove) {
for (ExpressionStatement statement : getExpressionStatements(block)) {
Expression expression = statement.getExpression();
if (expression instanceof MethodCallExpression methodCallExpression) {
ClosureExpression closure = getClosure(name, methodCallExpression);
if (closure != null) {
if (remove) {
block.getStatements().remove(statement);
}
return closure;
}
}
}
return null;
}
private static List<ExpressionStatement> getExpressionStatements(BlockStatement block) {
List<ExpressionStatement> statements = new ArrayList<>();
for (Statement statement : block.getStatements()) {
if (statement instanceof ExpressionStatement expressionStatement) {
statements.add(expressionStatement);
}
}
return statements;
}
private static ClosureExpression getClosure(String name, MethodCallExpression expression) {
Expression method = expression.getMethod();
if (method instanceof ConstantExpression constantExpression && name.equals(constantExpression.getValue())) {
return (ClosureExpression) ((ArgumentListExpression) expression.getArguments()).getExpression(0);
}
return null;
}
public static ClosureExpression getClosure(BlockStatement block, String name) {
return getClosure(block, name, false);
}
}

View File

@@ -1,95 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
/**
* Strategy that can be used to apply some auto-configuration during the
* {@link CompilePhase#CONVERSION} Groovy compile phase.
*
* @author Phillip Webb
* @since 1.0.0
*/
public abstract class CompilerAutoConfiguration {
/**
* Strategy method used to determine when compiler auto-configuration should be
* applied. Defaults to always.
* @param classNode the class node
* @return {@code true} if the compiler should be auto-configured using this class. If
* this method returns {@code false} no other strategy methods will be called.
*/
public boolean matches(ClassNode classNode) {
return true;
}
/**
* Apply any dependency customizations. This method will only be called if
* {@link #matches} returns {@code true}.
* @param dependencies dependency customizer
* @throws CompilationFailedException if the dependencies cannot be applied
*/
public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException {
}
/**
* Apply any import customizations. This method will only be called if
* {@link #matches} returns {@code true}.
* @param imports import customizer
* @throws CompilationFailedException if the imports cannot be applied
*/
public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
}
/**
* Apply any customizations to the main class. This method will only be called if
* {@link #matches} returns {@code true}. This method is useful when a groovy file
* defines more than one class but customization only applies to the first class.
* @param loader the class loader being used during compilation
* @param configuration the compiler configuration
* @param generatorContext the current context
* @param source the source unit
* @param classNode the main class
* @throws CompilationFailedException if the customizations cannot be applied
*/
public void applyToMainClass(GroovyClassLoader loader, GroovyCompilerConfiguration configuration,
GeneratorContext generatorContext, SourceUnit source, ClassNode classNode)
throws CompilationFailedException {
}
/**
* Apply any additional configuration.
* @param loader the class loader being used during compilation
* @param configuration the compiler configuration
* @param generatorContext the current context
* @param source the source unit
* @param classNode the class
* @throws CompilationFailedException if the configuration cannot be applied
*/
public void apply(GroovyClassLoader loader, GroovyCompilerConfiguration configuration,
GeneratorContext generatorContext, SourceUnit source, ClassNode classNode)
throws CompilationFailedException {
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.ASTTransformation;
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext;
import org.springframework.core.annotation.Order;
/**
* {@link ASTTransformation} to apply
* {@link CompilerAutoConfiguration#applyDependencies(DependencyCustomizer) dependency
* auto-configuration}.
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
* @since 1.0.0
*/
@Order(DependencyAutoConfigurationTransformation.ORDER)
public class DependencyAutoConfigurationTransformation implements ASTTransformation {
/**
* The order of the transformation.
*/
public static final int ORDER = DependencyManagementBomTransformation.ORDER + 100;
private final GroovyClassLoader loader;
private final DependencyResolutionContext dependencyResolutionContext;
private final Iterable<CompilerAutoConfiguration> compilerAutoConfigurations;
public DependencyAutoConfigurationTransformation(GroovyClassLoader loader,
DependencyResolutionContext dependencyResolutionContext,
Iterable<CompilerAutoConfiguration> compilerAutoConfigurations) {
this.loader = loader;
this.dependencyResolutionContext = dependencyResolutionContext;
this.compilerAutoConfigurations = compilerAutoConfigurations;
}
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {
for (ASTNode astNode : nodes) {
if (astNode instanceof ModuleNode moduleNode) {
visitModule(moduleNode);
}
}
}
private void visitModule(ModuleNode module) {
DependencyCustomizer dependencies = new DependencyCustomizer(this.loader, module,
this.dependencyResolutionContext);
for (ClassNode classNode : module.getClasses()) {
for (CompilerAutoConfiguration autoConfiguration : this.compilerAutoConfigurations) {
if (autoConfiguration.matches(classNode)) {
autoConfiguration.applyDependencies(dependencies);
}
}
}
}
}

View File

@@ -1,262 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import groovy.lang.Grab;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.springframework.boot.cli.compiler.dependencies.ArtifactCoordinatesResolver;
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext;
/**
* Customizer that allows dependencies to be added during compilation. Adding a dependency
* results in a {@link Grab @Grab} annotation being added to the primary {@link ClassNode
* class} is the {@link ModuleNode module} that's being customized.
* <p>
* This class provides a fluent API for conditionally adding dependencies. For example:
* {@code dependencies.ifMissing("com.corp.SomeClass").add(module)}.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.0.0
*/
public class DependencyCustomizer {
private final GroovyClassLoader loader;
private final ClassNode classNode;
private final DependencyResolutionContext dependencyResolutionContext;
/**
* Create a new {@link DependencyCustomizer} instance.
* @param loader the current classloader
* @param moduleNode the current module
* @param dependencyResolutionContext the context for dependency resolution
*/
public DependencyCustomizer(GroovyClassLoader loader, ModuleNode moduleNode,
DependencyResolutionContext dependencyResolutionContext) {
this.loader = loader;
this.classNode = moduleNode.getClasses().get(0);
this.dependencyResolutionContext = dependencyResolutionContext;
}
/**
* Create a new nested {@link DependencyCustomizer}.
* @param parent the parent customizer
*/
protected DependencyCustomizer(DependencyCustomizer parent) {
this.loader = parent.loader;
this.classNode = parent.classNode;
this.dependencyResolutionContext = parent.dependencyResolutionContext;
}
public String getVersion(String artifactId) {
return getVersion(artifactId, "");
}
public String getVersion(String artifactId, String defaultVersion) {
String version = this.dependencyResolutionContext.getArtifactCoordinatesResolver().getVersion(artifactId);
if (version == null) {
version = defaultVersion;
}
return version;
}
/**
* Create a nested {@link DependencyCustomizer} that only applies if any of the
* specified class names are not on the class path.
* @param classNames the class names to test
* @return a nested {@link DependencyCustomizer}
*/
public DependencyCustomizer ifAnyMissingClasses(String... classNames) {
return new DependencyCustomizer(this) {
@Override
protected boolean canAdd() {
for (String className : classNames) {
try {
Class.forName(className, false, DependencyCustomizer.this.loader);
}
catch (Exception ex) {
return true;
}
}
return false;
}
};
}
/**
* Create a nested {@link DependencyCustomizer} that only applies if all the specified
* class names are not on the class path.
* @param classNames the class names to test
* @return a nested {@link DependencyCustomizer}
*/
public DependencyCustomizer ifAllMissingClasses(String... classNames) {
return new DependencyCustomizer(this) {
@Override
protected boolean canAdd() {
for (String className : classNames) {
try {
Class.forName(className, false, DependencyCustomizer.this.loader);
return false;
}
catch (Exception ex) {
// swallow exception and continue
}
}
return DependencyCustomizer.this.canAdd();
}
};
}
/**
* Create a nested {@link DependencyCustomizer} that only applies if the specified
* paths are on the class path.
* @param paths the paths to test
* @return a nested {@link DependencyCustomizer}
*/
public DependencyCustomizer ifAllResourcesPresent(String... paths) {
return new DependencyCustomizer(this) {
@Override
protected boolean canAdd() {
for (String path : paths) {
try {
if (DependencyCustomizer.this.loader.getResource(path) == null) {
return false;
}
}
catch (Exception ex) {
// swallow exception and continue
}
}
return DependencyCustomizer.this.canAdd();
}
};
}
/**
* Create a nested {@link DependencyCustomizer} that only applies at least one of the
* specified paths is on the class path.
* @param paths the paths to test
* @return a nested {@link DependencyCustomizer}
*/
public DependencyCustomizer ifAnyResourcesPresent(String... paths) {
return new DependencyCustomizer(this) {
@Override
protected boolean canAdd() {
for (String path : paths) {
try {
return DependencyCustomizer.this.loader.getResource(path) != null;
}
catch (Exception ex) {
// swallow exception and continue
}
}
return DependencyCustomizer.this.canAdd();
}
};
}
/**
* Add dependencies and all of their dependencies. The group ID and version of the
* dependencies are resolved from the modules using the customizer's
* {@link ArtifactCoordinatesResolver}.
* @param modules the module IDs
* @return this {@link DependencyCustomizer} for continued use
*/
public DependencyCustomizer add(String... modules) {
for (String module : modules) {
add(module, null, null, true);
}
return this;
}
/**
* Add a single dependency and, optionally, all of its dependencies. The group ID and
* version of the dependency are resolved from the module using the customizer's
* {@link ArtifactCoordinatesResolver}.
* @param module the module ID
* @param transitive {@code true} if the transitive dependencies should also be added,
* otherwise {@code false}
* @return this {@link DependencyCustomizer} for continued use
*/
public DependencyCustomizer add(String module, boolean transitive) {
return add(module, null, null, transitive);
}
/**
* Add a single dependency with the specified classifier and type and, optionally, all
* of its dependencies. The group ID and version of the dependency are resolved from
* the module by using the customizer's {@link ArtifactCoordinatesResolver}.
* @param module the module ID
* @param classifier the classifier, may be {@code null}
* @param type the type, may be {@code null}
* @param transitive {@code true} if the transitive dependencies should also be added,
* otherwise {@code false}
* @return this {@link DependencyCustomizer} for continued use
*/
public DependencyCustomizer add(String module, String classifier, String type, boolean transitive) {
if (canAdd()) {
ArtifactCoordinatesResolver artifactCoordinatesResolver = this.dependencyResolutionContext
.getArtifactCoordinatesResolver();
this.classNode.addAnnotation(createGrabAnnotation(artifactCoordinatesResolver.getGroupId(module),
artifactCoordinatesResolver.getArtifactId(module), artifactCoordinatesResolver.getVersion(module),
classifier, type, transitive));
}
return this;
}
private AnnotationNode createGrabAnnotation(String group, String module, String version, String classifier,
String type, boolean transitive) {
AnnotationNode annotationNode = new AnnotationNode(new ClassNode(Grab.class));
annotationNode.addMember("group", new ConstantExpression(group));
annotationNode.addMember("module", new ConstantExpression(module));
annotationNode.addMember("version", new ConstantExpression(version));
if (classifier != null) {
annotationNode.addMember("classifier", new ConstantExpression(classifier));
}
if (type != null) {
annotationNode.addMember("type", new ConstantExpression(type));
}
annotationNode.addMember("transitive", new ConstantExpression(transitive));
annotationNode.addMember("initClass", new ConstantExpression(false));
return annotationNode;
}
/**
* Strategy called to test if dependencies can be added. Subclasses override as
* required. Returns {@code true} by default.
* @return {@code true} if dependencies can be added, otherwise {@code false}
*/
protected boolean canAdd() {
return true;
}
/**
* Returns the {@link DependencyResolutionContext}.
* @return the dependency resolution context
*/
public DependencyResolutionContext getDependencyResolutionContext() {
return this.dependencyResolutionContext;
}
}

View File

@@ -1,241 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import groovy.grape.Grape;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.Repository;
import org.apache.maven.model.building.DefaultModelBuilder;
import org.apache.maven.model.building.DefaultModelBuilderFactory;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.resolution.InvalidRepositoryException;
import org.apache.maven.model.resolution.ModelResolver;
import org.apache.maven.model.resolution.UnresolvableModelException;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.control.messages.Message;
import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
import org.codehaus.groovy.syntax.SyntaxException;
import org.codehaus.groovy.transform.ASTTransformation;
import org.springframework.boot.cli.compiler.dependencies.MavenModelDependencyManagement;
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext;
import org.springframework.boot.groovy.DependencyManagementBom;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* {@link ASTTransformation} for processing
* {@link DependencyManagementBom @DependencyManagementBom} annotations.
*
* @author Andy Wilkinson
* @since 1.3.0
*/
@Order(DependencyManagementBomTransformation.ORDER)
@SuppressWarnings("deprecation")
public class DependencyManagementBomTransformation extends AnnotatedNodeASTTransformation {
/**
* The order of the transformation.
*/
public static final int ORDER = Ordered.HIGHEST_PRECEDENCE + 100;
private static final Set<String> DEPENDENCY_MANAGEMENT_BOM_ANNOTATION_NAMES = Collections
.unmodifiableSet(new HashSet<>(Arrays.asList(DependencyManagementBom.class.getName(),
DependencyManagementBom.class.getSimpleName())));
private final DependencyResolutionContext resolutionContext;
public DependencyManagementBomTransformation(DependencyResolutionContext resolutionContext) {
super(DEPENDENCY_MANAGEMENT_BOM_ANNOTATION_NAMES, true);
this.resolutionContext = resolutionContext;
}
@Override
protected void processAnnotationNodes(List<AnnotationNode> annotationNodes) {
if (!annotationNodes.isEmpty()) {
if (annotationNodes.size() > 1) {
for (AnnotationNode annotationNode : annotationNodes) {
handleDuplicateDependencyManagementBomAnnotation(annotationNode);
}
}
else {
processDependencyManagementBomAnnotation(annotationNodes.get(0));
}
}
}
private void processDependencyManagementBomAnnotation(AnnotationNode annotationNode) {
Expression valueExpression = annotationNode.getMember("value");
List<Map<String, String>> bomDependencies = createDependencyMaps(valueExpression);
updateDependencyResolutionContext(bomDependencies);
}
private List<Map<String, String>> createDependencyMaps(Expression valueExpression) {
Map<String, String> dependency = null;
List<ConstantExpression> constantExpressions = getConstantExpressions(valueExpression);
List<Map<String, String>> dependencies = new ArrayList<>(constantExpressions.size());
for (ConstantExpression expression : constantExpressions) {
Object value = expression.getValue();
if (value instanceof String string) {
String[] components = string.split(":");
if (components.length == 3) {
dependency = new HashMap<>();
dependency.put("group", components[0]);
dependency.put("module", components[1]);
dependency.put("version", components[2]);
dependency.put("type", "pom");
dependencies.add(dependency);
}
else {
handleMalformedDependency(expression);
}
}
}
return dependencies;
}
private List<ConstantExpression> getConstantExpressions(Expression valueExpression) {
if (valueExpression instanceof ListExpression listExpression) {
return getConstantExpressions(listExpression);
}
if (valueExpression instanceof ConstantExpression constantExpression
&& constantExpression.getValue() instanceof String) {
return Arrays.asList(constantExpression);
}
reportError("@DependencyManagementBom requires an inline constant that is a string or a string array",
valueExpression);
return Collections.emptyList();
}
private List<ConstantExpression> getConstantExpressions(ListExpression valueExpression) {
List<ConstantExpression> expressions = new ArrayList<>();
for (Expression expression : valueExpression.getExpressions()) {
if (expression instanceof ConstantExpression constantExpression
&& constantExpression.getValue() instanceof String) {
expressions.add(constantExpression);
}
else {
reportError("Each entry in the array must be an inline string constant", expression);
}
}
return expressions;
}
private void handleMalformedDependency(Expression expression) {
Message message = createSyntaxErrorMessage(
String.format("The string must be of the form \"group:module:version\"%n"), expression);
getSourceUnit().getErrorCollector().addErrorAndContinue(message);
}
private void updateDependencyResolutionContext(List<Map<String, String>> bomDependencies) {
URI[] uris = Grape.getInstance().resolve(null, bomDependencies.toArray(new Map<?, ?>[0]));
DefaultModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
for (URI uri : uris) {
try {
DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
request.setModelResolver(new GrapeModelResolver());
request.setModelSource(new org.apache.maven.model.building.UrlModelSource(uri.toURL()));
request.setSystemProperties(System.getProperties());
Model model = modelBuilder.build(request).getEffectiveModel();
this.resolutionContext.addDependencyManagement(new MavenModelDependencyManagement(model));
}
catch (Exception ex) {
throw new IllegalStateException("Failed to build model for '" + uri + "'. Is it a valid Maven bom?",
ex);
}
}
}
private void handleDuplicateDependencyManagementBomAnnotation(AnnotationNode annotationNode) {
Message message = createSyntaxErrorMessage(
"Duplicate @DependencyManagementBom annotation. It must be declared at most once.", annotationNode);
getSourceUnit().getErrorCollector().addErrorAndContinue(message);
}
private void reportError(String message, ASTNode node) {
getSourceUnit().getErrorCollector().addErrorAndContinue(createSyntaxErrorMessage(message, node));
}
private Message createSyntaxErrorMessage(String message, ASTNode node) {
return new SyntaxErrorMessage(new SyntaxException(message, node.getLineNumber(), node.getColumnNumber(),
node.getLastLineNumber(), node.getLastColumnNumber()), getSourceUnit());
}
private static class GrapeModelResolver implements ModelResolver {
@Override
public org.apache.maven.model.building.ModelSource resolveModel(Parent parent)
throws UnresolvableModelException {
return resolveModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
}
@Override
public org.apache.maven.model.building.ModelSource resolveModel(Dependency dependency)
throws UnresolvableModelException {
return resolveModel(dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion());
}
@Override
public org.apache.maven.model.building.ModelSource resolveModel(String groupId, String artifactId,
String version) throws UnresolvableModelException {
Map<String, String> dependency = new HashMap<>();
dependency.put("group", groupId);
dependency.put("module", artifactId);
dependency.put("version", version);
dependency.put("type", "pom");
try {
return new org.apache.maven.model.building.UrlModelSource(
Grape.getInstance().resolve(null, dependency)[0].toURL());
}
catch (MalformedURLException ex) {
throw new UnresolvableModelException(ex.getMessage(), groupId, artifactId, version);
}
}
@Override
public void addRepository(Repository repository) throws InvalidRepositoryException {
}
@Override
public void addRepository(Repository repository, boolean replace) throws InvalidRepositoryException {
}
@Override
public ModelResolver newCopy() {
return this;
}
}
}

View File

@@ -1,245 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.SourceUnit;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
/**
* Extension of the {@link GroovyClassLoader} with support for obtaining '.class' files as
* resources.
*
* @author Phillip Webb
* @author Dave Syer
* @since 1.0.0
*/
public class ExtendedGroovyClassLoader extends GroovyClassLoader {
private static final String SHARED_PACKAGE = "org.springframework.boot.groovy";
private static final URL[] NO_URLS = new URL[] {};
private final Map<String, byte[]> classResources = new HashMap<>();
private final GroovyCompilerScope scope;
private final CompilerConfiguration configuration;
public ExtendedGroovyClassLoader(GroovyCompilerScope scope) {
this(scope, createParentClassLoader(scope), new CompilerConfiguration());
}
private static ClassLoader createParentClassLoader(GroovyCompilerScope scope) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (scope == GroovyCompilerScope.DEFAULT) {
classLoader = new DefaultScopeParentClassLoader(classLoader);
}
return classLoader;
}
private ExtendedGroovyClassLoader(GroovyCompilerScope scope, ClassLoader parent,
CompilerConfiguration configuration) {
super(parent, configuration);
this.configuration = configuration;
this.scope = scope;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
return super.findClass(name);
}
catch (ClassNotFoundException ex) {
if (this.scope == GroovyCompilerScope.DEFAULT && name.startsWith(SHARED_PACKAGE)) {
Class<?> sharedClass = findSharedClass(name);
if (sharedClass != null) {
return sharedClass;
}
}
throw ex;
}
}
private Class<?> findSharedClass(String name) {
try {
String path = name.replace('.', '/').concat(".class");
try (InputStream inputStream = getParent().getResourceAsStream(path)) {
if (inputStream != null) {
return defineClass(name, FileCopyUtils.copyToByteArray(inputStream));
}
}
return null;
}
catch (Exception ex) {
return null;
}
}
@Override
public InputStream getResourceAsStream(String name) {
InputStream resourceStream = super.getResourceAsStream(name);
if (resourceStream == null) {
byte[] bytes = this.classResources.get(name);
resourceStream = (bytes != null) ? new ByteArrayInputStream(bytes) : null;
}
return resourceStream;
}
@Override
public ClassCollector createCollector(CompilationUnit unit, SourceUnit su) {
return new ExtendedClassCollector(getInnerLoader(), unit, su);
}
private InnerLoader getInnerLoader() {
return new InnerLoader(ExtendedGroovyClassLoader.this) {
// Don't return URLs from the inner loader so that Tomcat only
// searches the parent. Fixes 'TLD skipped' issues
@Override
public URL[] getURLs() {
return NO_URLS;
}
};
}
public CompilerConfiguration getConfiguration() {
return this.configuration;
}
/**
* Inner collector class used to track as classes are added.
*/
protected class ExtendedClassCollector extends ClassCollector {
protected ExtendedClassCollector(InnerLoader loader, CompilationUnit unit, SourceUnit su) {
super(loader, unit, su);
}
@Override
protected Class<?> createClass(byte[] code, ClassNode classNode) {
Class<?> createdClass = super.createClass(code, classNode);
ExtendedGroovyClassLoader.this.classResources.put(classNode.getName().replace('.', '/') + ".class", code);
return createdClass;
}
}
/**
* ClassLoader used for a parent that filters so that only classes from groovy-all.jar
* are exposed.
*/
private static class DefaultScopeParentClassLoader extends ClassLoader {
private static final String[] GROOVY_JARS_PREFIXES = { "groovy", "antlr", "asm" };
private final URLClassLoader groovyOnlyClassLoader;
DefaultScopeParentClassLoader(ClassLoader parent) {
super(parent);
this.groovyOnlyClassLoader = new URLClassLoader(getGroovyJars(parent),
getClass().getClassLoader().getParent());
}
private URL[] getGroovyJars(ClassLoader parent) {
Set<URL> urls = new HashSet<>();
findGroovyJarsDirectly(parent, urls);
if (urls.isEmpty()) {
findGroovyJarsFromClassPath(urls);
}
Assert.state(!urls.isEmpty(), "Unable to find groovy JAR");
return new ArrayList<>(urls).toArray(new URL[0]);
}
private void findGroovyJarsDirectly(ClassLoader classLoader, Set<URL> urls) {
while (classLoader != null) {
if (classLoader instanceof URLClassLoader urlClassLoader) {
for (URL url : urlClassLoader.getURLs()) {
if (isGroovyJar(url.toString())) {
urls.add(url);
}
}
}
classLoader = classLoader.getParent();
}
}
private void findGroovyJarsFromClassPath(Set<URL> urls) {
String classpath = System.getProperty("java.class.path");
String[] entries = classpath.split(System.getProperty("path.separator"));
for (String entry : entries) {
if (isGroovyJar(entry)) {
File file = new File(entry);
if (file.canRead()) {
try {
urls.add(file.toURI().toURL());
}
catch (MalformedURLException ex) {
// Swallow and continue
}
}
}
}
}
private boolean isGroovyJar(String entry) {
entry = StringUtils.cleanPath(entry);
for (String jarPrefix : GROOVY_JARS_PREFIXES) {
if (entry.contains("/" + jarPrefix + "-")) {
return true;
}
}
return false;
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
return this.groovyOnlyClassLoader.getResources(name);
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (!name.startsWith("java.")) {
Class.forName(name, false, this.groovyOnlyClassLoader);
}
return super.loadClass(name, resolve);
}
}
}

View File

@@ -1,131 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.PackageNode;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.GroovyASTTransformation;
import org.springframework.boot.groovy.DependencyManagementBom;
import org.springframework.core.Ordered;
/**
* A base class that lets plugin authors easily add additional BOMs to all apps. All the
* dependencies in the BOM (and its transitives) will be added to the dependency
* management lookup, so an app can use just the artifact id (e.g. "spring-jdbc") in a
* {@code @Grab}. To install, implement the missing methods and list the class in
* {@code META-INF/services/org.springframework.boot.cli.compiler.SpringBootAstTransformation}
* . The {@link #getOrder()} value needs to be before
* {@link DependencyManagementBomTransformation#ORDER}.
*
* @author Dave Syer
* @since 1.3.0
*/
@GroovyASTTransformation(phase = CompilePhase.CONVERSION)
public abstract class GenericBomAstTransformation implements SpringBootAstTransformation, Ordered {
private static final ClassNode BOM = ClassHelper.make(DependencyManagementBom.class);
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {
for (ASTNode astNode : nodes) {
if (astNode instanceof ModuleNode moduleNode) {
visitModule(moduleNode, getBomModule());
}
}
}
/**
* The bom to be added to dependency management in compact form:
* {@code "<groupId>:<artifactId>:<version>"} (like in a {@code @Grab}).
* @return the maven co-ordinates of the BOM to add
*/
protected abstract String getBomModule();
private void visitModule(ModuleNode node, String module) {
addDependencyManagementBom(node, module);
}
private void addDependencyManagementBom(ModuleNode node, String module) {
AnnotatedNode annotated = getAnnotatedNode(node);
if (annotated != null) {
AnnotationNode bom = getAnnotation(annotated);
List<Expression> expressions = new ArrayList<>(getConstantExpressions(bom.getMember("value")));
expressions.add(new ConstantExpression(module));
bom.setMember("value", new ListExpression(expressions));
}
}
private AnnotationNode getAnnotation(AnnotatedNode annotated) {
List<AnnotationNode> annotations = annotated.getAnnotations(BOM);
if (!annotations.isEmpty()) {
return annotations.get(0);
}
AnnotationNode annotation = new AnnotationNode(BOM);
annotated.addAnnotation(annotation);
return annotation;
}
private AnnotatedNode getAnnotatedNode(ModuleNode node) {
PackageNode packageNode = node.getPackage();
if (packageNode != null && !packageNode.getAnnotations(BOM).isEmpty()) {
return packageNode;
}
if (!node.getClasses().isEmpty()) {
return node.getClasses().get(0);
}
return packageNode;
}
private List<ConstantExpression> getConstantExpressions(Expression valueExpression) {
if (valueExpression instanceof ListExpression listExpression) {
return getConstantExpressions(listExpression);
}
if (valueExpression instanceof ConstantExpression constantExpression
&& constantExpression.getValue() instanceof String) {
return Arrays.asList(constantExpression);
}
return Collections.emptyList();
}
private List<ConstantExpression> getConstantExpressions(ListExpression valueExpression) {
List<ConstantExpression> expressions = new ArrayList<>();
for (Expression expression : valueExpression.getExpressions()) {
if (expression instanceof ConstantExpression constantExpression
&& constantExpression.getValue() instanceof String) {
expressions.add(constantExpression);
}
}
return expressions;
}
}

View File

@@ -1,118 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.groovy.ast.expr.ClosureExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.ASTTransformation;
import org.springframework.core.annotation.Order;
/**
* {@link ASTTransformation} to resolve beans declarations inside application source
* files. Users only need to define a <code>beans{}</code> DSL element, and this
* transformation will remove it and make it accessible to the Spring application via an
* interface.
*
* @author Dave Syer
* @since 1.0.0
*/
@Order(GroovyBeansTransformation.ORDER)
public class GroovyBeansTransformation implements ASTTransformation {
/**
* The order of the transformation.
*/
public static final int ORDER = DependencyManagementBomTransformation.ORDER + 200;
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {
for (ASTNode node : nodes) {
if (node instanceof ModuleNode module) {
for (ClassNode classNode : new ArrayList<>(module.getClasses())) {
if (classNode.isScript()) {
classNode.visitContents(new ClassVisitor(source, classNode));
}
}
}
}
}
private class ClassVisitor extends ClassCodeVisitorSupport {
private static final String SOURCE_INTERFACE = "org.springframework.boot.BeanDefinitionLoader.GroovyBeanDefinitionSource";
private static final String BEANS = "beans";
private final SourceUnit source;
private final ClassNode classNode;
private boolean xformed = false;
ClassVisitor(SourceUnit source, ClassNode classNode) {
this.source = source;
this.classNode = classNode;
}
@Override
protected SourceUnit getSourceUnit() {
return this.source;
}
@Override
public void visitBlockStatement(BlockStatement block) {
if (block.isEmpty() || this.xformed) {
return;
}
ClosureExpression closure = beans(block);
if (closure != null) {
// Add a marker interface to the current script
this.classNode.addInterface(ClassHelper.make(SOURCE_INTERFACE));
// Implement the interface by adding a public read-only property with the
// same name as the method in the interface (getBeans). Make it return the
// closure.
this.classNode.addProperty(new PropertyNode(BEANS, Modifier.PUBLIC | Modifier.FINAL,
ClassHelper.CLOSURE_TYPE.getPlainNodeReference(), this.classNode, closure, null, null));
// Only do this once per class
this.xformed = true;
}
}
/**
* Extract a top-level <code>beans{}</code> closure from inside this block if
* there is one. Removes it from the block at the same time.
* @param block a block statement (class definition)
* @return a beans Closure if one can be found, null otherwise
*/
private ClosureExpression beans(BlockStatement block) {
return AstUtils.getClosure(block, BEANS, true);
}
}
}

View File

@@ -1,318 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import groovy.grape.GrapeEngine;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyClassLoader.ClassCollector;
import groovy.lang.GroovyCodeSource;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.Phases;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.CompilationCustomizer;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.codehaus.groovy.transform.ASTTransformation;
import org.codehaus.groovy.transform.ASTTransformationVisitor;
import org.springframework.boot.cli.compiler.dependencies.SpringBootDependenciesDependencyManagement;
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext;
import org.springframework.boot.cli.compiler.grape.GrapeEngineInstaller;
import org.springframework.boot.cli.compiler.grape.MavenResolverGrapeEngineFactory;
import org.springframework.boot.cli.util.ResourceUtils;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.ClassUtils;
/**
* Compiler for Groovy sources. Primarily a simple Facade for
* {@link GroovyClassLoader#parseClass(GroovyCodeSource)} with the following additional
* features:
* <ul>
* <li>{@link CompilerAutoConfiguration} strategies will be read from
* {@code META-INF/services/org.springframework.boot.cli.compiler.CompilerAutoConfiguration}
* (per the standard java {@link ServiceLoader} contract) and applied during compilation
* </li>
*
* <li>Multiple classes can be returned if the Groovy source defines more than one Class
* </li>
*
* <li>Generated class files can also be loaded using
* {@link ClassLoader#getResource(String)}</li>
* </ul>
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
* @since 1.0.0
*/
public class GroovyCompiler {
private final GroovyCompilerConfiguration configuration;
private final ExtendedGroovyClassLoader loader;
private final Iterable<CompilerAutoConfiguration> compilerAutoConfigurations;
private final List<ASTTransformation> transformations;
/**
* Create a new {@link GroovyCompiler} instance.
* @param configuration the compiler configuration
*/
public GroovyCompiler(GroovyCompilerConfiguration configuration) {
this.configuration = configuration;
this.loader = createLoader(configuration);
DependencyResolutionContext resolutionContext = new DependencyResolutionContext();
resolutionContext.addDependencyManagement(new SpringBootDependenciesDependencyManagement());
GrapeEngine grapeEngine = MavenResolverGrapeEngineFactory.create(this.loader,
configuration.getRepositoryConfiguration(), resolutionContext, configuration.isQuiet());
GrapeEngineInstaller.install(grapeEngine);
this.loader.getConfiguration().addCompilationCustomizers(new CompilerAutoConfigureCustomizer());
if (configuration.isAutoconfigure()) {
this.compilerAutoConfigurations = ServiceLoader.load(CompilerAutoConfiguration.class);
}
else {
this.compilerAutoConfigurations = Collections.emptySet();
}
this.transformations = new ArrayList<>();
this.transformations.add(new DependencyManagementBomTransformation(resolutionContext));
this.transformations.add(new DependencyAutoConfigurationTransformation(this.loader, resolutionContext,
this.compilerAutoConfigurations));
this.transformations.add(new GroovyBeansTransformation());
if (this.configuration.isGuessDependencies()) {
this.transformations.add(new ResolveDependencyCoordinatesTransformation(resolutionContext));
}
for (ASTTransformation transformation : ServiceLoader.load(SpringBootAstTransformation.class)) {
this.transformations.add(transformation);
}
this.transformations.sort(AnnotationAwareOrderComparator.INSTANCE);
}
/**
* Return a mutable list of the {@link ASTTransformation}s to be applied during
* {@link #compile(String...)}.
* @return the AST transformations to apply
*/
public List<ASTTransformation> getAstTransformations() {
return this.transformations;
}
public ExtendedGroovyClassLoader getLoader() {
return this.loader;
}
private ExtendedGroovyClassLoader createLoader(GroovyCompilerConfiguration configuration) {
ExtendedGroovyClassLoader loader = new ExtendedGroovyClassLoader(configuration.getScope());
for (URL url : getExistingUrls()) {
loader.addURL(url);
}
for (String classpath : configuration.getClasspath()) {
loader.addClasspath(classpath);
}
return loader;
}
private URL[] getExistingUrls() {
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
if (tccl instanceof ExtendedGroovyClassLoader groovyClassLoader) {
return groovyClassLoader.getURLs();
}
else {
return new URL[0];
}
}
public void addCompilationCustomizers(CompilationCustomizer... customizers) {
this.loader.getConfiguration().addCompilationCustomizers(customizers);
}
/**
* Compile the specified Groovy sources, applying any
* {@link CompilerAutoConfiguration}s. All classes defined in the sources will be
* returned from this method.
* @param sources the sources to compile
* @return compiled classes
* @throws CompilationFailedException in case of compilation failures
* @throws IOException in case of I/O errors
* @throws CompilationFailedException in case of compilation errors
*/
public Class<?>[] compile(String... sources) throws CompilationFailedException, IOException {
this.loader.clearCache();
List<Class<?>> classes = new ArrayList<>();
CompilerConfiguration configuration = this.loader.getConfiguration();
CompilationUnit compilationUnit = new CompilationUnit(configuration, null, this.loader);
ClassCollector collector = this.loader.createCollector(compilationUnit, null);
compilationUnit.setClassgenCallback(collector);
for (String source : sources) {
List<String> paths = ResourceUtils.getUrls(source, this.loader);
for (String path : paths) {
compilationUnit.addSource(new URL(path));
}
}
addAstTransformations(compilationUnit);
compilationUnit.compile(Phases.CLASS_GENERATION);
for (Object loadedClass : collector.getLoadedClasses()) {
classes.add((Class<?>) loadedClass);
}
ClassNode mainClassNode = MainClass.get(compilationUnit);
Class<?> mainClass = null;
for (Class<?> loadedClass : classes) {
if (mainClassNode.getName().equals(loadedClass.getName())) {
mainClass = loadedClass;
}
}
if (mainClass != null) {
classes.remove(mainClass);
classes.add(0, mainClass);
}
return ClassUtils.toClassArray(classes);
}
@SuppressWarnings("rawtypes")
private void addAstTransformations(CompilationUnit compilationUnit) {
Deque[] phaseOperations = getPhaseOperations(compilationUnit);
processConversionOperations((LinkedList) phaseOperations[Phases.CONVERSION]);
}
@SuppressWarnings("rawtypes")
private Deque[] getPhaseOperations(CompilationUnit compilationUnit) {
try {
Field field = CompilationUnit.class.getDeclaredField("phaseOperations");
field.setAccessible(true);
return (Deque[]) field.get(compilationUnit);
}
catch (Exception ex) {
throw new IllegalStateException("Phase operations not available from compilation unit");
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void processConversionOperations(LinkedList conversionOperations) {
int index = getIndexOfASTTransformationVisitor(conversionOperations);
conversionOperations.add(index, new CompilationUnit.ISourceUnitOperation() {
@Override
public void call(SourceUnit source) throws CompilationFailedException {
ASTNode[] nodes = new ASTNode[] { source.getAST() };
for (ASTTransformation transformation : GroovyCompiler.this.transformations) {
transformation.visit(nodes, source);
}
}
});
}
private int getIndexOfASTTransformationVisitor(List<?> conversionOperations) {
for (int index = 0; index < conversionOperations.size(); index++) {
if (conversionOperations.get(index).getClass().getName()
.startsWith(ASTTransformationVisitor.class.getName())) {
return index;
}
}
return conversionOperations.size();
}
/**
* {@link CompilationCustomizer} to call {@link CompilerAutoConfiguration}s.
*/
private class CompilerAutoConfigureCustomizer extends CompilationCustomizer {
CompilerAutoConfigureCustomizer() {
super(CompilePhase.CONVERSION);
}
@Override
public void call(SourceUnit source, GeneratorContext context, ClassNode classNode)
throws CompilationFailedException {
ImportCustomizer importCustomizer = new SmartImportCustomizer(source);
List<ClassNode> classNodes = source.getAST().getClasses();
ClassNode mainClassNode = MainClass.get(classNodes);
// Additional auto configuration
for (CompilerAutoConfiguration autoConfiguration : GroovyCompiler.this.compilerAutoConfigurations) {
if (classNodes.stream().anyMatch(autoConfiguration::matches)) {
if (GroovyCompiler.this.configuration.isGuessImports()) {
autoConfiguration.applyImports(importCustomizer);
importCustomizer.call(source, context, classNode);
}
if (classNode.equals(mainClassNode)) {
autoConfiguration.applyToMainClass(GroovyCompiler.this.loader,
GroovyCompiler.this.configuration, context, source, classNode);
}
autoConfiguration.apply(GroovyCompiler.this.loader, GroovyCompiler.this.configuration, context,
source, classNode);
}
}
importCustomizer.call(source, context, classNode);
}
}
private static class MainClass {
static ClassNode get(CompilationUnit source) {
return get(source.getAST().getClasses());
}
static ClassNode get(List<ClassNode> classes) {
for (ClassNode node : classes) {
if (AstUtils.hasAtLeastOneAnnotation(node, "Enable*AutoConfiguration")) {
return null; // No need to enhance this
}
if (AstUtils.hasAtLeastOneAnnotation(node, "*Controller", "Configuration", "Component", "*Service",
"Repository", "Enable*")) {
return node;
}
}
return classes.isEmpty() ? null : classes.get(0);
}
}
}

View File

@@ -1,81 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler;
import java.util.List;
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
/**
* Configuration for the {@link GroovyCompiler}.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.0.0
*/
public interface GroovyCompilerConfiguration {
/**
* Constant to be used when there is no {@link #getClasspath() classpath}.
*/
String[] DEFAULT_CLASSPATH = { "." };
/**
* Returns the scope in which the compiler operates.
* @return the scope of the compiler
*/
GroovyCompilerScope getScope();
/**
* Returns if import declarations should be guessed.
* @return {@code true} if imports should be guessed, otherwise {@code false}
*/
boolean isGuessImports();
/**
* Returns if jar dependencies should be guessed.
* @return {@code true} if dependencies should be guessed, otherwise {@code false}
*/
boolean isGuessDependencies();
/**
* Returns true if auto-configuration transformations should be applied.
* @return {@code true} if auto-configuration transformations should be applied,
* otherwise {@code false}
*/
boolean isAutoconfigure();
/**
* Returns the classpath for local resources.
* @return a path for local resources
*/
String[] getClasspath();
/**
* Returns the configuration for the repositories that will be used by the compiler to
* resolve dependencies.
* @return the repository configurations
*/
List<RepositoryConfiguration> getRepositoryConfiguration();
/**
* Returns if running in quiet mode.
* @return {@code true} if running in quiet mode
*/
boolean isQuiet();
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler;
/**
* The scope in which a groovy compiler operates.
*
* @author Phillip Webb
* @since 1.0.0
*/
public enum GroovyCompilerScope {
/**
* Default scope, exposes groovy.jar (loaded from the parent) and the shared cli
* package (loaded via groovy classloader).
*/
DEFAULT,
/**
* Extension scope, allows full access to internal CLI classes.
*/
EXTENSION
}

View File

@@ -1,129 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.maven.settings.Profile;
import org.apache.maven.settings.Repository;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.Interpolator;
import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
import org.springframework.boot.cli.compiler.maven.MavenSettings;
import org.springframework.boot.cli.compiler.maven.MavenSettingsReader;
import org.springframework.util.StringUtils;
/**
* Factory used to create {@link RepositoryConfiguration}s.
*
* @author Andy Wilkinson
* @author Dave Syer
* @since 1.0.0
*/
public final class RepositoryConfigurationFactory {
private static final RepositoryConfiguration MAVEN_CENTRAL = new RepositoryConfiguration("central",
URI.create("https://repo.maven.apache.org/maven2/"), false);
private static final RepositoryConfiguration SPRING_MILESTONE = new RepositoryConfiguration("spring-milestone",
URI.create("https://repo.spring.io/milestone"), false);
private static final RepositoryConfiguration SPRING_SNAPSHOT = new RepositoryConfiguration("spring-snapshot",
URI.create("https://repo.spring.io/snapshot"), true);
private RepositoryConfigurationFactory() {
}
/**
* Create a new default repository configuration.
* @return the newly-created default repository configuration
*/
public static List<RepositoryConfiguration> createDefaultRepositoryConfiguration() {
MavenSettings mavenSettings = new MavenSettingsReader().readSettings();
List<RepositoryConfiguration> repositoryConfiguration = new ArrayList<>();
repositoryConfiguration.add(MAVEN_CENTRAL);
if (!Boolean.getBoolean("disableSpringSnapshotRepos")) {
repositoryConfiguration.add(SPRING_MILESTONE);
repositoryConfiguration.add(SPRING_SNAPSHOT);
}
addDefaultCacheAsRepository(mavenSettings.getLocalRepository(), repositoryConfiguration);
addActiveProfileRepositories(mavenSettings.getActiveProfiles(), repositoryConfiguration);
return repositoryConfiguration;
}
private static void addDefaultCacheAsRepository(String localRepository,
List<RepositoryConfiguration> repositoryConfiguration) {
RepositoryConfiguration repository = new RepositoryConfiguration("local",
getLocalRepositoryDirectory(localRepository).toURI(), true);
if (!repositoryConfiguration.contains(repository)) {
repositoryConfiguration.add(0, repository);
}
}
private static void addActiveProfileRepositories(List<Profile> activeProfiles,
List<RepositoryConfiguration> configurations) {
for (Profile activeProfile : activeProfiles) {
Interpolator interpolator = new RegexBasedInterpolator();
interpolator.addValueSource(new PropertiesBasedValueSource(activeProfile.getProperties()));
for (Repository repository : activeProfile.getRepositories()) {
configurations.add(getRepositoryConfiguration(interpolator, repository));
}
}
}
private static RepositoryConfiguration getRepositoryConfiguration(Interpolator interpolator,
Repository repository) {
String name = interpolate(interpolator, repository.getId());
String url = interpolate(interpolator, repository.getUrl());
boolean snapshotsEnabled = false;
if (repository.getSnapshots() != null) {
snapshotsEnabled = repository.getSnapshots().isEnabled();
}
return new RepositoryConfiguration(name, URI.create(url), snapshotsEnabled);
}
private static String interpolate(Interpolator interpolator, String value) {
try {
return interpolator.interpolate(value);
}
catch (InterpolationException ex) {
return value;
}
}
private static File getLocalRepositoryDirectory(String localRepository) {
if (StringUtils.hasText(localRepository)) {
return new File(localRepository);
}
return new File(getM2HomeDirectory(), "repository");
}
private static File getM2HomeDirectory() {
String mavenRoot = System.getProperty("maven.home");
if (StringUtils.hasLength(mavenRoot)) {
return new File(mavenRoot);
}
return new File(System.getProperty("user.home"), ".m2");
}
}

View File

@@ -1,109 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import groovy.lang.Grab;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.transform.ASTTransformation;
import org.springframework.boot.cli.compiler.grape.DependencyResolutionContext;
import org.springframework.core.annotation.Order;
/**
* {@link ASTTransformation} to resolve {@link Grab @Grab} artifact coordinates.
*
* @author Andy Wilkinson
* @author Phillip Webb
* @since 1.0.0
*/
@Order(ResolveDependencyCoordinatesTransformation.ORDER)
public class ResolveDependencyCoordinatesTransformation extends AnnotatedNodeASTTransformation {
/**
* The order of the transformation.
*/
public static final int ORDER = DependencyManagementBomTransformation.ORDER + 300;
private static final Set<String> GRAB_ANNOTATION_NAMES = Collections
.unmodifiableSet(new HashSet<>(Arrays.asList(Grab.class.getName(), Grab.class.getSimpleName())));
private final DependencyResolutionContext resolutionContext;
public ResolveDependencyCoordinatesTransformation(DependencyResolutionContext resolutionContext) {
super(GRAB_ANNOTATION_NAMES, false);
this.resolutionContext = resolutionContext;
}
@Override
protected void processAnnotationNodes(List<AnnotationNode> annotationNodes) {
for (AnnotationNode annotationNode : annotationNodes) {
transformGrabAnnotation(annotationNode);
}
}
private void transformGrabAnnotation(AnnotationNode grabAnnotation) {
grabAnnotation.setMember("initClass", new ConstantExpression(false));
String value = getValue(grabAnnotation);
if (value != null && !isConvenienceForm(value)) {
applyGroupAndVersion(grabAnnotation, value);
}
}
private String getValue(AnnotationNode annotation) {
Expression expression = annotation.getMember("value");
if (expression instanceof ConstantExpression constantExpression) {
Object value = constantExpression.getValue();
return (value instanceof String string) ? string : null;
}
return null;
}
private boolean isConvenienceForm(String value) {
return value.contains(":") || value.contains("#");
}
private void applyGroupAndVersion(AnnotationNode annotation, String module) {
if (module != null) {
setMember(annotation, "module", module);
}
else {
Expression expression = annotation.getMembers().get("module");
module = (String) ((ConstantExpression) expression).getValue();
}
if (annotation.getMember("group") == null) {
setMember(annotation, "group", this.resolutionContext.getArtifactCoordinatesResolver().getGroupId(module));
}
if (annotation.getMember("version") == null) {
setMember(annotation, "version",
this.resolutionContext.getArtifactCoordinatesResolver().getVersion(module));
}
}
private void setMember(AnnotationNode annotation, String name, String value) {
ConstantExpression expression = new ConstantExpression(value);
annotation.setMember(name, expression);
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
/**
* Smart extension of {@link ImportCustomizer} that will only add a specific import if a
* class with the same name is not already explicitly imported.
*
* @author Dave Syer
*/
class SmartImportCustomizer extends ImportCustomizer {
private SourceUnit source;
SmartImportCustomizer(SourceUnit source) {
this.source = source;
}
@Override
public ImportCustomizer addImport(String alias, String className) {
if (this.source.getAST().getImport(ClassHelper.make(className).getNameWithoutPackage()) == null) {
super.addImport(alias, className);
}
return this;
}
@Override
public ImportCustomizer addImports(String... imports) {
for (String alias : imports) {
if (this.source.getAST().getImport(ClassHelper.make(alias).getNameWithoutPackage()) == null) {
super.addImports(alias);
}
}
return this;
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler;
import org.codehaus.groovy.transform.ASTTransformation;
/**
* Marker interface for AST transformations that should be installed automatically from
* {@code META-INF/services}.
*
* @author Dave Syer
* @since 1.0.0
*/
@FunctionalInterface
public interface SpringBootAstTransformation extends ASTTransformation {
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for the caching infrastructure.
*
* @author Stephane Nicoll
* @since 1.2.0
*/
public class CachingCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableCaching");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException {
dependencies.add("spring-context-support");
}
@Override
public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
imports.addStarImports("org.springframework.cache", "org.springframework.cache.annotation",
"org.springframework.cache.concurrent");
}
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
import org.springframework.boot.groovy.EnableGroovyTemplates;
import org.springframework.boot.groovy.GroovyTemplate;
/**
* {@link CompilerAutoConfiguration} for Groovy Templates (outside MVC).
*
* @author Dave Syer
* @since 1.1.0
*/
public class GroovyTemplatesCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableGroovyTemplates");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("groovy.text.TemplateEngine").add("groovy-templates");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addStarImports("groovy.text");
imports.addImports(EnableGroovyTemplates.class.getCanonicalName());
imports.addStaticImport(GroovyTemplate.class.getName(), "template");
}
}

View File

@@ -1,51 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring JDBC.
*
* @author Dave Syer
* @since 1.0.0
*/
public class JdbcCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneFieldOrMethod(classNode, "JdbcTemplate", "NamedParameterJdbcTemplate",
"DataSource");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("org.springframework.jdbc.core.JdbcTemplate").add("spring-boot-starter-jdbc");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addStarImports("org.springframework.jdbc.core", "org.springframework.jdbc.core.namedparam");
imports.addImports("javax.sql.DataSource");
}
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2012-2021 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring JMS.
*
* @author Greg Turnquist
* @author Stephane Nicoll
* @since 1.0.0
*/
public class JmsCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableJms")
|| AstUtils.hasAtLeastOneAnnotation(classNode, "EnableJmsMessaging");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException {
dependencies.add("spring-jms", "jakarta.jms-api");
}
@Override
public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
imports.addStarImports("jakarta.jms", "org.springframework.jms.annotation", "org.springframework.jms.config",
"org.springframework.jms.core", "org.springframework.jms.listener",
"org.springframework.jms.listener.adapter");
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring Rabbit.
*
* @author Greg Turnquist
* @author Stephane Nicoll
* @since 1.0.0
*/
public class RabbitCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableRabbit")
|| AstUtils.hasAtLeastOneAnnotation(classNode, "EnableRabbitMessaging");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) throws CompilationFailedException {
dependencies.add("spring-rabbit");
}
@Override
public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
imports.addStarImports("org.springframework.amqp.rabbit.annotation", "org.springframework.amqp.rabbit.core",
"org.springframework.amqp.rabbit.config", "org.springframework.amqp.rabbit.connection",
"org.springframework.amqp.rabbit.listener", "org.springframework.amqp.rabbit.listener.adapter",
"org.springframework.amqp.core");
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring Batch.
*
* @author Dave Syer
* @author Phillip Webb
* @since 1.0.0
*/
public class SpringBatchCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableBatchProcessing");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("org.springframework.batch.core.Job").add("spring-boot-starter-batch");
dependencies.ifAnyMissingClasses("org.springframework.jdbc.core.JdbcTemplate").add("spring-jdbc");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addImports("org.springframework.batch.repeat.RepeatStatus",
"org.springframework.batch.core.scope.context.ChunkContext",
"org.springframework.batch.core.step.tasklet.Tasklet",
"org.springframework.batch.core.configuration.annotation.StepScope",
"org.springframework.batch.core.configuration.annotation.JobBuilderFactory",
"org.springframework.batch.core.configuration.annotation.StepBuilderFactory",
"org.springframework.batch.core.configuration.annotation.EnableBatchProcessing",
"org.springframework.batch.core.Step", "org.springframework.batch.core.StepExecution",
"org.springframework.batch.core.StepContribution", "org.springframework.batch.core.Job",
"org.springframework.batch.core.JobExecution", "org.springframework.batch.core.JobParameter",
"org.springframework.batch.core.JobParameters", "org.springframework.batch.core.launch.JobLauncher",
"org.springframework.batch.core.converter.JobParametersConverter",
"org.springframework.batch.core.converter.DefaultJobParametersConverter");
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright 2012-2021 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
*
* https://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.cli.compiler.autoconfigure;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
/**
* {@link CompilerAutoConfiguration} for Spring.
*
* @author Dave Syer
* @author Phillip Webb
* @since 1.0.0
*/
public class SpringBootCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("org.springframework.boot.SpringApplication").add("spring-boot-starter");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addImports("jakarta.annotation.PostConstruct", "jakarta.annotation.PreDestroy",
"groovy.util.logging.Log", "org.springframework.stereotype.Controller",
"org.springframework.stereotype.Service", "org.springframework.stereotype.Component",
"org.springframework.beans.factory.annotation.Autowired",
"org.springframework.beans.factory.annotation.Value", "org.springframework.context.annotation.Import",
"org.springframework.context.annotation.ImportResource",
"org.springframework.context.annotation.Profile", "org.springframework.context.annotation.Scope",
"org.springframework.context.annotation.Configuration",
"org.springframework.context.annotation.ComponentScan", "org.springframework.context.annotation.Bean",
"org.springframework.context.ApplicationContext", "org.springframework.context.MessageSource",
"org.springframework.core.annotation.Order", "org.springframework.core.io.ResourceLoader",
"org.springframework.boot.ApplicationRunner", "org.springframework.boot.ApplicationArguments",
"org.springframework.boot.CommandLineRunner",
"org.springframework.boot.context.properties.ConfigurationProperties",
"org.springframework.boot.context.properties.EnableConfigurationProperties",
"org.springframework.boot.autoconfigure.EnableAutoConfiguration",
"org.springframework.boot.autoconfigure.SpringBootApplication",
"org.springframework.boot.context.properties.ConfigurationProperties",
"org.springframework.boot.context.properties.EnableConfigurationProperties");
imports.addStarImports("org.springframework.stereotype", "org.springframework.scheduling.annotation");
}
@Override
public void applyToMainClass(GroovyClassLoader loader, GroovyCompilerConfiguration configuration,
GeneratorContext generatorContext, SourceUnit source, ClassNode classNode)
throws CompilationFailedException {
addEnableAutoConfigurationAnnotation(classNode);
}
private void addEnableAutoConfigurationAnnotation(ClassNode classNode) {
if (!hasEnableAutoConfigureAnnotation(classNode)) {
AnnotationNode annotationNode = new AnnotationNode(ClassHelper.make("EnableAutoConfiguration"));
classNode.addAnnotation(annotationNode);
}
}
private boolean hasEnableAutoConfigureAnnotation(ClassNode classNode) {
for (AnnotationNode node : classNode.getAnnotations()) {
String name = node.getClassNode().getNameWithoutPackage();
if ("EnableAutoConfiguration".equals(name) || "SpringBootApplication".equals(name)) {
return true;
}
}
return false;
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring Integration.
*
* @author Dave Syer
* @author Artem Bilan
* @since 1.0.0
*/
public class SpringIntegrationCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableIntegration")
|| AstUtils.hasAtLeastOneAnnotation(classNode, "MessageEndpoint");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("org.springframework.integration.config.EnableIntegration")
.add("spring-boot-starter-integration");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addImports("org.springframework.messaging.Message", "org.springframework.messaging.MessageChannel",
"org.springframework.messaging.PollableChannel", "org.springframework.messaging.SubscribableChannel",
"org.springframework.messaging.MessageHeaders",
"org.springframework.integration.support.MessageBuilder",
"org.springframework.integration.channel.DirectChannel",
"org.springframework.integration.channel.QueueChannel",
"org.springframework.integration.channel.ExecutorChannel",
"org.springframework.integration.core.MessagingTemplate",
"org.springframework.integration.config.EnableIntegration");
imports.addStarImports("org.springframework.integration.annotation");
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
import org.springframework.boot.groovy.GroovyTemplate;
/**
* {@link CompilerAutoConfiguration} for Spring MVC.
*
* @author Dave Syer
* @author Phillip Webb
* @since 1.0.0
*/
public class SpringMvcCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "Controller", "RestController", "EnableWebMvc");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("org.springframework.web.servlet.mvc.Controller")
.add("spring-boot-starter-web");
dependencies.ifAnyMissingClasses("groovy.text.TemplateEngine").add("groovy-templates");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addStarImports("org.springframework.web.bind.annotation",
"org.springframework.web.servlet.config.annotation", "org.springframework.web.servlet",
"org.springframework.http", "org.springframework.web.servlet.handler", "org.springframework.http",
"org.springframework.ui", "groovy.text");
imports.addStaticImport(GroovyTemplate.class.getName(), "template");
}
}

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring Retry.
*
* @author Dave Syer
* @since 1.3.0
*/
public class SpringRetryCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableRetry", "Retryable", "Recover");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("org.springframework.retry.annotation.EnableRetry").add("spring-retry",
"spring-boot-starter-aop");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addStarImports("org.springframework.retry.annotation");
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring Security.
*
* @author Dave Syer
* @since 1.0.0
*/
public class SpringSecurityCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableWebSecurity", "EnableGlobalMethodSecurity");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses(
"org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity")
.add("spring-boot-starter-security");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addImports("org.springframework.security.core.Authentication",
"org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity",
"org.springframework.security.core.authority.AuthorityUtils")
.addStarImports("org.springframework.security.config.annotation.web.configuration",
"org.springframework.security.authentication",
"org.springframework.security.config.annotation.web",
"org.springframework.security.config.annotation.web.builders");
}
}

View File

@@ -1,73 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.classgen.GeneratorContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
/**
* {@link CompilerAutoConfiguration} for Spring Test.
*
* @author Dave Syer
* @since 1.1.0
*/
public class SpringTestCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "SpringBootTest");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("org.springframework.http.HttpHeaders").add("spring-boot-starter-web");
}
@Override
public void apply(GroovyClassLoader loader, GroovyCompilerConfiguration configuration,
GeneratorContext generatorContext, SourceUnit source, ClassNode classNode)
throws CompilationFailedException {
if (!AstUtils.hasAtLeastOneAnnotation(classNode, "RunWith")) {
AnnotationNode runWith = new AnnotationNode(ClassHelper.make("RunWith"));
runWith.addMember("value", new ClassExpression(ClassHelper.make("SpringRunner")));
classNode.addAnnotation(runWith);
}
}
@Override
public void applyImports(ImportCustomizer imports) throws CompilationFailedException {
imports.addStarImports("org.junit.runner", "org.springframework.boot.test",
"org.springframework.boot.test.context", "org.springframework.boot.test.web.client",
"org.springframework.http", "org.springframework.test.context.junit4",
"org.springframework.test.annotation")
.addImports("org.springframework.boot.test.context.SpringBootTest.WebEnvironment",
"org.springframework.boot.test.web.client.TestRestTemplate");
}
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring Websocket.
*
* @author Dave Syer
* @since 1.0.0
*/
public class SpringWebsocketCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableWebSocket", "EnableWebSocketMessageBroker");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("org.springframework.web.socket.config.annotation.EnableWebSocket")
.add("spring-boot-starter-websocket").add("spring-messaging");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addStarImports("org.springframework.messaging.handler.annotation",
"org.springframework.messaging.simp.config", "org.springframework.web.socket.handler",
"org.springframework.web.socket.sockjs.transport.handler",
"org.springframework.web.socket.config.annotation")
.addImports("org.springframework.web.socket.WebSocketHandler");
}
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.autoconfigure;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.control.customizers.ImportCustomizer;
import org.springframework.boot.cli.compiler.AstUtils;
import org.springframework.boot.cli.compiler.CompilerAutoConfiguration;
import org.springframework.boot.cli.compiler.DependencyCustomizer;
/**
* {@link CompilerAutoConfiguration} for Spring MVC.
*
* @author Dave Syer
* @author Phillip Webb
* @since 1.0.0
*/
public class TransactionManagementCompilerAutoConfiguration extends CompilerAutoConfiguration {
@Override
public boolean matches(ClassNode classNode) {
return AstUtils.hasAtLeastOneAnnotation(classNode, "EnableTransactionManagement");
}
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("org.springframework.transaction.annotation.Transactional").add("spring-tx",
"spring-boot-starter-aop");
}
@Override
public void applyImports(ImportCustomizer imports) {
imports.addStarImports("org.springframework.transaction.annotation", "org.springframework.transaction.support");
imports.addImports("org.springframework.transaction.PlatformTransactionManager",
"org.springframework.transaction.support.AbstractPlatformTransactionManager");
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* Classes for auto-configuring the Groovy compiler.
*/
package org.springframework.boot.cli.compiler.autoconfigure;

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.dependencies;
/**
* A resolver for artifacts' Maven coordinates, allowing group id, artifact id, or version
* to be obtained from a module identifier. A module identifier may be in the form
* {@code groupId:artifactId:version}, in which case coordinate resolution simply extracts
* the relevant piece from the identifier. Alternatively the identifier may be in the form
* {@code artifactId}, in which case coordinate resolution uses implementation-specific
* metadata to resolve the groupId and version.
*
* @author Andy Wilkinson
* @since 1.0.0
*/
public interface ArtifactCoordinatesResolver {
/**
* Gets the group id of the artifact identified by the given {@code module}. Returns
* {@code null} if the artifact is unknown to the resolver.
* @param module the id of the module
* @return the group id of the module
*/
String getGroupId(String module);
/**
* Gets the artifact id of the artifact identified by the given {@code module}.
* Returns {@code null} if the artifact is unknown to the resolver.
* @param module the id of the module
* @return the artifact id of the module
*/
String getArtifactId(String module);
/**
* Gets the version of the artifact identified by the given {@code module}. Returns
* {@code null} if the artifact is unknown to the resolver.
* @param module the id of the module
* @return the version of the module
*/
String getVersion(String module);
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.dependencies;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* {@link DependencyManagement} that delegates to one or more {@link DependencyManagement}
* instances.
*
* @author Andy Wilkinson
* @since 1.3.0
*/
public class CompositeDependencyManagement implements DependencyManagement {
private final List<DependencyManagement> delegates;
private final List<Dependency> dependencies = new ArrayList<>();
public CompositeDependencyManagement(DependencyManagement... delegates) {
this.delegates = Arrays.asList(delegates);
for (DependencyManagement delegate : delegates) {
this.dependencies.addAll(delegate.getDependencies());
}
}
@Override
public List<Dependency> getDependencies() {
return this.dependencies;
}
@Override
public String getSpringBootVersion() {
for (DependencyManagement delegate : this.delegates) {
String version = delegate.getSpringBootVersion();
if (version != null) {
return version;
}
}
return null;
}
@Override
public Dependency find(String artifactId) {
for (DependencyManagement delegate : this.delegates) {
Dependency found = delegate.find(artifactId);
if (found != null) {
return found;
}
}
return null;
}
}

View File

@@ -1,198 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.dependencies;
import java.util.Collections;
import java.util.List;
import org.springframework.util.Assert;
/**
* A single dependency.
*
* @author Phillip Webb
* @since 1.3.0
*/
public final class Dependency {
private final String groupId;
private final String artifactId;
private final String version;
private final List<Exclusion> exclusions;
/**
* Create a new {@link Dependency} instance.
* @param groupId the group ID
* @param artifactId the artifact ID
* @param version the version
*/
public Dependency(String groupId, String artifactId, String version) {
this(groupId, artifactId, version, Collections.emptyList());
}
/**
* Create a new {@link Dependency} instance.
* @param groupId the group ID
* @param artifactId the artifact ID
* @param version the version
* @param exclusions the exclusions
*/
public Dependency(String groupId, String artifactId, String version, List<Exclusion> exclusions) {
Assert.notNull(groupId, "GroupId must not be null");
Assert.notNull(artifactId, "ArtifactId must not be null");
Assert.notNull(version, "Version must not be null");
Assert.notNull(exclusions, "Exclusions must not be null");
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.exclusions = Collections.unmodifiableList(exclusions);
}
/**
* Return the dependency group id.
* @return the group ID
*/
public String getGroupId() {
return this.groupId;
}
/**
* Return the dependency artifact id.
* @return the artifact ID
*/
public String getArtifactId() {
return this.artifactId;
}
/**
* Return the dependency version.
* @return the version
*/
public String getVersion() {
return this.version;
}
/**
* Return the dependency exclusions.
* @return the exclusions
*/
public List<Exclusion> getExclusions() {
return this.exclusions;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() == obj.getClass()) {
Dependency other = (Dependency) obj;
boolean result = true;
result = result && this.groupId.equals(other.groupId);
result = result && this.artifactId.equals(other.artifactId);
result = result && this.version.equals(other.version);
result = result && this.exclusions.equals(other.exclusions);
return result;
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + this.groupId.hashCode();
result = prime * result + this.artifactId.hashCode();
result = prime * result + this.version.hashCode();
result = prime * result + this.exclusions.hashCode();
return result;
}
@Override
public String toString() {
return this.groupId + ":" + this.artifactId + ":" + this.version;
}
/**
* A dependency exclusion.
*/
public static final class Exclusion {
private final String groupId;
private final String artifactId;
Exclusion(String groupId, String artifactId) {
Assert.notNull(groupId, "GroupId must not be null");
Assert.notNull(artifactId, "ArtifactId must not be null");
this.groupId = groupId;
this.artifactId = artifactId;
}
/**
* Return the exclusion artifact ID.
* @return the exclusion artifact ID
*/
public String getArtifactId() {
return this.artifactId;
}
/**
* Return the exclusion group ID.
* @return the exclusion group ID
*/
public String getGroupId() {
return this.groupId;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() == obj.getClass()) {
Exclusion other = (Exclusion) obj;
boolean result = true;
result = result && this.groupId.equals(other.groupId);
result = result && this.artifactId.equals(other.artifactId);
return result;
}
return false;
}
@Override
public int hashCode() {
return this.groupId.hashCode() * 31 + this.artifactId.hashCode();
}
@Override
public String toString() {
return this.groupId + ":" + this.artifactId;
}
}
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.dependencies;
import java.util.List;
/**
* An encapsulation of dependency management information.
*
* @author Andy Wilkinson
* @since 1.3.0
*/
public interface DependencyManagement {
/**
* Returns the managed dependencies.
* @return the managed dependencies
*/
List<Dependency> getDependencies();
/**
* Returns the managed version of Spring Boot. May be {@code null}.
* @return the Spring Boot version, or {@code null}
*/
String getSpringBootVersion();
/**
* Finds the managed dependency with the given {@code artifactId}.
* @param artifactId the artifact ID of the dependency to find
* @return the dependency, or {@code null}
*/
Dependency find(String artifactId);
}

View File

@@ -1,73 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.dependencies;
import org.springframework.util.StringUtils;
/**
* {@link ArtifactCoordinatesResolver} backed by
* {@link SpringBootDependenciesDependencyManagement}.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.0.0
*/
public class DependencyManagementArtifactCoordinatesResolver implements ArtifactCoordinatesResolver {
private final DependencyManagement dependencyManagement;
public DependencyManagementArtifactCoordinatesResolver() {
this(new SpringBootDependenciesDependencyManagement());
}
public DependencyManagementArtifactCoordinatesResolver(DependencyManagement dependencyManagement) {
this.dependencyManagement = dependencyManagement;
}
@Override
public String getGroupId(String artifactId) {
Dependency dependency = find(artifactId);
return (dependency != null) ? dependency.getGroupId() : null;
}
@Override
public String getArtifactId(String id) {
Dependency dependency = find(id);
return (dependency != null) ? dependency.getArtifactId() : null;
}
private Dependency find(String id) {
if (StringUtils.countOccurrencesOf(id, ":") == 2) {
String[] tokens = id.split(":");
return new Dependency(tokens[0], tokens[1], tokens[2]);
}
if (id != null) {
if (id.startsWith("spring-boot")) {
return new Dependency("org.springframework.boot", id, this.dependencyManagement.getSpringBootVersion());
}
return this.dependencyManagement.find(id);
}
return null;
}
@Override
public String getVersion(String module) {
Dependency dependency = find(module);
return (dependency != null) ? dependency.getVersion() : null;
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.dependencies;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.maven.model.Model;
import org.springframework.boot.cli.compiler.dependencies.Dependency.Exclusion;
/**
* {@link DependencyManagement} derived from a Maven {@link Model}.
*
* @author Andy Wilkinson
* @since 1.3.0
*/
public class MavenModelDependencyManagement implements DependencyManagement {
private final List<Dependency> dependencies;
private final Map<String, Dependency> byArtifactId = new LinkedHashMap<>();
public MavenModelDependencyManagement(Model model) {
this.dependencies = extractDependenciesFromModel(model);
for (Dependency dependency : this.dependencies) {
this.byArtifactId.put(dependency.getArtifactId(), dependency);
}
}
private static List<Dependency> extractDependenciesFromModel(Model model) {
List<Dependency> dependencies = new ArrayList<>();
for (org.apache.maven.model.Dependency mavenDependency : model.getDependencyManagement().getDependencies()) {
List<Exclusion> exclusions = new ArrayList<>();
for (org.apache.maven.model.Exclusion mavenExclusion : mavenDependency.getExclusions()) {
exclusions.add(new Exclusion(mavenExclusion.getGroupId(), mavenExclusion.getArtifactId()));
}
Dependency dependency = new Dependency(mavenDependency.getGroupId(), mavenDependency.getArtifactId(),
mavenDependency.getVersion(), exclusions);
dependencies.add(dependency);
}
return dependencies;
}
@Override
public List<Dependency> getDependencies() {
return this.dependencies;
}
@Override
public String getSpringBootVersion() {
return find("spring-boot").getVersion();
}
@Override
public Dependency find(String artifactId) {
return this.byArtifactId.get(artifactId);
}
}

View File

@@ -1,53 +0,0 @@
/*
* Copyright 2012-2020 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
*
* https://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.cli.compiler.dependencies;
import java.io.IOException;
import org.apache.maven.model.Model;
import org.apache.maven.model.building.DefaultModelProcessor;
import org.apache.maven.model.io.DefaultModelReader;
import org.apache.maven.model.locator.DefaultModelLocator;
/**
* {@link DependencyManagement} derived from the effective pom of
* {@code spring-boot-dependencies}.
*
* @author Andy Wilkinson
* @since 1.3.0
*/
public class SpringBootDependenciesDependencyManagement extends MavenModelDependencyManagement {
public SpringBootDependenciesDependencyManagement() {
super(readModel());
}
private static Model readModel() {
DefaultModelProcessor modelProcessor = new DefaultModelProcessor();
modelProcessor.setModelLocator(new DefaultModelLocator());
modelProcessor.setModelReader(new DefaultModelReader());
try {
return modelProcessor.read(SpringBootDependenciesDependencyManagement.class
.getResourceAsStream("spring-boot-dependencies-effective-bom.xml"), null);
}
catch (IOException ex) {
throw new IllegalStateException("Failed to build model from effective pom", ex);
}
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* Classes for dependencies used during compilation.
*/
package org.springframework.boot.cli.compiler.dependencies;

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.grape;
import java.util.List;
import org.eclipse.aether.repository.Proxy;
import org.eclipse.aether.repository.ProxySelector;
import org.eclipse.aether.repository.RemoteRepository;
/**
* Composite {@link ProxySelector}.
*
* @author Dave Syer
* @since 1.1.0
*/
public class CompositeProxySelector implements ProxySelector {
private final List<ProxySelector> selectors;
public CompositeProxySelector(List<ProxySelector> selectors) {
this.selectors = selectors;
}
@Override
public Proxy getProxy(RemoteRepository repository) {
for (ProxySelector selector : this.selectors) {
Proxy proxy = selector.getProxy(repository);
if (proxy != null) {
return proxy;
}
}
return null;
}
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright 2012-2020 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
*
* https://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.cli.compiler.grape;
import java.io.File;
import java.util.Arrays;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.eclipse.aether.repository.ProxySelector;
import org.eclipse.aether.util.repository.JreProxySelector;
import org.springframework.util.StringUtils;
/**
* A {@link RepositorySystemSessionAutoConfiguration} that, in the absence of any
* configuration, applies sensible defaults.
*
* @author Andy Wilkinson
* @since 1.0.0
*/
public class DefaultRepositorySystemSessionAutoConfiguration implements RepositorySystemSessionAutoConfiguration {
@Override
public void apply(DefaultRepositorySystemSession session, RepositorySystem repositorySystem) {
if (session.getLocalRepositoryManager() == null) {
LocalRepository localRepository = new LocalRepository(getM2RepoDirectory());
LocalRepositoryManager localRepositoryManager = repositorySystem.newLocalRepositoryManager(session,
localRepository);
session.setLocalRepositoryManager(localRepositoryManager);
}
ProxySelector existing = session.getProxySelector();
if (!(existing instanceof CompositeProxySelector)) {
JreProxySelector fallback = new JreProxySelector();
ProxySelector selector = (existing != null) ? new CompositeProxySelector(Arrays.asList(existing, fallback))
: fallback;
session.setProxySelector(selector);
}
}
private File getM2RepoDirectory() {
return new File(getDefaultM2HomeDirectory(), "repository");
}
private File getDefaultM2HomeDirectory() {
String mavenRoot = System.getProperty("maven.home");
if (StringUtils.hasLength(mavenRoot)) {
return new File(mavenRoot);
}
return new File(System.getProperty("user.home"), ".m2");
}
}

View File

@@ -1,107 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.grape;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.Exclusion;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.springframework.boot.cli.compiler.dependencies.ArtifactCoordinatesResolver;
import org.springframework.boot.cli.compiler.dependencies.CompositeDependencyManagement;
import org.springframework.boot.cli.compiler.dependencies.DependencyManagement;
import org.springframework.boot.cli.compiler.dependencies.DependencyManagementArtifactCoordinatesResolver;
/**
* Context used when resolving dependencies.
*
* @author Andy Wilkinson
* @since 1.1.0
*/
public class DependencyResolutionContext {
private final Map<String, Dependency> managedDependencyByGroupAndArtifact = new HashMap<>();
private final List<Dependency> managedDependencies = new ArrayList<>();
private DependencyManagement dependencyManagement = null;
private ArtifactCoordinatesResolver artifactCoordinatesResolver;
private String getIdentifier(Dependency dependency) {
return getIdentifier(dependency.getArtifact().getGroupId(), dependency.getArtifact().getArtifactId());
}
private String getIdentifier(String groupId, String artifactId) {
return groupId + ":" + artifactId;
}
public ArtifactCoordinatesResolver getArtifactCoordinatesResolver() {
return this.artifactCoordinatesResolver;
}
public String getManagedVersion(String groupId, String artifactId) {
Dependency dependency = getManagedDependency(groupId, artifactId);
if (dependency == null) {
dependency = this.managedDependencyByGroupAndArtifact.get(getIdentifier(groupId, artifactId));
}
return (dependency != null) ? dependency.getArtifact().getVersion() : null;
}
public List<Dependency> getManagedDependencies() {
return Collections.unmodifiableList(this.managedDependencies);
}
private Dependency getManagedDependency(String group, String artifact) {
return this.managedDependencyByGroupAndArtifact.get(getIdentifier(group, artifact));
}
public void addManagedDependencies(List<Dependency> dependencies) {
this.managedDependencies.addAll(dependencies);
for (Dependency dependency : dependencies) {
this.managedDependencyByGroupAndArtifact.put(getIdentifier(dependency), dependency);
}
}
public void addDependencyManagement(DependencyManagement dependencyManagement) {
for (org.springframework.boot.cli.compiler.dependencies.Dependency dependency : dependencyManagement
.getDependencies()) {
List<Exclusion> aetherExclusions = new ArrayList<>();
for (org.springframework.boot.cli.compiler.dependencies.Dependency.Exclusion exclusion : dependency
.getExclusions()) {
aetherExclusions.add(new Exclusion(exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*"));
}
Dependency aetherDependency = new Dependency(new DefaultArtifact(dependency.getGroupId(),
dependency.getArtifactId(), "jar", dependency.getVersion()), JavaScopes.COMPILE, false,
aetherExclusions);
this.managedDependencies.add(0, aetherDependency);
this.managedDependencyByGroupAndArtifact.put(getIdentifier(aetherDependency), aetherDependency);
}
this.dependencyManagement = (this.dependencyManagement != null)
? new CompositeDependencyManagement(dependencyManagement, this.dependencyManagement)
: dependencyManagement;
this.artifactCoordinatesResolver = new DependencyManagementArtifactCoordinatesResolver(
this.dependencyManagement);
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.grape;
/**
* Thrown to indicate a failure during dependency resolution.
*
* @author Andy Wilkinson
* @since 1.0.0
*/
@SuppressWarnings("serial")
public class DependencyResolutionFailedException extends RuntimeException {
/**
* Creates a new {@code DependencyResolutionFailedException} with the given
* {@code cause}.
* @param cause the cause of the resolution failure
*/
public DependencyResolutionFailedException(Throwable cause) {
super(cause);
}
}

View File

@@ -1,66 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.grape;
import java.io.PrintStream;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.transfer.AbstractTransferListener;
import org.eclipse.aether.transfer.TransferCancelledException;
import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.transfer.TransferResource;
/**
* Provide detailed progress feedback for long running resolves.
*
* @author Andy Wilkinson
*/
final class DetailedProgressReporter implements ProgressReporter {
DetailedProgressReporter(DefaultRepositorySystemSession session, final PrintStream out) {
session.setTransferListener(new AbstractTransferListener() {
@Override
public void transferStarted(TransferEvent event) throws TransferCancelledException {
out.println("Downloading: " + getResourceIdentifier(event.getResource()));
}
@Override
public void transferSucceeded(TransferEvent event) {
out.printf("Downloaded: %s (%s)%n", getResourceIdentifier(event.getResource()),
getTransferSpeed(event));
}
});
}
private String getResourceIdentifier(TransferResource resource) {
return resource.getRepositoryUrl() + resource.getResourceName();
}
private String getTransferSpeed(TransferEvent event) {
long kb = event.getTransferredBytes() / 1024;
float seconds = (System.currentTimeMillis() - event.getResource().getTransferStartTime()) / 1000.0f;
return String.format("%dKB at %.1fKB/sec", kb, (kb / seconds));
}
@Override
public void finished() {
}
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.grape;
import java.lang.reflect.Field;
import groovy.grape.Grape;
import groovy.grape.GrapeEngine;
/**
* Utility to install a specific {@link Grape} engine with Groovy.
*
* @author Andy Wilkinson
* @since 1.0.0
*/
public abstract class GrapeEngineInstaller {
public static void install(GrapeEngine engine) {
synchronized (Grape.class) {
try {
Field field = Grape.class.getDeclaredField("instance");
field.setAccessible(true);
field.set(null, engine);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to install GrapeEngine", ex);
}
}
}
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.grape;
import java.io.File;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.springframework.util.StringUtils;
/**
* Honours the configuration of {@code grape.root} by customizing the session's local
* repository location.
*
* @author Andy Wilkinson
* @since 1.2.5
*/
public class GrapeRootRepositorySystemSessionAutoConfiguration implements RepositorySystemSessionAutoConfiguration {
@Override
public void apply(DefaultRepositorySystemSession session, RepositorySystem repositorySystem) {
String grapeRoot = System.getProperty("grape.root");
if (StringUtils.hasLength(grapeRoot)) {
configureLocalRepository(session, repositorySystem, grapeRoot);
}
}
private void configureLocalRepository(DefaultRepositorySystemSession session, RepositorySystem repositorySystem,
String grapeRoot) {
File repositoryDir = new File(grapeRoot, "repository");
LocalRepository localRepository = new LocalRepository(repositoryDir);
LocalRepositoryManager localRepositoryManager = repositorySystem.newLocalRepositoryManager(session,
localRepository);
session.setLocalRepositoryManager(localRepositoryManager);
}
}

View File

@@ -1,330 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler.grape;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import groovy.grape.GrapeEngine;
import groovy.lang.GroovyClassLoader;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.Exclusion;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResult;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.eclipse.aether.util.filter.DependencyFilterUtils;
/**
* A {@link GrapeEngine} implementation that uses
* <a href="https://maven.apache.org/resolver/index.html">Maven Resolver</a>, the
* dependency resolution system used by Maven.
*
* @author Andy Wilkinson
* @author Phillip Webb
* @since 2.5.9
*/
@SuppressWarnings("rawtypes")
public class MavenResolverGrapeEngine implements GrapeEngine {
private static final Collection<Exclusion> WILDCARD_EXCLUSION;
static {
List<Exclusion> exclusions = new ArrayList<>();
exclusions.add(new Exclusion("*", "*", "*", "*"));
WILDCARD_EXCLUSION = Collections.unmodifiableList(exclusions);
}
private final DependencyResolutionContext resolutionContext;
private final ProgressReporter progressReporter;
private final GroovyClassLoader classLoader;
private final DefaultRepositorySystemSession session;
private final RepositorySystem repositorySystem;
private final List<RemoteRepository> repositories;
public MavenResolverGrapeEngine(GroovyClassLoader classLoader, RepositorySystem repositorySystem,
DefaultRepositorySystemSession repositorySystemSession, List<RemoteRepository> remoteRepositories,
DependencyResolutionContext resolutionContext, boolean quiet) {
this.classLoader = classLoader;
this.repositorySystem = repositorySystem;
this.session = repositorySystemSession;
this.resolutionContext = resolutionContext;
this.repositories = new ArrayList<>();
List<RemoteRepository> remotes = new ArrayList<>(remoteRepositories);
Collections.reverse(remotes); // priority is reversed in addRepository
for (RemoteRepository repository : remotes) {
addRepository(repository);
}
this.progressReporter = getProgressReporter(this.session, quiet);
}
private ProgressReporter getProgressReporter(DefaultRepositorySystemSession session, boolean quiet) {
String progressReporter = (quiet ? "none"
: System.getProperty("org.springframework.boot.cli.compiler.grape.ProgressReporter"));
if ("detail".equals(progressReporter) || Boolean.getBoolean("groovy.grape.report.downloads")) {
return new DetailedProgressReporter(session, System.out);
}
if ("none".equals(progressReporter)) {
return () -> {
};
}
return new SummaryProgressReporter(session, System.out);
}
@Override
public Object grab(Map args) {
return grab(args, args);
}
@Override
public Object grab(Map args, Map... dependencyMaps) {
List<Exclusion> exclusions = createExclusions(args);
List<Dependency> dependencies = createDependencies(dependencyMaps, exclusions);
try {
List<File> files = resolve(dependencies);
GroovyClassLoader classLoader = getClassLoader(args);
for (File file : files) {
classLoader.addURL(file.toURI().toURL());
}
}
catch (MalformedURLException ex) {
throw new DependencyResolutionFailedException(ex);
}
return null;
}
@SuppressWarnings("unchecked")
private List<Exclusion> createExclusions(Map<?, ?> args) {
List<Exclusion> exclusions = new ArrayList<>();
if (args != null) {
List<Map<String, Object>> exclusionMaps = (List<Map<String, Object>>) args.get("excludes");
if (exclusionMaps != null) {
for (Map<String, Object> exclusionMap : exclusionMaps) {
exclusions.add(createExclusion(exclusionMap));
}
}
}
return exclusions;
}
private Exclusion createExclusion(Map<String, Object> exclusionMap) {
String group = (String) exclusionMap.get("group");
String module = (String) exclusionMap.get("module");
return new Exclusion(group, module, "*", "*");
}
private List<Dependency> createDependencies(Map<?, ?>[] dependencyMaps, List<Exclusion> exclusions) {
List<Dependency> dependencies = new ArrayList<>(dependencyMaps.length);
for (Map<?, ?> dependencyMap : dependencyMaps) {
dependencies.add(createDependency(dependencyMap, exclusions));
}
return dependencies;
}
private Dependency createDependency(Map<?, ?> dependencyMap, List<Exclusion> exclusions) {
Artifact artifact = createArtifact(dependencyMap);
if (isTransitive(dependencyMap)) {
return new Dependency(artifact, JavaScopes.COMPILE, false, exclusions);
}
return new Dependency(artifact, JavaScopes.COMPILE, null, WILDCARD_EXCLUSION);
}
private Artifact createArtifact(Map<?, ?> dependencyMap) {
String group = (String) dependencyMap.get("group");
String module = (String) dependencyMap.get("module");
String version = (String) dependencyMap.get("version");
if (version == null) {
version = this.resolutionContext.getManagedVersion(group, module);
}
String classifier = (String) dependencyMap.get("classifier");
String type = determineType(dependencyMap);
return new DefaultArtifact(group, module, classifier, type, version);
}
private String determineType(Map<?, ?> dependencyMap) {
String type = (String) dependencyMap.get("type");
String ext = (String) dependencyMap.get("ext");
if (type == null) {
type = ext;
if (type == null) {
type = "jar";
}
}
else if (ext != null && !type.equals(ext)) {
throw new IllegalArgumentException("If both type and ext are specified they must have the same value");
}
return type;
}
private boolean isTransitive(Map<?, ?> dependencyMap) {
Boolean transitive = (Boolean) dependencyMap.get("transitive");
return (transitive != null) ? transitive : true;
}
private List<Dependency> getDependencies(DependencyResult dependencyResult) {
List<Dependency> dependencies = new ArrayList<>();
for (ArtifactResult artifactResult : dependencyResult.getArtifactResults()) {
dependencies.add(new Dependency(artifactResult.getArtifact(), JavaScopes.COMPILE));
}
return dependencies;
}
private List<File> getFiles(DependencyResult dependencyResult) {
List<File> files = new ArrayList<>();
for (ArtifactResult result : dependencyResult.getArtifactResults()) {
files.add(result.getArtifact().getFile());
}
return files;
}
private GroovyClassLoader getClassLoader(Map args) {
GroovyClassLoader classLoader = (GroovyClassLoader) args.get("classLoader");
return (classLoader != null) ? classLoader : this.classLoader;
}
@Override
public void addResolver(Map<String, Object> args) {
String name = (String) args.get("name");
String root = (String) args.get("root");
RemoteRepository.Builder builder = new RemoteRepository.Builder(name, "default", root);
RemoteRepository repository = builder.build();
addRepository(repository);
}
protected void addRepository(RemoteRepository repository) {
if (this.repositories.contains(repository)) {
return;
}
repository = getPossibleMirror(repository);
repository = applyProxy(repository);
repository = applyAuthentication(repository);
this.repositories.add(0, repository);
}
private RemoteRepository getPossibleMirror(RemoteRepository remoteRepository) {
RemoteRepository mirror = this.session.getMirrorSelector().getMirror(remoteRepository);
if (mirror != null) {
return mirror;
}
return remoteRepository;
}
private RemoteRepository applyProxy(RemoteRepository repository) {
if (repository.getProxy() == null) {
RemoteRepository.Builder builder = new RemoteRepository.Builder(repository);
builder.setProxy(this.session.getProxySelector().getProxy(repository));
repository = builder.build();
}
return repository;
}
private RemoteRepository applyAuthentication(RemoteRepository repository) {
if (repository.getAuthentication() == null) {
RemoteRepository.Builder builder = new RemoteRepository.Builder(repository);
builder.setAuthentication(this.session.getAuthenticationSelector().getAuthentication(repository));
repository = builder.build();
}
return repository;
}
@Override
public Map<String, Map<String, List<String>>> enumerateGrapes() {
throw new UnsupportedOperationException("Grape enumeration is not supported");
}
@Override
public URI[] resolve(Map args, Map... dependencyMaps) {
return resolve(args, null, dependencyMaps);
}
@Override
public URI[] resolve(Map args, List depsInfo, Map... dependencyMaps) {
List<Exclusion> exclusions = createExclusions(args);
List<Dependency> dependencies = createDependencies(dependencyMaps, exclusions);
try {
List<File> files = resolve(dependencies);
List<URI> uris = new ArrayList<>(files.size());
for (File file : files) {
uris.add(file.toURI());
}
return uris.toArray(new URI[0]);
}
catch (Exception ex) {
throw new DependencyResolutionFailedException(ex);
}
}
private List<File> resolve(List<Dependency> dependencies) {
try {
CollectRequest collectRequest = getCollectRequest(dependencies);
DependencyRequest dependencyRequest = getDependencyRequest(collectRequest);
DependencyResult result = this.repositorySystem.resolveDependencies(this.session, dependencyRequest);
addManagedDependencies(result);
return getFiles(result);
}
catch (Exception ex) {
throw new DependencyResolutionFailedException(ex);
}
finally {
this.progressReporter.finished();
}
}
private CollectRequest getCollectRequest(List<Dependency> dependencies) {
CollectRequest collectRequest = new CollectRequest((Dependency) null, dependencies,
new ArrayList<>(this.repositories));
collectRequest.setManagedDependencies(this.resolutionContext.getManagedDependencies());
return collectRequest;
}
private DependencyRequest getDependencyRequest(CollectRequest collectRequest) {
return new DependencyRequest(collectRequest,
DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE, JavaScopes.RUNTIME));
}
private void addManagedDependencies(DependencyResult result) {
this.resolutionContext.addManagedDependencies(getDependencies(result));
}
@Override
public Map[] listDependencies(ClassLoader classLoader) {
throw new UnsupportedOperationException("Listing dependencies is not supported");
}
@Override
public Object grab(String endorsedModule) {
throw new UnsupportedOperationException("Grabbing an endorsed module is not supported");
}
}

View File

@@ -1,84 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler.grape;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import groovy.lang.GroovyClassLoader;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.spi.locator.ServiceLocator;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
/**
* Utility class to create a pre-configured {@link MavenResolverGrapeEngine}.
*
* @author Andy Wilkinson
* @since 2.5.9
*/
public abstract class MavenResolverGrapeEngineFactory {
public static MavenResolverGrapeEngine create(GroovyClassLoader classLoader,
List<RepositoryConfiguration> repositoryConfigurations,
DependencyResolutionContext dependencyResolutionContext, boolean quiet) {
RepositorySystem repositorySystem = createServiceLocator().getService(RepositorySystem.class);
DefaultRepositorySystemSession repositorySystemSession = MavenRepositorySystemUtils.newSession();
ServiceLoader<RepositorySystemSessionAutoConfiguration> autoConfigurations = ServiceLoader
.load(RepositorySystemSessionAutoConfiguration.class);
for (RepositorySystemSessionAutoConfiguration autoConfiguration : autoConfigurations) {
autoConfiguration.apply(repositorySystemSession, repositorySystem);
}
new DefaultRepositorySystemSessionAutoConfiguration().apply(repositorySystemSession, repositorySystem);
return new MavenResolverGrapeEngine(classLoader, repositorySystem, repositorySystemSession,
createRepositories(repositoryConfigurations), dependencyResolutionContext, quiet);
}
private static ServiceLocator createServiceLocator() {
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
locator.addService(RepositorySystem.class, DefaultRepositorySystem.class);
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
return locator;
}
private static List<RemoteRepository> createRepositories(List<RepositoryConfiguration> repositoryConfigurations) {
List<RemoteRepository> repositories = new ArrayList<>(repositoryConfigurations.size());
for (RepositoryConfiguration repositoryConfiguration : repositoryConfigurations) {
RemoteRepository.Builder builder = new RemoteRepository.Builder(repositoryConfiguration.getName(),
"default", repositoryConfiguration.getUri().toASCIIString());
if (!repositoryConfiguration.getSnapshotsEnabled()) {
builder.setSnapshotPolicy(new RepositoryPolicy(false, RepositoryPolicy.UPDATE_POLICY_NEVER,
RepositoryPolicy.CHECKSUM_POLICY_IGNORE));
}
repositories.add(builder.build());
}
return repositories;
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.grape;
/**
* Reports progress on a dependency resolution operation.
*
* @author Andy Wilkinson
*/
@FunctionalInterface
interface ProgressReporter {
/**
* Notification that the operation has completed.
*/
void finished();
}

View File

@@ -1,100 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.grape;
import java.net.URI;
import org.springframework.util.ObjectUtils;
/**
* The configuration of a repository.
*
* @author Andy Wilkinson
* @since 1.0.0
*/
public final class RepositoryConfiguration {
private final String name;
private final URI uri;
private final boolean snapshotsEnabled;
/**
* Creates a new {@code RepositoryConfiguration} instance.
* @param name the name of the repository
* @param uri the uri of the repository
* @param snapshotsEnabled {@code true} if the repository should enable access to
* snapshots, {@code false} otherwise
*/
public RepositoryConfiguration(String name, URI uri, boolean snapshotsEnabled) {
this.name = name;
this.uri = uri;
this.snapshotsEnabled = snapshotsEnabled;
}
/**
* Return the name of the repository.
* @return the repository name
*/
public String getName() {
return this.name;
}
/**
* Return the URI of the repository.
* @return the repository URI
*/
public URI getUri() {
return this.uri;
}
/**
* Return if the repository should enable access to snapshots.
* @return {@code true} if snapshot access is enabled
*/
public boolean getSnapshotsEnabled() {
return this.snapshotsEnabled;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
RepositoryConfiguration other = (RepositoryConfiguration) obj;
return ObjectUtils.nullSafeEquals(this.name, other.name);
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.name);
}
@Override
public String toString() {
return "RepositoryConfiguration [name=" + this.name + ", uri=" + this.uri + ", snapshotsEnabled="
+ this.snapshotsEnabled + "]";
}
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler.grape;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
/**
* Strategy that can be used to apply some auto-configuration during the installation of a
* {@link MavenResolverGrapeEngine}.
*
* @author Andy Wilkinson
* @since 1.0.0
*/
@FunctionalInterface
public interface RepositorySystemSessionAutoConfiguration {
/**
* Apply the configuration.
* @param session the repository system session
* @param repositorySystem the repository system
*/
void apply(DefaultRepositorySystemSession session, RepositorySystem repositorySystem);
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.grape;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.repository.LocalRepository;
import org.springframework.boot.cli.compiler.maven.MavenSettings;
import org.springframework.boot.cli.compiler.maven.MavenSettingsReader;
/**
* Auto-configuration for a RepositorySystemSession that uses Maven's settings.xml to
* determine the configuration settings.
*
* @author Andy Wilkinson
* @since 1.0.0
*/
public class SettingsXmlRepositorySystemSessionAutoConfiguration implements RepositorySystemSessionAutoConfiguration {
@Override
public void apply(DefaultRepositorySystemSession session, RepositorySystem repositorySystem) {
MavenSettings settings = getSettings(session);
String localRepository = settings.getLocalRepository();
if (localRepository != null) {
session.setLocalRepositoryManager(
repositorySystem.newLocalRepositoryManager(session, new LocalRepository(localRepository)));
}
}
private MavenSettings getSettings(DefaultRepositorySystemSession session) {
MavenSettings settings = new MavenSettingsReader().readSettings();
session.setOffline(settings.getOffline());
session.setMirrorSelector(settings.getMirrorSelector());
session.setAuthenticationSelector(settings.getAuthenticationSelector());
session.setProxySelector(settings.getProxySelector());
return settings;
}
}

View File

@@ -1,97 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.cli.compiler.grape;
import java.io.PrintStream;
import java.util.concurrent.TimeUnit;
import org.eclipse.aether.AbstractRepositoryListener;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositoryEvent;
import org.eclipse.aether.transfer.AbstractTransferListener;
import org.eclipse.aether.transfer.TransferEvent;
/**
* Provide high-level progress feedback for long running resolves.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
final class SummaryProgressReporter implements ProgressReporter {
private static final long INITIAL_DELAY = TimeUnit.SECONDS.toMillis(3);
private static final long PROGRESS_DELAY = TimeUnit.SECONDS.toMillis(1);
private final long startTime = System.currentTimeMillis();
private final PrintStream out;
private long lastProgressTime = System.currentTimeMillis();
private boolean started;
private boolean finished;
SummaryProgressReporter(DefaultRepositorySystemSession session, PrintStream out) {
this.out = out;
session.setTransferListener(new AbstractTransferListener() {
@Override
public void transferStarted(TransferEvent event) {
reportProgress();
}
@Override
public void transferProgressed(TransferEvent event) {
reportProgress();
}
});
session.setRepositoryListener(new AbstractRepositoryListener() {
@Override
public void artifactResolved(RepositoryEvent event) {
reportProgress();
}
});
}
private void reportProgress() {
if (!this.finished && System.currentTimeMillis() - this.startTime > INITIAL_DELAY) {
if (!this.started) {
this.started = true;
this.out.print("Resolving dependencies..");
this.lastProgressTime = System.currentTimeMillis();
}
else if (System.currentTimeMillis() - this.lastProgressTime > PROGRESS_DELAY) {
this.out.print(".");
this.lastProgressTime = System.currentTimeMillis();
}
}
}
@Override
public void finished() {
if (this.started && !this.finished) {
this.finished = true;
this.out.println();
}
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* CLI Groovy Grape integration.
*/
package org.springframework.boot.cli.compiler.grape;

View File

@@ -1,303 +0,0 @@
/*
* Copyright 2012-2022 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
*
* https://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.cli.compiler.maven;
import java.io.BufferedReader;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.maven.model.ActivationFile;
import org.apache.maven.model.ActivationOS;
import org.apache.maven.model.ActivationProperty;
import org.apache.maven.model.building.ModelProblemCollector;
import org.apache.maven.model.building.ModelProblemCollectorRequest;
import org.apache.maven.model.path.DefaultPathTranslator;
import org.apache.maven.model.profile.DefaultProfileSelector;
import org.apache.maven.model.profile.ProfileActivationContext;
import org.apache.maven.model.profile.activation.FileProfileActivator;
import org.apache.maven.model.profile.activation.JdkVersionProfileActivator;
import org.apache.maven.model.profile.activation.OperatingSystemProfileActivator;
import org.apache.maven.model.profile.activation.PropertyProfileActivator;
import org.apache.maven.settings.Activation;
import org.apache.maven.settings.Mirror;
import org.apache.maven.settings.Profile;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Server;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.repository.AuthenticationSelector;
import org.eclipse.aether.repository.MirrorSelector;
import org.eclipse.aether.repository.ProxySelector;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.util.repository.ConservativeAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultMirrorSelector;
import org.eclipse.aether.util.repository.DefaultProxySelector;
/**
* An encapsulation of settings read from a user's Maven settings.xml.
*
* @author Andy Wilkinson
* @since 1.3.0
* @see MavenSettingsReader
*/
public class MavenSettings {
private final boolean offline;
private final MirrorSelector mirrorSelector;
private final AuthenticationSelector authenticationSelector;
private final ProxySelector proxySelector;
private final String localRepository;
private final List<Profile> activeProfiles;
/**
* Create a new {@link MavenSettings} instance.
* @param settings the source settings
* @param decryptedSettings the decrypted settings
*/
public MavenSettings(Settings settings, SettingsDecryptionResult decryptedSettings) {
this.offline = settings.isOffline();
this.mirrorSelector = createMirrorSelector(settings);
this.authenticationSelector = createAuthenticationSelector(decryptedSettings);
this.proxySelector = createProxySelector(decryptedSettings);
this.localRepository = settings.getLocalRepository();
this.activeProfiles = determineActiveProfiles(settings);
}
private MirrorSelector createMirrorSelector(Settings settings) {
DefaultMirrorSelector selector = new DefaultMirrorSelector();
for (Mirror mirror : settings.getMirrors()) {
selector.add(mirror.getId(), mirror.getUrl(), mirror.getLayout(), false, false, mirror.getMirrorOf(),
mirror.getMirrorOfLayouts());
}
return selector;
}
private AuthenticationSelector createAuthenticationSelector(SettingsDecryptionResult decryptedSettings) {
DefaultAuthenticationSelector selector = new DefaultAuthenticationSelector();
for (Server server : decryptedSettings.getServers()) {
AuthenticationBuilder auth = new AuthenticationBuilder();
auth.addUsername(server.getUsername()).addPassword(server.getPassword());
auth.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
selector.add(server.getId(), auth.build());
}
return new ConservativeAuthenticationSelector(selector);
}
private ProxySelector createProxySelector(SettingsDecryptionResult decryptedSettings) {
DefaultProxySelector selector = new DefaultProxySelector();
for (Proxy proxy : decryptedSettings.getProxies()) {
Authentication authentication = new AuthenticationBuilder().addUsername(proxy.getUsername())
.addPassword(proxy.getPassword()).build();
selector.add(new org.eclipse.aether.repository.Proxy(proxy.getProtocol(), proxy.getHost(), proxy.getPort(),
authentication), proxy.getNonProxyHosts());
}
return selector;
}
private List<Profile> determineActiveProfiles(Settings settings) {
SpringBootCliModelProblemCollector problemCollector = new SpringBootCliModelProblemCollector();
List<org.apache.maven.model.Profile> activeModelProfiles = createProfileSelector().getActiveProfiles(
createModelProfiles(settings.getProfiles()),
new SpringBootCliProfileActivationContext(settings.getActiveProfiles()), problemCollector);
if (!problemCollector.getProblems().isEmpty()) {
throw new IllegalStateException(createFailureMessage(problemCollector));
}
List<Profile> activeProfiles = new ArrayList<>();
Map<String, Profile> profiles = settings.getProfilesAsMap();
for (org.apache.maven.model.Profile modelProfile : activeModelProfiles) {
activeProfiles.add(profiles.get(modelProfile.getId()));
}
return activeProfiles;
}
private String createFailureMessage(SpringBootCliModelProblemCollector problemCollector) {
StringWriter message = new StringWriter();
PrintWriter printer = new PrintWriter(message);
printer.println("Failed to determine active profiles:");
for (ModelProblemCollectorRequest problem : problemCollector.getProblems()) {
String location = (problem.getLocation() != null) ? " at " + problem.getLocation() : "";
printer.println(" " + problem.getMessage() + location);
if (problem.getException() != null) {
printer.println(indentStackTrace(problem.getException(), " "));
}
}
return message.toString();
}
private String indentStackTrace(Exception ex, String indent) {
return indentLines(printStackTrace(ex), indent);
}
private String printStackTrace(Exception ex) {
StringWriter stackTrace = new StringWriter();
PrintWriter printer = new PrintWriter(stackTrace);
ex.printStackTrace(printer);
return stackTrace.toString();
}
private String indentLines(String input, String indent) {
StringWriter indented = new StringWriter();
PrintWriter writer = new PrintWriter(indented);
BufferedReader reader = new BufferedReader(new StringReader(input));
reader.lines().forEach((line) -> writer.println(indent + line));
return indented.toString();
}
private DefaultProfileSelector createProfileSelector() {
DefaultProfileSelector selector = new DefaultProfileSelector();
selector.addProfileActivator(new FileProfileActivator().setPathTranslator(new DefaultPathTranslator()));
selector.addProfileActivator(new JdkVersionProfileActivator());
selector.addProfileActivator(new PropertyProfileActivator());
selector.addProfileActivator(new OperatingSystemProfileActivator());
return selector;
}
private List<org.apache.maven.model.Profile> createModelProfiles(List<Profile> profiles) {
List<org.apache.maven.model.Profile> modelProfiles = new ArrayList<>();
for (Profile profile : profiles) {
org.apache.maven.model.Profile modelProfile = new org.apache.maven.model.Profile();
modelProfile.setId(profile.getId());
if (profile.getActivation() != null) {
modelProfile.setActivation(createModelActivation(profile.getActivation()));
}
modelProfiles.add(modelProfile);
}
return modelProfiles;
}
private org.apache.maven.model.Activation createModelActivation(Activation activation) {
org.apache.maven.model.Activation modelActivation = new org.apache.maven.model.Activation();
modelActivation.setActiveByDefault(activation.isActiveByDefault());
if (activation.getFile() != null) {
ActivationFile activationFile = new ActivationFile();
activationFile.setExists(activation.getFile().getExists());
activationFile.setMissing(activation.getFile().getMissing());
modelActivation.setFile(activationFile);
}
modelActivation.setJdk(activation.getJdk());
if (activation.getOs() != null) {
ActivationOS os = new ActivationOS();
os.setArch(activation.getOs().getArch());
os.setFamily(activation.getOs().getFamily());
os.setName(activation.getOs().getName());
os.setVersion(activation.getOs().getVersion());
modelActivation.setOs(os);
}
if (activation.getProperty() != null) {
ActivationProperty property = new ActivationProperty();
property.setName(activation.getProperty().getName());
property.setValue(activation.getProperty().getValue());
modelActivation.setProperty(property);
}
return modelActivation;
}
public boolean getOffline() {
return this.offline;
}
public MirrorSelector getMirrorSelector() {
return this.mirrorSelector;
}
public AuthenticationSelector getAuthenticationSelector() {
return this.authenticationSelector;
}
public ProxySelector getProxySelector() {
return this.proxySelector;
}
public String getLocalRepository() {
return this.localRepository;
}
public List<Profile> getActiveProfiles() {
return this.activeProfiles;
}
private static final class SpringBootCliProfileActivationContext implements ProfileActivationContext {
private final List<String> activeProfiles;
SpringBootCliProfileActivationContext(List<String> activeProfiles) {
this.activeProfiles = activeProfiles;
}
@Override
public List<String> getActiveProfileIds() {
return this.activeProfiles;
}
@Override
public List<String> getInactiveProfileIds() {
return Collections.emptyList();
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Map<String, String> getSystemProperties() {
return (Map) System.getProperties();
}
@Override
public Map<String, String> getUserProperties() {
return Collections.emptyMap();
}
@Override
public File getProjectDirectory() {
return new File(".");
}
@Override
public Map<String, String> getProjectProperties() {
return Collections.emptyMap();
}
}
private static final class SpringBootCliModelProblemCollector implements ModelProblemCollector {
private final List<ModelProblemCollectorRequest> problems = new ArrayList<>();
@Override
public void add(ModelProblemCollectorRequest req) {
this.problems.add(req);
}
List<ModelProblemCollectorRequest> getProblems() {
return this.problems;
}
}
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright 2012-2020 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
*
* https://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.cli.compiler.maven;
import java.io.File;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
import org.apache.maven.settings.building.SettingsBuildingException;
import org.apache.maven.settings.building.SettingsBuildingRequest;
import org.apache.maven.settings.crypto.DefaultSettingsDecrypter;
import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
import org.apache.maven.settings.crypto.SettingsDecrypter;
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
import org.sonatype.plexus.components.cipher.PlexusCipherException;
import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
import org.springframework.boot.cli.util.Log;
/**
* {@code MavenSettingsReader} reads settings from a user's Maven settings.xml file,
* decrypting them if necessary using settings-security.xml.
*
* @author Andy Wilkinson
* @since 1.3.0
*/
public class MavenSettingsReader {
private final String homeDir;
public MavenSettingsReader() {
this(System.getProperty("user.home"));
}
public MavenSettingsReader(String homeDir) {
this.homeDir = homeDir;
}
public MavenSettings readSettings() {
Settings settings = loadSettings();
SettingsDecryptionResult decrypted = decryptSettings(settings);
if (!decrypted.getProblems().isEmpty()) {
Log.error("Maven settings decryption failed. Some Maven repositories may be inaccessible");
// Continue - the encrypted credentials may not be used
}
return new MavenSettings(settings, decrypted);
}
private Settings loadSettings() {
File settingsFile = new File(this.homeDir, ".m2/settings.xml");
SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
request.setUserSettingsFile(settingsFile);
request.setSystemProperties(System.getProperties());
try {
return new DefaultSettingsBuilderFactory().newInstance().build(request).getEffectiveSettings();
}
catch (SettingsBuildingException ex) {
throw new IllegalStateException("Failed to build settings from " + settingsFile, ex);
}
}
private SettingsDecryptionResult decryptSettings(Settings settings) {
DefaultSettingsDecryptionRequest request = new DefaultSettingsDecryptionRequest(settings);
return createSettingsDecrypter().decrypt(request);
}
private SettingsDecrypter createSettingsDecrypter() {
return new DefaultSettingsDecrypter(new SpringBootSecDispatcher());
}
private class SpringBootSecDispatcher extends DefaultSecDispatcher {
private static final String SECURITY_XML = ".m2/settings-security.xml";
SpringBootSecDispatcher() {
File file = new File(MavenSettingsReader.this.homeDir, SECURITY_XML);
this._configurationFile = file.getAbsolutePath();
try {
this._cipher = new DefaultPlexusCipher();
}
catch (PlexusCipherException ex) {
throw new IllegalStateException(ex);
}
}
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* CLI Maven integration.
*/
package org.springframework.boot.cli.compiler.maven;

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.
*/
/**
* CLI Groovy compiler integration.
*/
package org.springframework.boot.cli.compiler;

View File

@@ -1,171 +0,0 @@
/*
* Copyright 2012-2020 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
*
* https://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.cli.util;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Utilities for manipulating resource paths and URLs.
*
* @author Dave Syer
* @author Phillip Webb
* @since 1.0.0
*/
public abstract class ResourceUtils {
/**
* Pseudo URL prefix for loading from the class path: "classpath:".
*/
public static final String CLASSPATH_URL_PREFIX = "classpath:";
/**
* Pseudo URL prefix for loading all resources from the class path: "classpath*:".
*/
public static final String ALL_CLASSPATH_URL_PREFIX = "classpath*:";
/**
* URL prefix for loading from the file system: "file:".
*/
public static final String FILE_URL_PREFIX = "file:";
/**
* Return URLs from a given source path. Source paths can be simple file locations
* (/some/file.java) or wildcard patterns (/some/**). Additionally the prefixes
* "file:", "classpath:" and "classpath*:" can be used for specific path types.
* @param path the source path
* @param classLoader the class loader or {@code null} to use the default
* @return a list of URLs
*/
public static List<String> getUrls(String path, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
path = StringUtils.cleanPath(path);
try {
return getUrlsFromWildcardPath(path, classLoader);
}
catch (Exception ex) {
throw new IllegalArgumentException("Cannot create URL from path [" + path + "]", ex);
}
}
private static List<String> getUrlsFromWildcardPath(String path, ClassLoader classLoader) throws IOException {
if (path.contains(":")) {
return getUrlsFromPrefixedWildcardPath(path, classLoader);
}
Set<String> result = new LinkedHashSet<>();
try {
result.addAll(getUrls(FILE_URL_PREFIX + path, classLoader));
}
catch (IllegalArgumentException ex) {
// ignore
}
path = stripLeadingSlashes(path);
result.addAll(getUrls(ALL_CLASSPATH_URL_PREFIX + path, classLoader));
return new ArrayList<>(result);
}
private static List<String> getUrlsFromPrefixedWildcardPath(String path, ClassLoader classLoader)
throws IOException {
Resource[] resources = new PathMatchingResourcePatternResolver(new FileSearchResourceLoader(classLoader))
.getResources(path);
List<String> result = new ArrayList<>();
for (Resource resource : resources) {
if (resource.exists()) {
if ("file".equals(resource.getURI().getScheme()) && resource.getFile().isDirectory()) {
result.addAll(getChildFiles(resource));
continue;
}
result.add(absolutePath(resource));
}
}
return result;
}
private static List<String> getChildFiles(Resource resource) throws IOException {
Resource[] children = new PathMatchingResourcePatternResolver().getResources(resource.getURL() + "/**");
List<String> childFiles = new ArrayList<>();
for (Resource child : children) {
if (!child.getFile().isDirectory()) {
childFiles.add(absolutePath(child));
}
}
return childFiles;
}
private static String absolutePath(Resource resource) throws IOException {
if (!"file".equals(resource.getURI().getScheme())) {
return resource.getURL().toExternalForm();
}
return resource.getFile().getAbsoluteFile().toURI().toString();
}
private static String stripLeadingSlashes(String path) {
while (path.startsWith("/")) {
path = path.substring(1);
}
return path;
}
private static class FileSearchResourceLoader extends DefaultResourceLoader {
private final FileSystemResourceLoader files;
FileSearchResourceLoader(ClassLoader classLoader) {
super(classLoader);
this.files = new FileSystemResourceLoader();
}
@Override
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
if (location.startsWith(FILE_URL_PREFIX)) {
return this.files.getResource(location);
}
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.groovy;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Provides one or more additional sources of dependency management that is used when
* resolving {@code @Grab} dependencies.
*
* @author Andy Wilkinson
* @since 1.3.0
*/
@Target({ ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD,
ElementType.PARAMETER, ElementType.TYPE })
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface DependencyManagementBom {
/**
* One or more sets of colon-separated coordinates ({@code group:module:version}) of a
* Maven bom that contains dependency management that will add to and override the
* default dependency management.
* @return the BOM coordinates
*/
String[] value();
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2012-2019 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
*
* https://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.groovy;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.cli.compiler.autoconfigure.GroovyTemplatesCompilerAutoConfiguration;
/**
* Pseudo annotation used to trigger {@link GroovyTemplatesCompilerAutoConfiguration}.
*
* @author Dave Syer
* @since 1.1.0
*/
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableGroovyTemplates {
}

Some files were not shown because too many files have changed in this diff Show More