Java tutorial
/* * 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); } }