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:38 UTC

[sling-htl-maven-plugin] 39/39: SLING-7155 - htl-maven-plugin: Add option to precompile the HTL scripts

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-htl-maven-plugin.git

commit c7cfbbd49a262e10a73c77686d63871d33f25a84
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Wed Oct 18 16:00:20 2017 +0000

    SLING-7155 - htl-maven-plugin: Add option to precompile the HTL scripts
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1812545 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |   7 +-
 .../org/apache/sling/maven/htl/ValidateMojo.java   | 193 +++++++++++----------
 .../sling/maven/htl/compiler/HTLClassInfo.java     |  54 ++++++
 .../maven/htl/compiler/HTLJavaImportsAnalyzer.java |  42 +++++
 .../maven/htl/compiler/ScriptCompilationUnit.java  |  66 +++++++
 .../apache/sling/maven/htl/ValidateMojoTest.java   |  29 +++-
 .../test-project/default-includes.pom.xml          |   1 -
 .../test-project/explicit-excludes.pom.xml         |   1 +
 .../test-project/generate-java-classes.pom.xml     |  56 ++++++
 .../src/main/resources/apps/projects/Pojo.java     |   3 +
 .../src/main/resources/apps/projects/script.html   |   3 +-
 11 files changed, 360 insertions(+), 95 deletions(-)

diff --git a/pom.xml b/pom.xml
index e5dfe77..bb6d02e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -93,7 +93,12 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.scripting.sightly.compiler</artifactId>
-            <version>1.0.11-SNAPSHOT</version>
+            <version>1.0.14</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.scripting.sightly.compiler.java</artifactId>
+            <version>1.0.15-SNAPSHOT</version>
         </dependency>
         <dependency>
             <groupId>commons-io</groupId>
diff --git a/src/main/java/org/apache/sling/maven/htl/ValidateMojo.java b/src/main/java/org/apache/sling/maven/htl/ValidateMojo.java
index 78765b3..efa9389 100644
--- a/src/main/java/org/apache/sling/maven/htl/ValidateMojo.java
+++ b/src/main/java/org/apache/sling/maven/htl/ValidateMojo.java
@@ -16,16 +16,17 @@
  ******************************************************************************/
 package org.apache.sling.maven.htl;
 
+import java.io.BufferedWriter;
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
+import java.io.FileWriter;
 import java.io.IOException;
-import java.io.Reader;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
@@ -35,12 +36,16 @@ import org.apache.maven.plugins.annotations.LifecyclePhase;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
+import org.apache.sling.maven.htl.compiler.HTLClassInfo;
+import org.apache.sling.maven.htl.compiler.HTLJavaImportsAnalyzer;
+import org.apache.sling.maven.htl.compiler.ScriptCompilationUnit;
 import org.apache.sling.scripting.sightly.compiler.CompilationResult;
-import org.apache.sling.scripting.sightly.compiler.CompilationUnit;
 import org.apache.sling.scripting.sightly.compiler.CompilerMessage;
 import org.apache.sling.scripting.sightly.compiler.SightlyCompiler;
+import org.apache.sling.scripting.sightly.java.compiler.ClassInfo;
+import org.apache.sling.scripting.sightly.java.compiler.JavaClassBackendCompiler;
+import org.apache.sling.scripting.sightly.java.compiler.JavaImportsAnalyzer;
 import org.codehaus.plexus.util.Scanner;
-import org.codehaus.plexus.util.StringUtils;
 import org.sonatype.plexus.build.incremental.BuildContext;
 
 /**
@@ -54,7 +59,7 @@ import org.sonatype.plexus.build.incremental.BuildContext;
 public class ValidateMojo extends AbstractMojo {
 
     private static final String DEFAULT_INCLUDES = "**/*.html";
-    private static final String DEFAULT_EXCLUDES = "";
+    private static final int _16K = 16384;
 
     @Component
     private BuildContext buildContext;
@@ -65,8 +70,7 @@ public class ValidateMojo extends AbstractMojo {
     /**
      * Defines the root folder where this Mojo expects to find Sightly scripts to validate.
      */
-    @Parameter(property = "htl.sourceDirectory",
-               defaultValue = "${project.build.sourceDirectory}")
+    @Parameter(property = "htl.sourceDirectory", defaultValue = "${project.build.sourceDirectory}")
     private File sourceDirectory;
 
     /**
@@ -86,11 +90,31 @@ public class ValidateMojo extends AbstractMojo {
     /**
      * If set to "true" it will fail the build on compiler warnings.
      */
-    @Parameter(property = "htl.failOnWarnings",
-               defaultValue = "false")
+    @Parameter(property = "htl.failOnWarnings", defaultValue = "false")
     private boolean failOnWarnings;
 
     /**
+     * If set to "true" it will generate the Java classes resulted from transpiling the HTL scripts to Java. The generated classes will
+     * be stored in the folder identified by the {@code generatedJavaClassesDirectory} parameter.
+     */
+    @Parameter(property = "htl.generateJavaClasses", defaultValue = "false")
+    private boolean generateJavaClasses;
+
+    /**
+     * Defines the folder where the generated Java classes resulted from transpiling the project's HTL scripts will be stored. This
+     * folder will be added to the list of source folders for this project.
+     */
+    @Parameter(property = "htl.generatedJavaClassesDirectory", defaultValue = "${project.build.directory}/generated-sources/htl")
+    private File generatedJavaClassesDirectory;
+
+    /**
+     * Defines a list of Java packages that should be ignored when generating the import statements for the Java classes resulted from
+     * transpiling the project's HTL scripts. Subpackages of these packages will also be part automatically of the ignore list.
+     */
+    @Parameter(property = "htl.ignoreImports")
+    private Set<String> ignoreImports;
+
+    /**
      * If set to "true" the validation will be skipped.
      */
     @Parameter(property = "htl.skip", defaultValue = "false")
@@ -98,12 +122,8 @@ public class ValidateMojo extends AbstractMojo {
 
     private boolean hasWarnings = false;
     private boolean hasErrors = false;
-    private String processedIncludes = null;
-    private String processedExcludes = null;
     private List<File> processedFiles = Collections.emptyList();
 
-    private int sourceDirectoryLength = 0;
-
     public void execute() throws MojoExecutionException, MojoFailureException {
         if (skip) {
             getLog().info("Skipping validation.");
@@ -123,8 +143,25 @@ public class ValidateMojo extends AbstractMojo {
             throw new MojoExecutionException(
                     String.format("Configured sourceDirectory={%s} is not a directory.", sourceDirectory.getAbsolutePath()));
         }
+        if (generateJavaClasses) {
+            // validate generated Java classes folder
+            if (!generatedJavaClassesDirectory.isAbsolute()) {
+                generatedJavaClassesDirectory = new File(project.getBasedir(), generatedJavaClassesDirectory.getPath());
+            }
+            if (generatedJavaClassesDirectory.exists() && !generatedJavaClassesDirectory.isDirectory()) {
+                throw new MojoExecutionException(String.format("Configured generatedJavaClassesDirectory={%s} is not a directory.",
+                        generatedJavaClassesDirectory.getAbsolutePath()));
+            }
+            if (!generatedJavaClassesDirectory.exists()) {
+                if (!generatedJavaClassesDirectory.mkdirs()) {
+                    throw new MojoExecutionException(String.format("Unable to generate generatedJavaClassesDirectory={%s}.",
+                            generatedJavaClassesDirectory.getAbsolutePath()));
+                }
+            }
+            project.addCompileSourceRoot(generatedJavaClassesDirectory.getPath());
+        }
 
-        if ( !buildContext.hasDelta(sourceDirectory )) {
+        if (!buildContext.hasDelta(sourceDirectory)) {
             getLog().info("No files found to validate, skipping.");
             return;
         }
@@ -132,26 +169,24 @@ public class ValidateMojo extends AbstractMojo {
         // don't fail execution in Eclipse as it generates an error marker in the POM file, which is not desired
         boolean mayFailExecution = !buildContext.getClass().getName().startsWith("org.eclipse.m2e");
 
-        sourceDirectoryLength = sourceDirectory.getAbsolutePath().length();
-        processedIncludes = processIncludes();
-        processedExcludes = processExcludes();
         try {
-            SightlyCompiler compiler = new SightlyCompiler();
-
             Scanner scanner = buildContext.newScanner(sourceDirectory);
-            scanner.setExcludes(new String[] { processedExcludes } );
-            scanner.setIncludes(new String[] { processedIncludes } );
+            scanner.setExcludes(excludes);
+            scanner.setIncludes(includes);
             scanner.scan();
 
             String[] includedFiles = scanner.getIncludedFiles();
 
             processedFiles = new ArrayList<>(includedFiles.length);
-            for ( String includedFile : includedFiles ) {
+            for (String includedFile : includedFiles) {
                 processedFiles.add(new File(sourceDirectory, includedFile));
             }
-            Map<File, CompilationResult> compilationResults = new HashMap<>();
-            for (File script : processedFiles) {
-                compilationResults.put(script, compiler.compile(getCompilationUnit(script)));
+            Map<File, CompilationResult> compilationResults;
+            if (generateJavaClasses) {
+                compilationResults = transpileHTLScriptsToJavaClasses(processedFiles, new SightlyCompiler(), new HTLJavaImportsAnalyzer
+                        (ignoreImports));
+            } else {
+                compilationResults = compileHTLScripts(processedFiles, new SightlyCompiler());
             }
             for (Map.Entry<File, CompilationResult> entry : compilationResults.entrySet()) {
                 File script = entry.getKey();
@@ -160,20 +195,23 @@ public class ValidateMojo extends AbstractMojo {
 
                 if (result.getWarnings().size() > 0) {
                     for (CompilerMessage message : result.getWarnings()) {
-                        buildContext.addMessage(script, message.getLine(), message.getColumn(), message.getMessage(), BuildContext.SEVERITY_WARNING, null);
+                        buildContext.addMessage(script, message.getLine(), message.getColumn(), message.getMessage(),
+                                BuildContext.SEVERITY_WARNING, null);
                     }
                     hasWarnings = true;
                 }
                 if (result.getErrors().size() > 0) {
                     for (CompilerMessage message : result.getErrors()) {
                         String messageString = message.getMessage().replaceAll(System.lineSeparator(), "");
-                        buildContext.addMessage(script, message.getLine(), message.getColumn(), messageString, BuildContext.SEVERITY_ERROR, null);
+                        buildContext
+                                .addMessage(script, message.getLine(), message.getColumn(), messageString, BuildContext.SEVERITY_ERROR,
+                                        null);
                     }
                     hasErrors = true;
                 }
             }
 
-            getLog().info("Processed " + processedFiles.size() + " files in " + ( System.currentTimeMillis() - start ) + " milliseconds");
+            getLog().info("Processed " + processedFiles.size() + " files in " + (System.currentTimeMillis() - start) + "ms");
 
             if (mayFailExecution && hasWarnings && failOnWarnings) {
                 throw new MojoFailureException("Compilation warnings were configured to fail the build.");
@@ -183,81 +221,54 @@ public class ValidateMojo extends AbstractMojo {
             }
         } catch (IOException e) {
             throw new MojoExecutionException(String.format("Cannot filter files from {%s} with includes {%s} and excludes {%s}.",
-                    sourceDirectory.getAbsolutePath(), processedIncludes, processedExcludes), e);
+                    sourceDirectory.getAbsolutePath(), includes, excludes), e);
         }
 
     }
 
-    public File getSourceDirectory() {
-        return sourceDirectory;
+    private Map<File, CompilationResult> transpileHTLScriptsToJavaClasses(List<File> scripts, SightlyCompiler compiler, JavaImportsAnalyzer
+            javaImportsAnalyzer) throws IOException {
+        Map<File, CompilationResult> compilationResult = new LinkedHashMap<>(scripts.size());
+        for (File script : scripts) {
+            JavaClassBackendCompiler backendCompiler = new JavaClassBackendCompiler(javaImportsAnalyzer);
+            ScriptCompilationUnit compilationUnit = new ScriptCompilationUnit(sourceDirectory, script);
+            compilationResult.put(script, compiler.compile(compilationUnit, backendCompiler));
+            ClassInfo classInfo = new HTLClassInfo(script.getPath().replaceFirst(sourceDirectory.getAbsolutePath(), ""));
+            String javaSourceCode = backendCompiler.build(classInfo);
+            File generatedClassFile = new File(generatedJavaClassesDirectory, classInfo.getFullyQualifiedClassName()
+                    .replaceAll("\\.", File.separator) + ".java");
+            generatedClassFile.getParentFile().mkdirs();
+            PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(generatedClassFile), _16K));
+            out.write(javaSourceCode);
+            compilationUnit.dispose();
+            out.close();
+        }
+        return compilationResult;
     }
 
-    public boolean shouldFailOnWarnings() {
-        return failOnWarnings;
+    private Map<File, CompilationResult> compileHTLScripts(List<File> scripts, SightlyCompiler compiler) throws IOException {
+        Map<File, CompilationResult> compilationResult = new LinkedHashMap<>(scripts.size());
+        for (File script : scripts) {
+            ScriptCompilationUnit scriptCompilationUnit = new ScriptCompilationUnit(sourceDirectory, script);
+            compilationResult.put(script, compiler.compile(scriptCompilationUnit));
+            scriptCompilationUnit.dispose();
+        }
+        return compilationResult;
+    }
+    // visible for testing only
+    void setBuildContext(BuildContext buildContext) {
+        this.buildContext = buildContext;
     }
 
-    public boolean hasWarnings() {
+    boolean hasWarnings() {
         return hasWarnings;
     }
 
-    public boolean hasErrors() {
+    boolean hasErrors() {
         return hasErrors;
     }
 
-    public String getIncludes() {
-        return processedIncludes;
-    }
-
-    public String getExcludes() {
-        return processedExcludes;
-    }
-
-    public List<File> getProcessedFiles() {
+    List<File> getProcessedFiles() {
         return processedFiles;
     }
-
-    private String processIncludes() {
-        // since default = "" leads to null deal with that as well here
-        if (includes == null) {
-            return "";
-        }
-        return join(includes, ',');
-    }
-
-    private String processExcludes() {
-        if (excludes == null) {
-            return DEFAULT_EXCLUDES;
-        }
-        return join(excludes, ',');
-    }
-
-    private String join(String[] array, char joinChar) {
-        StringBuilder stringBuilder = new StringBuilder();
-        for (int index = 0; index < array.length; index++) {
-            stringBuilder.append(StringUtils.trim(array[index]));
-            if (index < array.length - 1) {
-                stringBuilder.append(joinChar);
-            }
-        }
-        return stringBuilder.toString();
-    }
-
-    private CompilationUnit getCompilationUnit(final File file) throws FileNotFoundException {
-        final Reader reader = new FileReader(file);
-        return new CompilationUnit() {
-            public String getScriptName() {
-                return file.getAbsolutePath().substring(sourceDirectoryLength);
-            }
-
-            public Reader getScriptReader() {
-                return reader;
-            }
-        };
-    }
-
-    // visible for testing only
-    void setBuildContext(BuildContext buildContext) {
-
-        this.buildContext = buildContext;
-    }
 }
diff --git a/src/main/java/org/apache/sling/maven/htl/compiler/HTLClassInfo.java b/src/main/java/org/apache/sling/maven/htl/compiler/HTLClassInfo.java
new file mode 100644
index 0000000..be213f9
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/htl/compiler/HTLClassInfo.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * 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.htl.compiler;
+
+import org.apache.sling.scripting.sightly.java.compiler.ClassInfo;
+import org.apache.sling.scripting.sightly.java.compiler.JavaEscapeUtils;
+
+public class HTLClassInfo implements ClassInfo {
+
+    private String fqcn;
+    private String simpleClassName;
+    private String packageName;
+
+    public HTLClassInfo(String script) {
+        fqcn = JavaEscapeUtils.makeJavaPackage(script);
+    }
+
+    @Override
+    public String getSimpleClassName() {
+        if (simpleClassName == null) {
+            simpleClassName = fqcn.substring(fqcn.lastIndexOf(".") + 1);
+        }
+        return simpleClassName;
+    }
+
+    @Override
+    public String getPackageName() {
+        if (packageName == null) {
+            packageName = fqcn.substring(0, fqcn.lastIndexOf("."));
+        }
+        return packageName;
+    }
+
+    @Override
+    public String getFullyQualifiedClassName() {
+        return fqcn;
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/htl/compiler/HTLJavaImportsAnalyzer.java b/src/main/java/org/apache/sling/maven/htl/compiler/HTLJavaImportsAnalyzer.java
new file mode 100644
index 0000000..93d72a6
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/htl/compiler/HTLJavaImportsAnalyzer.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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.htl.compiler;
+
+import java.util.Set;
+
+import org.apache.sling.scripting.sightly.java.compiler.JavaImportsAnalyzer;
+
+public class HTLJavaImportsAnalyzer implements JavaImportsAnalyzer {
+
+    private Set<String> ignoreImports;
+
+    public HTLJavaImportsAnalyzer(Set<String> ignoreImports) {
+        this.ignoreImports = ignoreImports;
+    }
+
+    @Override
+    public boolean allowImport(String importedClass) {
+        for (String ignoredImport : ignoreImports) {
+            if (importedClass.startsWith(ignoredImport + ".")) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/htl/compiler/ScriptCompilationUnit.java b/src/main/java/org/apache/sling/maven/htl/compiler/ScriptCompilationUnit.java
new file mode 100644
index 0000000..e482689
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/htl/compiler/ScriptCompilationUnit.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * 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.htl.compiler;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+
+import org.apache.sling.scripting.sightly.compiler.CompilationUnit;
+
+public class ScriptCompilationUnit implements CompilationUnit {
+
+    private Reader reader;
+    private File sourceDirectory;
+    private File script;
+    private String scriptName;
+    private static final int _16K = 16384;
+
+    public ScriptCompilationUnit(File sourceDirectory, File script) throws FileNotFoundException {
+        reader = new BufferedReader(new FileReader(script), _16K);
+        this.sourceDirectory = sourceDirectory;
+        this.script = script;
+    }
+
+    @Override
+    public String getScriptName() {
+        if (scriptName == null) {
+            scriptName = script.getAbsolutePath().substring(sourceDirectory.getAbsolutePath().length());
+        }
+        return scriptName;
+    }
+
+    @Override
+    public Reader getScriptReader() {
+        return reader;
+    }
+
+    public void dispose() {
+        try {
+            if (reader != null) {
+                reader.close();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/sling/maven/htl/ValidateMojoTest.java b/src/test/java/org/apache/sling/maven/htl/ValidateMojoTest.java
index 24833e4..1b98f1a 100644
--- a/src/test/java/org/apache/sling/maven/htl/ValidateMojoTest.java
+++ b/src/test/java/org/apache/sling/maven/htl/ValidateMojoTest.java
@@ -17,8 +17,11 @@
 package org.apache.sling.maven.htl;
 
 import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
 import java.util.List;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.maven.execution.DefaultMavenExecutionRequest;
 import org.apache.maven.execution.MavenExecutionRequest;
 import org.apache.maven.execution.MavenSession;
@@ -30,6 +33,7 @@ import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.ProjectBuilder;
 import org.apache.maven.project.ProjectBuildingRequest;
 import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -50,6 +54,7 @@ public class ValidateMojoTest {
     private static final String EXPLICIT_EXCLUDES_POM = "explicit-excludes.pom.xml";
     private static final String FAIL_ON_WARNINGS_POM = "fail-on-warnings.pom.xml";
     private static final String DEFAULT_INCLUDES_POM = "default-includes.pom.xml";
+    private static final String GENERATE_JAVA_CLASSES_POM = "generate-java-classes.pom.xml";
 
     @Rule
     public MojoRule mojoRule = new MojoRule() {
@@ -64,6 +69,12 @@ public class ValidateMojoTest {
         }
     };
 
+    @After
+    public void tearDown() throws IOException {
+        File baseDir = new File(System.getProperty("basedir"));
+        FileUtils.deleteQuietly(new File(baseDir, "target"));
+    }
+
     @Test
     public void testExplicitIncludes() throws Exception {
         File baseDir = new File(System.getProperty("basedir"));
@@ -127,6 +138,22 @@ public class ValidateMojoTest {
         assertEquals("Did not expect compilation warnings.", false, validateMojo.hasWarnings());
     }
 
+    @Test
+    public void testGenerateJavaClasses() throws Exception {
+        File baseDir = new File(System.getProperty("basedir"));
+        ValidateMojo validateMojo = getMojo(baseDir, GENERATE_JAVA_CLASSES_POM);
+        validateMojo.execute();
+        List<File> processedFiles = validateMojo.getProcessedFiles();
+        assertEquals("Expected 1 files to process.", 1, processedFiles.size());
+        assertTrue("Expected script.html to be one of the processed files.", processedFiles.contains(new File(baseDir,
+                SCRIPT_HTML)));
+        String generatedSourceCode = FileUtils.readFileToString(new File(baseDir,
+                "target/generated-sources/htl/apps/projects/script_html.java"), Charset
+                .forName("UTF-8"));
+        assertTrue(generatedSourceCode.contains("import org.apache.sling.settings.SlingSettingsService;"));
+        assertTrue(generatedSourceCode.contains("import apps.projects.Pojo;"));
+    }
+
     private ValidateMojo getMojo(File baseDir, String pomFile) throws Exception {
         SilentLog log = new SilentLog();
         DefaultBuildContext buildContext = new DefaultBuildContext();
@@ -144,7 +171,7 @@ public class ValidateMojoTest {
     }
 
     /**
-     * Copied from {@code org.apache.maven.plugin.testing.readMavenProject(...)} but customized to allow custom pom names
+     * Copied from {@link org.apache.maven.plugin.testing.MojoRule#readMavenProject(java.io.File)} but customized to allow custom pom names
      */
     private MavenProject readMavenProject(File basedir, String pomFileName) throws Exception {
         File pom = new File(basedir, pomFileName);
diff --git a/src/test/resources/test-project/default-includes.pom.xml b/src/test/resources/test-project/default-includes.pom.xml
index c06629d..a4a20d7 100644
--- a/src/test/resources/test-project/default-includes.pom.xml
+++ b/src/test/resources/test-project/default-includes.pom.xml
@@ -32,7 +32,6 @@
                 <artifactId>htl-maven-plugin</artifactId>
                 <configuration>
                     <sourceDirectory>src/main/resources</sourceDirectory>
-                    <!-- only exclude.html will be compiled -->
                 </configuration>
                 <executions>
                     <execution>
diff --git a/src/test/resources/test-project/explicit-excludes.pom.xml b/src/test/resources/test-project/explicit-excludes.pom.xml
index 4fb5fe7..f01320f 100644
--- a/src/test/resources/test-project/explicit-excludes.pom.xml
+++ b/src/test/resources/test-project/explicit-excludes.pom.xml
@@ -35,6 +35,7 @@
                     <!-- only script.html will be compiled -->
                     <excludes>
                         <exclude>**/exclude.html</exclude>
+                        <exclude>**/javaclasses.html</exclude>
                     </excludes>
                 </configuration>
                 <executions>
diff --git a/src/test/resources/test-project/generate-java-classes.pom.xml b/src/test/resources/test-project/generate-java-classes.pom.xml
new file mode 100644
index 0000000..b9d95c3
--- /dev/null
+++ b/src/test/resources/test-project/generate-java-classes.pom.xml
@@ -0,0 +1,56 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.sling</groupId>
+    <artifactId>htl-maven-plugin-it-generate-java-classes</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <name>HTL Maven Plugin IT - Generate Java Classes</name>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.sling</groupId>
+                <artifactId>htl-maven-plugin</artifactId>
+                <configuration>
+                    <sourceDirectory>src/main/resources</sourceDirectory>
+                    <!-- only the script.html file will be compiled -->
+                    <includes>
+                        <include>**/script.html</include>
+                    </includes>
+                    <failOnWarnings>true</failOnWarnings>
+                    <generateJavaClasses>true</generateJavaClasses>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>validate-scripts</id>
+                        <goals>
+                            <goal>validate</goal>
+                        </goals>
+                        <phase>generate-sources</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/test/resources/test-project/src/main/resources/apps/projects/Pojo.java b/src/test/resources/test-project/src/main/resources/apps/projects/Pojo.java
new file mode 100644
index 0000000..588cfa2
--- /dev/null
+++ b/src/test/resources/test-project/src/main/resources/apps/projects/Pojo.java
@@ -0,0 +1,3 @@
+package apps.projects;
+
+public class Pojo {}
diff --git a/src/test/resources/test-project/src/main/resources/apps/projects/script.html b/src/test/resources/test-project/src/main/resources/apps/projects/script.html
index 5df9256..ae74f7d 100644
--- a/src/test/resources/test-project/src/main/resources/apps/projects/script.html
+++ b/src/test/resources/test-project/src/main/resources/apps/projects/script.html
@@ -14,4 +14,5 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
-${this.is.an.expression @ i18n, locale='de'}
+<div data-sly-use.slingSettings="org.apache.sling.settings.SlingSettingsService">${slingSettings.slingId}</div>
+<div data-sly-use.pojo="apps.projects.Pojo">${pojo.toString}</div>

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