You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/10/18 23:30:23 UTC

[sling-jspc-maven-plugin] 15/23: SLING-6925 Make JSPC plugin useful to validation and analysis. Patch from Tobias Bocanegra

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

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

commit 8d193dd29b3d0f42aa98a40fe2ea313a3dd42327
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Fri Jun 2 12:24:26 2017 +0000

    SLING-6925 Make JSPC plugin useful to validation and analysis. Patch from Tobias Bocanegra
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1797381 13f79535-47bb-0310-9956-ffa450edef68
---
 .../java/org/apache/sling/maven/jspc/JspcMojo.java | 148 +++++++++++++++++++--
 .../sling/maven/jspc/TrackingClassLoader.java      |  83 ++++++++++++
 2 files changed, 221 insertions(+), 10 deletions(-)

diff --git a/src/main/java/org/apache/sling/maven/jspc/JspcMojo.java b/src/main/java/org/apache/sling/maven/jspc/JspcMojo.java
index 043c205..dab978a 100644
--- a/src/main/java/org/apache/sling/maven/jspc/JspcMojo.java
+++ b/src/main/java/org/apache/sling/maven/jspc/JspcMojo.java
@@ -19,11 +19,15 @@ package org.apache.sling.maven.jspc;
 import java.io.File;
 import java.io.IOException;
 import java.net.URL;
-import java.net.URLClassLoader;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
 import javax.servlet.ServletContext;
@@ -51,6 +55,7 @@ import org.apache.sling.scripting.jsp.jasper.compiler.JspRuntimeContext;
 import org.apache.sling.scripting.jsp.jasper.compiler.TagPluginManager;
 import org.apache.sling.scripting.jsp.jasper.compiler.TldLocationsCache;
 import org.codehaus.plexus.util.DirectoryScanner;
+import org.codehaus.plexus.util.StringUtils;
 
 /**
  * The <code>JspcMojo</code> is implements the Sling Maven JspC goal
@@ -113,6 +118,12 @@ public class JspcMojo extends AbstractMojo implements Options {
     private String compilerSourceVM;
 
     /**
+     * Prints a compilation report by listing all the packages and dependencies that were used during processing the JSPs.
+     */
+    @Parameter ( property = "jspc.printCompilationReport", defaultValue = "false")
+    private boolean printCompilationReport;
+
+    /**
      * Comma separated list of extensions of files to be compiled by the plugin.
      * @deprecated Use the {@link #includes} filter instead.
      */
@@ -147,16 +158,18 @@ public class JspcMojo extends AbstractMojo implements Options {
 
     private JspRuntimeContext rctxt;
 
-    private URLClassLoader loader = null;
+    private TrackingClassLoader loader;
+
+    private List<Artifact> jspcCompileArtifacts;
 
     /**
      * Cache for the TLD locations
      */
-    private TldLocationsCache tldLocationsCache = null;
+    private TldLocationsCache tldLocationsCache;
 
-    private JspConfig jspConfig = null;
+    private JspConfig jspConfig;
 
-    private TagPluginManager tagPluginManager = null;
+    private TagPluginManager tagPluginManager;
 
     /*
      * (non-Javadoc)
@@ -189,10 +202,11 @@ public class JspcMojo extends AbstractMojo implements Options {
         String oldValue = System.getProperty(LogFactoryImpl.LOG_PROPERTY);
         try {
             // ensure the JSP Compiler does not try to use Log4J
-            System.setProperty(LogFactoryImpl.LOG_PROPERTY,
-                SimpleLog.class.getName());
-
+            System.setProperty(LogFactoryImpl.LOG_PROPERTY, SimpleLog.class.getName());
             executeInternal();
+            if (printCompilationReport) {
+                printCompilationReport();
+            }
         } catch (JasperException je) {
             getLog().error("Compilation Failure", je);
             throw new MojoExecutionException(je.getMessage(), je);
@@ -354,13 +368,13 @@ public class JspcMojo extends AbstractMojo implements Options {
     private void initClassLoader() throws IOException,
             DependencyResolutionRequiredException {
         List<URL> classPath = new ArrayList<URL>();
-
         // add output directory to classpath
         final String targetDirectory = project.getBuild().getOutputDirectory();
         classPath.add(new File(targetDirectory).toURI().toURL());
 
         // add artifacts from project
         Set<Artifact> artifacts = project.getDependencyArtifacts();
+        jspcCompileArtifacts = new ArrayList<Artifact>(artifacts.size());
         for (Artifact a: artifacts) {
             final String scope = a.getScope();
             if ("provided".equals(scope) || "runtime".equals(scope) || "compile".equals(scope)) {
@@ -369,6 +383,7 @@ public class JspcMojo extends AbstractMojo implements Options {
                     continue;
                 }
                 classPath.add(a.getFile().toURI().toURL());
+                jspcCompileArtifacts.add(a);
             }
         }
 
@@ -382,7 +397,7 @@ public class JspcMojo extends AbstractMojo implements Options {
         // this is dangerous to use this classloader as parent as the compilation will depend on the classes provided
         // in the plugin dependencies. but if we omit this, we get errors by not being able to load the TagExtraInfo classes.
         // this is because this plugin uses classes from the javax.servlet.jsp that are also loaded via the TLDs.
-        loader = new URLClassLoader(classPath.toArray(new URL[classPath.size()]), this.getClass().getClassLoader());
+        loader = new TrackingClassLoader(classPath.toArray(new URL[classPath.size()]), this.getClass().getClassLoader());
     }
 
     /**
@@ -398,6 +413,119 @@ public class JspcMojo extends AbstractMojo implements Options {
         return isJSPApi;
     }
 
+    /**
+     * Prints the dependency report.
+     */
+    private void printCompilationReport() {
+        if (loader == null) {
+            return;
+        }
+
+        // first scan all the dependencies for their classes
+        Map<String, Set<String>> artifactsByPackage = new HashMap<String, Set<String>>();
+        Set<String> usedDependencies = new HashSet<String>();
+        for (Artifact a: jspcCompileArtifacts) {
+            scanArtifactPackages(artifactsByPackage, a);
+            usedDependencies.add(a.getId());
+        }
+
+        // create the report
+        StringBuilder report = new StringBuilder("JSP compilation report:\n\n");
+        List<String> packages = new ArrayList<String>(loader.getPackageNames());
+        int pad = 10;
+        for (String packageName: artifactsByPackage.keySet()) {
+            pad = Math.max(pad, packageName.length());
+        }
+        pad += 2;
+        report.append(StringUtils.rightPad("Package", pad)).append("Dependency");
+        report.append("\n---------------------------------------------------------------\n");
+        Collections.sort(packages);
+        for (String packageName: packages) {
+            report.append(StringUtils.rightPad(packageName, pad));
+            Set<String> a = artifactsByPackage.get(packageName);
+            if (a == null || a.isEmpty()) {
+                report.append("n/a");
+            } else {
+                StringBuilder ids = new StringBuilder();
+                for (String id: a) {
+                    usedDependencies.remove(id);
+                    if (ids.length() > 0) {
+                        ids.append(", ");
+                    }
+                    ids.append(id);
+                }
+                report.append(ids);
+            }
+            report.append("\n");
+        }
+
+        // print the unused dependencies
+        report.append("\n");
+        report.append(usedDependencies.size()).append(" dependencies not used by JSPs:\n");
+        if (!usedDependencies.isEmpty()) {
+            report.append("---------------------------------------------------------------\n");
+            for (String id: usedDependencies) {
+                report.append(id).append("\n");
+            }
+        }
+
+        // create the package list that are double defined
+        int doubleDefined = 0;
+        StringBuilder msg = new StringBuilder();
+        packages = new ArrayList<String>(artifactsByPackage.keySet());
+        Collections.sort(packages);
+        for (String packageName: packages) {
+            Set<String> a = artifactsByPackage.get(packageName);
+            if (a != null && a.size() > 1) {
+                doubleDefined++;
+                msg.append(StringUtils.rightPad(packageName, pad));
+                msg.append(StringUtils.join(a.iterator(), ", ")).append("\n");
+            }
+        }
+        report.append("\n");
+        report.append(doubleDefined).append(" packages are multiply defined by dependencies:\n");
+        if (doubleDefined > 0) {
+            report.append("---------------------------------------------------------------\n");
+            report.append(msg);
+        }
+
+        getLog().info(report);
+    }
+
+    /**
+     * Scans the given artifact for classes and add their packages to the given map.
+     * @param artifactsByPackage the package to artifact lookup map
+     * @param a the artifact to scan
+     */
+    private void scanArtifactPackages(Map<String, Set<String>> artifactsByPackage, Artifact a) {
+        try {
+            JarFile jar = new JarFile(a.getFile());
+            Enumeration<JarEntry> entries = jar.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry e = entries.nextElement();
+                if (e.isDirectory()) {
+                    continue;
+                }
+                String path = e.getName();
+                if (path.endsWith(".class")) {
+                    path = StringUtils.chomp(path, "/");
+                    if (path.charAt(0) == '/') {
+                        path = path.substring(1);
+                    }
+                    String packageName = path.replaceAll("/", ".");
+                    Set<String> artifacts = artifactsByPackage.get(packageName);
+                    if (artifacts == null) {
+                        artifacts = new HashSet<String>();
+                        artifactsByPackage.put(packageName, artifacts);
+                    }
+                    artifacts.add(a.getId());
+                }
+            }
+        } catch (IOException e) {
+            getLog().error("Error while accessing jar file: " + e.getMessage());
+        }
+    }
+
     // ---------- Options interface --------------------------------------------
 
     /*
diff --git a/src/main/java/org/apache/sling/maven/jspc/TrackingClassLoader.java b/src/main/java/org/apache/sling/maven/jspc/TrackingClassLoader.java
new file mode 100644
index 0000000..989c47b
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/jspc/TrackingClassLoader.java
@@ -0,0 +1,83 @@
+/*
+ * 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.maven.jspc;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Classloader that tracks which classes are loaded.
+ */
+public class TrackingClassLoader extends URLClassLoader {
+
+    private final Set<String> classNames = Collections.synchronizedSet(new HashSet<String>());
+
+    private final Set<String> packageNames = Collections.synchronizedSet(new HashSet<String>());
+
+    public TrackingClassLoader(URL[] urls, ClassLoader parent) {
+        super(urls, parent);
+    }
+
+    /**
+     * Returns the loaded classes.
+     * @return the set of class names.
+     */
+    public Set<String> getClassNames() {
+        return classNames;
+    }
+
+    /**
+     * Returns the package names of the loaded classes.
+     * @return the set of package names.
+     */
+    public Set<String> getPackageNames() {
+        return packageNames;
+    }
+
+    /**
+     * @see java.lang.ClassLoader#loadClass(java.lang.String)
+     */
+    @Override
+    public Class<?> loadClass(final String name) throws ClassNotFoundException {
+        final Class<?> c = super.loadClass(name);
+        this.classNames.add(name);
+        this.packageNames.add(c.getPackage().getName());
+        return c;
+    }
+
+    @Override
+    public URL findResource(String name) {
+        final URL url = super.findResource(name);
+        if (url != null && name.endsWith(".class")) {
+            int lastDot = name.lastIndexOf('.');
+            int lastSlash = name.lastIndexOf('/');
+            String className = name.substring(0, lastDot).replaceAll("/", ".");
+            classNames.add(className);
+            if (lastSlash > 0) {
+                packageNames.add(className.substring(0, lastSlash));
+            }
+        }
+        return url;
+    }
+
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.