This is an automated email from the ASF dual-hosted git repository.

cziegeler pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-slingfeature-maven-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new 77f56e9  SLING-9437 : Correct detection of missing and wrong packages
77f56e9 is described below

commit 77f56e934f64fa0b2b57c24d400efc9b25b8abf1
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Sun May 10 11:53:26 2020 +0200

    SLING-9437 : Correct detection of missing and wrong packages
---
 .../sling/feature/maven/mojos/ApisJarMojo.java     | 179 ++++++++++++---------
 .../feature/maven/mojos/apis/ApisJarContext.java   |  16 +-
 .../feature/maven/mojos/apis/JavadocLinks.java     |  64 ++++++++
 3 files changed, 174 insertions(+), 85 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/feature/maven/mojos/ApisJarMojo.java 
b/src/main/java/org/apache/sling/feature/maven/mojos/ApisJarMojo.java
index 83283dc..a3b2c8c 100644
--- a/src/main/java/org/apache/sling/feature/maven/mojos/ApisJarMojo.java
+++ b/src/main/java/org/apache/sling/feature/maven/mojos/ApisJarMojo.java
@@ -29,12 +29,12 @@ import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
 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 java.util.TreeSet;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 import java.util.jar.Manifest;
@@ -93,6 +93,7 @@ import 
org.apache.sling.feature.maven.mojos.apis.ApisJarContext;
 import org.apache.sling.feature.maven.mojos.apis.ApisJarContext.ArtifactInfo;
 import org.apache.sling.feature.maven.mojos.apis.ApisUtil;
 import org.apache.sling.feature.maven.mojos.apis.JavadocExecutor;
+import org.apache.sling.feature.maven.mojos.apis.JavadocLinks;
 import org.codehaus.plexus.archiver.UnArchiver;
 import org.codehaus.plexus.archiver.jar.JarArchiver;
 import org.codehaus.plexus.archiver.manager.ArchiverManager;
@@ -312,6 +313,14 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
     @Parameter
     private String apiRepositoryUrls;
 
+    /**
+     * Fail the build if errors are detected. For example, errors could be 
missing packages in the api jars, or
+     * too many packages in those jars.
+     * @since 1.3.2
+     */
+    @Parameter(defaultValue = "false")
+    private boolean failOnError;
+
     @Parameter(defaultValue = "${project.build.directory}/apis-jars", readonly 
= true)
     private File mainOutputDir;
 
@@ -499,31 +508,24 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
             onArtifact(ctx, artifact);
         }
 
-        final List<String> globalReport = new ArrayList<>();
-
-        if ( !ctx.getPackagesWithoutJavaClassesMap().isEmpty() ) {
-            globalReport.add("The following exported packages do not contain 
any java classes:");
-            ctx.getPackagesWithoutJavaClassesMap().entrySet().forEach(p -> 
globalReport.add("- ".concat(p.getKey()).concat(" from 
").concat(p.getValue().toMvnId())));
-        }
-        if ( !ctx.getPackagesWithoutSourcesMap().isEmpty() ) {
-            globalReport.add("The following exported packages do not have 
sources:");
-            ctx.getPackagesWithoutSourcesMap().entrySet().forEach(p -> 
globalReport.add("- ".concat(p.getKey()).concat(" from 
").concat(p.getValue().toMvnId())));
-        }
+        boolean hasErrors = false;
 
         // recollect and package stuff per region
         for (final ApiRegion apiRegion : regions.listRegions()) {
-            final List<String> report = new ArrayList<>(globalReport);
+            final List<String> report = new ArrayList<>();
 
             final File regionDir = new File(featureDir, apiRegion.getName());
 
             if (generateApiJar) {
-                final File apiJar = createArchive(ctx, apiRegion, 
ArtifactType.APIS, this.apiResources, this.useApiDependencies);
-                report(ctx, apiJar, ArtifactType.APIS, apiRegion, 
this.useApiDependencies, report);
+                final File apiJar = createArchive(ctx, apiRegion, 
ArtifactType.APIS, this.apiResources,
+                        ctx.getArtifactInfos(apiRegion, 
this.useApiDependencies));
+                report(ctx, apiJar, ArtifactType.APIS, apiRegion, 
this.useApiDependencies, report, null);
             }
 
             if (generateSourceJar) {
-                final File sourceJar = createArchive(ctx, apiRegion, 
ArtifactType.SOURCES, this.apiSourceResources, this.useApiDependencies);
-                report(ctx, sourceJar, ArtifactType.SOURCES, apiRegion, 
this.useApiDependencies, report);
+                final File sourceJar = createArchive(ctx, apiRegion, 
ArtifactType.SOURCES, this.apiSourceResources,
+                        ctx.getArtifactInfos(apiRegion, 
this.useApiDependencies));
+                report(ctx, sourceJar, ArtifactType.SOURCES, apiRegion, 
this.useApiDependencies, report, null);
             }
 
             if ( this.useApiDependencies && (this.generateApiJar || 
this.generateSourceJar)) {
@@ -532,10 +534,13 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
 
             if (generateJavadocJar) {
                 final File javadocsDir = new File(regionDir, 
ArtifactType.JAVADOC.getId());
-                if ( generateJavadoc(ctx, apiRegion, javadocsDir) ) {
+                final JavadocLinks links = new JavadocLinks();
+                links.calculateLinks(this.javadocLinks, 
ctx.getArtifactInfos(apiRegion, false));
+                final Collection<ArtifactInfo> infos = generateJavadoc(ctx, 
apiRegion, links, javadocsDir);
+                if ( infos != null ) {
                     ctx.setJavadocDir(javadocsDir);
-                    final File javadocJar = createArchive(ctx, apiRegion, 
ArtifactType.JAVADOC, this.apiJavadocResources, false);
-                    report(ctx, javadocJar, ArtifactType.JAVADOC, apiRegion, 
false, report);
+                    final File javadocJar = createArchive(ctx, apiRegion, 
ArtifactType.JAVADOC, this.apiJavadocResources, infos);
+                    report(ctx, javadocJar, ArtifactType.JAVADOC, apiRegion, 
false, report, links);
                 } else {
                     getLog().warn("Javadoc JAR will NOT be generated - sources 
directory " + ctx.getDeflatedSourcesDir()
                             + " was empty or contained no Java files!");
@@ -551,6 +556,7 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
                 } catch (final IOException e) {
                     throw new MojoExecutionException("Unable to write " + 
reportFile, e);
                 }
+                hasErrors = true;
             } else {
                 if ( reportFile.exists()) {
                     reportFile.delete();
@@ -558,6 +564,10 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
             }
         }
 
+        if ( hasErrors && this.failOnError ) {
+            throw new MojoExecutionException("API generation has errors, 
please see report files for more information");
+        }
+
         getLog().info(MessageUtils.buffer().a("APIs JARs for Feature 
").debug(feature.getId().toMvnId())
                 .a(" succesfully created").toString());
     }
@@ -567,48 +577,61 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
             final ArtifactType artifactType,
             final ApiRegion apiRegion,
             final boolean omitDependencyArtifacts,
-            final List<String> report)
+            final List<String> report,
+            final JavadocLinks links)
     throws MojoExecutionException {
-        final List<String> packages = getPackages(jarFile, 
artifactType.getContentExtension());
+        final Map.Entry<Set<String>, Set<String>> packageResult = 
getPackages(jarFile, artifactType.getContentExtension());
+        final Set<String> apiPackages = packageResult.getKey();
+        final Set<String> otherPackages = packageResult.getValue();
         if ( omitDependencyArtifacts ) {
             for(final ArtifactInfo info : ctx.getArtifactInfos(apiRegion, 
false)) {
                 if ( info.isUseAsDependencyPerRegion(apiRegion) ) {
                     for(final Clause c : 
info.getUsedExportedPackages(apiRegion) ) {
-                        packages.add(c.getName());
+                        apiPackages.add(c.getName());
                     }
                 }
             }
         }
+        if ( links != null ) {
+            apiPackages.addAll(links.getLinkedPackages());
+        }
+        if ( artifactType == ArtifactType.JAVADOC ) {
+            otherPackages.addAll(ctx.getPackagesWithoutSources());
+        }
         final List<ApiExport> missing = new ArrayList<>();
 
-        // for the report we always use the binaries as the source of truth
-        // that's why we check against getPackagesWithoutJavaClasses()
         for (final ApiExport exp : apiRegion.listExports()) {
             final String packageName = exp.getName();
-            if (!packages.remove(packageName) && 
!ctx.getPackagesWithoutJavaClasses().contains(packageName)) {
+            if ( !apiPackages.remove(packageName) && 
!otherPackages.remove(packageName)) {
                 missing.add(exp);
             }
         }
-        if (missing.isEmpty() && packages.isEmpty()) {
+        if ( links != null ) {
+            apiPackages.removeAll(links.getLinkedPackages());
+        }
+        if ( artifactType == ArtifactType.JAVADOC ) {
+            otherPackages.removeAll(ctx.getPackagesWithoutSources());
+        }
+        apiPackages.addAll(otherPackages);
+        if (missing.isEmpty() && apiPackages.isEmpty()) {
             getLog().info("Verified " + artifactType.getId() + " jar for 
region " + apiRegion.getName());
         } else {
             Collections.sort(missing);
-            report.add(artifactType.getId() + " jar for region " + 
apiRegion.getName() + " has " + ( missing.size() + packages.size() ) + " 
errors:");
-            for (final ApiExport m : missing) {
+            report.add(artifactType.getId().concat(" jar for region 
").concat(apiRegion.getName()).concat(" has ").concat( 
String.valueOf(missing.size() + apiPackages.size()) ).concat(" errors:"));
+            for (final ApiExport exp : missing) {
                 final List<String> candidates = new ArrayList<>();
                 for(final ArtifactInfo info : ctx.getArtifactInfos()) {
                     for(final Clause clause : 
info.getUsedExportedPackages(apiRegion)) {
-                        if ( m.getName().equals(clause.getName())) {
+                        if ( exp.getName().equals(clause.getName())) {
                             candidates.add(info.getId().toMvnName());
                             break;
                         }
                     }
                 }
-                report.add("- Missing package " + m.getName() + " from 
bundle(s) "
-                        + String.join(",", candidates));
+                report.add("- Missing package ".concat(exp.getName()).concat(" 
from bundle(s) ").concat(String.join(",", candidates)));
             }
-            for (final String m : packages) {
-                report.add("- Wrong package " + m);
+            for (final String m : apiPackages) {
+                report.add("- Wrong package ".concat(m));
             }
         }
     }
@@ -783,7 +806,7 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
                     // We need to record this kind of packages and ensure we 
don't trigger warnings for them
                     // when checking the api jars for correctness.
                     getLog().debug("No sources found in " + pck);
-                    ctx.getPackagesWithoutSourcesMap().put(pck, info.getId());
+                    ctx.getPackagesWithoutSources().add(pck);
                 }
             }
         }
@@ -807,7 +830,7 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
             // We need to record this kind of packages and ensure we don't 
trigger warnings for them
             // when checking the api jars for correctness.
             getLog().debug("No classes found in " + pck);
-            ctx.getPackagesWithoutJavaClassesMap().put(pck, info.getId());
+            ctx.getPackagesWithoutJavaClasses().add(pck);
         }
     }
 
@@ -1460,12 +1483,9 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
             final ApiRegion apiRegion,
             final ArtifactType archiveType,
             final List<File> resources,
-            final boolean omitDependencyArtifacts) throws 
MojoExecutionException {
+            final Collection<ArtifactInfo> infos) throws 
MojoExecutionException {
         final JarArchiver jarArchiver = new JarArchiver();
 
-        // get all artifacts for this region
-        final Collection<ArtifactInfo> infos = ctx.getArtifactInfos(apiRegion, 
omitDependencyArtifacts);
-
         if ( archiveType == ArtifactType.APIS || archiveType == 
ArtifactType.SOURCES ) {
             // api or source
             for(final ArtifactInfo info : infos) {
@@ -1645,45 +1665,28 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
      * @return {@code true} if generation succeeded
      * @throws MojoExecutionException on error
      */
-    private boolean generateJavadoc(final ApisJarContext ctx, final ApiRegion 
region, final File javadocDir)
+    private Collection<ArtifactInfo> generateJavadoc(final ApisJarContext ctx, 
final ApiRegion region, final JavadocLinks links, final File javadocDir)
             throws MojoExecutionException {
-        final Map<String, Set<String>> linkedPackagesMap = new HashMap<>();
-        final Set<String> linkedGlobalPackages = new HashSet<>();
-
-        final List<String> docLinks = new ArrayList<>();
-        if ( this.javadocLinks != null ) {
-            for(final String val : this.javadocLinks) {
-                docLinks.add(val);
-                ApisUtil.getPackageList(val, linkedGlobalPackages, 
linkedPackagesMap);
-            }
-        }
-
+        final Collection<ArtifactInfo> usedInfos = new ArrayList<>();
 
         final List<String> sourceDirectories = new ArrayList<>();
         final Set<String> javadocPackages = new HashSet<>();
         for(final ArtifactInfo info : ctx.getArtifactInfos(region, false)) {
-            final Set<String> linkedPackages = new 
HashSet<>(linkedGlobalPackages);
-            final List<String> links = 
ApisUtil.getJavadocLinks(info.getArtifact());
-            if ( links != null ) {
-                for(final String v : links) {
-                    ApisUtil.getPackageList(v, linkedPackages, 
linkedPackagesMap);
-                    docLinks.add(v);
-                }
-            }
             boolean addDirectory = false;
             for(final Clause clause : info.getUsedExportedPackages(region)) {
-                if ( 
!ctx.getPackagesWithoutSources().contains(clause.getName()) && 
!linkedPackages.contains(clause.getName())) {
+                if ( 
!ctx.getPackagesWithoutSources().contains(clause.getName()) && 
!links.getLinkedPackages().contains(clause.getName())) {
                     addDirectory = true;
                     javadocPackages.add(clause.getName());
                 }
             }
             if ( addDirectory && info.getSourceDirectory() != null ) {
+                usedInfos.add(info);
                 
sourceDirectories.add(info.getSourceDirectory().getAbsolutePath());
             }
         }
 
         if (javadocPackages.isEmpty()) {
-            return false;
+            return null;
         }
 
         javadocDir.mkdirs();
@@ -1726,8 +1729,8 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
                                               
project.getOrganization().getName().trim()));
         }
 
-        if ( !docLinks.isEmpty()) {
-            javadocExecutor.addArguments("-link", docLinks);
+        if ( !links.getJavadocLinks().isEmpty()) {
+            javadocExecutor.addArguments("-link", links.getJavadocLinks());
         }
 
         if (!ctx.getJavadocClasspath().isEmpty()) {
@@ -1749,18 +1752,47 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
         // .addArgument("-J-Xmx2048m")
         javadocExecutor.execute(javadocDir, getLog(), 
this.ignoreJavadocErrors);
 
-        return true;
+        return usedInfos;
     }
 
-    private List<String> getPackages(final File file, final String extension) 
throws MojoExecutionException {
-        final Set<String> packages = new HashSet<>();
+    /**
+     * Get all packages contained in the archive
+     * @param file The archive to check
+     * @param extension The extension to check for
+     * @return A tuple of packages containing files with the extension and 
packages with files not having the extension
+     * @throws MojoExecutionException
+     */
+    private Map.Entry<Set<String>, Set<String>> getPackages(final File file, 
final String extension) throws MojoExecutionException {
+        final Set<String> packages = new TreeSet<>();
+        final Set<String> otherPackages = new TreeSet<>();
+
+        final Set<String> excludes = new HashSet<>();
+        for(final String v : resourceFolders.split(",")) {
+            excludes.add(v.concat("/"));
+        }
+
         try (final JarInputStream jis = new JarInputStream(new 
FileInputStream(file))) {
             JarEntry entry;
             while ((entry = jis.getNextJarEntry()) != null) {
-                if (entry.getName().endsWith(extension)) {
-                    final int lastPos = entry.getName().lastIndexOf('/');
-                    if (lastPos != -1) {
-                        packages.add(entry.getName().substring(0, 
lastPos).replace('/', '.'));
+                if ( !entry.isDirectory() ) {
+                    boolean exclude = false;
+                    for(final String v : excludes) {
+                        if ( entry.getName().startsWith(v)) {
+                            exclude = true;
+                            break;
+                        }
+                    }
+                    if ( !exclude ) {
+                        final int lastPos = entry.getName().lastIndexOf('/');
+                        if (lastPos != -1) {
+                            final String packageName = 
entry.getName().substring(0, lastPos).replace('/', '.');
+
+                            if (entry.getName().endsWith(extension)) {
+                                packages.add(packageName);
+                            } else {
+                                otherPackages.add(packageName);
+                            }
+                        }
                     }
                 }
                 jis.closeEntry();
@@ -1768,9 +1800,10 @@ public class ApisJarMojo extends 
AbstractIncludingFeatureMojo {
         } catch (final IOException ioe) {
             throw new MojoExecutionException("Unable to scan file " + file + " 
: " + ioe.getMessage());
         }
-        final List<String> sorted = new ArrayList<>(packages);
-        Collections.sort(sorted);
-        return sorted;
+
+        otherPackages.removeAll(packages);
+
+        return Collections.singletonMap(packages, 
otherPackages).entrySet().iterator().next();
     }
 
     private File createLicenseReport(final ApisJarContext ctx, final ApiRegion 
region, final Collection<ArtifactInfo> infos) throws MojoExecutionException {
diff --git 
a/src/main/java/org/apache/sling/feature/maven/mojos/apis/ApisJarContext.java 
b/src/main/java/org/apache/sling/feature/maven/mojos/apis/ApisJarContext.java
index 2dd0eba..356ea6c 100644
--- 
a/src/main/java/org/apache/sling/feature/maven/mojos/apis/ApisJarContext.java
+++ 
b/src/main/java/org/apache/sling/feature/maven/mojos/apis/ApisJarContext.java
@@ -193,9 +193,9 @@ public class ApisJarContext {
 
     private final Set<String> javadocClasspath = new HashSet<>();
 
-    private final Map<String, ArtifactId> packagesWithoutJavaClassesMap = new 
HashMap<>();
+    private final Set<String> packagesWithoutJavaClasses = new HashSet<>();
 
-    private final Map<String, ArtifactId> packagesWithoutSourcesMap = new 
HashMap<>();
+    private final Set<String> packagesWithoutSources = new HashSet<>();
 
     private final File deflatedBinDir;
 
@@ -264,20 +264,12 @@ public class ApisJarContext {
         this.javadocDir = javadocDir;
     }
 
-    public Map<String, ArtifactId> getPackagesWithoutJavaClassesMap() {
-        return packagesWithoutJavaClassesMap;
-    }
-
-    public Map<String, ArtifactId> getPackagesWithoutSourcesMap() {
-        return packagesWithoutSourcesMap;
-    }
-
     public Set<String> getPackagesWithoutJavaClasses() {
-        return packagesWithoutJavaClassesMap.keySet();
+        return packagesWithoutJavaClasses;
     }
 
     public Set<String> getPackagesWithoutSources() {
-        return packagesWithoutSourcesMap.keySet();
+        return packagesWithoutSources;
     }
 
     public ArtifactInfo addArtifactInfo(final Artifact artifact) {
diff --git 
a/src/main/java/org/apache/sling/feature/maven/mojos/apis/JavadocLinks.java 
b/src/main/java/org/apache/sling/feature/maven/mojos/apis/JavadocLinks.java
new file mode 100644
index 0000000..f67e9a5
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/maven/mojos/apis/JavadocLinks.java
@@ -0,0 +1,64 @@
+/*
+ * 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.sling.feature.maven.mojos.apis;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.sling.feature.maven.mojos.apis.ApisJarContext.ArtifactInfo;
+
+public class JavadocLinks {
+
+    private final Set<String> linkedGlobalPackages = new HashSet<>();
+
+    private final Set<String> docLinks = new HashSet<>();
+
+    public void calculateLinks(final String[] globalJavaDocLinks,
+            final Collection<ArtifactInfo> infos) throws 
MojoExecutionException {
+        final Map<String, Set<String>> linkedPackagesMap = new HashMap<>();
+        this.docLinks.clear();
+        this.linkedGlobalPackages.clear();
+        if ( globalJavaDocLinks != null ) {
+            for(final String val : globalJavaDocLinks) {
+                docLinks.add(val);
+                ApisUtil.getPackageList(val, linkedGlobalPackages, 
linkedPackagesMap);
+            }
+        }
+        for(final ArtifactInfo info : infos) {
+            final List<String> links = 
ApisUtil.getJavadocLinks(info.getArtifact());
+            if ( links != null ) {
+                for(final String v : links) {
+                    ApisUtil.getPackageList(v, linkedGlobalPackages, 
linkedPackagesMap);
+                    docLinks.add(v);
+                }
+            }
+        }
+    }
+
+    public Set<String> getJavadocLinks() {
+        return this.docLinks;
+    }
+
+    public Set<String> getLinkedPackages() {
+        return this.linkedGlobalPackages;
+    }
+}

Reply via email to