GH-1272 Remove Maven Resource Resolver dependency
This commit effectively removes dependency on spring-cloud-deployer Resolves #1272
This commit is contained in:
@@ -15,6 +15,10 @@
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<commons-io.version>2.16.1</commons-io.version>
|
||||
<maven.version>3.9.6</maven.version>
|
||||
<maven-resolver.version>1.9.18</maven-resolver.version>
|
||||
<maven-wagon.version>3.5.3</maven-wagon.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -34,11 +38,7 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-deployer-resource-maven</artifactId>
|
||||
<version>2.5.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
@@ -55,6 +55,66 @@
|
||||
<version>2.2.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-model-builder</artifactId>
|
||||
<version>${maven.version}</version>
|
||||
<!-- @TODO boot3 remove when updated -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-resolver-provider</artifactId>
|
||||
<version>${maven.version}</version>
|
||||
<!-- @TODO boot3 remove when updated -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.inject</groupId>
|
||||
<artifactId>javax.inject</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-connector-basic</artifactId>
|
||||
<version>${maven-resolver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-transport-file</artifactId>
|
||||
<version>${maven-resolver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-transport-http</artifactId>
|
||||
<version>${maven-resolver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-transport-wagon</artifactId>
|
||||
<version>${maven-resolver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-impl</artifactId>
|
||||
<version>${maven-resolver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.19.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.wagon</groupId>
|
||||
<artifactId>wagon-http</artifactId>
|
||||
<version>3.5.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -34,10 +34,10 @@ import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.boot.loader.archive.Archive;
|
||||
import org.springframework.boot.loader.archive.ExplodedArchive;
|
||||
import org.springframework.boot.loader.archive.JarFileArchive;
|
||||
import org.springframework.cloud.deployer.resource.maven.MavenProperties;
|
||||
import org.springframework.cloud.deployer.resource.maven.MavenResourceLoader;
|
||||
import org.springframework.cloud.function.context.FunctionProperties;
|
||||
import org.springframework.cloud.function.context.FunctionRegistry;
|
||||
import org.springframework.cloud.function.deployer.utils.MavenProperties;
|
||||
import org.springframework.cloud.function.deployer.utils.MavenResourceLoader;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2019-2025 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.deployer.utils;
|
||||
|
||||
import org.eclipse.aether.AbstractRepositoryListener;
|
||||
import org.eclipse.aether.RepositoryEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author Corneil du Plessis
|
||||
*/
|
||||
public class LoggingRepositoryListener extends AbstractRepositoryListener {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LoggingRepositoryListener.class);
|
||||
|
||||
public void artifactDeployed(RepositoryEvent event) {
|
||||
println("artifactDeployed", event.getArtifact() + " to " + event.getRepository());
|
||||
}
|
||||
|
||||
public void artifactDeploying(RepositoryEvent event) {
|
||||
println("artifactDeploying", event.getArtifact() + " to " + event.getRepository());
|
||||
}
|
||||
|
||||
public void artifactDescriptorInvalid(RepositoryEvent event) {
|
||||
println("artifactDescriptorInvalid", "for " + event.getArtifact() + ": " + event.getException().getMessage());
|
||||
}
|
||||
|
||||
public void artifactDescriptorMissing(RepositoryEvent event) {
|
||||
println("artifactDescriptorMissing", "for " + event.getArtifact());
|
||||
}
|
||||
|
||||
public void artifactInstalled(RepositoryEvent event) {
|
||||
println("artifactInstalled", event.getArtifact() + " to " + event.getFile());
|
||||
}
|
||||
|
||||
public void artifactInstalling(RepositoryEvent event) {
|
||||
println("artifactInstalling", event.getArtifact() + " to " + event.getFile());
|
||||
}
|
||||
|
||||
public void artifactResolved(RepositoryEvent event) {
|
||||
println("artifactResolved", event.getArtifact() + " from " + event.getRepository());
|
||||
}
|
||||
|
||||
public void artifactDownloading(RepositoryEvent event) {
|
||||
println("artifactDownloading", event.getArtifact() + " from " + event.getRepository());
|
||||
}
|
||||
|
||||
public void artifactDownloaded(RepositoryEvent event) {
|
||||
println("artifactDownloaded", event.getArtifact() + " from " + event.getRepository());
|
||||
}
|
||||
|
||||
public void artifactResolving(RepositoryEvent event) {
|
||||
println("artifactResolving", event.getArtifact().toString());
|
||||
}
|
||||
|
||||
public void metadataDeployed(RepositoryEvent event) {
|
||||
println("metadataDeployed", event.getMetadata() + " to " + event.getRepository());
|
||||
}
|
||||
|
||||
public void metadataDeploying(RepositoryEvent event) {
|
||||
println("metadataDeploying", event.getMetadata() + " to " + event.getRepository());
|
||||
}
|
||||
|
||||
public void metadataInstalled(RepositoryEvent event) {
|
||||
println("metadataInstalled", event.getMetadata() + " to " + event.getFile());
|
||||
}
|
||||
|
||||
public void metadataInstalling(RepositoryEvent event) {
|
||||
println("metadataInstalling", event.getMetadata() + " to " + event.getFile());
|
||||
}
|
||||
|
||||
public void metadataInvalid(RepositoryEvent event) {
|
||||
println("metadataInvalid", event.getMetadata().toString());
|
||||
}
|
||||
|
||||
public void metadataResolved(RepositoryEvent event) {
|
||||
println("metadataResolved", event.getMetadata() + " from " + event.getRepository());
|
||||
}
|
||||
|
||||
public void metadataResolving(RepositoryEvent event) {
|
||||
println("metadataResolving", event.getMetadata() + " from " + event.getRepository());
|
||||
}
|
||||
|
||||
private void println(String event, String message) {
|
||||
logger.info("Aether Repository - " + event + ": " + message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
* Copyright 2019-2025 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.deployer.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
|
||||
import org.eclipse.aether.ConfigurationProperties;
|
||||
import org.eclipse.aether.DefaultRepositorySystemSession;
|
||||
import org.eclipse.aether.RepositorySystem;
|
||||
import org.eclipse.aether.RepositorySystemSession;
|
||||
import org.eclipse.aether.artifact.Artifact;
|
||||
import org.eclipse.aether.artifact.DefaultArtifact;
|
||||
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
|
||||
import org.eclipse.aether.impl.DefaultServiceLocator;
|
||||
import org.eclipse.aether.repository.Authentication;
|
||||
import org.eclipse.aether.repository.AuthenticationContext;
|
||||
import org.eclipse.aether.repository.AuthenticationDigest;
|
||||
import org.eclipse.aether.repository.LocalRepository;
|
||||
import org.eclipse.aether.repository.Proxy;
|
||||
import org.eclipse.aether.repository.RemoteRepository;
|
||||
import org.eclipse.aether.repository.RepositoryPolicy;
|
||||
import org.eclipse.aether.resolution.ArtifactRequest;
|
||||
import org.eclipse.aether.resolution.ArtifactResolutionException;
|
||||
import org.eclipse.aether.resolution.ArtifactResult;
|
||||
import org.eclipse.aether.resolution.VersionRangeRequest;
|
||||
import org.eclipse.aether.resolution.VersionRangeResolutionException;
|
||||
import org.eclipse.aether.resolution.VersionRangeResult;
|
||||
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
|
||||
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
|
||||
import org.eclipse.aether.transport.file.FileTransporterFactory;
|
||||
import org.eclipse.aether.transport.http.HttpTransporterFactory;
|
||||
import org.eclipse.aether.util.artifact.JavaScopes;
|
||||
import org.eclipse.aether.util.repository.DefaultProxySelector;
|
||||
import org.eclipse.aether.version.Version;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Resolves a {@link MavenResource} to
|
||||
* locate the artifact (uber jar) in a local Maven repository, downloading the latest update from a
|
||||
* remote repository if necessary.
|
||||
* <p>A set of default remote repos (Maven Central, Spring Snapshots, Spring Milestones) will be automatically added to
|
||||
* the head of the list of remote repos. If the default repo is already explicitly configured (exact match on the repo url)
|
||||
* then that particular default will be omitted. To skip the automatic default repos behavior altogether, set the
|
||||
* {@link MavenProperties#isIncludeDefaultRemoteRepos()} property to {@code false}.
|
||||
*
|
||||
* @author David Turanski
|
||||
* @author Mark Fisher
|
||||
* @author Marius Bogoevici
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author Donovan Muller
|
||||
* @author Corneil du Plessis
|
||||
* @author Chris Bono
|
||||
*/
|
||||
class MavenArtifactResolver {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MavenArtifactResolver.class);
|
||||
|
||||
private static final String DEFAULT_CONTENT_TYPE = "default";
|
||||
|
||||
private final RepositorySystem repositorySystem;
|
||||
|
||||
private final MavenProperties properties;
|
||||
|
||||
private final List<RemoteRepository> remoteRepositories = new LinkedList<>();
|
||||
|
||||
private final Authentication proxyAuthentication;
|
||||
|
||||
/**
|
||||
* Create an instance using the provided properties.
|
||||
*
|
||||
* @param properties the properties for the maven repositories, proxies, and authentication
|
||||
*/
|
||||
MavenArtifactResolver(MavenProperties properties) {
|
||||
Assert.notNull(properties, "MavenProperties must not be null");
|
||||
Assert.notNull(properties.getLocalRepository(), "Local repository path cannot be null");
|
||||
this.properties = properties;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Configured local repository: " + properties.getLocalRepository());
|
||||
logger.debug("Configured remote repositories: " + configuredRemoteRepositoriesDescription());
|
||||
}
|
||||
if (isProxyEnabled() && proxyHasCredentials()) {
|
||||
final String username = this.properties.getProxy().getAuth().getUsername();
|
||||
final String password = this.properties.getProxy().getAuth().getPassword();
|
||||
this.proxyAuthentication = newAuthentication(username, password);
|
||||
}
|
||||
else {
|
||||
this.proxyAuthentication = null;
|
||||
}
|
||||
File localRepository = new File(this.properties.getLocalRepository());
|
||||
if (!localRepository.exists()) {
|
||||
boolean created = localRepository.mkdirs();
|
||||
// May have been created by another thread after above check. Double check.
|
||||
Assert.isTrue(created || localRepository.exists(),
|
||||
"Unable to create directory for local repository: " + localRepository);
|
||||
}
|
||||
|
||||
Map<String, String> defaultRepoUrlsToIds = defaultRemoteRepos();
|
||||
|
||||
for (Map.Entry<String, MavenProperties.RemoteRepository> entry : this.properties.getRemoteRepositories()
|
||||
.entrySet()) {
|
||||
MavenProperties.RemoteRepository remoteRepository = entry.getValue();
|
||||
RemoteRepository.Builder remoteRepositoryBuilder = new RemoteRepository.Builder(
|
||||
entry.getKey(), DEFAULT_CONTENT_TYPE, remoteRepository.getUrl());
|
||||
// Update policies when set.
|
||||
if (remoteRepository.getPolicy() != null) {
|
||||
remoteRepositoryBuilder.setPolicy(new RepositoryPolicy(remoteRepository.getPolicy().isEnabled(),
|
||||
remoteRepository.getPolicy().getUpdatePolicy(),
|
||||
remoteRepository.getPolicy().getChecksumPolicy()));
|
||||
}
|
||||
if (remoteRepository.getReleasePolicy() != null) {
|
||||
remoteRepositoryBuilder
|
||||
.setReleasePolicy(new RepositoryPolicy(remoteRepository.getReleasePolicy().isEnabled(),
|
||||
remoteRepository.getReleasePolicy().getUpdatePolicy(),
|
||||
remoteRepository.getReleasePolicy().getChecksumPolicy()));
|
||||
}
|
||||
if (remoteRepository.getSnapshotPolicy() != null) {
|
||||
remoteRepositoryBuilder
|
||||
.setSnapshotPolicy(new RepositoryPolicy(remoteRepository.getSnapshotPolicy().isEnabled(),
|
||||
remoteRepository.getSnapshotPolicy().getUpdatePolicy(),
|
||||
remoteRepository.getSnapshotPolicy().getChecksumPolicy()));
|
||||
}
|
||||
if (remoteRepositoryHasCredentials(remoteRepository)) {
|
||||
final String username = remoteRepository.getAuth().getUsername();
|
||||
final String password = remoteRepository.getAuth().getPassword();
|
||||
remoteRepositoryBuilder.setAuthentication(newAuthentication(username, password));
|
||||
}
|
||||
// do not add default repo if explicitly configured
|
||||
defaultRepoUrlsToIds.remove(remoteRepository.getUrl());
|
||||
|
||||
RemoteRepository repo = proxyRepoIfProxyEnabled(remoteRepositoryBuilder.build());
|
||||
this.remoteRepositories.add(repo);
|
||||
}
|
||||
|
||||
if (!defaultRepoUrlsToIds.isEmpty() && this.properties.isIncludeDefaultRemoteRepos()) {
|
||||
List<RemoteRepository> defaultRepos = new ArrayList<>();
|
||||
defaultRepoUrlsToIds.forEach((url, id) -> {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding {} ({}) to remote repositories list", id, url);
|
||||
}
|
||||
RemoteRepository defaultRepo = proxyRepoIfProxyEnabled(new RemoteRepository.Builder(id, DEFAULT_CONTENT_TYPE, url).build());
|
||||
defaultRepos.add(defaultRepo);
|
||||
});
|
||||
this.remoteRepositories.addAll(0, defaultRepos);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using remote repositories: {}", actualRemoteRepositoriesDescription());
|
||||
}
|
||||
this.repositorySystem = newRepositorySystem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default repos to automatically add.
|
||||
* @return map of default repos (repo url to repo id)
|
||||
*/
|
||||
protected Map<String, String> defaultRemoteRepos() {
|
||||
Map<String, String> defaultRepos = new LinkedHashMap<>();
|
||||
defaultRepos.put("https://repo.maven.apache.org/maven2", "mavenCentral-default");
|
||||
defaultRepos.put("https://repo.spring.io/snapshot", "springSnapshot-default");
|
||||
defaultRepos.put("https://repo.spring.io/milestone", "springMilestone-default");
|
||||
return defaultRepos;
|
||||
}
|
||||
|
||||
private RemoteRepository proxyRepoIfProxyEnabled(RemoteRepository remoteRepo) {
|
||||
if (!isProxyEnabled()) {
|
||||
return remoteRepo;
|
||||
}
|
||||
Proxy proxy;
|
||||
MavenProperties.Proxy proxyProperties = this.properties.getProxy();
|
||||
if (this.proxyAuthentication != null) {
|
||||
proxy = new Proxy(
|
||||
proxyProperties.getProtocol(),
|
||||
proxyProperties.getHost(),
|
||||
proxyProperties.getPort(),
|
||||
this.proxyAuthentication);
|
||||
}
|
||||
else {
|
||||
// if proxy does not require authentication
|
||||
proxy = new Proxy(
|
||||
proxyProperties.getProtocol(),
|
||||
proxyProperties.getHost(),
|
||||
proxyProperties.getPort());
|
||||
}
|
||||
DefaultProxySelector proxySelector = new DefaultProxySelector();
|
||||
proxySelector.add(proxy, this.properties.getProxy().getNonProxyHosts());
|
||||
proxy = proxySelector.getProxy(remoteRepo);
|
||||
|
||||
RemoteRepository.Builder remoteRepositoryBuilder = new RemoteRepository.Builder(remoteRepo);
|
||||
remoteRepositoryBuilder.setProxy(proxy);
|
||||
return remoteRepositoryBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the proxy settings are provided.
|
||||
*
|
||||
* @return boolean true if the proxy settings are provided.
|
||||
*/
|
||||
private boolean isProxyEnabled() {
|
||||
return (this.properties.getProxy() != null &&
|
||||
this.properties.getProxy().getHost() != null &&
|
||||
this.properties.getProxy().getPort() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the proxy setting has username/password set.
|
||||
*
|
||||
* @return boolean true if both the username/password are set
|
||||
*/
|
||||
private boolean proxyHasCredentials() {
|
||||
return (this.properties.getProxy() != null &&
|
||||
this.properties.getProxy().getAuth() != null &&
|
||||
this.properties.getProxy().getAuth().getUsername() != null &&
|
||||
this.properties.getProxy().getAuth().getPassword() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the {@link MavenProperties.RemoteRepository} setting has username/password set.
|
||||
*
|
||||
* @return boolean true if both the username/password are set
|
||||
*/
|
||||
private boolean remoteRepositoryHasCredentials(MavenProperties.RemoteRepository remoteRepository) {
|
||||
return remoteRepository != null &&
|
||||
remoteRepository.getAuth() != null &&
|
||||
remoteRepository.getAuth().getUsername() != null &&
|
||||
remoteRepository.getAuth().getPassword() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link Authentication} given a username/password.
|
||||
*
|
||||
* @param username the user
|
||||
* @param password the password
|
||||
* @return a configured {@link Authentication}
|
||||
*/
|
||||
private Authentication newAuthentication(final String username, final String password) {
|
||||
return new Authentication() {
|
||||
|
||||
@Override
|
||||
public void fill(AuthenticationContext context, String key, Map<String, String> data) {
|
||||
context.put(AuthenticationContext.USERNAME, username);
|
||||
context.put(AuthenticationContext.PASSWORD, password);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void digest(AuthenticationDigest digest) {
|
||||
digest.update(AuthenticationContext.USERNAME, username,
|
||||
AuthenticationContext.PASSWORD, password);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
DefaultRepositorySystemSession newRepositorySystemSession() {
|
||||
return this.newRepositorySystemSession(this.repositorySystem, this.properties.getLocalRepository());
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a session to manage remote and local synchronization.
|
||||
*/
|
||||
private DefaultRepositorySystemSession newRepositorySystemSession(RepositorySystem system, String localRepoPath) {
|
||||
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
|
||||
LocalRepository localRepo = new LocalRepository(localRepoPath);
|
||||
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
|
||||
session.setOffline(this.properties.isOffline());
|
||||
session.setUpdatePolicy(this.properties.getUpdatePolicy());
|
||||
session.setChecksumPolicy(this.properties.getChecksumPolicy());
|
||||
if (this.properties.isEnableRepositoryListener()) {
|
||||
session.setRepositoryListener(new LoggingRepositoryListener());
|
||||
}
|
||||
if (this.properties.getConnectTimeout() != null) {
|
||||
session.setConfigProperty(ConfigurationProperties.CONNECT_TIMEOUT, this.properties.getConnectTimeout());
|
||||
}
|
||||
if (this.properties.getRequestTimeout() != null) {
|
||||
session.setConfigProperty(ConfigurationProperties.REQUEST_TIMEOUT, this.properties.getRequestTimeout());
|
||||
}
|
||||
if (isProxyEnabled()) {
|
||||
DefaultProxySelector proxySelector = new DefaultProxySelector();
|
||||
Proxy proxy = new Proxy(this.properties.getProxy().getProtocol(),
|
||||
this.properties.getProxy().getHost(),
|
||||
this.properties.getProxy().getPort(),
|
||||
this.proxyAuthentication);
|
||||
proxySelector.add(proxy, this.properties.getProxy().getNonProxyHosts());
|
||||
session.setProxySelector(proxySelector);
|
||||
}
|
||||
// wagon configs
|
||||
for (Entry<String, MavenProperties.RemoteRepository> entry : this.properties.getRemoteRepositories().entrySet()) {
|
||||
session.setConfigProperty("aether.connector.wagon.config." + entry.getKey(), entry.getValue().getWagon());
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/*
|
||||
* Aether's components implement {@link org.eclipse.aether.spi.locator.Service} to ease manual wiring.
|
||||
* Using the prepopulated {@link DefaultServiceLocator}, we need to register the repository connector
|
||||
* and transporter factories
|
||||
*/
|
||||
private RepositorySystem newRepositorySystem() {
|
||||
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
|
||||
locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
|
||||
locator.addService(TransporterFactory.class, FileTransporterFactory.class);
|
||||
|
||||
locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
|
||||
|
||||
locator.setErrorHandler(new DefaultServiceLocator.ErrorHandler() {
|
||||
@Override
|
||||
public void serviceCreationFailed(Class<?> type, Class<?> impl, Throwable exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
});
|
||||
return locator.getService(RepositorySystem.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of configured remote repositories.
|
||||
* @return unmodifiable list of configured remote repositories.
|
||||
*/
|
||||
List<RemoteRepository> remoteRepositories() {
|
||||
return Collections.unmodifiableList(this.remoteRepositories);
|
||||
}
|
||||
|
||||
private String actualRemoteRepositoriesDescription() {
|
||||
return this.remoteRepositories.stream().map((repo) -> String.format("%s (%s)", repo.getId(), repo.getUrl()))
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
}
|
||||
|
||||
private String configuredRemoteRepositoriesDescription() {
|
||||
return this.properties.getRemoteRepositories().entrySet().stream()
|
||||
.map((e) -> String.format("%s (%s)", e.getKey(), e.getValue().getUrl()))
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
}
|
||||
|
||||
List<String> getVersions(String coordinates) {
|
||||
Artifact artifact = new DefaultArtifact(coordinates);
|
||||
VersionRangeRequest rangeRequest = new VersionRangeRequest();
|
||||
rangeRequest.setArtifact(artifact);
|
||||
rangeRequest.setRepositories(this.remoteRepositories);
|
||||
try {
|
||||
VersionRangeResult versionResult = this.repositorySystem.resolveVersionRange(newRepositorySystemSession(), rangeRequest);
|
||||
List<String> versions = new ArrayList<>();
|
||||
for (Version version: versionResult.getVersions()) {
|
||||
versions.add(version.toString());
|
||||
}
|
||||
return versions;
|
||||
}
|
||||
catch (VersionRangeResolutionException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an artifact and return its location in the local repository. Aether performs the normal
|
||||
* Maven resolution process ensuring that the latest update is cached to the local repository.
|
||||
* In addition, if the {@code MavenProperties.resolvePom} flag is <code>true</code>,
|
||||
* the POM is also resolved and cached.
|
||||
* @param resource the {@link MavenResource} representing the artifact
|
||||
* @return a {@link FileSystemResource} representing the resolved artifact in the local repository
|
||||
* @throws IllegalStateException if the artifact does not exist or the resolution fails
|
||||
*/
|
||||
Resource resolve(MavenResource resource) {
|
||||
Assert.notNull(resource, "MavenResource must not be null");
|
||||
validateCoordinates(resource);
|
||||
RepositorySystemSession session = newRepositorySystemSession(this.repositorySystem, this.properties.getLocalRepository());
|
||||
try {
|
||||
List<ArtifactRequest> artifactRequests = new ArrayList<>(2);
|
||||
if (properties.isResolvePom()) {
|
||||
artifactRequests.add(new ArtifactRequest(toPomArtifact(resource), this.remoteRepositories, JavaScopes.RUNTIME));
|
||||
}
|
||||
artifactRequests.add(new ArtifactRequest(toJarArtifact(resource), this.remoteRepositories, JavaScopes.RUNTIME));
|
||||
List<ArtifactResult> results = this.repositorySystem.resolveArtifacts(session, artifactRequests);
|
||||
return toResource(results.get(results.size() - 1));
|
||||
}
|
||||
catch (ArtifactResolutionException ex) {
|
||||
String errorMsg = String.format("Failed to resolve %s using remote repo(s): %s",
|
||||
resource, actualRemoteRepositoriesDescription());
|
||||
throw new IllegalStateException(errorMsg, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateCoordinates(MavenResource resource) {
|
||||
Assert.hasText(resource.getGroupId(), "groupId must not be blank.");
|
||||
Assert.hasText(resource.getArtifactId(), "artifactId must not be blank.");
|
||||
Assert.hasText(resource.getExtension(), "extension must not be blank.");
|
||||
Assert.hasText(resource.getVersion(), "version must not be blank.");
|
||||
}
|
||||
|
||||
public FileSystemResource toResource(ArtifactResult resolvedArtifact) {
|
||||
return new FileSystemResource(resolvedArtifact.getArtifact().getFile());
|
||||
}
|
||||
|
||||
private Artifact toJarArtifact(MavenResource resource) {
|
||||
return toArtifact(resource, resource.getExtension());
|
||||
}
|
||||
|
||||
private Artifact toPomArtifact(MavenResource resource) {
|
||||
return toArtifact(resource, "pom");
|
||||
}
|
||||
|
||||
private Artifact toArtifact(MavenResource resource, String extension) {
|
||||
return new DefaultArtifact(resource.getGroupId(),
|
||||
resource.getArtifactId(),
|
||||
resource.getClassifier() != null ? resource.getClassifier() : "",
|
||||
extension,
|
||||
resource.getVersion());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* Copyright 2019-2025 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.deployer.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Configuration Properties for Maven.
|
||||
*
|
||||
* @author Ilayaperumal Gopinathan
|
||||
* @author Eric Bottard
|
||||
* @author Mark Fisher
|
||||
* @author Donovan Muller
|
||||
*/
|
||||
public class MavenProperties {
|
||||
|
||||
/**
|
||||
* Default file path to a locally available maven repository.
|
||||
*/
|
||||
private static String DEFAULT_LOCAL_REPO = System.getProperty("user.home") +
|
||||
File.separator + ".m2" + File.separator + "repository";
|
||||
|
||||
/**
|
||||
* Whether default remote repositories should be automatically included in the list of remote repositories.
|
||||
*/
|
||||
private boolean includeDefaultRemoteRepos = true;
|
||||
|
||||
/**
|
||||
* File path to a locally available maven repository, where artifacts will be downloaded.
|
||||
*/
|
||||
private String localRepository = DEFAULT_LOCAL_REPO;
|
||||
|
||||
/**
|
||||
* Locations of remote maven repositories from which artifacts will be downloaded, if not available locally.
|
||||
*/
|
||||
private Map<String, RemoteRepository> remoteRepositories = new TreeMap<>();
|
||||
|
||||
/**
|
||||
* Whether the resolver should operate in offline mode.
|
||||
*/
|
||||
private boolean offline;
|
||||
|
||||
/**
|
||||
* Proxy configuration properties.
|
||||
*/
|
||||
private Proxy proxy;
|
||||
|
||||
/**
|
||||
* The connect timeout. If <code>null</code>, the underlying default will be used.
|
||||
*/
|
||||
private Integer connectTimeout;
|
||||
|
||||
/**
|
||||
* The request timeout. If <code>null</code>, the underlying default will be used.
|
||||
*/
|
||||
private Integer requestTimeout;
|
||||
|
||||
/**
|
||||
* In addition to resolving the JAR artifact, if true, resolve the POM artifact.
|
||||
* This is consistent with the way that Maven resolves artifacts.
|
||||
*/
|
||||
private boolean resolvePom;
|
||||
|
||||
private String updatePolicy;
|
||||
|
||||
private String checksumPolicy;
|
||||
|
||||
/**
|
||||
* Add the ConsoleRepositoryListener to the session for debugging of artifact resolution.
|
||||
*/
|
||||
private boolean enableRepositoryListener = false;
|
||||
|
||||
boolean isIncludeDefaultRemoteRepos() {
|
||||
return includeDefaultRemoteRepos;
|
||||
}
|
||||
|
||||
void setIncludeDefaultRemoteRepos(boolean includeDefaultRemoteRepos) {
|
||||
this.includeDefaultRemoteRepos = includeDefaultRemoteRepos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use maven wagon based transport for http based artifacts.
|
||||
*/
|
||||
private boolean useWagon;
|
||||
|
||||
public void setUseWagon(boolean useWagon) {
|
||||
this.useWagon = useWagon;
|
||||
}
|
||||
|
||||
public boolean isUseWagon() {
|
||||
return useWagon;
|
||||
}
|
||||
|
||||
public boolean isEnableRepositoryListener() {
|
||||
return enableRepositoryListener;
|
||||
}
|
||||
|
||||
public void setEnableRepositoryListener(boolean enableRepositoryListener) {
|
||||
this.enableRepositoryListener = enableRepositoryListener;
|
||||
}
|
||||
|
||||
public String getUpdatePolicy() {
|
||||
return updatePolicy;
|
||||
}
|
||||
|
||||
public void setUpdatePolicy(String updatePolicy) {
|
||||
this.updatePolicy = updatePolicy;
|
||||
}
|
||||
|
||||
public String getChecksumPolicy() {
|
||||
return checksumPolicy;
|
||||
}
|
||||
|
||||
public void setChecksumPolicy(String checksumPolicy) {
|
||||
this.checksumPolicy = checksumPolicy;
|
||||
}
|
||||
|
||||
public Map<String, RemoteRepository> getRemoteRepositories() {
|
||||
return remoteRepositories;
|
||||
}
|
||||
|
||||
public void setRemoteRepositories(final Map<String, RemoteRepository> remoteRepositories) {
|
||||
this.remoteRepositories = new TreeMap<>(remoteRepositories);
|
||||
}
|
||||
|
||||
public void setLocalRepository(String localRepository) {
|
||||
this.localRepository = localRepository;
|
||||
}
|
||||
|
||||
public String getLocalRepository() {
|
||||
return localRepository;
|
||||
}
|
||||
|
||||
public boolean isOffline() {
|
||||
return offline;
|
||||
}
|
||||
|
||||
public void setOffline(Boolean offline) {
|
||||
this.offline = offline;
|
||||
}
|
||||
|
||||
public Integer getConnectTimeout() {
|
||||
return this.connectTimeout;
|
||||
}
|
||||
|
||||
public void setConnectTimeout(Integer connectTimeout) {
|
||||
this.connectTimeout = connectTimeout;
|
||||
}
|
||||
|
||||
public Integer getRequestTimeout() {
|
||||
return this.requestTimeout;
|
||||
}
|
||||
|
||||
public void setRequestTimeout(Integer requestTimeout) {
|
||||
this.requestTimeout = requestTimeout;
|
||||
}
|
||||
|
||||
public Proxy getProxy() {
|
||||
return this.proxy;
|
||||
}
|
||||
|
||||
public void setProxy(Proxy proxy) {
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
public boolean isResolvePom() {
|
||||
return resolvePom;
|
||||
}
|
||||
|
||||
public void setResolvePom(final boolean resolvePom) {
|
||||
this.resolvePom = resolvePom;
|
||||
}
|
||||
|
||||
public static class Proxy {
|
||||
|
||||
/**
|
||||
* Protocol to use for proxy settings.
|
||||
*/
|
||||
private String protocol = "http";
|
||||
|
||||
/**
|
||||
* Host for the proxy.
|
||||
*/
|
||||
private String host;
|
||||
|
||||
/**
|
||||
* Port for the proxy.
|
||||
*/
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* List of non proxy hosts.
|
||||
*/
|
||||
private String nonProxyHosts;
|
||||
|
||||
private Authentication auth;
|
||||
|
||||
public String getProtocol() {
|
||||
return this.protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getNonProxyHosts() {
|
||||
return this.nonProxyHosts;
|
||||
}
|
||||
|
||||
public void setNonProxyHosts(String nonProxyHosts) {
|
||||
this.nonProxyHosts = nonProxyHosts;
|
||||
}
|
||||
|
||||
public Authentication getAuth() {
|
||||
return this.auth;
|
||||
}
|
||||
|
||||
public void setAuth(Authentication auth) {
|
||||
this.auth = auth;
|
||||
}
|
||||
}
|
||||
|
||||
public enum WagonHttpMethod {
|
||||
// directly maps to http methods in org.apache.maven.wagon.shared.http.HttpConfiguration
|
||||
/**
|
||||
* All methods.
|
||||
*/
|
||||
all,
|
||||
/**
|
||||
* GET method.
|
||||
*/
|
||||
get,
|
||||
/**
|
||||
* PUT method.
|
||||
*/
|
||||
put,
|
||||
/**
|
||||
* HEAD method.
|
||||
*/
|
||||
head;
|
||||
}
|
||||
|
||||
public static class WagonHttpMethodProperties {
|
||||
// directly maps to settings in org.apache.maven.wagon.shared.http.HttpMethodConfiguration
|
||||
private boolean usePreemptive;
|
||||
private boolean useDefaultHeaders;
|
||||
private Integer connectionTimeout;
|
||||
private Integer readTimeout;
|
||||
private Map<String, String> headers = new HashMap<>();
|
||||
private Map<String, String> params = new HashMap<>();
|
||||
|
||||
public boolean isUsePreemptive() {
|
||||
return usePreemptive;
|
||||
}
|
||||
|
||||
public void setUsePreemptive(boolean usePreemptive) {
|
||||
this.usePreemptive = usePreemptive;
|
||||
}
|
||||
|
||||
public boolean isUseDefaultHeaders() {
|
||||
return useDefaultHeaders;
|
||||
}
|
||||
|
||||
public void setUseDefaultHeaders(boolean useDefaultHeaders) {
|
||||
this.useDefaultHeaders = useDefaultHeaders;
|
||||
}
|
||||
|
||||
public Integer getConnectionTimeout() {
|
||||
return connectionTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionTimeout(Integer connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
}
|
||||
|
||||
public Integer getReadTimeout() {
|
||||
return readTimeout;
|
||||
}
|
||||
|
||||
public void setReadTimeout(Integer readTimeout) {
|
||||
this.readTimeout = readTimeout;
|
||||
}
|
||||
|
||||
public Map<String, String> getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public void setHeaders(Map<String, String> headers) {
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
public Map<String, String> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams(Map<String, String> params) {
|
||||
this.params = params;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Wagon {
|
||||
|
||||
private Map<WagonHttpMethod, WagonHttpMethodProperties> http = new HashMap<>();
|
||||
|
||||
public Map<WagonHttpMethod, WagonHttpMethodProperties> getHttp() {
|
||||
return http;
|
||||
}
|
||||
|
||||
public void setHttp(Map<WagonHttpMethod, WagonHttpMethodProperties> http) {
|
||||
this.http = http;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RemoteRepository {
|
||||
|
||||
/**
|
||||
* URL of the remote maven repository. E.g. https://my.repo.com
|
||||
*/
|
||||
private String url;
|
||||
|
||||
private Authentication auth;
|
||||
|
||||
private RepositoryPolicy policy;
|
||||
|
||||
private RepositoryPolicy snapshotPolicy;
|
||||
|
||||
private RepositoryPolicy releasePolicy;
|
||||
|
||||
private Wagon wagon = new Wagon();
|
||||
|
||||
public RemoteRepository() {
|
||||
}
|
||||
|
||||
public RemoteRepository(final String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public RemoteRepository(final String url, final Authentication auth) {
|
||||
this.url = url;
|
||||
this.auth = auth;
|
||||
}
|
||||
|
||||
public Wagon getWagon() {
|
||||
return wagon;
|
||||
}
|
||||
|
||||
public void setWagon(Wagon wagon) {
|
||||
this.wagon = wagon;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(final String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public Authentication getAuth() {
|
||||
return auth;
|
||||
}
|
||||
|
||||
public void setAuth(final Authentication auth) {
|
||||
this.auth = auth;
|
||||
}
|
||||
|
||||
public RepositoryPolicy getPolicy() {
|
||||
return policy;
|
||||
}
|
||||
|
||||
public void setPolicy(RepositoryPolicy policy) {
|
||||
this.policy = policy;
|
||||
}
|
||||
|
||||
public RepositoryPolicy getSnapshotPolicy() {
|
||||
return snapshotPolicy;
|
||||
}
|
||||
|
||||
public void setSnapshotPolicy(RepositoryPolicy snapshotPolicy) {
|
||||
this.snapshotPolicy = snapshotPolicy;
|
||||
}
|
||||
|
||||
public RepositoryPolicy getReleasePolicy() {
|
||||
return releasePolicy;
|
||||
}
|
||||
|
||||
public void setReleasePolicy(RepositoryPolicy releasePolicy) {
|
||||
this.releasePolicy = releasePolicy;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RepositoryPolicy {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
private String updatePolicy = "always";
|
||||
|
||||
private String checksumPolicy = "warn";
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getUpdatePolicy() {
|
||||
return updatePolicy;
|
||||
}
|
||||
|
||||
public void setUpdatePolicy(String updatePolicy) {
|
||||
this.updatePolicy = updatePolicy;
|
||||
}
|
||||
|
||||
public String getChecksumPolicy() {
|
||||
return checksumPolicy;
|
||||
}
|
||||
|
||||
public void setChecksumPolicy(String checksumPolicy) {
|
||||
this.checksumPolicy = checksumPolicy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Authentication {
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
public Authentication() {
|
||||
}
|
||||
|
||||
public Authentication(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright 2019-2025 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.deployer.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link Resource} implementation for resolving an artifact via maven coordinates.
|
||||
* <p>
|
||||
* The {@code MavenResource} class contains <a href="https://maven.apache.org/pom.html#Maven_Coordinates">
|
||||
* Maven coordinates</a> for a jar file containing an app/library, or a Bill of Materials pom.
|
||||
* <p>
|
||||
* To create a new instance, either use {@link Builder} to set the individual fields:
|
||||
* <pre>
|
||||
* new MavenResource.Builder()
|
||||
* .setGroupId("org.springframework.sample")
|
||||
* .setArtifactId("some-app")
|
||||
* .setExtension("jar") //optional
|
||||
* .setClassifier("exec") //optional
|
||||
* .setVersion("2.0.0")
|
||||
* .build()
|
||||
* </pre>
|
||||
* ...or use {@link #parse(String)} to parse the coordinates as a colon delimited string:
|
||||
* <code><groupId>:<artifactId>[:<extension>[:<classifier>]]:<version></code>
|
||||
* <pre>
|
||||
* MavenResource.parse("org.springframework.sample:some-app:2.0.0);
|
||||
* MavenResource.parse("org.springframework.sample:some-app:jar:exec:2.0.0);
|
||||
* </pre>
|
||||
* @author David Turanski
|
||||
* @author Mark Fisher
|
||||
* @author Patrick Peralta
|
||||
* @author Venil Noronha
|
||||
* @author Ilayaperumal Gopinathan
|
||||
*/
|
||||
public final class MavenResource extends AbstractResource {
|
||||
|
||||
/**
|
||||
* URI Scheme.
|
||||
*/
|
||||
public static String URI_SCHEME = "maven";
|
||||
|
||||
/**
|
||||
* The default extension for the artifact.
|
||||
*/
|
||||
final static String DEFAULT_EXTENSION = "jar";
|
||||
|
||||
/**
|
||||
* String representing an empty classifier.
|
||||
*/
|
||||
final static String EMPTY_CLASSIFIER = "";
|
||||
|
||||
/**
|
||||
* Group ID for artifact; generally this includes the name of the
|
||||
* organization that generated the artifact.
|
||||
*/
|
||||
private final String groupId;
|
||||
|
||||
/**
|
||||
* Artifact ID; generally this includes the name of the app or library.
|
||||
*/
|
||||
private final String artifactId;
|
||||
|
||||
/**
|
||||
* Extension of the artifact.
|
||||
*/
|
||||
private final String extension;
|
||||
|
||||
/**
|
||||
* Classifier of the artifact.
|
||||
*/
|
||||
private final String classifier;
|
||||
|
||||
/**
|
||||
* Version of the artifact.
|
||||
*/
|
||||
private final String version;
|
||||
|
||||
private final MavenArtifactResolver resolver;
|
||||
|
||||
/**
|
||||
* Construct a {@code MavenResource} object.
|
||||
*
|
||||
* @param groupId group ID for artifact
|
||||
* @param artifactId artifact ID
|
||||
* @param extension the file extension
|
||||
* @param classifier artifact classifier - can be null
|
||||
* @param version artifact version
|
||||
* @param properties Maven configuration properties
|
||||
*/
|
||||
private MavenResource(String groupId, String artifactId, String extension, String classifier,
|
||||
String version, MavenProperties properties) {
|
||||
Assert.hasText(groupId, "groupId must not be blank");
|
||||
Assert.hasText(artifactId, "artifactId must not be blank");
|
||||
Assert.hasText(extension, "extension must not be blank");
|
||||
Assert.hasText(version, "version must not be blank");
|
||||
this.groupId = groupId;
|
||||
this.artifactId = artifactId;
|
||||
this.extension = extension;
|
||||
this.classifier = classifier == null ? EMPTY_CLASSIFIER : classifier;
|
||||
this.version = version;
|
||||
this.resolver = new MavenArtifactResolver(properties != null ? properties : new MavenProperties());
|
||||
}
|
||||
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public String getArtifactId() {
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return extension;
|
||||
}
|
||||
|
||||
public String getClassifier() {
|
||||
return classifier;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return resolver.resolve(this).getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFile() throws IOException {
|
||||
return resolver.resolve(this).getFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return StringUtils.hasLength(classifier) ?
|
||||
String.format("%s-%s-%s.%s", artifactId, version, classifier, extension) :
|
||||
String.format("%s-%s.%s", artifactId, version, extension);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
try {
|
||||
return super.exists();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Resource.exists() has no throws clause, so return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof MavenResource)) {
|
||||
return false;
|
||||
}
|
||||
MavenResource that = (MavenResource) o;
|
||||
return this.groupId.equals(that.groupId) &&
|
||||
this.artifactId.equals(that.artifactId) &&
|
||||
this.extension.equals(that.extension) &&
|
||||
this.classifier.equals(that.classifier) &&
|
||||
this.version.equals(that.version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = groupId.hashCode();
|
||||
result = 31 * result + artifactId.hashCode();
|
||||
result = 31 * result + extension.hashCode();
|
||||
if (StringUtils.hasLength(classifier)) {
|
||||
result = 31 * result + classifier.hashCode();
|
||||
}
|
||||
result = 31 * result + version.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the coordinates encoded as
|
||||
* <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>,
|
||||
* conforming to the <a href="https://www.eclipse.org/aether">Aether</a> convention.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringUtils.hasLength(classifier) ?
|
||||
String.format("%s:%s:%s:%s:%s", groupId, artifactId, extension, classifier, version) :
|
||||
String.format("%s:%s:%s:%s", groupId, artifactId, extension, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() throws IOException {
|
||||
return URI.create(URI_SCHEME + "://" + toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link MavenResource} for the provided coordinates and default properties.
|
||||
*
|
||||
* @param coordinates coordinates encoded as <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>,
|
||||
* conforming to the <a href="https://www.eclipse.org/aether">Aether</a> convention.
|
||||
* @return the {@link MavenResource}
|
||||
*/
|
||||
public static MavenResource parse(String coordinates) {
|
||||
return parse(coordinates, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link MavenResource} for the provided coordinates and properties.
|
||||
*
|
||||
* @param coordinates coordinates encoded as <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>,
|
||||
* conforming to the <a href="https://www.eclipse.org/aether">Aether</a> convention.
|
||||
* @param properties the properties for the repositories, proxies, and authentication
|
||||
* @return the {@link MavenResource}
|
||||
*/
|
||||
public static MavenResource parse(String coordinates, MavenProperties properties) {
|
||||
Assert.hasText(coordinates, "coordinates are required");
|
||||
Pattern p = Pattern.compile("([^: ]+):([^: ]+)(:([^: ]*)(:([^: ]+))?)?:([^: ]+)");
|
||||
Matcher m = p.matcher(coordinates);
|
||||
Assert.isTrue(m.matches(), "Bad artifact coordinates " + coordinates
|
||||
+ ", expected format is <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>");
|
||||
String groupId = m.group(1);
|
||||
String artifactId = m.group(2);
|
||||
String extension = StringUtils.hasLength(m.group(4)) ? m.group(4) : DEFAULT_EXTENSION;
|
||||
String classifier = StringUtils.hasLength(m.group(6)) ? m.group(6) : EMPTY_CLASSIFIER;
|
||||
String version = m.group(7);
|
||||
return new MavenResource(groupId, artifactId, extension, classifier, version, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the available versions on this maven co-ordinate.
|
||||
* @param coordinates the co-ordinate with the version constraint added.
|
||||
* Example: org.springframework.cloud.stream.app:http-source-rabbit:[0,)
|
||||
* @return the list of all the available versions
|
||||
*/
|
||||
public List<String> getVersions(String coordinates) {
|
||||
return this.resolver.getVersions(coordinates);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private String groupId;
|
||||
|
||||
private String artifactId;
|
||||
|
||||
private String extension = DEFAULT_EXTENSION;
|
||||
|
||||
private String classifier = EMPTY_CLASSIFIER;
|
||||
|
||||
private String version;
|
||||
|
||||
private final MavenProperties properties;
|
||||
|
||||
public Builder() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public Builder(MavenProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public Builder groupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder artifactId(String artifactId) {
|
||||
this.artifactId = artifactId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder extension(String extension) {
|
||||
this.extension = extension;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder classifier(String classifier) {
|
||||
this.classifier = classifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder version(String version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MavenResource build() {
|
||||
return new MavenResource(groupId, artifactId, extension, classifier, version, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2019-2025 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.deployer.utils;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A {@link ResourceLoader} that loads {@link MavenResource}s from locations of the format
|
||||
* {@literal maven://<coordinates>} where the value for "coordinates" conforms to the rules
|
||||
* described on {@link MavenResource#parse(String)} .
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public class MavenResourceLoader implements ResourceLoader {
|
||||
|
||||
private static final String URI_SCHEME = "maven";
|
||||
|
||||
private final MavenProperties properties;
|
||||
|
||||
private final ClassLoader classLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
/**
|
||||
* Create a {@link MavenResourceLoader} that uses the provided {@link MavenProperties}.
|
||||
*
|
||||
* @param properties the {@link MavenProperties} to use when instantiating {@link MavenResource}s
|
||||
*/
|
||||
public MavenResourceLoader(MavenProperties properties) {
|
||||
Assert.notNull(properties, "MavenProperties must not be null");
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link MavenResource} for the provided location.
|
||||
*
|
||||
* @param location the coordinates conforming to the rules described on
|
||||
* {@link MavenResource#parse(String)}. May optionally be preceded by {@value #URI_SCHEME}
|
||||
* followed by a colon and zero or more forward slashes, e.g.
|
||||
* {@literal maven://group:artifact:version}
|
||||
* @return the {@link MavenResource}
|
||||
*/
|
||||
@Override
|
||||
public Resource getResource(String location) {
|
||||
Assert.hasText(location, "location is required");
|
||||
String coordinates = location.replaceFirst(URI_SCHEME + ":\\/*", "");
|
||||
return MavenResource.parse(coordinates, this.properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ClassLoader} for this ResourceLoader.
|
||||
*/
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return this.classLoader;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,9 +32,9 @@ import reactor.util.function.Tuples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.deployer.resource.maven.MavenProperties;
|
||||
import org.springframework.cloud.function.cloudevent.CloudEventMessageBuilder;
|
||||
import org.springframework.cloud.function.context.FunctionCatalog;
|
||||
import org.springframework.cloud.function.deployer.utils.MavenProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
Reference in New Issue
Block a user