All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertx.stack.resolver.ResolverImpl Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright (c) 2011-2015 The original author or authors
 *  ------------------------------------------------------
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *       The Eclipse Public License is available at
 *       http://www.eclipse.org/legal/epl-v10.html
 *
 *       The Apache License v2.0 is available at
 *       http://www.opensource.org/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 */

package io.vertx.stack.resolver;


import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.stack.model.Artifact;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.collection.CollectRequest;
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.graph.Exclusion;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.repository.*;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
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.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.eclipse.aether.util.repository.AuthenticationBuilder;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * An implementation of {@link Resolver} based on Aether.
 *
 * @author Clement Escoffier
 */
public class ResolverImpl implements Resolver {

  private final static Logger LOGGER = LoggerFactory.getLogger("vertx-stack-resolver");

  public static final String REMOTE_SNAPSHOT_POLICY_SYS_PROP = "vertx.maven.remoteSnapshotPolicy";

  private final RepositorySystem system;
  private final LocalRepository localRepo;
  private final List remotes = new ArrayList<>();

  /**
   * Creates a new instance of {@link ResolverImpl} with the given options.
   *
   * @param options the options
   */
  public ResolverImpl(ResolverOptions options) {
    String localMavenRepo = options.getLocalRepository();
    List remoteMavenRepos = options.getRemoteRepositories();
    String httpProxy = options.getHttpProxy();
    String httpsProxy = options.getHttpsProxy();

    DefaultServiceLocator locator = getDefaultServiceLocator();

    system = locator.getService(RepositorySystem.class);
    localRepo = new LocalRepository(localMavenRepo);
    Proxy proxy = getHttpProxy(httpProxy);
    Proxy secureProxy = getHttpsProxy(httpsProxy);

    configureRemoteRepositories(remoteMavenRepos, proxy, secureProxy);
  }

  private Proxy getHttpsProxy(String httpsProxy) {
    Proxy secureProxy = null;
    if (httpsProxy != null) {
      URL url = url(httpsProxy);
      Authentication authentication = extractAuth(url);
      secureProxy = new Proxy("https", url.getHost(), url.getPort(), authentication);
    }
    return secureProxy;
  }

  private Proxy getHttpProxy(String httpProxy) {
    Proxy proxy = null;
    if (httpProxy != null) {
      URL url = url(httpProxy);
      Authentication authentication = extractAuth(url);
      proxy = new Proxy("http", url.getHost(), url.getPort(), authentication);
    }
    return proxy;
  }

  private void configureRemoteRepositories(List remoteMavenRepos, Proxy proxy, Proxy secureProxy) {
    int count = 0;
    for (String remote : remoteMavenRepos) {
      URL url = url(remote);
      Authentication auth = extractAuth(url);
      if (auth != null) {
        url = url(url.getProtocol(), url.getHost(), url.getPort(), url.getFile());
      }
      RemoteRepository.Builder builder = new RemoteRepository.Builder("repo" + (count++), "default", url.toString());
      if (auth != null) {
        builder.setAuthentication(auth);
      }
      switch (url.getProtocol()) {
        case "http":
          if (proxy != null) {
            builder.setProxy(proxy);
          }
          break;
        case "https":
          if (secureProxy != null) {
            builder.setProxy(secureProxy);
          }
          break;
      }
      customizeRemoteRepoBuilder(builder);
      RemoteRepository remoteRepo = builder.build();
      remotes.add(remoteRepo);
    }
  }

  private static DefaultServiceLocator getDefaultServiceLocator() {
    DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
    locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
    locator.addService(TransporterFactory.class, FileTransporterFactory.class);
    locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
    locator.setErrorHandler(new DefaultServiceLocator.ErrorHandler() {
      @Override
      public void serviceCreationFailed(Class type, Class impl, Throwable exception) {
        LOGGER.error("Service creation failure: " + exception.getMessage(), exception);
      }
    });
    return locator;
  }

  private URL url(String u) {
    try {
      return new URL(u);
    } catch (MalformedURLException e) {
      LOGGER.error("Cannot create url from " + u, e);
      throw new IllegalArgumentException("Invalid url " + u);
    }
  }

  private URL url(String protocol, String host, int port, String file) {
    try {
      return new URL(protocol, host, port, file);
    } catch (MalformedURLException e) {
      final String url = "{protocol:" + protocol + ", host:" + host + ", port:" + port + ", file:" + file + "}";
      LOGGER.error("Cannot create url from " + url, e);
      throw new IllegalArgumentException("Invalid url " + url);
    }
  }

  /**
   * Resolve the given artifact.
   *
   * @param artifact   the artifact
   * @param withTransitive whether the transitive dependencies needs to be resolved too
   * @param exclusions the list of exclusions
   * @return the list of resolved artifacts
   */
  private DependencyNode resolve(Artifact artifact, boolean withTransitive, List exclusions) {
    CollectRequest collectRequest = collectRequest(artifact, exclusions, remotes);
    DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, dependencyFilter());
    RepositorySystemSession session = session(system, localRepo);
    try {
      DependencyResult dependencyResult = system.resolveDependencies(session, dependencyRequest);
      DependencyNode root = dependencyResult.getRoot();
      if (withTransitive) {
        return root;
      } else {
        root.setChildren(new ArrayList<>());
        return root;
      }
    } catch (DependencyResolutionException e) {
      throw new IllegalArgumentException("Cannot resolve artifact " + artifact.toString() +
        " in maven repositories: " + e.getMessage());
    }
  }

  protected void customizeRemoteRepoBuilder(RemoteRepository.Builder builder) {
    String updatePolicy = System.getProperty(REMOTE_SNAPSHOT_POLICY_SYS_PROP);
    if (updatePolicy != null && !updatePolicy.isEmpty()) {
      builder.setSnapshotPolicy(new RepositoryPolicy(true, updatePolicy, RepositoryPolicy.CHECKSUM_POLICY_WARN));
    }
  }

  private static Authentication extractAuth(URL url) {
    String userInfo = url.getUserInfo();
    if (userInfo != null) {
      AuthenticationBuilder authBuilder = new AuthenticationBuilder();
      int sep = userInfo.indexOf(':');
      if (sep != -1) {
        authBuilder.addUsername(userInfo.substring(0, sep));
        authBuilder.addPassword(userInfo.substring(sep + 1));
      } else {
        authBuilder.addUsername(userInfo);
      }
      return authBuilder.build();
    }
    return null;
  }

  private static RepositorySystemSession session(RepositorySystem system, LocalRepository localRepo) {
    DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
    session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
    return session;
  }

  private static CollectRequest collectRequest(Artifact artifact, List exclusions, List remotes) {
    CollectRequest collectRequest = new CollectRequest();
    Dependency root = new Dependency(artifact, JavaScopes.COMPILE)
      .setExclusions(
        exclusions.stream()
          .map(e -> {
            // Exclusion are structured as groupId:artifactId.
            String[] segments = e.split(":");
            if (segments.length != 2) {
              throw new IllegalStateException("Invalid exclusion format: " + e + " - exclusion are " +
                "structured as follows: groupId:artifactId");
            }
            return new Exclusion(segments[0], segments[1], null, null);
          })
          .collect(Collectors.toList()));
    collectRequest.setRoot(root);
    collectRequest.setRepositories(remotes);
    return collectRequest;
  }

  private static DependencyFilter dependencyFilter() {
    return
      DependencyFilterUtils.andFilter(
        DependencyFilterUtils.classpathFilter(
          JavaScopes.COMPILE
        ),
        // Remove optionals and dependencies of optionals
        (dependencyNode, list) -> {
          for (DependencyNode parent : list) {
            if (parent.getDependency().isOptional()) {
              return false;
            }
          }

          return !dependencyNode.getDependency().isOptional();
        },

        // Remove excluded dependencies
        (dependencyNode, list) -> {
          // Build the list of exclusion, traverse the tree.
          Collection ex = new ArrayList<>();
          for (DependencyNode parent : list) {
            ex.addAll(parent.getDependency().getExclusions());
          }

          for (Exclusion e : ex) {
            // Check the the passed artifact is excluded
            if (e.getArtifactId().equals(dependencyNode.getArtifact().getArtifactId())
              && e.getGroupId().equals(dependencyNode.getArtifact().getGroupId())) {
              return false;
            }

            // Check if a parent artifact is excluded
            for (DependencyNode parent : list) {
              if (e.getArtifactId().equals(parent.getArtifact().getArtifactId())
                && e.getGroupId().equals(parent.getArtifact().getGroupId())) {
                return false;
              }
            }
          }
          return true;
        },

        // Remove provided dependencies and transitive dependencies of provided dependencies
        (dependencyNode, list) -> {
          for (DependencyNode parent : list) {
            if (!parent.getDependency().getScope().equalsIgnoreCase("compile")) {
              return false;
            }
          }
          return dependencyNode.getDependency().getScope().equalsIgnoreCase("compile");
        }
      );
  }

  @Override
  public List resolve(String gacv, ResolutionOptions options) {
    DependencyNode root = resolve(new Artifact(gacv), options.isWithTransitive(), options.getExclusions());
    List exclusions = Stream.concat(Stream.of(root), root.getChildren().stream())
      .map(DependencyNode::getDependency)
      .flatMap(dependency -> dependency.getExclusions().stream())
      .collect(Collectors.toList());
    Artifact rootArtifact = new Artifact(root.getArtifact(), null);
    return Stream
      .concat(Stream.of(rootArtifact), toArtifacts(root, rootArtifact, exclusions))
      .collect(Collectors.toList());
  }

  private Stream toArtifacts(DependencyNode dependencyNode, Artifact rootArtifact, List exclusions) {
    return dependencyNode.getChildren().stream()
      // remove optional dependencies
      .filter(childNode -> !childNode.getDependency().isOptional())
      // remove excluded dependencies
      .filter(childNode -> exclusions.stream().noneMatch(exclusion ->
        exclusion.getGroupId().equals(childNode.getArtifact().getGroupId())
          && exclusion.getArtifactId().equals(childNode.getArtifact().getArtifactId())))
      // remove provided dependencies and transitive dependencies of provided dependencies
      .filter(childNode -> childNode.getDependency().getScope().equalsIgnoreCase("compile"))
      .flatMap(childNode -> {
        Artifact childArtifact = new Artifact(childNode.getArtifact(), rootArtifact);
        return Stream.concat(Stream.of(childArtifact), toArtifacts(childNode, childArtifact, exclusions));
      });
  }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy