Allow dependencies to be specified when compiling, using maven coords
This commit is contained in:
@@ -46,6 +46,31 @@
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-core</artifactId>
|
||||
<version>3.3.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-embedder</artifactId>
|
||||
<version>3.3.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.aether</groupId>
|
||||
<artifactId>aether-transport-file</artifactId>
|
||||
<version>1.0.2.v20150114</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.aether</groupId>
|
||||
<artifactId>aether-transport-http</artifactId>
|
||||
<version>1.0.2.v20150114</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.aether</groupId>
|
||||
<artifactId>aether-connector-basic</artifactId>
|
||||
<version>1.0.2.v20150114</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.function.compiler.java;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -41,6 +42,8 @@ public class CompilationResult {
|
||||
List<Class<?>> compiledClasses = new ArrayList<>();
|
||||
|
||||
private Map<String, byte[]> classBytes = new HashMap<>();
|
||||
|
||||
private List<File> resolvedAdditionalDependencies = new ArrayList<>();
|
||||
|
||||
public CompilationResult(boolean successfulCompilation) {
|
||||
this.successfulCompilation = successfulCompilation;
|
||||
@@ -49,6 +52,14 @@ public class CompilationResult {
|
||||
public void addClassBytes(String name, byte[] bytes) {
|
||||
this.classBytes.put(name, bytes);
|
||||
}
|
||||
|
||||
public void setResolvedAdditionalDependencies(List<File> resolvedAdditionalDependencies) {
|
||||
this.resolvedAdditionalDependencies = resolvedAdditionalDependencies;
|
||||
}
|
||||
|
||||
public List<File> getResolvedAdditionalDependencies() {
|
||||
return this.resolvedAdditionalDependencies;
|
||||
}
|
||||
|
||||
public byte[] getClassBytes(String classname) {
|
||||
return this.classBytes.get(classname);
|
||||
@@ -66,8 +77,12 @@ public class CompilationResult {
|
||||
return Collections.unmodifiableList(compilationMessages);
|
||||
}
|
||||
|
||||
public void recordCompilationMessage(CompilationMessage compilationMessage) {
|
||||
this.compilationMessages.add(compilationMessage);
|
||||
public void recordCompilationMessage(CompilationMessage message) {
|
||||
this.compilationMessages.add(message);
|
||||
}
|
||||
|
||||
public void recordCompilationMessages(List<CompilationMessage> messages) {
|
||||
this.compilationMessages.addAll(messages);
|
||||
}
|
||||
|
||||
public void setCompiledClasses(List<Class<?>> compiledClasses) {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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.function.compiler.java;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.aether.repository.Proxy;
|
||||
import org.eclipse.aether.repository.ProxySelector;
|
||||
import org.eclipse.aether.repository.RemoteRepository;
|
||||
|
||||
/**
|
||||
* Composite {@link ProxySelector}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class CompositeProxySelector implements ProxySelector {
|
||||
|
||||
private List<ProxySelector> selectors = new ArrayList<ProxySelector>();
|
||||
|
||||
public CompositeProxySelector(List<ProxySelector> selectors) {
|
||||
this.selectors = selectors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Proxy getProxy(RemoteRepository repository) {
|
||||
for (ProxySelector selector : this.selectors) {
|
||||
Proxy proxy = selector.getProxy(repository);
|
||||
if (proxy != null) {
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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.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;
|
||||
|
||||
public class DependencyResolver {
|
||||
|
||||
private static DependencyResolver instance = new DependencyResolver();
|
||||
|
||||
private static Properties globals;
|
||||
|
||||
private LocalRepositoryManagerFactory localRepositoryManagerFactory;
|
||||
|
||||
private PlexusContainer container;
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
private ProjectBuilder projectBuilder;
|
||||
|
||||
private RepositorySystem repositorySystem;
|
||||
|
||||
private MavenSettings settings;
|
||||
|
||||
public static DependencyResolver instance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static void close() {
|
||||
instance = new DependencyResolver();
|
||||
}
|
||||
|
||||
private DependencyResolver() {
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
if (this.container == null) {
|
||||
synchronized (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());
|
||||
localRepositoryManagerFactory = container
|
||||
.lookup(LocalRepositoryManagerFactory.class);
|
||||
container.addComponent(
|
||||
new ClassRealmManager((MutablePlexusContainer) container,
|
||||
new DefaultBeanLocator()),
|
||||
ClassRealmManager.class.getName());
|
||||
projectBuilder = container.lookup(ProjectBuilder.class);
|
||||
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 = 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(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(
|
||||
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(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 = 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;
|
||||
}
|
||||
|
||||
static Properties getGlobals() {
|
||||
return globals;
|
||||
}
|
||||
|
||||
@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 = properties;
|
||||
return new BufferedInputStream(resource.getInputStream()) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
DependencyResolver.globals = null;
|
||||
super.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocation() {
|
||||
return 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.function.compiler.java;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.maven.model.ActivationFile;
|
||||
import org.apache.maven.model.ActivationOS;
|
||||
import org.apache.maven.model.ActivationProperty;
|
||||
import org.apache.maven.model.building.ModelProblemCollector;
|
||||
import org.apache.maven.model.building.ModelProblemCollectorRequest;
|
||||
import org.apache.maven.model.path.DefaultPathTranslator;
|
||||
import org.apache.maven.model.profile.DefaultProfileSelector;
|
||||
import org.apache.maven.model.profile.ProfileActivationContext;
|
||||
import org.apache.maven.model.profile.activation.FileProfileActivator;
|
||||
import org.apache.maven.model.profile.activation.JdkVersionProfileActivator;
|
||||
import org.apache.maven.model.profile.activation.OperatingSystemProfileActivator;
|
||||
import org.apache.maven.model.profile.activation.PropertyProfileActivator;
|
||||
import org.apache.maven.settings.Activation;
|
||||
import org.apache.maven.settings.Mirror;
|
||||
import org.apache.maven.settings.Profile;
|
||||
import org.apache.maven.settings.Proxy;
|
||||
import org.apache.maven.settings.Server;
|
||||
import org.apache.maven.settings.Settings;
|
||||
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
|
||||
import org.eclipse.aether.repository.Authentication;
|
||||
import org.eclipse.aether.repository.AuthenticationSelector;
|
||||
import org.eclipse.aether.repository.MirrorSelector;
|
||||
import org.eclipse.aether.repository.ProxySelector;
|
||||
import org.eclipse.aether.util.repository.AuthenticationBuilder;
|
||||
import org.eclipse.aether.util.repository.ConservativeAuthenticationSelector;
|
||||
import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
|
||||
import org.eclipse.aether.util.repository.DefaultMirrorSelector;
|
||||
import org.eclipse.aether.util.repository.DefaultProxySelector;
|
||||
|
||||
/**
|
||||
* An encapsulation of settings read from a user's Maven settings.xml.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @see MavenSettingsReader
|
||||
*/
|
||||
public class MavenSettings {
|
||||
|
||||
private final boolean offline;
|
||||
|
||||
private final MirrorSelector mirrorSelector;
|
||||
|
||||
private final AuthenticationSelector authenticationSelector;
|
||||
|
||||
private final ProxySelector proxySelector;
|
||||
|
||||
private final String localRepository;
|
||||
|
||||
private final List<Profile> activeProfiles;
|
||||
|
||||
/**
|
||||
* Create a new {@link MavenSettings} instance.
|
||||
* @param settings the source settings
|
||||
* @param decryptedSettings the decrypted settings
|
||||
*/
|
||||
public MavenSettings(Settings settings, SettingsDecryptionResult decryptedSettings) {
|
||||
this.offline = settings.isOffline();
|
||||
this.mirrorSelector = createMirrorSelector(settings);
|
||||
this.authenticationSelector = createAuthenticationSelector(decryptedSettings);
|
||||
this.proxySelector = createProxySelector(decryptedSettings);
|
||||
this.localRepository = settings.getLocalRepository();
|
||||
this.activeProfiles = determineActiveProfiles(settings);
|
||||
}
|
||||
|
||||
private MirrorSelector createMirrorSelector(Settings settings) {
|
||||
DefaultMirrorSelector selector = new DefaultMirrorSelector();
|
||||
for (Mirror mirror : settings.getMirrors()) {
|
||||
selector.add(mirror.getId(), mirror.getUrl(), mirror.getLayout(), false,
|
||||
mirror.getMirrorOf(), mirror.getMirrorOfLayouts());
|
||||
}
|
||||
return selector;
|
||||
}
|
||||
|
||||
private AuthenticationSelector createAuthenticationSelector(
|
||||
SettingsDecryptionResult decryptedSettings) {
|
||||
DefaultAuthenticationSelector selector = new DefaultAuthenticationSelector();
|
||||
for (Server server : decryptedSettings.getServers()) {
|
||||
AuthenticationBuilder auth = new AuthenticationBuilder();
|
||||
auth.addUsername(server.getUsername()).addPassword(server.getPassword());
|
||||
auth.addPrivateKey(server.getPrivateKey(), server.getPassphrase());
|
||||
selector.add(server.getId(), auth.build());
|
||||
}
|
||||
return new ConservativeAuthenticationSelector(selector);
|
||||
}
|
||||
|
||||
private ProxySelector createProxySelector(
|
||||
SettingsDecryptionResult decryptedSettings) {
|
||||
DefaultProxySelector selector = new DefaultProxySelector();
|
||||
for (Proxy proxy : decryptedSettings.getProxies()) {
|
||||
Authentication authentication = new AuthenticationBuilder()
|
||||
.addUsername(proxy.getUsername()).addPassword(proxy.getPassword())
|
||||
.build();
|
||||
selector.add(
|
||||
new org.eclipse.aether.repository.Proxy(proxy.getProtocol(),
|
||||
proxy.getHost(), proxy.getPort(), authentication),
|
||||
proxy.getNonProxyHosts());
|
||||
}
|
||||
return selector;
|
||||
}
|
||||
|
||||
private List<Profile> determineActiveProfiles(Settings settings) {
|
||||
SpringBootCliModelProblemCollector problemCollector = new SpringBootCliModelProblemCollector();
|
||||
List<org.apache.maven.model.Profile> activeModelProfiles = createProfileSelector()
|
||||
.getActiveProfiles(createModelProfiles(settings.getProfiles()),
|
||||
new SpringBootCliProfileActivationContext(
|
||||
settings.getActiveProfiles()),
|
||||
problemCollector);
|
||||
if (!problemCollector.getProblems().isEmpty()) {
|
||||
throw new IllegalStateException(createFailureMessage(problemCollector));
|
||||
}
|
||||
List<Profile> activeProfiles = new ArrayList<Profile>();
|
||||
Map<String, Profile> profiles = settings.getProfilesAsMap();
|
||||
for (org.apache.maven.model.Profile modelProfile : activeModelProfiles) {
|
||||
activeProfiles.add(profiles.get(modelProfile.getId()));
|
||||
}
|
||||
return activeProfiles;
|
||||
}
|
||||
|
||||
private String createFailureMessage(
|
||||
SpringBootCliModelProblemCollector problemCollector) {
|
||||
StringWriter message = new StringWriter();
|
||||
PrintWriter printer = new PrintWriter(message);
|
||||
printer.println("Failed to determine active profiles:");
|
||||
for (ModelProblemCollectorRequest problem : problemCollector.getProblems()) {
|
||||
printer.println(" " + problem.getMessage() + (problem.getLocation() != null
|
||||
? " at " + problem.getLocation() : ""));
|
||||
if (problem.getException() != null) {
|
||||
printer.println(indentStackTrace(problem.getException(), " "));
|
||||
}
|
||||
}
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
private String indentStackTrace(Exception ex, String indent) {
|
||||
return indentLines(printStackTrace(ex), indent);
|
||||
}
|
||||
|
||||
private String printStackTrace(Exception ex) {
|
||||
StringWriter stackTrace = new StringWriter();
|
||||
PrintWriter printer = new PrintWriter(stackTrace);
|
||||
ex.printStackTrace(printer);
|
||||
return stackTrace.toString();
|
||||
}
|
||||
|
||||
private String indentLines(String input, String indent) {
|
||||
StringWriter indented = new StringWriter();
|
||||
PrintWriter writer = new PrintWriter(indented);
|
||||
String line;
|
||||
BufferedReader reader = new BufferedReader(new StringReader(input));
|
||||
try {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
writer.println(indent + line);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return input;
|
||||
}
|
||||
return indented.toString();
|
||||
}
|
||||
|
||||
private DefaultProfileSelector createProfileSelector() {
|
||||
DefaultProfileSelector selector = new DefaultProfileSelector();
|
||||
|
||||
selector.addProfileActivator(new FileProfileActivator()
|
||||
.setPathTranslator(new DefaultPathTranslator()));
|
||||
selector.addProfileActivator(new JdkVersionProfileActivator());
|
||||
selector.addProfileActivator(new PropertyProfileActivator());
|
||||
selector.addProfileActivator(new OperatingSystemProfileActivator());
|
||||
return selector;
|
||||
}
|
||||
|
||||
private List<org.apache.maven.model.Profile> createModelProfiles(
|
||||
List<Profile> profiles) {
|
||||
List<org.apache.maven.model.Profile> modelProfiles = new ArrayList<org.apache.maven.model.Profile>();
|
||||
for (Profile profile : profiles) {
|
||||
org.apache.maven.model.Profile modelProfile = new org.apache.maven.model.Profile();
|
||||
modelProfile.setId(profile.getId());
|
||||
if (profile.getActivation() != null) {
|
||||
modelProfile
|
||||
.setActivation(createModelActivation(profile.getActivation()));
|
||||
}
|
||||
modelProfiles.add(modelProfile);
|
||||
}
|
||||
return modelProfiles;
|
||||
}
|
||||
|
||||
private org.apache.maven.model.Activation createModelActivation(
|
||||
Activation activation) {
|
||||
org.apache.maven.model.Activation modelActivation = new org.apache.maven.model.Activation();
|
||||
modelActivation.setActiveByDefault(activation.isActiveByDefault());
|
||||
if (activation.getFile() != null) {
|
||||
ActivationFile activationFile = new ActivationFile();
|
||||
activationFile.setExists(activation.getFile().getExists());
|
||||
activationFile.setMissing(activation.getFile().getMissing());
|
||||
modelActivation.setFile(activationFile);
|
||||
}
|
||||
modelActivation.setJdk(activation.getJdk());
|
||||
if (activation.getOs() != null) {
|
||||
ActivationOS os = new ActivationOS();
|
||||
os.setArch(activation.getOs().getArch());
|
||||
os.setFamily(activation.getOs().getFamily());
|
||||
os.setName(activation.getOs().getName());
|
||||
os.setVersion(activation.getOs().getVersion());
|
||||
modelActivation.setOs(os);
|
||||
}
|
||||
if (activation.getProperty() != null) {
|
||||
ActivationProperty property = new ActivationProperty();
|
||||
property.setName(activation.getProperty().getName());
|
||||
property.setValue(activation.getProperty().getValue());
|
||||
modelActivation.setProperty(property);
|
||||
}
|
||||
return modelActivation;
|
||||
}
|
||||
|
||||
public boolean getOffline() {
|
||||
return this.offline;
|
||||
}
|
||||
|
||||
public MirrorSelector getMirrorSelector() {
|
||||
return this.mirrorSelector;
|
||||
}
|
||||
|
||||
public AuthenticationSelector getAuthenticationSelector() {
|
||||
return this.authenticationSelector;
|
||||
}
|
||||
|
||||
public ProxySelector getProxySelector() {
|
||||
return this.proxySelector;
|
||||
}
|
||||
|
||||
public String getLocalRepository() {
|
||||
return this.localRepository;
|
||||
}
|
||||
|
||||
public List<Profile> getActiveProfiles() {
|
||||
return this.activeProfiles;
|
||||
}
|
||||
|
||||
private static final class SpringBootCliProfileActivationContext
|
||||
implements ProfileActivationContext {
|
||||
|
||||
private final List<String> activeProfiles;
|
||||
|
||||
SpringBootCliProfileActivationContext(List<String> activeProfiles) {
|
||||
this.activeProfiles = activeProfiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getActiveProfileIds() {
|
||||
return this.activeProfiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getInactiveProfileIds() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public Map<String, String> getSystemProperties() {
|
||||
return (Map) System.getProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getUserProperties() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getProjectDirectory() {
|
||||
return new File(".");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getProjectProperties() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class SpringBootCliModelProblemCollector
|
||||
implements ModelProblemCollector {
|
||||
|
||||
private final List<ModelProblemCollectorRequest> problems = new ArrayList<ModelProblemCollectorRequest>();
|
||||
|
||||
@Override
|
||||
public void add(ModelProblemCollectorRequest req) {
|
||||
this.problems.add(req);
|
||||
}
|
||||
|
||||
List<ModelProblemCollectorRequest> getProblems() {
|
||||
return this.problems;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.function.compiler.java;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.apache.maven.settings.Settings;
|
||||
import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
|
||||
import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
|
||||
import org.apache.maven.settings.building.SettingsBuildingException;
|
||||
import org.apache.maven.settings.building.SettingsBuildingRequest;
|
||||
import org.apache.maven.settings.crypto.DefaultSettingsDecrypter;
|
||||
import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
|
||||
import org.apache.maven.settings.crypto.SettingsDecrypter;
|
||||
import org.apache.maven.settings.crypto.SettingsDecryptionResult;
|
||||
import org.eclipse.aether.DefaultRepositorySystemSession;
|
||||
import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
|
||||
import org.eclipse.aether.repository.LocalRepository;
|
||||
import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.sonatype.plexus.components.cipher.DefaultPlexusCipher;
|
||||
import org.sonatype.plexus.components.cipher.PlexusCipherException;
|
||||
import org.sonatype.plexus.components.sec.dispatcher.DefaultSecDispatcher;
|
||||
|
||||
/**
|
||||
* {@code MavenSettingsReader} reads settings from a user's Maven settings.xml file,
|
||||
* decrypting them if necessary using settings-security.xml.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class MavenSettingsReader {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MavenSettingsReader.class);
|
||||
|
||||
private final String homeDir;
|
||||
|
||||
public MavenSettingsReader() {
|
||||
this(System.getProperty("user.home"));
|
||||
}
|
||||
|
||||
public MavenSettingsReader(String homeDir) {
|
||||
this.homeDir = homeDir;
|
||||
}
|
||||
|
||||
public MavenSettings readSettings() {
|
||||
Settings settings = loadSettings();
|
||||
SettingsDecryptionResult decrypted = decryptSettings(settings);
|
||||
if (!decrypted.getProblems().isEmpty()) {
|
||||
log.error(
|
||||
"Maven settings decryption failed. Some Maven repositories may be inaccessible");
|
||||
// Continue - the encrypted credentials may not be used
|
||||
}
|
||||
return new MavenSettings(settings, decrypted);
|
||||
}
|
||||
|
||||
public static void applySettings(MavenSettings settings,
|
||||
DefaultRepositorySystemSession session) {
|
||||
if (settings.getLocalRepository() != null) {
|
||||
try {
|
||||
session.setLocalRepositoryManager(
|
||||
new SimpleLocalRepositoryManagerFactory().newInstance(session,
|
||||
new LocalRepository(settings.getLocalRepository())));
|
||||
}
|
||||
catch (NoLocalRepositoryManagerException e) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot set local repository to " + settings.getLocalRepository(),
|
||||
e);
|
||||
}
|
||||
}
|
||||
session.setOffline(settings.getOffline());
|
||||
session.setMirrorSelector(settings.getMirrorSelector());
|
||||
session.setAuthenticationSelector(settings.getAuthenticationSelector());
|
||||
session.setProxySelector(settings.getProxySelector());
|
||||
}
|
||||
|
||||
private Settings loadSettings() {
|
||||
File settingsFile = new File(this.homeDir, ".m2/settings.xml");
|
||||
if (settingsFile.exists()) {
|
||||
log.info("Reading settings from: " + settingsFile);
|
||||
}
|
||||
else {
|
||||
log.info("No settings found at: " + settingsFile);
|
||||
}
|
||||
SettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
|
||||
request.setUserSettingsFile(settingsFile);
|
||||
request.setSystemProperties(System.getProperties());
|
||||
try {
|
||||
return new DefaultSettingsBuilderFactory().newInstance().build(request)
|
||||
.getEffectiveSettings();
|
||||
}
|
||||
catch (SettingsBuildingException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Failed to build settings from " + settingsFile, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private SettingsDecryptionResult decryptSettings(Settings settings) {
|
||||
DefaultSettingsDecryptionRequest request = new DefaultSettingsDecryptionRequest(
|
||||
settings);
|
||||
|
||||
return createSettingsDecrypter().decrypt(request);
|
||||
}
|
||||
|
||||
private SettingsDecrypter createSettingsDecrypter() {
|
||||
SettingsDecrypter settingsDecrypter = new DefaultSettingsDecrypter();
|
||||
setField(DefaultSettingsDecrypter.class, "securityDispatcher", settingsDecrypter,
|
||||
new SpringBootSecDispatcher());
|
||||
return settingsDecrypter;
|
||||
}
|
||||
|
||||
private void setField(Class<?> sourceClass, String fieldName, Object target,
|
||||
Object value) {
|
||||
try {
|
||||
Field field = sourceClass.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
field.set(target, value);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(
|
||||
"Failed to set field '" + fieldName + "' on '" + target + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private class SpringBootSecDispatcher extends DefaultSecDispatcher {
|
||||
|
||||
private static final String SECURITY_XML = ".m2/settings-security.xml";
|
||||
|
||||
SpringBootSecDispatcher() {
|
||||
File file = new File(MavenSettingsReader.this.homeDir, SECURITY_XML);
|
||||
this._configurationFile = file.getAbsolutePath();
|
||||
try {
|
||||
this._cipher = new DefaultPlexusCipher();
|
||||
}
|
||||
catch (PlexusCipherException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,9 @@ import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.tools.FileObject;
|
||||
@@ -31,6 +33,8 @@ import javax.tools.JavaFileObject;
|
||||
import javax.tools.JavaFileObject.Kind;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
import org.eclipse.aether.artifact.DefaultArtifact;
|
||||
import org.eclipse.aether.graph.Dependency;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -50,6 +54,8 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
||||
|
||||
private List<CloseableFilterableJavaFileObjectIterable> toClose = new ArrayList<>();
|
||||
|
||||
private Map<String,File> resolvedAdditionalDependencies = new LinkedHashMap<>();
|
||||
|
||||
public MemoryBasedJavaFileManager() {
|
||||
outputCollector = new CompilationOutputCollector();
|
||||
}
|
||||
@@ -84,6 +90,11 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
||||
else if (location == StandardLocation.CLASS_PATH
|
||||
&& (kinds == null || kinds.contains(Kind.CLASS))) {
|
||||
String javaClassPath = getClassPath();
|
||||
if (!resolvedAdditionalDependencies.isEmpty()) {
|
||||
for (File resolvedAdditionalDependency: resolvedAdditionalDependencies.values()) {
|
||||
javaClassPath += File.pathSeparatorChar + resolvedAdditionalDependency.toURI().toString().substring("file:".length());
|
||||
}
|
||||
}
|
||||
logger.debug("Creating iterable for class path: {}", javaClassPath);
|
||||
resultIterable = new IterableClasspath(javaClassPath, packageName, recurse);
|
||||
toClose.add(resultIterable);
|
||||
@@ -203,4 +214,35 @@ public class MemoryBasedJavaFileManager implements JavaFileManager {
|
||||
return outputCollector.getCompiledClasses();
|
||||
}
|
||||
|
||||
public List<CompilationMessage> addAndResolveDependencies(String[] dependencies) {
|
||||
List<CompilationMessage> resolutionMessages = new ArrayList<>();
|
||||
for (String dependency: dependencies) {
|
||||
if (dependency.startsWith("maven:")) {
|
||||
// Resolving an explicit external archive
|
||||
String coordinates = dependency.replaceFirst("maven:\\/*", "");
|
||||
DependencyResolver engine = DependencyResolver.instance();
|
||||
try {
|
||||
File resolved = engine.resolve(new Dependency(new DefaultArtifact(coordinates), "runtime"));
|
||||
// Example:
|
||||
// dependency = maven://org.springframework:spring-expression:4.3.9.RELEASE
|
||||
// resolved.toURI() = file:/Users/aclement/.m2/repository/org/springframework/spring-expression/4.3.9.RELEASE/spring-expression-4.3.9.RELEASE.jar
|
||||
resolvedAdditionalDependencies.put(dependency, resolved);
|
||||
} catch (RuntimeException re) {
|
||||
CompilationMessage compilationMessage =
|
||||
new CompilationMessage(CompilationMessage.Kind.ERROR,re.getMessage(),null,0,0);
|
||||
resolutionMessages.add(compilationMessage);
|
||||
}
|
||||
}
|
||||
else {
|
||||
resolutionMessages.add(new CompilationMessage(CompilationMessage.Kind.ERROR,
|
||||
"Unrecognized dependency: "+dependency+" (expected something of the form: maven://groupId:artifactId:version)",null,0,0));
|
||||
}
|
||||
}
|
||||
return resolutionMessages;
|
||||
}
|
||||
|
||||
public Map<String, File> getResolvedAdditionalDependencies() {
|
||||
return resolvedAdditionalDependencies;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class RuntimeJavaCompiler {
|
||||
|
||||
|
||||
private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(RuntimeJavaCompiler.class);
|
||||
@@ -49,13 +49,15 @@ public class RuntimeJavaCompiler {
|
||||
* classes.
|
||||
* @param className the name of the class (dotted form, e.g. com.foo.bar.Goo)
|
||||
* @param classSourceCode the full source code for the class
|
||||
* @param dependencies optional maven coordinates for dependencies "maven://groupId:artifactId:version"
|
||||
* @return a CompilationResult that encapsulates what happened during compilation (classes/messages produced)
|
||||
*/
|
||||
public CompilationResult compile(String className, String classSourceCode) {
|
||||
public CompilationResult compile(String className, String classSourceCode, String... dependencies) {
|
||||
logger.info("Compiling source for class {} using compiler {}",className,compiler.getClass().getName());
|
||||
|
||||
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
|
||||
MemoryBasedJavaFileManager fileManager = new MemoryBasedJavaFileManager();
|
||||
List<CompilationMessage> resolutionMessages = fileManager.addAndResolveDependencies(dependencies);
|
||||
// JavaFileObject sourceFile = new StringBasedJavaSourceFileObject(className, classSourceCode);
|
||||
JavaFileObject sourceFile = InMemoryJavaFileObject.getSourceJavaFileObject(className, classSourceCode);
|
||||
// new InMemoryJavaFileObject(StandardLocation.SOURCE_PATH, className, javax.tools.JavaFileObject.Kind.SOURCE, null);
|
||||
@@ -69,6 +71,8 @@ public class RuntimeJavaCompiler {
|
||||
|
||||
boolean success = task.call();
|
||||
CompilationResult compilationResult = new CompilationResult(success);
|
||||
compilationResult.recordCompilationMessages(resolutionMessages);
|
||||
compilationResult.setResolvedAdditionalDependencies(new ArrayList<>(fileManager.getResolvedAdditionalDependencies().values()));
|
||||
|
||||
// If successful there may be no errors but there might be info/warnings
|
||||
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnosticCollector.getDiagnostics()) {
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.function.compiler.java;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Very simple classloader that can be used to load the compiled types.
|
||||
@@ -32,6 +34,22 @@ public class SimpleClassLoader extends URLClassLoader {
|
||||
super(NO_URLS, classLoader);
|
||||
}
|
||||
|
||||
public SimpleClassLoader(List<File> resolvedAdditionalDependencies, ClassLoader classLoader) {
|
||||
super(toUrls(resolvedAdditionalDependencies), classLoader);
|
||||
}
|
||||
|
||||
private static URL[] toUrls(List<File> resolvedAdditionalDependencies) {
|
||||
URL[] urls = new URL[resolvedAdditionalDependencies.size()];
|
||||
for (int i=0,max=resolvedAdditionalDependencies.size();i<max;i++) {
|
||||
try {
|
||||
urls[i] = resolvedAdditionalDependencies.get(i).toURI().toURL();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
public Class<?> defineClass(String name, byte[] bytes) {
|
||||
return super.defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2017 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.function.compiler.java;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class RuntimeJavaCompilerTests {
|
||||
|
||||
@Test
|
||||
public void basicCompilation() {
|
||||
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
||||
CompilationResult cr = rjc.compile("A", "public class A {}");
|
||||
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
||||
assertTrue(compilationMessages.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingType() throws Exception {
|
||||
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
||||
CompilationResult cr = rjc.compile("A",
|
||||
"public class A implements java.util.function.Supplier { "+
|
||||
" public String get() {\n"+
|
||||
" ExpressionParser parser = new SpelExpressionParser();\n" +
|
||||
" Expression exp = parser.parseExpression(\"'Hello World'\");\n" +
|
||||
" String message = (String) exp.getValue();"+
|
||||
" return message;\n"+
|
||||
" }\n"+
|
||||
"}");
|
||||
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
||||
assertEquals(3,compilationMessages.size());
|
||||
assertTrue(compilationMessages.get(0).getMessage().contains("cannot find symbol"));
|
||||
assertTrue(compilationMessages.get(0).getMessage().contains("class ExpressionParser"));
|
||||
assertTrue(compilationMessages.get(1).getMessage().contains("cannot find symbol"));
|
||||
assertTrue(compilationMessages.get(1).getMessage().contains("class SpelExpressionParser"));
|
||||
assertTrue(compilationMessages.get(2).getMessage().contains("cannot find symbol"));
|
||||
assertTrue(compilationMessages.get(2).getMessage().contains("class Expression"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void okWithImportedDependencies() throws Exception {
|
||||
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
||||
CompilationResult cr = rjc.compile("A",
|
||||
"import org.springframework.expression.*;\n"+
|
||||
"import org.springframework.expression.spel.standard.*;\n"+
|
||||
"public class A implements java.util.function.Supplier {\n"+
|
||||
" public String get() {\n"+
|
||||
" ExpressionParser parser = new SpelExpressionParser();\n" +
|
||||
" Expression exp = parser.parseExpression(\"'Hello World'\");\n" +
|
||||
" String message = (String) exp.getValue();\n"+
|
||||
" return message;\n"+
|
||||
" }\n"+
|
||||
"}","maven://org.springframework:spring-expression:4.3.9.RELEASE");
|
||||
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
||||
assertTrue(compilationMessages.isEmpty());
|
||||
try (SimpleClassLoader cl = new SimpleClassLoader(this.getClass().getClassLoader())) {
|
||||
Class<?> clazz = cl.defineClass("A",cr.getClassBytes("A"));
|
||||
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
|
||||
assertEquals("Hello World",supplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void okWithImportedDependencies2() throws Exception {
|
||||
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
||||
String source =
|
||||
"import org.joda.time.*;\n"+
|
||||
"public class A implements java.util.function.Supplier {\n"+
|
||||
" public String get() {\n"+
|
||||
" DateTime dt = new DateTime();\n" +
|
||||
" int month = dt.getMonthOfYear();\n"+
|
||||
" return String.valueOf(month>0);\n"+
|
||||
" }\n"+
|
||||
"}";
|
||||
CompilationResult cr = rjc.compile("A", source, "maven://joda-time:joda-time:2.9.9");
|
||||
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
||||
assertTrue(compilationMessages.isEmpty());
|
||||
List<File> resolvedAdditionalDependencies = cr.getResolvedAdditionalDependencies();
|
||||
try (SimpleClassLoader cl = new SimpleClassLoader(resolvedAdditionalDependencies, this.getClass().getClassLoader())) {
|
||||
Class<?> clazz = cl.defineClass("A",cr.getClassBytes("A"));
|
||||
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
|
||||
assertEquals("true",supplier.get());
|
||||
}
|
||||
|
||||
cr = rjc.compile("A", source,
|
||||
"maven://org.springframework:spring-expression:4.3.9.RELEASE",
|
||||
"maven://joda-time:joda-time:2.9.9");
|
||||
compilationMessages = cr.getCompilationMessages();
|
||||
assertTrue(compilationMessages.isEmpty());
|
||||
resolvedAdditionalDependencies = cr.getResolvedAdditionalDependencies();
|
||||
try (SimpleClassLoader cl = new SimpleClassLoader(resolvedAdditionalDependencies, this.getClass().getClassLoader())) {
|
||||
Class<?> clazz = cl.defineClass("A",cr.getClassBytes("A"));
|
||||
Supplier<String> supplier = (Supplier<String>) clazz.newInstance();
|
||||
assertEquals("true",supplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dependencyResolution() throws Exception {
|
||||
// Failure:
|
||||
RuntimeJavaCompiler rjc = new RuntimeJavaCompiler();
|
||||
CompilationResult cr = rjc.compile("A",
|
||||
"public class A {}",
|
||||
"maven://org.springframework:spring-expression2:4.3.9.RELEASE"); // extra '2' in there
|
||||
List<CompilationMessage> compilationMessages = cr.getCompilationMessages();
|
||||
assertEquals(1,compilationMessages.size());
|
||||
// ERROR:org.eclipse.aether.resolution.ArtifactResolutionException: Could not find artifact org.springframework:spring-expression2:jar:4.3.9.RELEASE in spring-snapshots (https://repo.spring.io/libs-snapshot)
|
||||
assertTrue(compilationMessages.get(0).getMessage().contains("Could not find artifact org.springframework:spring-expression2:jar:4.3.9.RELEASE"));
|
||||
|
||||
// Failure:
|
||||
rjc = new RuntimeJavaCompiler();
|
||||
cr = rjc.compile("A",
|
||||
"public class A {}",
|
||||
"trouble://org.springframework:spring-expression:4.3.9.RELEASE"); // rogue prefix (should be "maven:")
|
||||
compilationMessages = cr.getCompilationMessages();
|
||||
assertEquals(1,compilationMessages.size());
|
||||
assertTrue(compilationMessages.get(0).toString(),compilationMessages.get(0).getMessage().contains("Unrecognized dependency: "));
|
||||
|
||||
// Success
|
||||
rjc = new RuntimeJavaCompiler();
|
||||
cr = rjc.compile("A",
|
||||
"public class A {}",
|
||||
"maven://joda-time:joda-time:2.9.9");
|
||||
compilationMessages = cr.getCompilationMessages();
|
||||
assertEquals(0,compilationMessages.size());
|
||||
List<File> resolvedAdditionalDependencies = cr.getResolvedAdditionalDependencies();
|
||||
assertEquals(1, resolvedAdditionalDependencies.size());
|
||||
assertTrue("Expected this to end with 'joda-time-2.9.9.jar': "+resolvedAdditionalDependencies.get(0).toString(),
|
||||
resolvedAdditionalDependencies.get(0).toString().endsWith("joda-time-2.9.9.jar"));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user