Support proxy settings for Aether Module resolver

- Add `AetherProxyProperties` as a ConfigurationProperties for the proxy settings needed for the ProxySelector in Aether system
 - Update proxy settings in two places:
   1) Where the remote repositories are setup
   2) WHere the repository system session is created to resolve the artifacts

This resolves #272

Set authentication only for proxy settings

Set port type to `int`
This commit is contained in:
Ilayaperumal Gopinathan
2016-01-20 16:49:16 +05:30
committed by Marius Bogoevici
parent 9c3eef49da
commit b98d91da39
5 changed files with 200 additions and 20 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-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.
@@ -34,7 +34,11 @@ import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.Authentication;
import org.eclipse.aether.repository.AuthenticationContext;
import org.eclipse.aether.repository.AuthenticationDigest;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.Proxy;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
@@ -47,6 +51,7 @@ import org.eclipse.aether.spi.connector.transport.TransporterFactory;
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.repository.DefaultProxySelector;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
@@ -63,6 +68,7 @@ import org.springframework.util.StringUtils;
* @author David Turanski
* @author Mark Fisher
* @author Marius Bogoevici
* @author Ilayaperumal Gopinathan
*/
public class AetherModuleResolver implements ModuleResolver {
@@ -78,13 +84,19 @@ public class AetherModuleResolver implements ModuleResolver {
private volatile boolean offline = false;
private final AetherProxyProperties proxyProperties;
private Authentication authentication;
/**
* Create an instance specifying the locations of the local and remote repositories.
* @param localRepository the root path of the local maven repository
* @param remoteRepositories a Map containing pairs of (repository ID,repository URL). This
* may be null or empty if the local repository is off line.
* @param proxyProperties the proxy properties for the maven proxy settings.
*/
public AetherModuleResolver(File localRepository, Map<String, String> remoteRepositories) {
public AetherModuleResolver(File localRepository, Map<String, String> remoteRepositories,
final AetherProxyProperties proxyProperties) {
Assert.notNull(localRepository, "Local repository path cannot be null");
if (log.isDebugEnabled()) {
log.debug("Local repository: " + localRepository);
@@ -93,6 +105,22 @@ public class AetherModuleResolver implements ModuleResolver {
log.debug("Remote repositories: " + StringUtils.collectionToCommaDelimitedString(remoteRepositories.values()));
}
}
this.proxyProperties = proxyProperties;
if (isProxyEnabled() && proxyHasCredentials()) {
this.authentication = new Authentication() {
@Override
public void fill(AuthenticationContext context, String key, Map<String, String> data) {
context.put(context.USERNAME, proxyProperties.getAuth().getUsername());
context.put(context.PASSWORD, proxyProperties.getAuth().getPassword());
}
@Override
public void digest(AuthenticationDigest digest) {
digest.update(AuthenticationContext.USERNAME, proxyProperties.getAuth().getUsername(),
AuthenticationContext.PASSWORD, proxyProperties.getAuth().getPassword());
}
};
}
if (!localRepository.exists()) {
Assert.isTrue(localRepository.mkdirs(),
"Unable to create directory for local repository: " + localRepository);
@@ -101,14 +129,38 @@ public class AetherModuleResolver implements ModuleResolver {
this.remoteRepositories = new LinkedList<>();
if (!CollectionUtils.isEmpty(remoteRepositories)) {
for (Map.Entry<String, String> remoteRepo : remoteRepositories.entrySet()) {
RemoteRepository remoteRepository = new RemoteRepository.Builder(remoteRepo.getKey(),
DEFAULT_CONTENT_TYPE, remoteRepo.getValue()).build();
this.remoteRepositories.add(remoteRepository);
RemoteRepository.Builder remoteRepositoryBuilder = new RemoteRepository.Builder(remoteRepo.getKey(),
DEFAULT_CONTENT_TYPE, remoteRepo.getValue());
if (this.authentication != null) {
//todo: Set direct authentication for the remote repositories
remoteRepositoryBuilder.setProxy(new Proxy(proxyProperties.getProtocol(), proxyProperties.getHost(),
proxyProperties.getPort(), authentication));
}
this.remoteRepositories.add(remoteRepositoryBuilder.build());
}
}
repositorySystem = newRepositorySystem();
}
/**
* Check if the proxy settings are provided.
*
* @return boolean true if the proxy settings are provided.
*/
private boolean isProxyEnabled() {
return (this.proxyProperties != null && this.proxyProperties.getHost() != null && proxyProperties.getPort() > 0);
}
/**
* Check if the proxy setting has username/password set.
*
* @return boolean true if both the username/password are set
*/
private boolean proxyHasCredentials() {
return (this.proxyProperties != null && this.proxyProperties.getAuth() != null &&
this.proxyProperties.getAuth().getUsername() != null && this.proxyProperties.getAuth().getPassword() != null);
}
public void setOffline(boolean offline) {
this.offline = offline;
}
@@ -133,6 +185,13 @@ public class AetherModuleResolver implements ModuleResolver {
LocalRepository localRepo = new LocalRepository(localRepoPath);
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
session.setOffline(this.offline);
if (isProxyEnabled()) {
DefaultProxySelector proxySelector = new DefaultProxySelector();
Proxy proxy = new Proxy(proxyProperties.getProtocol(), proxyProperties.getHost(), proxyProperties.getPort(),
authentication);
proxySelector.add(proxy, proxyProperties.getNonProxyHosts());
session.setProxySelector(proxySelector);
}
return session;
}
@@ -214,7 +273,8 @@ public class AetherModuleResolver implements ModuleResolver {
result.add(toResource(artifactResult));
}
}
} catch (DependencyResolutionException e) {
}
catch (DependencyResolutionException e) {
throw new RuntimeException(e);
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 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.cloud.stream.module.resolver;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Proxy properties for the Aether Module Resolver.
*
* @author Ilayaperumal Gopinathan
*/
@ConfigurationProperties(prefix = "aether.proxy")
public class AetherProxyProperties {
/**
* Protocol to use for proxy settings.
*/
private String protocol = "http";
/**
* Host for the proxy.
*/
private String host;
/**
* Port for the proxy.
*/
private int port;
/**
* List of non proxy hosts.
*/
private String nonProxyHosts;
private Authentication auth;
public String getProtocol() {
return this.protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getHost() {
return this.host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return this.port;
}
public void setPort(int port) {
this.port = port;
}
public String getNonProxyHosts() {
return this.nonProxyHosts;
}
public void setNonProxyHosts(String nonProxyHosts) {
this.nonProxyHosts = nonProxyHosts;
}
public Authentication getAuth() {
return this.auth;
}
public void setAuth(Authentication auth) {
this.auth = auth;
}
public static class Authentication {
/**
* Username for the proxy.
*/
private String username;
/**
* Password for the proxy.
*/
private String password;
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-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.
@@ -28,22 +28,27 @@ import org.springframework.context.annotation.Bean;
* Sets up the default Aether-based module resolver, unless overridden.
*
* @author Eric Bottard
* @author Ilayaperumal Gopinathan
*/
@EnableConfigurationProperties(ModuleResolverProperties.class)
@EnableConfigurationProperties({ModuleResolverProperties.class, AetherProxyProperties.class})
public class ModuleResolverConfiguration {
@Autowired
private ModuleResolverProperties properties;
@Autowired
private AetherProxyProperties proxyProperties;
@Bean
@ConditionalOnMissingBean(ModuleResolver.class)
public ModuleResolver moduleResolver() {
int i = 1;
Map<String, String> repositoriesMap = new HashMap<>();
for (String repository: properties.getRemoteRepositories()) {
for (String repository : properties.getRemoteRepositories()) {
repositoriesMap.put("repository " + i++, repository);
}
AetherModuleResolver aetherModuleResolver = new AetherModuleResolver(properties.getLocalRepository(), repositoriesMap);
AetherModuleResolver aetherModuleResolver = new AetherModuleResolver(properties.getLocalRepository(),
repositoriesMap, proxyProperties);
aetherModuleResolver.setOffline(properties.isOffline());
return aetherModuleResolver;
}

View File

@@ -51,6 +51,7 @@ public class ModuleResolverProperties {
public String[] getRemoteRepositories() {
return remoteRepositories;
}
public void setLocalRepository(File localRepository) {
this.localRepository = localRepository;
}

View File

@@ -20,14 +20,10 @@ 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.arrayContaining;
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.hamcrest.object.HasToString.hasToString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -65,7 +61,7 @@ public class AetherModuleResolverTests {
public void testResolveLocal() throws IOException {
ClassPathResource cpr = new ClassPathResource("local-repo");
File localRepository = cpr.getFile();
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, null);
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, null, null);
Resource resource = defaultModuleResolver.resolve(new Coordinates("foo.bar", "foo-bar", "jar", "", "1.0.0"));
assertTrue(resource.exists());
assertEquals(resource.getFile().getName(), "foo-bar-1.0.0.jar");
@@ -75,7 +71,7 @@ public class AetherModuleResolverTests {
public void testResolveLocalWithIncludes() throws IOException {
ClassPathResource cpr = new ClassPathResource("local-repo");
File localRepository = cpr.getFile();
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, null);
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, null, null);
Resource[] resources = defaultModuleResolver.resolve(
new Coordinates("foo.bar", "foo-bar", "jar", "", "1.0.0"),
new Coordinates[]{new Coordinates("qux.bar", "qux-bar", "jar", "", "1.0.0")},new String[]{});
@@ -92,7 +88,7 @@ public class AetherModuleResolverTests {
public void testResolveDoesNotExist() throws IOException {
ClassPathResource cpr = new ClassPathResource("local-repo");
File localRepository = cpr.getFile();
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, null);
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, null, null);
defaultModuleResolver.resolve(new Coordinates("niente", "nada", "jar", "", "zilch"));
}
@@ -103,7 +99,7 @@ public class AetherModuleResolverTests {
localRepository.deleteOnExit();
Map<String, String> remoteRepos = new HashMap<>();
remoteRepos.put("modules", "http://repo.spring.io/libs-snapshot");
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos);
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos, null);
Resource resource = defaultModuleResolver.resolve(
new Coordinates("org.springframework.cloud.stream.module", "time-source", "jar", "exec", "1.0.0.BUILD-SNAPSHOT"));
assertTrue(resource.exists());
@@ -123,7 +119,7 @@ public class AetherModuleResolverTests {
.willReturn(aResponse()
.withStatus(200)
.withBodyFile(stubFileName)));
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos);
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos, null);
Resource resource = defaultModuleResolver.resolve(new Coordinates("org.bar", "foo", "jar", "", "1.0.0"));
assertTrue(resource.exists());
assertEquals(resource.getFile().getName(), "foo-1.0.0.jar");
@@ -143,7 +139,7 @@ public class AetherModuleResolverTests {
.willReturn(aResponse()
.withStatus(200)
.withBodyFile(stubFileName)));
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos);
AetherModuleResolver defaultModuleResolver = new AetherModuleResolver(localRepository, remoteRepos, null);
defaultModuleResolver.setOffline(true);
defaultModuleResolver.resolve(new Coordinates("org.bar", "foo", "jar", "", "1.0.0"));
} catch (RuntimeException e) {