com.alexnederlof.jasperreport.JasperReporter Maven / Gradle / Ivy
package com.alexnederlof.jasperreport;
/*
* 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.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.design.JRCompiler;
import net.sf.jasperreports.engine.design.JRJdtCompiler;
import net.sf.jasperreports.engine.xml.JRReportSaxParserFactory;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
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.plugins.annotations.ResolutionScope;
import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
import org.codehaus.plexus.compiler.util.scan.mapping.SourceMapping;
import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
/**
* This plugin compiles jasper source files to the target folder. While doing
* so, it keeps the folder structure in tact.
*/
@Mojo(defaultPhase = LifecyclePhase.PROCESS_RESOURCES, name = "jasper", requiresDependencyResolution =
ResolutionScope.COMPILE)
public class JasperReporter extends AbstractMojo {
static final String ERROR_JRE_COMPILE_ERROR =
"Some Jasper reports could not be compiled. See log above for details.";
/**
* This is the java compiler used
*/
@Parameter(defaultValue = "net.sf.jasperreports.engine.design.JRJdtCompiler", required = true)
private String compiler;
/**
* This is where the .jasper files are written.
*/
@Parameter(defaultValue = "${project.build.outputDirectory}/jasper")
private File outputDirectory;
/**
* This is where the xml report design files should be.
*/
@Parameter(defaultValue = "src/main/jasperreports")
private File sourceDirectory;
/**
* The extension of the source files to look for. Finds files with a .jrxml
* extension by default.
*/
@Parameter(defaultValue = ".jrxml")
private String sourceFileExt;
/**
* The extension of the compiled report files. Creates files with a .jasper
* extension by default.
*
*/
@Parameter(defaultValue = ".jasper")
private String outputFileExt;
/**
* Check the source files before compiling. Default value is true.
*
*/
@Parameter(defaultValue = "tue")
private boolean xmlValidation;
/**
* If verbose is on the plug-in will report which reports it is compiling
* and which files are being skipped.
*
*/
@Parameter(defaultValue = "false")
private boolean verbose;
/**
* The number of threads the reporting will use. Default is 4 which is good
* for a lot of reports on a hard drive (in stead of SSD). If you only have
* a few, or if you have SSD, it might be faster to set it to 2.
*
*/
@Parameter(defaultValue = "4")
private int numberOfThreads;
@Parameter(property = "project.compileClasspathElements")
private List classpathElements;
/**
* Use this parameter to add additional properties to the Jasper compiler.
* For example.
*
*
* {@code
*
* ...
*
* true
*
* Courier
* UTF-8
* true
*
*
* }
*
*
*/
@Parameter
private Map additionalProperties;
/**
* If failOnMissingSourceDirectory is on the plug-in will fail the build if
* source directory does not exist. Default value is true.
*
*/
@Parameter(defaultValue = "true")
private boolean failOnMissingSourceDirectory = true;
/**
* This is the source inclusion scanner class used, a
* org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner
* implementation class.
*
*/
@Parameter(defaultValue = "org.codehaus.plexus.compiler.util.scan.StaleSourceScanner")
private String sourceScanner = StaleSourceScanner.class.getName();
/**
* Provides the option to add additional JARs to the Classpath for compiling. This is handy in case you have
* references to external Java-Beans in your JasperReports.
*
*
* {@code
*
* ...
* /web/lib/ServiceBeans.jar;/web/lib/WebForms.jar
* ...
*
* }
*
*
*/
@Parameter
private String additionalClasspath;
private Log log;
public JasperReporter() {
}
@Override
public void execute() throws MojoExecutionException {
log = getLog();
if (verbose) {
logConfiguration(log);
}
checkOutDirWritable(outputDirectory);
SourceMapping mapping = new SuffixMapping(sourceFileExt, outputFileExt);
Set sources = jrxmlFilesToCompile(mapping);
if (sources.isEmpty()) {
log.info("Nothing to compile - all Jasper reports are up to date");
}
else {
log.info("Compiling " + sources.size() + " Jasper reports design files.");
List tasks = generateTasks(sources, mapping);
if (tasks.isEmpty()) {
log.info("Nothing to compile");
return;
}
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getClassLoader(classLoader));
try {
configureJasper();
executeTasks(tasks);
}
finally {
if (classLoader != null) {
Thread.currentThread().setContextClassLoader(classLoader);
}
}
}
}
/**
* Determines source files to be compiled.
*
* @param mapping The source files
*
* @return set of jxml files to compile
*
* @throws MojoExecutionException When there's trouble with the input
*/
protected Set jrxmlFilesToCompile(SourceMapping mapping) throws MojoExecutionException {
if (!sourceDirectory.isDirectory()) {
String message = sourceDirectory.getName() + " is not a directory";
if (failOnMissingSourceDirectory) {
throw new IllegalArgumentException(message);
}
else {
log.warn(message + ", skip JasperReports reports compiling.");
return Collections.emptySet();
}
}
try {
SourceInclusionScanner scanner = createSourceInclusionScanner();
scanner.addSourceMapping(mapping);
return scanner.getIncludedSources(sourceDirectory, outputDirectory);
}
catch (InclusionScanException e) {
throw new MojoExecutionException("Error scanning source root: \'" + sourceDirectory + "\'.", e);
}
}
private void logConfiguration(Log log) {
log.info("Generating Jasper reports");
log.info("Output dir: " + outputDirectory.getAbsolutePath());
log.info("Source dir: " + sourceDirectory.getAbsolutePath());
log.info("Output ext: " + outputFileExt);
log.info("Source ext: " + sourceFileExt);
log.info("Addition properties: " + additionalProperties);
log.info("XML Validation: " + xmlValidation);
log.info("JasperReports Compiler: " + compiler);
log.info("Number of threads: " + numberOfThreads);
log.info("classpathElements: " + classpathElements);
log.info("additionalClasspath: " + additionalClasspath);
log.info("Source Scanner: " + sourceScanner);
}
/**
* Check if the output directory exist and is writable. If not, try to
* create an output dir and see if that is writable.
*
* @param outputDirectory The dir where the result will be placed
*
* @throws MojoExecutionException When the output directory is not writable
*/
private void checkOutDirWritable(File outputDirectory) throws MojoExecutionException {
if (!outputDirectory.exists()) {
checkIfOutputCanBeCreated();
checkIfOutputDirIsWritable();
if (verbose) {
log.info("Output dir check OK");
}
}
else if (!outputDirectory.canWrite()) {
throw new MojoExecutionException(
"The output dir exists but was not writable. "
+ "Try running maven with the 'clean' goal.");
}
}
private void configureJasper() {
DefaultJasperReportsContext jrContext = DefaultJasperReportsContext.getInstance();
jrContext.setProperty(JRReportSaxParserFactory.COMPILER_XML_VALIDATION, String.valueOf(xmlValidation));
jrContext.setProperty(JRCompiler.COMPILER_PREFIX, compiler == null ? JRJdtCompiler.class.getName() : compiler);
jrContext.setProperty(JRCompiler.COMPILER_KEEP_JAVA_FILE, Boolean.FALSE.toString());
if (additionalProperties != null) {
configureAdditionalProperties(JRPropertiesUtil.getInstance(jrContext));
}
}
private ClassLoader getClassLoader(ClassLoader classLoader)
throws MojoExecutionException {
List classpath = new ArrayList();
if (classpathElements != null) {
for (String element : classpathElements) {
try {
File f = new File(element);
classpath.add(f.toURI().toURL());
log.debug("Added to classpath " + element);
}
catch (Exception e) {
throw new MojoExecutionException(
"Error setting classpath " + element + " " + e.getMessage());
}
}
}
if (additionalClasspath != null) {
for (String element : additionalClasspath.split("[;]")) {
try {
File f = new File(element);
classpath.add(f.toURI().toURL());
log.debug("Added additionalClasspath to classpath " + element);
}
catch (Exception e) {
throw new MojoExecutionException("Error setting classpath " + element + " " + e.getMessage());
}
}
}
URL[] urls = classpath.toArray(new URL[classpath.size()]);
return new URLClassLoader(urls, classLoader);
}
private void configureAdditionalProperties(JRPropertiesUtil propertiesUtil) {
for (Map.Entry additionalProperty : additionalProperties.entrySet()) {
propertiesUtil.setProperty(additionalProperty.getKey(), additionalProperty.getValue());
}
}
private void checkIfOutputCanBeCreated() throws MojoExecutionException {
if (!outputDirectory.mkdirs()) {
throw new MojoExecutionException(this, "Output folder could not be created", "Outputfolder "
+ outputDirectory.getAbsolutePath() + " is not a folder");
}
}
private void checkIfOutputDirIsWritable() throws MojoExecutionException {
if (!outputDirectory.canWrite()) {
throw new MojoExecutionException(this, "Could not write to output folder",
"Could not write to output folder: " + outputDirectory.getAbsolutePath());
}
}
private String getRelativePath(String root, File file) throws MojoExecutionException {
try {
return file.getCanonicalPath().substring(root.length() + 1);
}
catch (IOException e) {
throw new MojoExecutionException("Could not getCanonicalPath from file " + file, e);
}
}
private List generateTasks(Set sources, SourceMapping mapping) throws MojoExecutionException {
List tasks = new LinkedList();
try {
String root = sourceDirectory.getCanonicalPath();
for (File src : sources) {
String srcName = getRelativePath(root, src);
try {
File destination = mapping.getTargetFiles(outputDirectory, srcName).iterator().next();
createDestination(destination.getParentFile());
tasks.add(new CompileTask(src, destination, log, verbose));
}
catch (InclusionScanException e) {
throw new MojoExecutionException("Error compiling report design : " + src, e);
}
}
}
catch (IOException e) {
throw new MojoExecutionException("Could not getCanonicalPath from source directory " + sourceDirectory, e);
}
return tasks;
}
private void createDestination(File destinationDirectory) throws MojoExecutionException {
if (!destinationDirectory.exists()) {
if (destinationDirectory.mkdirs()) {
log.debug("Created directory " + destinationDirectory.getName());
}
else {
throw new MojoExecutionException("Could not create directory " + destinationDirectory.getName());
}
}
}
private void executeTasks(List tasks) throws MojoExecutionException {
try {
long t1 = System.currentTimeMillis();
List> output =
Executors.newFixedThreadPool(numberOfThreads).invokeAll(tasks);
long time = (System.currentTimeMillis() - t1);
log.info("Generated " + output.size() + " jasper reports in " + (time / 1000.0) + " seconds");
checkForExceptions(output);
}
catch (InterruptedException e) {
log.error("Failed to compile Japser reports: Interrupted!", e);
throw new MojoExecutionException("Error while compiling Jasper reports", e);
}
catch (ExecutionException e) {
if (e.getCause() instanceof JRException) {
throw new MojoExecutionException(ERROR_JRE_COMPILE_ERROR, e);
}
else {
throw new MojoExecutionException("Error while compiling Jasper reports", e);
}
}
}
private void checkForExceptions(List> output) throws InterruptedException, ExecutionException {
for (Future future : output) {
future.get();
}
}
private SourceInclusionScanner createSourceInclusionScanner() throws MojoExecutionException {
if (sourceScanner.equals(StaleSourceScanner.class.getName())) {
return new StaleSourceScanner();
}
else if (sourceScanner.equals(SimpleSourceInclusionScanner.class.getName())) {
return new SimpleSourceInclusionScanner(Collections.singleton("**/*" + sourceFileExt),
Collections.emptySet());
}
else {
throw new MojoExecutionException("sourceScanner not supported: \'" + sourceScanner + "\'.");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy