Allow custom dependency metadata to be used with the CLI

Add support for a new annotation, @GrabMetadata, that can be used
to provide the coordinates of one or more properties files, such as
the one published by Spring IO Platform, as a source of dependency
metadata. For example:

@GrabMetadata("com.example:metadata:1.0.0")

The referenced properties files must be in the format
group:module=version.

Limitations:

 - Only a single @GrabMetadata annotation is supported
 - The referenced properties file must be accessible in one of the
   default repositories, i.e. it cannot be accessed in a repository
   that's added using @GrabResolver

Closes #814
This commit is contained in:
Andy Wilkinson
2014-05-19 15:58:42 +01:00
parent 3f498a4803
commit d2fc80b818
19 changed files with 589 additions and 143 deletions

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.cli.compiler;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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 List<AnnotationNode> annotationNodes = new ArrayList<AnnotationNode>();
private SourceUnit sourceUnit;
protected AnnotatedNodeASTTransformation(Set<String> interestingAnnotationNames) {
this.interestingAnnotationNames = interestingAnnotationNames;
}
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {
this.sourceUnit = source;
ClassVisitor classVisitor = new ClassVisitor(source);
for (ASTNode node : nodes) {
if (node instanceof ModuleNode) {
ModuleNode module = (ModuleNode) node;
visitAnnotatedNode(module.getPackage());
for (ImportNode importNode : module.getImports()) {
visitAnnotatedNode(importNode);
}
for (ImportNode importNode : module.getStarImports()) {
visitAnnotatedNode(importNode);
}
for (Map.Entry<String, ImportNode> entry : module.getStaticImports()
.entrySet()) {
visitAnnotatedNode(entry.getValue());
}
for (Map.Entry<String, ImportNode> entry : module.getStaticStarImports()
.entrySet()) {
visitAnnotatedNode(entry.getValue());
}
for (ClassNode classNode : module.getClasses()) {
visitAnnotatedNode(classNode);
classNode.visitContents(classVisitor);
}
}
}
processAnnotationNodes(this.annotationNodes);
}
protected SourceUnit getSourceUnit() {
return this.sourceUnit;
}
protected abstract void processAnnotationNodes(List<AnnotationNode> annotationNodes);
private void visitAnnotatedNode(AnnotatedNode annotatedNode) {
if (annotatedNode != null) {
for (AnnotationNode annotationNode : annotatedNode.getAnnotations()) {
if (this.interestingAnnotationNames.contains(annotationNode
.getClassNode().getName())) {
this.annotationNodes.add(annotationNode);
}
}
}
}
private class ClassVisitor extends ClassCodeVisitorSupport {
private final SourceUnit source;
public ClassVisitor(SourceUnit source) {
this.source = source;
}
@Override
protected SourceUnit getSourceUnit() {
return this.source;
}
@Override
public void visitAnnotations(AnnotatedNode node) {
visitAnnotatedNode(node);
}
}
}

View File

@@ -23,13 +23,12 @@ 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.dependencies.ArtifactCoordinatesResolver;
/**
* {@link ASTTransformation} to apply
* {@link CompilerAutoConfiguration#applyDependencies(DependencyCustomizer) dependency
* auto-configuration}.
*
*
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
@@ -38,15 +37,15 @@ public class DependencyAutoConfigurationTransformation implements ASTTransformat
private final GroovyClassLoader loader;
private final ArtifactCoordinatesResolver coordinatesResolver;
private final DependencyResolutionContext dependencyResolutionContext;
private final Iterable<CompilerAutoConfiguration> compilerAutoConfigurations;
public DependencyAutoConfigurationTransformation(GroovyClassLoader loader,
ArtifactCoordinatesResolver coordinatesResolver,
DependencyResolutionContext dependencyResolutionContext,
Iterable<CompilerAutoConfiguration> compilerAutoConfigurations) {
this.loader = loader;
this.coordinatesResolver = coordinatesResolver;
this.dependencyResolutionContext = dependencyResolutionContext;
this.compilerAutoConfigurations = compilerAutoConfigurations;
}
@@ -62,7 +61,7 @@ public class DependencyAutoConfigurationTransformation implements ASTTransformat
private void visitModule(ModuleNode module) {
DependencyCustomizer dependencies = new DependencyCustomizer(this.loader, module,
this.coordinatesResolver);
this.dependencyResolutionContext);
for (ClassNode classNode : module.getClasses()) {
for (CompilerAutoConfiguration autoConfiguration : this.compilerAutoConfigurations) {
if (autoConfiguration.matches(classNode)) {

View File

@@ -32,7 +32,7 @@ import org.springframework.boot.cli.compiler.dependencies.ArtifactCoordinatesRes
* <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
*/
@@ -42,17 +42,17 @@ public class DependencyCustomizer {
private final ClassNode classNode;
private final ArtifactCoordinatesResolver coordinatesResolver;
private final DependencyResolutionContext dependencyResolutionContext;
/**
* Create a new {@link DependencyCustomizer} instance.
* @param loader
*/
public DependencyCustomizer(GroovyClassLoader loader, ModuleNode moduleNode,
ArtifactCoordinatesResolver coordinatesResolver) {
DependencyResolutionContext dependencyResolutionContext) {
this.loader = loader;
this.classNode = moduleNode.getClasses().get(0);
this.coordinatesResolver = coordinatesResolver;
this.dependencyResolutionContext = dependencyResolutionContext;
}
/**
@@ -62,7 +62,7 @@ public class DependencyCustomizer {
protected DependencyCustomizer(DependencyCustomizer parent) {
this.loader = parent.loader;
this.classNode = parent.classNode;
this.coordinatesResolver = parent.coordinatesResolver;
this.dependencyResolutionContext = parent.dependencyResolutionContext;
}
public String getVersion(String artifactId) {
@@ -71,7 +71,8 @@ public class DependencyCustomizer {
}
public String getVersion(String artifactId, String defaultVersion) {
String version = this.coordinatesResolver.getVersion(artifactId);
String version = this.dependencyResolutionContext
.getArtifactCoordinatesResolver().getVersion(artifactId);
if (version == null) {
version = defaultVersion;
}
@@ -201,9 +202,11 @@ public class DependencyCustomizer {
*/
public DependencyCustomizer add(String module, boolean transitive) {
if (canAdd()) {
ArtifactCoordinatesResolver artifactCoordinatesResolver = this.dependencyResolutionContext
.getArtifactCoordinatesResolver();
this.classNode.addAnnotation(createGrabAnnotation(
this.coordinatesResolver.getGroupId(module), module,
this.coordinatesResolver.getVersion(module), transitive));
artifactCoordinatesResolver.getGroupId(module), module,
artifactCoordinatesResolver.getVersion(module), transitive));
}
return this;
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.cli.compiler;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.aether.graph.Dependency;
import org.springframework.boot.cli.compiler.dependencies.ArtifactCoordinatesResolver;
import org.springframework.boot.cli.compiler.dependencies.ManagedDependenciesArtifactCoordinatesResolver;
import org.springframework.boot.cli.compiler.grape.ManagedDependenciesFactory;
import org.springframework.boot.dependency.tools.ManagedDependencies;
/**
* @author Andy Wilkinson
* @since 1.1.0
*/
public class DependencyResolutionContext {
private ArtifactCoordinatesResolver artifactCoordinatesResolver;
private List<Dependency> managedDependencies = new ArrayList<Dependency>();
public DependencyResolutionContext() {
this(new ManagedDependenciesArtifactCoordinatesResolver());
}
DependencyResolutionContext(ArtifactCoordinatesResolver artifactCoordinatesResolver) {
this.artifactCoordinatesResolver = artifactCoordinatesResolver;
}
void setManagedDependencies(ManagedDependencies managedDependencies) {
this.artifactCoordinatesResolver = new ManagedDependenciesArtifactCoordinatesResolver(
managedDependencies);
this.managedDependencies = new ArrayList<Dependency>(
new ManagedDependenciesFactory(managedDependencies)
.getManagedDependencies());
}
ArtifactCoordinatesResolver getArtifactCoordinatesResolver() {
return this.artifactCoordinatesResolver;
}
public List<Dependency> getManagedDependencies() {
return this.managedDependencies;
}
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.cli.compiler;
import groovy.grape.Grape;
import java.io.IOException;
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 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.dependency.tools.ManagedDependencies;
import org.springframework.boot.dependency.tools.PropertiesFileManagedDependencies;
import org.springframework.boot.dependency.tools.VersionManagedDependencies;
import org.springframework.boot.groovy.GrabMetadata;
/**
* {@link ASTTransformation} for processing {@link GrabMetadata @GrabMetadata}
*
* @author Andy Wilkinson
* @since 1.1.0
*/
public class GrabMetadataTransformation extends AnnotatedNodeASTTransformation {
private static final Set<String> GRAB_METADATA_ANNOTATION_NAMES = Collections
.unmodifiableSet(new HashSet<String>(Arrays.asList(
GrabMetadata.class.getName(), GrabMetadata.class.getSimpleName())));
private final DependencyResolutionContext resolutionContext;
public GrabMetadataTransformation(DependencyResolutionContext resolutionContext) {
super(GRAB_METADATA_ANNOTATION_NAMES);
this.resolutionContext = resolutionContext;
}
@Override
protected void processAnnotationNodes(List<AnnotationNode> annotationNodes) {
if (!annotationNodes.isEmpty()) {
if (annotationNodes.size() > 1) {
for (AnnotationNode annotationNode : annotationNodes) {
handleDuplicateGrabMetadataAnnotation(annotationNode);
}
}
else {
processGrabMetadataAnnotation(annotationNodes.get(0));
}
}
}
private void processGrabMetadataAnnotation(AnnotationNode annotationNode) {
Expression valueExpression = annotationNode.getMember("value");
List<Map<String, String>> metadataDependencies = createDependencyMaps(valueExpression);
updateArtifactCoordinatesResolver(metadataDependencies);
}
private List<Map<String, String>> createDependencyMaps(Expression valueExpression) {
Map<String, String> dependency = null;
List<ConstantExpression> constantExpressions = new ArrayList<ConstantExpression>();
if (valueExpression instanceof ListExpression) {
ListExpression listExpression = (ListExpression) valueExpression;
for (Expression expression : listExpression.getExpressions()) {
if (expression instanceof ConstantExpression
&& ((ConstantExpression) expression).getValue() instanceof String) {
constantExpressions.add((ConstantExpression) expression);
}
else {
reportError(
"Each entry in the array must be an inline string constant",
expression);
}
}
}
else if (valueExpression instanceof ConstantExpression
&& ((ConstantExpression) valueExpression).getValue() instanceof String) {
constantExpressions = Arrays.asList((ConstantExpression) valueExpression);
}
else {
reportError(
"@GrabMetadata requires an inline constant that is a string or a string array",
valueExpression);
}
List<Map<String, String>> dependencies = new ArrayList<Map<String, String>>(
constantExpressions.size());
for (ConstantExpression expression : constantExpressions) {
Object value = expression.getValue();
if (value instanceof String) {
String[] components = ((String) expression.getValue()).split(":");
if (components.length == 3) {
dependency = new HashMap<String, String>();
dependency.put("group", components[0]);
dependency.put("module", components[1]);
dependency.put("version", components[2]);
dependency.put("type", "properties");
dependencies.add(dependency);
}
else {
handleMalformedDependency(expression);
}
}
}
return dependencies;
}
private void handleMalformedDependency(Expression expression) {
Message message = createSyntaxErrorMessage(
"The string must be of the form \"group:module:version\"\n", expression);
getSourceUnit().getErrorCollector().addErrorAndContinue(message);
}
private void updateArtifactCoordinatesResolver(
List<Map<String, String>> metadataDependencies) {
URI[] uris = Grape.getInstance().resolve(null,
metadataDependencies.toArray(new Map[metadataDependencies.size()]));
List<ManagedDependencies> managedDependencies = new ArrayList<ManagedDependencies>(
uris.length);
for (URI uri : uris) {
try {
managedDependencies.add(new PropertiesFileManagedDependencies(uri.toURL()
.openStream()));
}
catch (IOException e) {
throw new IllegalStateException("Failed to parse '" + uris[0]
+ "'. Is it a valid properties file?");
}
}
this.resolutionContext.setManagedDependencies(new VersionManagedDependencies(
managedDependencies));
}
private void handleDuplicateGrabMetadataAnnotation(AnnotationNode annotationNode) {
Message message = createSyntaxErrorMessage(
"Duplicate @GrabMetadata 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());
}
}

View File

@@ -43,8 +43,6 @@ 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.ArtifactCoordinatesResolver;
import org.springframework.boot.cli.compiler.dependencies.ManagedDependenciesArtifactCoordinatesResolver;
import org.springframework.boot.cli.compiler.grape.AetherGrapeEngine;
import org.springframework.boot.cli.compiler.grape.AetherGrapeEngineFactory;
import org.springframework.boot.cli.compiler.grape.GrapeEngineInstaller;
@@ -58,21 +56,19 @@ import org.springframework.boot.cli.util.ResourceUtils;
* <li>{@link CompilerAutoConfiguration} strategies will be read from
* <code>META-INF/services/org.springframework.boot.cli.compiler.CompilerAutoConfiguration</code>
* (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
*/
public class GroovyCompiler {
private final ArtifactCoordinatesResolver coordinatesResolver;
private final GroovyCompilerConfiguration configuration;
private final ExtendedGroovyClassLoader loader;
@@ -90,10 +86,10 @@ public class GroovyCompiler {
this.configuration = configuration;
this.loader = createLoader(configuration);
this.coordinatesResolver = new ManagedDependenciesArtifactCoordinatesResolver();
DependencyResolutionContext resolutionContext = new DependencyResolutionContext();
AetherGrapeEngine grapeEngine = AetherGrapeEngineFactory.create(this.loader,
configuration.getRepositoryConfiguration());
configuration.getRepositoryConfiguration(), resolutionContext);
GrapeEngineInstaller.install(grapeEngine);
@@ -108,12 +104,13 @@ public class GroovyCompiler {
}
this.transformations = new ArrayList<ASTTransformation>();
this.transformations.add(new GrabMetadataTransformation(resolutionContext));
this.transformations.add(new DependencyAutoConfigurationTransformation(
this.loader, this.coordinatesResolver, this.compilerAutoConfigurations));
this.loader, resolutionContext, this.compilerAutoConfigurations));
this.transformations.add(new GroovyBeansTransformation());
if (this.configuration.isGuessDependencies()) {
this.transformations.add(new ResolveDependencyCoordinatesTransformation(
this.coordinatesResolver));
resolutionContext));
}
}
@@ -170,7 +167,7 @@ public class GroovyCompiler {
* @throws IOException
*/
public Class<?>[] compile(String... sources) throws CompilationFailedException,
IOException {
IOException {
this.loader.clearCache();
List<Class<?>> classes = new ArrayList<Class<?>>();
@@ -290,9 +287,9 @@ public class GroovyCompiler {
classNode);
}
autoConfiguration
.apply(GroovyCompiler.this.loader,
GroovyCompiler.this.configuration, context, source,
classNode);
.apply(GroovyCompiler.this.loader,
GroovyCompiler.this.configuration, context, source,
classNode);
}
}
importCustomizer.call(source, context, classNode);

View File

@@ -21,81 +21,39 @@ import groovy.lang.Grab;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
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.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.transform.ASTTransformation;
import org.springframework.boot.cli.compiler.dependencies.ArtifactCoordinatesResolver;
/**
* {@link ASTTransformation} to resolve {@link Grab} artifact coordinates.
*
*
* @author Andy Wilkinson
* @author Phillip Webb
*/
public class ResolveDependencyCoordinatesTransformation implements ASTTransformation {
public class ResolveDependencyCoordinatesTransformation extends
AnnotatedNodeASTTransformation {
private static final Set<String> GRAB_ANNOTATION_NAMES = Collections
.unmodifiableSet(new HashSet<String>(Arrays.asList(Grab.class.getName(),
Grab.class.getSimpleName())));
private final ArtifactCoordinatesResolver coordinatesResolver;
private final DependencyResolutionContext resolutionContext;
public ResolveDependencyCoordinatesTransformation(
ArtifactCoordinatesResolver coordinatesResolver) {
this.coordinatesResolver = coordinatesResolver;
DependencyResolutionContext resolutionContext) {
super(GRAB_ANNOTATION_NAMES);
this.resolutionContext = resolutionContext;
}
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {
ClassVisitor classVisitor = new ClassVisitor(source);
for (ASTNode node : nodes) {
if (node instanceof ModuleNode) {
ModuleNode module = (ModuleNode) node;
visitAnnotatedNode(module.getPackage());
for (ImportNode importNode : module.getImports()) {
visitAnnotatedNode(importNode);
}
for (ImportNode importNode : module.getStarImports()) {
visitAnnotatedNode(importNode);
}
for (Map.Entry<String, ImportNode> entry : module.getStaticImports()
.entrySet()) {
visitAnnotatedNode(entry.getValue());
}
for (Map.Entry<String, ImportNode> entry : module.getStaticStarImports()
.entrySet()) {
visitAnnotatedNode(entry.getValue());
}
for (ClassNode classNode : module.getClasses()) {
visitAnnotatedNode(classNode);
classNode.visitContents(classVisitor);
}
}
}
}
private void visitAnnotatedNode(AnnotatedNode annotatedNode) {
if (annotatedNode != null) {
for (AnnotationNode annotationNode : annotatedNode.getAnnotations()) {
if (GRAB_ANNOTATION_NAMES.contains(annotationNode.getClassNode()
.getName())) {
transformGrabAnnotation(annotationNode);
}
}
protected void processAnnotationNodes(List<AnnotationNode> annotationNodes) {
for (AnnotationNode annotationNode : annotationNodes) {
transformGrabAnnotation(annotationNode);
}
}
@@ -129,10 +87,12 @@ public class ResolveDependencyCoordinatesTransformation implements ASTTransforma
module = (String) ((ConstantExpression) expression).getValue();
}
if (annotation.getMember("group") == null) {
setMember(annotation, "group", this.coordinatesResolver.getGroupId(module));
setMember(annotation, "group", this.resolutionContext
.getArtifactCoordinatesResolver().getGroupId(module));
}
if (annotation.getMember("version") == null) {
setMember(annotation, "version", this.coordinatesResolver.getVersion(module));
setMember(annotation, "version", this.resolutionContext
.getArtifactCoordinatesResolver().getVersion(module));
}
}
@@ -140,24 +100,4 @@ public class ResolveDependencyCoordinatesTransformation implements ASTTransforma
ConstantExpression expression = new ConstantExpression(value);
annotation.setMember(name, expression);
}
private class ClassVisitor extends ClassCodeVisitorSupport {
private final SourceUnit source;
public ClassVisitor(SourceUnit source) {
this.source = source;
}
@Override
protected SourceUnit getSourceUnit() {
return this.source;
}
@Override
public void visitAnnotations(AnnotatedNode node) {
visitAnnotatedNode(node);
}
}
}

View File

@@ -30,7 +30,7 @@ import org.springframework.boot.cli.compiler.GroovyCompilerConfiguration;
/**
* {@link CompilerAutoConfiguration} for Spring.
*
*
* @author Dave Syer
* @author Phillip Webb
*/
@@ -39,7 +39,7 @@ public class SpringBootCompilerAutoConfiguration extends CompilerAutoConfigurati
@Override
public void applyDependencies(DependencyCustomizer dependencies) {
dependencies.ifAnyMissingClasses("org.springframework.boot.SpringApplication")
.add("spring-boot-starter");
.add("spring-boot-starter");
}
@Override
@@ -63,7 +63,8 @@ public class SpringBootCompilerAutoConfiguration extends CompilerAutoConfigurati
"org.springframework.core.annotation.Order",
"org.springframework.core.io.ResourceLoader",
"org.springframework.boot.CommandLineRunner",
"org.springframework.boot.autoconfigure.EnableAutoConfiguration");
"org.springframework.boot.autoconfigure.EnableAutoConfiguration",
"org.springframework.boot.groovy.GrabMetadata");
imports.addStarImports("org.springframework.stereotype",
"org.springframework.scheduling.annotation");
}

View File

@@ -22,7 +22,7 @@ import org.springframework.boot.dependency.tools.VersionManagedDependencies;
/**
* {@link ArtifactCoordinatesResolver} backed by {@link ManagedDependencies}.
*
*
* @author Phillip Webb
*/
public class ManagedDependenciesArtifactCoordinatesResolver implements
@@ -34,7 +34,7 @@ public class ManagedDependenciesArtifactCoordinatesResolver implements
this(new VersionManagedDependencies());
}
ManagedDependenciesArtifactCoordinatesResolver(ManagedDependencies dependencies) {
public ManagedDependenciesArtifactCoordinatesResolver(ManagedDependencies dependencies) {
this.dependencies = dependencies;
}

View File

@@ -43,12 +43,13 @@ 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;
import org.springframework.boot.cli.compiler.DependencyResolutionContext;
/**
* A {@link GrapeEngine} implementation that uses <a
* href="http://eclipse.org/aether">Aether</a>, the dependency resolution system used by
* Maven.
*
*
* @author Andy Wilkinson
* @author Phillip Webb
*/
@@ -58,7 +59,7 @@ public class AetherGrapeEngine implements GrapeEngine {
private static final Collection<Exclusion> WILDCARD_EXCLUSION = Arrays
.asList(new Exclusion("*", "*", "*", "*"));
private final List<Dependency> managedDependencies = new ArrayList<Dependency>();
private final DependencyResolutionContext resolutionContext;
private final ProgressReporter progressReporter;
@@ -74,11 +75,11 @@ public class AetherGrapeEngine implements GrapeEngine {
RepositorySystem repositorySystem,
DefaultRepositorySystemSession repositorySystemSession,
List<RemoteRepository> remoteRepositories,
List<Dependency> managedDependencies) {
DependencyResolutionContext resolutionContext) {
this.classLoader = classLoader;
this.repositorySystem = repositorySystem;
this.session = repositorySystemSession;
this.managedDependencies.addAll(managedDependencies);
this.resolutionContext = resolutionContext;
this.repositories = new ArrayList<RemoteRepository>();
List<RemoteRepository> remotes = new ArrayList<RemoteRepository>(
@@ -128,11 +129,13 @@ public class AetherGrapeEngine implements GrapeEngine {
@SuppressWarnings("unchecked")
private List<Exclusion> createExclusions(Map<?, ?> args) {
List<Exclusion> exclusions = new ArrayList<Exclusion>();
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));
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;
@@ -168,7 +171,13 @@ public class AetherGrapeEngine implements GrapeEngine {
String group = (String) dependencyMap.get("group");
String module = (String) dependencyMap.get("module");
String version = (String) dependencyMap.get("version");
return new DefaultArtifact(group, module, "jar", version);
String type = (String) dependencyMap.get("type");
if (type == null) {
type = "jar";
}
return new DefaultArtifact(group, module, type, version);
}
private boolean isTransitive(Map<?, ?> dependencyMap) {
@@ -182,7 +191,8 @@ public class AetherGrapeEngine implements GrapeEngine {
try {
CollectRequest collectRequest = new CollectRequest((Dependency) null,
dependencies, new ArrayList<RemoteRepository>(this.repositories));
collectRequest.setManagedDependencies(this.managedDependencies);
collectRequest.setManagedDependencies(this.resolutionContext
.getManagedDependencies());
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest,
DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE));
@@ -190,7 +200,8 @@ public class AetherGrapeEngine implements GrapeEngine {
DependencyResult dependencyResult = this.repositorySystem
.resolveDependencies(this.session, dependencyRequest);
this.managedDependencies.addAll(getDependencies(dependencyResult));
this.resolutionContext.getManagedDependencies().addAll(
getDependencies(dependencyResult));
return getFiles(dependencyResult);
}
@@ -252,13 +263,26 @@ public class AetherGrapeEngine implements GrapeEngine {
}
@Override
public URI[] resolve(Map args, Map... dependencies) {
throw new UnsupportedOperationException("Resolving to URIs is not supported");
public URI[] resolve(Map args, Map... dependencyMaps) {
return this.resolve(args, null, dependencyMaps);
}
@Override
public URI[] resolve(Map args, List depsInfo, Map... dependencies) {
throw new UnsupportedOperationException("Resolving to URIs is not supported");
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<URI>(files.size());
for (File file : files) {
uris.add(file.toURI());
}
return uris.toArray(new URI[uris.size()]);
}
catch (Exception e) {
throw new DependencyResolutionFailedException(e);
}
}
@Override

View File

@@ -26,7 +26,6 @@ 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.graph.Dependency;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
import org.eclipse.aether.repository.RemoteRepository;
@@ -36,16 +35,18 @@ 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;
import org.springframework.boot.cli.compiler.DependencyResolutionContext;
/**
* Utility class to create a pre-configured {@link AetherGrapeEngine}.
*
*
* @author Andy Wilkinson
*/
public abstract class AetherGrapeEngineFactory {
public static AetherGrapeEngine create(GroovyClassLoader classLoader,
List<RepositoryConfiguration> repositoryConfigurations) {
List<RepositoryConfiguration> repositoryConfigurations,
DependencyResolutionContext dependencyResolutionContext) {
RepositorySystem repositorySystem = createServiceLocator().getService(
RepositorySystem.class);
@@ -63,12 +64,9 @@ public abstract class AetherGrapeEngineFactory {
new DefaultRepositorySystemSessionAutoConfiguration().apply(
repositorySystemSession, repositorySystem);
List<Dependency> managedDependencies = new ManagedDependenciesFactory()
.getManagedDependencies();
return new AetherGrapeEngine(classLoader, repositorySystem,
repositorySystemSession, createRepositories(repositoryConfigurations),
managedDependencies);
dependencyResolutionContext);
}
private static ServiceLocator createServiceLocator() {
@@ -88,7 +86,7 @@ public abstract class AetherGrapeEngineFactory {
for (RepositoryConfiguration repositoryConfiguration : repositoryConfigurations) {
RemoteRepository.Builder builder = new RemoteRepository.Builder(
repositoryConfiguration.getName(), "default", repositoryConfiguration
.getUri().toASCIIString());
.getUri().toASCIIString());
if (!repositoryConfiguration.getSnapshotsEnabled()) {
builder.setSnapshotPolicy(new RepositoryPolicy(false,

View File

@@ -30,7 +30,7 @@ import org.springframework.boot.dependency.tools.VersionManagedDependencies;
/**
* Factory to create Maven {@link Dependency} objects from Boot
* {@link PomManagedDependencies}.
*
*
* @author Phillip Webb
*/
public class ManagedDependenciesFactory {
@@ -41,7 +41,7 @@ public class ManagedDependenciesFactory {
this(new VersionManagedDependencies());
}
ManagedDependenciesFactory(ManagedDependencies dependencies) {
public ManagedDependenciesFactory(ManagedDependencies dependencies) {
this.dependencies = dependencies;
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.groovy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Used to provide an alternative source of dependency metadata that is used to deduce
* groups and versions when processing {@code @Grab} dependencies.
*
* @author Andy Wilkinson
* @since 1.1.0
*/
@Target({ ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE,
ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE })
@Retention(RetentionPolicy.SOURCE)
public @interface GrabMetadata {
/**
* One or more sets of colon-separated coordinates ({@code group:module:version}) of a
* properties file that contains dependency metadata that will add to and override the
* default metadata.
*/
String[] value();
}