From 5ecbb270253e49e41920a3e99f0afbc113d8f2d5 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 21 Oct 2016 14:39:40 +0100 Subject: [PATCH 01/10] Refactor aether parts of AetherGrapeEngine into new module Grape and aether are sort of independent and it would be useful to have the aether bits on their own to use as a library in other places. The main entry point is the AetherEngine with service locators for additional session configurations. --- spring-boot-cli/pom.xml | 4 + .../RepositoryConfigurationFactory.java | 4 +- .../cli/compiler/grape/AetherGrapeEngine.java | 150 +-------- .../grape/AetherGrapeEngineFactory.java | 72 +---- .../grape/DependencyResolutionContext.java | 51 +--- ...RepositorySystemSessionConfiguration.java} | 5 +- ...ether.RepositorySystemSessionConfiguration | 1 + ...e.RepositorySystemSessionAutoConfiguration | 2 - .../grape/AetherGrapeEngineTests.java | 81 +---- ...itorySystemSessionConfigurationTests.java} | 12 +- spring-boot-dependencies/pom.xml | 5 + .../spring-boot-sample-ant/pom.xml | 9 +- spring-boot-tools/pom.xml | 2 + spring-boot-tools/spring-boot-aether/pom.xml | 101 +++++++ .../boot/aether/AetherEngine.java | 285 ++++++++++++++++++ .../boot/aether}/CompositeProxySelector.java | 2 +- ...ositorySystemSessionAutoConfiguration.java | 6 +- .../aether/DependencyManagementContext.java | 74 +++++ .../DependencyResolutionFailedException.java | 2 +- .../aether}/DetailedProgressReporter.java | 2 +- .../boot/aether}/ProgressReporter.java | 2 +- .../boot/aether/RepositoryConfiguration.java | 99 ++++++ .../RepositorySystemSessionConfiguration.java | 6 +- .../boot/aether}/SummaryProgressReporter.java | 2 +- .../boot/aether}/maven/MavenSettings.java | 2 +- .../aether}/maven/MavenSettingsReader.java | 6 +- ...lRepositorySystemSessionConfiguration.java | 9 +- ...ether.RepositorySystemSessionConfiguration | 1 + .../boot/aether/AetherEngineTests.java | 124 ++++++++ .../DetailedProgressReporterTests.java | 2 +- ...sitorySystemSessionConfigurationTests.java | 16 +- .../aether/testutil/SystemProperties.java | 64 ++++ .../encrypted/.m2/settings-security.xml | 3 + .../spring-boot-thin-wrapper/pom.xml | 43 +++ .../boot/loader/wrapper/ThinJarWrapper.java | 222 ++++++++++++++ 35 files changed, 1104 insertions(+), 367 deletions(-) rename spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/{GrapeRootRepositorySystemSessionAutoConfiguration.java => GrapeRootRepositorySystemSessionConfiguration.java} (90%) create mode 100644 spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.aether.RepositorySystemSessionConfiguration delete mode 100644 spring-boot-cli/src/main/resources/META-INF/services/org.springframework.boot.cli.compiler.grape.RepositorySystemSessionAutoConfiguration rename spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/{GrapeRootRepositorySystemSessionAutoConfigurationTests.java => GrapeRootRepositorySystemSessionConfigurationTests.java} (89%) create mode 100644 spring-boot-tools/spring-boot-aether/pom.xml create mode 100644 spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/AetherEngine.java rename {spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape => spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether}/CompositeProxySelector.java (96%) rename {spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape => spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether}/DefaultRepositorySystemSessionAutoConfiguration.java (92%) create mode 100644 spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/DependencyManagementContext.java rename {spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape => spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether}/DependencyResolutionFailedException.java (95%) rename {spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape => spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether}/DetailedProgressReporter.java (97%) rename {spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape => spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether}/ProgressReporter.java (93%) create mode 100644 spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositoryConfiguration.java rename spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/RepositorySystemSessionAutoConfiguration.java => spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositorySystemSessionConfiguration.java (88%) rename {spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape => spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether}/SummaryProgressReporter.java (98%) rename {spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler => spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether}/maven/MavenSettings.java (99%) rename {spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler => spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether}/maven/MavenSettingsReader.java (97%) rename spring-boot-cli/src/main/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfiguration.java => spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/maven/SettingsXmlRepositorySystemSessionConfiguration.java (84%) create mode 100644 spring-boot-tools/spring-boot-aether/src/main/resources/META-INF/services/org.springframework.boot.aether.RepositorySystemSessionConfiguration create mode 100644 spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/AetherEngineTests.java rename {spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape => spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether}/DetailedProgressReporterTests.java (98%) rename spring-boot-cli/src/test/java/org/springframework/boot/cli/compiler/grape/SettingsXmlRepositorySystemSessionAutoConfigurationTests.java => spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/maven/SettingsXmlRepositorySystemSessionConfigurationTests.java (90%) create mode 100644 spring-boot-tools/spring-boot-aether/src/test/java/org/springframework/boot/aether/testutil/SystemProperties.java create mode 100644 spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/encrypted/.m2/settings-security.xml create mode 100644 spring-boot-tools/spring-boot-thin-wrapper/pom.xml create mode 100644 spring-boot-tools/spring-boot-thin-wrapper/src/main/java/org/springframework/boot/loader/wrapper/ThinJarWrapper.java 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..529b5dc3d744 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 @@ -28,9 +28,9 @@ 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.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; /** 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..a9a1c145a09b 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 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..3b3e7336ef71 --- /dev/null +++ b/spring-boot-tools/spring-boot-aether/pom.xml @@ -0,0 +1,101 @@ + + + 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-tools + + + 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-jar + test + + + org.springframework.boot + spring-boot-test + test + + + 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-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/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/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-thin-wrapper/pom.xml b/spring-boot-tools/spring-boot-thin-wrapper/pom.xml new file mode 100644 index 000000000000..8d30dbd48a9a --- /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..f7389fe01c99 --- /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.experimental:spring-boot-thin-launcher:0.0.1.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"; + } + + } + +} From e07f40322996c67d4662e43741b6734658ab81d1 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 24 Oct 2016 09:24:49 +0100 Subject: [PATCH 02/10] Migrate RepositoryConfigurationFactory to spring-boot-aether --- .../RepositoryConfigurationFactory.java | 96 ++----------- .../RepositoryConfigurationFactory.java | 130 ++++++++++++++++++ 2 files changed, 140 insertions(+), 86 deletions(-) create mode 100644 spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/aether/RepositoryConfigurationFactory.java 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 529b5dc3d744..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.aether.maven.MavenSettings; -import org.springframework.boot.aether.maven.MavenSettingsReader; import org.springframework.boot.cli.compiler.grape.RepositoryConfiguration; -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-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"); + } + +} From 7c6815b26a53a7e0f309cd4c80a0d1b1d82630eb Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 24 Oct 2016 11:10:49 +0100 Subject: [PATCH 03/10] Add ThinJarLauncher to spring-boot-loader --- spring-boot-tools/spring-boot-aether/pom.xml | 4 - .../boot/loader/tools/MainClassFinder.java | 3 + spring-boot-tools/spring-boot-loader/pom.xml | 39 +- .../src/assembly/launcher.xml | 28 ++ .../boot/loader/thin/PomLoader.java | 104 +++++ .../boot/loader/thin/ThinJarLauncher.java | 271 +++++++++++++ .../boot/loader/util/MainClassFinder.java | 356 ++++++++++++++++++ .../boot/loader/wrapper/ThinJarWrapper.java | 6 +- 8 files changed, 797 insertions(+), 14 deletions(-) create mode 100644 spring-boot-tools/spring-boot-loader/src/assembly/launcher.xml create mode 100644 spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/thin/PomLoader.java create mode 100644 spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java create mode 100644 spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/util/MainClassFinder.java diff --git a/spring-boot-tools/spring-boot-aether/pom.xml b/spring-boot-tools/spring-boot-aether/pom.xml index 3b3e7336ef71..88a6eeb2d997 100644 --- a/spring-boot-tools/spring-boot-aether/pom.xml +++ b/spring-boot-tools/spring-boot-aether/pom.xml @@ -19,10 +19,6 @@ - - org.springframework.boot - spring-boot-loader-tools - org.springframework spring-core 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/pom.xml b/spring-boot-tools/spring-boot-loader/pom.xml index eca0b8db15b5..acbee6f7b056 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 @@ -26,14 +27,15 @@ true - org.slf4j - jcl-over-slf4j - test + org.springframework.boot + spring-boot-aether + true - ch.qos.logback - logback-classic - test + commons-logging + commons-logging + 1.2 + true org.springframework @@ -78,6 +80,29 @@ + + org.apache.maven.plugins + maven-assembly-plugin + + + stub + prepare-package + + single + + false + + true + ${basedir}/src/assembly/launcher.xml + + + org.springframework.boot.loader.thin.ThinJarLauncher + + + + + + diff --git a/spring-boot-tools/spring-boot-loader/src/assembly/launcher.xml b/spring-boot-tools/spring-boot-loader/src/assembly/launcher.xml new file mode 100644 index 000000000000..49301f1f5518 --- /dev/null +++ b/spring-boot-tools/spring-boot-loader/src/assembly/launcher.xml @@ -0,0 +1,28 @@ + + exec + + jar + + false + + + + org.springframework.boot:spring-boot-aether:* + org.springframework:spring-core:* + commons-logging:commons-logging:* + + true + true + + + + + ${basedir}/target/classes + / + + **/* + + + + \ No newline at end of file diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/thin/PomLoader.java b/spring-boot-tools/spring-boot-loader/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-loader/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-loader/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java new file mode 100644 index 000000000000..7d89aa7eeff9 --- /dev/null +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java @@ -0,0 +1,271 @@ +/* + * 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.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.core.io.support.PropertiesLoaderUtils; + +/** + * 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:1.4.1.RELEASE"; + 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(DEFAULT_BOM)); + } + + List archives = archives( + resolve(new ArrayList(boms), new ArrayList(dependencies))); + if (!archives.isEmpty()) { + archives.set(0, getArchive()); + } + else { + archives.add(getArchive()); + } + return archives; + } + + 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()) { + pom = new FileSystemResource("./pom.xml"); + } + return pom; + } + + @Override + protected boolean isNestedArchive(Entry entry) { + return false; + } + +} 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..8cb9e2675777 --- /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 + */ + 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 + */ + 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 { + + private final Set classNames = new LinkedHashSet(); + + @Override + public Object doWith(String className) { + this.classNames.add(className); + return null; + } + + public String getMainClass() { + if (this.classNames.size() > 1) { + throw new IllegalStateException( + "Unable to find a single main class from the following candidates " + + this.classNames); + } + return this.classNames.isEmpty() ? null : this.classNames.iterator().next(); + } + + } + +} 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 index f7389fe01c99..7fefb07f0415 100644 --- 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 @@ -33,7 +33,7 @@ public class ThinJarWrapper { /** - * System property key for the main library where the launcher class is located. + * System property key for the main library where the launcher class is located. */ public static final String MAIN_LIBRARY = "main.library"; @@ -57,7 +57,7 @@ public class ThinJarWrapper { private static final String DEFAULT_LAUNCHER_CLASS = "org.springframework.boot.loader.thin.ThinJarLauncher"; - private static final String DEFAULT_LIBRARY = "org.springframework.boot.experimental:spring-boot-thin-launcher:0.0.1.BUILD-SNAPSHOT"; + private static final String DEFAULT_LIBRARY = "org.springframework.boot:spring-boot-loader:exec:1.5.0.BUILD-SNAPSHOT"; private Library library; @@ -214,7 +214,7 @@ public String getClassifier() { public String getPath() { return "/" + this.groupId.replace(".", "/") + "/" + this.artifactId + "/" + this.version + "/" + this.artifactId + "-" + this.version - + (this.classifier != null ? this.classifier : "") + ".jar"; + + (this.classifier != null ? "-" + this.classifier : "") + ".jar"; } } From 70bb95e63bfbb3893c4a1646c100960b33435f2f Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 24 Oct 2016 12:29:23 +0100 Subject: [PATCH 04/10] Remove unused depdendency --- spring-boot-tools/spring-boot-aether/pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/spring-boot-tools/spring-boot-aether/pom.xml b/spring-boot-tools/spring-boot-aether/pom.xml index 88a6eeb2d997..1146bc39c593 100644 --- a/spring-boot-tools/spring-boot-aether/pom.xml +++ b/spring-boot-tools/spring-boot-aether/pom.xml @@ -82,12 +82,6 @@ aether-util - - org.springframework.boot - spring-boot - test-jar - test - org.springframework.boot spring-boot-test From 4190d6ff32c4ea72cbca533fc41bc08acb9a33a9 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 24 Oct 2016 12:33:11 +0100 Subject: [PATCH 05/10] Update project name --- spring-boot-tools/spring-boot-thin-wrapper/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-boot-tools/spring-boot-thin-wrapper/pom.xml b/spring-boot-tools/spring-boot-thin-wrapper/pom.xml index 8d30dbd48a9a..6223f9f68f4d 100644 --- a/spring-boot-tools/spring-boot-thin-wrapper/pom.xml +++ b/spring-boot-tools/spring-boot-thin-wrapper/pom.xml @@ -7,7 +7,7 @@ 1.5.0.BUILD-SNAPSHOT spring-boot-thin-wrapper - spring-boot-thin-wrapper + Spring Boot Thin Wrapper Spring Boot Loader http://projects.spring.io/spring-boot/ From 3727ed8bab46681c24e086a5a17359725a79e36a Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 24 Oct 2016 12:34:37 +0100 Subject: [PATCH 06/10] Add missing config directory --- .../src/test/resources/.m2/settings.xml | 33 +++++++ .../.m2/settings.xml | 97 +++++++++++++++++++ .../maven-settings/basic/.m2/settings.xml | 48 +++++++++ .../maven-settings/encrypted/.m2/settings.xml | 31 ++++++ .../property-interpolation/.m2/settings.xml | 8 ++ 5 files changed, 217 insertions(+) create mode 100644 spring-boot-tools/spring-boot-aether/src/test/resources/.m2/settings.xml create mode 100644 spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/active-profile-repositories/.m2/settings.xml create mode 100644 spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/basic/.m2/settings.xml create mode 100644 spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/encrypted/.m2/settings.xml create mode 100644 spring-boot-tools/spring-boot-aether/src/test/resources/maven-settings/property-interpolation/.m2/settings.xml 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.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 + + From 1a9645d200e1613185dd5fdf6dbff4d1366eacbf Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 24 Oct 2016 15:57:36 +0100 Subject: [PATCH 07/10] Add layout=THIN support to Maven build --- spring-boot-dependencies/pom.xml | 5 +++ .../spring-boot-loader-tools/pom.xml | 13 ++++++-- .../boot/loader/tools/JarWriter.java | 15 ++++++++- .../boot/loader/tools/Layouts.java | 27 ++++++++++++++++ .../boot/loader/tools/Repackager.java | 3 +- .../boot/loader/thin/ThinJarLauncher.java | 31 ++++++++++++++++--- .../boot/maven/RepackageMojo.java | 15 +++++++-- 7 files changed, 98 insertions(+), 11 deletions(-) diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index a9a1c145a09b..f31a50830bee 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -262,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-tools/spring-boot-loader-tools/pom.xml b/spring-boot-tools/spring-boot-loader-tools/pom.xml index b84220d5db94..fd27f72cdb46 100644 --- a/spring-boot-tools/spring-boot-loader-tools/pom.xml +++ b/spring-boot-tools/spring-boot-loader-tools/pom.xml @@ -23,12 +23,15 @@ org.springframework spring-core - + + org.springframework.boot + spring-boot-thin-wrapper + org.springframework.boot spring-boot-loader - provided + ch.qos.logback logback-classic @@ -66,6 +69,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/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/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java index 7d89aa7eeff9..4065dcf570f2 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java @@ -41,10 +41,12 @@ 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. @@ -53,7 +55,10 @@ */ public class ThinJarLauncher extends ExecutableArchiveLauncher { - private static final String DEFAULT_BOM = "org.springframework.boot:spring-boot-dependencies:1.4.1.RELEASE"; + 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 { @@ -154,11 +159,11 @@ protected List getClassPathArchives() throws Exception { dependencies.addAll(getPomDependencies()); if (boms.isEmpty()) { - boms.add(dependency(DEFAULT_BOM)); + boms.add(dependency(getDefaultBom())); } - List archives = archives( - resolve(new ArrayList(boms), new ArrayList(dependencies))); + List archives = archives(resolve(new ArrayList(boms), + new ArrayList(dependencies))); if (!archives.isEmpty()) { archives.set(0, getArchive()); } @@ -168,6 +173,15 @@ protected List getClassPathArchives() throws Exception { 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"); @@ -257,6 +271,15 @@ private List getPomDependencyManagement() throws Exception { 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"); } 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; From 3ca16969503d5107366c77241729af647264dacf Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 24 Oct 2016 16:05:23 +0100 Subject: [PATCH 08/10] Add layout=THIN to Gradle build --- .../springframework/boot/gradle/SpringBootPluginExtension.java | 2 ++ 1 file changed, 2 insertions(+) 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; From 31e4503cf782d335ab39494e098e4e26537ba6d3 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 24 Oct 2016 16:12:35 +0100 Subject: [PATCH 09/10] Switch usage of deprecated class to its replacement --- .../main/java/org/springframework/boot/ant/FindMainClass.java | 2 +- .../springframework/boot/gradle/run/FindMainClassTask.java | 2 +- .../boot/loader/tools/MainClassFinderTests.java | 3 ++- .../org/springframework/boot/loader/util/MainClassFinder.java | 4 ++-- .../java/org/springframework/boot/maven/AbstractRunMojo.java | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) 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/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/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/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 index 8cb9e2675777..37ee3dc85ca0 100644 --- 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 @@ -108,7 +108,7 @@ public static String findSingleMainClass(File rootFolder) throws IOException { * @return the first callback result or {@code null} * @throws IOException in case of I/O errors */ - static T doWithMainClasses(File rootFolder, ClassNameCallback callback) + public static T doWithMainClasses(File rootFolder, ClassNameCallback callback) throws IOException { if (!rootFolder.exists()) { return null; // nothing to do @@ -199,7 +199,7 @@ public static String findSingleMainClass(JarFile jarFile, String classesLocation * @return the first callback result or {@code null} * @throws IOException in case of I/O errors */ - static T doWithMainClasses(JarFile jarFile, String classesLocation, + public static T doWithMainClasses(JarFile jarFile, String classesLocation, ClassNameCallback callback) throws IOException { List classEntries = getClassEntries(jarFile, classesLocation); Collections.sort(classEntries, new ClassEntryComparator()); 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. From de6e65e61c40519ecc33939214a6f8c598d51e79 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 25 Oct 2016 11:56:56 +0100 Subject: [PATCH 10/10] Move ThinJarLauncher to aether module Avoids issues with class loader in PropertiesLauncher (which may resurface later, but they are not related to this PR). --- spring-boot-tools/spring-boot-aether/pom.xml | 43 ++++++++++++++++++- .../boot/loader/thin/PomLoader.java | 0 .../boot/loader/thin/ThinJarLauncher.java | 0 spring-boot-tools/spring-boot-antlib/pom.xml | 12 ++++-- .../spring-boot-loader-tools/pom.xml | 4 ++ spring-boot-tools/spring-boot-loader/pom.xml | 34 --------------- .../src/assembly/launcher.xml | 28 ------------ .../src/it/executable-props/pom.xml | 1 + .../spring-boot-maven-plugin/pom.xml | 8 ++++ .../boot/loader/wrapper/ThinJarWrapper.java | 2 +- 10 files changed, 65 insertions(+), 67 deletions(-) rename spring-boot-tools/{spring-boot-loader => spring-boot-aether}/src/main/java/org/springframework/boot/loader/thin/PomLoader.java (100%) rename spring-boot-tools/{spring-boot-loader => spring-boot-aether}/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java (100%) delete mode 100644 spring-boot-tools/spring-boot-loader/src/assembly/launcher.xml diff --git a/spring-boot-tools/spring-boot-aether/pom.xml b/spring-boot-tools/spring-boot-aether/pom.xml index 1146bc39c593..99651fa44f96 100644 --- a/spring-boot-tools/spring-boot-aether/pom.xml +++ b/spring-boot-tools/spring-boot-aether/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 org.springframework.boot @@ -19,6 +20,10 @@ + + org.springframework.boot + spring-boot-loader + org.springframework spring-core @@ -88,4 +93,40 @@ 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-loader/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 similarity index 100% rename from spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/thin/PomLoader.java rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/loader/thin/PomLoader.java diff --git a/spring-boot-tools/spring-boot-loader/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 similarity index 100% rename from spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java rename to spring-boot-tools/spring-boot-aether/src/main/java/org/springframework/boot/loader/thin/ThinJarLauncher.java 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-loader-tools/pom.xml b/spring-boot-tools/spring-boot-loader-tools/pom.xml index fd27f72cdb46..986271a3fd7a 100644 --- a/spring-boot-tools/spring-boot-loader-tools/pom.xml +++ b/spring-boot-tools/spring-boot-loader-tools/pom.xml @@ -31,6 +31,10 @@ org.springframework.boot spring-boot-loader + + org.springframework.boot + spring-boot-aether + ch.qos.logback diff --git a/spring-boot-tools/spring-boot-loader/pom.xml b/spring-boot-tools/spring-boot-loader/pom.xml index acbee6f7b056..3a406b860ed7 100644 --- a/spring-boot-tools/spring-boot-loader/pom.xml +++ b/spring-boot-tools/spring-boot-loader/pom.xml @@ -26,17 +26,6 @@ spring-core true - - org.springframework.boot - spring-boot-aether - true - - - commons-logging - commons-logging - 1.2 - true - org.springframework spring-webmvc @@ -80,29 +69,6 @@ - - org.apache.maven.plugins - maven-assembly-plugin - - - stub - prepare-package - - single - - false - - true - ${basedir}/src/assembly/launcher.xml - - - org.springframework.boot.loader.thin.ThinJarLauncher - - - - - - diff --git a/spring-boot-tools/spring-boot-loader/src/assembly/launcher.xml b/spring-boot-tools/spring-boot-loader/src/assembly/launcher.xml deleted file mode 100644 index 49301f1f5518..000000000000 --- a/spring-boot-tools/spring-boot-loader/src/assembly/launcher.xml +++ /dev/null @@ -1,28 +0,0 @@ - - exec - - jar - - false - - - - org.springframework.boot:spring-boot-aether:* - org.springframework:spring-core:* - commons-logging:commons-logging:* - - true - true - - - - - ${basedir}/target/classes - / - - **/* - - - - \ No newline at end of file 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-maven-plugin/pom.xml b/spring-boot-tools/spring-boot-maven-plugin/pom.xml index 2a0647b22646..b664c231d48d 100644 --- a/spring-boot-tools/spring-boot-maven-plugin/pom.xml +++ b/spring-boot-tools/spring-boot-maven-plugin/pom.xml @@ -124,6 +124,14 @@ org.springframework.boot spring-boot-loader-tools + + org.springframework.boot + spring-boot-loader + + + org.springframework + spring-core + org.apache.maven maven-archiver 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 index 7fefb07f0415..a40ae32f4066 100644 --- 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 @@ -57,7 +57,7 @@ public class ThinJarWrapper { private static final String DEFAULT_LAUNCHER_CLASS = "org.springframework.boot.loader.thin.ThinJarLauncher"; - private static final String DEFAULT_LIBRARY = "org.springframework.boot:spring-boot-loader:exec:1.5.0.BUILD-SNAPSHOT"; + private static final String DEFAULT_LIBRARY = "org.springframework.boot:spring-boot-aether:exec:1.5.0.BUILD-SNAPSHOT"; private Library library;