Support bom-based dependency management in the CLI

Previously, the CLI’s dependency management used proprietary Properties
file-based metadata to configure its dependency management. Since
spring-boot-gradle-plugin’s move to using the separate dependency
management plugin the CLI was the only user of this format.

This commit updates the CLI to use Maven boms to configure its
dependency management. By default it uses the spring-boot-dependencies
bom. This configuration can be augmented and overridden using the new
@DependencyManagementBom annotation which replaces @GrabMetadata.

Closes gh-2688
Closes gh-2439
This commit is contained in:
Andy Wilkinson
2015-03-19 17:15:10 +00:00
parent 390e6fa690
commit 51c49b69c5
73 changed files with 1001 additions and 1624 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.boot.cli.compiler;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -42,12 +43,16 @@ public abstract class AnnotatedNodeASTTransformation implements ASTTransformatio
private final Set<String> interestingAnnotationNames;
private final boolean removeAnnotations;
private List<AnnotationNode> annotationNodes = new ArrayList<AnnotationNode>();
private SourceUnit sourceUnit;
protected AnnotatedNodeASTTransformation(Set<String> interestingAnnotationNames) {
protected AnnotatedNodeASTTransformation(Set<String> interestingAnnotationNames,
boolean removeAnnotations) {
this.interestingAnnotationNames = interestingAnnotationNames;
this.removeAnnotations = removeAnnotations;
}
@Override
@@ -94,10 +99,16 @@ public abstract class AnnotatedNodeASTTransformation implements ASTTransformatio
private void visitAnnotatedNode(AnnotatedNode annotatedNode) {
if (annotatedNode != null) {
for (AnnotationNode annotationNode : annotatedNode.getAnnotations()) {
Iterator<AnnotationNode> annotationNodes = annotatedNode.getAnnotations()
.iterator();
while (annotationNodes.hasNext()) {
AnnotationNode annotationNode = annotationNodes.next();
if (this.interestingAnnotationNames.contains(annotationNode
.getClassNode().getName())) {
this.annotationNodes.add(annotationNode);
if (this.removeAnnotations) {
annotationNodes.remove();
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@ import org.springframework.core.annotation.Order;
@Order(DependencyAutoConfigurationTransformation.ORDER)
public class DependencyAutoConfigurationTransformation implements ASTTransformation {
public static final int ORDER = GrabMetadataTransformation.ORDER + 100;
public static final int ORDER = DependencyManagementBomTransformation.ORDER + 100;
private final GroovyClassLoader loader;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@ package org.springframework.boot.cli.compiler;
import groovy.grape.Grape;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
@@ -29,6 +29,16 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.maven.model.Model;
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.building.ModelSource;
import org.apache.maven.model.building.UrlModelSource;
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;
@@ -38,33 +48,33 @@ 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.dependency.tools.Dependencies;
import org.springframework.boot.dependency.tools.ManagedDependencies;
import org.springframework.boot.dependency.tools.PropertiesFileDependencies;
import org.springframework.boot.groovy.GrabMetadata;
import org.springframework.boot.groovy.DependencyManagementBom;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* {@link ASTTransformation} for processing {@link GrabMetadata @GrabMetadata}
* {@link ASTTransformation} for processing {@link DependencyManagementBom} annotations
*
* @author Andy Wilkinson
* @since 1.1.0
* @since 1.3.0
*/
@Order(GrabMetadataTransformation.ORDER)
public class GrabMetadataTransformation extends AnnotatedNodeASTTransformation {
@Order(DependencyManagementBomTransformation.ORDER)
public class DependencyManagementBomTransformation extends AnnotatedNodeASTTransformation {
public static final int ORDER = Ordered.HIGHEST_PRECEDENCE;
private static final Set<String> GRAB_METADATA_ANNOTATION_NAMES = Collections
private static final Set<String> DEPENDENCY_MANAGEMENT_BOM_ANNOTATION_NAMES = Collections
.unmodifiableSet(new HashSet<String>(Arrays.asList(
GrabMetadata.class.getName(), GrabMetadata.class.getSimpleName())));
DependencyManagementBom.class.getName(),
DependencyManagementBom.class.getSimpleName())));
private final DependencyResolutionContext resolutionContext;
public GrabMetadataTransformation(DependencyResolutionContext resolutionContext) {
super(GRAB_METADATA_ANNOTATION_NAMES);
public DependencyManagementBomTransformation(
DependencyResolutionContext resolutionContext) {
super(DEPENDENCY_MANAGEMENT_BOM_ANNOTATION_NAMES, true);
this.resolutionContext = resolutionContext;
}
@@ -73,19 +83,19 @@ public class GrabMetadataTransformation extends AnnotatedNodeASTTransformation {
if (!annotationNodes.isEmpty()) {
if (annotationNodes.size() > 1) {
for (AnnotationNode annotationNode : annotationNodes) {
handleDuplicateGrabMetadataAnnotation(annotationNode);
handleDuplicateDependencyManagementBomAnnotation(annotationNode);
}
}
else {
processGrabMetadataAnnotation(annotationNodes.get(0));
processDependencyManagementBomAnnotation(annotationNodes.get(0));
}
}
}
private void processGrabMetadataAnnotation(AnnotationNode annotationNode) {
private void processDependencyManagementBomAnnotation(AnnotationNode annotationNode) {
Expression valueExpression = annotationNode.getMember("value");
List<Map<String, String>> metadataDependencies = createDependencyMaps(valueExpression);
updateArtifactCoordinatesResolver(metadataDependencies);
List<Map<String, String>> bomDependencies = createDependencyMaps(valueExpression);
updateDependencyResolutionContext(bomDependencies);
}
private List<Map<String, String>> createDependencyMaps(Expression valueExpression) {
@@ -104,7 +114,7 @@ public class GrabMetadataTransformation extends AnnotatedNodeASTTransformation {
dependency.put("group", components[0]);
dependency.put("module", components[1]);
dependency.put("version", components[2]);
dependency.put("type", "properties");
dependency.put("type", "pom");
dependencies.add(dependency);
}
else {
@@ -126,7 +136,7 @@ public class GrabMetadataTransformation extends AnnotatedNodeASTTransformation {
return Arrays.asList((ConstantExpression) valueExpression);
}
reportError("@GrabMetadata requires an inline constant that is a "
reportError("@DependencyManagementBom requires an inline constant that is a "
+ "string or a string array", valueExpression);
return Collections.emptyList();
}
@@ -152,29 +162,34 @@ public class GrabMetadataTransformation extends AnnotatedNodeASTTransformation {
getSourceUnit().getErrorCollector().addErrorAndContinue(message);
}
private void updateArtifactCoordinatesResolver(
List<Map<String, String>> metadataDependencies) {
private void updateDependencyResolutionContext(
List<Map<String, String>> bomDependencies) {
URI[] uris = Grape.getInstance().resolve(null,
metadataDependencies.toArray(new Map[metadataDependencies.size()]));
List<Dependencies> managedDependencies = new ArrayList<Dependencies>(uris.length);
bomDependencies.toArray(new Map[bomDependencies.size()]));
DefaultModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
for (URI uri : uris) {
try {
managedDependencies.add(new PropertiesFileDependencies(uri.toURL()
.openStream()));
DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
request.setModelResolver(new GrapeModelResolver());
request.setModelSource(new UrlModelSource(uri.toURL()));
Model model = modelBuilder.build(request).getEffectiveModel();
this.resolutionContext
.addDependencyManagement(new MavenModelDependencyManagement(model));
}
catch (IOException ex) {
throw new IllegalStateException("Failed to parse '" + uris[0]
+ "'. Is it a valid properties file?");
catch (Exception ex) {
throw new IllegalStateException("Failed to build model for '" + uri
+ "'. Is it a valid Maven bom?", ex);
}
}
this.resolutionContext.setManagedDependencies(ManagedDependencies
.get(managedDependencies));
}
private void handleDuplicateGrabMetadataAnnotation(AnnotationNode annotationNode) {
private void handleDuplicateDependencyManagementBomAnnotation(
AnnotationNode annotationNode) {
Message message = createSyntaxErrorMessage(
"Duplicate @GrabMetadata annotation. It must be declared at most once.",
"Duplicate @DependencyManagementBom annotation. It must be declared at most once.",
annotationNode);
getSourceUnit().getErrorCollector().addErrorAndContinue(message);
}
@@ -189,4 +204,36 @@ public class GrabMetadataTransformation extends AnnotatedNodeASTTransformation {
node.getColumnNumber(), node.getLastLineNumber(),
node.getLastColumnNumber()), getSourceUnit());
}
private static class GrapeModelResolver implements ModelResolver {
@Override
public ModelSource resolveModel(String groupId, String artifactId, String version)
throws UnresolvableModelException {
Map<String, String> dependency = new HashMap<String, String>();
dependency.put("group", groupId);
dependency.put("module", artifactId);
dependency.put("version", version);
dependency.put("type", "pom");
try {
return new UrlModelSource(
Grape.getInstance().resolve(null, dependency)[0].toURL());
}
catch (MalformedURLException e) {
throw new UnresolvableModelException(e.getMessage(), groupId, artifactId,
version);
}
}
@Override
public void addRepository(Repository repository)
throws InvalidRepositoryException {
}
@Override
public ModelResolver newCopy() {
return this;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ import org.springframework.core.annotation.Order;
@Order(GroovyBeansTransformation.ORDER)
public class GroovyBeansTransformation implements ASTTransformation {
public static final int ORDER = GrabMetadataTransformation.ORDER + 200;
public static final int ORDER = DependencyManagementBomTransformation.ORDER + 200;
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {

View File

@@ -105,7 +105,7 @@ public class GroovyCompiler {
}
this.transformations = new ArrayList<ASTTransformation>();
this.transformations.add(new GrabMetadataTransformation(resolutionContext));
this.transformations.add(new DependencyManagementBomTransformation(resolutionContext));
this.transformations.add(new DependencyAutoConfigurationTransformation(
this.loader, resolutionContext, this.compilerAutoConfigurations));
this.transformations.add(new GroovyBeansTransformation());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,7 +41,7 @@ import org.springframework.core.annotation.Order;
public class ResolveDependencyCoordinatesTransformation extends
AnnotatedNodeASTTransformation {
public static final int ORDER = GrabMetadataTransformation.ORDER + 300;
public static final int ORDER = DependencyManagementBomTransformation.ORDER + 300;
private static final Set<String> GRAB_ANNOTATION_NAMES = Collections
.unmodifiableSet(new HashSet<String>(Arrays.asList(Grab.class.getName(),
@@ -51,7 +51,7 @@ public class ResolveDependencyCoordinatesTransformation extends
public ResolveDependencyCoordinatesTransformation(
DependencyResolutionContext resolutionContext) {
super(GRAB_ANNOTATION_NAMES);
super(GRAB_ANNOTATION_NAMES, false);
this.resolutionContext = resolutionContext;
}

View File

@@ -70,8 +70,7 @@ public class SpringBootCompilerAutoConfiguration extends CompilerAutoConfigurati
"org.springframework.boot.context.properties.EnableConfigurationProperties",
"org.springframework.boot.autoconfigure.EnableAutoConfiguration",
"org.springframework.boot.context.properties.ConfigurationProperties",
"org.springframework.boot.context.properties.EnableConfigurationProperties",
"org.springframework.boot.groovy.GrabMetadata");
"org.springframework.boot.context.properties.EnableConfigurationProperties");
imports.addStarImports("org.springframework.stereotype",
"org.springframework.scheduling.annotation");
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.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<Dependency>();
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

@@ -0,0 +1,199 @@
/*
* 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.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.<Exclusion> 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 String toString() {
return this.groupId + ":" + this.artifactId + ":" + this.version;
}
@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 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 &= this.groupId.equals(other.groupId);
result &= this.artifactId.equals(other.artifactId);
result &= this.version.equals(other.version);
result &= this.exclusions.equals(other.exclusions);
return result;
}
return false;
}
/**
* 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(groupId, "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 String toString() {
return this.groupId + ":" + this.artifactId;
}
@Override
public int hashCode() {
return this.groupId.hashCode() * 31 + this.artifactId.hashCode();
}
@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 &= this.groupId.equals(other.groupId);
result &= this.artifactId.equals(other.artifactId);
return result;
}
return false;
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.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

@@ -0,0 +1,73 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.cli.compiler.dependencies;
import org.springframework.util.StringUtils;
/**
* {@link ArtifactCoordinatesResolver} backed by {@link SpringBootDependenciesDependencyManagement}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
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 ? null : dependency.getGroupId());
}
@Override
public String getArtifactId(String id) {
Dependency dependency = find(id);
return dependency == null ? null : dependency.getArtifactId();
}
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 ? null : dependency.getVersion();
}
}

View File

@@ -1,74 +0,0 @@
/*
* 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.dependencies;
import org.springframework.boot.dependency.tools.Dependencies;
import org.springframework.boot.dependency.tools.Dependency;
import org.springframework.boot.dependency.tools.ManagedDependencies;
import org.springframework.util.StringUtils;
/**
* {@link ArtifactCoordinatesResolver} backed by {@link Dependencies}.
*
* @author Phillip Webb
*/
public class ManagedDependenciesArtifactCoordinatesResolver implements
ArtifactCoordinatesResolver {
private final ManagedDependencies dependencies;
public ManagedDependenciesArtifactCoordinatesResolver() {
this(ManagedDependencies.get());
}
public ManagedDependenciesArtifactCoordinatesResolver(ManagedDependencies dependencies) {
this.dependencies = dependencies;
}
@Override
public String getGroupId(String artifactId) {
Dependency dependency = find(artifactId);
return (dependency == null ? null : dependency.getGroupId());
}
@Override
public String getVersion(String artifactId) {
Dependency dependency = find(artifactId);
return (dependency == null ? null : dependency.getVersion());
}
@Override
public String getArtifactId(String artifactId) {
Dependency dependency = find(artifactId);
return (dependency == null ? null : dependency.getArtifactId());
}
private Dependency find(String artifactId) {
if (StringUtils.countOccurrencesOf(artifactId, ":") == 2) {
String[] tokens = artifactId.split(":");
return new Dependency(tokens[0], tokens[1], tokens[2]);
}
if (artifactId != null) {
if (artifactId.startsWith("spring-boot")) {
return new Dependency("org.springframework.boot", artifactId,
this.dependencies.getSpringBootVersion());
}
return this.dependencies.find(artifactId);
}
return null;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.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<String, Dependency>();
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<Dependency>();
for (org.apache.maven.model.Dependency mavenDependency : model
.getDependencyManagement().getDependencies()) {
List<Exclusion> exclusions = new ArrayList<Exclusion>();
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

@@ -0,0 +1,54 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.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 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("effective-pom.xml"), null);
}
catch (IOException ex) {
throw new IllegalStateException("Failed to build model from effective pom",
ex);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -167,6 +167,9 @@ public class AetherGrapeEngine implements GrapeEngine {
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);
@@ -324,7 +327,7 @@ public class AetherGrapeEngine implements GrapeEngine {
}
private void addManagedDependencies(DependencyResult result) {
this.resolutionContext.getManagedDependencies().addAll(getDependencies(result));
this.resolutionContext.addManagedDependencies(getDependencies(result));
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,12 +17,20 @@
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.ManagedDependenciesArtifactCoordinatesResolver;
import org.springframework.boot.dependency.tools.ManagedDependencies;
import org.springframework.boot.cli.compiler.dependencies.CompositeDependencyManagement;
import org.springframework.boot.cli.compiler.dependencies.DependencyManagement;
import org.springframework.boot.cli.compiler.dependencies.DependencyManagementArtifactCoordinatesResolver;
import org.springframework.boot.cli.compiler.dependencies.SpringBootDependenciesDependencyManagement;
/**
* Context used when resolving dependencies.
@@ -32,34 +40,77 @@ import org.springframework.boot.dependency.tools.ManagedDependencies;
*/
public class DependencyResolutionContext {
private ArtifactCoordinatesResolver artifactCoordinatesResolver;
private final Map<String, Dependency> managedDependencyByGroupAndArtifact = new HashMap<String, Dependency>();
private List<Dependency> managedDependencies = new ArrayList<Dependency>();
private final List<Dependency> managedDependencies = new ArrayList<Dependency>();
private DependencyManagement dependencyManagement = new SpringBootDependenciesDependencyManagement();
private ArtifactCoordinatesResolver artifactCoordinatesResolver = new DependencyManagementArtifactCoordinatesResolver(
this.dependencyManagement);
public DependencyResolutionContext() {
this(new ManagedDependenciesArtifactCoordinatesResolver());
addDependencyManagement(this.dependencyManagement);
}
public DependencyResolutionContext(
ArtifactCoordinatesResolver artifactCoordinatesResolver) {
this.artifactCoordinatesResolver = artifactCoordinatesResolver;
this.managedDependencies = new ManagedDependenciesFactory()
.getManagedDependencies();
private String getIdentifier(Dependency dependency) {
return getIdentifier(dependency.getArtifact().getGroupId(), dependency
.getArtifact().getArtifactId());
}
public void setManagedDependencies(ManagedDependencies managedDependencies) {
this.artifactCoordinatesResolver = new ManagedDependenciesArtifactCoordinatesResolver(
managedDependencies);
this.managedDependencies = new ArrayList<Dependency>(
new ManagedDependenciesFactory(managedDependencies)
.getManagedDependencies());
private String getIdentifier(String groupId, String artifactId) {
return groupId + ":" + artifactId;
}
public ArtifactCoordinatesResolver getArtifactCoordinatesResolver() {
return this.artifactCoordinatesResolver;
}
public List<Dependency> getManagedDependencies() {
return this.managedDependencies;
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));
}
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<Exclusion>();
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.artifactCoordinatesResolver = new DependencyManagementArtifactCoordinatesResolver(
new CompositeDependencyManagement(dependencyManagement,
this.dependencyManagement));
}
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.cli.compiler.grape;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.springframework.boot.dependency.tools.ManagedDependencies;
import org.springframework.boot.dependency.tools.PomDependencies;
/**
* Factory to create Maven {@link Dependency} objects from Boot {@link PomDependencies}.
*
* @author Phillip Webb
*/
public class ManagedDependenciesFactory {
private final ManagedDependencies dependencies;
ManagedDependenciesFactory() {
this(ManagedDependencies.get());
}
public ManagedDependenciesFactory(ManagedDependencies dependencies) {
this.dependencies = dependencies;
}
/**
* Return a list of the managed dependencies.
* @return the managed dependencies
*/
public List<Dependency> getManagedDependencies() {
List<Dependency> result = new ArrayList<Dependency>();
for (org.springframework.boot.dependency.tools.Dependency dependency : this.dependencies) {
Artifact artifact = asArtifact(dependency);
result.add(new Dependency(artifact, JavaScopes.COMPILE));
}
return result;
}
private Artifact asArtifact(
org.springframework.boot.dependency.tools.Dependency dependency) {
return new DefaultArtifact(dependency.getGroupId(), dependency.getArtifactId(),
"jar", dependency.getVersion());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,21 +22,21 @@ 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.
* Provides one or more additional sources of dependency management that is used when
* resolving {@code @Grab} dependencies.
*
* @author Andy Wilkinson
* @since 1.1.0
* @since 1.3.0
*/
@Target({ ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE,
ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE })
@Retention(RetentionPolicy.SOURCE)
public @interface GrabMetadata {
public @interface DependencyManagementBom {
/**
* 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.
* Maven bom that contains dependency management that will add to and override the
* default dependency management.
*/
String[] value();