I am using the Fitnesse Maven classpath plugin that allows Fitnesse to resolve the classpath using the Maven repository.
The plugin works, but it does not seem to be able to resolve dependencies that are stored in my remote repository.
It can resolve a snapshot if it's in my local repository but not remotely.
If I run a mvn install
(from outside Fitnesse) then it has no problems finding the dependencies so that suggests my settings.xml
is set up correctly.
I've debugged the plugin but am having difficulty pinpointing exactly why it cannot resolve the remote snapshots.
Question: How can I change this to allow it to resolve snapshots?
Edit: Adding More Details
This can easily be reproduced by running this unit test: MavenClasspathExtractorTest
This unit test attempts to resolve the commons-lang dependency.
If you remove this dependency from your local repository then the test fails as it does not seem to be able to retrieve from the remote repository.
If you put it back into your local repository it passes once again.
This is the crux of the issue.
Potential Solution
The following is a potential solution using the jcabi-aether
library that uses the local repository first, and if not there will download from the remote repository. Does this look fool-proof? It works for my needs
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
import org.apache.maven.settings.Profile;
import org.apache.maven.settings.Repository;
import org.apache.maven.settings.Settings;
import org.apache.maven.settings.building.*;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.console.ConsoleLoggerManager;
import org.sonatype.aether.artifact.Artifact;
import org.sonatype.aether.repository.RemoteRepository;
import org.sonatype.aether.util.artifact.DefaultArtifact;
import com.jcabi.aether.Aether;
/**
* Utility class to extract classpath elements from Maven projects. Heavily based on code copied from Jenkin's Maven
* support.
*/
public class MavenClasspathExtractor {
public static final String userHome = System.getProperty( "user.home" );
public static final File userMavenConfigurationHome = new File( userHome, ".m2" );
public static final String envM2Home = System.getenv("M2_HOME");
public final static String DEFAULT_SCOPE = "test";
public static final File DEFAULT_USER_SETTINGS_FILE = new File( userMavenConfigurationHome, "settings.xml" );
public static final File DEFAULT_GLOBAL_SETTINGS_FILE =
new File( System.getProperty( "maven.home", envM2Home != null ? envM2Home : "" ), "conf/settings.xml" );
private final Logger logger = new ConsoleLoggerManager().getLoggerForComponent("maven-classpath-plugin");
// Ensure M2_HOME variable is handled in a way similar to the mvn executable (script). To the extend possible.
static {
String m2Home = System.getenv().get("M2_HOME");
if (m2Home != null && System.getProperty("maven.home") == null) {
System.setProperty("maven.home", m2Home);
}
}
public List<String> extractClasspathEntries(File pomFile) throws MavenClasspathExtractionException {
return extractClasspathEntries(pomFile, DEFAULT_SCOPE);
}
public List<String> extractClasspathEntries(File pomFile, String scope) throws MavenClasspathExtractionException {
MavenXpp3Reader mavenReader;
FileReader reader = null;
try {
mavenReader = new MavenXpp3Reader();
reader = new FileReader(pomFile);
Model model = mavenReader.read(reader);
model.setPomFile(pomFile);
Collection<RemoteRepository> remoteRepositories = getRemoteRepositories();
File localRepo = new File( RepositorySystem.defaultUserLocalRepository.getAbsolutePath());
MavenProject project = new MavenProject(model);
Aether aether = new Aether(remoteRepositories, localRepo);
Artifact root = new DefaultArtifact(project.getGroupId(), project.getArtifactId(), project.getPackaging(), project.getVersion());
List<Artifact> artifacts = aether.resolve(root, scope);
List<String> paths = new ArrayList<>();
for(Artifact artifact : artifacts) {
paths.add(artifact.getFile().getAbsolutePath());
}
return paths;
} catch (Exception e) {
throw new MavenClasspathExtractionException(e);
} finally {
if(reader != null) {
try {
reader.close();
} catch (IOException e) {
throw new MavenClasspathExtractionException(e);
}
}
}
}
private Collection<RemoteRepository> getRemoteRepositories() throws SettingsBuildingException {
SettingsBuildingRequest settingsBuildingRequest = new DefaultSettingsBuildingRequest();
settingsBuildingRequest.setSystemProperties(System.getProperties());
settingsBuildingRequest.setUserSettingsFile(DEFAULT_USER_SETTINGS_FILE);
settingsBuildingRequest.setGlobalSettingsFile(DEFAULT_GLOBAL_SETTINGS_FILE);
DefaultSettingsBuilderFactory mvnSettingBuilderFactory = new DefaultSettingsBuilderFactory();
DefaultSettingsBuilder settingsBuilder = mvnSettingBuilderFactory.newInstance();
SettingsBuildingResult settingsBuildingResult = settingsBuilder.build(settingsBuildingRequest);
Settings effectiveSettings = settingsBuildingResult.getEffectiveSettings();
Map<String, Profile> profilesMap = effectiveSettings.getProfilesAsMap();
Collection<RemoteRepository> remotes = new ArrayList<RemoteRepository>(20);
for (String profileName : effectiveSettings.getActiveProfiles())
{
Profile profile = profilesMap.get(profileName);
List<Repository> repositories = profile.getRepositories();
for (Repository repo : repositories)
{
RemoteRepository remoteRepo
= new RemoteRepository(repo.getId(), "default", repo.getUrl());
remotes.add(remoteRepo);
}
}
return remotes;
}
}