Repository: nifi-maven Updated Branches: refs/heads/master 82d8f6173 -> 7932897ae
NIFI-376: - Adding a new maven dependency for printing the dependencies provided through a NAR. New features require at least Maven 3.1.0. Signed-off-by: joewitt <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/nifi-maven/repo Commit: http://git-wip-us.apache.org/repos/asf/nifi-maven/commit/7932897a Tree: http://git-wip-us.apache.org/repos/asf/nifi-maven/tree/7932897a Diff: http://git-wip-us.apache.org/repos/asf/nifi-maven/diff/7932897a Branch: refs/heads/master Commit: 7932897aec68a9292961d4ac37626fb84d672b11 Parents: 82d8f61 Author: Matt Gilman <[email protected]> Authored: Wed Aug 19 13:35:07 2015 -0400 Committer: joewitt <[email protected]> Committed: Wed Aug 19 16:42:09 2015 -0400 ---------------------------------------------------------------------- README.md | 2 +- pom.xml | 21 +- .../nifi/NarProvidedDependenciesMojo.java | 317 +++++++++++++++++++ 3 files changed, 336 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/nifi-maven/blob/7932897a/README.md ---------------------------------------------------------------------- diff --git a/README.md b/README.md index 71772ac..a9c0f23 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Apache NiFi NAR Maven Plugin helps to build NiFi Archive bundles to support the ## Requirements * JDK 1.7 or higher -* Apache Maven 3.0.5 or higher +* Apache Maven 3.1.0 or higher ## Getting Started http://git-wip-us.apache.org/repos/asf/nifi-maven/blob/7932897a/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 5d1778c..431d87c 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ </parent> <groupId>org.apache.nifi</groupId> <artifactId>nifi-nar-maven-plugin</artifactId> - <version>1.0.2-SNAPSHOT</version> + <version>1.1.0-SNAPSHOT</version> <packaging>maven-plugin</packaging> <description>Apache NiFi Nar Maven Plugin</description> <url>http://nifi.apache.org</url> @@ -76,7 +76,7 @@ <properties> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> - <maven.min-version>3.0.5</maven.min-version> + <maven.min-version>3.1.0</maven.min-version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <inceptionYear>2014</inceptionYear> @@ -365,7 +365,17 @@ <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> - <version>2.2.1</version> + <version>3.1.0</version> + </dependency> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-artifact</artifactId> + <version>3.1.0</version> + </dependency> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-compat</artifactId> + <version>3.1.0</version> </dependency> <dependency> <groupId>org.apache.maven.plugins</groupId> @@ -373,6 +383,11 @@ <type>maven-plugin</type> <version>2.9</version> </dependency> + <dependency> + <groupId>org.apache.maven.shared</groupId> + <artifactId>maven-dependency-tree</artifactId> + <version>2.2</version> + </dependency> <dependency> <!-- No code from maven-jar-plugin is actually used; it's included just to simplify the dependencies list. --> http://git-wip-us.apache.org/repos/asf/nifi-maven/blob/7932897a/src/main/java/org/apache/nifi/NarProvidedDependenciesMojo.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/nifi/NarProvidedDependenciesMojo.java b/src/main/java/org/apache/nifi/NarProvidedDependenciesMojo.java new file mode 100644 index 0000000..63b2abd --- /dev/null +++ b/src/main/java/org/apache/nifi/NarProvidedDependenciesMojo.java @@ -0,0 +1,317 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.nifi; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.project.DefaultProjectBuildingRequest; +import org.apache.maven.project.ProjectBuilder; +import org.apache.maven.project.ProjectBuildingException; +import org.apache.maven.project.ProjectBuildingRequest; +import org.apache.maven.project.ProjectBuildingResult; +import org.apache.maven.shared.dependency.tree.DependencyNode; +import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder; +import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException; +import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor; +import org.eclipse.aether.RepositorySystemSession; + +/** + * Generates the listing of dependencies that is provided by the NAR dependency of the current NAR. This is important as artifacts that bundle dependencies will + * not project those dependences using the traditional maven dependency plugin. This plugin will override that setting in order to print the dependencies being + * inherited at runtime. + */ +@Mojo(name = "provided-nar-dependencies", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = false, requiresDependencyResolution = ResolutionScope.RUNTIME) +public class NarProvidedDependenciesMojo extends AbstractMojo { + + private static final String NAR = "nar"; + + /** + * The Maven project. + */ + @Parameter(defaultValue = "${project}", readonly = true, required = true) + private MavenProject project; + + /** + * The local artifact repository. + */ + @Parameter(defaultValue = "${localRepository}", readonly = true) + private ArtifactRepository localRepository; + + /** + * The {@link RepositorySystemSession} used for obtaining the local and remote artifact repositories. + */ + @Parameter(defaultValue = "${repositorySystemSession}", readonly = true) + private RepositorySystemSession repoSession; + + /** + * If specified, this parameter will cause the dependency tree to be written using the specified format. Currently supported format are: <code>tree</code> + * or <code>pom</code>. + */ + @Parameter(property = "mode", defaultValue = "tree") + private String mode; + + /** + * The dependency tree builder to use for verbose output. + */ + @Component + private DependencyTreeBuilder dependencyTreeBuilder; + + /** + * * + * The {@link ArtifactHandlerManager} into which any extension {@link ArtifactHandler} instances should have been injected when the extensions were loaded. + */ + @Component + private ArtifactHandlerManager artifactHandlerManager; + + /** + * The {@link ProjectBuilder} used to generate the {@link MavenProject} for the nar artifact the dependency tree is being generated for. + */ + @Component + private ProjectBuilder projectBuilder; + + /* + * @see org.apache.maven.plugin.Mojo#execute() + */ + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + try { + // find the nar dependency + Artifact narArtifact = null; + for (final Artifact artifact : project.getDependencyArtifacts()) { + if (NAR.equals(artifact.getType())) { + // ensure the project doesn't have two nar dependencies + if (narArtifact != null) { + throw new MojoExecutionException("Project can only have one NAR dependency."); + } + + // record the nar dependency + narArtifact = artifact; + } + } + + // ensure there is a nar dependency + if (narArtifact == null) { + throw new MojoExecutionException("Project does not have any NAR dependencies."); + } + + // build the project for the nar artifact + final ProjectBuildingRequest narRequest = new DefaultProjectBuildingRequest(); + narRequest.setRepositorySession(repoSession); + final ProjectBuildingResult narResult = projectBuilder.build(narArtifact, narRequest); + + // get the artifact handler for excluding dependencies + final ArtifactHandler narHandler = excludesDependencies(narArtifact); + narArtifact.setArtifactHandler(narHandler); + + // nar artifacts by nature includes dependencies, however this prevents the + // transitive dependencies from printing using tools like dependency:tree. + // here we are overriding the artifact handler for all nars so the + // dependencies can be listed. this is important because nar dependencies + // will be used as the parent classloader for this nar and seeing what + // dependencies are provided is critical. + final Map<String, ArtifactHandler> narHandlerMap = new HashMap<>(); + narHandlerMap.put(NAR, narHandler); + artifactHandlerManager.addHandlers(narHandlerMap); + + // get the dependency tree + final DependencyNode root = dependencyTreeBuilder.buildDependencyTree(narResult.getProject(), localRepository, null); + + // write the appropriate output + DependencyNodeVisitor visitor = null; + if ("tree".equals(mode)) { + visitor = new TreeWriter(); + } else if ("pom".equals(mode)) { + visitor = new PomWriter(); + } + + // ensure the mode was specified correctly + if (visitor == null) { + throw new MojoExecutionException("The specified mode is invalid. Supported options are 'tree' and 'pom'."); + } + + // visit and print the results + root.accept(visitor); + getLog().info("--- Provided NAR Dependencies ---\n\n" + visitor.toString()); + } catch (DependencyTreeBuilderException | ProjectBuildingException e) { + throw new MojoExecutionException("Cannot build project dependency tree", e); + } + } + + /** + * Gets the Maven project used by this mojo. + * + * @return the Maven project + */ + public MavenProject getProject() { + return project; + } + + /** + * Creates a new ArtifactHandler for the specified Artifact that overrides the includeDependencies flag. When set, this flag prevents transitive + * dependencies from being printed in dependencies plugin. + * + * @param artifact The artifact + * @return The handler for the artifact + */ + private ArtifactHandler excludesDependencies(final Artifact artifact) { + final ArtifactHandler orig = artifact.getArtifactHandler(); + + return new ArtifactHandler() { + @Override + public String getExtension() { + return orig.getExtension(); + } + + @Override + public String getDirectory() { + return orig.getDirectory(); + } + + @Override + public String getClassifier() { + return orig.getClassifier(); + } + + @Override + public String getPackaging() { + return orig.getPackaging(); + } + + // mark dependencies has excluded so they will appear in tree listing + @Override + public boolean isIncludesDependencies() { + return false; + } + + @Override + public String getLanguage() { + return orig.getLanguage(); + } + + @Override + public boolean isAddedToClasspath() { + return orig.isAddedToClasspath(); + } + }; + } + + /** + * Returns whether the specified dependency has test scope. + * + * @param node The dependency + * @return What the dependency is a test scoped dep + */ + private boolean isTest(final DependencyNode node) { + return "test".equals(node.getArtifact().getScope()); + } + + /** + * A dependency visitor that builds a dependency tree. + */ + private class TreeWriter implements DependencyNodeVisitor { + + private final StringBuilder output = new StringBuilder(); + private final Deque<DependencyNode> hierarchy = new ArrayDeque<>(); + + @Override + public boolean visit(DependencyNode node) { + // add this node + hierarchy.push(node); + + // don't print test deps, but still add to hierarchy as they will + // be removed in endVisit below + if (isTest(node)) { + return false; + } + + // build the padding + final StringBuilder pad = new StringBuilder(); + for (int i = 0; i < hierarchy.size() - 1; i++) { + pad.append(" "); + } + pad.append("+- "); + + // log it + output.append(pad).append(node.toNodeString()).append("\n"); + + return true; + } + + @Override + public boolean endVisit(DependencyNode node) { + hierarchy.pop(); + return true; + } + + @Override + public String toString() { + return output.toString(); + } + } + + /** + * A dependency visitor that generates output that can be copied into a pom's dependency management section. + */ + private class PomWriter implements DependencyNodeVisitor { + + private final StringBuilder output = new StringBuilder(); + + @Override + public boolean visit(DependencyNode node) { + if (isTest(node)) { + return false; + } + + final Artifact artifact = node.getArtifact(); + if (!NAR.equals(artifact.getType())) { + output.append("<dependency>\n"); + output.append(" <groupId>").append(artifact.getGroupId()).append("</groupId>\n"); + output.append(" <artifactId>").append(artifact.getArtifactId()).append("</artifactId>\n"); + output.append(" <version>").append(artifact.getVersion()).append("</version>\n"); + output.append(" <scope>provided</scope>\n"); + output.append("</dependency>\n"); + } + + return true; + } + + @Override + public boolean endVisit(DependencyNode node) { + return true; + } + + @Override + public String toString() { + return output.toString(); + } + } +}
