[java-identity-provider] 03/07: JPAR-175 Add back trace from WAR content to provoking artifact in pom.

Rod Widdowson rdw at steadingsoftware.com
Tue Jun 22 09:23:52 UTC 2021


This is an automated email from the git hooks/post-receive script.

rdw pushed a commit to branch main
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=3c90e2ff1c046f8ef76f934c16316d8ffeed5532

commit 3c90e2ff1c046f8ef76f934c16316d8ffeed5532
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Thu Jun 17 14:30:30 2021 +0100

    JPAR-175  Add back trace from WAR content to provoking artifact in pom.
    
    https://issues.shibboleth.net/jira/browse/JPAR-175
---
 .../idp/dependencies/DependencyTest.java           | 243 ++++++++++++++++++---
 .../net/shibboleth/idp/dependencies/ParsedPom.java |  59 +++--
 2 files changed, 257 insertions(+), 45 deletions(-)

diff --git a/idp-installer/src/test/java/net/shibboleth/idp/dependencies/DependencyTest.java b/idp-installer/src/test/java/net/shibboleth/idp/dependencies/DependencyTest.java
index 60dd0d87d..9eeb2ec38 100644
--- a/idp-installer/src/test/java/net/shibboleth/idp/dependencies/DependencyTest.java
+++ b/idp-installer/src/test/java/net/shibboleth/idp/dependencies/DependencyTest.java
@@ -18,11 +18,13 @@
 package net.shibboleth.idp.dependencies;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -32,23 +34,26 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Set;
 
 import org.apache.maven.shared.invoker.DefaultInvocationRequest;
 import org.apache.maven.shared.invoker.DefaultInvoker;
 import org.apache.maven.shared.invoker.InvocationRequest;
 import org.apache.maven.shared.invoker.Invoker;
 import org.apache.maven.shared.invoker.MavenInvocationException;
-import org.junit.AfterClass;
 import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import net.shibboleth.idp.dependencies.ParsedPom.PomArtifact;
 import net.shibboleth.idp.installer.plugin.impl.PluginInstallerSupport;
+import net.shibboleth.utilities.java.support.collection.Pair;
 import net.shibboleth.utilities.java.support.xml.ParserPool;
 import net.shibboleth.utilities.java.support.xml.XMLParserException;
 
@@ -58,7 +63,7 @@ import net.shibboleth.utilities.java.support.xml.XMLParserException;
 public class DependencyTest extends OpenSAMLInitBaseTestCase {
 
     /** Set this up if you want to run the tests from eclipse. */
-    private static String LOCAL_MAVEN_HOME = null;
+    private static String LOCAL_MAVEN_HOME = "C:/Program Files (x86)/apache-maven-3.6.1";
     
     /** Parse for us to use. */
     private ParserPool parserPool;
@@ -77,6 +82,8 @@ public class DependencyTest extends OpenSAMLInitBaseTestCase {
     
     private PrintWriter report;
 
+    private PomArtifact parentArtefact;
+
     
     /**  We have as an assumption that the CWD is idp-installer.  Test this.
      * @throws IOException
@@ -128,15 +135,23 @@ public class DependencyTest extends OpenSAMLInitBaseTestCase {
         }
         workingDir = Files.createTempDirectory("dependencyTest");
         parserPool = XMLObjectProviderRegistrySupport.getParserPool();
-        final ParsedPom idpParent = new ParsedPom(parserPool, Path.of("../idp-parent/pom.xml"), true);
+        ParsedPom idpParent = new ParsedPom(parserPool, Path.of("../idp-parent/pom.xml"), null);
         idpArtefact = idpParent.getOurInfo();
-        assertNotNull(idpParent.getParent());
-        final Path parentPath = downloadPom(idpParent.getParent());
-        final ParsedPom projectParent = new ParsedPom(parserPool, parentPath, false);
+        parentArtefact = idpParent.getParent(); 
+        assertNotNull(parentArtefact);
+        final Path parentPath = downloadPom(parentArtefact);
+        final ParsedPom projectParent = new ParsedPom(parserPool, parentPath, new Properties());
+        idpParent = new ParsedPom(parserPool, Path.of("../idp-parent/pom.xml"), projectParent.getProperties());
         dependencies.addAll(projectParent.getCompileDependencies());
+        dependencies.addAll(idpParent.getCompileDependencies());
         for (final PomArtifact bom : projectParent.getBomDependencies()) {
             final Path bomPath = downloadPom(bom);
-            final ParsedPom bomContents = new ParsedPom(parserPool, bomPath, false);
+            final ParsedPom bomContents = new ParsedPom(parserPool, bomPath, projectParent.getProperties());
+            dependencies.addAll(bomContents.getCompileDependencies());
+        }
+        for (final PomArtifact bom : idpParent.getBomDependencies()) {
+            final Path bomPath = downloadPom(bom);
+            final ParsedPom bomContents = new ParsedPom(parserPool, bomPath, projectParent.getProperties());
             dependencies.addAll(bomContents.getCompileDependencies());
         }
         final File out = new File("target/dependencyReport.txt");
@@ -150,9 +165,10 @@ public class DependencyTest extends OpenSAMLInitBaseTestCase {
     }
     
     /** The guts of the first test.  Are all the files what we expected?
-     * @throws IOException
+     * @throws IOException if the file doesn't exist
+     * @throws MavenInvocationException if we fail to download a pom or a dependency
      */
-    @Test public void testDependencies() throws IOException {
+    @Test public void testDependencies() throws IOException, MavenInvocationException {
         if (!mavenAvailable) {
             return;
         }
@@ -162,54 +178,215 @@ public class DependencyTest extends OpenSAMLInitBaseTestCase {
         int wrongVersion = 0;
         int found = 0;
         int nonUsed = 0;
-        int dupNames = Files.list(lib).mapToInt(e -> addName(names, lib.relativize(e).toString())).sum();
+        int dupNames = 0;
+        Files.list(lib).forEach(e -> addName(names, lib.relativize(e).toString()));
         report.format("WAR CONTENTS\n=== ========\n");
         Collections.sort(dependencies);
+        // ArtifactId->(Ver->[source, source])
+        final Map<String, Map<String, Set<String>>> dependencySource = new HashMap<>();
+        PomArtifact last = null;
         for (PomArtifact artifact : dependencies) {
             final String id = artifact.getArtifactId();
             final String ver = artifact.getVersion();
-            final String version = names.get(id);
-            if (version == null) {
+            final String version = names.remove(id);
+            if (artifact.equals(last)) {
+                report.format("%-22s\t: %-12s\tDUPLICATE artifact\n", id, ver);
+                dupNames++;
+            } else if (version == null) {
                 report.format("%-22s\t: %-12s\tNOT found in war\n", id, ver);
                 nonUsed++;
             } else if (version.equals(ver)) {
                 report.format("%-22s\t: %-12s\tFound in war\n", id, ver);
                 found++;
+                analyzeChild(dependencySource, artifact);
             } else {
                 report.format("%-22s\t: %-12s\tVERSION MISMATCH - found %s\n", id, ver, version);
+                analyzeChild(dependencySource, artifact);
                 wrongVersion++;
             }
+            last = artifact;
         }
         if (dupNames != 0) {
             report.format("\n%d Duplicate names\n", dupNames); 
         }
         
-        report.format("\n%d dependencies, %d found, %d not found %d mismatched", dependencies.size(), found, nonUsed, wrongVersion);
+        report.format("\n%d dependencies, %d found, %d not found, %d mismatched\n\nDependency Sources\n", dependencies.size(), found, nonUsed, wrongVersion);
+        
+        final List<String> contributedDeps = new ArrayList<>(names.keySet());
+        Collections.sort(contributedDeps);
+        int noSource = 0, verMismatch = 0;
+
+        for (final String dependency: contributedDeps) {
+            final Map<String, Set<String>> map = dependencySource.get(dependency);
+            final String version = names.get(dependency);
+            if (map == null) {
+                if (!dependency.startsWith("idp-")) {
+                    report.format("%-35s\t: %-12s\tNo source artefact found\n", dependency, version);
+                    noSource++;
+                }
+            } else {
+                final Set<String> sources = map.remove(version);
+                if (sources == null) {
+                    report.format("%-35s\t: %-12s\tNO Dependency contributes this version\n", dependency, version);
+                    verMismatch ++;
+                } else {
+                    List<String> vers = new ArrayList<>(sources);
+                    Collections.sort(vers);
+                    report.format("%-35s\t: %-12s\tContributed by %s\n", dependency, version, String.join(", ", vers));
+                }
+                final List<String> versions = new ArrayList<>(map.keySet());
+                Collections.sort(versions);
+                for (final String ver:versions) {
+                    List<String> vers = new ArrayList<>(map.get(ver));
+                    Collections.sort(vers);
+                    report.format("%-22s\t: %-12s\tContributed by %s\n", " ", ver, String.join(" ", vers));
+                }
+            }
+        }
+        report.format("%d Version mismatches, %d lost artifacts", verMismatch, noSource);
         report.flush();
         report.close();
         assertEquals(wrongVersion,  0, "Mismatched version");
         assertEquals(dupNames,  0, "Multiple similarly named jars");
     }
-    
+
+    /** Given an artifact do an "mvn dependency:copy-dependencies" on it.
+     * Then analyse the output file into the map.  The dependency name
+     * yields a map.  Looking this up with a version yields a set of the sources.
+     * @param dependencySource where to accumulate the results
+     * @param artifact what to start with.
+     * @throws MavenInvocationException  if maven fails.
+     * @throws IOException if a file doesbn't exist.
+     */
+    private void analyzeChild(final Map<String, Map<String, Set<String>>> dependencySource,
+            final PomArtifact artifact) throws MavenInvocationException, IOException {
+        final File pomFile = outputPom(artifact);
+        final String artifactName = artifact.getArtifactId()+"-"+artifact.getVersion();
+        final Path outputDir = workingDir.resolve(artifactName);
+
+        final Properties props = new Properties(2);
+        props.setProperty("includeScope","runtime");
+        props.setProperty("outputDirectory", outputDir.toString());
+        InvocationRequest request = new DefaultInvocationRequest().setProperties(props).setPomFile(pomFile).setGoals( Arrays.asList( "dependency:copy-dependencies" ) );
+
+        Invoker invoker = new DefaultInvoker();
+        invoker.execute( request );
+        Files.list(outputDir).forEach(e -> addDep(dependencySource, outputDir.relativize(e).toString(), artifact));
+    }
+
+    /** Add the artifact as a source of this file.
+     * @param dependencySources where to accumulate the answers 
+     * @param dep the file name of the dependency which was down-loaded
+     * @param artifact the artifact which provoked the download
+     */
+    private void addDep(final Map<String, Map<String, Set<String>>> dependencySources,
+            final String dep,
+            final PomArtifact artifact) {
+        final Pair<String,String> depId = splitFileName(dep);
+        
+        if (artifact.getArtifactId().equals(depId.getFirst()) && artifact.getVersion().equals(depId.getSecond())) {
+            // it's us.  Not interesting
+            return;
+        }
+        
+        // for each version, what contributed this dependency
+        Map<String, Set<String>> depEntry = dependencySources.get(depId.getFirst());
+        if (depEntry == null) {
+            depEntry = new HashMap<>();
+            dependencySources.put(depId.getFirst(), depEntry);
+        }
+        Set<String> provider = depEntry.get(depId.getSecond());
+        if (provider == null) {
+            provider = new HashSet<>();
+            depEntry.put(depId.getSecond(), provider);
+        }
+        provider.add(artifact.getArtifactId()+"-"+artifact.getVersion());
+    }
+
+    /** Create a pom file which has one dependency - this artifact.
+     * @param artifact the artifact.
+     * @return the file.
+     * @throws FileNotFoundException 
+     */
+    private File outputPom(PomArtifact artifact) throws FileNotFoundException {
+        final File file = workingDir.resolve(new StringBuilder(artifact.getArtifactId())
+                .append("-")
+                .append(artifact.getVersion())
+                .append(".xml").
+                toString()).toFile();
+        final FileOutputStream outStream = new FileOutputStream(file);
+        final PrintWriter pom = new PrintWriter(new BufferedOutputStream(outStream));
+        pom.format("<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+                + "     xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\n"
+                + "    <modelVersion>4.0.0</modelVersion>\n"
+                + "\n"
+                + "    <parent>\n"
+                + "        <groupId>%s</groupId>\n"
+                + "        <artifactId>%s</artifactId>\n"
+                + "        <version>%s</version>\n"
+                + "    </parent>\n"
+                + "\n", parentArtefact.getGroupId(), parentArtefact.getArtifactId(), parentArtefact.getVersion());
+        pom.format("    <groupId>shibboleth.net.dependency</groupId>\n"
+                + "    <version>0.0.1</version>\n"
+                + "    <name>Shibboleth Dependency</name>\n"
+                + "    <artifactId>idp-dep-%s</artifactId>\n"
+                + "    <packaging>jar</packaging>\n\n", artifact.getArtifactId());
+        pom.format("    <dependencies>\n"
+                + "    <dependency>\n"
+                + "            <groupId>%s</groupId><artifactId>%s</artifactId><version>%s</version>\n"
+                + "    </dependency>\n"
+                + "    </dependencies>\n\n", artifact.getGroupId(),artifact.getArtifactId(), artifact.getVersion());
+        pom.format("    <repositories>\n"
+                + "        <repository>\n"
+                + "            <id>shib-release</id>\n"
+                + "            <url>https://build.shibboleth.net/nexus/content/groups/public</url>\n"
+                + "            <snapshots>\n"
+                + "                <enabled>false</enabled>\n"
+                + "            </snapshots>\n"
+                + "        </repository>\n"
+                + "        <repository>\n"
+                + "            <id>shib-snapshot</id>\n"
+                + "            <url>https://build.shibboleth.net/nexus/content/repositories/snapshots</url>\n"
+                + "            <releases>\n"
+                + "                <enabled>false</enabled>\n"
+                + "            </releases>\n"
+                + "        </repository>\n"
+                + "    </repositories>\n"
+                + "</project>\n");
+        pom.flush();
+        pom.close();
+        return file;
+    }
+
+    /** Split the file name into the artifact (first) and version (second).
+     * @param inName the file name
+     * @return a pair.
+     */
+    private Pair<String, String> splitFileName(final String inName) {
+        final String name;
+        if (inName.endsWith(".jar")) {
+            name = inName.substring(0, inName.length()-4);
+        } else {
+            name = inName;
+        }
+        final int last;
+        if (name.endsWith("-SNAPSHOT")) {
+            last = name.substring(0, name.length()-9).lastIndexOf("-");
+        } else {
+            last = name.lastIndexOf("-");
+        }
+        final String base = name.substring(0, last);
+        String versionExtension = name.substring(last+1);
+        return new Pair<>(base, versionExtension);
+    }
+
     /** Trivial accumulator to pull a name in the lib directory apart and insert it into the map.
      * @param names The map to accumulate into
      * @param jarPath the file we are looking at.
-     * @return 1 if here was a previous entry.
      */
-    private int addName(Map<String, String> names, String jarPath) {
-        final int last = jarPath.lastIndexOf("-");
-        final String base = jarPath.substring(0, last);
-        String versionExtension = jarPath.substring(last+1);
-        final String oldVersion; 
-        if (versionExtension.endsWith(".jar")) {
-            versionExtension = versionExtension.substring(0, versionExtension.length()-4);
-        }
-        oldVersion = names.put(base, versionExtension);
-        if (oldVersion == null) {
-            return 0;
-        }
-        report.format("%-22s\t: Duplicate version %-12s\t& %s\n", base, versionExtension, oldVersion);
-        return 1;
+    private void addName(Map<String, String> names, String jarPath) {
+        final Pair<String, String> nm = splitFileName(jarPath);
+        names.put(nm.getFirst(), nm.getSecond());
     }
 
     /** tell maven to download the artifact and returns it's path.
@@ -218,6 +395,8 @@ public class DependencyTest extends OpenSAMLInitBaseTestCase {
      * @throws MavenInvocationException 
      */
     private Path downloadPom(final PomArtifact artifact) throws MavenInvocationException {
+        final Path output =  workingDir.resolve(artifact.getArtifactId() + ".pom");
+        assertFalse(Files.exists(output));
         final String fullArtifactName = new StringBuilder(artifact.getGroupId())
                     .append(':')
                     .append(artifact.getArtifactId())
@@ -226,15 +405,17 @@ public class DependencyTest extends OpenSAMLInitBaseTestCase {
                     .append(":pom")
                     .toString();
         
-        final Properties props = new Properties(2);
+        final Properties props = new Properties(3);
         props.setProperty("artifact",fullArtifactName);
+        props.setProperty("mdep.stripVersion","true");
         props.setProperty("outputDirectory", workingDir.toString());
        
         InvocationRequest request = new DefaultInvocationRequest().setProperties(props).setGoals( Arrays.asList( "dependency:copy" ) );
 
         Invoker invoker = new DefaultInvoker();
         invoker.execute( request );
+        assertTrue(Files.exists(output));
         
-        return workingDir.resolve(artifact.getArtifactId() + "-" + artifact.getVersion() + ".pom");
+        return output;
     }
 }
diff --git a/idp-installer/src/test/java/net/shibboleth/idp/dependencies/ParsedPom.java b/idp-installer/src/test/java/net/shibboleth/idp/dependencies/ParsedPom.java
index b5a3cc420..ed7fb9ede 100644
--- a/idp-installer/src/test/java/net/shibboleth/idp/dependencies/ParsedPom.java
+++ b/idp-installer/src/test/java/net/shibboleth/idp/dependencies/ParsedPom.java
@@ -63,19 +63,23 @@ public class ParsedPom extends OpenSAMLInitBaseTestCase{
     
     /** Us. */
     private final PomArtifact us;
-    
+
     /**
      * Constructor.
      *
-     * @param parsers a short vut to let us parse XML
+     * @param parsers a short-cut to let us parse XML
      * @param pom the {@link Path} to the pom.
-     * @param parentOnly Do we just want the parent (and our) info
+     * @param parentPomProperties if present it is properties from the parent (which might be empty), if null we are *only*
+     * looking for the parent pom coordinates.
      * @throws IOException from parsing
      * @throws FileNotFoundException if the file doesn't exist
      * @throws XMLParserException from parsing
      */
-    public ParsedPom(final ParserPool parsers, final Path pom, final boolean parentOnly) 
+    public ParsedPom(@Nonnull final ParserPool parsers,
+                     @Nonnull final Path pom,
+                     @Nullable final Properties parentPomProperties)
             throws FileNotFoundException, IOException, XMLParserException {
+
         Document document;
         try (final InputStream stream = new BufferedInputStream(new FileInputStream(pom.toFile()))) {
             document = parsers.parse(stream);
@@ -92,9 +96,13 @@ public class ParsedPom extends OpenSAMLInitBaseTestCase{
 
         us = new PomArtifact(el, parent);
 
-        if (parentOnly) {
+        if (parentPomProperties == null) {
             return;
         }
+        for (final Object p:parentPomProperties.keySet()) {
+            String pName = (String) p;
+            properties.setProperty(pName, parentPomProperties.getProperty(pName));
+        }
         final List<Element> props = ElementSupport.getChildElementsByTagName(el, "properties");
         if (!props.isEmpty()) {
             parseProperties(props.get(0));
@@ -154,7 +162,6 @@ public class ParsedPom extends OpenSAMLInitBaseTestCase{
         }
     }
 
-
     /**
      * @param item
      */
@@ -167,7 +174,6 @@ public class ParsedPom extends OpenSAMLInitBaseTestCase{
         }
     }
 
-
     /**
      * @param item
      */
@@ -181,21 +187,21 @@ public class ParsedPom extends OpenSAMLInitBaseTestCase{
     public List<PomArtifact> getCompileDependencies() {
         return compileDependencies;
     }
-    
+
     /**
      * @return Returns the bomDependencies.
      */
     public List<PomArtifact> getBomDependencies() {
         return bomDependencies;
     }
-    
+
     /** Return our artifactInformation.
      * @return us.
      */
     public PomArtifact getOurInfo() {
         return us;
     }
-    
+
     /**
      * @return Returns the parent.
      */
@@ -203,6 +209,13 @@ public class ParsedPom extends OpenSAMLInitBaseTestCase{
         return parent;
     }
 
+    /**
+     * @return Returns the properties.
+     */
+    public Properties getProperties() {
+        return properties;
+    }
+
     /** Encapsulation of a <dependency> element. */
     public class PomArtifact implements Comparable<PomArtifact>{
         
@@ -216,7 +229,7 @@ public class ParsedPom extends OpenSAMLInitBaseTestCase{
         @Nonnull private final String version;
 
         /** <exclusions>. */
-        @Nonnull private final Set<Pair<String, String>> exclusion = new HashSet<>();
+        @Nonnull private final Set<Pair<String, String>> exclusions = new HashSet<>();
 
         /**
          * Constructor.
@@ -269,7 +282,7 @@ public class ParsedPom extends OpenSAMLInitBaseTestCase{
                     els = ElementSupport.getChildElementsByTagName(e, "artifactId");
                     Constraint.isGreaterThan(0, els.size(), "<artifactId> should exist in exclusion");
                     final String art = getElementContent(els.get(0));
-                    exclusion.add(new Pair<>(grp, art));
+                    exclusions.add(new Pair<>(grp, art));
                 }
             }
         }
@@ -280,14 +293,14 @@ public class ParsedPom extends OpenSAMLInitBaseTestCase{
         public String getGroupId() {
             return groupId;
         }
-        
+
         /**
          * @return Returns the artifactId.
          */
         public String getArtifactId() {
             return artifactId;
         }
-        
+
         /**
          * @return Returns the version.
          */
@@ -295,9 +308,27 @@ public class ParsedPom extends OpenSAMLInitBaseTestCase{
             return version;
         }
 
+        /**
+         * @return Returns the exclusions.
+         */
+        public Set<Pair<String, String>> getExclusions() {
+            return exclusions;
+        }
+
         /** {@inheritDoc} */
         public int compareTo(final PomArtifact o) {
             return getArtifactId().compareTo(o.getArtifactId());
         }
+        
+        /** {@inheritDoc} */
+        public boolean equals(final Object obj) {
+            if (obj != null && obj instanceof PomArtifact ) {
+                final PomArtifact him = (PomArtifact) obj;
+                return  him.getArtifactId().equals(getArtifactId()) &&
+                        him.getGroupId().equals(getGroupId()) &&
+                        him.getVersion().equals(getVersion());
+            }
+            return false;
+        }
     }
 }

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list