Fix for Bug 114. 87/5287/12
authorMartin Vitez <mvitez@cisco.com>
Wed, 12 Feb 2014 13:46:16 +0000 (14:46 +0100)
committerRobert Varga <rovarga@cisco.com>
Mon, 28 Apr 2014 11:48:47 +0000 (11:48 +0000)
Added check for artifact versions pulled by project against ones declared in plugin dependencies.

Change-Id: Ib7663eaa775fa6799ae11a3132fbd7ca080cb0e7
Signed-off-by: Martin Vitez <mvitez@cisco.com>
yang/yang-maven-plugin-it/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/it/YangToSourcesPluginTestIT.java
yang/yang-maven-plugin-it/src/test/resources/InvalidVersion/pom.xml [new file with mode: 0644]
yang/yang-maven-plugin/pom.xml
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/Util.java
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesMojo.java
yang/yang-maven-plugin/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/GenerateSourcesTest.java

index 72796c6752da78649ebf795e001187f2b1dc52e7..daf737089d1bff86c2297fd263255d31cf716d58 100644 (file)
@@ -109,6 +109,13 @@ public class YangToSourcesPluginTestIT {
         v.verifyTextInLog("[WARNING] yang-to-sources: No code generators provided");
     }
 
+    @Test
+    public void testInvalidVersion() throws Exception {
+        Verifier v = setUp("InvalidVersion/", false);
+        v.verifyErrorFreeLog();
+        v.verifyTextInLog("[WARNING] yang-to-sources: Dependency resolution conflict:");
+    }
+
     @Test
     public void testUnknownGenerator() throws Exception {
         Verifier v = setUp("UnknownGenerator/", true);
diff --git a/yang/yang-maven-plugin-it/src/test/resources/InvalidVersion/pom.xml b/yang/yang-maven-plugin-it/src/test/resources/InvalidVersion/pom.xml
new file mode 100644 (file)
index 0000000..76c8f17
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!-- Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. This 
+    program and the accompanying materials are made available under the terms of the 
+    Eclipse Public License v1.0 which accompanies this distribution, and is available 
+    at http://www.eclipse.org/legal/epl-v10.html -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.opendaylight.yangtools</groupId>
+    <version>0.5-SNAPSHOT</version>
+    <artifactId>test</artifactId>
+
+    <!-- Testing dependency -->
+    <!-- yang-common dependency added here only for purpose of 'artifact version 
+        check' test: version of yang-common artifact has to be different than one defined 
+        in yang-maven-plugin to make test pass -->
+    <repositories>
+        <repository>
+            <id>opendaylight-mirror</id>
+            <name>opendaylight-mirror</name>
+            <url>http://nexus.opendaylight.org/content/groups/public/</url>
+            <snapshots>
+                <enabled>false</enabled>
+            </snapshots>
+            <releases>
+                <enabled>true</enabled>
+                <updatePolicy>never</updatePolicy>
+            </releases>
+        </repository>
+    </repositories>
+    <dependencies>
+        <dependency>
+            <groupId>org.opendaylight.yangtools</groupId>
+            <artifactId>yang-common</artifactId>
+            <version>0.6.1</version>
+        </dependency>
+    </dependencies>
+    <!-- End of Testing dependency -->
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.opendaylight.yangtools</groupId>
+                <artifactId>yang-maven-plugin</artifactId>
+                <version>${it-project.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-sources</goal>
+                        </goals>
+                        <configuration>
+                            <yangFilesRootDir>../files</yangFilesRootDir>
+                            <inspectDependencies>false</inspectDependencies>
+                            <codeGenerators>
+                            </codeGenerators>
+                            <resourceProviders>
+                                <provider>
+                                    <resourceProviderClass>
+                                        org.opendaylight.yangtools.yang2sources.spi.ResourceProviderTestImpl
+                                    </resourceProviderClass>
+                                    <outputBaseDir>
+                                        outDir/
+                                    </outputBaseDir>
+                                </provider>
+                            </resourceProviders>
+                        </configuration>
+                    </execution>
+                </executions>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.opendaylight.yangtools</groupId>
+                        <artifactId>yang-maven-plugin-spi</artifactId>
+                        <version>${it-project.version}</version>
+                        <type>test-jar</type>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+</project>
index 8ffa8204c2ec04fc49f8815ab0b226a68fccdbd8..61530777a8256c70dba00d1c63b7480c69f77bfd 100644 (file)
         <dependency>
             <groupId>org.codehaus.plexus</groupId>
             <artifactId>plexus-slf4j-logging</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>google-collections</artifactId>
+                    <groupId>com.google.collections</groupId>
+                </exclusion>
+            </exclusions>
         </dependency>
 
         <dependency>
                 <artifactId>sisu-inject-plexus</artifactId>
                 <version>2.5.0</version>
             </dependency>
+            <dependency>
+                <groupId>org.sonatype.aether</groupId>
+                <artifactId>aether-api</artifactId>
+                <version>1.13.1</version>
+            </dependency>
+            <dependency>
+                <groupId>org.sonatype.aether</groupId>
+                <artifactId>aether-util</artifactId>
+                <version>1.13.1</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
index b1dee91efec763483ea461f7fdd0f23cea2a631d..bcb7bc9b3c29cffc9bb03d8775add5bfe4f6531c 100644 (file)
@@ -17,6 +17,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -25,12 +26,24 @@ import java.util.zip.ZipFile;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.maven.artifact.Artifact;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Plugin;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.project.MavenProject;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
+import org.sonatype.aether.RepositorySystem;
+import org.sonatype.aether.RepositorySystemSession;
+import org.sonatype.aether.collection.CollectRequest;
+import org.sonatype.aether.graph.DependencyFilter;
+import org.sonatype.aether.repository.RemoteRepository;
+import org.sonatype.aether.resolution.ArtifactResult;
+import org.sonatype.aether.resolution.DependencyRequest;
+import org.sonatype.aether.resolution.DependencyResolutionException;
+import org.sonatype.aether.util.artifact.JavaScopes;
+import org.sonatype.aether.util.filter.DependencyFilterUtils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
@@ -146,6 +159,118 @@ final class Util {
         return dependencies;
     }
 
+    /**
+     * Read current project dependencies and check if it don't grab incorrect
+     * artifacts versions which could be in conflict with plugin dependencies.
+     *
+     * @param project
+     *            current project
+     * @param log
+     *            logger
+     * @param repoSystem
+     *            repository system
+     * @param repoSession
+     *            repository system session
+     * @param remoteRepos
+     *            remote repositories
+     */
+    static void checkClasspath(MavenProject project, Log log, RepositorySystem repoSystem,
+            RepositorySystemSession repoSession, List<RemoteRepository> remoteRepos) {
+        Plugin plugin = project.getPlugin(YangToSourcesMojo.PLUGIN_NAME);
+        if (plugin == null) {
+            log.warn(message("%s not found, dependencies version check skipped", YangToSourcesProcessor.LOG_PREFIX,
+                    YangToSourcesMojo.PLUGIN_NAME));
+        } else {
+            Map<org.sonatype.aether.artifact.Artifact, List<org.sonatype.aether.artifact.Artifact>> pluginDependencies = new HashMap<>();
+            getPluginTransitiveDependencies(plugin, pluginDependencies, repoSystem, repoSession, remoteRepos, log);
+
+            Set<Artifact> projectDependencies = project.getDependencyArtifacts();
+            for (Map.Entry<org.sonatype.aether.artifact.Artifact, List<org.sonatype.aether.artifact.Artifact>> entry : pluginDependencies
+                    .entrySet()) {
+                checkArtifact(entry.getKey(), projectDependencies, log);
+                for (org.sonatype.aether.artifact.Artifact dependency : entry.getValue()) {
+                    checkArtifact(dependency, projectDependencies, log);
+                }
+            }
+        }
+    }
+
+    /**
+     * Read transitive dependencies of given plugin and store them in map.
+     *
+     * @param plugin
+     *            plugin to read
+     * @param map
+     *            map, where founded transitive dependencies will be stored
+     * @param repoSystem
+     *            repository system
+     * @param repoSession
+     *            repository system session
+     * @param remoteRepos
+     *            list of remote repositories
+     * @param log
+     *            logger
+     */
+    private static void getPluginTransitiveDependencies(Plugin plugin,
+            Map<org.sonatype.aether.artifact.Artifact, List<org.sonatype.aether.artifact.Artifact>> map,
+            RepositorySystem repoSystem, RepositorySystemSession repoSession, List<RemoteRepository> remoteRepos,
+            Log log) {
+        for (Dependency dep : plugin.getDependencies()) {
+            String artifactCoords = dep.getGroupId() + ":" + dep.getArtifactId() + ":" + dep.getVersion();
+            org.sonatype.aether.artifact.Artifact artifact = new org.sonatype.aether.util.artifact.DefaultArtifact(
+                    artifactCoords);
+
+            if (!(map.containsKey(artifact))) {
+                DependencyFilter classpathFlter = DependencyFilterUtils.classpathFilter(JavaScopes.COMPILE);
+                CollectRequest collectRequest = new CollectRequest();
+                collectRequest.setRoot(new org.sonatype.aether.graph.Dependency(artifact, JavaScopes.COMPILE));
+                collectRequest.setRepositories(remoteRepos);
+                DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, classpathFlter);
+
+                List<ArtifactResult> artifactResults;
+                try {
+                    artifactResults = repoSystem.resolveDependencies(repoSession, dependencyRequest)
+                            .getArtifactResults();
+                    List<org.sonatype.aether.artifact.Artifact> deps = new ArrayList<>();
+                    for (ArtifactResult ar : artifactResults) {
+                        deps.add(ar.getArtifact());
+                    }
+                    map.put(artifact, deps);
+                } catch (DependencyResolutionException e) {
+                    log.warn(message("Failed to resolve plugin dependencies, version check skipped",
+                            YangToSourcesProcessor.LOG_PREFIX));
+                }
+            }
+        }
+    }
+
+    /**
+     * Check artifact against collection of dependencies. If collection contains
+     * artifact with same groupId and artifactId, but different version, logs a
+     * warning.
+     *
+     * @param artifact
+     *            artifact to check
+     * @param dependencies
+     *            collection of dependencies
+     * @param log
+     *            logger
+     */
+    private static void checkArtifact(org.sonatype.aether.artifact.Artifact artifact, Collection<Artifact> dependencies,
+            Log log) {
+        for (Artifact d : dependencies) {
+            if (artifact.getGroupId().equals(d.getGroupId()) && artifact.getArtifactId().equals(d.getArtifactId())) {
+                if (!(artifact.getVersion().equals(d.getVersion()))) {
+                    log.warn(message("Dependency resolution conflict:", YangToSourcesProcessor.LOG_PREFIX));
+                    log.warn(message("'%s' dependency [%s] has different version than one "
+                            + "declared in current project [%s]. It is recommended to fix this problem "
+                            + "because it may cause compilation errors.", YangToSourcesProcessor.LOG_PREFIX,
+                            YangToSourcesMojo.PLUGIN_NAME, artifact, d));
+                }
+            }
+        }
+    }
+
     private static final String JAR_SUFFIX = ".jar";
 
     private static boolean isJar(File element) {
index 174e2acf5f571902a7dad7f873079a689b22449f..933b0d58e82e82bbd9e6047e2af496c3c9a50759 100644 (file)
@@ -27,6 +27,9 @@ import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
 import org.opendaylight.yangtools.yang2sources.spi.CodeGenerator;
 import org.slf4j.impl.StaticLoggerBinder;
+import org.sonatype.aether.RepositorySystem;
+import org.sonatype.aether.RepositorySystemSession;
+import org.sonatype.aether.repository.RemoteRepository;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -46,6 +49,7 @@ import com.google.common.annotations.VisibleForTesting;
  */
 @Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true)
 public final class YangToSourcesMojo extends AbstractMojo {
+    public static final String PLUGIN_NAME = "org.opendaylight.yangtools:yang-maven-plugin";
 
     /**
      * Classes implementing {@link CodeGenerator} interface. An instance will be
@@ -78,6 +82,16 @@ public final class YangToSourcesMojo extends AbstractMojo {
 
     private YangToSourcesProcessor yangToSourcesProcessor;
 
+    @Component
+    private RepositorySystem repoSystem;
+
+    @Parameter( readonly = true, defaultValue = "${repositorySystemSession}" )
+    private RepositorySystemSession repoSession;
+
+    @Parameter( readonly = true, defaultValue = "${project.remoteProjectRepositories}" )
+    private List<RemoteRepository> remoteRepos;
+
+
     public YangToSourcesMojo() {
 
     }
@@ -94,6 +108,9 @@ public final class YangToSourcesMojo extends AbstractMojo {
     @Override
     public void execute() throws MojoExecutionException, MojoFailureException {
         StaticLoggerBinder.getSingleton().setMavenLog(this.getLog());
+
+        Util.checkClasspath(project, getLog(), repoSystem, repoSession, remoteRepos);
+
         if (yangToSourcesProcessor == null) {
             List<CodeGeneratorArg> codeGeneratorArgs = processCodeGenerators(codeGenerators);
 
index 3ab8a8c228734b633f9f80a5d3a82abceca320e4..3389839e325f0fda116cf5701ac3da603949bd6e 100644 (file)
@@ -16,9 +16,12 @@ import static org.mockito.Mockito.*;
 import java.io.File;
 import java.io.IOException;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.maven.model.Plugin;
+import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.project.MavenProject;
 import org.junit.Before;
@@ -40,6 +43,8 @@ public class GenerateSourcesTest {
     private File outDir;
     @Mock
     private MavenProject project;
+    @Mock
+    private Plugin plugin;
 
     @Before
     public void setUp() throws Exception {
@@ -56,6 +61,8 @@ public class GenerateSourcesTest {
                 mock);
         mojo = new YangToSourcesMojo(processor);
         doReturn(new File("")).when(project).getBasedir();
+        doReturn(Collections.emptyList()).when(plugin).getDependencies();
+        doReturn(plugin).when(project).getPlugin(YangToSourcesMojo.PLUGIN_NAME);
         mojo.setProject(project);
     }