org.springframework.cloud.function.compiler.java.DependencyResolver.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.cloud.function.compiler.java.DependencyResolver.java

Source

/*
 * Copyright 2012-2019 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
 *
 *      https://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.function.compiler.java;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.inject.Singleton;

import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
import org.apache.maven.artifact.repository.MavenArtifactRepository;
import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.DefaultModelReader;
import org.apache.maven.model.io.ModelReader;
import org.apache.maven.model.locator.DefaultModelLocator;
import org.apache.maven.model.locator.ModelLocator;
import org.apache.maven.model.validation.DefaultModelValidator;
import org.apache.maven.model.validation.ModelValidator;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.DependencyResolutionResult;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingRequest.RepositoryMerging;
import org.apache.maven.project.ProjectBuildingResult;
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.apache.maven.settings.Profile;
import org.apache.maven.settings.Repository;
import org.codehaus.plexus.ContainerConfiguration;
import org.codehaus.plexus.DefaultContainerConfiguration;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.MutablePlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.impl.ArtifactDescriptorReader;
import org.eclipse.aether.impl.MetadataGeneratorFactory;
import org.eclipse.aether.impl.VersionRangeResolver;
import org.eclipse.aether.impl.VersionResolver;
import org.eclipse.aether.impl.guice.AetherModule;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
import org.eclipse.aether.repository.ProxySelector;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.util.repository.JreProxySelector;
import org.eclipse.sisu.inject.DefaultBeanLocator;
import org.eclipse.sisu.plexus.ClassRealmManager;

import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

/**
 * Dependency resolver utility class.
 *
 * @author Andy Clement
 */
public final class DependencyResolver {

    private static DependencyResolver instance = new DependencyResolver();

    private static Properties globals;

    private final Object lock = new Object();

    private LocalRepositoryManagerFactory localRepositoryManagerFactory;

    private PlexusContainer container;

    private ProjectBuilder projectBuilder;

    private RepositorySystem repositorySystem;

    private MavenSettings settings;

    private DependencyResolver() {
    }

    public static DependencyResolver instance() {
        return instance;
    }

    public static void close() {
        instance = new DependencyResolver();
    }

    static Properties getGlobals() {
        return globals;
    }

    private void initialize() {
        if (this.container == null) {
            synchronized (this.lock) {
                if (this.container == null) {
                    ClassWorld classWorld = new ClassWorld("plexus.core",
                            Thread.currentThread().getContextClassLoader());
                    ContainerConfiguration config = new DefaultContainerConfiguration().setClassWorld(classWorld)
                            .setRealm(classWorld.getClassRealm("plexus.core"))
                            .setClassPathScanning(PlexusConstants.SCANNING_INDEX).setAutoWiring(true)
                            .setName("maven");
                    PlexusContainer container;
                    try {
                        container = new DefaultPlexusContainer(config, new AetherModule(),
                                new DependencyResolutionModule());
                        this.localRepositoryManagerFactory = container.lookup(LocalRepositoryManagerFactory.class);
                        container.addComponent(
                                new ClassRealmManager((MutablePlexusContainer) container, new DefaultBeanLocator()),
                                ClassRealmManager.class.getName());
                        this.projectBuilder = container.lookup(ProjectBuilder.class);
                        this.repositorySystem = container.lookup(RepositorySystem.class);
                    } catch (Exception e) {
                        throw new IllegalStateException("Cannot create container", e);
                    }
                    this.container = container;
                    this.settings = new MavenSettingsReader().readSettings();
                }
            }
        }
    }

    public List<Dependency> dependencies(Resource resource) {
        return dependencies(resource, new Properties());
    }

    public List<Dependency> dependencies(final Resource resource, final Properties properties) {
        initialize();
        try {
            ProjectBuildingRequest request = getProjectBuildingRequest(properties);
            request.setResolveDependencies(true);
            synchronized (DependencyResolver.class) {
                ProjectBuildingResult result = this.projectBuilder
                        .build(new PropertiesModelSource(properties, resource), request);
                DependencyResolver.globals = null;
                DependencyResolutionResult dependencies = result.getDependencyResolutionResult();
                if (!dependencies.getUnresolvedDependencies().isEmpty()) {
                    StringBuilder builder = new StringBuilder();
                    for (Dependency dependency : dependencies.getUnresolvedDependencies()) {
                        List<Exception> errors = dependencies.getResolutionErrors(dependency);
                        for (Exception exception : errors) {
                            if (builder.length() > 0) {
                                builder.append("\n");
                            }
                            builder.append(exception.getMessage());
                        }
                    }
                    throw new RuntimeException(builder.toString());
                }
                return runtime(dependencies.getDependencies());
            }
        } catch (ProjectBuildingException | NoLocalRepositoryManagerException e) {
            throw new IllegalStateException("Cannot build model", e);
        }
    }

    public File resolve(Dependency dependency) {
        initialize();
        return collectNonTransitive(Arrays.asList(dependency)).iterator().next().getArtifact().getFile();
    }

    private List<Dependency> runtime(List<Dependency> dependencies) {
        List<Dependency> list = new ArrayList<>();
        for (Dependency dependency : dependencies) {
            if (!"test".equals(dependency.getScope()) && !"provided".equals(dependency.getScope())) {
                list.add(dependency);
            }
        }
        return list;
    }

    private ProjectBuildingRequest getProjectBuildingRequest(Properties properties)
            throws NoLocalRepositoryManagerException {
        DefaultProjectBuildingRequest projectBuildingRequest = new DefaultProjectBuildingRequest();
        DefaultRepositorySystemSession session = createSession(properties);
        projectBuildingRequest.setRepositoryMerging(RepositoryMerging.REQUEST_DOMINANT);
        projectBuildingRequest.setRemoteRepositories(mavenRepositories(properties));
        projectBuildingRequest.getRemoteRepositories().addAll(mavenRepositories(this.settings));
        projectBuildingRequest.setRepositorySession(session);
        projectBuildingRequest.setProcessPlugins(false);
        projectBuildingRequest.setBuildStartTime(new Date());
        projectBuildingRequest.setUserProperties(properties);
        projectBuildingRequest.setSystemProperties(System.getProperties());
        return projectBuildingRequest;
    }

    private Collection<? extends ArtifactRepository> mavenRepositories(MavenSettings settings) {
        List<ArtifactRepository> list = new ArrayList<>();
        for (Profile profile : settings.getActiveProfiles()) {
            for (Repository repository : profile.getRepositories()) {
                addRepositoryIfMissing(list, repository.getId(), repository.getUrl(),
                        repository.getReleases() != null ? repository.getReleases().isEnabled() : true,
                        repository.getSnapshots() != null ? repository.getSnapshots().isEnabled() : false);
            }
        }
        return list;
    }

    private List<ArtifactRepository> mavenRepositories(Properties properties) {
        List<ArtifactRepository> list = new ArrayList<>();
        addRepositoryIfMissing(list, "spring-snapshots", "https://repo.spring.io/libs-snapshot", true, true);
        addRepositoryIfMissing(list, "central", "https://repo1.maven.org/maven2", true, false);
        return list;
    }

    private List<RemoteRepository> aetherRepositories(Properties properties) {
        List<RemoteRepository> list = new ArrayList<>();
        for (ArtifactRepository input : mavenRepositories(properties)) {
            list.add(remote(input));
        }
        return list;
    }

    private RemoteRepository remote(ArtifactRepository input) {
        return new RemoteRepository.Builder(input.getId(), input.getLayout().getId(), input.getUrl())
                .setSnapshotPolicy(policy(input.getSnapshots())).setReleasePolicy(policy(input.getReleases()))
                .build();
    }

    private RepositoryPolicy policy(ArtifactRepositoryPolicy input) {
        RepositoryPolicy policy = new RepositoryPolicy(input.isEnabled(), RepositoryPolicy.UPDATE_POLICY_DAILY,
                RepositoryPolicy.CHECKSUM_POLICY_WARN);
        return policy;
    }

    private void addRepositoryIfMissing(List<ArtifactRepository> list, String id, String url, boolean releases,
            boolean snapshots) {
        for (ArtifactRepository repo : list) {
            if (url.equals(repo.getUrl())) {
                return;
            }
            if (id.equals(repo.getId())) {
                return;
            }
        }
        list.add(repo(id, url, releases, snapshots));
    }

    private ArtifactRepository repo(String id, String url, boolean releases, boolean snapshots) {
        MavenArtifactRepository repository = new MavenArtifactRepository();
        repository.setLayout(new DefaultRepositoryLayout());
        repository.setId(id);
        repository.setUrl(url);
        ArtifactRepositoryPolicy enabled = new ArtifactRepositoryPolicy();
        enabled.setEnabled(true);
        ArtifactRepositoryPolicy disabled = new ArtifactRepositoryPolicy();
        disabled.setEnabled(false);
        repository.setReleaseUpdatePolicy(releases ? enabled : disabled);
        repository.setSnapshotUpdatePolicy(snapshots ? enabled : disabled);
        return repository;
    }

    private DefaultRepositorySystemSession createSession(Properties properties)
            throws NoLocalRepositoryManagerException {
        DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
        LocalRepository repository = localRepository(properties);
        session.setLocalRepositoryManager(this.localRepositoryManagerFactory.newInstance(session, repository));
        applySettings(session);
        ProxySelector existing = session.getProxySelector();
        if (existing == null || !(existing instanceof CompositeProxySelector)) {
            JreProxySelector fallback = new JreProxySelector();
            ProxySelector selector = existing == null ? fallback
                    : new CompositeProxySelector(Arrays.asList(existing, fallback));
            session.setProxySelector(selector);
        }
        return session;
    }

    private void applySettings(DefaultRepositorySystemSession session) {
        MavenSettingsReader.applySettings(this.settings, session);
    }

    private LocalRepository localRepository(Properties properties) {
        return new LocalRepository(getM2RepoDirectory());
    }

    public Model readModel(Resource resource) {
        return readModel(resource, new Properties());
    }

    public Model readModel(final Resource resource, final Properties properties) {
        initialize();
        try {
            ProjectBuildingRequest request = getProjectBuildingRequest(properties);
            request.setResolveDependencies(false);
            ProjectBuildingResult result = this.projectBuilder
                    .build(new PropertiesModelSource(properties, resource), request);
            return result.getProject().getModel();
        } catch (Exception e) {
            throw new IllegalStateException("Failed to build model from effective pom", e);
        }
    }

    private File getM2RepoDirectory() {
        return new File(getDefaultM2HomeDirectory(), "repository");
    }

    private File getDefaultM2HomeDirectory() {
        String mavenRoot = System.getProperty("maven.home");
        if (StringUtils.hasLength(mavenRoot)) {
            return new File(mavenRoot);
        }
        return new File(System.getProperty("user.home"), ".m2");
    }

    private List<ArtifactResult> collectNonTransitive(List<Dependency> dependencies) {
        try {
            List<ArtifactRequest> artifactRequests = getArtifactRequests(dependencies);
            List<ArtifactResult> result = this.repositorySystem.resolveArtifacts(createSession(new Properties()),
                    artifactRequests);
            return result;
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
    }

    private List<ArtifactRequest> getArtifactRequests(List<Dependency> dependencies) {
        List<ArtifactRequest> list = new ArrayList<>();
        for (Dependency dependency : dependencies) {
            ArtifactRequest request = new ArtifactRequest(dependency.getArtifact(), null, null);
            request.setRepositories(aetherRepositories(new Properties()));
            list.add(request);
        }
        return list;
    }

    @SuppressWarnings("deprecation")
    private static final class PropertiesModelSource implements org.apache.maven.model.building.ModelSource {

        private final Properties properties;

        private final Resource resource;

        private PropertiesModelSource(Properties properties, Resource resource) {
            this.properties = properties;
            this.resource = resource;
        }

        @Override
        public InputStream getInputStream() throws IOException {
            DependencyResolver.globals = this.properties;
            return new BufferedInputStream(this.resource.getInputStream()) {
                @Override
                public void close() throws IOException {
                    DependencyResolver.globals = null;
                    super.close();
                }
            };
        }

        @Override
        public String getLocation() {
            return this.resource.getDescription();
        }

    }

}

class DependencyResolutionModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(ModelLocator.class).to(DefaultModelLocator.class).in(Singleton.class);
        bind(ModelReader.class).to(DefaultModelReader.class).in(Singleton.class);
        bind(ModelValidator.class).to(DefaultModelValidator.class).in(Singleton.class);
        bind(RepositoryConnectorFactory.class).to(BasicRepositoryConnectorFactory.class).in(Singleton.class);
        bind(ArtifactDescriptorReader.class) //
                .to(DefaultArtifactDescriptorReader.class).in(Singleton.class);
        bind(VersionResolver.class) //
                .to(DefaultVersionResolver.class).in(Singleton.class);
        bind(VersionRangeResolver.class) //
                .to(DefaultVersionRangeResolver.class).in(Singleton.class);
        bind(MetadataGeneratorFactory.class).annotatedWith(Names.named("snapshot")) //
                .to(SnapshotMetadataGeneratorFactory.class).in(Singleton.class);
        bind(MetadataGeneratorFactory.class).annotatedWith(Names.named("versions")) //
                .to(VersionsMetadataGeneratorFactory.class).in(Singleton.class);
        bind(TransporterFactory.class).annotatedWith(Names.named("http")).to(HttpTransporterFactory.class)
                .in(Singleton.class);
        bind(TransporterFactory.class).annotatedWith(Names.named("file")).to(FileTransporterFactory.class)
                .in(Singleton.class);
    }

    @Provides
    @Singleton
    Set<MetadataGeneratorFactory> provideMetadataGeneratorFactories(
            @Named("snapshot") MetadataGeneratorFactory snapshot,
            @Named("versions") MetadataGeneratorFactory versions) {
        Set<MetadataGeneratorFactory> factories = new HashSet<>();
        factories.add(snapshot);
        factories.add(versions);
        return Collections.unmodifiableSet(factories);
    }

    @Provides
    @Singleton
    Set<RepositoryConnectorFactory> provideRepositoryConnectorFactories(RepositoryConnectorFactory factory) {
        return Collections.singleton(factory);
    }

    @Provides
    @Singleton
    Set<TransporterFactory> provideTransporterFactories(@Named("file") TransporterFactory file,
            @Named("http") TransporterFactory http) {
        // Order is decided elsewhere (by priority)
        Set<TransporterFactory> factories = new HashSet<TransporterFactory>();
        factories.add(file);
        factories.add(http);
        return Collections.unmodifiableSet(factories);
    }

}