You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2020/03/19 09:20:13 UTC

[sling-slingfeature-maven-plugin] branch master updated: SLING-9221 : Add report about duplicates to info mojo

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 fb5b28f  SLING-9221 : Add report about duplicates to info mojo
fb5b28f is described below

commit fb5b28f8b6c49b9130c76ffb4f1ba8e3b0893a11
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Thu Mar 19 10:19:47 2020 +0100

    SLING-9221 : Add report about duplicates to info mojo
---
 README.md                                          |  50 ++++-
 .../apache/sling/feature/maven/mojos/InfoMojo.java | 203 ++++++++++++++++++++-
 .../feature/maven/mojos/UpdateVersionsMojo.java    |   2 +-
 3 files changed, 242 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index 62138d0..0bb346d 100644
--- a/README.md
+++ b/README.md
@@ -276,7 +276,7 @@ found in `src/main/features` as well as features produce with the `aggregate-fea
 Attach a feature archive for each feature in the project to the projects produced artifacts. This includes features
 found in `src/main/features` as well as features produce with the `aggregate-features` goal if no configuration is specified.
 
-## extract-extension
+## Extract contents of an Extension (extract-extension)
 This goal can be used to extract the contents of an extension into a local file, which may be useful for other tools that can work on the content of the extension.
 
 The goal is configured as in the following example:
@@ -304,7 +304,7 @@ Output files are written to the output directory follows where the file name is
 * ARTIFACT extensions: the file contains the Maven IDs for each artifact. One ID per line.
 
 
-## update-feature-versions
+## Update artifact Versions (update-feature-versions)
 
 Dependencies get out of date over time, with the `update-feature-versions` goal, all artifacts in a feature can be checked for available updates and updated to a newer version. By default all artifacts in all feature files in the project are checked and updated:
 
@@ -346,7 +346,7 @@ Instead of specifying a scope, `includes` can also be used to define a specific
 ```
 
 
-## repository
+## Create an artifact repository (repository)
 
 With the repository goal, a directory with all artifacts from the selected features will be created.
 
@@ -372,6 +372,50 @@ With the repository goal, a directory with all artifacts from the selected featu
  </execution>
 ```
 
+## Extract Infos (info)
+
+The `info` goal allows to extract information about one or more features. It can be used within a Maven project or standalone.
+
+### Duplicates Report
+
+The duplicates report is turned off by default, it can be enabled by specifying the `outputDuplicates` either as a configuration for the mojo or a parameter from the command line. Possible values are `all`, `bundles`, `configurations`, `artifacts`, `framework-properties`. The value can be a comma separated list of these values.
+
+The report will be generated in the build directory in a file named 'duplicates-report.txt'.
+
+### Exported Packages
+
+By default a text file with a list of exported packages is generated for each feature. Generation of this file can be turned off by specifying the configuration/command line parameter `outputExportedPackages` with a value of *false*.
+
+### Usage in a Maven Project
+
+When used in a Maven project, the list of features for the input goal can be specified by defining the feature set for the goal. The features are configured as described in the global configuration section above. If no configuration is provided, all feature files from the project are used.
+
+```
+<execution>
+  <id>info</id>
+  <goals>
+    <goal>info</goal>
+  </goals>
+  <configuration>
+    <infoFeatures>
+        <!-- specify which feature files to include -->
+        <!-- optional include for local files, this can be specified more than once -->
+        <filesInclude>**/*.json</filesInclude>
+    </infoFeatures>
+  </configuration>
+</execution>
+```
+
+
+### Standalone Usage
+
+The `info` goal can also be used standalone without a maven project. In this case the property `featureFile` needs to point to a feature file:
+
+```
+    mvn slingfeature:info -DfeatureFile=/path/to/my/feature.json
+```
+
+
 ## Feature Launcher (launch-features)
 
 **Attention**: This Mojo is BETA meaning under development and new released
diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/InfoMojo.java b/src/main/java/org/apache/sling/feature/maven/mojos/InfoMojo.java
index c6c57c7..3a74411 100644
--- a/src/main/java/org/apache/sling/feature/maven/mojos/InfoMojo.java
+++ b/src/main/java/org/apache/sling/feature/maven/mojos/InfoMojo.java
@@ -24,16 +24,26 @@ import java.io.Reader;
 import java.io.Writer;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
 
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Configuration;
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionType;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.builder.ArtifactProvider;
 import org.apache.sling.feature.io.json.FeatureJSONReader;
@@ -43,7 +53,6 @@ import org.apache.sling.feature.scanner.FeatureDescriptor;
 import org.apache.sling.feature.scanner.PackageInfo;
 import org.apache.sling.feature.scanner.Scanner;
 
-import edu.emory.mathcs.backport.java.util.Collections;
 
 /**
  * Extract information from a feature This mojo does not require a project, it
@@ -57,19 +66,41 @@ import edu.emory.mathcs.backport.java.util.Collections;
 @Mojo(requiresProject = false, name = "info", threadSafe = true)
 public class InfoMojo extends AbstractIncludingFeatureMojo {
 
+    public enum DUPLICATE {
+        bundles,
+        configurations,
+        artifacts,
+        frameworkproperties
+    }
+
     private static final String FILE_EXPORT_PACKAGES = "export-packages.txt";
 
+    private static final String FILE_DUPLICATES_REPORT = "duplicates-report.txt";
+
+    private static final String DUPLICATES_ALL = "all";
+
+    private static final String DUPLICATES_BUNDLES = "bundles";
+
+    private static final String DUPLICATES_CONFIGURATIONS = "configurations";
+
+    private static final String DUPLICATES_ARTIFACTS = "artifacts";
+
+    private static final String DUPLICATES_PROPERTIES = "framework-properties";
+
     @Parameter(property = "featureFile")
     private File featureFile;
 
     @Parameter(property = "outputExportedPackages", defaultValue = "true")
     private boolean outputExportedPackages;
 
+    @Parameter(property = "outputDuplicates")
+    private String outputDuplicates;
+
     @Parameter(readonly = true, defaultValue = "${project.build.directory}")
     private File buildDirectory;
 
     /**
-     * Select the features for api generation.
+     * Select the features for info generation.
      */
     @Parameter
     private FeatureSelectionConfig infoFeatures;
@@ -78,20 +109,148 @@ public class InfoMojo extends AbstractIncludingFeatureMojo {
     public void execute() throws MojoExecutionException, MojoFailureException {
         final boolean isStandalone = "standalone-pom".equals(project.getArtifactId());
 
-        // setup scanner
-        final Scanner scanner = setupScanner();
+        final List<Map.Entry<Feature, File>> selection = selectFeatures(isStandalone);
+
+        if (outputExportedPackages) {
+            // setup scanner
+            final Scanner scanner = setupScanner();
+            for(final Map.Entry<Feature, File> entry : selection ) {
+                process(scanner, entry.getKey(), entry.getValue());
+            }
+        }
+        if ( selection.size() > 1 && this.outputDuplicates != null ) {
+            processDuplicates(this.outputDuplicates, selection);
+        }
+    }
+
 
+    /**
+     * Select the features to process
+     * @throws MojoExecutionException
+     */
+    private List<Map.Entry<Feature, File>>  selectFeatures(final boolean isStandalone) throws MojoExecutionException {
+        final List<Map.Entry<Feature, File>> result = new ArrayList<>();
         if (isStandalone || featureFile != null) {
-            final Feature feature = readFeature();
-            // wired code to get the current directory, but its needed
-            process(scanner, feature, Paths.get(".").toAbsolutePath().getParent().toFile());
+            final Map.Entry<Feature, File> entry = new MapEntry(readFeature(), Paths.get(".").toAbsolutePath().getParent().toFile());
+
+            result.add(entry);
         } else {
             checkPreconditions();
 
-            final Map<String, Feature> features = this.getSelectedFeatures(infoFeatures);
+            final Map<String, Feature> features = infoFeatures == null ? this.selectAllFeatureFiles() : this.getSelectedFeatures(infoFeatures);
             for (final Feature f : features.values()) {
-                process(scanner, f, new File(
+                final Map.Entry<Feature, File> entry = new MapEntry(f, new File(
                         this.project.getBuild().getDirectory() + File.separator + f.getId().toMvnName() + ".info"));
+                result.add(entry);
+            }
+        }
+        return result;
+    }
+
+    private Set<DUPLICATE> getDuplicateConfiguration(final String duplicatesConfig) throws MojoExecutionException {
+        final Set<DUPLICATE> cfg = new HashSet<>();
+        for(final String c : duplicatesConfig.split(",")) {
+            final String value = c.trim();
+            boolean valid = false;
+            if ( DUPLICATES_ARTIFACTS.equals(value) || DUPLICATES_ALL.equals(value)) {
+                cfg.add(DUPLICATE.artifacts);
+                valid = true;
+            }
+            if ( DUPLICATES_BUNDLES.equals(value) || DUPLICATES_ALL.equals(value)) {
+                cfg.add(DUPLICATE.bundles);
+                valid = true;
+            }
+            if ( DUPLICATES_CONFIGURATIONS.equals(value) || DUPLICATES_ALL.equals(value)) {
+                cfg.add(DUPLICATE.configurations);
+                valid = true;
+            }
+            if ( DUPLICATES_PROPERTIES.equals(value) || DUPLICATES_ALL.equals(value)) {
+                cfg.add(DUPLICATE.frameworkproperties);
+                valid = true;
+            }
+            if ( !valid) {
+                throw new MojoExecutionException("Invalid configuration value for duplicates : " + value);
+            }
+        }
+        return cfg;
+    }
+
+    private void processDuplicates(final String duplicatesConfig, List<Map.Entry<Feature, File>> selection) throws MojoExecutionException {
+         final Set<DUPLICATE> cfg = getDuplicateConfiguration(duplicatesConfig);
+
+         final Map<String, List<ArtifactId>> artifactMap = new TreeMap<>();
+         final Map<String, List<ArtifactId>> bundleMap = new TreeMap<>();
+         final Map<String, List<ArtifactId>> configMap = new TreeMap<>();
+         final Map<String, List<ArtifactId>> propsMap = new TreeMap<>();
+
+         for(final Map.Entry<Feature, File> entry : selection) {
+             final Feature feature = entry.getKey();
+             if ( cfg.contains(DUPLICATE.artifacts)) {
+                 for(final Extension ext : feature.getExtensions()) {
+                     if ( ext.getType() == ExtensionType.ARTIFACTS ) {
+                         for(final Artifact a : ext.getArtifacts()) {
+                             artifactMap.putIfAbsent(a.getId().toMvnId(), new ArrayList<>());
+                             artifactMap.get(a.getId().toMvnId()).add(feature.getId());
+                         }
+                     }
+                 }
+             }
+
+             if ( cfg.contains(DUPLICATE.bundles)) {
+                 for(final Artifact a : feature.getBundles()) {
+                     bundleMap.putIfAbsent(a.getId().toMvnId(), new ArrayList<>());
+                     bundleMap.get(a.getId().toMvnId()).add(feature.getId());
+                 }
+             }
+
+             if ( cfg.contains(DUPLICATE.configurations)) {
+                 for(final Configuration c : feature.getConfigurations()) {
+                     configMap.putIfAbsent(c.getPid(), new ArrayList<>());
+                     configMap.get(c.getPid()).add(feature.getId());
+                 }
+             }
+
+             if ( cfg.contains(DUPLICATE.frameworkproperties)) {
+                 for(final String a : feature.getFrameworkProperties().keySet()) {
+                     propsMap.putIfAbsent(a, new ArrayList<>());
+                     propsMap.get(a).add(feature.getId());
+                 }
+             }
+         }
+         final List<String> output = new ArrayList<>();
+         outputDuplicates(output, DUPLICATES_PROPERTIES, propsMap);
+         outputDuplicates(output, DUPLICATES_BUNDLES, bundleMap);
+         outputDuplicates(output, DUPLICATES_CONFIGURATIONS, configMap);
+         outputDuplicates(output, DUPLICATES_ARTIFACTS, artifactMap);
+         if ( output.isEmpty()) {
+             output.add("No duplicates found");
+         }
+
+         final File out = new File(this.buildDirectory, FILE_DUPLICATES_REPORT);
+         try {
+             Files.write(out.toPath(), output);
+         } catch (IOException e) {
+             throw new MojoExecutionException("Unable to write report", e);
+         }
+         getLog().info("Generated duplicates report at " + out);
+    }
+
+    private void outputDuplicates(final List<String> output, final String key, final Map<String, List<ArtifactId>> duplicates) {
+        boolean writeHeader;
+        if ( !duplicates.isEmpty() ) {
+            writeHeader = true;
+            for(final Map.Entry<String, List<ArtifactId>> entry : duplicates.entrySet()) {
+                if ( entry.getValue().size() > 1 ) {
+                    if ( writeHeader ) {
+                        writeHeader = false;
+                        output.add(key);
+                        output.add("-------------------------------------------");
+                    }
+                    output.add(entry.getKey().concat(" : ").concat(entry.getValue().stream().map(id -> id.getClassifier()).collect(Collectors.toList()).toString()));
+                }
+            }
+            if ( !writeHeader ) {
+                output.add("");
             }
         }
     }
@@ -185,4 +344,30 @@ public class InfoMojo extends AbstractIncludingFeatureMojo {
             throw new MojoExecutionException("Unable to write output file " + ioe.getMessage(), ioe);
         }
     }
+
+    private static final class MapEntry implements Map.Entry<Feature, File> {
+
+        private final Feature feature;
+        private final File file;
+
+        public MapEntry(final Feature f, final File file) {
+            this.feature = f;
+            this.file = file;
+        }
+
+        @Override
+        public Feature getKey() {
+            return this.feature;
+        }
+
+        @Override
+        public File getValue() {
+            return this.file;
+        }
+
+        @Override
+        public File setValue(File value) {
+            return null;
+        }
+    }
 }
diff --git a/src/main/java/org/apache/sling/feature/maven/mojos/UpdateVersionsMojo.java b/src/main/java/org/apache/sling/feature/maven/mojos/UpdateVersionsMojo.java
index 2850e7a..e4280d4 100644
--- a/src/main/java/org/apache/sling/feature/maven/mojos/UpdateVersionsMojo.java
+++ b/src/main/java/org/apache/sling/feature/maven/mojos/UpdateVersionsMojo.java
@@ -163,7 +163,7 @@ public class UpdateVersionsMojo extends AbstractIncludingFeatureMojo {
                             selected = true;
                             break;
                         }
-                    } else if ( classifier.equals(c)) {
+                    } else if ( classifier.trim().equals(c)) {
                         selected = true;
                         break;
                     }