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>.