diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherConfiguration.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherConfiguration.java index 5b921ac59..8584e4ce0 100644 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherConfiguration.java +++ b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/launcher/ModuleLauncherConfiguration.java @@ -53,7 +53,9 @@ public class ModuleLauncherConfiguration { for (String repository: properties.getRemoteRepositories()) { repositoriesMap.put("repository " + i++, repository); } - return new AetherModuleResolver(properties.getLocalRepository(), repositoriesMap); + AetherModuleResolver aetherModuleResolver = new AetherModuleResolver(properties.getLocalRepository(), repositoriesMap); + aetherModuleResolver.setOffline(properties.isOffline()); + return aetherModuleResolver; } @Bean diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolver.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolver.java index c780b64dc..361f9c413 100644 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolver.java +++ b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolver.java @@ -68,6 +68,8 @@ public class AetherModuleResolver implements ModuleResolver { private final RepositorySystem repositorySystem; + private volatile boolean offline = false; + /** * Create an instance specifying the locations of the local and remote repositories. * @param localRepository the root path of the local maven repository @@ -99,6 +101,10 @@ public class AetherModuleResolver implements ModuleResolver { repositorySystem = newRepositorySystem(); } + public void setOffline(boolean offline) { + this.offline = offline; + } + /** * Resolve an artifact and return its location in the local repository. Aether performs the normal * Maven resolution process ensuring that the latest update is cached to the local repository. @@ -107,8 +113,8 @@ public class AetherModuleResolver implements ModuleResolver { * @param extension the file extension * @param classifier classifier can be null if none * @param version the version - * @return a {@ link FileSystemResource} representing the resolved artifact in the local repository - * @throws a RuntimeException if the artifact does not exist or the resolution fails + * @return a {@link FileSystemResource} representing the resolved artifact in the local repository + * @throws RuntimeException if the artifact does not exist or the resolution fails */ @Override public Resource resolve(String groupId, String artifactId, String extension, String classifier, String version) { @@ -141,6 +147,7 @@ public class AetherModuleResolver implements ModuleResolver { DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); LocalRepository localRepo = new LocalRepository(localRepoPath); session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo)); + session.setOffline(this.offline); return session; } diff --git a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolverProperties.java b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolverProperties.java index 0036a0412..4801dd481 100644 --- a/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolverProperties.java +++ b/spring-cloud-stream-module-launcher/src/main/java/org/springframework/cloud/stream/module/resolver/ModuleResolverProperties.java @@ -39,6 +39,11 @@ public class ModuleResolverProperties { */ private String[] remoteRepositories = new String[] {"https://repo.spring.io/libs-snapshot"}; + /** + * Whether the resolver should operate in offline mode. + */ + private boolean offline = false; + public void setRemoteRepositories(String[] remoteRepositories) { this.remoteRepositories = remoteRepositories; } @@ -54,4 +59,11 @@ public class ModuleResolverProperties { return localRepository; } + public boolean isOffline() { + return offline; + } + + public void setOffline(boolean offline) { + this.offline = offline; + } } diff --git a/spring-cloud-stream-module-launcher/src/test/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolverTests.java b/spring-cloud-stream-module-launcher/src/test/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolverTests.java index 40e85c5af..6725e4d79 100644 --- a/spring-cloud-stream-module-launcher/src/test/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolverTests.java +++ b/spring-cloud-stream-module-launcher/src/test/java/org/springframework/cloud/stream/module/resolver/AetherModuleResolverTests.java @@ -20,23 +20,31 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.util.HashMap; import java.util.Map; +import java.util.UUID; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.hamcrest.Matchers; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.util.SocketUtils; import com.github.tomakehurst.wiremock.junit.WireMockRule; +import wiremock.org.mortbay.resource.FileResource; /** * @author David Turanski @@ -69,8 +77,8 @@ public class AetherModuleResolverTests { @Test @Ignore public void testResolveRemote() throws IOException { - ClassPathResource cpr = new ClassPathResource("local-repo"); - File localRepository = cpr.getFile(); + File localRepository = Files.createTempDirectory(UUID.randomUUID().toString()).toFile(); + localRepository.deleteOnExit(); Map remoteRepos = new HashMap<>(); remoteRepos.put("modules", "http://repo.spring.io/libs-snapshot"); AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos); @@ -82,8 +90,8 @@ public class AetherModuleResolverTests { @Test public void testResolveMockRemote() throws IOException { - ClassPathResource cpr = new ClassPathResource("local-repo"); - File localRepository = cpr.getFile(); + File localRepository = Files.createTempDirectory(UUID.randomUUID().toString()).toFile(); + localRepository.deleteOnExit(); ClassPathResource stubJarResource = new ClassPathResource("__files/foo.jar"); String stubFileName = stubJarResource.getFile().getName(); Map remoteRepos = new HashMap<>(); @@ -98,4 +106,27 @@ public class AetherModuleResolverTests { assertTrue(resource.exists()); assertEquals(resource.getFile().getName(), "foo-1.0.0.jar"); } + + @Test + public void testResolveOfflineWithMockRemote() throws IOException { + try { + File localRepository = Files.createTempDirectory(UUID.randomUUID().toString()).toFile(); + localRepository.deleteOnExit(); + ClassPathResource stubJarResource = new ClassPathResource("__files/foo.jar"); + String stubFileName = stubJarResource.getFile().getName(); + Map remoteRepos = new HashMap<>(); + remoteRepos.put("repo0", "http://localhost:" + port + "/repo0"); + remoteRepos.put("repo1", "http://localhost:" + port + "/repo1"); + stubFor(get(urlEqualTo("/repo1/org/bar/foo/1.0.0/foo-1.0.0.jar")) + .willReturn(aResponse() + .withStatus(200) + .withBodyFile(stubFileName))); + AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos); + defaultModuleResolver.setOffline(true); + defaultModuleResolver.resolve("org.bar", "foo", "jar", "", "1.0.0"); + } catch (RuntimeException e) { + // remote resolution fails because the resolver is operating offline + assertThat(e.getCause(), instanceOf(ArtifactResolutionException.class)); + } + } }