org.codehaus.mojo.jspc.CompilationMojoSupport Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jspc-maven-plugin Show documentation
Show all versions of jspc-maven-plugin Show documentation
Support to pre-compile your JSPs for your web applications.
The newest version!
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo 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 the following location:
*
* 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.codehaus.mojo.jspc;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.XmlStreamReader;
import org.apache.commons.io.output.XmlStreamWriter;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang.time.StopWatch;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.filtering.MavenFileFilter;
import org.apache.maven.shared.filtering.MavenFileFilterRequest;
import org.apache.maven.shared.filtering.MavenFilteringException;
import org.apache.maven.shared.model.fileset.FileSet;
import org.codehaus.mojo.jspc.compiler.JspCompiler;
import org.codehaus.mojo.jspc.compiler.JspCompilerFactory;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.Scanner;
import org.codehaus.plexus.util.StringUtils;
import org.sonatype.plexus.build.incremental.BuildContext;
abstract class CompilationMojoSupport extends AbstractMojo {
private static final String DEFAULT_INJECT_STRING = "";
/**
* The working directory to create the generated java source files.
*/
@Parameter(defaultValue="${project.build.directory}/jsp-source", required=true)
File workingDirectory;
/**
* The sources of the webapp. If not specified all files under {@link #defaultSourcesDirectory} are used
*/
@Parameter
FileSet sources;
/**
* Used if {@link #sources} is not specified
*/
@Parameter(defaultValue="${project.basedir}/src/main/webapp")
File defaultSourcesDirectory;
/**
* The path and location to the web fragment file.
*/
@Parameter(defaultValue="${project.build.directory}/web-fragment.xml", required=true)
File webFragmentFile;
/**
* The path and location of the original web.xml file.
*/
@Parameter(defaultValue="${basedir}/src/main/webapp/WEB-INF/web.xml", required=true)
File inputWebXml;
/**
* The final path and file name of the web.xml.
*/
@Parameter(defaultValue="${project.build.directory}/jspweb.xml", required=true)
File outputWebXml;
/**
* Character encoding.
*/
@Parameter(property="encoding", defaultValue="${project.build.sourceEncoding}")
String javaEncoding = "UTF-8";
/**
* Provide source compatibility with specified release.
*/
@Parameter(defaultValue="${project.build.sourceVersion}")
String source;
/**
* Generate class files for specific VM version.
*/
@Parameter(defaultValue="${project.build.targetVersion}")
String target;
/**
* Sets if you want to compile the JSP classes.
*/
@Parameter(defaultValue="true")
boolean compile;
/**
* Set this to false if you don"t want to include the compiled JSPs
* in your web.xml nor add the generated sources to your project"s
* source roots.
*/
@Parameter(defaultValue="true")
boolean includeInProject;
/**
* The string to look for in the web.xml to replace with the web fragment
* contents
*
* If not defined, fragment will be appended before the </webapp> tag
* which is fine for servlet 2.4 and greater. If using this parameter its
* recommanded to use Strings such as
* <!-- [INSERT FRAGMENT HERE] -->
*
* Be aware the < and > are for your pom verbatim.
*/
@Parameter(defaultValue=DEFAULT_INJECT_STRING)
String injectString;
/**
* The package in which the jsp files will be contained.
*/
@Parameter(defaultValue="jsp")
String packageName;
/**
* Verbose level option for JspC.
*/
@Parameter(defaultValue="0")
int verbose;
/**
* Show Success option for JspC.
*/
@Parameter(defaultValue="true")
boolean showSuccess;
/**
* Set Smap Dumped option for JspC.
*/
@Parameter(defaultValue="false")
boolean smapDumped;
/**
* Set Smap Suppressed option for JspC.
*/
@Parameter(defaultValue="false")
boolean smapSuppressed;
/**
* List Errors option for JspC.
*/
@Parameter(defaultValue="true")
boolean listErrors;
/**
* Validate XML option for JspC.
*/
@Parameter(defaultValue="false")
boolean validateXml;
/**
* Removes the spaces from the generated JSP files.
*/
@Parameter(defaultValue="true")
boolean trimSpaces;
/**
* Provides filtering of the generated web.xml text.
*/
@Parameter(defaultValue="true")
boolean filtering;
/**
* Set to {@code true} to disable the plugin.
*/
@Parameter(property="jspc.skip", defaultValue="false")
boolean skip;
/**
* Issue an error when the value of the class attribute in a useBean action is
* not a valid bean class
*/
@Parameter(defaultValue="true")
boolean errorOnUseBeanInvalidClassAttribute;
/**
* Set Caching option for JspC.
*/
@Parameter(defaultValue="true")
boolean caching;
/**
* Determines whether text strings are to be generated as char arrays,
* which improves performance in some cases.
*/
@Parameter(defaultValue="false")
boolean genStringAsCharArray;
/**
* Set Pooling Enabled option for JspC.
*/
@Parameter(defaultValue="true")
boolean poolingEnabled;
/**
* Set Class Debug Enabled option for JspC.
*/
@Parameter(defaultValue="true")
boolean classDebugInfo;
/**
* Number of threads to compile with
*/
@Parameter(defaultValue="1")
int compileThreads;
/**
* maximum amount of time compilation can take or be killed in minutes
*/
@Parameter(defaultValue="5")
int compilationTimeout;
//
// Components
//
/**
* The Maven project.
*/
@Component
protected MavenProject project;
@Component( role = MavenFileFilter.class, hint = "default" )
private MavenFileFilter mavenFileFilter;
@Component
private MavenSession session;
@Component
private BuildContext buildContext;
@Component
private JspCompilerFactory jspCompilerFactory;
// Sub-class must provide
protected abstract List getClasspathElements() throws MojoExecutionException;
public void execute() throws MojoExecutionException, MojoFailureException {
if (skip) {
return;
}
final Log log = this.getLog();
final boolean isWar = "war".equals(project.getPackaging());
if (!isWar) {
log.warn("Compiled JSPs will not be added to the project and web.xml will " +
"not be modified because the project's packaging is not 'war'.");
}
if (!includeInProject) {
log.warn("Compiled JSPs will not be added to the project and web.xml will " +
"not be modified because includeInProject is set to false.");
}
final JspCompiler jspCompiler = this.jspCompilerFactory.createJspCompiler();
// Setup defaults (complex, can"t init from expression)
if (sources == null) {
sources = new FileSet();
sources.setDirectory(this.defaultSourcesDirectory.getAbsolutePath());
sources.setExcludes(Arrays.asList("WEB-INF/web.xml", "META-INF/**"));
}
jspCompiler.setWebappDirectory(sources.getDirectory());
log.debug("Source directory: " + this.sources.getDirectory());
jspCompiler.setOutputDirectory(this.workingDirectory);
log.debug("Output directory: " + this.workingDirectory);
jspCompiler.setEncoding(this.javaEncoding);
log.debug("Encoding: " + this.javaEncoding);
jspCompiler.setShowSuccess(this.showSuccess);
jspCompiler.setListErrors(this.listErrors);
jspCompiler.setWebFragmentFile(webFragmentFile);
log.debug("Web Fragment: " + this.webFragmentFile);
jspCompiler.setPackageName(packageName);
log.debug("Package Name: " + this.packageName);
final List classpathElements = getClasspathElements();
jspCompiler.setClasspath(classpathElements);
log.debug("Classpath: " + classpathElements);
final List jspFiles;
if (sources.getIncludes() != null) {
//Always need to get a full list of JSP files as incremental builds would result in an invalid web.xml
final Scanner scanner = this.buildContext.newScanner(new File(sources.getDirectory()), true);
scanner.setIncludes(sources.getIncludesArray());
scanner.setExcludes(sources.getExcludesArray());
scanner.addDefaultExcludes();
scanner.scan();
final String[] includes = scanner.getIncludedFiles();
jspFiles = new ArrayList(includes.length);
for (final String it : includes) {
jspFiles.add(new File(sources.getDirectory(), it));
}
}
else {
jspFiles = Collections.emptyList();
}
jspCompiler.setSmapDumped(smapDumped);
jspCompiler.setSmapSuppressed(smapSuppressed);
jspCompiler.setCompile(compile);
jspCompiler.setValidateXml(validateXml);
jspCompiler.setTrimSpaces(trimSpaces);
jspCompiler.setVerbose(verbose);
jspCompiler.setErrorOnUseBeanInvalidClassAttribute(errorOnUseBeanInvalidClassAttribute);
jspCompiler.setCompilerSourceVM(source);
jspCompiler.setCompilerTargetVM(target);
jspCompiler.setCaching(caching);
jspCompiler.setGenStringAsCharArray(genStringAsCharArray);
jspCompiler.setPoolingEnabled(poolingEnabled);
jspCompiler.setClassDebugInfo(classDebugInfo);
jspCompiler.setCompileThreads(compileThreads);
jspCompiler.setCompileTimeout(TimeUnit.MINUTES.toMillis(compilationTimeout));
// Make directories if needed
workingDirectory.mkdirs();
webFragmentFile.getParentFile().mkdirs();
outputWebXml.getParentFile().mkdirs();
// JspC needs URLClassLoader, with tools.jar
final ClassLoader parent = Thread.currentThread().getContextClassLoader();
final JspcMojoClassLoader cl = new JspcMojoClassLoader(parent);
cl.addURL(findToolsJar());
Thread.currentThread().setContextClassLoader(cl);
try {
// Show a nice message when we know how many files are included
if (!jspFiles.isEmpty()) {
log.info("Compiling " + jspFiles.size() + " JSP source file" + (jspFiles.size() > 1 ? "s" : "") + " to " + workingDirectory);
}
else {
log.info("Compiling JSP source files to " + workingDirectory);
}
final StopWatch watch = new StopWatch();
watch.start();
jspCompiler.compile(jspFiles);
log.info("Compilation completed in " + watch);
}
catch (Exception e) {
throw new MojoFailureException("Failed to compile JSPS", e);
}
finally {
// Set back the old classloader
Thread.currentThread().setContextClassLoader(parent);
}
//Notify the build context that the jspFiles have been modified by the jsp compiler
for (final File jspFile : jspFiles) {
this.buildContext.refresh(jspFile);
}
// Maybe install the generated classes into the default output directory
if (compile && isWar) {
final Scanner scanner = buildContext.newScanner(this.workingDirectory);
scanner.addDefaultExcludes();
scanner.setIncludes(new String[] { "**/*.class" });
scanner.scan();
for (final String includedFile : scanner.getIncludedFiles()) {
final File s = new File(this.workingDirectory, includedFile);
final File d = new File(this.project.getBuild().getOutputDirectory(), includedFile);
d.getParentFile().mkdirs();
OutputStream fos = null;
try {
fos = this.buildContext.newFileOutputStream(d);
org.apache.commons.io.FileUtils.copyFile(s, fos);
}
catch (IOException e) {
throw new MojoFailureException("Failed to copy '" + s + "' to '" + d + "'", e);
}
finally {
IOUtils.closeQuietly(fos);
}
}
}
if (isWar && includeInProject) {
writeWebXml();
project.addCompileSourceRoot(workingDirectory.toString());
}
}
/**
* Figure out where the tools.jar file lives.
*/
private URL findToolsJar() throws MojoExecutionException {
final File javaHome = FileUtils.resolveFile(new File(File.pathSeparator), System.getProperty("java.home"));
final List toolsPaths = new ArrayList();
File file = null;
if (SystemUtils.IS_OS_MAC_OSX) {
file = FileUtils.resolveFile(javaHome, "../Classes/classes.jar");
toolsPaths.add(file);
}
if (file == null || !file.exists()) {
file = FileUtils.resolveFile(javaHome, "../lib/tools.jar");
toolsPaths.add(file);
}
if (!file.exists()) {
throw new MojoExecutionException("Could not find tools.jar at " + toolsPaths + " under java.home: " + javaHome);
}
getLog().debug("Using tools.jar: " + file);
final URI fileUri = file.toURI();
try {
return fileUri.toURL();
}
catch (MalformedURLException e) {
throw new MojoExecutionException("Could not generate URL from URI: " + fileUri, e);
}
}
private void writeWebXml() throws MojoExecutionException {
if (!inputWebXml.exists()) {
throw new MojoExecutionException("web.xml does not exist at: " + inputWebXml);
}
if (!webFragmentFile.exists()) {
throw new MojoExecutionException("web-fragment.xml does not exist at: " + webFragmentFile);
}
final String webXml = readXmlToString(inputWebXml);
if (webXml.indexOf(injectString) == -1) {
throw new MojoExecutionException("web.xml does not contain inject string '" + injectString + "' - " + webFragmentFile);
}
getLog().debug("Injecting " + webFragmentFile + " into " + inputWebXml + " and copying to " + outputWebXml);
final String fragmentXml = readXmlToString(webFragmentFile);
String output = StringUtils.replace(webXml, injectString, fragmentXml);
// If using the default, then tack on the end of the document
if (DEFAULT_INJECT_STRING.equals(injectString)) {
output += DEFAULT_INJECT_STRING;
}
// Write the jsp web.xml file
final File workingWebXml = new File(workingDirectory, "jspweb.xml");
XmlStreamWriter xmlStreamWriter = null;
try {
xmlStreamWriter = new XmlStreamWriter(workingWebXml, this.javaEncoding);
IOUtils.write(output, xmlStreamWriter);
}
catch (IOException e) {
throw new MojoExecutionException("Failed to write '" + outputWebXml + "' as XML file with default encoding: " + this.javaEncoding, e);
}
finally {
IOUtils.closeQuietly(xmlStreamWriter);
}
// Make sure parent dirs exist
outputWebXml.getParentFile().mkdirs();
// Copy the file into place filtering it on the way
final MavenFileFilterRequest request = new MavenFileFilterRequest();
request.setEncoding(this.javaEncoding);
request.setMavenSession(this.session);
request.setMavenProject(this.project);
request.setFiltering(this.filtering);
request.setFrom(workingWebXml);
request.setTo(outputWebXml);
try {
this.mavenFileFilter.copyFile(request);
}
catch (MavenFilteringException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
protected String readXmlToString(File f) throws MojoExecutionException {
Reader reader = null;
try {
reader = new XmlStreamReader(new BufferedInputStream(new FileInputStream(f)), true, this.javaEncoding);
return IOUtils.toString(reader);
}
catch (IOException e) {
throw new MojoExecutionException("Failed to read '" + f + "' as XML file with default encoding: " + this.javaEncoding, e);
}
finally {
IOUtils.closeQuietly(reader);
}
}
}