All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alexnederlof.jasperreport.JasperReporter Maven / Gradle / Ivy

There is a newer version: 2.8
Show newest version
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