7

Maven 実行環境外で Maven モジュールのすべての依存関係をプログラムで取得するにはどうすればよいですか?

これまでのところ、私は持っています:

maven-core経由:

Path pomPath = ...;
MavenXpp3Reader reader = new MavenXpp3Reader();
try (InputStream is = Files.newInputStream(pomPath)) {
    Model model = reader.read(is);
    this.mavenProject = new MavenProject(model);
}

そしてjcabi-aether経由:

File localRepo = Paths.get(System.getProperty("user.home"), ".m2").toFile();
new Classpath(mavenProject, localRepo, "runtime")

これまでのところ、これは一般的に正しいですか?

今の問題は、NullPointerException を取得していることです。

Caused by: java.lang.NullPointerException
    at com.jcabi.aether.Aether.mrepos(Aether.java:197)
    at com.jcabi.aether.Aether.<init>(Aether.java:140)
    at com.jcabi.aether.Classpath.<init>(Classpath.java:125)

mavenProject.getRemoteProjectRepositories() は null を返すためです。

settings.xml ファイル (ミラー、プロキシ、リポジトリなど) も考慮して、構成済みのリモート リポジトリを含むように MavenProject を初期化するにはどうすればよいですか?

4

1 に答える 1

12

Maven プラグインの外部では、アーティファクトを操作する方法は Aether を介して行われます。チームには、 と呼ばれる特定のアーティファクトの推移的な依存関係を取得するためのサンプル プロジェクトがありますResolveTransitiveDependencies。Aether の依存関係を設定したら (ここに示すように)、次のように簡単に設定できます。

public static void main(final String[] args) throws Exception {
    DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
    RepositorySystem system = newRepositorySystem(locator);
    RepositorySystemSession session = newSession(system);

    RemoteRepository central = new RemoteRepository.Builder("central", "default", "http://repo1.maven.org/maven2/").build();

    Artifact artifact = new DefaultArtifact("group.id:artifact.id:version");

    CollectRequest collectRequest = new CollectRequest(new Dependency(artifact, JavaScopes.COMPILE), Arrays.asList(central));
    DependencyFilter filter = DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE);
    DependencyRequest request = new DependencyRequest(collectRequest, filter);
    DependencyResult result = system.resolveDependencies(session, request);

    for (ArtifactResult artifactResult : result.getArtifactResults()) {
        System.out.println(artifactResult.getArtifact().getFile());
    }
}

private static RepositorySystem newRepositorySystem(DefaultServiceLocator locator) {
    locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
    locator.addService(TransporterFactory.class, FileTransporterFactory.class);
    locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
    return locator.getService(RepositorySystem.class);
}

private static RepositorySystemSession newSession(RepositorySystem system) {
    DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
    LocalRepository localRepo = new LocalRepository("target/local-repo");
    session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo));
    return session;
}

アーティファクトをダウンロードして に配置します"target/local-repo"

システム セッションでDefaultProxySelectorおよびを使用して、プロキシとミラーを設定できることに注意してください。DefaultMirrorSelectorMaven 設定ファイルを読み取り、それを使用してセッションにデータを入力することは可能ですが、物事は本当に醜いものになります...


処理する POM にアクセスできるため Maven 自体との密結合が必要であり、設定を考慮したい場合は、Maven をプログラムで直接呼び出す方がはるかに簡単です。この場合、推移的な依存関係を含む、特定の POM ファイルの各依存関係のパスに関心があります。そのために、dependency:list目標をoutputAbsoluteArtifactFilenametotrueに設定することで、(ほぼ)正確にそれが得られます。

プログラムで Maven を呼び出すには、Invoker APIを使用できます。プロジェクトに依存関係を追加します。

<dependency>
  <groupId>org.apache.maven.shared</groupId>
  <artifactId>maven-invoker</artifactId>
  <version>2.2</version>
</dependency>

あなたが持つことができます:

InvocationRequest request = new DefaultInvocationRequest();
request.setPomFile(new File(pomPath));
request.setGoals(Arrays.asList("dependency:list"));
Properties properties = new Properties();
properties.setProperty("outputFile", "dependencies.txt"); // redirect output to a file
properties.setProperty("outputAbsoluteArtifactFilename", "true"); // with paths
properties.setProperty("includeScope", "runtime"); // only runtime (scope compile + runtime)
// if only interested in scope runtime, you may replace with excludeScope = compile
request.setProperties(properties);

Invoker invoker = new DefaultInvoker();
// the Maven home can be omitted if the "maven.home" system property is set
invoker.setMavenHome(new File("/path/to/maven/home"));
invoker.setOutputHandler(null); // not interested in Maven output itself
InvocationResult result = invoker.execute(request);
if (result.getExitCode() != 0) {
    throw new IllegalStateException("Build failed.");
}

Pattern pattern = Pattern.compile("(?:compile|runtime):(.*)");
try (BufferedReader reader = Files.newBufferedReader(Paths.get("dependencies.txt"))) {
    while (!"The following files have been resolved:".equals(reader.readLine()));
    String line;
    while ((line = reader.readLine()) != null && !line.isEmpty()) {
        Matcher matcher = pattern.matcher(line);
        if (matcher.find()) {
            // group 1 contains the path to the file
            System.out.println(matcher.group(1));
        }
    }
}

mvn dependency:list -Dprop=valueこれにより、コマンドラインで起動する場合と同様に、呼び出す目標とシステム プロパティを含む呼び出し要求が作成されます。使用する設定へのパスは、デフォルトで の標準の場所になりますが、および"${user.home}/settings.xml"で設定へのパスを指定することもできます。システム プロパティが設定されていない場合に限り、インボーカーは Maven ホーム (つまり、インストール ディレクトリ) を設定する必要があります。request.setUserSettingsFile(...)request.setGlobalSettingsFile(...)"maven.home"

呼び出しの結果はdependency:listファイルにリダイレクトされ、後で後処理されます。その目標の出力は、次の形式の依存関係のリストで構成されます (分類子がない場合は、分類子が存在しない可能性があります)。

group.id:artifact.id:type[:classifier]:version:scope:pathname

解決されたアーティファクトのファイルのパスのみを出力する方法はなく、分類子が存在しない可能性があるという事実は、解析を少し複雑にします (:パスに:.. .)。最初に、解決されたアーティファクトは"The following files have been resolved:"出力ファイルの行の下にあります。次に、必要なスコープが または のみであるcompileため、またはruntimeの後にあるすべてのものを取得する単純な正規表現を使用して、アーティファクトのファイルのパスを取得できます。そのパスは、.として直接使用できます。compile:runtime:new File

後処理中のフープが壊れやすいように見える場合は、解決されたアーティファクトのファイル名を出力するだけの独自のプラグインを作成できると思います。

于 2016-11-26T16:30:21.167 に答える