diff --git a/spring-boot-cli/pom.xml b/spring-boot-cli/pom.xml
index 3e2dd26170ee..2ae02a51bc75 100644
--- a/spring-boot-cli/pom.xml
+++ b/spring-boot-cli/pom.xml
@@ -38,6 +38,10 @@
org.springframework.boot
spring-boot-loader-tools
+
+ org.springframework.boot
+ spring-boot-aether
+
jline
jline
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/RepositoryConfigurationFactory.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/RepositoryConfigurationFactory.java
index 3b60f5d3d942..1b361270f4d7 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/RepositoryConfigurationFactory.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/RepositoryConfigurationFactory.java
@@ -16,22 +16,10 @@
package org.springframework.boot.cli.compiler;
-import java.io.File;
-import java.net.URI;
import java.util.ArrayList;
import java.util.List;
-import org.apache.maven.settings.Profile;
-import org.apache.maven.settings.Repository;
-import org.codehaus.plexus.interpolation.InterpolationException;
-import org.codehaus.plexus.interpolation.Interpolator;
-import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
-import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
-
import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration;
-import org.springframework.boot.cli.compiler.maven.MavenSettings;
-import org.springframework.boot.cli.compiler.maven.MavenSettingsReader;
-import org.springframework.util.StringUtils;
/**
* Factory used to create {@link RepositoryConfiguration}s.
@@ -41,15 +29,6 @@
*/
public final class RepositoryConfigurationFactory {
- private static final RepositoryConfiguration MAVEN_CENTRAL = new RepositoryConfiguration(
- "central", URI.create("http://repo1.maven.org/maven2/"), false);
-
- private static final RepositoryConfiguration SPRING_MILESTONE = new RepositoryConfiguration(
- "spring-milestone", URI.create("http://repo.spring.io/milestone"), false);
-
- private static final RepositoryConfiguration SPRING_SNAPSHOT = new RepositoryConfiguration(
- "spring-snapshot", URI.create("http://repo.spring.io/snapshot"), true);
-
private RepositoryConfigurationFactory() {
}
@@ -58,74 +37,19 @@ private RepositoryConfigurationFactory() {
* @return the newly-created default repository configuration
*/
public static List createDefaultRepositoryConfiguration() {
- MavenSettings mavenSettings = new MavenSettingsReader().readSettings();
- List repositoryConfiguration = new ArrayList();
- repositoryConfiguration.add(MAVEN_CENTRAL);
- if (!Boolean.getBoolean("disableSpringSnapshotRepos")) {
- repositoryConfiguration.add(SPRING_MILESTONE);
- repositoryConfiguration.add(SPRING_SNAPSHOT);
- }
- addDefaultCacheAsRepository(mavenSettings.getLocalRepository(),
- repositoryConfiguration);
- addActiveProfileRepositories(mavenSettings.getActiveProfiles(),
- repositoryConfiguration);
- return repositoryConfiguration;
- }
-
- private static void addDefaultCacheAsRepository(String localRepository,
- List repositoryConfiguration) {
- RepositoryConfiguration repository = new RepositoryConfiguration("local",
- getLocalRepositoryDirectory(localRepository).toURI(), true);
- if (!repositoryConfiguration.contains(repository)) {
- repositoryConfiguration.add(0, repository);
- }
- }
-
- private static void addActiveProfileRepositories(List activeProfiles,
- List configurations) {
- for (Profile activeProfile : activeProfiles) {
- Interpolator interpolator = new RegexBasedInterpolator();
- interpolator.addValueSource(
- new PropertiesBasedValueSource(activeProfile.getProperties()));
- for (Repository repository : activeProfile.getRepositories()) {
- configurations.add(getRepositoryConfiguration(interpolator, repository));
- }
- }
- }
-
- private static RepositoryConfiguration getRepositoryConfiguration(
- Interpolator interpolator, Repository repository) {
- String name = interpolate(interpolator, repository.getId());
- String url = interpolate(interpolator, repository.getUrl());
- boolean snapshotsEnabled = false;
- if (repository.getSnapshots() != null) {
- snapshotsEnabled = repository.getSnapshots().isEnabled();
- }
- return new RepositoryConfiguration(name, URI.create(url), snapshotsEnabled);
- }
-
- private static String interpolate(Interpolator interpolator, String value) {
- try {
- return interpolator.interpolate(value);
- }
- catch (InterpolationException ex) {
- return value;
- }
- }
-
- private static File getLocalRepositoryDirectory(String localRepository) {
- if (StringUtils.hasText(localRepository)) {
- return new File(localRepository);
- }
- return new File(getM2HomeDirectory(), "repository");
+ return convert(org.springframework.boot.aether.RepositoryConfigurationFactory
+ .createDefaultRepositoryConfiguration());
}
- private static File getM2HomeDirectory() {
- String mavenRoot = System.getProperty("maven.home");
- if (StringUtils.hasLength(mavenRoot)) {
- return new File(mavenRoot);
+ private static List convert(
+ List repositoryConfigurations) {
+ List list = new ArrayList();
+ for (org.springframework.boot.aether.RepositoryConfiguration repositoryConfiguration : repositoryConfigurations) {
+ list.add(new RepositoryConfiguration(repositoryConfiguration.getName(),
+ repositoryConfiguration.getUri(),
+ repositoryConfiguration.getSnapshotsEnabled()));
}
- return new File(System.getProperty("user.home"), ".m2");
+ return list;
}
}
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java
index 82e8e78fd14d..4eb412788deb 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngine.java
@@ -27,20 +27,16 @@
import groovy.grape.GrapeEngine;
import groovy.lang.GroovyClassLoader;
-import org.eclipse.aether.DefaultRepositorySystemSession;
-import org.eclipse.aether.RepositorySystem;
+
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
-import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.Exclusion;
import org.eclipse.aether.repository.RemoteRepository;
-import org.eclipse.aether.resolution.ArtifactResolutionException;
-import org.eclipse.aether.resolution.ArtifactResult;
-import org.eclipse.aether.resolution.DependencyRequest;
-import org.eclipse.aether.resolution.DependencyResult;
import org.eclipse.aether.util.artifact.JavaScopes;
-import org.eclipse.aether.util.filter.DependencyFilterUtils;
+
+import org.springframework.boot.aether.AetherEngine;
+import org.springframework.boot.aether.DependencyResolutionFailedException;
/**
* A {@link GrapeEngine} implementation that uses
@@ -63,40 +59,15 @@ public class AetherGrapeEngine implements GrapeEngine {
private final DependencyResolutionContext resolutionContext;
- private final ProgressReporter progressReporter;
+ private final AetherEngine engine;
private final GroovyClassLoader classLoader;
- private final DefaultRepositorySystemSession session;
-
- private final RepositorySystem repositorySystem;
-
- private final List repositories;
-
- public AetherGrapeEngine(GroovyClassLoader classLoader,
- RepositorySystem repositorySystem,
- DefaultRepositorySystemSession repositorySystemSession,
- List remoteRepositories,
+ public AetherGrapeEngine(GroovyClassLoader classLoader, AetherEngine engine,
DependencyResolutionContext resolutionContext) {
this.classLoader = classLoader;
- this.repositorySystem = repositorySystem;
- this.session = repositorySystemSession;
+ this.engine = engine;
this.resolutionContext = resolutionContext;
- this.repositories = new ArrayList();
- List remotes = new ArrayList(
- remoteRepositories);
- Collections.reverse(remotes); // priority is reversed in addRepository
- for (RemoteRepository repository : remotes) {
- addRepository(repository);
- }
- this.progressReporter = getProgressReporter(this.session);
- }
-
- private ProgressReporter getProgressReporter(DefaultRepositorySystemSession session) {
- if (Boolean.getBoolean("groovy.grape.report.downloads")) {
- return new DetailedProgressReporter(session, System.out);
- }
- return new SummaryProgressReporter(session, System.out);
}
@Override
@@ -115,9 +86,6 @@ public Object grab(Map args, Map... dependencyMaps) {
classLoader.addURL(file.toURI().toURL());
}
}
- catch (ArtifactResolutionException ex) {
- throw new DependencyResolutionFailedException(ex);
- }
catch (MalformedURLException ex) {
throw new DependencyResolutionFailedException(ex);
}
@@ -196,23 +164,6 @@ private boolean isTransitive(Map, ?> dependencyMap) {
return (transitive == null ? true : transitive);
}
- private List getDependencies(DependencyResult dependencyResult) {
- List dependencies = new ArrayList();
- for (ArtifactResult artifactResult : dependencyResult.getArtifactResults()) {
- dependencies.add(
- new Dependency(artifactResult.getArtifact(), JavaScopes.COMPILE));
- }
- return dependencies;
- }
-
- private List getFiles(DependencyResult dependencyResult) {
- List files = new ArrayList();
- for (ArtifactResult result : dependencyResult.getArtifactResults()) {
- files.add(result.getArtifact().getFile());
- }
- return files;
- }
-
private GroovyClassLoader getClassLoader(Map args) {
GroovyClassLoader classLoader = (GroovyClassLoader) args.get("classLoader");
return (classLoader == null ? this.classLoader : classLoader);
@@ -229,41 +180,7 @@ public void addResolver(Map args) {
}
protected void addRepository(RemoteRepository repository) {
- if (this.repositories.contains(repository)) {
- return;
- }
- repository = getPossibleMirror(repository);
- repository = applyProxy(repository);
- repository = applyAuthentication(repository);
- this.repositories.add(0, repository);
- }
-
- private RemoteRepository getPossibleMirror(RemoteRepository remoteRepository) {
- RemoteRepository mirror = this.session.getMirrorSelector()
- .getMirror(remoteRepository);
- if (mirror != null) {
- return mirror;
- }
- return remoteRepository;
- }
-
- private RemoteRepository applyProxy(RemoteRepository repository) {
- if (repository.getProxy() == null) {
- RemoteRepository.Builder builder = new RemoteRepository.Builder(repository);
- builder.setProxy(this.session.getProxySelector().getProxy(repository));
- repository = builder.build();
- }
- return repository;
- }
-
- private RemoteRepository applyAuthentication(RemoteRepository repository) {
- if (repository.getAuthentication() == null) {
- RemoteRepository.Builder builder = new RemoteRepository.Builder(repository);
- builder.setAuthentication(this.session.getAuthenticationSelector()
- .getAuthentication(repository));
- repository = builder.build();
- }
- return repository;
+ this.engine.addRepository(repository);
}
@Override
@@ -280,54 +197,17 @@ public URI[] resolve(Map args, Map... dependencyMaps) {
public URI[] resolve(Map args, List depsInfo, Map... dependencyMaps) {
List exclusions = createExclusions(args);
List dependencies = createDependencies(dependencyMaps, exclusions);
- try {
- List files = resolve(dependencies);
- List uris = new ArrayList(files.size());
- for (File file : files) {
- uris.add(file.toURI());
- }
- return uris.toArray(new URI[uris.size()]);
- }
- catch (Exception ex) {
- throw new DependencyResolutionFailedException(ex);
+ List files = resolve(dependencies);
+ List uris = new ArrayList(files.size());
+ for (File file : files) {
+ uris.add(file.toURI());
}
+ return uris.toArray(new URI[uris.size()]);
}
private List resolve(List dependencies)
- throws ArtifactResolutionException {
- try {
- CollectRequest collectRequest = getCollectRequest(dependencies);
- DependencyRequest dependencyRequest = getDependencyRequest(collectRequest);
- DependencyResult result = this.repositorySystem
- .resolveDependencies(this.session, dependencyRequest);
- addManagedDependencies(result);
- return getFiles(result);
- }
- catch (Exception ex) {
- throw new DependencyResolutionFailedException(ex);
- }
- finally {
- this.progressReporter.finished();
- }
- }
-
- private CollectRequest getCollectRequest(List dependencies) {
- CollectRequest collectRequest = new CollectRequest((Dependency) null,
- dependencies, new ArrayList(this.repositories));
- collectRequest
- .setManagedDependencies(this.resolutionContext.getManagedDependencies());
- return collectRequest;
- }
-
- private DependencyRequest getDependencyRequest(CollectRequest collectRequest) {
- DependencyRequest dependencyRequest = new DependencyRequest(collectRequest,
- DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE,
- JavaScopes.RUNTIME));
- return dependencyRequest;
- }
-
- private void addManagedDependencies(DependencyResult result) {
- this.resolutionContext.addManagedDependencies(getDependencies(result));
+ throws DependencyResolutionFailedException {
+ return this.engine.resolve(dependencies);
}
@Override
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngineFactory.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngineFactory.java
index b691ca594605..2735cbd42f06 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngineFactory.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngineFactory.java
@@ -18,22 +18,10 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.ServiceLoader;
import groovy.lang.GroovyClassLoader;
-import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
-import org.eclipse.aether.DefaultRepositorySystemSession;
-import org.eclipse.aether.RepositorySystem;
-import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
-import org.eclipse.aether.impl.DefaultServiceLocator;
-import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
-import org.eclipse.aether.repository.RemoteRepository;
-import org.eclipse.aether.repository.RepositoryPolicy;
-import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
-import org.eclipse.aether.spi.connector.transport.TransporterFactory;
-import org.eclipse.aether.spi.locator.ServiceLocator;
-import org.eclipse.aether.transport.file.FileTransporterFactory;
-import org.eclipse.aether.transport.http.HttpTransporterFactory;
+
+import org.springframework.boot.aether.AetherEngine;
/**
* Utility class to create a pre-configured {@link AetherGrapeEngine}.
@@ -44,55 +32,21 @@ public abstract class AetherGrapeEngineFactory {
public static AetherGrapeEngine create(GroovyClassLoader classLoader,
List repositoryConfigurations,
- DependencyResolutionContext dependencyResolutionContext) {
-
- RepositorySystem repositorySystem = createServiceLocator()
- .getService(RepositorySystem.class);
-
- DefaultRepositorySystemSession repositorySystemSession = MavenRepositorySystemUtils
- .newSession();
-
- ServiceLoader autoConfigurations = ServiceLoader
- .load(RepositorySystemSessionAutoConfiguration.class);
-
- for (RepositorySystemSessionAutoConfiguration autoConfiguration : autoConfigurations) {
- autoConfiguration.apply(repositorySystemSession, repositorySystem);
- }
-
- new DefaultRepositorySystemSessionAutoConfiguration()
- .apply(repositorySystemSession, repositorySystem);
-
- return new AetherGrapeEngine(classLoader, repositorySystem,
- repositorySystemSession, createRepositories(repositoryConfigurations),
- dependencyResolutionContext);
- }
-
- private static ServiceLocator createServiceLocator() {
- DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
- locator.addService(RepositorySystem.class, DefaultRepositorySystem.class);
- locator.addService(RepositoryConnectorFactory.class,
- BasicRepositoryConnectorFactory.class);
- locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
- locator.addService(TransporterFactory.class, FileTransporterFactory.class);
- return locator;
+ DependencyResolutionContext dependencyManagement) {
+ AetherEngine engine = AetherEngine.create(convert(repositoryConfigurations),
+ dependencyManagement);
+ return new AetherGrapeEngine(classLoader, engine, dependencyManagement);
}
- private static List createRepositories(
+ private static List convert(
List repositoryConfigurations) {
- List repositories = new ArrayList(
- repositoryConfigurations.size());
+ List list = new ArrayList();
for (RepositoryConfiguration repositoryConfiguration : repositoryConfigurations) {
- RemoteRepository.Builder builder = new RemoteRepository.Builder(
- repositoryConfiguration.getName(), "default",
- repositoryConfiguration.getUri().toASCIIString());
-
- if (!repositoryConfiguration.getSnapshotsEnabled()) {
- builder.setSnapshotPolicy(
- new RepositoryPolicy(false, RepositoryPolicy.UPDATE_POLICY_NEVER,
- RepositoryPolicy.CHECKSUM_POLICY_IGNORE));
- }
- repositories.add(builder.build());
+ list.add(new org.springframework.boot.aether.RepositoryConfiguration(
+ repositoryConfiguration.getName(), repositoryConfiguration.getUri(),
+ repositoryConfiguration.getSnapshotsEnabled()));
}
- return repositories;
+ return list;
}
+
}
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DependencyResolutionContext.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DependencyResolutionContext.java
index 4095830acdda..fac821f4dffd 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DependencyResolutionContext.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DependencyResolutionContext.java
@@ -17,16 +17,14 @@
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.aether.DependencyManagementContext;
import org.springframework.boot.cli.compiler.dependencies.ArtifactCoordinatesResolver;
import org.springframework.boot.cli.compiler.dependencies.CompositeDependencyManagement;
import org.springframework.boot.cli.compiler.dependencies.DependencyManagement;
@@ -39,11 +37,7 @@
* @author Andy Wilkinson
* @since 1.1.0
*/
-public class DependencyResolutionContext {
-
- private final Map managedDependencyByGroupAndArtifact = new HashMap();
-
- private final List managedDependencies = new ArrayList();
+public class DependencyResolutionContext extends DependencyManagementContext {
private DependencyManagement dependencyManagement = null;
@@ -53,46 +47,12 @@ public DependencyResolutionContext() {
addDependencyManagement(new SpringBootDependenciesDependencyManagement());
}
- private String getIdentifier(Dependency dependency) {
- return getIdentifier(dependency.getArtifact().getGroupId(),
- dependency.getArtifact().getArtifactId());
- }
-
- private String getIdentifier(String groupId, String artifactId) {
- return groupId + ":" + artifactId;
- }
-
public ArtifactCoordinatesResolver getArtifactCoordinatesResolver() {
return this.artifactCoordinatesResolver;
}
- public String getManagedVersion(String groupId, String artifactId) {
- Dependency dependency = getManagedDependency(groupId, artifactId);
- if (dependency == null) {
- dependency = this.managedDependencyByGroupAndArtifact
- .get(getIdentifier(groupId, artifactId));
- }
- return dependency != null ? dependency.getArtifact().getVersion() : null;
- }
-
- public List getManagedDependencies() {
- return Collections.unmodifiableList(this.managedDependencies);
- }
-
- private Dependency getManagedDependency(String group, String artifact) {
- return this.managedDependencyByGroupAndArtifact
- .get(getIdentifier(group, artifact));
- }
-
- void addManagedDependencies(List dependencies) {
- this.managedDependencies.addAll(dependencies);
- for (Dependency dependency : dependencies) {
- this.managedDependencyByGroupAndArtifact.put(getIdentifier(dependency),
- dependency);
- }
- }
-
public void addDependencyManagement(DependencyManagement dependencyManagement) {
+ List dependencies = new ArrayList();
for (org.springframework.boot.cli.compiler.dependencies.Dependency dependency : dependencyManagement
.getDependencies()) {
List aetherExclusions = new ArrayList();
@@ -105,10 +65,9 @@ public void addDependencyManagement(DependencyManagement dependencyManagement) {
new DefaultArtifact(dependency.getGroupId(),
dependency.getArtifactId(), "jar", dependency.getVersion()),
JavaScopes.COMPILE, false, aetherExclusions);
- this.managedDependencies.add(0, aetherDependency);
- this.managedDependencyByGroupAndArtifact.put(getIdentifier(aetherDependency),
- aetherDependency);
+ dependencies.add(aetherDependency);
}
+ addManagedDependencies(dependencies);
this.dependencyManagement = this.dependencyManagement == null
? dependencyManagement
: new CompositeDependencyManagement(dependencyManagement,
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionAutoConfiguration.java b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionConfiguration.java
similarity index 90%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionAutoConfiguration.java
rename to spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionConfiguration.java
index 0c6949ba958e..d596bdf0052d 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionAutoConfiguration.java
+++ b/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionConfiguration.java
@@ -23,6 +23,7 @@
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
+import org.springframework.boot.aether.RepositorySystemSessionConfiguration;
import org.springframework.util.StringUtils;
/**
@@ -32,8 +33,8 @@
* @author Andy Wilkinson
* @since 1.2.5
*/
-public class GrapeRootRepositorySystemSessionAutoConfiguration
- implements RepositorySystemSessionAutoConfiguration {
+public class GrapeRootRepositorySystemSessionConfiguration
+ implements RepositorySystemSessionConfiguration {
@Override
public void apply(DefaultRepositorySystemSession session,
diff --git a/spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.aether.RepositorySystemSessionConfiguration b/spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.aether.RepositorySystemSessionConfiguration
new file mode 100644
index 000000000000..d6bc07678145
--- /dev/null
+++ b/spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.aether.RepositorySystemSessionConfiguration
@@ -0,0 +1 @@
+org.springframework.boot.cli.compiler.grape.GrapeRootRepositorySystemSessionConfiguration
\ No newline at end of file
diff --git a/spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.cli.compiler.grape.RepositorySystemSessionAutoConfiguration b/spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.cli.compiler.grape.RepositorySystemSessionAutoConfiguration
deleted file mode 100644
index cf394a7e183e..000000000000
--- a/spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.cli.compiler.grape.RepositorySystemSessionAutoConfiguration
+++ /dev/null
@@ -1,2 +0,0 @@
-org.springframework.boot.cli.compiler.grape.SettingsXmlRepositorySystemSessionAutoConfiguration
-org.springframework.boot.cli.compiler.grape.GrapeRootRepositorySystemSessionAutoConfiguration
\ No newline at end of file
diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngineTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngineTests.java
index de40b02aa15f..77e15e7fb2b4 100644
--- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngineTests.java
+++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/AetherGrapeEngineTests.java
@@ -16,7 +16,6 @@
package org.springframework.boot.cli.compiler.grape;
-import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
@@ -27,12 +26,8 @@
import java.util.Map;
import groovy.lang.GroovyClassLoader;
-import org.eclipse.aether.DefaultRepositorySystemSession;
-import org.eclipse.aether.repository.Authentication;
-import org.eclipse.aether.repository.RemoteRepository;
-import org.junit.Test;
-import org.springframework.test.util.ReflectionTestUtils;
+import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
@@ -66,59 +61,6 @@ public void dependencyResolution() {
assertThat(this.groovyClassLoader.getURLs()).hasSize(5);
}
- @Test
- public void proxySelector() {
- doWithCustomUserHome(new Runnable() {
-
- @Override
- public void run() {
- AetherGrapeEngine grapeEngine = createGrapeEngine();
-
- DefaultRepositorySystemSession session = (DefaultRepositorySystemSession) ReflectionTestUtils
- .getField(grapeEngine, "session");
-
- assertThat(session.getProxySelector() instanceof CompositeProxySelector)
- .isTrue();
- }
-
- });
- }
-
- @Test
- public void repositoryMirrors() {
- doWithCustomUserHome(new Runnable() {
-
- @SuppressWarnings("unchecked")
- @Override
- public void run() {
- AetherGrapeEngine grapeEngine = createGrapeEngine();
-
- List repositories = (List) ReflectionTestUtils
- .getField(grapeEngine, "repositories");
- assertThat(repositories).hasSize(1);
- assertThat(repositories.get(0).getId()).isEqualTo("central-mirror");
- }
- });
- }
-
- @Test
- public void repositoryAuthentication() {
- doWithCustomUserHome(new Runnable() {
-
- @SuppressWarnings("unchecked")
- @Override
- public void run() {
- AetherGrapeEngine grapeEngine = createGrapeEngine();
-
- List repositories = (List) ReflectionTestUtils
- .getField(grapeEngine, "repositories");
- assertThat(repositories).hasSize(1);
- Authentication authentication = repositories.get(0).getAuthentication();
- assertThat(authentication).isNotNull();
- }
- });
- }
-
@Test
public void dependencyResolutionWithExclusions() {
Map args = new HashMap();
@@ -242,25 +184,4 @@ private Map createExclusion(String group, String module) {
return exclusion;
}
- private void doWithCustomUserHome(Runnable action) {
- doWithSystemProperty("user.home",
- new File("src/test/resources").getAbsolutePath(), action);
- }
-
- private void doWithSystemProperty(String key, String value, Runnable action) {
- String previousValue = setOrClearSystemProperty(key, value);
- try {
- action.run();
- }
- finally {
- setOrClearSystemProperty(key, previousValue);
- }
- }
-
- private String setOrClearSystemProperty(String key, String value) {
- if (value != null) {
- return System.setProperty(key, value);
- }
- return System.clearProperty(key);
- }
}
diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionAutoConfigurationTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionConfigurationTests.java
similarity index 89%
rename from spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionAutoConfigurationTests.java
rename to spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionConfigurationTests.java
index 6d0680cc3434..dd1befd0105a 100644
--- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionAutoConfigurationTests.java
+++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/GrapeRootRepositorySystemSessionConfigurationTests.java
@@ -39,11 +39,11 @@
import static org.mockito.Mockito.verify;
/**
- * Tests for {@link GrapeRootRepositorySystemSessionAutoConfiguration}
+ * Tests for {@link GrapeRootRepositorySystemSessionConfiguration}
*
* @author Andy Wilkinson
*/
-public class GrapeRootRepositorySystemSessionAutoConfigurationTests {
+public class GrapeRootRepositorySystemSessionConfigurationTests {
private DefaultRepositorySystemSession session = MavenRepositorySystemUtils
.newSession();
@@ -69,12 +69,12 @@ public LocalRepositoryManager answer(
.getArgumentAt(1, LocalRepository.class);
return new SimpleLocalRepositoryManagerFactory()
.newInstance(
- GrapeRootRepositorySystemSessionAutoConfigurationTests.this.session,
+ GrapeRootRepositorySystemSessionConfigurationTests.this.session,
localRepository);
}
});
- new GrapeRootRepositorySystemSessionAutoConfiguration().apply(this.session,
+ new GrapeRootRepositorySystemSessionConfiguration().apply(this.session,
this.repositorySystem);
verify(this.repositorySystem, times(0))
.newLocalRepositoryManager(eq(this.session), any(LocalRepository.class));
@@ -89,7 +89,7 @@ public void grapeRootConfiguresLocalRepositoryLocation() {
System.setProperty("grape.root", "foo");
try {
- new GrapeRootRepositorySystemSessionAutoConfiguration().apply(this.session,
+ new GrapeRootRepositorySystemSessionConfiguration().apply(this.session,
this.repositorySystem);
}
finally {
@@ -111,7 +111,7 @@ public LocalRepositoryManager answer(InvocationOnMock invocation)
LocalRepository localRepository = invocation.getArgumentAt(1,
LocalRepository.class);
return new SimpleLocalRepositoryManagerFactory().newInstance(
- GrapeRootRepositorySystemSessionAutoConfigurationTests.this.session,
+ GrapeRootRepositorySystemSessionConfigurationTests.this.session,
localRepository);
}
diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml
index 5b16dc4088c2..f31a50830bee 100644
--- a/spring-boot-dependencies/pom.xml
+++ b/spring-boot-dependencies/pom.xml
@@ -247,6 +247,11 @@
spring-boot-devtools
1.5.0.BUILD-SNAPSHOT
+
+ org.springframework.boot
+ spring-boot-aether
+ 1.5.0.BUILD-SNAPSHOT
+
org.springframework.boot
spring-boot-loader
@@ -257,6 +262,11 @@
spring-boot-loader-tools
1.5.0.BUILD-SNAPSHOT
+
+ org.springframework.boot
+ spring-boot-thin-wrapper
+ 1.5.0.BUILD-SNAPSHOT
+
org.springframework.boot
spring-boot-starter
diff --git a/spring-boot-samples/spring-boot-sample-ant/pom.xml b/spring-boot-samples/spring-boot-sample-ant/pom.xml
index c681c3e2328b..47e3e965002f 100644
--- a/spring-boot-samples/spring-boot-sample-ant/pom.xml
+++ b/spring-boot-samples/spring-boot-sample-ant/pom.xml
@@ -1,5 +1,4 @@
-
-
+
4.0.0
@@ -60,8 +59,8 @@
package
-
-
+
+
@@ -101,4 +100,4 @@
-
+
\ No newline at end of file
diff --git a/spring-boot-tools/pom.xml b/spring-boot-tools/pom.xml
index cbf35f78954a..42941a1a2357 100644
--- a/spring-boot-tools/pom.xml
+++ b/spring-boot-tools/pom.xml
@@ -22,6 +22,8 @@
spring-boot-configuration-metadata
spring-boot-configuration-processor
+ spring-boot-thin-wrapper
+ spring-boot-aether
spring-boot-loader
spring-boot-loader-tools
spring-boot-maven-plugin
diff --git a/spring-boot-tools/spring-boot-aether/pom.xml b/spring-boot-tools/spring-boot-aether/pom.xml
new file mode 100644
index 000000000000..99651fa44f96
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/pom.xml
@@ -0,0 +1,132 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-tools
+ 1.5.0.BUILD-SNAPSHOT
+
+ spring-boot-aether
+ Spring Boot Aether
+ Spring Boot Aether
+ http://projects.spring.io/spring-boot/
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+ ${basedir}/..
+
+
+
+
+ org.springframework.boot
+ spring-boot-loader
+
+
+ org.springframework
+ spring-core
+
+
+ org.apache.maven
+ maven-aether-provider
+
+
+ org.eclipse.sisu.plexus
+ org.eclipse.sisu
+
+
+
+
+ org.apache.maven
+ maven-settings-builder
+
+
+ org.codehaus.plexus
+ plexus-component-api
+
+
+ *
+ *
+
+
+
+
+ org.eclipse.aether
+ aether-api
+
+
+ org.eclipse.aether
+ aether-connector-basic
+
+
+ org.eclipse.aether
+ aether-impl
+
+
+ org.eclipse.aether
+ aether-spi
+
+
+ org.eclipse.aether
+ aether-transport-file
+
+
+ org.eclipse.aether
+ aether-transport-http
+
+
+ jcl-over-slf4j
+ org.slf4j
+
+
+
+
+ org.eclipse.aether
+ aether-util
+
+
+
+ org.springframework.boot
+ spring-boot-test
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+
+ true
+ exec
+ true
+ true
+
+
+ org.springframework.boot.loader.thin.ThinJarLauncher
+
+
+
+
+ commons-logging:commons-logging:*
+
+ org/apache/**
+
+
+
+
+
+
+ shade-runtime-dependencies
+ package
+
+ shade
+
+
+
+
+
+
+
diff --git a/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/AetherEngine.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/AetherEngine.java
new file mode 100644
index 000000000000..6b4e54529904
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/AetherEngine.java
@@ -0,0 +1,285 @@
+/*
+ * 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.aether;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.ServiceLoader;
+
+import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystem;
+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.RemoteRepository;
+import org.eclipse.aether.repository.RepositoryPolicy;
+import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
+import org.eclipse.aether.resolution.ArtifactDescriptorResult;
+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.spi.locator.ServiceLocator;
+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;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * A utility wrapper for Aether, the dependency
+ * resolution system used by Maven.
+ *
+ * @author Andy Wilkinson
+ * @author Phillip Webb
+ * @author Dave Syer
+ */
+public class AetherEngine {
+
+ private static ServiceLocator serviceLocator;
+
+ private final DependencyManagementContext dependencyManagement;
+
+ private final ProgressReporter progressReporter;
+
+ private final DefaultRepositorySystemSession session;
+
+ private final RepositorySystem repositorySystem;
+
+ private final List repositories;
+
+ public static AetherEngine create(
+ List repositoryConfigurations,
+ DependencyManagementContext dependencyManagement) {
+
+ RepositorySystem repositorySystem = getServiceLocator()
+ .getService(RepositorySystem.class);
+
+ DefaultRepositorySystemSession repositorySystemSession = MavenRepositorySystemUtils
+ .newSession();
+
+ ServiceLoader autoConfigurations = ServiceLoader
+ .load(RepositorySystemSessionConfiguration.class);
+
+ for (RepositorySystemSessionConfiguration autoConfiguration : autoConfigurations) {
+ autoConfiguration.apply(repositorySystemSession, repositorySystem);
+ }
+
+ new DefaultRepositorySystemSessionAutoConfiguration()
+ .apply(repositorySystemSession, repositorySystem);
+
+ return new AetherEngine(repositorySystem, repositorySystemSession,
+ createRepositories(repositoryConfigurations),
+ dependencyManagement);
+ }
+
+ AetherEngine(RepositorySystem repositorySystem,
+ DefaultRepositorySystemSession repositorySystemSession,
+ List remoteRepositories,
+ DependencyManagementContext dependencyManagement) {
+ this.repositorySystem = repositorySystem;
+ this.session = repositorySystemSession;
+ this.dependencyManagement = dependencyManagement;
+ this.repositories = new ArrayList();
+ List remotes = new ArrayList(
+ remoteRepositories);
+ Collections.reverse(remotes); // priority is reversed in addRepository
+ for (RemoteRepository repository : remotes) {
+ addRepository(repository);
+ }
+ this.progressReporter = getProgressReporter(this.session);
+ }
+
+ private ProgressReporter getProgressReporter(DefaultRepositorySystemSession session) {
+ if (Boolean.getBoolean("groovy.grape.report.downloads")) {
+ return new DetailedProgressReporter(session, System.out);
+ }
+ return new SummaryProgressReporter(session, System.out);
+ }
+
+ private List getDependencies(DependencyResult dependencyResult) {
+ List dependencies = new ArrayList();
+ for (ArtifactResult artifactResult : dependencyResult.getArtifactResults()) {
+ dependencies.add(
+ new Dependency(artifactResult.getArtifact(), JavaScopes.COMPILE));
+ }
+ return dependencies;
+ }
+
+ private List getFiles(DependencyResult dependencyResult) {
+ List files = new ArrayList();
+ for (ArtifactResult result : dependencyResult.getArtifactResults()) {
+ files.add(result.getArtifact().getFile());
+ }
+ return files;
+ }
+
+ public void addRepository(RemoteRepository repository) {
+ if (this.repositories.contains(repository)) {
+ return;
+ }
+ repository = getPossibleMirror(repository);
+ repository = applyProxy(repository);
+ repository = applyAuthentication(repository);
+ this.repositories.add(0, repository);
+ }
+
+ private RemoteRepository getPossibleMirror(RemoteRepository remoteRepository) {
+ RemoteRepository mirror = this.session.getMirrorSelector()
+ .getMirror(remoteRepository);
+ if (mirror != null) {
+ return mirror;
+ }
+ return remoteRepository;
+ }
+
+ private RemoteRepository applyProxy(RemoteRepository repository) {
+ if (repository.getProxy() == null) {
+ RemoteRepository.Builder builder = new RemoteRepository.Builder(repository);
+ builder.setProxy(this.session.getProxySelector().getProxy(repository));
+ repository = builder.build();
+ }
+ return repository;
+ }
+
+ private RemoteRepository applyAuthentication(RemoteRepository repository) {
+ if (repository.getAuthentication() == null) {
+ RemoteRepository.Builder builder = new RemoteRepository.Builder(repository);
+ builder.setAuthentication(this.session.getAuthenticationSelector()
+ .getAuthentication(repository));
+ repository = builder.build();
+ }
+ return repository;
+ }
+
+ public List resolve(List dependencies)
+ throws DependencyResolutionFailedException {
+ try {
+ CollectRequest collectRequest = getCollectRequest(dependencies);
+ DependencyRequest dependencyRequest = getDependencyRequest(collectRequest);
+ DependencyResult result = this.repositorySystem
+ .resolveDependencies(this.session, dependencyRequest);
+ addManagedDependencies(result);
+ return getFiles(result);
+ }
+ catch (Exception ex) {
+ throw new DependencyResolutionFailedException(ex);
+ }
+ finally {
+ this.progressReporter.finished();
+ }
+ }
+
+ public void addDependencyManagementBoms(List boms) {
+ for (Dependency bom : boms) {
+ try {
+ ArtifactDescriptorReader resolver = AetherEngine.getServiceLocator()
+ .getService(ArtifactDescriptorReader.class);
+ ArtifactDescriptorRequest request = new ArtifactDescriptorRequest(
+ bom.getArtifact(), this.repositories, null);
+ ArtifactDescriptorResult descriptor = resolver
+ .readArtifactDescriptor(this.session, request);
+ List managedDependencies = descriptor
+ .getManagedDependencies();
+ this.dependencyManagement.addManagedDependencies(managedDependencies);
+ }
+ catch (Exception ex) {
+ throw new IllegalStateException("Failed to build model for '" + bom
+ + "'. Is it a valid Maven bom?", ex);
+ }
+ }
+ }
+
+ private CollectRequest getCollectRequest(List dependencies) {
+ List resolve = new ArrayList();
+ for (Dependency dependency : dependencies) {
+ String version = dependency.getArtifact().getVersion();
+ String group = dependency.getArtifact().getGroupId();
+ String module = dependency.getArtifact().getArtifactId();
+ if (!StringUtils.hasText(version)) {
+ version = this.dependencyManagement.getManagedVersion(group, module);
+ if (version == null) {
+ throw new IllegalStateException(
+ "Cannot resolve version for " + dependency);
+ }
+ resolve.add(dependency
+ .setArtifact(dependency.getArtifact().setVersion(version)));
+ }
+ else {
+ resolve.add(dependency);
+ }
+ }
+ CollectRequest collectRequest = new CollectRequest((Dependency) null, resolve,
+ new ArrayList(this.repositories));
+ collectRequest
+ .setManagedDependencies(this.dependencyManagement.getManagedDependencies());
+ return collectRequest;
+ }
+
+ private DependencyRequest getDependencyRequest(CollectRequest collectRequest) {
+ DependencyRequest dependencyRequest = new DependencyRequest(collectRequest,
+ DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE,
+ JavaScopes.RUNTIME));
+ return dependencyRequest;
+ }
+
+ private void addManagedDependencies(DependencyResult result) {
+ this.dependencyManagement.addManagedDependencies(getDependencies(result));
+ }
+
+ public static ServiceLocator getServiceLocator() {
+ if (AetherEngine.serviceLocator == null) {
+ DefaultServiceLocator locator = MavenRepositorySystemUtils
+ .newServiceLocator();
+ locator.addService(RepositorySystem.class, DefaultRepositorySystem.class);
+ locator.addService(RepositoryConnectorFactory.class,
+ BasicRepositoryConnectorFactory.class);
+ locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
+ locator.addService(TransporterFactory.class, FileTransporterFactory.class);
+ AetherEngine.serviceLocator = locator;
+ }
+ return AetherEngine.serviceLocator;
+ }
+
+ private static List createRepositories(
+ List repositoryConfigurations) {
+ List repositories = new ArrayList(
+ repositoryConfigurations.size());
+ for (RepositoryConfiguration repositoryConfiguration : repositoryConfigurations) {
+ RemoteRepository.Builder builder = new RemoteRepository.Builder(
+ repositoryConfiguration.getName(), "default",
+ repositoryConfiguration.getUri().toASCIIString());
+
+ if (!repositoryConfiguration.getSnapshotsEnabled()) {
+ builder.setSnapshotPolicy(
+ new RepositoryPolicy(false, RepositoryPolicy.UPDATE_POLICY_NEVER,
+ RepositoryPolicy.CHECKSUM_POLICY_IGNORE));
+ }
+ repositories.add(builder.build());
+ }
+ return repositories;
+ }
+
+}
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/CompositeProxySelector.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/CompositeProxySelector.java
similarity index 96%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/CompositeProxySelector.java
rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/CompositeProxySelector.java
index f34652c4fdc6..e4beb3afcc4b 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/CompositeProxySelector.java
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/CompositeProxySelector.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.grape;
+package org.springframework.boot.aether;
import java.util.ArrayList;
import java.util.List;
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DefaultRepositorySystemSessionAutoConfiguration.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DefaultRepositorySystemSessionAutoConfiguration.java
similarity index 92%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DefaultRepositorySystemSessionAutoConfiguration.java
rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DefaultRepositorySystemSessionAutoConfiguration.java
index d3fc4bf35028..d99c060c7e7a 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DefaultRepositorySystemSessionAutoConfiguration.java
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DefaultRepositorySystemSessionAutoConfiguration.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.grape;
+package org.springframework.boot.aether;
import java.io.File;
import java.util.Arrays;
@@ -29,13 +29,13 @@
import org.springframework.util.StringUtils;
/**
- * A {@link RepositorySystemSessionAutoConfiguration} that, in the absence of any
+ * A {@link RepositorySystemSessionConfiguration} that, in the absence of any
* configuration, applies sensible defaults.
*
* @author Andy Wilkinson
*/
public class DefaultRepositorySystemSessionAutoConfiguration
- implements RepositorySystemSessionAutoConfiguration {
+ implements RepositorySystemSessionConfiguration {
@Override
public void apply(DefaultRepositorySystemSession session,
diff --git a/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DependencyManagementContext.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DependencyManagementContext.java
new file mode 100644
index 000000000000..4b4e56392596
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DependencyManagementContext.java
@@ -0,0 +1,74 @@
+/*
+ * 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.aether;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.aether.graph.Dependency;
+
+/**
+ * Context used when resolving dependencies.
+ *
+ * @author Andy Wilkinson
+ * @since 1.1.0
+ */
+public class DependencyManagementContext {
+
+ private final Map managedDependencyByGroupAndArtifact = new HashMap();
+
+ private final List managedDependencies = new ArrayList();
+
+ private String getIdentifier(Dependency dependency) {
+ return getIdentifier(dependency.getArtifact().getGroupId(),
+ dependency.getArtifact().getArtifactId());
+ }
+
+ private String getIdentifier(String groupId, String artifactId) {
+ return groupId + ":" + artifactId;
+ }
+
+ 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 getManagedDependencies() {
+ return Collections.unmodifiableList(this.managedDependencies);
+ }
+
+ private Dependency getManagedDependency(String group, String artifact) {
+ return this.managedDependencyByGroupAndArtifact
+ .get(getIdentifier(group, artifact));
+ }
+
+ public void addManagedDependencies(List dependencies) {
+ this.managedDependencies.addAll(dependencies);
+ for (Dependency dependency : dependencies) {
+ this.managedDependencyByGroupAndArtifact.put(getIdentifier(dependency),
+ dependency);
+ }
+ }
+
+}
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DependencyResolutionFailedException.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DependencyResolutionFailedException.java
similarity index 95%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DependencyResolutionFailedException.java
rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DependencyResolutionFailedException.java
index 17a708cd4b7a..79c4030c9e3e 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DependencyResolutionFailedException.java
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DependencyResolutionFailedException.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.grape;
+package org.springframework.boot.aether;
/**
* Thrown to indicate a failure during dependency resolution.
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DetailedProgressReporter.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DetailedProgressReporter.java
similarity index 97%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DetailedProgressReporter.java
rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DetailedProgressReporter.java
index 1d6426bfc76b..e23e84d6ac9c 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/DetailedProgressReporter.java
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DetailedProgressReporter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.grape;
+package org.springframework.boot.aether;
import java.io.PrintStream;
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/ProgressReporter.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/ProgressReporter.java
similarity index 93%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/ProgressReporter.java
rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/ProgressReporter.java
index e0283979c596..7e4f3d368c82 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/ProgressReporter.java
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/ProgressReporter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.grape;
+package org.springframework.boot.aether;
/**
* Reports progress on a dependency resolution operation.
diff --git a/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositoryConfiguration.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositoryConfiguration.java
new file mode 100644
index 000000000000..5f30456343ce
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositoryConfiguration.java
@@ -0,0 +1,99 @@
+/*
+ * 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.aether;
+
+import java.net.URI;
+
+import org.springframework.util.ObjectUtils;
+
+/**
+ * The configuration of a repository.
+ *
+ * @author Andy Wilkinson
+ */
+public final class RepositoryConfiguration {
+
+ private final String name;
+
+ private final URI uri;
+
+ private final boolean snapshotsEnabled;
+
+ /**
+ * Creates a new {@code RepositoryConfiguration} instance.
+ * @param name The name of the repository
+ * @param uri The uri of the repository
+ * @param snapshotsEnabled {@code true} if the repository should enable access to
+ * snapshots, {@code false} otherwise
+ */
+ public RepositoryConfiguration(String name, URI uri, boolean snapshotsEnabled) {
+ this.name = name;
+ this.uri = uri;
+ this.snapshotsEnabled = snapshotsEnabled;
+ }
+
+ /**
+ * Return the name of the repository.
+ * @return the repository name
+ */
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String toString() {
+ return "RepositoryConfiguration [name=" + this.name + ", uri=" + this.uri
+ + ", snapshotsEnabled=" + this.snapshotsEnabled + "]";
+ }
+
+ /**
+ * Return the URI of the repository.
+ * @return the repository URI
+ */
+ public URI getUri() {
+ return this.uri;
+ }
+
+ /**
+ * Return if the repository should enable access to snapshots.
+ * @return {@code true} if snapshot access is enabled
+ */
+ public boolean getSnapshotsEnabled() {
+ return this.snapshotsEnabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return ObjectUtils.nullSafeHashCode(this.name);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ RepositoryConfiguration other = (RepositoryConfiguration) obj;
+ return ObjectUtils.nullSafeEquals(this.name, other.name);
+ }
+
+}
diff --git a/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositoryConfigurationFactory.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositoryConfigurationFactory.java
new file mode 100644
index 000000000000..82a9023f8bfc
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositoryConfigurationFactory.java
@@ -0,0 +1,130 @@
+/*
+ * 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.aether;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.settings.Profile;
+import org.apache.maven.settings.Repository;
+import org.codehaus.plexus.interpolation.InterpolationException;
+import org.codehaus.plexus.interpolation.Interpolator;
+import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
+import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
+
+import org.springframework.boot.aether.maven.MavenSettings;
+import org.springframework.boot.aether.maven.MavenSettingsReader;
+import org.springframework.util.StringUtils;
+
+/**
+ * Factory used to create {@link RepositoryConfiguration}s.
+ *
+ * @author Andy Wilkinson
+ * @author Dave Syer
+ */
+public final class RepositoryConfigurationFactory {
+
+ private static final RepositoryConfiguration MAVEN_CENTRAL = new RepositoryConfiguration(
+ "central", URI.create("http://repo1.maven.org/maven2/"), false);
+
+ private static final RepositoryConfiguration SPRING_MILESTONE = new RepositoryConfiguration(
+ "spring-milestone", URI.create("http://repo.spring.io/milestone"), false);
+
+ private static final RepositoryConfiguration SPRING_SNAPSHOT = new RepositoryConfiguration(
+ "spring-snapshot", URI.create("http://repo.spring.io/snapshot"), true);
+
+ private RepositoryConfigurationFactory() {
+ }
+
+ /**
+ * Create a new default repository configuration.
+ * @return the newly-created default repository configuration
+ */
+ public static List createDefaultRepositoryConfiguration() {
+ MavenSettings mavenSettings = new MavenSettingsReader().readSettings();
+ List repositoryConfiguration = new ArrayList();
+ repositoryConfiguration.add(MAVEN_CENTRAL);
+ if (!Boolean.getBoolean("disableSpringSnapshotRepos")) {
+ repositoryConfiguration.add(SPRING_MILESTONE);
+ repositoryConfiguration.add(SPRING_SNAPSHOT);
+ }
+ addDefaultCacheAsRepository(mavenSettings.getLocalRepository(),
+ repositoryConfiguration);
+ addActiveProfileRepositories(mavenSettings.getActiveProfiles(),
+ repositoryConfiguration);
+ return repositoryConfiguration;
+ }
+
+ private static void addDefaultCacheAsRepository(String localRepository,
+ List repositoryConfiguration) {
+ RepositoryConfiguration repository = new RepositoryConfiguration("local",
+ getLocalRepositoryDirectory(localRepository).toURI(), true);
+ if (!repositoryConfiguration.contains(repository)) {
+ repositoryConfiguration.add(0, repository);
+ }
+ }
+
+ private static void addActiveProfileRepositories(List activeProfiles,
+ List configurations) {
+ for (Profile activeProfile : activeProfiles) {
+ Interpolator interpolator = new RegexBasedInterpolator();
+ interpolator.addValueSource(
+ new PropertiesBasedValueSource(activeProfile.getProperties()));
+ for (Repository repository : activeProfile.getRepositories()) {
+ configurations.add(getRepositoryConfiguration(interpolator, repository));
+ }
+ }
+ }
+
+ private static RepositoryConfiguration getRepositoryConfiguration(
+ Interpolator interpolator, Repository repository) {
+ String name = interpolate(interpolator, repository.getId());
+ String url = interpolate(interpolator, repository.getUrl());
+ boolean snapshotsEnabled = false;
+ if (repository.getSnapshots() != null) {
+ snapshotsEnabled = repository.getSnapshots().isEnabled();
+ }
+ return new RepositoryConfiguration(name, URI.create(url), snapshotsEnabled);
+ }
+
+ private static String interpolate(Interpolator interpolator, String value) {
+ try {
+ return interpolator.interpolate(value);
+ }
+ catch (InterpolationException ex) {
+ return value;
+ }
+ }
+
+ private static File getLocalRepositoryDirectory(String localRepository) {
+ if (StringUtils.hasText(localRepository)) {
+ return new File(localRepository);
+ }
+ return new File(getM2HomeDirectory(), "repository");
+ }
+
+ private static File getM2HomeDirectory() {
+ String mavenRoot = System.getProperty("maven.home");
+ if (StringUtils.hasLength(mavenRoot)) {
+ return new File(mavenRoot);
+ }
+ return new File(System.getProperty("user.home"), ".m2");
+ }
+
+}
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/RepositorySystemSessionAutoConfiguration.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositorySystemSessionConfiguration.java
similarity index 88%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/RepositorySystemSessionAutoConfiguration.java
rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositorySystemSessionConfiguration.java
index 44dc2c25c6ad..2ecc2f0c4c94 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/RepositorySystemSessionAutoConfiguration.java
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositorySystemSessionConfiguration.java
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.grape;
+package org.springframework.boot.aether;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
/**
* Strategy that can be used to apply some auto-configuration during the installation of
- * an {@link AetherGrapeEngine}.
+ * an {@link AetherEngine}.
*
* @author Andy Wilkinson
*/
-public interface RepositorySystemSessionAutoConfiguration {
+public interface RepositorySystemSessionConfiguration {
/**
* Apply the configuration.
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SummaryProgressReporter.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/SummaryProgressReporter.java
similarity index 98%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SummaryProgressReporter.java
rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/SummaryProgressReporter.java
index 6ad5ca9d817f..c7a21c40f587 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SummaryProgressReporter.java
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/SummaryProgressReporter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.grape;
+package org.springframework.boot.aether;
import java.io.PrintStream;
import java.util.concurrent.TimeUnit;
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/maven/MavenSettings.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/maven/MavenSettings.java
similarity index 99%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/maven/MavenSettings.java
rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/maven/MavenSettings.java
index f3a6bbc3acd0..259f59c61acc 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/maven/MavenSettings.java
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/maven/MavenSettings.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.maven;
+package org.springframework.boot.aether.maven;
import java.io.BufferedReader;
import java.io.File;
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/maven/MavenSettingsReader.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/maven/MavenSettingsReader.java
similarity index 97%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/maven/MavenSettingsReader.java
rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/maven/MavenSettingsReader.java
index 97d0fa7196df..a98fa3f9c628 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/maven/MavenSettingsReader.java
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/maven/MavenSettingsReader.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.maven;
+package org.springframework.boot.aether.maven;
import java.io.File;
import java.lang.reflect.Field;
@@ -32,8 +32,6 @@
import org.sonatype.plexus.components.cipher.PlexusCipherException;
import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
-import org.springframework.boot.cli.util.Log;
-
/**
* {@code MavenSettingsReader} reads settings from a user's Maven settings.xml file,
* decrypting them if necessary using settings-security.xml.
@@ -57,7 +55,7 @@ public MavenSettings readSettings() {
Settings settings = loadSettings();
SettingsDecryptionResult decrypted = decryptSettings(settings);
if (!decrypted.getProblems().isEmpty()) {
- Log.error(
+ System.err.println(
"Maven settings decryption failed. Some Maven repositories may be inaccessible");
// Continue - the encrypted credentials may not be used
}
diff --git a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfiguration.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/maven/SettingsXmlRepositorySystemSessionConfiguration.java
similarity index 84%
rename from spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfiguration.java
rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/maven/SettingsXmlRepositorySystemSessionConfiguration.java
index b0438f716968..ec3902853c43 100644
--- a/spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfiguration.java
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/maven/SettingsXmlRepositorySystemSessionConfiguration.java
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.grape;
+package org.springframework.boot.aether.maven;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.repository.LocalRepository;
-import org.springframework.boot.cli.compiler.maven.MavenSettings;
-import org.springframework.boot.cli.compiler.maven.MavenSettingsReader;
+import org.springframework.boot.aether.RepositorySystemSessionConfiguration;
/**
* Auto-configuration for a RepositorySystemSession that uses Maven's settings.xml to
@@ -29,8 +28,8 @@
*
* @author Andy Wilkinson
*/
-public class SettingsXmlRepositorySystemSessionAutoConfiguration
- implements RepositorySystemSessionAutoConfiguration {
+public class SettingsXmlRepositorySystemSessionConfiguration
+ implements RepositorySystemSessionConfiguration {
@Override
public void apply(DefaultRepositorySystemSession session,
diff --git a/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/loader/thin/PomLoader.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/loader/thin/PomLoader.java
new file mode 100644
index 000000000000..83a6a6b262b8
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/loader/thin/PomLoader.java
@@ -0,0 +1,104 @@
+/*
+ * 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.loader.thin;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.maven.model.Model;
+import org.apache.maven.model.Parent;
+import org.apache.maven.model.building.DefaultModelProcessor;
+import org.apache.maven.model.io.DefaultModelReader;
+import org.apache.maven.model.locator.DefaultModelLocator;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.graph.Dependency;
+
+import org.springframework.core.io.Resource;
+
+/**
+ * Utility class to help with reading and extracting dependencies from a physical pom.
+ *
+ * @author Dave Syer
+ *
+ */
+public class PomLoader {
+
+ public List getDependencies(Resource pom) {
+ if (!pom.exists()) {
+ return Collections.emptyList();
+ }
+ Model model = readModel(pom);
+ return convert(model.getDependencies());
+ }
+
+ public List getDependencyManagement(Resource pom) {
+ if (!pom.exists()) {
+ return Collections.emptyList();
+ }
+ List list = new ArrayList();
+ Model model = readModel(pom);
+ if (model.getParent() != null) {
+ list.add(new Dependency(getParentArtifact(model), "import"));
+ }
+ if (model.getDependencyManagement() != null) {
+ list.addAll(convert(model.getDependencyManagement().getDependencies()));
+ }
+ return list;
+ }
+
+ private Artifact getParentArtifact(Model model) {
+ Parent parent = model.getParent();
+ return new DefaultArtifact(parent.getGroupId(), parent.getArtifactId(), "pom",
+ parent.getVersion());
+ }
+
+ private List convert(
+ List dependencies) {
+ List result = new ArrayList();
+ for (org.apache.maven.model.Dependency dependency : dependencies) {
+ String scope = dependency.getScope();
+ if (!"test".equals(scope) && !"provided".equals(scope)) {
+ result.add(new Dependency(artifact(dependency), dependency.getScope()));
+ }
+ }
+ return result;
+ }
+
+ private Artifact artifact(org.apache.maven.model.Dependency dependency) {
+ return new DefaultArtifact(dependency.getGroupId(), dependency.getArtifactId(),
+ dependency.getClassifier(), dependency.getType(),
+ dependency.getVersion());
+ }
+
+ private static Model readModel(Resource resource) {
+ DefaultModelProcessor modelProcessor = new DefaultModelProcessor();
+ modelProcessor.setModelLocator(new DefaultModelLocator());
+ modelProcessor.setModelReader(new DefaultModelReader());
+
+ try {
+ return modelProcessor.read(resource.getInputStream(), null);
+ }
+ catch (IOException ex) {
+ throw new IllegalStateException("Failed to build model from effective pom",
+ ex);
+ }
+ }
+
+}
diff --git a/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java
new file mode 100644
index 000000000000..4065dcf570f2
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2012-2016 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.loader.thin;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.jar.JarFile;
+
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.graph.Dependency;
+
+import org.springframework.boot.aether.AetherEngine;
+import org.springframework.boot.aether.DependencyManagementContext;
+import org.springframework.boot.aether.RepositoryConfigurationFactory;
+import org.springframework.boot.loader.ExecutableArchiveLauncher;
+import org.springframework.boot.loader.LaunchedURLClassLoader;
+import org.springframework.boot.loader.archive.Archive;
+import org.springframework.boot.loader.archive.Archive.Entry;
+import org.springframework.boot.loader.archive.ExplodedArchive;
+import org.springframework.boot.loader.archive.JarFileArchive;
+import org.springframework.boot.loader.util.MainClassFinder;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.UrlResource;
+import org.springframework.core.io.support.PropertiesLoaderUtils;
+import org.springframework.core.io.support.ResourcePatternUtils;
+
+/**
+ * Launcher that downloads the dependencies for the app before it starts.
+ *
+ * @author Dave Syer
+ */
+public class ThinJarLauncher extends ExecutableArchiveLauncher {
+
+ private static final String DEFAULT_BOM = "org.springframework.boot:spring-boot-dependencies";
+
+ private static final String DEFAULT_VERSION = "1.5.0.BUILD-SNAPSHOT";
+
+ private PomLoader pomLoader = new PomLoader();
+
+ public static void main(String[] args) throws Exception {
+ new ThinJarLauncher().launch(args);
+ }
+
+ public ThinJarLauncher() throws Exception {
+ super(computeArchive());
+ }
+
+ @Override
+ protected void launch(String[] args) throws Exception {
+ String root = System.getProperty("main.root");
+ String debug = System.getProperty("debug");
+ if (root != null) {
+ // There is a grape root that is used by the aether engine internally
+ System.setProperty("grape.root", root);
+ }
+ if (System.getProperty("main.dryrun") != null) {
+ getClassPathArchives();
+ if (debug != null) {
+ System.out.println(
+ "Downloaded dependencies" + (root == null ? "" : " to " + root));
+ }
+ return;
+ }
+ super.launch(args);
+ }
+
+ protected ClassLoader createClassLoader(URL[] urls) throws Exception {
+ return new LaunchedURLClassLoader(urls, getClass().getClassLoader().getParent());
+ }
+
+ @Override
+ protected String getMainClass() throws Exception {
+ if (System.getProperty("main.class") != null) {
+ return System.getProperty("main.class");
+ }
+ try {
+ return super.getMainClass();
+ }
+ catch (IllegalStateException e) {
+ File root = new File(getArchive().getUrl().toURI());
+ if (getArchive() instanceof ExplodedArchive) {
+ return MainClassFinder.findSingleMainClass(root);
+ }
+ else {
+ return MainClassFinder.findSingleMainClass(new JarFile(root), "/");
+ }
+ }
+ }
+
+ private static Archive computeArchive() throws Exception {
+ File file = new File(findArchive());
+ if (file.isDirectory()) {
+ return new ExplodedArchive(file);
+ }
+ return new JarFileArchive(file);
+ }
+
+ private static URI findArchive() throws Exception {
+ String path = System.getProperty("main.archive");
+ URI archive = path == null ? null : new URI(path);
+ File dir = new File("target/classes");
+ if (archive == null && dir.exists()) {
+ archive = dir.toURI();
+ }
+ if (archive == null) {
+ dir = new File("build/classes");
+ if (dir.exists()) {
+ archive = dir.toURI();
+ }
+ }
+ if (archive == null) {
+ dir = new File(".");
+ archive = dir.toURI();
+ }
+ return archive;
+ }
+
+ @Override
+ protected List getClassPathArchives() throws Exception {
+ Collection dependencies = new LinkedHashSet();
+ Collection boms = new LinkedHashSet();
+ // TODO: Maybe use something that conserves order?
+ Properties libs = loadLibraryProperties();
+ for (String key : libs.stringPropertyNames()) {
+ String lib = libs.getProperty(key);
+ if (key.startsWith("dependencies")) {
+ dependencies.add(dependency(lib));
+ }
+ if (key.startsWith("boms")) {
+ boms.add(dependency(lib));
+ }
+ }
+
+ boms.addAll(getPomDependencyManagement());
+ dependencies.addAll(getPomDependencies());
+
+ if (boms.isEmpty()) {
+ boms.add(dependency(getDefaultBom()));
+ }
+
+ List archives = archives(resolve(new ArrayList(boms),
+ new ArrayList(dependencies)));
+ if (!archives.isEmpty()) {
+ archives.set(0, getArchive());
+ }
+ else {
+ archives.add(getArchive());
+ }
+ return archives;
+ }
+
+ private String getDefaultBom() {
+ return DEFAULT_BOM + ":" + getVersion();
+ }
+
+ private String getVersion() {
+ Package pkg = ThinJarLauncher.class.getPackage();
+ return (pkg != null ? pkg.getImplementationVersion() : DEFAULT_VERSION);
+ }
+
+ private Properties loadLibraryProperties() throws IOException, MalformedURLException {
+ UrlResource resource = new UrlResource(
+ getArchive().getUrl() + "META-INF/lib.properties");
+ Properties props = resource.exists()
+ ? PropertiesLoaderUtils.loadProperties(resource) : new Properties();
+ FileSystemResource local = new FileSystemResource("lib.properties");
+ if (local.exists()) {
+ PropertiesLoaderUtils.fillProperties(props, local);
+ }
+ return props;
+ }
+
+ private List archives(List files) throws IOException {
+ List archives = new ArrayList();
+ for (File file : files) {
+ archives.add(new JarFileArchive(file, file.toURI().toURL()));
+ }
+ return archives;
+ }
+
+ private Dependency dependency(String coordinates) {
+ String[] parts = coordinates.split(":");
+ if (parts.length < 2) {
+ throw new IllegalArgumentException(
+ "Co-ordinates should contain group:artifact[:extension][:classifier][:version]");
+ }
+ String extension = "jar";
+ String classifier;
+ String version;
+ String artifactId;
+ String groupId;
+ if (parts.length > 4) {
+ extension = parts[2];
+ classifier = parts[3];
+ version = parts[4];
+ }
+ else if (parts.length > 3) {
+ if (parts[3].contains(".")) {
+ version = parts[3];
+ classifier = parts[2];
+ }
+ else {
+ extension = parts[2];
+ classifier = parts[3];
+ version = null;
+ }
+
+ }
+ else if (parts.length > 2) {
+ if (parts[2].contains(".")) {
+ version = parts[2];
+ classifier = null;
+ }
+ else {
+ classifier = parts[2];
+ version = null;
+ }
+ }
+ else {
+ classifier = null;
+ version = null;
+ }
+ groupId = parts[0];
+ artifactId = parts[1];
+ return new Dependency(
+ new DefaultArtifact(groupId, artifactId, classifier, extension, version),
+ "compile");
+ }
+
+ private List resolve(List boms, List dependencies)
+ throws Exception {
+ AetherEngine engine = AetherEngine.create(
+ RepositoryConfigurationFactory.createDefaultRepositoryConfiguration(),
+ new DependencyManagementContext());
+ engine.addDependencyManagementBoms(boms);
+ List files = engine.resolve(dependencies);
+ return files;
+ }
+
+ private List getPomDependencies() throws Exception {
+ return this.pomLoader.getDependencies(getPom());
+ }
+
+ private List getPomDependencyManagement() throws Exception {
+ return this.pomLoader.getDependencyManagement(getPom());
+ }
+
+ private Resource getPom() throws Exception {
+ Resource pom = new UrlResource(getArchive().getUrl() + "pom.xml");
+ if (!pom.exists()) {
+ for (Resource resource : ResourcePatternUtils
+ .getResourcePatternResolver(new DefaultResourceLoader())
+ .getResources(getArchive().getUrl() + "META-INF/maven/**/pom.xml")) {
+ if (resource.exists()) {
+ return resource;
+ }
+ }
+ }
+ if (!pom.exists()) {
+ pom = new FileSystemResource("./pom.xml");
+ }
+ return pom;
+ }
+
+ @Override
+ protected boolean isNestedArchive(Entry entry) {
+ return false;
+ }
+
+}
diff --git a/spring-boot-tools/spring-boot-aether/src/main/resources/META-INF/services/org.springframework.boot.aether.RepositorySystemSessionConfiguration b/spring-boot-tools/spring-boot-aether/src/main/resources/META-INF/services/org.springframework.boot.aether.RepositorySystemSessionConfiguration
new file mode 100644
index 000000000000..17db239ede0b
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/main/resources/META-INF/services/org.springframework.boot.aether.RepositorySystemSessionConfiguration
@@ -0,0 +1 @@
+org.springframework.boot.aether.maven.SettingsXmlRepositorySystemSessionConfiguration
\ No newline at end of file
diff --git a/spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/AetherEngineTests.java b/spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/AetherEngineTests.java
new file mode 100644
index 000000000000..81fced9ff6f6
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/AetherEngineTests.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2012-2016 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.aether;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.repository.Authentication;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.junit.Test;
+
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link AetherEngine}.
+ *
+ * @author Andy Wilkinson
+ */
+public class AetherEngineTests {
+
+ private AetherEngine createEngine(RepositoryConfiguration... additionalRepositories) {
+ List repositoryConfigurations = new ArrayList();
+ repositoryConfigurations.add(new RepositoryConfiguration("central",
+ URI.create("http://repo1.maven.org/maven2"), false));
+ repositoryConfigurations.addAll(Arrays.asList(additionalRepositories));
+ return AetherEngine.create(repositoryConfigurations,
+ new DependencyManagementContext());
+ }
+
+ @Test
+ public void proxySelector() {
+ doWithCustomUserHome(new Runnable() {
+
+ @Override
+ public void run() {
+ AetherEngine grapeEngine = createEngine();
+
+ DefaultRepositorySystemSession session = (DefaultRepositorySystemSession) ReflectionTestUtils
+ .getField(grapeEngine, "session");
+
+ assertThat(session.getProxySelector() instanceof CompositeProxySelector)
+ .isTrue();
+ }
+
+ });
+ }
+
+ @Test
+ public void repositoryMirrors() {
+ doWithCustomUserHome(new Runnable() {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void run() {
+ AetherEngine grapeEngine = createEngine();
+
+ List repositories = (List) ReflectionTestUtils
+ .getField(grapeEngine, "repositories");
+ assertThat(repositories).hasSize(1);
+ assertThat(repositories.get(0).getId()).isEqualTo("central-mirror");
+ }
+ });
+ }
+
+ @Test
+ public void repositoryAuthentication() {
+ doWithCustomUserHome(new Runnable() {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void run() {
+ AetherEngine grapeEngine = createEngine();
+
+ List repositories = (List) ReflectionTestUtils
+ .getField(grapeEngine, "repositories");
+ assertThat(repositories).hasSize(1);
+ Authentication authentication = repositories.get(0).getAuthentication();
+ assertThat(authentication).isNotNull();
+ }
+ });
+ }
+
+ private void doWithCustomUserHome(Runnable action) {
+ doWithSystemProperty("user.home",
+ new File("src/test/resources").getAbsolutePath(), action);
+ }
+
+ private void doWithSystemProperty(String key, String value, Runnable action) {
+ String previousValue = setOrClearSystemProperty(key, value);
+ try {
+ action.run();
+ }
+ finally {
+ setOrClearSystemProperty(key, previousValue);
+ }
+ }
+
+ private String setOrClearSystemProperty(String key, String value) {
+ if (value != null) {
+ return System.setProperty(key, value);
+ }
+ return System.clearProperty(key);
+ }
+}
diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/DetailedProgressReporterTests.java b/spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/DetailedProgressReporterTests.java
similarity index 98%
rename from spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/DetailedProgressReporterTests.java
rename to spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/DetailedProgressReporterTests.java
index 9f9d3c0c25d1..b3d53e94c1e4 100644
--- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/DetailedProgressReporterTests.java
+++ b/spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/DetailedProgressReporterTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.grape;
+package org.springframework.boot.aether;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfigurationTests.java b/spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/maven/SettingsXmlRepositorySystemSessionConfigurationTests.java
similarity index 90%
rename from spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfigurationTests.java
rename to spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/maven/SettingsXmlRepositorySystemSessionConfigurationTests.java
index 9d768e5154dd..8687256d7371 100644
--- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfigurationTests.java
+++ b/spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/maven/SettingsXmlRepositorySystemSessionConfigurationTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.cli.compiler.grape;
+package org.springframework.boot.aether.maven;
import java.io.File;
@@ -38,7 +38,7 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import org.springframework.boot.cli.testutil.SystemProperties;
+import org.springframework.boot.aether.testutil.SystemProperties;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@@ -46,11 +46,11 @@
import static org.mockito.Matchers.eq;
/**
- * Tests for {@link SettingsXmlRepositorySystemSessionAutoConfiguration}.
+ * Tests for {@link SettingsXmlRepositorySystemSessionConfiguration}.
*
* @author Andy Wilkinson
*/
-public class SettingsXmlRepositorySystemSessionAutoConfigurationTests {
+public class SettingsXmlRepositorySystemSessionConfigurationTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@@ -94,8 +94,8 @@ public LocalRepositoryManager answer(
SystemProperties.doWithSystemProperties(new Runnable() {
@Override
public void run() {
- new SettingsXmlRepositorySystemSessionAutoConfiguration().apply(session,
- SettingsXmlRepositorySystemSessionAutoConfigurationTests.this.repositorySystem);
+ new SettingsXmlRepositorySystemSessionConfiguration().apply(session,
+ SettingsXmlRepositorySystemSessionConfigurationTests.this.repositorySystem);
}
}, "user.home:src/test/resources/maven-settings/property-interpolation",
"foo:bar");
@@ -110,8 +110,8 @@ private void assertSessionCustomization(String userHome) {
SystemProperties.doWithSystemProperties(new Runnable() {
@Override
public void run() {
- new SettingsXmlRepositorySystemSessionAutoConfiguration().apply(session,
- SettingsXmlRepositorySystemSessionAutoConfigurationTests.this.repositorySystem);
+ new SettingsXmlRepositorySystemSessionConfiguration().apply(session,
+ SettingsXmlRepositorySystemSessionConfigurationTests.this.repositorySystem);
}
}, "user.home:" + userHome);
diff --git a/spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/testutil/SystemProperties.java b/spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/testutil/SystemProperties.java
new file mode 100644
index 000000000000..76ef1819c8cc
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/testutil/SystemProperties.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2016 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.aether.testutil;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Utilities for working with System properties in unit tests
+ *
+ * @author Andy Wilkinson
+ */
+public final class SystemProperties {
+
+ private SystemProperties() {
+ }
+
+ /**
+ * Performs the given {@code action} with the given system properties set. System
+ * properties are restored to their previous values once the action has run.
+ *
+ * @param action The action to perform
+ * @param systemPropertyPairs The system properties, each in the form
+ * {@code key:value}
+ */
+ public static void doWithSystemProperties(Runnable action,
+ String... systemPropertyPairs) {
+ Map originalValues = new HashMap();
+ for (String pair : systemPropertyPairs) {
+ String[] components = pair.split(":");
+ String key = components[0];
+ String value = components[1];
+ originalValues.put(key, System.setProperty(key, value));
+ }
+ try {
+ action.run();
+ }
+ finally {
+ for (Entry entry : originalValues.entrySet()) {
+ if (entry.getValue() == null) {
+ System.clearProperty(entry.getKey());
+ }
+ else {
+ System.setProperty(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ }
+}
diff --git a/spring-boot-tools/spring-boot-aether/src/test/resources/.m2/settings.xml b/spring-boot-tools/spring-boot-aether/src/test/resources/.m2/settings.xml
new file mode 100644
index 000000000000..86a68acfac13
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/test/resources/.m2/settings.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ central-mirror
+ http://central-mirror.example.com/maven2
+ central
+
+
+
+
+
+ central-mirror
+ user
+ password
+
+
+
+
+
+ true
+ http
+ proxy.example.com
+ 3128
+ user
+ password
+
+
+
+
\ No newline at end of file
diff --git a/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/active-profile-repositories/.m2/settings.xml b/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/active-profile-repositories/.m2/settings.xml
new file mode 100644
index 000000000000..8cc312378d8e
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/active-profile-repositories/.m2/settings.xml
@@ -0,0 +1,97 @@
+
+
+
+
+ my-mirror
+ http://maven.example.com/mirror
+ my-server
+
+
+
+
+
+ my-server
+ tester
+ secret
+
+
+
+
+
+ my-proxy
+ true
+ http
+ proxy.example.com
+ 8080
+ proxyuser
+ somepassword
+
+
+
+
+
+ active-by-default
+
+ true
+
+
+
+ active-by-default
+ maven.example.com/activeByDefault
+
+
+
+
+ active-by-property
+
+
+ foo
+ bar
+
+
+
+
+ active-by-property
+ maven.example.com/activeByProperty
+
+
+
+
+ interpolation-profile
+
+
+ interpolate
+ true
+
+
+
+ maven.example.com
+ ${repo.base}/content
+
+
+
+ interpolate-releases
+ ${repo.content}/releases
+
+ true
+ never
+
+
+ false
+
+
+
+ interpolate-snapshots
+ ${repo.content}/snapshots
+
+ false
+
+
+ true
+
+
+
+
+
+
+
diff --git a/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/basic/.m2/settings.xml b/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/basic/.m2/settings.xml
new file mode 100644
index 000000000000..5439ce8cfb85
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/basic/.m2/settings.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ my-mirror
+ http://maven.example.com/mirror
+ my-server
+
+
+
+
+
+ my-server
+ tester
+ secret
+
+
+
+
+
+ my-proxy
+ true
+ http
+ proxy.example.com
+ 8080
+ proxyuser
+ somepassword
+
+
+
+
+
+ test-profile
+
+
+ ${user.home}/.m2/some_file
+
+
+
+
+ example-repository
+ http://repo.example.com
+
+
+
+
+
+
diff --git a/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/encrypted/.m2/settings-security.xml b/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/encrypted/.m2/settings-security.xml
new file mode 100644
index 000000000000..7b6597c44e94
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/encrypted/.m2/settings-security.xml
@@ -0,0 +1,3 @@
+
+ {oAyWuFO63U8HHgiplpqtgXih0/pwcRA0d+uA+Z7TBEk=}
+
\ No newline at end of file
diff --git a/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/encrypted/.m2/settings.xml b/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/encrypted/.m2/settings.xml
new file mode 100644
index 000000000000..b8701c783a1f
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/encrypted/.m2/settings.xml
@@ -0,0 +1,31 @@
+
+
+
+
+ my-mirror
+ http://maven.example.com/mirror
+ my-server
+
+
+
+
+
+ my-server
+ tester
+ {Ur5BpeQGlYUHhXsHahO/HbMBcPSFSUtN5gbWuFFPYGw=}
+
+
+
+
+
+ my-proxy
+ true
+ http
+ proxy.example.com
+ 8080
+ proxyuser
+ {3iRQQyaIUgQHwH8uzTvr9/52pZAjLOTWz/SlWDB7CM4=}
+
+
+
+
\ No newline at end of file
diff --git a/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/property-interpolation/.m2/settings.xml b/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/property-interpolation/.m2/settings.xml
new file mode 100644
index 000000000000..873642e6dd86
--- /dev/null
+++ b/spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/property-interpolation/.m2/settings.xml
@@ -0,0 +1,8 @@
+
+
+ ${foo}/repository
+
+
diff --git a/spring-boot-tools/spring-boot-antlib/pom.xml b/spring-boot-tools/spring-boot-antlib/pom.xml
index 1b28cf68c5ac..e2674972a7c8 100644
--- a/spring-boot-tools/spring-boot-antlib/pom.xml
+++ b/spring-boot-tools/spring-boot-antlib/pom.xml
@@ -21,12 +21,17 @@
org.springframework.boot
- spring-boot-loader
- provided
+ spring-boot-loader-tools
+ compile
+
+
+ org.springframework
+ spring-core
+ compile
org.springframework.boot
- spring-boot-loader-tools
+ spring-boot-loader
compile
@@ -45,6 +50,7 @@
org.springframework.boot:spring-boot-loader-tools
+ org.springframework.boot:spring-boot-loader
org.springframework:spring-core
diff --git a/spring-boot-tools/spring-boot-antlib/src/main/java/org/springframework/boot/ant/FindMainClass.java b/spring-boot-tools/spring-boot-antlib/src/main/java/org/springframework/boot/ant/FindMainClass.java
index 94f2fe0a8479..ef38afc6e820 100644
--- a/spring-boot-tools/spring-boot-antlib/src/main/java/org/springframework/boot/ant/FindMainClass.java
+++ b/spring-boot-tools/spring-boot-antlib/src/main/java/org/springframework/boot/ant/FindMainClass.java
@@ -24,7 +24,7 @@
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
-import org.springframework.boot.loader.tools.MainClassFinder;
+import org.springframework.boot.loader.util.MainClassFinder;
import org.springframework.util.StringUtils;
/**
diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java
index 987336250ccb..067c4483e08d 100644
--- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java
+++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/SpringBootPluginExtension.java
@@ -291,6 +291,8 @@ enum LayoutType {
MODULE(new Layouts.Module()),
+ THIN(new Layouts.Thin()),
+
NONE(new Layouts.None());
Layout layout;
diff --git a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/FindMainClassTask.java b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/FindMainClassTask.java
index a2a99428ce57..320db7b74d7c 100644
--- a/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/FindMainClassTask.java
+++ b/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/run/FindMainClassTask.java
@@ -29,7 +29,7 @@
import org.gradle.api.tasks.TaskAction;
import org.springframework.boot.gradle.SpringBootPluginExtension;
-import org.springframework.boot.loader.tools.MainClassFinder;
+import org.springframework.boot.loader.util.MainClassFinder;
/**
* Task to find and set the 'mainClassName' convention when it's missing by searching the
diff --git a/spring-boot-tools/spring-boot-loader-tools/pom.xml b/spring-boot-tools/spring-boot-loader-tools/pom.xml
index b84220d5db94..986271a3fd7a 100644
--- a/spring-boot-tools/spring-boot-loader-tools/pom.xml
+++ b/spring-boot-tools/spring-boot-loader-tools/pom.xml
@@ -23,12 +23,19 @@
org.springframework
spring-core
-
+
+ org.springframework.boot
+ spring-boot-thin-wrapper
+
org.springframework.boot
spring-boot-loader
- provided
+
+ org.springframework.boot
+ spring-boot-aether
+
+
ch.qos.logback
logback-classic
@@ -66,6 +73,12 @@
${project.version}
spring-boot-loader.jar
+
+ org.springframework.boot
+ spring-boot-thin-wrapper
+ ${project.version}
+ spring-boot-thin-wrapper.jar
+
${basedir}/target/generated-resources/loader/META-INF/loader
false
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java
index d5a05dd0946f..a2fba9476ba7 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/JarWriter.java
@@ -55,6 +55,8 @@ public class JarWriter {
private static final String NESTED_LOADER_JAR = "META-INF/loader/spring-boot-loader.jar";
+ private static final String NESTED_WRAPPER_JAR = "META-INF/loader/spring-boot-thin-wrapper.jar";
+
private static final int BUFFER_SIZE = 32 * 1024;
private final JarOutputStream jarOutput;
@@ -208,7 +210,18 @@ private long getNestedLibraryTime(File file) {
* @throws IOException if the classes cannot be written
*/
public void writeLoaderClasses() throws IOException {
- URL loaderJar = getClass().getClassLoader().getResource(NESTED_LOADER_JAR);
+ writeLoaderClasses(NESTED_LOADER_JAR);
+ }
+
+ /**
+ * Write the required spring-boot-loader classes to the JAR.
+ *
+ * @param launcherClassName the class that is going to be the main class
+ * @throws IOException if the classes cannot be written
+ */
+ public void writeLoaderClasses(String launcherClassName) throws IOException {
+ String loaderJarLocation = launcherClassName.contains("Thin") ? NESTED_WRAPPER_JAR : NESTED_LOADER_JAR;
+ URL loaderJar = getClass().getClassLoader().getResource(loaderJarLocation);
JarInputStream inputStream = new JarInputStream(
new BufferedInputStream(loaderJar.openStream()));
JarEntry entry;
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java
index 3cebdc7a05c5..56f1624e9ece 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Layouts.java
@@ -118,6 +118,33 @@ public boolean isExecutable() {
}
+ /**
+ * Thin executable layout.
+ */
+ public static class Thin implements Layout {
+
+ @Override
+ public String getLauncherClassName() {
+ return "org.springframework.boot.loader.wrapper.ThinJarWrapper";
+ }
+
+ @Override
+ public boolean isExecutable() {
+ return true;
+ }
+
+ @Override
+ public String getLibraryDestination(String libraryName, LibraryScope scope) {
+ return null;
+ }
+
+ @Override
+ public String getClassesLocation() {
+ return "";
+ }
+
+ }
+
/**
* Executable WAR layout.
*/
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/MainClassFinder.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/MainClassFinder.java
index d9d09426368e..6c7e0afc1f32 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/MainClassFinder.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/MainClassFinder.java
@@ -46,7 +46,10 @@
* search.
*
* @author Phillip Webb
+ *
+ * @deprecated in favour of MainClassFinder in spring-boot-loader
*/
+@Deprecated
public abstract class MainClassFinder {
private static final String DOT_CLASS = ".class";
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java
index c25129844bbb..6d15d135bcbb 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Repackager.java
@@ -29,6 +29,7 @@
import java.util.jar.Manifest;
import org.springframework.boot.loader.tools.JarWriter.EntryTransformer;
+import org.springframework.boot.loader.util.MainClassFinder;
import org.springframework.lang.UsesJava8;
/**
@@ -217,7 +218,7 @@ public void library(Library library) throws IOException {
}
writeNestedLibraries(standardLibraries, seen, writer);
if (this.layout.isExecutable()) {
- writer.writeLoaderClasses();
+ writer.writeLoaderClasses(this.layout.getLauncherClassName());
}
}
finally {
diff --git a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/MainClassFinderTests.java b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/MainClassFinderTests.java
index 84e098c7bd15..ebb05ad2b4ba 100644
--- a/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/MainClassFinderTests.java
+++ b/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/MainClassFinderTests.java
@@ -26,9 +26,10 @@
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
-import org.springframework.boot.loader.tools.MainClassFinder.ClassNameCallback;
import org.springframework.boot.loader.tools.sample.ClassWithMainMethod;
import org.springframework.boot.loader.tools.sample.ClassWithoutMainMethod;
+import org.springframework.boot.loader.util.MainClassFinder;
+import org.springframework.boot.loader.util.MainClassFinder.ClassNameCallback;
import static org.assertj.core.api.Assertions.assertThat;
diff --git a/spring-boot-tools/spring-boot-loader/pom.xml b/spring-boot-tools/spring-boot-loader/pom.xml
index eca0b8db15b5..3a406b860ed7 100644
--- a/spring-boot-tools/spring-boot-loader/pom.xml
+++ b/spring-boot-tools/spring-boot-loader/pom.xml
@@ -1,5 +1,6 @@
-
+
4.0.0
org.springframework.boot
@@ -25,16 +26,6 @@
spring-core
true
-
- org.slf4j
- jcl-over-slf4j
- test
-
-
- ch.qos.logback
- logback-classic
- test
-
org.springframework
spring-webmvc
diff --git a/spring-boot-tools/spring-boot-loader/src/it/executable-props/pom.xml b/spring-boot-tools/spring-boot-loader/src/it/executable-props/pom.xml
index ac5eaa5184d0..3c17f76c4751 100644
--- a/spring-boot-tools/spring-boot-loader/src/it/executable-props/pom.xml
+++ b/spring-boot-tools/spring-boot-loader/src/it/executable-props/pom.xml
@@ -39,6 +39,7 @@
jar
+ /*/MANIFEST.MF,/*/manifest.mf
${project.build.directory}/assembly
diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/util/MainClassFinder.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/util/MainClassFinder.java
new file mode 100644
index 000000000000..37ee3dc85ca0
--- /dev/null
+++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/util/MainClassFinder.java
@@ -0,0 +1,356 @@
+/*
+ * 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.loader.util;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.springframework.asm.ClassReader;
+import org.springframework.asm.ClassVisitor;
+import org.springframework.asm.MethodVisitor;
+import org.springframework.asm.Opcodes;
+import org.springframework.asm.Type;
+
+/**
+ * Finds any class with a {@code public static main} method by performing a breadth first
+ * search.
+ *
+ * @author Phillip Webb
+ */
+public abstract class MainClassFinder {
+
+ private static final String DOT_CLASS = ".class";
+
+ private static final Type STRING_ARRAY_TYPE = Type.getType(String[].class);
+
+ private static final Type MAIN_METHOD_TYPE = Type.getMethodType(Type.VOID_TYPE,
+ STRING_ARRAY_TYPE);
+
+ private static final String MAIN_METHOD_NAME = "main";
+
+ private static final FileFilter CLASS_FILE_FILTER = new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ return (file.isFile() && file.getName().endsWith(DOT_CLASS));
+ }
+ };
+
+ private static final FileFilter PACKAGE_FOLDER_FILTER = new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ return file.isDirectory() && !file.getName().startsWith(".");
+ }
+ };
+
+ /**
+ * Find the main class from a given folder.
+ * @param rootFolder the root folder to search
+ * @return the main class or {@code null}
+ * @throws IOException if the folder cannot be read
+ */
+ public static String findMainClass(File rootFolder) throws IOException {
+ return doWithMainClasses(rootFolder, new ClassNameCallback() {
+ @Override
+ public String doWith(String className) {
+ return className;
+ }
+ });
+ }
+
+ /**
+ * Find a single main class from a given folder.
+ * @param rootFolder the root folder to search
+ * @return the main class or {@code null}
+ * @throws IOException if the folder cannot be read
+ */
+ public static String findSingleMainClass(File rootFolder) throws IOException {
+ MainClassesCallback callback = new MainClassesCallback();
+ MainClassFinder.doWithMainClasses(rootFolder, callback);
+ return callback.getMainClass();
+ }
+
+ /**
+ * Perform the given callback operation on all main classes from the given root
+ * folder.
+ * @param the result type
+ * @param rootFolder the root folder
+ * @param callback the callback
+ * @return the first callback result or {@code null}
+ * @throws IOException in case of I/O errors
+ */
+ public static T doWithMainClasses(File rootFolder, ClassNameCallback callback)
+ throws IOException {
+ if (!rootFolder.exists()) {
+ return null; // nothing to do
+ }
+ if (!rootFolder.isDirectory()) {
+ throw new IllegalArgumentException(
+ "Invalid root folder '" + rootFolder + "'");
+ }
+ String prefix = rootFolder.getAbsolutePath() + "/";
+ Deque stack = new ArrayDeque();
+ stack.push(rootFolder);
+ while (!stack.isEmpty()) {
+ File file = stack.pop();
+ if (file.isFile()) {
+ InputStream inputStream = new FileInputStream(file);
+ try {
+ if (isMainClass(inputStream)) {
+ String className = convertToClassName(file.getAbsolutePath(),
+ prefix);
+ T result = callback.doWith(className);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ finally {
+ inputStream.close();
+ }
+ }
+ if (file.isDirectory()) {
+ pushAllSorted(stack, file.listFiles(PACKAGE_FOLDER_FILTER));
+ pushAllSorted(stack, file.listFiles(CLASS_FILE_FILTER));
+ }
+ }
+ return null;
+ }
+
+ private static void pushAllSorted(Deque stack, File[] files) {
+ Arrays.sort(files, new Comparator() {
+ @Override
+ public int compare(File o1, File o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+ for (File file : files) {
+ stack.push(file);
+ }
+ }
+
+ /**
+ * Find the main class in a given jar file.
+ * @param jarFile the jar file to search
+ * @param classesLocation the location within the jar containing classes
+ * @return the main class or {@code null}
+ * @throws IOException if the jar file cannot be read
+ */
+ public static String findMainClass(JarFile jarFile, String classesLocation)
+ throws IOException {
+ return doWithMainClasses(jarFile, classesLocation,
+ new ClassNameCallback() {
+ @Override
+ public String doWith(String className) {
+ return className;
+ }
+ });
+ }
+
+ /**
+ * Find a single main class in a given jar file.
+ * @param jarFile the jar file to search
+ * @param classesLocation the location within the jar containing classes
+ * @return the main class or {@code null}
+ * @throws IOException if the jar file cannot be read
+ */
+ public static String findSingleMainClass(JarFile jarFile, String classesLocation)
+ throws IOException {
+ MainClassesCallback callback = new MainClassesCallback();
+ MainClassFinder.doWithMainClasses(jarFile, classesLocation, callback);
+ return callback.getMainClass();
+ }
+
+ /**
+ * Perform the given callback operation on all main classes from the given jar.
+ * @param the result type
+ * @param jarFile the jar file to search
+ * @param classesLocation the location within the jar containing classes
+ * @param callback the callback
+ * @return the first callback result or {@code null}
+ * @throws IOException in case of I/O errors
+ */
+ public static T doWithMainClasses(JarFile jarFile, String classesLocation,
+ ClassNameCallback callback) throws IOException {
+ List classEntries = getClassEntries(jarFile, classesLocation);
+ Collections.sort(classEntries, new ClassEntryComparator());
+ for (JarEntry entry : classEntries) {
+ InputStream inputStream = new BufferedInputStream(
+ jarFile.getInputStream(entry));
+ try {
+ if (isMainClass(inputStream)) {
+ String className = convertToClassName(entry.getName(),
+ classesLocation);
+ T result = callback.doWith(className);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ finally {
+ inputStream.close();
+ }
+ }
+ return null;
+ }
+
+ private static String convertToClassName(String name, String prefix) {
+ name = name.replace("/", ".");
+ name = name.replace('\\', '.');
+ name = name.substring(0, name.length() - DOT_CLASS.length());
+ if (prefix != null) {
+ name = name.substring(prefix.length());
+ }
+ return name;
+ }
+
+ private static List getClassEntries(JarFile source,
+ String classesLocation) {
+ classesLocation = (classesLocation != null ? classesLocation : "");
+ Enumeration sourceEntries = source.entries();
+ List classEntries = new ArrayList();
+ while (sourceEntries.hasMoreElements()) {
+ JarEntry entry = sourceEntries.nextElement();
+ if (entry.getName().startsWith(classesLocation)
+ && entry.getName().endsWith(DOT_CLASS)) {
+ classEntries.add(entry);
+ }
+ }
+ return classEntries;
+ }
+
+ private static boolean isMainClass(InputStream inputStream) {
+ try {
+ ClassReader classReader = new ClassReader(inputStream);
+ MainMethodFinder mainMethodFinder = new MainMethodFinder();
+ classReader.accept(mainMethodFinder, ClassReader.SKIP_CODE);
+ return mainMethodFinder.isFound();
+ }
+ catch (IOException ex) {
+ return false;
+ }
+ }
+
+ private static class ClassEntryComparator implements Comparator {
+
+ @Override
+ public int compare(JarEntry o1, JarEntry o2) {
+ Integer d1 = getDepth(o1);
+ Integer d2 = getDepth(o2);
+ int depthCompare = d1.compareTo(d2);
+ if (depthCompare != 0) {
+ return depthCompare;
+ }
+ return o1.getName().compareTo(o2.getName());
+ }
+
+ private int getDepth(JarEntry entry) {
+ return entry.getName().split("/").length;
+ }
+
+ }
+
+ private static class MainMethodFinder extends ClassVisitor {
+
+ private boolean found;
+
+ MainMethodFinder() {
+ super(Opcodes.ASM4);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc,
+ String signature, String[] exceptions) {
+ if (isAccess(access, Opcodes.ACC_PUBLIC, Opcodes.ACC_STATIC)
+ && MAIN_METHOD_NAME.equals(name)
+ && MAIN_METHOD_TYPE.getDescriptor().equals(desc)) {
+ this.found = true;
+ }
+ return null;
+ }
+
+ private boolean isAccess(int access, int... requiredOpsCodes) {
+ for (int requiredOpsCode : requiredOpsCodes) {
+ if ((access & requiredOpsCode) == 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isFound() {
+ return this.found;
+ }
+
+ }
+
+ /**
+ * Callback interface used to receive class names.
+ * @param the result type
+ */
+ public interface ClassNameCallback {
+
+ /**
+ * Handle the specified class name.
+ * @param className the class name
+ * @return a non-null value if processing should end or {@code null} to continue
+ */
+ T doWith(String className);
+
+ }
+
+ /**
+ * Find a single main class, throwing an {@link IllegalStateException} if multiple
+ * candidates exist.
+ */
+ private static class MainClassesCallback implements ClassNameCallback
+
+ org.springframework.boot
+ spring-boot-loader
+
+
+ org.springframework
+ spring-core
+
org.apache.maven
maven-archiver
diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java
index b22b1b7e5703..7c484dbb86b9 100644
--- a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java
+++ b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java
@@ -38,7 +38,7 @@
import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
import org.springframework.boot.loader.tools.FileUtils;
-import org.springframework.boot.loader.tools.MainClassFinder;
+import org.springframework.boot.loader.util.MainClassFinder;
/**
* Base class to run a spring application.
diff --git a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java
index 309eb72db37d..9b0635e89b3d 100644
--- a/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java
+++ b/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/RepackageMojo.java
@@ -126,7 +126,7 @@ public class RepackageMojo extends AbstractDependencyFilterMojo {
/**
* The type of archive (which corresponds to how the dependencies are laid out inside
- * it). Possible values are JAR, WAR, ZIP, DIR, NONE. Defaults to a guess based on the
+ * it). Possible values are JAR, THIN, WAR, ZIP, DIR, NONE. Defaults to a guess based on the
* archive type.
* @since 1.0
*/
@@ -197,7 +197,7 @@ private void repackage() throws MojoExecutionException {
File source = this.project.getArtifact().getFile();
File target = getTargetFile();
Repackager repackager = getRepackager(source);
- Set artifacts = filterDependencies(this.project.getArtifacts(),
+ Set artifacts = filterDependencies(getLibraries(),
getFilters(getAdditionalFilters()));
Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack,
getLog());
@@ -211,6 +211,10 @@ private void repackage() throws MojoExecutionException {
updateArtifact(source, target, repackager.getBackupFile());
}
+ private Set getLibraries() {
+ return this.project.getArtifacts();
+ }
+
private File getTargetFile() {
String classifier = (this.classifier == null ? "" : this.classifier.trim());
if (classifier.length() > 0 && !classifier.startsWith("-")) {
@@ -342,7 +346,12 @@ public enum LayoutType {
/**
* No Layout.
*/
- NONE(new Layouts.None());
+ NONE(new Layouts.None()),
+
+ /**
+ * Thin Layout.
+ */
+ THIN(new Layouts.Thin());
private final Layout layout;
diff --git a/spring-boot-tools/spring-boot-thin-wrapper/pom.xml b/spring-boot-tools/spring-boot-thin-wrapper/pom.xml
new file mode 100644
index 000000000000..6223f9f68f4d
--- /dev/null
+++ b/spring-boot-tools/spring-boot-thin-wrapper/pom.xml
@@ -0,0 +1,43 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-tools
+ 1.5.0.BUILD-SNAPSHOT
+
+ spring-boot-thin-wrapper
+ Spring Boot Thin Wrapper
+ Spring Boot Loader
+ http://projects.spring.io/spring-boot/
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+
+ ${basedir}/..
+
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ org.springframework.boot.loader.wrapper.ThinJarWrapper
+
+
+
+
+
+
+
diff --git a/spring-boot-tools/spring-boot-thin-wrapper/src/main/java/org/springframework/boot/loader/wrapper/ThinJarWrapper.java b/spring-boot-tools/spring-boot-thin-wrapper/src/main/java/org/springframework/boot/loader/wrapper/ThinJarWrapper.java
new file mode 100644
index 000000000000..a40ae32f4066
--- /dev/null
+++ b/spring-boot-tools/spring-boot-thin-wrapper/src/main/java/org/springframework/boot/loader/wrapper/ThinJarWrapper.java
@@ -0,0 +1,222 @@
+/*
+ * 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.loader.wrapper;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * Very thin main class that downloads another library to be the real launcher.
+ *
+ * @author Dave Syer
+ *
+ */
+public class ThinJarWrapper {
+
+ /**
+ * System property key for the main library where the launcher class is located.
+ */
+ public static final String MAIN_LIBRARY = "main.library";
+
+ /**
+ * System property key used to store the local file system location of the main
+ * archive (the one that this class is found in).
+ */
+ public static final String MAIN_ARCHIVE = "main.archive";
+
+ /**
+ * System property key for remote location of main archive (the one that this class is
+ * found in).
+ */
+ public static final String MAIN_REPO = "main.repo";
+
+ /**
+ * System property key used to override the launcher main class if necessary. Defaults
+ * to ThinJarLauncher
.
+ */
+ public static final String MAIN_LAUNCHER = "main.launcher";
+
+ private static final String DEFAULT_LAUNCHER_CLASS = "org.springframework.boot.loader.thin.ThinJarLauncher";
+
+ private static final String DEFAULT_LIBRARY = "org.springframework.boot:spring-boot-aether:exec:1.5.0.BUILD-SNAPSHOT";
+
+ private Library library;
+
+ public static void main(String[] args) throws Exception {
+ Class> launcher = ThinJarWrapper.class;
+ System.setProperty(MAIN_ARCHIVE, launcher.getProtectionDomain().getCodeSource()
+ .getLocation().toURI().toString());
+ new ThinJarWrapper().launch(args);
+ }
+
+ public ThinJarWrapper() {
+ this.library = library();
+ }
+
+ private Library library() {
+ String coordinates = System.getProperty(MAIN_LIBRARY);
+ return new Library(coordinates == null ? DEFAULT_LIBRARY : coordinates);
+ }
+
+ private void launch(String... args) throws Exception {
+ ClassLoader classLoader = getClassLoader();
+ Class> launcher = classLoader.loadClass(launcherClass());
+ findMainMethod(launcher).invoke(null, new Object[] { args });
+ }
+
+ private String launcherClass() {
+ String launcher = System.getProperty(MAIN_LAUNCHER);
+ return launcher == null ? DEFAULT_LAUNCHER_CLASS : launcher;
+ }
+
+ private Method findMainMethod(Class> launcher) throws NoSuchMethodException {
+ return launcher.getMethod("main", String[].class);
+ }
+
+ private ClassLoader getClassLoader() throws Exception {
+ URL[] urls = getUrls();
+ URLClassLoader classLoader = new URLClassLoader(urls,
+ ThinJarWrapper.class.getClassLoader().getParent());
+ Thread.currentThread().setContextClassLoader(classLoader);
+ return classLoader;
+ }
+
+ private URL[] getUrls() throws Exception {
+ this.library.download(mavenLocal());
+ return new URL[] {
+ new File(mavenLocal() + this.library.getPath()).toURI().toURL() };
+ }
+
+ private String mavenLocal() {
+ return home() + "/.m2/repository";
+ }
+
+ private String home() {
+ String home = System.getProperty("user.home");
+ return home == null ? "." : home;
+ }
+
+ /**
+ * Convenience class to hold the co-ordinates of the library to be downloaded.
+ *
+ */
+ static class Library {
+
+ private String coordinates;
+ private String groupId;
+ private String artifactId;
+ private String version;
+ private String classifier;
+
+ Library(String coordinates) {
+ this.coordinates = coordinates;
+ String[] parts = coordinates.split(":");
+ if (parts.length < 3) {
+ throw new IllegalArgumentException(
+ "Co-ordinates should contain group:artifact[:classifier]:version");
+ }
+ if (parts.length > 3) {
+ this.classifier = parts[2];
+ this.version = parts[3];
+ }
+ else {
+ this.version = parts[2];
+ }
+ this.groupId = parts[0];
+ this.artifactId = parts[1];
+ }
+
+ public void download(String path) {
+ File target = new File(path + getPath());
+ if (!target.exists()) {
+ String repo = repo();
+ InputStream input = null;
+ OutputStream output = null;
+ try {
+ input = new URL(repo + getPath()).openStream();
+ if (target.getParentFile().mkdirs()) {
+ output = new FileOutputStream(target);
+ byte[] bytes = new byte[4096];
+ int count = input.read(bytes);
+ while (count > 0) {
+ output.write(bytes, 0, count);
+ count = input.read(bytes);
+ }
+ }
+ }
+ catch (Exception e) {
+ throw new IllegalStateException(
+ "Cannot download library for launcher " + coordinates, e);
+ }
+ finally {
+ if (input != null) {
+ try {
+ input.close();
+ }
+ catch (Exception e) {
+ }
+ }
+ if (output != null) {
+ try {
+ output.close();
+ }
+ catch (Exception e) {
+ }
+ }
+ }
+ }
+ }
+
+ private static String repo() {
+ String repo = System.getProperty(MAIN_REPO);
+ return repo != null ? repo : "https://repo.spring.io/libs-snapshot";
+ }
+
+ public String getCoordinates() {
+ return this.coordinates;
+ }
+
+ public String getGroupId() {
+ return this.groupId;
+ }
+
+ public String getArtifactId() {
+ return this.artifactId;
+ }
+
+ public String getVersion() {
+ return this.version;
+ }
+
+ public String getClassifier() {
+ return this.classifier;
+ }
+
+ public String getPath() {
+ return "/" + this.groupId.replace(".", "/") + "/" + this.artifactId + "/"
+ + this.version + "/" + this.artifactId + "-" + this.version
+ + (this.classifier != null ? "-" + this.classifier : "") + ".jar";
+ }
+
+ }
+
+}