【问题标题】:Fitnesse Maven Classpath plugin cannot resolve remote dependenciesFitnesse Maven Classpath 插件无法解析远程依赖项
【发布时间】:2016-05-03 13:23:32
【问题描述】:

我正在使用Fitnesse Maven classpath plugin,它允许 Fitnesse 使用 Maven 存储库解析类路径。

该插件有效,但它似乎无法解析存储在我的远程存储库中的依赖项。
如果它在我的本地存储库中但不是远程的,它可以解析快照。

如果我运行 mvn install(从 Fitnesse 外部),那么查找依赖项没有问题,这表明我的 settings.xml 设置正确。

我已经调试了plugin,但很难准确指出它为什么无法解析远程快照。

问题:如何更改它以使其能够解析快照?

编辑:添加更多细节

这可以通过运行此单元测试轻松重现:MavenClasspathExtractorTest

此单元测试尝试解决 commons-lang 依赖关系。

如果您从本地存储库中删除此依赖项,则测试将失败,因为它似乎无法从远程存储库中检索。

如果你把它放回你的本地存储库,它会再次通过。

这是问题的症结所在。

可能的解决方案

以下是使用jcabi-aether 库的潜在解决方案,它首先使用本地存储库,如果没有,将从远程存储库下载。这看起来是万无一失的吗?它满足我的需求

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;
    }
}

【问题讨论】:

  • 你能提供更多细节吗?最重要的是,您实际使用的pom.xml 文件(或要共享的相关/缩减部分)?
  • 在上面添加了更多细节

标签: maven maven-3 dependency-management fitnesse


【解决方案1】:

您看到的行为是插件的预期行为:此插件的目标不是重现 Maven 为您下载依赖项的能力。 它的目标是使您在使用 Maven 进行夹具开发期间对您可用的依赖项(即为夹具编写 Java 代码然后编译它们)在使用您的夹具时也可供 wiki 使用。 它假设您使用 Maven 编译了 Java 源代码(因此将任何需要的依赖项下载到本地存储库)并且您现在想要使用生成的类(以及它们来自 wiki 的依赖项)。 (这也是@A_Di-Matteo 的经历。)

如果您想让所有依赖项对不编译代码的人可用,您应该将依赖项复制到 FitNesse 安装并与 wiki 一起分发。我知道获取所有依赖项并将它们放在一个位置的最简单方法是使用 maven-dependency-plugin 的 copy-dependencies 目标。

在我的FitNesse baseline 中,我使用 Maven 类路径插件来确保在开发过程中不必手动更新 wiki 的类路径。我将依赖项复制到单个目录并与 wiki 一起分发它们,这样人们就可以使用固定装置执行/编写测试,而无需在他们的系统上编译或安装 Maven(以创建“独立(不需要 JDK 或 Maven)Fitnesse 环境”)。在我的根页面上,我结合了两种方法:

The location when working standalone:
!path fixtures
!path fixtures/*.jar

When developing and changing the fixtures, we will work based on the pom.xml:
!pomFile ../pom.xml@compile

【讨论】:

  • 感谢您的解释。理想情况下,我希望插件从 Maven 存储库下载依赖项,否则 Fitnesse 安装可能会变得非常大,特别是如果您有多个 Fitnesse 项目。我现在正在编写一些代码来下载依赖项并使它们可用
  • 我确认这是我猜测并复制的行为:依赖项必须已经在本地缓存中,否则它将不起作用。当 pom 被修改时,Eclipse 实际上正在为我下载它们,否则应该手动完成(在测试 pom 上调用 maven 或使用此答案中提到的依赖插件,例如)。
【解决方案2】:

我能够成功运行MavenClasspathExtractorTest,然后:

  • 更改了测试使用的pom.xml文件中的commons-lang版本,更改为2.0,在我的Maven缓存中不存在:测试成功,在我的Maven缓存中下载了依赖项
  • 本地删除了pom.xml2.6指定的common-lang版本:测试成功,依赖再次下载到我的本地缓存

因此,您遇到的行为是不可重现的:它正确地从远程存储库中获取 Maven 依赖项。

但是,它确实无法获取 SNAPSHOT 依赖项(将它们添加到上面相同的 pom.xml 测试文件中)。以下警告显示在我的 IDE 控制台中:

[警告] 无法将元数据 /maven-metadata.xml 从/向 nexus 传输 (http:///nexus/content/groups/public/):没有可用于访问存储库 nexus 的连接器 (http:///nexus /content/groups/public/) 类型默认使用可用工厂

注意:我删除了一些信息(网址、依赖项名称)。

这基本上是因为项目 pom.xml 中缺少一些依赖项,因此在运行时嵌入式 Maven 找不到任何有效的工厂来从给定存储库传输依赖项。

将以下依赖项添加到 pom(项目的,而不是测试的)解决了这个问题:

<dependency>
    <groupId>org.eclipse.aether</groupId>
    <artifactId>aether-connector-basic</artifactId>
    <version>1.1.0</version>
</dependency>
<dependency>
    <groupId>org.eclipse.aether</groupId>
    <artifactId>aether-transport-wagon</artifactId>
    <version>1.1.0</version>
</dependency>
<dependency>
    <groupId>org.apache.maven.wagon</groupId>
    <artifactId>wagon-http</artifactId>
    <version>2.8</version>
</dependency>
<dependency>
    <groupId>org.apache.maven.wagon</groupId>
    <artifactId>wagon-provider-api</artifactId>
    <version>2.8</version>
</dependency>

SNAPSHOT 依赖项随后被正确获取。

更新
上述行为受到 IDE(在我的情况下为 Eclipse)的影响,它在编辑测试 pom 时自动下载依赖项。否则,依赖项应该已经在本地 maven 缓存中才能成功执行测试。

【讨论】:

  • 感谢您的回复。不幸的是,在将这些依赖项添加到主 pom.xml 之后,我仍然看到上面相同的错误。此外,我仍然无法解决我觉得很奇怪的远程发布依赖项。任何想法为什么会这样?
  • @DJ180 上面同样的错误是指Could not transfer metadata等错误?
  • 是的,我遇到了同样的错误。我也对如何解决远程发布的依赖项感到困惑
  • @DJ180 我可能明白了,我在 Eclipse 中打开了测试 pom 文件,编辑它时 Eclipse 实际上正在解析它并因此下载依赖项,所以就像我之前在测试 pom 上调用 Maven运行测试。我将为此制定一组可重现的步骤(无需 IDE 干预)并更新我的答案。
猜你喜欢
  • 2017-05-06
  • 1970-01-01
  • 2014-05-03
  • 1970-01-01
  • 2018-02-27
  • 1970-01-01
  • 2014-11-17
  • 2020-12-20
  • 2017-03-03
相关资源
最近更新 更多