Support @Grab when app has multiple groovy scripts

The AST transformation that processes @Grab annotations is driven once
per source file. Previously, this meant that if an app consisted of
multiple source files then multiple, discrete dependency resolutions
would be performed.

This commit updates AetherGrapeEngine to cache a previous resolution's
outcome and use its dependency to influence the outcome of subsequent
resolutions. For example if a one resolution results in spring-core
4.0.0.RELEASE being added to the classpath, subsequent resolutions
that depend upon spring-core will always get the 4.0.0.RELEASE
version. This is achieved by using the dependencies found by earlier
resolutions as dependency management configuration of the current
resolution. This removes the possibility of multiple versions of the
same dependency ending up on the classpath.

In addition to using the results of earlier resolutions to provide
dependency management configuration, default dependency management
configuration is also provided. This configuration is specified by
the springcli.properties file and ensures that, where Boot prescribes
certain versions of a dependency, that is the version that will be
resolved. For example, this ensures that spring-data-redis, which
depends upon Spring 3.1.4, pulls in the version of Spring that Boot
requires instead.

Fixes #224
pull/234/head
Andy Wilkinson 11 years ago
parent b45683f103
commit 5fb42c3c33

@ -58,6 +58,8 @@ public class AetherGrapeEngine implements GrapeEngine {
private static final Collection<Exclusion> WILDCARD_EXCLUSION = Arrays private static final Collection<Exclusion> WILDCARD_EXCLUSION = Arrays
.asList(new Exclusion("*", "*", "*", "*")); .asList(new Exclusion("*", "*", "*", "*"));
private final List<Dependency> managedDependencies = new ArrayList<Dependency>();
private final ProgressReporter progressReporter; private final ProgressReporter progressReporter;
private final GroovyClassLoader classLoader; private final GroovyClassLoader classLoader;
@ -71,10 +73,12 @@ public class AetherGrapeEngine implements GrapeEngine {
public AetherGrapeEngine(GroovyClassLoader classLoader, public AetherGrapeEngine(GroovyClassLoader classLoader,
RepositorySystem repositorySystem, RepositorySystem repositorySystem,
DefaultRepositorySystemSession repositorySystemSession, DefaultRepositorySystemSession repositorySystemSession,
List<RemoteRepository> remoteRepositories) { List<RemoteRepository> remoteRepositories,
List<Dependency> managedDependencies) {
this.classLoader = classLoader; this.classLoader = classLoader;
this.repositorySystem = repositorySystem; this.repositorySystem = repositorySystem;
this.session = repositorySystemSession; this.session = repositorySystemSession;
this.managedDependencies.addAll(managedDependencies);
this.repositories = new ArrayList<RemoteRepository>(); this.repositories = new ArrayList<RemoteRepository>();
List<RemoteRepository> remotes = new ArrayList<RemoteRepository>( List<RemoteRepository> remotes = new ArrayList<RemoteRepository>(
@ -174,13 +178,20 @@ public class AetherGrapeEngine implements GrapeEngine {
private List<File> resolve(List<Dependency> dependencies) private List<File> resolve(List<Dependency> dependencies)
throws ArtifactResolutionException { throws ArtifactResolutionException {
try { try {
CollectRequest collectRequest = new CollectRequest((Dependency) null, CollectRequest collectRequest = new CollectRequest((Dependency) null,
dependencies, new ArrayList<RemoteRepository>(this.repositories)); dependencies, new ArrayList<RemoteRepository>(this.repositories));
collectRequest.setManagedDependencies(this.managedDependencies);
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, DependencyRequest dependencyRequest = new DependencyRequest(collectRequest,
DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE)); DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE));
DependencyResult dependencyResult = this.repositorySystem DependencyResult dependencyResult = this.repositorySystem
.resolveDependencies(this.session, dependencyRequest); .resolveDependencies(this.session, dependencyRequest);
this.managedDependencies.addAll(getDependencies(dependencyResult));
return getFiles(dependencyResult); return getFiles(dependencyResult);
} }
catch (Exception ex) { catch (Exception ex) {
@ -191,6 +202,15 @@ public class AetherGrapeEngine implements GrapeEngine {
} }
} }
private List<Dependency> getDependencies(DependencyResult dependencyResult) {
List<Dependency> dependencies = new ArrayList<Dependency>();
for (ArtifactResult artifactResult : dependencyResult.getArtifactResults()) {
dependencies.add(new Dependency(artifactResult.getArtifact(),
JavaScopes.COMPILE));
}
return dependencies;
}
private List<File> getFiles(DependencyResult dependencyResult) { private List<File> getFiles(DependencyResult dependencyResult) {
List<File> files = new ArrayList<File>(); List<File> files = new ArrayList<File>();
for (ArtifactResult result : dependencyResult.getArtifactResults()) { for (ArtifactResult result : dependencyResult.getArtifactResults()) {

@ -26,6 +26,7 @@ import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.impl.DefaultServiceLocator; import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.internal.impl.DefaultRepositorySystem; import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RemoteRepository;
@ -62,8 +63,12 @@ public abstract class AetherGrapeEngineFactory {
new DefaultRepositorySystemSessionAutoConfiguration().apply( new DefaultRepositorySystemSessionAutoConfiguration().apply(
repositorySystemSession, repositorySystem); repositorySystemSession, repositorySystem);
List<Dependency> managedDependencies = new PropertiesManagedDependenciesFactory()
.getManagedDependencies();
return new AetherGrapeEngine(classLoader, repositorySystem, return new AetherGrapeEngine(classLoader, repositorySystem,
repositorySystemSession, createRepositories(repositoryConfigurations)); repositorySystemSession, createRepositories(repositoryConfigurations),
managedDependencies);
} }
private static ServiceLocator createServiceLocator() { private static ServiceLocator createServiceLocator() {

@ -0,0 +1,32 @@
/*
* 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.grape;
import java.util.List;
import org.eclipse.aether.graph.Dependency;
/**
* An abstraction for accessing the managed dependencies that should be used to influence
* the outcome of dependency resolution performed by Aether.
*
* @author Andy Wilkinson
*/
public interface ManagedDependenciesFactory {
List<Dependency> getManagedDependencies();
}

@ -0,0 +1,93 @@
/*
* 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.grape;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
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.util.Assert;
/**
* A {@link ManagedDependenciesFactory} that uses a properties file to configure the list
* of managed dependencies that it returns.
*
* @author Andy Wilkinson
*/
class PropertiesManagedDependenciesFactory implements ManagedDependenciesFactory {
private static final String PROPERTY_SUFFIX_GROUP_ID = ".groupId";
private static final String PROPERTY_SUFFIX_VERSION = ".version";
private final List<Dependency> managedDependencies;
public PropertiesManagedDependenciesFactory() {
Properties properties = loadProperties();
this.managedDependencies = getManagedDependencies(properties);
}
private static Properties loadProperties() {
Properties properties = new Properties();
InputStream inputStream = PropertiesManagedDependenciesFactory.class
.getClassLoader().getResourceAsStream("META-INF/springcli.properties");
Assert.state(inputStream != null, "Unable to load springcli properties");
try {
properties.load(inputStream);
return properties;
}
catch (IOException ex) {
throw new IllegalStateException("Unable to load springcli properties", ex);
}
}
private static List<Dependency> getManagedDependencies(Properties properties) {
List<Dependency> dependencies = new ArrayList<Dependency>();
for (Entry<Object, Object> entry : properties.entrySet()) {
String propertyName = (String) entry.getKey();
if (propertyName.endsWith(PROPERTY_SUFFIX_GROUP_ID)) {
String artifactId = propertyName.substring(0, propertyName.length()
- PROPERTY_SUFFIX_GROUP_ID.length());
String groupId = (String) entry.getValue();
String version = properties.getProperty(artifactId
+ PROPERTY_SUFFIX_VERSION);
if (version != null) {
Artifact artifact = new DefaultArtifact(groupId, artifactId, "jar",
version);
dependencies.add(new Dependency(artifact, JavaScopes.COMPILE));
}
}
}
return dependencies;
}
@Override
public List<Dependency> getManagedDependencies() {
return new ArrayList<Dependency>(this.managedDependencies);
}
}
Loading…
Cancel
Save