/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.model.test;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader;
import org.apache.maven.repository.internal.DefaultVersionRangeResolver;
import org.apache.maven.repository.internal.DefaultVersionResolver;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.repository.internal.SnapshotMetadataGeneratorFactory;
import org.apache.maven.repository.internal.VersionsMetadataGeneratorFactory;
import org.eclipse.aether.AbstractRepositoryListener;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositoryEvent;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.DependencyCollectionException;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.impl.ArtifactDescriptorReader;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.impl.MetadataGeneratorFactory;
import org.eclipse.aether.impl.VersionRangeResolver;
import org.eclipse.aether.impl.VersionResolver;
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;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transfer.AbstractTransferListener;
import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.transfer.TransferResource;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.util.filter.ExclusionsDependencyFilter;
import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
import org.eclipse.aether.util.version.GenericVersionScheme;
import org.eclipse.aether.version.InvalidVersionSpecificationException;
import org.eclipse.aether.version.VersionScheme;
import org.jboss.logging.Logger;
/**
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
* @author Tomaz Cerar
*/
class MavenUtil {
private final RepositorySystem REPOSITORY_SYSTEM;
private final List<RemoteRepository> remoteRepositories;
private static final String PROXY_HTTP_PREFIX = "http.";
private static final String PROXY_HTTPS_PREFIX = "https.";
private static final String PROXY_HOST = "proxyHost";
private static final String PROXY_PORT = "proxyPort";
private static MavenSettings mavenSettings = MavenSettings.getSettings();
private DefaultRepositorySystemSession session;
private static final Logger log = Logger.getLogger("maven.downloader");
private MavenUtil(RepositorySystem repositorySystem, List<RemoteRepository> remoteRepositories) {
this.REPOSITORY_SYSTEM = repositorySystem;
this.remoteRepositories = remoteRepositories;
}
static MavenUtil create(boolean useEapRepository) {
return new MavenUtil(newRepositorySystem(), createRemoteRepositories(useEapRepository));
}
URL createMavenGavURL(String artifactGav) throws MalformedURLException {
Artifact artifact = new DefaultArtifact(artifactGav);
if (artifact.getVersion() == null) {
throw new IllegalArgumentException("Null version");
}
VersionScheme versionScheme = new GenericVersionScheme();
try {
versionScheme.parseVersion(artifact.getVersion());
} catch (InvalidVersionSpecificationException e) {
throw new IllegalArgumentException(e);
}
try {
versionScheme.parseVersionRange(artifact.getVersion());
throw new IllegalArgumentException(artifact.getVersion() + " is a version range. A specific version is needed");
} catch (InvalidVersionSpecificationException expected) {
}
RepositorySystemSession session = newRepositorySystemSession();
ArtifactRequest artifactRequest = new ArtifactRequest();
artifactRequest.setArtifact(artifact);
for (RemoteRepository remoteRepo : remoteRepositories) {
artifactRequest.addRepository(remoteRepo);
}
ArtifactResult artifactResult;
try {
artifactResult = REPOSITORY_SYSTEM.resolveArtifact(session, artifactRequest);
} catch (ArtifactResolutionException e) {
throw new RuntimeException(e);
}
File file = artifactResult.getArtifact().getFile().getAbsoluteFile();
return file.toURI().toURL();
}
List<URL> createMavenGavRecursiveURLs(String artifactGav, String... excludes) throws MalformedURLException, DependencyCollectionException, DependencyResolutionException {
Artifact artifact = new DefaultArtifact(artifactGav);
if (artifact.getVersion() == null) {
throw new IllegalArgumentException("Null version");
}
VersionScheme versionScheme = new GenericVersionScheme();
try {
versionScheme.parseVersion(artifact.getVersion());
} catch (InvalidVersionSpecificationException e) {
throw new IllegalArgumentException(e);
}
try {
versionScheme.parseVersionRange(artifact.getVersion());
throw new IllegalArgumentException(artifact.getVersion() + " is a version range. A specific version is needed");
} catch (InvalidVersionSpecificationException expected) {
}
RepositorySystemSession session = newRepositorySystemSession();
ArtifactRequest artifactRequest = new ArtifactRequest();
artifactRequest.setArtifact(artifact);
for (RemoteRepository remoteRepo : remoteRepositories) {
artifactRequest.addRepository(remoteRepo);
}
ArtifactResult artifactResult;
try {
artifactResult = REPOSITORY_SYSTEM.resolveArtifact(session, artifactRequest);
} catch (ArtifactResolutionException e) {
throw new RuntimeException(e);
}
List<URL> urls = new ArrayList<>();
urls.add(artifactToUrl(artifactResult.getArtifact()));
CollectRequest collectRequest = new CollectRequest();
collectRequest.setRoot(new Dependency(artifact, "compile"));
for (RemoteRepository remoteRepo : remoteRepositories) {
collectRequest.addRepository(remoteRepo);
}
DependencyNode node = REPOSITORY_SYSTEM.collectDependencies(session, collectRequest).getRoot();
DependencyFilter filter = new ExclusionsDependencyFilter(Arrays.asList(excludes));
DependencyRequest dependencyRequest = new DependencyRequest(node, filter);
REPOSITORY_SYSTEM.resolveDependencies(session, dependencyRequest);
PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
node.accept(nlg);
for (Artifact cur : nlg.getArtifacts(false)) {
urls.add(artifactToUrl(cur));
}
log.debug("--------------------");
log.debug(nlg.getClassPath());
log.debug("--------------------");
return urls;
}
private static Integer getProxyPort(String systemProperty) {
String port = System.getProperty(systemProperty);
if (port != null && !port.isEmpty()) {
try {
Integer intPort = Integer.parseInt(port);
return intPort;
} catch (NumberFormatException e) {
return null;
}
}
return null;
}
private static List<RemoteRepository> createRemoteRepositories(boolean useEapRepository) {
// prepare proxy
String httpProxyHost = System.getProperty(String.format("%s%s", PROXY_HTTP_PREFIX, PROXY_HOST));
String httpsProxyHost = System.getProperty(String.format("%s%s", PROXY_HTTPS_PREFIX, PROXY_HOST));
Integer httpProxyPort = getProxyPort(String.format("%s%s", PROXY_HTTP_PREFIX, PROXY_PORT));
Integer httpsProxyPort = getProxyPort(String.format("%s%s", PROXY_HTTPS_PREFIX, PROXY_PORT));
Proxy httpProxy = null;
Proxy httpsProxy = null;
if (httpProxyHost != null && httpProxyPort != null && !httpProxyHost.isEmpty()) {
httpProxy = new Proxy(Proxy.TYPE_HTTP, httpProxyHost, httpProxyPort);
}
if (httpsProxyHost != null && httpsProxyPort != null && !httpsProxyHost.isEmpty()) {
httpsProxy = new Proxy(Proxy.TYPE_HTTPS, httpsProxyHost, httpsProxyPort);
}
String remoteReposFromSysProp = System.getProperty(ChildFirstClassLoaderBuilder.MAVEN_REPOSITORY_URLS);
List<RemoteRepository> remoteRepositories = new ArrayList<>();
if (remoteReposFromSysProp == null || remoteReposFromSysProp.trim().length() == 0 || remoteReposFromSysProp.startsWith("${")) {
if (useEapRepository) {
RemoteRepository.Builder repository = new RemoteRepository.Builder("product-repository", "default", "https://maven.repository.redhat.com/ga/");
if (httpsProxy != null) {
repository.setProxy(httpsProxy);
}
remoteRepositories.add(repository.build());
}
//always add jboss developer repository
RemoteRepository.Builder repository = new RemoteRepository.Builder("jboss-developer", "default", "http://repository.jboss.org/nexus/content/groups/developer/");
if (httpProxy != null) {
repository.setProxy(httpProxy);
}
remoteRepositories.add(repository.build());
//add repos from users settings.xml
List<String> remoteRepositories1 = mavenSettings.getRemoteRepositories();
for (int i = 0; i < remoteRepositories1.size(); i++) {
String repo = remoteRepositories1.get(i);
RemoteRepository.Builder myRepo = new RemoteRepository.Builder("repo-" +i, "default", repo);
if (httpProxy != null && repo.startsWith("http")) {
myRepo.setProxy(httpProxy);
}
if (httpsProxy != null && repo.startsWith("https")) {
myRepo.setProxy(httpsProxy);
}
remoteRepositories.add(myRepo.build());
}
} else {
int i = 0;
for (String repoUrl : remoteReposFromSysProp.split(",")) {
//remoteRepositories.add(new RemoteRepository("repo" + i, "default", repoUrl.trim()));
repoUrl = repoUrl.trim();
RemoteRepository.Builder repository = new RemoteRepository.Builder("repo" + i, "default", repoUrl);
if (repoUrl.startsWith("https:") && httpsProxy != null) {
repository.setProxy(httpsProxy);
}
if (repoUrl.startsWith("http:") && httpProxy != null) {
repository.setProxy(httpProxy);
}
remoteRepositories.add(repository.build());
i++;
}
}
return remoteRepositories;
}
private RepositorySystemSession newRepositorySystemSession() {
if (this.session != null){
return this.session;
}
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
LocalRepository localRepo = new LocalRepository(mavenSettings.getLocalRepository().toString());
session.setLocalRepositoryManager(REPOSITORY_SYSTEM.newLocalRepositoryManager(session, localRepo));
//Copy these from the aether demo if they are nice to have
session.setTransferListener(new ConsoleTransferListener());
session.setRepositoryListener(new ConsoleRepositoryListener());
session.setTransferListener(new AbstractTransferListener() {
@Override
public void transferFailed(TransferEvent event) {
super.transferFailed(event);
}
});
this.session = session;
return session;
}
private static URL artifactToUrl(Artifact artifact) throws MalformedURLException {
return artifact.getFile().toURI().toURL();
}
static RepositorySystem newRepositorySystem() {
/*
* Aether's components implement
* org.sonatype.aether.spi.locator.Service to ease manual wiring and
* using the prepopulated DefaultServiceLocator, we only need to
* register the repository connector factories.
*/
DefaultServiceLocator locator = new DefaultServiceLocator();
locator.addService(ArtifactDescriptorReader.class, DefaultArtifactDescriptorReader.class);
locator.addService(VersionResolver.class, DefaultVersionResolver.class);
locator.addService(VersionRangeResolver.class, DefaultVersionRangeResolver.class);
locator.addService(MetadataGeneratorFactory.class, SnapshotMetadataGeneratorFactory.class);
locator.addService(MetadataGeneratorFactory.class, VersionsMetadataGeneratorFactory.class);
locator.setErrorHandler(new MyErrorHandler());
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
//locator.addService(TransporterFactory.class, WagonTransporterFactory.class);
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
return locator.getService(RepositorySystem.class);
}
static class MyErrorHandler extends DefaultServiceLocator.ErrorHandler {
@Override
public void serviceCreationFailed(Class<?> type, Class<?> impl, Throwable exception) {
log.error("Could not create type: " + type + " impl: " + impl, exception);
}
}
private static class ConsoleRepositoryListener extends AbstractRepositoryListener {
ConsoleRepositoryListener() {
}
public void artifactDeployed(RepositoryEvent event) {
log.debug("Deployed " + event.getArtifact() + " to " + event.getRepository());
}
public void artifactDeploying(RepositoryEvent event) {
log.debug("Deploying " + event.getArtifact() + " to " + event.getRepository());
}
public void artifactDescriptorInvalid(RepositoryEvent event) {
log.debug("Invalid artifact descriptor for " + event.getArtifact() + ": " ,event.getException());
}
public void artifactDescriptorMissing(RepositoryEvent event) {
log.debug("Missing artifact descriptor for " + event.getArtifact());
}
public void artifactInstalled(RepositoryEvent event) {
log.debug("Installed " + event.getArtifact() + " to " + event.getFile());
}
public void artifactInstalling(RepositoryEvent event) {
log.debug("Installing " + event.getArtifact() + " to " + event.getFile());
}
public void artifactResolved(RepositoryEvent event) {
log.debug("Resolved artifact " + event.getArtifact() + " from " + event.getRepository());
}
public void artifactDownloading(RepositoryEvent event) {
log.debug("Downloading artifact " + event.getArtifact() + " from " + event.getRepository());
}
public void artifactDownloaded(RepositoryEvent event) {
log.debug("Downloaded artifact " + event.getArtifact() + " from " + event.getRepository());
}
public void artifactResolving(RepositoryEvent event) {
log.debug("Resolving artifact " + event.getArtifact());
}
public void metadataDeployed(RepositoryEvent event) {
log.debug("Deployed " + event.getMetadata() + " to " + event.getRepository());
}
public void metadataDeploying(RepositoryEvent event) {
log.debug("Deploying " + event.getMetadata() + " to " + event.getRepository());
}
public void metadataInstalled(RepositoryEvent event) {
log.debug("Installed " + event.getMetadata() + " to " + event.getFile());
}
public void metadataInstalling(RepositoryEvent event) {
log.debug("Installing " + event.getMetadata() + " to " + event.getFile());
}
public void metadataInvalid(RepositoryEvent event) {
log.debug("Invalid metadata " + event.getMetadata());
}
public void metadataResolved(RepositoryEvent event) {
log.debug("Resolved metadata " + event.getMetadata() + " from " + event.getRepository());
}
public void metadataResolving(RepositoryEvent event) {
log.debug("Resolving metadata " + event.getMetadata() + " from " + event.getRepository());
}
}
private static class ConsoleTransferListener extends AbstractTransferListener {
private Map<TransferResource, Long> downloads = new ConcurrentHashMap<>();
private int lastLength;
ConsoleTransferListener() {
}
@Override
public void transferInitiated(TransferEvent event) {
String message = event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploading" : "Downloading";
log.debug(message + ": " + event.getResource().getRepositoryUrl() + event.getResource().getResourceName());
}
@Override
public void transferProgressed(TransferEvent event) {
TransferResource resource = event.getResource();
downloads.put(resource, Long.valueOf(event.getTransferredBytes()));
StringBuilder buffer = new StringBuilder(64);
for (Map.Entry<TransferResource, Long> entry : downloads.entrySet()) {
long total = entry.getKey().getContentLength();
long complete = entry.getValue().longValue();
buffer.append(getStatus(complete, total)).append(" ");
}
int pad = lastLength - buffer.length();
lastLength = buffer.length();
pad(buffer, pad);
buffer.append('\r');
log.trace(buffer);
}
private String getStatus(long complete, long total) {
if (total >= 1024) {
return toKB(complete) + "/" + toKB(total) + " KB ";
} else if (total >= 0) {
return complete + "/" + total + " B ";
} else if (complete >= 1024) {
return toKB(complete) + " KB ";
} else {
return complete + " B ";
}
}
private void pad(StringBuilder buffer, int spaces) {
String block = " ";
while (spaces > 0) {
int n = Math.min(spaces, block.length());
buffer.append(block, 0, n);
spaces -= n;
}
}
@Override
public void transferSucceeded(TransferEvent event) {
transferCompleted(event);
TransferResource resource = event.getResource();
long contentLength = event.getTransferredBytes();
if (contentLength >= 0) {
String type = (event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploaded" : "Downloaded");
String len = contentLength >= 1024 ? toKB(contentLength) + " KB" : contentLength + " B";
String throughput = "";
long duration = System.currentTimeMillis() - resource.getTransferStartTime();
if (duration > 0) {
DecimalFormat format = new DecimalFormat("0.0", new DecimalFormatSymbols(Locale.ENGLISH));
double kbPerSec = (contentLength / 1024.0) / (duration / 1000.0);
throughput = " at " + format.format(kbPerSec) + " KB/sec";
}
log.debug(type + ": " + resource.getRepositoryUrl() + resource.getResourceName() + " (" + len + throughput
+ ")");
}
}
@Override
public void transferFailed(TransferEvent event) {
transferCompleted(event);
log.warn(event.getException());
}
private void transferCompleted(TransferEvent event) {
downloads.remove(event.getResource());
StringBuilder buffer = new StringBuilder(64);
pad(buffer, lastLength);
buffer.append('\r');
log.trace(buffer);
}
public void transferCorrupted(TransferEvent event) {
log.debug(event.getException());
}
long toKB(long bytes) {
return (bytes + 1023) / 1024;
}
}
}