com.oracle.bedrock.maven.Maven Maven / Gradle / Ivy
/*
* File: Maven.java
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* The contents of this file are subject to the terms and conditions of
* the Common Development and Distribution License 1.0 (the "License").
*
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the License by consulting the LICENSE.txt file
* distributed with this file, or by consulting https://oss.oracle.com/licenses/CDDL
*
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file LICENSE.txt.
*
* MODIFICATIONS:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*/
package com.oracle.bedrock.maven;
import com.oracle.bedrock.ComposableOption;
import com.oracle.bedrock.Option;
import com.oracle.bedrock.OptionsByType;
import com.oracle.bedrock.runtime.Application;
import com.oracle.bedrock.runtime.MetaClass;
import com.oracle.bedrock.runtime.Platform;
import com.oracle.bedrock.runtime.Profile;
import com.oracle.bedrock.runtime.java.ClassPath;
import com.oracle.bedrock.runtime.java.JavaApplication;
import com.oracle.bedrock.runtime.options.PlatformSeparators;
import org.apache.maven.settings.Repository;
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.SettingsBuilder;
import org.apache.maven.settings.building.SettingsBuildingException;
import org.eclipse.aether.DefaultRepositoryCache;
import org.eclipse.aether.RepositoryException;
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.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactDescriptorResult;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.VersionRangeRequest;
import org.eclipse.aether.resolution.VersionRangeResult;
import org.eclipse.aether.spi.artifact.decorator.ArtifactDecorator;
import org.eclipse.aether.spi.artifact.decorator.ArtifactDecoratorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.supplier.RepositorySystemSupplier;
import org.eclipse.aether.supplier.SessionBuilderSupplier;
import org.eclipse.aether.transport.jdk.JdkTransporterFactory;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.eclipse.aether.util.filter.DependencyFilterUtils;
import org.eclipse.aether.version.Version;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The Maven {@link Profile} provides the ability define a Maven environment, configuration,
* together with zero or more {@link Artifact}s to be resolved to automatically construct a
* {@link ClassPath} for a {@link JavaApplication}.
*
* {@link Maven} {@link Profile}s are {@link ComposableOption}s. When multiple are provided
* as a parameter, they will be composed in order of declaration into a single new instance.
*
* Copyright (c) 2016. All Rights Reserved. Oracle Corporation.
* Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
*
* @author Brian Oliver
*/
public class Maven implements Profile, ComposableOption
{
/**
* The {@link Logger} for this class.
*/
private static final Logger LOGGER = Logger.getLogger(Maven.class.getName());
/**
* The {@link File} for the user settings (ie: settings.xml)
*
* When null
we assume ${user.home}/.m2/settings.xml
*/
private File userSettingsFile;
/**
* The {@link File} for the global settings
*
* When null
we assume M2_HOME/conf/settings.xml
*/
private File globalSettingsFile;
/**
* Should we operate in offline mode?
*
* When null
we assume online (false)
*/
private Boolean isOffline;
/**
* The scope to use when resolving {@link Artifact}s.
*
* When null
we assume {@link JavaScopes#RUNTIME}.
*/
private String scope;
/**
* The list of {@link Artifact}s, indexed by group, artifact, classifier and extension,
* thus allowing overriding of artifacts with different versions.
*/
private final LinkedHashMap artifacts;
/**
* The additional {@link ClassPath} to add to the {@link ClassPath} of the resolved artifacts.
*/
private ClassPath additionalClassPath;
/**
* Constructs a {@link Maven} {@link Profile} based on another {@link Maven} {@link Profile}.
*
* @param maven the {@link Maven} {@link Profile}
*/
private Maven(Maven maven)
{
this(maven.globalSettingsFile,
maven.userSettingsFile,
maven.isOffline,
maven.scope,
maven.artifacts,
maven.additionalClassPath);
}
/**
* Constructs a {@link Maven} {@link Profile}.
*
* @param globalSettingsFile the location of the global settings.xml (use {@code null} for the default)
* @param userSettingsFile the location of the user settings.xml (use {@code null} for the default)
* @param isOffline if {@link Maven} is to operate in offline-mode (use {@code null} for default)
* @param scope the scope to use for resolving artifacts
* @param artifacts the table of {@link Artifact}s
* @param additionalClassPath the additional {@link ClassPath} to add to the resolved artifacts
*/
private Maven(File globalSettingsFile,
File userSettingsFile,
Boolean isOffline,
String scope,
LinkedHashMap artifacts,
ClassPath additionalClassPath)
{
this.globalSettingsFile = globalSettingsFile;
this.userSettingsFile = userSettingsFile;
this.isOffline = isOffline;
this.scope = scope;
this.artifacts = new LinkedHashMap<>();
this.additionalClassPath = additionalClassPath;
// add all of the artifacts (if there are any)
if (artifacts != null)
{
this.artifacts.putAll(artifacts);
}
}
/**
* Obtain the Maven {@link Settings} for this {@link Maven} profile
* given the specified {@link OptionsByType}.
*
* @param optionsByType the launch {@link OptionsByType}
*
* @return a new {@link Settings}
*/
private Settings getSettings(OptionsByType optionsByType)
{
SettingsBuilder settingsBuilder = new DefaultSettingsBuilderFactory().newInstance();
DefaultSettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
request.setGlobalSettingsFile(globalSettingsFile);
request.setUserSettingsFile(userSettingsFile);
// we use the system-properties of the current process to resolve artifacts
request.setSystemProperties(System.getProperties());
Settings settings;
try
{
settings = settingsBuilder.build(request).getEffectiveSettings();
return settings;
}
catch (SettingsBuildingException e)
{
LOGGER.log(Level.WARNING, "Could not process settings.xml: " + e.getMessage(), e);
throw new IllegalArgumentException("Could not process the settings.xml", e);
}
}
/**
* Obtains the {@link RepositorySystem} we'll use for resolving {@link Artifact}s.
*
* @return a new {@link RepositorySystem}.
*/
private RepositorySystem newRepositorySystem()
{
return new RepositorySystemSupplier() {
@Override
protected Map createArtifactDecoratorFactories() {
Map result = super.createArtifactDecoratorFactories();
result.put("color", new ArtifactDecoratorFactory() {
@Override
public ArtifactDecorator newInstance(RepositorySystemSession session) {
return new ArtifactDecorator() {
@Override
public Artifact decorateArtifact(ArtifactDescriptorResult artifactDescriptorResult) {
Map properties = new HashMap<>(
artifactDescriptorResult.getArtifact().getProperties());
properties.put("color", "red");
return artifactDescriptorResult.getArtifact().setProperties(properties);
}
};
}
@Override
public float getPriority() {
return 0;
}
});
return result;
}
@Override
protected Map createTransporterFactories() {
Map result = super.createTransporterFactories();
result.put(
JdkTransporterFactory.NAME,
new JdkTransporterFactory(getChecksumExtractor(), getPathProcessor()));
return result;
}
}.get();
}
@SuppressWarnings("deprecation")
@Override
public void onLaunching(Platform platform,
MetaClass metaClass,
OptionsByType optionsByType)
{
// resolve the class path based on the required maven artifacts
try
{
perform(
(system, session, repositories, scope) -> {
// we only filter based on the scope
DependencyFilter filter = DependencyFilterUtils.classpathFilter(scope);
// collect class paths for each resolved artifact
LinkedHashSet artifactPaths = new LinkedHashSet<>();
for (Artifact artifact : artifacts.values())
{
ArtifactRequest artifactRequest = new ArtifactRequest();
artifactRequest.setArtifact(artifact);
artifactRequest.setRepositories(repositories);
ArtifactResult artifactResult = system.resolveArtifact(session, artifactRequest);
Artifact actualArtifact = artifactResult.getArtifact();
artifactPaths.add(ClassPath.ofPath(actualArtifact.getPath()));
CollectRequest collectRequest = new CollectRequest();
collectRequest.setRoot(new Dependency(artifact, scope));
collectRequest.setRepositories(repositories);
DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, filter);
List artifactResults = system.resolveDependencies(session,
dependencyRequest)
.getArtifactResults();
//artifactPaths.add(ClassPath.ofPath(artifact.getPath()));
for (ArtifactResult dependencyResult : artifactResults)
{
artifactPaths.add(ClassPath.ofPath(dependencyResult.getArtifact().getPath()));
}
}
// define the ClassPath based on the artifact paths
ClassPath classPath = new ClassPath(artifactPaths);
// add the additional ClassPaths (when defined)
classPath = additionalClassPath == null ? classPath : new ClassPath(classPath, additionalClassPath);
optionsByType.add(classPath);
},
optionsByType);
}
catch (RepositoryException e)
{
throw new RuntimeException("Failed to resolve artifact", e);
}
}
@Override
public void onLaunched(Platform platform,
Application application,
OptionsByType optionsByType)
{
// nothing to do after launch
}
@Override
public void onClosing(Platform platform,
Application application,
OptionsByType optionsByType)
{
// nothing to do before closing
}
/**
* Create a {@link Maven} {@link Profile} consisting of the specified {@link Artifact},
* parsed from the {@link String} in the following format:
* {@code :[:[:]]:}.
*
* @param artifact the g:a[:e][:c]:v coordinates of the {@link Artifact}
*
* @return a new {@link Maven} {@link Profile}
*/
public static Maven artifact(String artifact)
{
Maven maven = Maven.autoDetect();
DefaultArtifact defaultArtifact = new DefaultArtifact(artifact);
maven.artifacts.put(getArtifactIdentity(defaultArtifact), new DefaultArtifact(artifact));
return maven;
}
/**
* Create a {@link Maven} {@link Profile} consisting of the artifact specified groupId,
* artifactId and version (using the default extension .jar and no classifier)
*
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
*
* @return a new {@link Maven} {@link Profile}
*/
public static Maven artifact(String groupId,
String artifactId,
String version)
{
Maven maven = Maven.autoDetect();
DefaultArtifact defaultArtifact = new DefaultArtifact(groupId, artifactId, "jar", version);
maven.artifacts.put(getArtifactIdentity(defaultArtifact), defaultArtifact);
return maven;
}
/**
* Create a {@link Maven} {@link Profile} consisting of the artifact specified groupId,
* artifactId, version and extension (without a classifier)
*
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
* @param extension the extension
*
* @return a new {@link Maven} {@link Profile}
*/
public static Maven artifact(String groupId,
String artifactId,
String version,
String extension)
{
Maven maven = Maven.autoDetect();
DefaultArtifact defaultArtifact = new DefaultArtifact(groupId, artifactId, extension, version);
maven.artifacts.put(getArtifactIdentity(defaultArtifact), defaultArtifact);
return maven;
}
/**
* Create a {@link Maven} {@link Profile} consisting of the artifact specified groupId,
* artifactId, version, extension and classifier
*
* @param groupId the group id
* @param artifactId the artifact id
* @param version the version
* @param extension the extension
* @param classifier the classifier
*
* @return a new {@link Maven} {@link Profile}
*/
public static Maven artifact(String groupId,
String artifactId,
String version,
String extension,
String classifier)
{
Maven maven = Maven.autoDetect();
DefaultArtifact defaultArtifact = new DefaultArtifact(groupId, artifactId, classifier, extension, version);
maven.artifacts.put(getArtifactIdentity(defaultArtifact), defaultArtifact);
return maven;
}
/**
* Create a default {@link Maven} {@link Profile} that is offline.
*
* @return an offline {@link Maven} {@link Profile}
*/
public static Maven offline()
{
Maven maven = Maven.autoDetect();
maven.isOffline = true;
return maven;
}
/**
* Create a default {@link Maven} {@link Profile} using the specified offline status
*
* @param isOffline true
if the {@link Maven} {@link Profile} should be offline,
* false
otherwise
*
* @return a {@link Maven} {@link Profile} with the specified offline status
*/
public static Maven offline(boolean isOffline)
{
Maven maven = Maven.autoDetect();
maven.isOffline = isOffline;
return maven;
}
/**
* Create a default {@link Maven} {@link Profile} and use the specified user settings.xml location.
*
* @param settingsFileLocation the location of the Maven settings.xml file, or null
* to use the default/auto-detect
*
* @return a {@link Maven} {@link Profile} with the specified user settings file
*/
public static Maven settings(String settingsFileLocation)
{
Maven maven = Maven.autoDetect();
maven.userSettingsFile = settingsFileLocation == null ? null : new File(settingsFileLocation);
return maven;
}
/**
* Create a default {@link Maven} {@link Profile} and use the specified user settings.xml location.
*
* @param settingsFileLocation the location of the Maven settings.xml file, or null
* to use the default/auto-detect
*
* @return a {@link Maven} {@link Profile} with the specified user settings file
*/
public static Maven settings(File settingsFileLocation)
{
Maven maven = Maven.autoDetect();
maven.userSettingsFile = settingsFileLocation;
return maven;
}
/**
* Create a default {@link Maven} {@link Profile} and use the specified scope when resolving
* artifacts.
*
* @param scope the Maven Scope for artifact resolution, or null
to use the default
* (which is RUNTIME)
*
* @return a {@link Maven} {@link Profile} with the specified scope
*
* @see JavaScopes
*/
public static Maven scope(String scope)
{
Maven maven = Maven.autoDetect();
maven.scope = scope;
return maven;
}
/**
* Create a default {@link Maven} {@link Profile} that includes the specified {@link ClassPath}, that is
* included after any resolved Maven artifacts.
*
* @param classPath the additional {@link ClassPath}
*
* @return a {@link Maven} {@link Profile} with the additional {@link ClassPath}
*/
public static Maven include(ClassPath classPath)
{
Maven maven = Maven.autoDetect();
maven.additionalClassPath = classPath;
return maven;
}
/**
* Creates a {@link Maven} profile based on and composed by a number of other
* {@link Maven} profiles.
*
* @param mavens the other {@link Maven} profiles
*
* @return a new {@link Maven} profile
*/
public static Maven from(Maven... mavens)
{
Maven maven = autoDetect();
if (maven != null)
{
// compose maven options
for (Maven m : mavens)
{
maven = maven.compose(m);
}
}
return maven;
}
/**
* Obtains a {@link Maven} {@link Profile}, automatically detecting the
* configuration from the underlying environment, without any {@link Artifact}s
* defined, and being online by default.
*
* @return a {@link Maven} {@link Profile}
*/
@OptionsByType.Default
public static Maven autoDetect()
{
return new Maven(null, null, null, null, null, null);
}
@Override
public Maven compose(Maven other)
{
// construct a new Maven profile
Maven maven = new Maven(this);
// override the global settings
// (if defined by the other Maven profile)
if (other.globalSettingsFile != null)
{
maven.globalSettingsFile = other.globalSettingsFile;
}
// override the global settings
// (if defined by the other Maven profile)
if (other.userSettingsFile != null)
{
maven.userSettingsFile = other.userSettingsFile;
}
// override the scope
// (if defined by the other Maven profile)
if (other.scope != null)
{
maven.scope = other.scope;
}
// override the offline state
// (if defined by the other Maven profile)
if (other.isOffline != null)
{
maven.isOffline = other.isOffline;
}
// add all of the artifacts
// (we combine them together)
for (Artifact artifact : other.artifacts.values())
{
maven.artifacts.put(getArtifactIdentity(artifact), artifact);
}
// include the additional ClassPath
if (other.additionalClassPath != null)
{
maven.additionalClassPath = maven.additionalClassPath == null
? other.additionalClassPath : new ClassPath(maven.additionalClassPath,
other.additionalClassPath);
}
return maven;
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (!(o instanceof Maven))
{
return false;
}
Maven maven = (Maven) o;
if (userSettingsFile != null
? !userSettingsFile.equals(maven.userSettingsFile) : maven.userSettingsFile != null)
{
return false;
}
if (globalSettingsFile != null
? !globalSettingsFile.equals(maven.globalSettingsFile) : maven.globalSettingsFile != null)
{
return false;
}
if (isOffline != null ? !isOffline.equals(maven.isOffline) : maven.isOffline != null)
{
return false;
}
if (scope != null ? !scope.equals(maven.scope) : maven.scope != null)
{
return false;
}
if (artifacts != null ? !artifacts.equals(maven.artifacts) : maven.artifacts != null)
{
return false;
}
return additionalClassPath != null
? additionalClassPath.equals(maven.additionalClassPath) : maven.additionalClassPath == null;
}
@Override
public int hashCode()
{
int result = userSettingsFile != null ? userSettingsFile.hashCode() : 0;
result = 31 * result + (globalSettingsFile != null ? globalSettingsFile.hashCode() : 0);
result = 31 * result + (isOffline != null ? isOffline.hashCode() : 0);
result = 31 * result + (scope != null ? scope.hashCode() : 0);
result = 31 * result + (artifacts != null ? artifacts.hashCode() : 0);
result = 31 * result + (additionalClassPath != null ? additionalClassPath.hashCode() : 0);
return result;
}
/**
* Obtains the identity of an {@link Artifact}, not including the version.
*
* @param artifact the {@link Artifact}
*
* @return a {@link String}-based identity of an {@link Artifact}
*/
private static String getArtifactIdentity(Artifact artifact)
{
if (artifact == null)
{
return "";
}
else
{
return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getClassifier() + ":"
+ artifact.getExtension();
}
}
/**
* Performs the specified {@link RepositorySystemOperation} against the Maven repository
* using the settings defined by the profile.
*
* @throws RepositoryException when an exception occurs interacting with the repository
*/
private void perform(RepositorySystemOperation operation,
OptionsByType optionsByType) throws RepositoryException
{
// obtain the PlatformSeparators
PlatformSeparators separators = optionsByType.get(PlatformSeparators.class);
// define the global settings location if it's not defined
if (globalSettingsFile == null)
{
// determine the location of the Maven Home
String mavenHome = System.getenv("M2_HOME");
globalSettingsFile = new File(System.getProperty("maven.home",
mavenHome != null ? mavenHome : ""),
"conf" + separators.getFileSeparator() + "settings.xml");
}
// define the user settings location if it's not defined
if (userSettingsFile == null)
{
userSettingsFile = new File(System.getProperty("user.home") + separators.getFileSeparator() + ".m2"
+ separators.getFileSeparator() + "settings.xml");
}
// define the scope (if it's not defined)
if (scope == null || scope.isEmpty())
{
this.scope = JavaScopes.RUNTIME;
}
// acquire the Maven Settings for the profile
Settings settings = getSettings(optionsByType);
// ----- establish the repository system using the settings -----
RepositorySystem system = newRepositorySystem();
// ----- establish the session for the repository system -----
RepositorySystemSession.SessionBuilder sessionBuilder = new SessionBuilderSupplier(system).get();
sessionBuilder.setOffline(isOffline == null ? false : isOffline);
sessionBuilder.setCache(new DefaultRepositoryCache());
// define the local repository
File localRepositoryLocation = new File(System.getProperty("user.home") + separators.getFileSeparator() + ".m2"
+ separators.getFileSeparator() + "repository");
LocalRepository localRepo = new LocalRepository(localRepositoryLocation);
sessionBuilder.withLocalRepositoryBaseDirectories(localRepositoryLocation.toPath());
RepositorySystemSession.CloseableSession session = sessionBuilder.build();
sessionBuilder.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
// ----- establish the remote repositories to use from the settings -----
Map profiles = settings.getProfilesAsMap();
ArrayList remoteRepositories = new ArrayList<>(20);
for (String profileName : settings.getActiveProfiles())
{
for (Repository repo : profiles.get(profileName).getRepositories())
{
RemoteRepository remoteRepository = new RemoteRepository.Builder(repo.getId(),
"default",
repo.getUrl()).build();
remoteRepositories.add(remoteRepository);
}
}
// perform the operation
operation.perform(system, session, remoteRepositories, scope);
}
/**
* Obtains the versions of a Maven artifact resolvable from the {@link Maven} profile.
*
* @param groupId the Maven Group Id
* @param artifactId the Maven Artifact Id
* @param extension the Maven Artifact Extension
* @param classifier the Maven Artifact Classifier
* @param versionPattern the Maven Artifact Version number range pattern. eg: "[1.0,)"
* @param options the {@link Option}s
* (for example {@link PlatformSeparators} if the local platform defaults are unacceptable)
*
* @return a {@link List} of available artifact versions, empty if none are available (not null)
*
* @see Maven Version Number Ranges
* @see Oracle's guidelines on Maven Version Number ranges
*/
public List versionsOf(String groupId,
String artifactId,
String extension,
String classifier,
String versionPattern,
Option... options) throws RepositoryException
{
OptionsByType optionsByType = OptionsByType.of(options);
List versions = new ArrayList<>();
perform(
(system, session, repositories, scope) -> {
Artifact artifact = new DefaultArtifact(groupId + ":" + artifactId + ":" + extension + ":" + classifier
+ ":" + versionPattern);
VersionRangeRequest rangeRequest = new VersionRangeRequest();
rangeRequest.setArtifact(artifact);
rangeRequest.setRepositories(repositories);
VersionRangeResult rangeResult = system.resolveVersionRange(session, rangeRequest);
for (Version version : rangeResult.getVersions())
{
versions.add(version.toString());
}
},
optionsByType);
return versions;
}
/**
* Obtains the versions of a Maven artifact resolvable from the {@link Maven} profile.
*
* @param groupId the Maven Group Id
* @param artifactId the Maven Artifact Id
* @param extension the Maven Artifact Extension
* @param versionPattern the Maven Artifact Version number range pattern. eg: "[1.0,)"
* @param options the {@link Option}s
* (for example {@link PlatformSeparators} if the local platform defaults are unacceptable)
*
* @return a {@link List} of available artifact versions, empty if none are available (not null)
*
* @see Maven Version Number Ranges
* @see Oracle's guidelines on Maven Version Number ranges
*/
public List versionsOf(String groupId,
String artifactId,
String extension,
String versionPattern,
Option... options) throws RepositoryException
{
OptionsByType optionsByType = OptionsByType.of(options);
List versions = new ArrayList<>();
perform(
(system, session, repositories, scope) -> {
Artifact artifact = new DefaultArtifact(groupId + ":" + artifactId + ":" + extension + ":"
+ versionPattern);
VersionRangeRequest rangeRequest = new VersionRangeRequest();
rangeRequest.setArtifact(artifact);
rangeRequest.setRepositories(repositories);
VersionRangeResult rangeResult = system.resolveVersionRange(session, rangeRequest);
for (Version version : rangeResult.getVersions())
{
versions.add(version.toString());
}
},
optionsByType);
return versions;
}
/**
* Obtains the versions of a Maven artifact resolvable from the {@link Maven} profile.
*
* @param groupId the Maven Group Id
* @param artifactId the Maven Artifact Id
* @param versionPattern the Maven Artifact Version number range pattern. eg: "[1.0,)"
* @param options the {@link Option}s
* (for example {@link PlatformSeparators} if the local platform defaults are unacceptable)
*
* @return a {@link List} of available artifact versions, empty if none are available (not null)
*
* @see Maven Version Number Ranges
* @see Oracle's guidelines on Maven Version Number ranges
*/
public List versionsOf(String groupId,
String artifactId,
String versionPattern,
Option... options) throws RepositoryException
{
OptionsByType optionsByType = OptionsByType.of(options);
List versions = new ArrayList<>();
perform(
(system, session, repositories, scope) -> {
Artifact artifact = new DefaultArtifact(groupId + ":" + artifactId + ":" + versionPattern);
VersionRangeRequest rangeRequest = new VersionRangeRequest();
rangeRequest.setArtifact(artifact);
rangeRequest.setRepositories(repositories);
VersionRangeResult rangeResult = system.resolveVersionRange(session, rangeRequest);
for (Version version : rangeResult.getVersions())
{
versions.add(version.toString());
}
},
optionsByType);
return versions;
}
@FunctionalInterface
private interface RepositorySystemOperation
{
/**
* Perform an operation against a {@link RepositorySystem}.
*
* @param system the {@link RepositorySystem}
* @param session the {@link RepositorySystemSession}
* @param repositories the {@link RemoteRepository}s
* @param scope the Maven scope of the operation
*
* @throws RepositoryException when an exception occurs interacting with the repository
*/
void perform(RepositorySystem system,
RepositorySystemSession session,
List repositories,
String scope) throws RepositoryException;
}
}