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/06/01 10:37:14 UTC

[sling-jspc-maven-plugin] branch issue/SLING-10399 created (now 678be9f)

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

radu pushed a change to branch issue/SLING-10399
in repository https://gitbox.apache.org/repos/asf/sling-jspc-maven-plugin.git.


      at 678be9f  SLING-10399 - Enhance the compilation report provided by the jspc-maven-plugin

This branch includes the following new commits:

     new 678be9f  SLING-10399 - Enhance the compilation report provided by the jspc-maven-plugin

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[sling-jspc-maven-plugin] 01/01: SLING-10399 - Enhance the compilation report provided by the jspc-maven-plugin

Posted by ra...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 678be9f4c4b8e2a22f69c3ba966f9117d4ccd8e6
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Tue Jun 1 12:35:24 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
    packged 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() %>