You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2021/07/05 08:32:05 UTC

[sling-jspc-maven-plugin] branch master updated: SLING-10399 - Enhance the compilation report provided by the jspc-maven-plugin

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

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


The following commit(s) were added to refs/heads/master by this push:
     new e611ace  SLING-10399 - Enhance the compilation report provided by the jspc-maven-plugin
e611ace is described below

commit e611ace465d1765bfd56657d377b41e11aa40aea
Author: Radu Cotescu <17...@users.noreply.github.com>
AuthorDate: Mon Jul 5 10:31:58 2021 +0200

    SLING-10399 - Enhance the compilation report provided by the jspc-maven-plugin
    
    * extended the compilation report with information about JSPs dependencies
    collected via the usage of the include directive
    * added an option to generate the compilation report in a JSON file to be
    packaged with the project where the jspc-maven-plugin is used for JSP compilation
---
 pom.xml                                            |   9 +-
 .../apache/sling/maven/jspc/DependencyTracker.java | 158 +++++++++++++++
 .../java/org/apache/sling/maven/jspc/JspcMojo.java | 212 +++++++++++++++------
 .../org/apache/sling/maven/jspc/JspcMojoTest.java  |  54 ++++++
 .../jspc-maven-plugin-it-includes/pom.xml          |   8 +
 .../0.0.1/jspc-maven-plugin-it-deps-0.0.1.jar      | Bin 0 -> 1434 bytes
 .../0.0.1/jspc-maven-plugin-it-deps-0.0.1.pom}     |  16 +-
 .../maven-metadata-local.xml}                      |  19 +-
 .../src/main/scripts/main.jsp                      |   2 +
 9 files changed, 404 insertions(+), 74 deletions(-)

diff --git a/pom.xml b/pom.xml
index c5fc441..e5023b9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -44,7 +44,7 @@
 
     <properties>
         <maven.site.path>${project.artifactId}-archives/${project.artifactId}-LATEST</maven.site.path>
-        <site.jira.version.id>12313225,12313347,12319479,12319561,12325751,12347153,12348438,12348556</site.jira.version.id>
+        <site.jira.version.id>12313225,12313347,12319479,12319561,12325751,12347153,12348438,12348556,12350208</site.jira.version.id>
         <maven.version>3.5.0</maven.version>
     </properties>
 
@@ -157,7 +157,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.scripting.jsp</artifactId>
-            <version>2.5.0</version>
+            <version>2.5.3-SNAPSHOT</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
@@ -173,6 +173,11 @@
             <scope>compile</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.johnzon</artifactId>
+            <version>1.2.6</version>
+        </dependency>
+        <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
             <scope>compile</scope>
diff --git a/src/main/java/org/apache/sling/maven/jspc/DependencyTracker.java b/src/main/java/org/apache/sling/maven/jspc/DependencyTracker.java
new file mode 100644
index 0000000..7ab4a59
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/jspc/DependencyTracker.java
@@ -0,0 +1,158 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.IOException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.sling.scripting.jsp.jasper.compiler.PageInfo;
+import org.codehaus.plexus.util.StringUtils;
+
+public class DependencyTracker {
+
+    private final Log logger;
+    private final Path projectDirectory;
+    private final Path sourceDirectory;
+    private final JspCServletContext jspCServletContext;
+    private final TrackingClassLoader classLoader;
+    private final List<Artifact> compileScopeArtifacts;
+    private final Map<String, Set<String>> packageProviders;
+    private final Set<String> unusedDependencies;
+    private final Map<String, Set<String>> jspDependencies;
+    private final Map<String, PageInfo> jspInfo;
+    private final Path relativeSourceDirectory;
+
+
+    public DependencyTracker(Log logger, Path projectDirectory, Path sourceDirectory, JspCServletContext jspCServletContext,
+                             TrackingClassLoader classLoader,
+                             List<Artifact> compileScopeArtifacts) {
+        this.logger = logger;
+        this.projectDirectory = projectDirectory;
+        this.sourceDirectory = sourceDirectory;
+        this.jspCServletContext = jspCServletContext;
+        this.classLoader = classLoader;
+        this.compileScopeArtifacts = compileScopeArtifacts;
+        this.packageProviders = new ConcurrentHashMap<>();
+        this.unusedDependencies = Collections.synchronizedSet(new HashSet<>());
+        this.jspDependencies = new ConcurrentHashMap<>();
+        this.jspInfo = new ConcurrentHashMap<>();
+        this.relativeSourceDirectory = projectDirectory.relativize(sourceDirectory);
+    }
+
+    public void processCompileDependencies() {
+        compileScopeArtifacts.forEach(artifact -> {
+            unusedDependencies.add(artifact.getId());
+            try (JarFile jar = new JarFile(artifact.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.replace("/", ".");
+                        Set<String> artifacts = packageProviders.computeIfAbsent(packageName, k -> new HashSet<>());
+                        artifacts.add(artifact.getId());
+                    }
+                }
+            } catch (IOException e) {
+                logger.error("Error while accessing jar file " + artifact.getFile().getAbsolutePath(), e);
+            }
+        });
+        List<String> packages = new ArrayList<>(classLoader.getPackageNames());
+        Collections.sort(packages);
+        for (String packageName: packages) {
+            Set<String> artifacts = packageProviders.get(packageName);
+            if (artifacts != null && !artifacts.isEmpty()) {
+                artifacts.forEach(unusedDependencies::remove);
+            }
+        }
+        jspInfo.forEach((jsp, pageInfo) -> {
+            List dependencies = pageInfo.getDependants();
+            if (!dependencies.isEmpty()) {
+                Set<String> dependenciesSet =
+                        jspDependencies.computeIfAbsent(Paths.get(relativeSourceDirectory.toString(), jsp).toString(), key -> new HashSet<>());
+                for (Object d : dependencies) {
+                    String dependency = (String) d;
+                    try {
+                        URL dependencyURL = jspCServletContext.getResource(dependency);
+                        if (dependencyURL != null) {
+                            Path dependencyPath = Paths.get(dependencyURL.getPath());
+                            if (dependencyPath.startsWith(sourceDirectory)) {
+                                dependenciesSet.add(projectDirectory.relativize(dependencyPath).toString());
+                            } else {
+                                dependenciesSet.add(dependencyURL.toExternalForm());
+                            }
+                        } else {
+                            // the dependency comes from a JAR
+                            if (dependency.startsWith("/")) {
+                                dependency = dependency.substring(1);
+                            }
+                            JarURLConnection jarURLConnection = (JarURLConnection) classLoader.findResource(dependency).openConnection();
+                            for (Artifact a : compileScopeArtifacts) {
+                                if (a.getFile().getAbsolutePath().equals(jarURLConnection.getJarFile().getName())) {
+                                    unusedDependencies.remove(a.getId());
+                                }
+                            }
+                            dependenciesSet.add(Paths.get(jarURLConnection.getJarFileURL().getPath()).getFileName() + ":/" + dependency);
+                        }
+                    } catch (IOException e) {
+                        dependenciesSet.add(dependency);
+                    }
+                }
+            }
+        });
+    }
+
+    public void collectJSPInfo(String jspFile, PageInfo pageInfo) {
+        jspInfo.put(jspFile, pageInfo);
+    }
+
+    public Map<String, Set<String>> getPackageProviders() {
+        return Collections.unmodifiableMap(packageProviders);
+    }
+
+    public Set<String> getUnusedDependencies() {
+        return Collections.unmodifiableSet(unusedDependencies);
+    }
+
+    public Map<String, Set<String>> getJspDependencies() {
+        return Collections.unmodifiableMap(jspDependencies);
+    }
+}
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 9fa867f..b894452 100644
--- a/src/main/java/org/apache/sling/maven/jspc/JspcMojo.java
+++ b/src/main/java/org/apache/sling/maven/jspc/JspcMojo.java
@@ -19,17 +19,26 @@ package org.apache.sling.maven.jspc;
 import java.io.File;
 import java.io.IOException;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 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.json.Json;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonWriter;
+import javax.json.JsonWriterFactory;
+import javax.json.stream.JsonGenerator;
+
 import org.apache.commons.logging.impl.LogFactoryImpl;
 import org.apache.commons.logging.impl.SimpleLog;
 import org.apache.maven.artifact.Artifact;
@@ -45,12 +54,12 @@ import org.apache.sling.commons.classloader.ClassLoaderWriter;
 import org.apache.sling.commons.compiler.JavaCompiler;
 import org.apache.sling.commons.compiler.impl.EclipseJavaCompiler;
 import org.apache.sling.scripting.jsp.jasper.Constants;
-import org.apache.sling.scripting.jsp.jasper.IOProvider;
 import org.apache.sling.scripting.jsp.jasper.JasperException;
 import org.apache.sling.scripting.jsp.jasper.JspCompilationContext;
 import org.apache.sling.scripting.jsp.jasper.Options;
 import org.apache.sling.scripting.jsp.jasper.compiler.JspConfig;
 import org.apache.sling.scripting.jsp.jasper.compiler.JspRuntimeContext;
+import org.apache.sling.scripting.jsp.jasper.compiler.PageInfo;
 import org.apache.sling.scripting.jsp.jasper.compiler.TagPluginManager;
 import org.apache.sling.scripting.jsp.jasper.compiler.TldLocationsCache;
 import org.codehaus.plexus.util.DirectoryScanner;
@@ -138,6 +147,44 @@ public class JspcMojo extends AbstractMojo implements Options {
     private boolean printCompilationReport;
 
     /**
+     * Generates a compilation report text file (compilation_report.json) in the {@code ${project.build.outputDirectory}}.
+     *
+     * <p>
+     *     The compilation report has the following format:
+     *     <pre>
+     *     {
+     *         "packageProviders": [
+     *             {
+     *                 "package": "&lt;a Java package name&gt;",
+     *                 "providers": [
+     *                     "&lt;Maven Artifact ID&gt;"
+     *                 ]
+     *             }
+     *         ],
+     *         "jspDependencies": [
+     *             {
+     *                 "jsp": "src/main/scripts/&lt;a script&gt;.jsp",
+     *                 "dependencies": [
+     *                     "&lt;jar file name&gt;:&lt;path to JSP&gt;",
+     *                     "src/main/scripts/&lt;path to file in local project&gt;.jsp"
+     *                 ]
+     *             }
+     *         ],
+     *         "unusedDependencies": [
+     *             "&lt;Maven Artifact ID&gt;"
+     *         ]
+     *      }
+     *     </pre>
+     * </p>
+     *
+     * @since 2.3.0
+     */
+    @Parameter(property = "jspc.generateCompilationReport", defaultValue = "false")
+    private boolean generateCompilationReport;
+
+    private static final String COMPILATION_REPORT = "compilation_report.json";
+
+    /**
      * Comma separated list of extensions of files to be compiled by the plugin.
      * @deprecated Use the {@link #includes} filter instead.
      */
@@ -185,6 +232,10 @@ public class JspcMojo extends AbstractMojo implements Options {
 
     private TagPluginManager tagPluginManager;
 
+    private JspCIOProvider ioProvider;
+
+    private DependencyTracker dependencyTracker;
+
     /*
      * (non-Javadoc)
      *
@@ -221,8 +272,13 @@ public class JspcMojo extends AbstractMojo implements Options {
             previousJasperPackageName =
                     System.setProperty(Constants.JSP_PACKAGE_NAME_PROPERTY_NAME, (servletPackage != null ? servletPackage : ""));
             executeInternal();
-            if (printCompilationReport) {
-                printCompilationReport();
+            if (dependencyTracker != null) {
+                if (printCompilationReport) {
+                    printCompilationReport(dependencyTracker);
+                }
+                if (generateCompilationReport) {
+                    generateCompilationReport(dependencyTracker);
+                }
             }
         } catch (JasperException je) {
             getLog().error("Compilation Failure", je);
@@ -273,6 +329,17 @@ public class JspcMojo extends AbstractMojo implements Options {
                 initServletContext();
             }
 
+
+            if (printCompilationReport || generateCompilationReport) {
+                dependencyTracker = new DependencyTracker(
+                        getLog(),
+                        project.getBasedir().toPath(),
+                        sourceDirectory.toPath(),
+                        context,
+                        loader,
+                        jspcCompileArtifacts);
+            }
+
             if (includes == null) {
                 includes = new String[]{ "**/*.jsp" };
             }
@@ -313,6 +380,9 @@ public class JspcMojo extends AbstractMojo implements Options {
                     }
                 }
             });
+            if (dependencyTracker != null) {
+                dependencyTracker.processCompileDependencies();
+            }
 
         } catch (JasperException je) {
             Throwable rootCause = je;
@@ -335,7 +405,11 @@ public class JspcMojo extends AbstractMojo implements Options {
             final String jspUri = file.replace('\\', '/');
             final JspCompilationContext clctxt = new JspCompilationContext(jspUri, false, this, context, rctxt, false);
 
-            JasperException error = clctxt.compile();
+            JasperException error = clctxt.compile(true);
+            PageInfo pageInfo = clctxt.getCompiler().getPageInfo();
+            if (pageInfo != null && dependencyTracker != null) {
+                dependencyTracker.collectJSPInfo(file, pageInfo);
+            }
             if (error != null) {
                 throw error;
             }
@@ -384,7 +458,7 @@ public class JspcMojo extends AbstractMojo implements Options {
 
         JavaCompiler compiler = new EclipseJavaCompiler();
         ClassLoaderWriter writer = new JspCClassLoaderWriter(loader, new File(outputDirectory));
-        IOProvider ioProvider = new JspCIOProvider(loader, compiler, writer);
+        ioProvider = new JspCIOProvider(loader, compiler, writer);
         rctxt = new JspRuntimeContext(context, this, ioProvider);
         jspConfig = new JspConfig(context);
         tagPluginManager = new TagPluginManager(context);
@@ -448,39 +522,27 @@ public class JspcMojo extends AbstractMojo implements Options {
     /**
      * 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
+    private void printCompilationReport(DependencyTracker dependencyTracker) {
         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()) {
+        Map<String, Set<String>> packageProviders = dependencyTracker.getPackageProviders();
+        List<String> packages = new ArrayList<>(packageProviders.keySet());
+        for (String packageName: packages) {
             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()) {
+            Set<String> artifacts = packageProviders.get(packageName);
+            if (artifacts == null || artifacts.isEmpty()) {
                 report.append("n/a");
             } else {
                 StringBuilder ids = new StringBuilder();
-                for (String id: a) {
-                    usedDependencies.remove(id);
+                for (String id: artifacts) {
                     if (ids.length() > 0) {
                         ids.append(", ");
                     }
@@ -493,10 +555,11 @@ public class JspcMojo extends AbstractMojo implements Options {
 
         // print the unused dependencies
         report.append("\n");
-        report.append(usedDependencies.size()).append(" dependencies not used by JSPs:\n");
-        if (!usedDependencies.isEmpty()) {
+        Set<String> unusedDependencies = dependencyTracker.getUnusedDependencies();
+        report.append(unusedDependencies.size()).append(" dependencies not used by JSPs:\n");
+        if (!unusedDependencies.isEmpty()) {
             report.append("---------------------------------------------------------------\n");
-            for (String id: usedDependencies) {
+            for (String id: unusedDependencies) {
                 report.append(id).append("\n");
             }
         }
@@ -504,10 +567,8 @@ public class JspcMojo extends AbstractMojo implements Options {
         // 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);
+            Set<String> a = packageProviders.get(packageName);
             if (a != null && a.size() > 1) {
                 doubleDefined++;
                 msg.append(StringUtils.rightPad(packageName, pad));
@@ -515,45 +576,72 @@ public class JspcMojo extends AbstractMojo implements Options {
             }
         }
         report.append("\n");
-        report.append(doubleDefined).append(" packages are multiply defined by dependencies:\n");
+        report.append(doubleDefined).append(" packages are defined by multiple dependencies:\n");
         if (doubleDefined > 0) {
             report.append("---------------------------------------------------------------\n");
             report.append(msg);
         }
 
+        Map<String, Set<String>> jspDependencies = dependencyTracker.getJspDependencies();
+        if (!jspDependencies.isEmpty()) {
+            pad = 10;
+            List<String> jspsWithDependencies = new ArrayList<>(jspDependencies.keySet());
+            for (String jsp : jspsWithDependencies) {
+                pad = Math.max(pad, jsp.length());
+            }
+            pad += 2;
+            report.append("\n");
+            report.append(StringUtils.rightPad("JSP", pad)).append("Dependencies");
+            report.append("\n---------------------------------------------------------------\n");
+            Collections.sort(jspsWithDependencies);
+            for (String jsp : jspsWithDependencies) {
+                report.append(StringUtils.rightPad(jsp, pad));
+                report.append(String.join(", ", jspDependencies.get(jsp)));
+            }
+
+        }
         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;
+    private void generateCompilationReport(DependencyTracker dependencyTracker) {
+        JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder();
+        JsonArrayBuilder providers = Json.createArrayBuilder();
+        JsonArrayBuilder jsps = Json.createArrayBuilder();
+        dependencyTracker.getPackageProviders().forEach(
+                (pkg, providersForPackage) -> {
+                    JsonArrayBuilder providersForPackageBuilder = Json.createArrayBuilder(providersForPackage);
+                    JsonObject provider = Json.createObjectBuilder()
+                            .add("package", pkg)
+                            .add("providers", providersForPackageBuilder)
+                            .build();
+                    providers.add(provider);
                 }
-                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());
+        );
+        dependencyTracker.getJspDependencies().forEach(
+                (jsp, dependencies) -> {
+                    JsonArrayBuilder dependenciesBuilder = Json.createArrayBuilder(dependencies);
+                    JsonObject jspDependency = Json.createObjectBuilder()
+                            .add("jsp", jsp)
+                            .add("dependencies", dependenciesBuilder)
+                            .build();
+                    jsps.add(jspDependency);
                 }
-            }
+        );
+        JsonArrayBuilder unusedDependencies = Json.createArrayBuilder(dependencyTracker.getUnusedDependencies());
+        Path compilationReportPath = Paths.get(project.getBuild().getOutputDirectory(), COMPILATION_REPORT);
+        Map<String, Object> properties = new HashMap<>(1);
+        properties.put(JsonGenerator.PRETTY_PRINTING, true);
+        JsonWriterFactory writerFactory = Json.createWriterFactory(properties);
+        try (JsonWriter jsonWriter = writerFactory.createWriter(Files.newBufferedWriter(compilationReportPath, StandardCharsets.UTF_8))) {
+            jsonWriter.writeObject(
+                    jsonObjectBuilder
+                            .add("packageProviders", providers.build())
+                            .add("jspDependencies", jsps.build())
+                            .add("unusedDependencies", unusedDependencies.build())
+                            .build()
+            );
         } catch (IOException e) {
-            getLog().error("Error while accessing jar file: " + e.getMessage());
+            getLog().error("Cannot generate the " + COMPILATION_REPORT + " file.", e);
         }
     }
 
diff --git a/src/test/java/org/apache/sling/maven/jspc/JspcMojoTest.java b/src/test/java/org/apache/sling/maven/jspc/JspcMojoTest.java
index f7b9d39..111447e 100644
--- a/src/test/java/org/apache/sling/maven/jspc/JspcMojoTest.java
+++ b/src/test/java/org/apache/sling/maven/jspc/JspcMojoTest.java
@@ -21,12 +21,24 @@ package org.apache.sling.maven.jspc;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.json.JsonString;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
 import org.apache.maven.artifact.repository.DefaultArtifactRepository;
+import org.apache.maven.artifact.repository.MavenArtifactRepository;
 import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
 import org.apache.maven.execution.DefaultMavenExecutionRequest;
 import org.apache.maven.execution.MavenExecutionRequest;
@@ -36,12 +48,16 @@ import org.apache.maven.plugin.testing.MojoRule;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.ProjectBuilder;
 import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.repository.UserLocalArtifactRepository;
+import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 
@@ -94,4 +110,42 @@ public class JspcMojoTest {
         assertTrue("Some files were not correctly included: " + expectedContent.toString(), expectedContent.isEmpty());
     }
 
+    @Test
+    public void testCompilationReport() throws Exception {
+        jspcMojo.execute();
+        Path compilationReportPath = Paths.get(mavenProject.getBuild().getOutputDirectory(), "compilation_report.json");
+        try (JsonReader reader = Json.createReader(Files.newBufferedReader(compilationReportPath))) {
+            JsonObject compilationReport = reader.readObject();
+
+            JsonArray unusedDependencies = compilationReport.getJsonArray("unusedDependencies");
+            assertNotNull(unusedDependencies);
+            assertTrue(unusedDependencies.isEmpty());
+
+            JsonArray jspDependencies = compilationReport.getJsonArray("jspDependencies");
+            assertNotNull(jspDependencies);
+            assertEquals(1, jspDependencies.size());
+            JsonObject mainJsp = jspDependencies.getJsonObject(0);
+            assertEquals("src/main/scripts/main.jsp", mainJsp.getString("jsp"));
+            JsonArray mainJspDependencies = mainJsp.getJsonArray("dependencies");
+            assertNotNull(mainJspDependencies);
+            assertEquals(3, mainJspDependencies.size());
+
+            assertEquals(new HashSet<>(Arrays.asList("jspc-maven-plugin-it-includes-deps-0.0.1.jar:/libs/l1/l2/included-cp.jsp", "jspc" +
+                    "-maven-plugin-it-includes-deps-0.0.1.jar:/included-cp.jsp", "src/main/scripts/included-fs.jsp")),
+                    new HashSet<>(mainJspDependencies.getValuesAs(JsonString.class).stream().map(JsonString::getString).collect(
+                            Collectors.toList())));
+
+            JsonArray packageProviders = compilationReport.getJsonArray("packageProviders");
+            assertNotNull(packageProviders);
+            assertEquals(1, packageProviders.size());
+
+            JsonObject provider = packageProviders.getJsonObject(0);
+            assertEquals("org.apache.sling.maven.jspc.it", provider.getString("package"));
+            JsonArray providers = provider.getJsonArray("providers");
+            assertNotNull(providers);
+            assertEquals(1, providers.size());
+            assertEquals("org.apache.sling:jspc-maven-plugin-it-deps:jar:0.0.1", providers.getString(0));
+        }
+    }
+
 }
diff --git a/src/test/resources/jspc-maven-plugin-it-includes/pom.xml b/src/test/resources/jspc-maven-plugin-it-includes/pom.xml
index 80831c4..4f59ad9 100644
--- a/src/test/resources/jspc-maven-plugin-it-includes/pom.xml
+++ b/src/test/resources/jspc-maven-plugin-it-includes/pom.xml
@@ -39,6 +39,9 @@
             <plugin>
                 <groupId>org.apache.sling</groupId>
                 <artifactId>jspc-maven-plugin</artifactId>
+                <configuration>
+                    <generateCompilationReport>true</generateCompilationReport>
+                </configuration>
                 <executions>
                     <execution>
                         <goals>
@@ -56,5 +59,10 @@
             <artifactId>jspc-maven-plugin-it-includes-deps</artifactId>
             <version>0.0.1</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>jspc-maven-plugin-it-deps</artifactId>
+            <version>0.0.1</version>
+        </dependency>
     </dependencies>
 </project>
diff --git a/src/test/resources/jspc-maven-plugin-it-includes/repo/org/apache/sling/jspc-maven-plugin-it-deps/0.0.1/jspc-maven-plugin-it-deps-0.0.1.jar b/src/test/resources/jspc-maven-plugin-it-includes/repo/org/apache/sling/jspc-maven-plugin-it-deps/0.0.1/jspc-maven-plugin-it-deps-0.0.1.jar
new file mode 100644
index 0000000..cb589cd
Binary files /dev/null and b/src/test/resources/jspc-maven-plugin-it-includes/repo/org/apache/sling/jspc-maven-plugin-it-deps/0.0.1/jspc-maven-plugin-it-deps-0.0.1.jar differ
diff --git a/src/test/resources/jspc-maven-plugin-it-includes/src/main/scripts/main.jsp b/src/test/resources/jspc-maven-plugin-it-includes/repo/org/apache/sling/jspc-maven-plugin-it-deps/0.0.1/jspc-maven-plugin-it-deps-0.0.1.pom
similarity index 61%
copy from src/test/resources/jspc-maven-plugin-it-includes/src/main/scripts/main.jsp
copy to src/test/resources/jspc-maven-plugin-it-includes/repo/org/apache/sling/jspc-maven-plugin-it-deps/0.0.1/jspc-maven-plugin-it-deps-0.0.1.pom
index 3761afb..2791d6a 100644
--- a/src/test/resources/jspc-maven-plugin-it-includes/src/main/scripts/main.jsp
+++ b/src/test/resources/jspc-maven-plugin-it-includes/repo/org/apache/sling/jspc-maven-plugin-it-deps/0.0.1/jspc-maven-plugin-it-deps-0.0.1.pom
@@ -1,4 +1,5 @@
-<%--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+<?xml version="1.0" encoding="UTF-8"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   ~ 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
@@ -15,7 +16,12 @@
   ~ KIND, either express or implied.  See the License for the
   ~ specific language governing permissions and limitations
   ~ under the License.
-  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--%>
-<%@include file="included-fs.jsp"%>
-<%@include file="included-cp.jsp"%>
-<%@include file="/libs/l1/l2/included-cp.jsp"%>
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.apache.sling</groupId>
+  <artifactId>jspc-maven-plugin-it-deps</artifactId>
+  <version>0.0.1</version>
+  <description>POM was created from install:install-file</description>
+</project>
diff --git a/src/test/resources/jspc-maven-plugin-it-includes/src/main/scripts/main.jsp b/src/test/resources/jspc-maven-plugin-it-includes/repo/org/apache/sling/jspc-maven-plugin-it-deps/maven-metadata-local.xml
similarity index 69%
copy from src/test/resources/jspc-maven-plugin-it-includes/src/main/scripts/main.jsp
copy to src/test/resources/jspc-maven-plugin-it-includes/repo/org/apache/sling/jspc-maven-plugin-it-deps/maven-metadata-local.xml
index 3761afb..49bc1d6 100644
--- a/src/test/resources/jspc-maven-plugin-it-includes/src/main/scripts/main.jsp
+++ b/src/test/resources/jspc-maven-plugin-it-includes/repo/org/apache/sling/jspc-maven-plugin-it-deps/maven-metadata-local.xml
@@ -1,4 +1,5 @@
-<%--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+<?xml version="1.0" encoding="UTF-8"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   ~ 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
@@ -15,7 +16,15 @@
   ~ KIND, either express or implied.  See the License for the
   ~ specific language governing permissions and limitations
   ~ under the License.
-  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--%>
-<%@include file="included-fs.jsp"%>
-<%@include file="included-cp.jsp"%>
-<%@include file="/libs/l1/l2/included-cp.jsp"%>
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<metadata>
+  <groupId>org.apache.sling</groupId>
+  <artifactId>jspc-maven-plugin-it-deps</artifactId>
+  <versioning>
+    <release>0.0.1</release>
+    <versions>
+      <version>0.0.1</version>
+    </versions>
+    <lastUpdated>20210601101524</lastUpdated>
+  </versioning>
+</metadata>
diff --git a/src/test/resources/jspc-maven-plugin-it-includes/src/main/scripts/main.jsp b/src/test/resources/jspc-maven-plugin-it-includes/src/main/scripts/main.jsp
index 3761afb..78f5b57 100644
--- a/src/test/resources/jspc-maven-plugin-it-includes/src/main/scripts/main.jsp
+++ b/src/test/resources/jspc-maven-plugin-it-includes/src/main/scripts/main.jsp
@@ -16,6 +16,8 @@
   ~ specific language governing permissions and limitations
   ~ under the License.
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--%>
+<%@page import="org.apache.sling.maven.jspc.it.Hello" %>
 <%@include file="included-fs.jsp"%>
 <%@include file="included-cp.jsp"%>
 <%@include file="/libs/l1/l2/included-cp.jsp"%>
+<%= Hello.sayHello() %>