Merge branch 'aether-grab'
commit
da748656ba
@ -0,0 +1,175 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-parent</artifactId>
|
||||||
|
<version>0.5.0.BUILD-SNAPSHOT</version>
|
||||||
|
<relativePath>../spring-boot-parent</relativePath>
|
||||||
|
</parent>
|
||||||
|
<artifactId>spring-boot-cli-grape</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<properties>
|
||||||
|
<main.basedir>${basedir}/..</main.basedir>
|
||||||
|
<start-class>org.springframework.boot.cli.SpringCli</start-class>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<!-- Provided -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.groovy</groupId>
|
||||||
|
<artifactId>groovy</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- Compile -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-logging</groupId>
|
||||||
|
<artifactId>commons-logging</artifactId>
|
||||||
|
<version>1.1.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-aether-provider</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>org.eclipse.sisu.plexus</artifactId>
|
||||||
|
<groupId>org.eclipse.sisu</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.aether</groupId>
|
||||||
|
<artifactId>aether-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.aether</groupId>
|
||||||
|
<artifactId>aether-connector-basic</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.aether</groupId>
|
||||||
|
<artifactId>aether-impl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.aether</groupId>
|
||||||
|
<artifactId>aether-spi</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.aether</groupId>
|
||||||
|
<artifactId>aether-transport-file</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.aether</groupId>
|
||||||
|
<artifactId>aether-transport-http</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>jcl-over-slf4j</artifactId>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.aether</groupId>
|
||||||
|
<artifactId>aether-util</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- Test -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExcludes>org.springframework:spring-core</classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExcludes>org.springframework:spring-beans</classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExcludes>org.springframework:spring-aop</classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExcludes>org.springframework:spring-tx</classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExcludes>org.springframework:spring-expression</classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExcludes>org.springframework:spring-context</classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExcludes>org.springframework:spring-test</classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExcludes>org.springframework.retry:spring-retry</classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExcludes>org.springframework.integration:spring-integration-core</classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExcludes>org.springframework.integration:spring-integration-dsl-groovy-core</classpathDependencyExcludes>
|
||||||
|
</classpathDependencyExcludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<filters>
|
||||||
|
<filter>
|
||||||
|
<artifact>*:*</artifact>
|
||||||
|
<excludes>
|
||||||
|
<exclude>META-INF/*.SF</exclude>
|
||||||
|
<exclude>META-INF/*.DSA</exclude>
|
||||||
|
<exclude>META-INF/*.RSA</exclude>
|
||||||
|
</excludes>
|
||||||
|
</filter>
|
||||||
|
</filters>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer
|
||||||
|
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
|
||||||
|
<transformer
|
||||||
|
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>${start-class}</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.eclipse.m2e</groupId>
|
||||||
|
<artifactId>lifecycle-mapping</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<configuration>
|
||||||
|
<lifecycleMappingMetadata>
|
||||||
|
<pluginExecutions>
|
||||||
|
<pluginExecution>
|
||||||
|
<pluginExecutionFilter>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-antrun-plugin</artifactId>
|
||||||
|
<versionRange>[1.7,)</versionRange>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
</pluginExecutionFilter>
|
||||||
|
<action>
|
||||||
|
<execute/>
|
||||||
|
</action>
|
||||||
|
</pluginExecution>
|
||||||
|
</pluginExecutions>
|
||||||
|
</lifecycleMappingMetadata>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
<pluginRepositories>
|
||||||
|
<pluginRepository>
|
||||||
|
<id>objectstyle</id>
|
||||||
|
<name>ObjectStyle.org Repository</name>
|
||||||
|
<url>http://objectstyle.org/maven2/</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</pluginRepository>
|
||||||
|
</pluginRepositories>
|
||||||
|
</project>
|
@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 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.GrapeEngine;
|
||||||
|
import groovy.lang.GroovyClassLoader;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
|
||||||
|
import org.eclipse.aether.AbstractRepositoryListener;
|
||||||
|
import org.eclipse.aether.DefaultRepositorySystemSession;
|
||||||
|
import org.eclipse.aether.RepositoryEvent;
|
||||||
|
import org.eclipse.aether.RepositorySystem;
|
||||||
|
import org.eclipse.aether.RepositorySystemSession;
|
||||||
|
import org.eclipse.aether.artifact.Artifact;
|
||||||
|
import org.eclipse.aether.artifact.DefaultArtifact;
|
||||||
|
import org.eclipse.aether.collection.CollectRequest;
|
||||||
|
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
|
||||||
|
import org.eclipse.aether.graph.Dependency;
|
||||||
|
import org.eclipse.aether.impl.ArtifactDescriptorReader;
|
||||||
|
import org.eclipse.aether.impl.DefaultServiceLocator;
|
||||||
|
import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
|
||||||
|
import org.eclipse.aether.repository.LocalRepository;
|
||||||
|
import org.eclipse.aether.repository.RemoteRepository;
|
||||||
|
import org.eclipse.aether.resolution.ArtifactDescriptorException;
|
||||||
|
import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
|
||||||
|
import org.eclipse.aether.resolution.ArtifactDescriptorResult;
|
||||||
|
import org.eclipse.aether.resolution.ArtifactResolutionException;
|
||||||
|
import org.eclipse.aether.resolution.ArtifactResult;
|
||||||
|
import org.eclipse.aether.resolution.DependencyRequest;
|
||||||
|
import org.eclipse.aether.resolution.DependencyResult;
|
||||||
|
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
|
||||||
|
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
|
||||||
|
import org.eclipse.aether.transfer.AbstractTransferListener;
|
||||||
|
import org.eclipse.aether.transfer.TransferCancelledException;
|
||||||
|
import org.eclipse.aether.transfer.TransferEvent;
|
||||||
|
import org.eclipse.aether.transport.file.FileTransporterFactory;
|
||||||
|
import org.eclipse.aether.transport.http.HttpTransporterFactory;
|
||||||
|
import org.eclipse.aether.util.artifact.JavaScopes;
|
||||||
|
import org.eclipse.aether.util.filter.DependencyFilterUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link GrapeEngine} implementation that uses <a
|
||||||
|
* href="http://eclipse.org/aether">Aether</a>, the dependency resolution system used by
|
||||||
|
* Maven.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
public class AetherGrapeEngine implements GrapeEngine {
|
||||||
|
|
||||||
|
private static final String DEPENDENCY_MODULE = "module";
|
||||||
|
|
||||||
|
private static final String DEPENDENCY_GROUP = "group";
|
||||||
|
|
||||||
|
private static final String DEPENDENCY_VERSION = "version";
|
||||||
|
|
||||||
|
private final Artifact parentArtifact;
|
||||||
|
|
||||||
|
private final ProgressReporter progressReporter = new ProgressReporter();
|
||||||
|
|
||||||
|
private final ArtifactDescriptorReader artifactDescriptorReader;
|
||||||
|
|
||||||
|
private final GroovyClassLoader defaultClassLoader;
|
||||||
|
|
||||||
|
private final RepositorySystemSession repositorySystemSession;
|
||||||
|
|
||||||
|
private final RepositorySystem repositorySystem;
|
||||||
|
|
||||||
|
private final List<RemoteRepository> repositories;
|
||||||
|
|
||||||
|
public AetherGrapeEngine(GroovyClassLoader classLoader, String parentGroupId,
|
||||||
|
String parentArtifactId, String parentVersion) {
|
||||||
|
this.defaultClassLoader = classLoader;
|
||||||
|
this.parentArtifact = new DefaultArtifact(parentGroupId, parentArtifactId, "pom",
|
||||||
|
parentVersion);
|
||||||
|
|
||||||
|
DefaultServiceLocator mavenServiceLocator = MavenRepositorySystemUtils
|
||||||
|
.newServiceLocator();
|
||||||
|
mavenServiceLocator.addService(RepositorySystem.class,
|
||||||
|
DefaultRepositorySystem.class);
|
||||||
|
|
||||||
|
mavenServiceLocator.addService(RepositoryConnectorFactory.class,
|
||||||
|
BasicRepositoryConnectorFactory.class);
|
||||||
|
|
||||||
|
mavenServiceLocator.addService(TransporterFactory.class,
|
||||||
|
HttpTransporterFactory.class);
|
||||||
|
mavenServiceLocator.addService(TransporterFactory.class,
|
||||||
|
FileTransporterFactory.class);
|
||||||
|
|
||||||
|
this.repositorySystem = mavenServiceLocator.getService(RepositorySystem.class);
|
||||||
|
|
||||||
|
DefaultRepositorySystemSession repositorySystemSession = MavenRepositorySystemUtils
|
||||||
|
.newSession();
|
||||||
|
repositorySystemSession.setTransferListener(new AbstractTransferListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferStarted(TransferEvent event)
|
||||||
|
throws TransferCancelledException {
|
||||||
|
AetherGrapeEngine.this.progressReporter.reportProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferProgressed(TransferEvent event)
|
||||||
|
throws TransferCancelledException {
|
||||||
|
AetherGrapeEngine.this.progressReporter.reportProgress();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
repositorySystemSession.setRepositoryListener(new AbstractRepositoryListener() {
|
||||||
|
@Override
|
||||||
|
public void artifactResolved(RepositoryEvent event) {
|
||||||
|
AetherGrapeEngine.this.progressReporter.reportProgress();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
LocalRepository localRepo = new LocalRepository(new File(
|
||||||
|
System.getProperty("user.home"), ".m2/repository"));
|
||||||
|
repositorySystemSession.setLocalRepositoryManager(this.repositorySystem
|
||||||
|
.newLocalRepositoryManager(repositorySystemSession, localRepo));
|
||||||
|
|
||||||
|
this.repositorySystemSession = repositorySystemSession;
|
||||||
|
|
||||||
|
this.repositories = Arrays.asList(new RemoteRepository.Builder("central",
|
||||||
|
"default", "http://repo1.maven.org/maven2/").build(),
|
||||||
|
new RemoteRepository.Builder("spring-snapshot", "default",
|
||||||
|
"http://repo.spring.io/snapshot").build(),
|
||||||
|
new RemoteRepository.Builder("spring-milestone", "default",
|
||||||
|
"http://repo.spring.io/milestone").build());
|
||||||
|
|
||||||
|
this.artifactDescriptorReader = mavenServiceLocator
|
||||||
|
.getService(ArtifactDescriptorReader.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object grab(Map args) {
|
||||||
|
return grab(args, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object grab(Map args, Map... dependencyMaps) {
|
||||||
|
List<Dependency> dependencies = createDependencies(dependencyMaps);
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<File> files = resolve(dependencies);
|
||||||
|
GroovyClassLoader classLoader = (GroovyClassLoader) args.get("classLoader");
|
||||||
|
if (classLoader == null) {
|
||||||
|
classLoader = this.defaultClassLoader;
|
||||||
|
}
|
||||||
|
for (File file : files) {
|
||||||
|
classLoader.addURL(file.toURI().toURL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ArtifactResolutionException ex) {
|
||||||
|
throw new DependencyResolutionFailedException(ex);
|
||||||
|
}
|
||||||
|
catch (MalformedURLException ex) {
|
||||||
|
throw new DependencyResolutionFailedException(ex);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Dependency> createDependencies(Map<?, ?>... dependencyMaps) {
|
||||||
|
List<Dependency> dependencies = new ArrayList<Dependency>(dependencyMaps.length);
|
||||||
|
for (Map<?, ?> dependencyMap : dependencyMaps) {
|
||||||
|
dependencies.add(createDependency(dependencyMap));
|
||||||
|
}
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dependency createDependency(Map<?, ?> dependencyMap) {
|
||||||
|
String group = (String) dependencyMap.get(DEPENDENCY_GROUP);
|
||||||
|
String module = (String) dependencyMap.get(DEPENDENCY_MODULE);
|
||||||
|
String version = (String) dependencyMap.get(DEPENDENCY_VERSION);
|
||||||
|
|
||||||
|
// TODO Transitivity
|
||||||
|
|
||||||
|
Artifact artifact = new DefaultArtifact(group, module, "jar", version);
|
||||||
|
return new Dependency(artifact, JavaScopes.COMPILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<File> resolve(List<Dependency> dependencies)
|
||||||
|
throws ArtifactResolutionException {
|
||||||
|
|
||||||
|
CollectRequest collectRequest = new CollectRequest((Dependency) null,
|
||||||
|
dependencies, this.repositories);
|
||||||
|
collectRequest.setManagedDependencies(getManagedDependencies());
|
||||||
|
|
||||||
|
try {
|
||||||
|
DependencyResult dependencyResult = this.repositorySystem
|
||||||
|
.resolveDependencies(
|
||||||
|
this.repositorySystemSession,
|
||||||
|
new DependencyRequest(collectRequest, DependencyFilterUtils
|
||||||
|
.classpathFilter(JavaScopes.COMPILE)));
|
||||||
|
List<File> files = new ArrayList<File>();
|
||||||
|
for (ArtifactResult result : dependencyResult.getArtifactResults()) {
|
||||||
|
files.add(result.getArtifact().getFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new DependencyResolutionFailedException(ex);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
this.progressReporter.finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Dependency> getManagedDependencies() {
|
||||||
|
ArtifactDescriptorRequest parentRequest = new ArtifactDescriptorRequest();
|
||||||
|
parentRequest.setArtifact(this.parentArtifact);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ArtifactDescriptorResult artifactDescriptorResult = this.artifactDescriptorReader
|
||||||
|
.readArtifactDescriptor(this.repositorySystemSession, parentRequest);
|
||||||
|
return artifactDescriptorResult.getManagedDependencies();
|
||||||
|
}
|
||||||
|
catch (ArtifactDescriptorException ex) {
|
||||||
|
throw new DependencyResolutionFailedException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Map<String, List<String>>> enumerateGrapes() {
|
||||||
|
throw new UnsupportedOperationException("Grape enumeration is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI[] resolve(Map args, Map... dependencies) {
|
||||||
|
throw new UnsupportedOperationException("Resolving to URIs is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI[] resolve(Map args, List depsInfo, Map... dependencies) {
|
||||||
|
throw new UnsupportedOperationException("Resolving to URIs is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map[] listDependencies(ClassLoader classLoader) {
|
||||||
|
throw new UnsupportedOperationException("Listing dependencies is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addResolver(Map<String, Object> args) {
|
||||||
|
throw new UnsupportedOperationException("Adding a resolver is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object grab(String endorsedModule) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Grabbing an endorsed module is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ProgressReporter {
|
||||||
|
|
||||||
|
private static final long INITIAL_DELAY = TimeUnit.SECONDS.toMillis(3);
|
||||||
|
|
||||||
|
private static final long PROGRESS_DELAY = TimeUnit.SECONDS.toMillis(1);
|
||||||
|
|
||||||
|
private long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
private long lastProgressTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
private boolean started;
|
||||||
|
|
||||||
|
private boolean finished;
|
||||||
|
|
||||||
|
void reportProgress() {
|
||||||
|
if (!this.finished
|
||||||
|
&& System.currentTimeMillis() - this.startTime > INITIAL_DELAY) {
|
||||||
|
if (!this.started) {
|
||||||
|
this.started = true;
|
||||||
|
System.out.print("Resolving dependencies..");
|
||||||
|
this.lastProgressTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
else if (System.currentTimeMillis() - this.lastProgressTime > PROGRESS_DELAY) {
|
||||||
|
System.out.print(".");
|
||||||
|
this.lastProgressTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void finished() {
|
||||||
|
if (this.started && !this.finished) {
|
||||||
|
this.finished = true;
|
||||||
|
System.out.println("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown to indicate a failure during dependency resolution.
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#Generated by Git-Commit-Id-Plugin
|
||||||
|
#Tue Oct 22 10:25:03 BST 2013
|
||||||
|
git.commit.id.abbrev=040321b
|
||||||
|
git.commit.user.email=awilkinson@gopivotal.com
|
||||||
|
git.commit.message.full=Isolate Aether in a separate class loader\n\nPrior to this commit, the Aether-based GrapeEngine was loaded in the\nsame class loader as the rest of Boot. This led to Aether's and its\ndependencies' types polluting the application's class path. Most\nnotably, this caused problems with logging as the logging framework\ncould be permaturely initialized.\n\nThis commit isolates AetherGrapeEngine, Aether and its dependencies\ninto a separate class loader. This is done by customizing the\npackaging of the CLI's jar file with the internal directory housing\nall of the types that will be loaded by the separate class loader.\n
|
||||||
|
git.commit.id=040321bf153db007290786623d45903cee27fa88
|
||||||
|
git.commit.message.short=Isolate Aether in a separate class loader
|
||||||
|
git.commit.user.name=Andy Wilkinson
|
||||||
|
git.build.user.name=Andy Wilkinson
|
||||||
|
git.build.user.email=awilkinson@gopivotal.com
|
||||||
|
git.branch=aether-grab
|
||||||
|
git.commit.time=2013-10-21T16\:24\:40+0100
|
||||||
|
git.build.time=2013-10-22T10\:25\:03+0100
|
@ -0,0 +1,25 @@
|
|||||||
|
<assembly>
|
||||||
|
<id>repackaged</id>
|
||||||
|
<formats>
|
||||||
|
<format>jar</format>
|
||||||
|
</formats>
|
||||||
|
<includeBaseDirectory>false</includeBaseDirectory>
|
||||||
|
<dependencySets>
|
||||||
|
<dependencySet>
|
||||||
|
<includes>
|
||||||
|
<include>org.springframework.boot:spring-boot-cli:jar:*</include>
|
||||||
|
</includes>
|
||||||
|
<unpack>true</unpack>
|
||||||
|
<directoryMode>755</directoryMode>
|
||||||
|
</dependencySet>
|
||||||
|
<dependencySet>
|
||||||
|
<includes>
|
||||||
|
<include>org.springframework.boot:spring-boot-cli-grape:jar:*</include>
|
||||||
|
</includes>
|
||||||
|
<outputDirectory>internal</outputDirectory>
|
||||||
|
<directoryMode>755</directoryMode>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<unpack>true</unpack>
|
||||||
|
</dependencySet>
|
||||||
|
</dependencySets>
|
||||||
|
</assembly>
|
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012-2013 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.command.tester;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract base class for tester implementations.
|
|
||||||
*
|
|
||||||
* @author Greg Turnquist
|
|
||||||
*/
|
|
||||||
public abstract class AbstractTester {
|
|
||||||
|
|
||||||
public TestResults findAndTest(List<Class<?>> compiled) throws FileNotFoundException {
|
|
||||||
Set<Class<?>> testable = findTestableClasses(compiled);
|
|
||||||
if (testable.size() == 0) {
|
|
||||||
return TestResults.NONE;
|
|
||||||
}
|
|
||||||
return test(testable.toArray(new Class<?>[] {}));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract Set<Class<?>> findTestableClasses(List<Class<?>> compiled);
|
|
||||||
|
|
||||||
protected abstract TestResults test(Class<?>[] testable);
|
|
||||||
|
|
||||||
}
|
|
@ -1,356 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012-2013 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.GrapeEngine;
|
|
||||||
import groovy.grape.GrapeIvy;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileFilter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
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 java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.apache.ivy.Ivy;
|
|
||||||
import org.apache.ivy.core.cache.ArtifactOrigin;
|
|
||||||
import org.apache.ivy.core.event.IvyEvent;
|
|
||||||
import org.apache.ivy.core.event.IvyListener;
|
|
||||||
import org.apache.ivy.core.event.resolve.EndResolveEvent;
|
|
||||||
import org.apache.ivy.core.module.descriptor.Artifact;
|
|
||||||
import org.apache.ivy.core.module.id.ModuleId;
|
|
||||||
import org.apache.ivy.core.settings.IvySettings;
|
|
||||||
import org.apache.ivy.plugins.parser.m2.PomReader;
|
|
||||||
import org.apache.ivy.plugins.repository.Resource;
|
|
||||||
import org.apache.ivy.plugins.repository.url.URLRepository;
|
|
||||||
import org.apache.ivy.plugins.repository.url.URLResource;
|
|
||||||
import org.apache.ivy.plugins.resolver.ChainResolver;
|
|
||||||
import org.apache.ivy.plugins.resolver.DependencyResolver;
|
|
||||||
import org.apache.ivy.plugins.resolver.IBiblioResolver;
|
|
||||||
import org.apache.ivy.util.AbstractMessageLogger;
|
|
||||||
import org.apache.ivy.util.MessageLogger;
|
|
||||||
import org.springframework.boot.cli.Log;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Customizes the groovy grape engine to enhance and patch the behavior of ivy. Can add
|
|
||||||
* Spring repos to the search path, provide simple log progress feedback if downloads are
|
|
||||||
* taking a long time, and also fixes a problem where ivy cannot use a local Maven cache
|
|
||||||
* repo.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Dave Syer
|
|
||||||
*/
|
|
||||||
class GrapeEngineCustomizer {
|
|
||||||
|
|
||||||
private GrapeIvy engine;
|
|
||||||
|
|
||||||
public GrapeEngineCustomizer(GrapeEngine engine) {
|
|
||||||
this.engine = (GrapeIvy) engine;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void customize() {
|
|
||||||
Ivy ivy = this.engine.getIvyInstance();
|
|
||||||
IvySettings settings = this.engine.getSettings();
|
|
||||||
addDownloadingLogSupport(ivy);
|
|
||||||
setupResolver(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addDownloadingLogSupport(Ivy ivy) {
|
|
||||||
final DownloadingLog downloadingLog = new DownloadingLog();
|
|
||||||
ivy.getLoggerEngine().pushLogger(downloadingLog);
|
|
||||||
ivy.getEventManager().addIvyListener(new IvyListener() {
|
|
||||||
@Override
|
|
||||||
public void progress(IvyEvent event) {
|
|
||||||
if (event instanceof EndResolveEvent) {
|
|
||||||
downloadingLog.finished();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private void setupResolver(IvySettings settings) {
|
|
||||||
ChainResolver grapesResolver = (ChainResolver) settings
|
|
||||||
.getResolver("downloadGrapes");
|
|
||||||
List<DependencyResolver> grapesResolvers = grapesResolver.getResolvers();
|
|
||||||
|
|
||||||
// Replace localm2 resolver to fix missing artifact errors
|
|
||||||
for (int i = 0; i < grapesResolvers.size(); i++) {
|
|
||||||
DependencyResolver resolver = grapesResolvers.get(i);
|
|
||||||
if ("localm2".equals(resolver.getName())) {
|
|
||||||
((IBiblioResolver) resolver).setRepository(new LocalM2Repository());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new top level resolver, encapsulating the default resolvers
|
|
||||||
SpringBootResolver springBootResolver = new SpringBootResolver(grapesResolvers);
|
|
||||||
springBootResolver.setSettings(settings);
|
|
||||||
springBootResolver.setReturnFirst(grapesResolver.isReturnFirst());
|
|
||||||
springBootResolver.setName("springBoot");
|
|
||||||
|
|
||||||
// Add support for spring snapshots and milestones if required
|
|
||||||
if (!Boolean.getBoolean("disableSpringSnapshotRepos")) {
|
|
||||||
springBootResolver.addSpringSnapshotResolver(newResolver("spring-snapshot",
|
|
||||||
"http://repo.spring.io/snapshot"));
|
|
||||||
springBootResolver.addSpringSnapshotResolver(newResolver("spring-milestone",
|
|
||||||
"http://repo.spring.io/milestone"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace the original resolvers
|
|
||||||
grapesResolvers.clear();
|
|
||||||
grapesResolvers.add(springBootResolver);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IBiblioResolver newResolver(String name, String root) {
|
|
||||||
IBiblioResolver resolver = new IBiblioResolver();
|
|
||||||
resolver.setName(name);
|
|
||||||
resolver.setRoot(root);
|
|
||||||
resolver.setM2compatible(true);
|
|
||||||
resolver.setSettings(this.engine.getSettings());
|
|
||||||
return resolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link DependencyResolver} that is optimized for Spring Boot.
|
|
||||||
*/
|
|
||||||
private static class SpringBootResolver extends ChainResolver {
|
|
||||||
|
|
||||||
private static final Object SPRING_BOOT_GROUP_ID = "org.springframework.boot";
|
|
||||||
|
|
||||||
private static final String STARTER_PREFIX = "spring-boot-starter";
|
|
||||||
|
|
||||||
private static final Object SOURCE_TYPE = "source";
|
|
||||||
|
|
||||||
private static final Object JAVADOC_TYPE = "javadoc";
|
|
||||||
|
|
||||||
private static final Set<String> POM_ONLY_DEPENDENCIES;
|
|
||||||
static {
|
|
||||||
Set<String> dependencies = new HashSet<String>();
|
|
||||||
dependencies.add("spring-boot-dependencies");
|
|
||||||
dependencies.add("spring-boot-parent");
|
|
||||||
dependencies.add("spring-boot-starters");
|
|
||||||
POM_ONLY_DEPENDENCIES = Collections.unmodifiableSet(dependencies);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<DependencyResolver> springSnapshotResolvers = new ArrayList<DependencyResolver>();
|
|
||||||
|
|
||||||
public SpringBootResolver(List<DependencyResolver> resolvers) {
|
|
||||||
for (DependencyResolver resolver : resolvers) {
|
|
||||||
add(resolver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addSpringSnapshotResolver(DependencyResolver resolver) {
|
|
||||||
add(resolver);
|
|
||||||
this.springSnapshotResolvers.add(resolver);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ArtifactOrigin locate(Artifact artifact) {
|
|
||||||
if (isUnresolvable(artifact)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (isSpringSnapshot(artifact)) {
|
|
||||||
for (DependencyResolver resolver : this.springSnapshotResolvers) {
|
|
||||||
ArtifactOrigin origin = resolver.locate(artifact);
|
|
||||||
if (origin != null) {
|
|
||||||
return origin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.locate(artifact);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isUnresolvable(Artifact artifact) {
|
|
||||||
try {
|
|
||||||
ModuleId moduleId = artifact.getId().getArtifactId().getModuleId();
|
|
||||||
if (SPRING_BOOT_GROUP_ID.equals(moduleId.getOrganisation())) {
|
|
||||||
// Skip any POM only deps if they are not pom ext
|
|
||||||
if (POM_ONLY_DEPENDENCIES.contains(moduleId.getName())
|
|
||||||
&& !("pom".equalsIgnoreCase(artifact.getId().getExt()))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip starter javadoc and source
|
|
||||||
if (moduleId.getName().startsWith(STARTER_PREFIX)
|
|
||||||
&& (SOURCE_TYPE.equals(artifact.getType()) || JAVADOC_TYPE
|
|
||||||
.equals(artifact.getType()))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSpringSnapshot(Artifact artifact) {
|
|
||||||
try {
|
|
||||||
ModuleId moduleId = artifact.getId().getArtifactId().getModuleId();
|
|
||||||
String revision = artifact.getModuleRevisionId().getRevision();
|
|
||||||
return (SPRING_BOOT_GROUP_ID.equals(moduleId.getOrganisation()) && (revision
|
|
||||||
.endsWith("SNAPSHOT") || revision.contains("M")));
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Variant of {@link URLRepository} used to fix the 'localm2' so that when the local
|
|
||||||
* repo contains a POM but not an artifact we continue to maven central.
|
|
||||||
* @see "http://issues.gradle.org/browse/GRADLE-2034"
|
|
||||||
*/
|
|
||||||
private static class LocalM2Repository extends URLRepository {
|
|
||||||
|
|
||||||
private Map<String, Resource> resourcesCache = new HashMap<String, Resource>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Resource getResource(String source) throws IOException {
|
|
||||||
Resource resource = this.resourcesCache.get(source);
|
|
||||||
if (resource == null) {
|
|
||||||
URL url = new URL(source);
|
|
||||||
resource = new LocalM2Resource(url);
|
|
||||||
this.resourcesCache.put(source, resource);
|
|
||||||
}
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class LocalM2Resource extends URLResource {
|
|
||||||
|
|
||||||
private Boolean artifactExists;
|
|
||||||
|
|
||||||
public LocalM2Resource(URL url) {
|
|
||||||
super(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean exists() {
|
|
||||||
if (getURL().getPath().endsWith(".pom")) {
|
|
||||||
return super.exists() && artifactExists();
|
|
||||||
}
|
|
||||||
return super.exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean artifactExists() {
|
|
||||||
if (this.artifactExists == null) {
|
|
||||||
try {
|
|
||||||
final String packaging = getPackaging();
|
|
||||||
if ("pom".equals(packaging)) {
|
|
||||||
this.artifactExists = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
File parent = new File(getURL().toURI()).getParentFile();
|
|
||||||
File[] artifactFiles = parent.listFiles(new FileFilter() {
|
|
||||||
@Override
|
|
||||||
public boolean accept(File file) {
|
|
||||||
return file.getName().endsWith("." + packaging);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.artifactExists = artifactFiles.length > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
throw new IllegalStateException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.artifactExists;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPackaging() throws IOException, SAXException {
|
|
||||||
PomReader reader = new PomReader(getURL(), this);
|
|
||||||
String packaging = reader.getPackaging();
|
|
||||||
if ("bundle".equals(packaging)) {
|
|
||||||
packaging = "jar";
|
|
||||||
}
|
|
||||||
return packaging;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link MessageLogger} to provide simple progress information.
|
|
||||||
*/
|
|
||||||
private static class DownloadingLog extends AbstractMessageLogger {
|
|
||||||
|
|
||||||
private static final long INITIAL_DELAY = TimeUnit.SECONDS.toMillis(3);
|
|
||||||
|
|
||||||
private static final long PROGRESS_DELAY = TimeUnit.SECONDS.toMillis(1);
|
|
||||||
|
|
||||||
private long startTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
private long lastProgressTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
private boolean started;
|
|
||||||
|
|
||||||
private boolean finished;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void log(String msg, int level) {
|
|
||||||
logDownloadingMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void rawlog(String msg, int level) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doProgress() {
|
|
||||||
logDownloadingMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doEndProgress(String msg) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logDownloadingMessage() {
|
|
||||||
if (!this.finished
|
|
||||||
&& System.currentTimeMillis() - this.startTime > INITIAL_DELAY) {
|
|
||||||
if (!this.started) {
|
|
||||||
this.started = true;
|
|
||||||
Log.infoPrint("Downloading dependencies..");
|
|
||||||
this.lastProgressTime = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
else if (System.currentTimeMillis() - this.lastProgressTime > PROGRESS_DELAY) {
|
|
||||||
Log.infoPrint(".");
|
|
||||||
this.lastProgressTime = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void finished() {
|
|
||||||
if (!this.finished) {
|
|
||||||
this.finished = true;
|
|
||||||
if (this.started) {
|
|
||||||
Log.info("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 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 groovy.grape.GrapeEngine;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class GrapeEngineInstaller {
|
||||||
|
|
||||||
|
private final GrapeEngine grapeEngine;
|
||||||
|
|
||||||
|
public GrapeEngineInstaller(GrapeEngine grapeEngine) {
|
||||||
|
this.grapeEngine = grapeEngine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void install() {
|
||||||
|
synchronized (Grape.class) {
|
||||||
|
try {
|
||||||
|
Field instanceField = Grape.class.getDeclaredField("instance");
|
||||||
|
instanceField.setAccessible(true);
|
||||||
|
|
||||||
|
GrapeEngine existingGrapeEngine = (GrapeEngine) instanceField.get(null);
|
||||||
|
|
||||||
|
if (existingGrapeEngine == null) {
|
||||||
|
instanceField.set(null, this.grapeEngine);
|
||||||
|
}
|
||||||
|
else if (!existingGrapeEngine.getClass().equals(
|
||||||
|
this.grapeEngine.getClass())) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Another GrapeEngine of a different type has already been initialized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new IllegalStateException("Failed to install GrapeEngine", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 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.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File utility methods
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class FileUtils {
|
||||||
|
|
||||||
|
private FileUtils() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively deletes the given {@code file} and all files beneath it.
|
||||||
|
* @param file The root of the structure to delete
|
||||||
|
* @throw IllegalStateException if the delete fails
|
||||||
|
*/
|
||||||
|
public static void recursiveDelete(File file) {
|
||||||
|
if (file.exists()) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
for (File inDir : file.listFiles()) {
|
||||||
|
recursiveDelete(inDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!file.delete()) {
|
||||||
|
throw new IllegalStateException("Failed to delete " + file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the given {@code file} and all the files beneath it.
|
||||||
|
* @param file The root of the structure to delete
|
||||||
|
* @return The list of files and directories
|
||||||
|
*/
|
||||||
|
public static List<File> recursiveList(File file) {
|
||||||
|
List<File> files = new ArrayList<File>();
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
for (File inDir : file.listFiles()) {
|
||||||
|
files.addAll(recursiveList(inDir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files.add(file);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the data read from the given {@code source} {@link URL} to the given
|
||||||
|
* {@code target} {@link File}.
|
||||||
|
* @param source The source to copy from
|
||||||
|
* @param target The target to copy to
|
||||||
|
*/
|
||||||
|
public static void copy(URL source, File target) {
|
||||||
|
InputStream input = null;
|
||||||
|
OutputStream output = null;
|
||||||
|
try {
|
||||||
|
input = source.openStream();
|
||||||
|
output = new FileOutputStream(target);
|
||||||
|
IoUtils.copy(input, output);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new IllegalStateException("Failed to copy '" + source + "' to '"
|
||||||
|
+ target + "'", ex);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
IoUtils.closeQuietly(input, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 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.util;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General IO utility methods
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class IoUtils {
|
||||||
|
|
||||||
|
private IoUtils() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the entire contents of the resource referenced by {@code uri} and returns
|
||||||
|
* them as a String.
|
||||||
|
* @param uri The resource to read
|
||||||
|
* @return The contents of the resource
|
||||||
|
*/
|
||||||
|
public static String readEntirely(String uri) {
|
||||||
|
try {
|
||||||
|
InputStream stream = URI.create(uri).toURL().openStream();
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
|
||||||
|
String line;
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
result.append(line);
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the data read from {@code input} into {@code output}.
|
||||||
|
* <p>
|
||||||
|
* <strong>Note:</strong> it is the caller's responsibility to close the streams
|
||||||
|
* @param input The stream to read data from
|
||||||
|
* @param output The stream to write data to
|
||||||
|
* @throws IOException if the copy fails
|
||||||
|
*/
|
||||||
|
public static void copy(InputStream input, OutputStream output) throws IOException {
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int read;
|
||||||
|
while ((read = input.read(buffer)) >= 0) {
|
||||||
|
output.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quietly closes the given {@link Closeables Closeable}. Any exceptions thrown by
|
||||||
|
* {@link Closeable#close() close()} are swallowed. Any {@code null}
|
||||||
|
* {@code Closeables} are ignored.
|
||||||
|
* @param closeables The {@link Closeable closeables} to close
|
||||||
|
*/
|
||||||
|
public static void closeQuietly(Closeable... closeables) {
|
||||||
|
for (Closeable closeable : closeables) {
|
||||||
|
if (closeable != null) {
|
||||||
|
try {
|
||||||
|
closeable.close();
|
||||||
|
}
|
||||||
|
catch (IOException ioe) {
|
||||||
|
// Closing quietly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#Generated by Git-Commit-Id-Plugin
|
||||||
|
#Tue Oct 22 10:25:04 BST 2013
|
||||||
|
git.commit.id.abbrev=040321b
|
||||||
|
git.commit.user.email=awilkinson@gopivotal.com
|
||||||
|
git.commit.message.full=Isolate Aether in a separate class loader\n\nPrior to this commit, the Aether-based GrapeEngine was loaded in the\nsame class loader as the rest of Boot. This led to Aether's and its\ndependencies' types polluting the application's class path. Most\nnotably, this caused problems with logging as the logging framework\ncould be permaturely initialized.\n\nThis commit isolates AetherGrapeEngine, Aether and its dependencies\ninto a separate class loader. This is done by customizing the\npackaging of the CLI's jar file with the internal directory housing\nall of the types that will be loaded by the separate class loader.\n
|
||||||
|
git.commit.id=040321bf153db007290786623d45903cee27fa88
|
||||||
|
git.commit.message.short=Isolate Aether in a separate class loader
|
||||||
|
git.commit.user.name=Andy Wilkinson
|
||||||
|
git.build.user.name=Andy Wilkinson
|
||||||
|
git.build.user.email=awilkinson@gopivotal.com
|
||||||
|
git.branch=aether-grab
|
||||||
|
git.commit.time=2013-10-21T16\:24\:40+0100
|
||||||
|
git.build.time=2013-10-22T10\:25\:04+0100
|
@ -1,161 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012-2013 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.GrapeIvy;
|
|
||||||
import groovy.grape.IvyGrabRecord;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.ivy.core.module.id.ModuleId;
|
|
||||||
import org.apache.ivy.core.module.id.ModuleRevisionId;
|
|
||||||
import org.apache.ivy.core.report.ResolveReport;
|
|
||||||
import org.apache.ivy.plugins.resolver.ChainResolver;
|
|
||||||
import org.apache.ivy.plugins.resolver.IBiblioResolver;
|
|
||||||
import org.apache.ivy.util.FileUtil;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.rules.ExpectedException;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Dave Syer
|
|
||||||
*/
|
|
||||||
public class GrapeEngineCustomizerTests {
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public ExpectedException expected = ExpectedException.none();
|
|
||||||
private String level;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() {
|
|
||||||
this.level = System.getProperty("ivy.message.logger.level");
|
|
||||||
System.setProperty("ivy.message.logger.level", "3");
|
|
||||||
System.setProperty("disableSpringSnapshotRepos", "true");
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void shutdown() {
|
|
||||||
if (this.level == null) {
|
|
||||||
System.clearProperty("ivy.message.logger.level");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
System.setProperty("ivy.message.logger.level", this.level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void vanillaEngineWithPomExistsAndJarDoesToo() throws Exception {
|
|
||||||
GrapeIvy engine = new GrapeIvy();
|
|
||||||
prepareFoo(engine, true);
|
|
||||||
ResolveReport report = resolveFoo(engine, "foo", "foo", "1.0");
|
|
||||||
assertFalse(report.hasError());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void vanillaEngineWithPomExistsButJarDoesNot() throws Exception {
|
|
||||||
GrapeIvy engine = new GrapeIvy();
|
|
||||||
prepareFoo(engine, false);
|
|
||||||
this.expected.expectMessage("Error grabbing Grapes");
|
|
||||||
ResolveReport report = resolveFoo(engine, "foo", "foo", "1.0");
|
|
||||||
assertTrue(report.hasError());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Test
|
|
||||||
public void customizedEngineWithPomExistsButJarCanBeResolved() throws Exception {
|
|
||||||
|
|
||||||
GrapeIvy engine = new GrapeIvy();
|
|
||||||
GrapeEngineCustomizer customizer = new GrapeEngineCustomizer(engine);
|
|
||||||
ChainResolver grapesResolver = (ChainResolver) engine.getSettings().getResolver(
|
|
||||||
"downloadGrapes");
|
|
||||||
|
|
||||||
// Add a resolver that will actually resolve the artifact
|
|
||||||
IBiblioResolver resolver = new IBiblioResolver();
|
|
||||||
resolver.setName("target");
|
|
||||||
resolver.setRoot("file:" + System.getProperty("user.dir") + "/target/repository");
|
|
||||||
resolver.setM2compatible(true);
|
|
||||||
resolver.setSettings(engine.getSettings());
|
|
||||||
grapesResolver.getResolvers().add(resolver);
|
|
||||||
|
|
||||||
// Allow resolvers to be customized
|
|
||||||
customizer.customize();
|
|
||||||
prepareFoo(engine, false);
|
|
||||||
prepareFoo(engine, "target/repository/foo/foo/1.0", true);
|
|
||||||
ResolveReport report = resolveFoo(engine, "foo", "foo", "1.0");
|
|
||||||
assertFalse(report.hasError());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void customizedEngineWithPomExistsButJarCannotBeResolved() throws Exception {
|
|
||||||
|
|
||||||
GrapeIvy engine = new GrapeIvy();
|
|
||||||
GrapeEngineCustomizer customizer = new GrapeEngineCustomizer(engine);
|
|
||||||
|
|
||||||
// Allow resolvers to be customized
|
|
||||||
customizer.customize();
|
|
||||||
prepareFoo(engine, false);
|
|
||||||
this.expected.expectMessage("Error grabbing Grapes");
|
|
||||||
ResolveReport report = resolveFoo(engine, "foo", "foo", "1.0");
|
|
||||||
assertFalse(report.hasError());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResolveReport resolveFoo(GrapeIvy engine, String group, String artifact,
|
|
||||||
String version) {
|
|
||||||
Map<String, Object> args = new HashMap<String, Object>();
|
|
||||||
args.put("autoDownload", true);
|
|
||||||
IvyGrabRecord record = new IvyGrabRecord();
|
|
||||||
record.setConf(Arrays.asList("default"));
|
|
||||||
record.setForce(true);
|
|
||||||
record.setTransitive(true);
|
|
||||||
record.setExt("");
|
|
||||||
record.setType("");
|
|
||||||
record.setMrid(new ModuleRevisionId(new ModuleId(group, artifact), version));
|
|
||||||
ResolveReport report = engine.getDependencies(args, record);
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepareFoo(GrapeIvy engine, boolean includeJar) throws IOException {
|
|
||||||
prepareFoo(engine, System.getProperty("user.home")
|
|
||||||
+ "/.m2/repository/foo/foo/1.0", includeJar);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepareFoo(GrapeIvy engine, String root, boolean includeJar)
|
|
||||||
throws IOException {
|
|
||||||
File maven = new File(root);
|
|
||||||
FileUtil.forceDelete(maven);
|
|
||||||
FileUtil.copy(new File("src/test/resources/foo.pom"), new File(maven,
|
|
||||||
"foo-1.0.pom"), null);
|
|
||||||
if (includeJar) {
|
|
||||||
FileUtil.copy(new File("src/test/resources/foo.jar"), new File(maven,
|
|
||||||
"foo-1.0.jar"), null);
|
|
||||||
}
|
|
||||||
File ivy = new File(engine.getGrapeCacheDir() + "/foo");
|
|
||||||
FileUtil.forceDelete(ivy);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue