Java tutorial
/* * #%L * Mojo's Maven plugin for Cobertura * %% * Copyright (C) 2005 - 2013 Codehaus * %% * Licensed 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. * #L% */ package org.codehaus.mojo.cobertura; import net.sourceforge.cobertura.coveragedata.CoverageDataFileHandler; import net.sourceforge.cobertura.coveragedata.ProjectData; import org.apache.maven.artifact.Artifact; import org.apache.maven.doxia.siterenderer.Renderer; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.apache.maven.reporting.AbstractMavenReport; import org.apache.maven.reporting.MavenReportException; import org.codehaus.mojo.cobertura.configuration.MaxHeapSizeUtil; import org.codehaus.mojo.cobertura.tasks.CommandLineArguments; import org.codehaus.mojo.cobertura.tasks.ReportTask; import java.io.File; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; /** * Instrument the compiled classes, run the unit tests and generate a Cobertura * report. * * @author <a href="will.gwaltney@sas.com">Will Gwaltney</a> * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> * @goal cobertura * @execute phase="test" lifecycle="cobertura" */ public class CoberturaReportMojo extends AbstractMavenReport { /** * The format of the report. Supports 'html' or 'xml'. Defaults to 'html'. * * @parameter expression="${cobertura.report.format}" * @deprecated */ private String format; /** * The formats of the report. Can be 'html' and/or 'xml'. Defaults to 'html'. * * @parameter */ private String[] formats = new String[] { "html" }; /** * The encoding for the java source code files. * * @parameter expression="${project.build.sourceEncoding}" default-value="UTF-8". * @since 2.4 */ private String encoding; /** * Maximum memory to pass to the JVM for Cobertura processes. * * @parameter expression="${cobertura.maxmem}" */ private String maxmem = "64m"; /** * <p> * The Datafile Location. * </p> * * @parameter expression="${cobertura.datafile}" default-value="${project.build.directory}/cobertura/cobertura.ser" * @required * @readonly */ private File dataFile; /** * <i>Maven Internal</i>: List of artifacts for the plugin. * * @parameter default-value="${plugin.artifacts}" * @required * @readonly */ private List<Artifact> pluginClasspathList; /** * The output directory for the report. * * @parameter default-value="${project.reporting.outputDirectory}/cobertura" * @required */ private File outputDirectory; /** * Only output Cobertura errors, avoid info messages. * * @parameter expression="${quiet}" default-value="false" * @since 2.1 */ private boolean quiet; /** * Generate aggregate reports in multi-module projects. * * @parameter expression="${cobertura.aggregate}" default-value="false" * @since 2.5 */ private boolean aggregate; /** * Whether to remove GPL licensed files from the generated report. * This is required to distribute the report as part of a distribution, * which is licensed under the ASL, or a similar license, which is * incompatible with the GPL. * * @parameter default-value="false" expression="${cobertura.omitGplFiles}" * @since 2.5 */ private boolean omitGplFiles; /** * <i>Maven Internal</i>: The Doxia Site Renderer. * * @component */ private Renderer siteRenderer; /** * List of maven project of the current build * * @parameter expression="${reactorProjects}" * @required * @readonly */ private List<MavenProject> reactorProjects; /** * <i>Maven Internal</i>: Project to interact with. * * @parameter default-value="${project}" * @required * @readonly */ private MavenProject project; private Map<MavenProject, List<MavenProject>> projectChildren; private String relDataFileName; private String relAggregateOutputDir; /** * Constructs a <code>CoberturaReportMojo</code>. * Sets the max memory to the maven max memory if set, otherwise * the default <code>CoberturaReportMojo</code> value is used. */ public CoberturaReportMojo() { if (MaxHeapSizeUtil.getInstance().envHasMavenMaxMemSetting()) { maxmem = MaxHeapSizeUtil.getInstance().getMavenMaxMemSetting(); } } /** * @param locale for the message bundle * @return localized cobertura name * @see org.apache.maven.reporting.MavenReport#getName(java.util.Locale) */ public String getName(Locale locale) { return getBundle(locale).getString("report.cobertura.name"); } /** * @param locale for the message bundle * @return localized description * @see org.apache.maven.reporting.MavenReport#getDescription(java.util.Locale) */ public String getDescription(Locale locale) { return getBundle(locale).getString("report.cobertura.description"); } @Override protected String getOutputDirectory() { return outputDirectory.getAbsolutePath(); } @Override protected MavenProject getProject() { return project; } @Override protected Renderer getSiteRenderer() { return siteRenderer; } /** * perform the actual reporting * * @param task * @param outputFormat * @throws MavenReportException */ private void executeReportTask(ReportTask task, String outputFormat) throws MavenReportException { task.setOutputFormat(outputFormat); // execute task try { task.execute(); } catch (MojoExecutionException e) { // throw new MavenReportException( "Error in Cobertura Report generation: " + e.getMessage(), e ); // better don't break the build if report is not generated, also due to the sporadic MCOBERTURA-56 getLog().error("Error in Cobertura Report generation: " + e.getMessage(), e); } } /** * {@inheritDoc} */ @Override protected void executeReport(Locale locale) throws MavenReportException { if (canGenerateSimpleReport()) { executeReport(getDataFile(), outputDirectory, getCompileSourceRoots()); } if (canGenerateAggregateReports()) { executeAggregateReport(locale); } } /** * Generates aggregate cobertura reports for all multi-module projects. */ private void executeAggregateReport(Locale locale) throws MavenReportException { for (MavenProject proj : reactorProjects) { if (!isMultiModule(proj)) { continue; } executeAggregateReport(locale, proj); } } /** * Generates an aggregate cobertura report for the given project. */ private void executeAggregateReport(Locale locale, MavenProject curProject) throws MavenReportException { List<MavenProject> children = getAllChildren(curProject); if (children.isEmpty()) { return; } List<File> serFiles = getOutputFiles(children); if (serFiles.isEmpty()) { getLog().info("Not executing aggregate cobertura:report for " + curProject.getName() + " as no child cobertura data files could not be found"); return; } getLog().info("Executing aggregate cobertura:report for " + curProject.getName()); ProjectData aggProjectData = new ProjectData(); for (File serFile : serFiles) { ProjectData data = CoverageDataFileHandler.loadCoverageData(serFile); aggProjectData.merge(data); } File aggSerFile = new File(curProject.getBasedir(), relDataFileName); aggSerFile.getAbsoluteFile().getParentFile().mkdirs(); getLog().info("Saving aggregate cobertura information in " + aggSerFile.getAbsolutePath()); CoverageDataFileHandler.saveCoverageData(aggProjectData, aggSerFile); // get all compile source roots List<String> aggCompileSourceRoots = new ArrayList<String>(); for (MavenProject child : children) { aggCompileSourceRoots.addAll(child.getCompileSourceRoots()); } File reportDir = new File(curProject.getBasedir(), relAggregateOutputDir); reportDir.mkdirs(); executeReport(aggSerFile, reportDir, aggCompileSourceRoots); } /** * Executes the cobertura report task for the given dataFile, outputDirectory, and compileSourceRoots. */ private void executeReport(File curDataFile, File curOutputDirectory, List<String> curCompileSourceRoots) throws MavenReportException { ReportTask task = new ReportTask(); // task defaults task.setLog(getLog()); task.setPluginClasspathList(pluginClasspathList); task.setQuiet(quiet); // task specifics task.setMaxmem(maxmem); task.setDataFile(curDataFile); task.setOutputDirectory(curOutputDirectory); task.setCompileSourceRoots(curCompileSourceRoots); task.setSourceEncoding(encoding); CommandLineArguments cmdLineArgs; cmdLineArgs = new CommandLineArguments(); cmdLineArgs.setUseCommandsFile(true); task.setCmdLineArgs(cmdLineArgs); if (format != null) { formats = new String[] { format }; } for (int i = 0; i < formats.length; i++) { executeReportTask(task, formats[i]); } removeGplFiles(); } /** * Removes files from the generated report, which are distributed under * the GPL. */ private void removeGplFiles() throws MavenReportException { if (omitGplFiles) { final String[] files = new String[] { "js/customsorttypes.js", "js/sortabletable.js", "js/stringbuilder.js" }; for (int i = 0; i < files.length; i++) { final File f = new File(outputDirectory, files[i]); if (f.exists()) { if (f.delete()) { getLog().debug("Removed GPL licensed file " + f.getPath()); } else { throw new MavenReportException("Unable to remove GPL licensed file " + f.getPath()); } } else { getLog().info("GPL licensed file " + f.getPath() + " not found."); } } } } /** * {@inheritDoc} */ public String getOutputName() { return "cobertura/index"; } @Override public boolean isExternalReport() { return true; } @Override public boolean canGenerateReport() { if (canGenerateSimpleReport()) { return true; } else { getLog().info("Not executing cobertura:report as the cobertura data file (" + getDataFile() + ") could not be found"); } if (canGenerateAggregateReports()) { return true; } if (aggregate && isMultiModule(project)) { // unfortunately, we don't know before hand whether we can generate an aggregate report for a // multi-module. if we return false here, then we won't get a link in the main reports list. so we'll // just be optimistic return true; } return false; } /** * Returns whether or not we can generate a simple (non-aggregate) report for this project. * * @return <code>true</code> if a simple report can be generated, otherwise <code>false</code> */ private boolean canGenerateSimpleReport() { /* * Don't have to check for source directories or java code or the like for report generation. Checks for source * directories or java project classpath existence should only occur in the Instrument Mojo. */ return getDataFile().exists() && getDataFile().isFile(); } /** * Returns whether or not we can generate any aggregate reports at this time. */ private boolean canGenerateAggregateReports() { // we only generate aggregate reports after the last project runs if (aggregate && isLastProject(project, reactorProjects)) { buildAggregateInfo(); if (!getOutputFiles(reactorProjects).isEmpty()) { return true; } } return false; } /** * Returns the compileSourceRoots for the currently executing project. */ @SuppressWarnings("unchecked") private List<String> getCompileSourceRoots() { return project.getExecutionProject().getCompileSourceRoots(); } @Override public void setReportOutputDirectory(File reportOutputDirectory) { if ((reportOutputDirectory != null) && (!reportOutputDirectory.getAbsolutePath().endsWith("cobertura"))) { this.outputDirectory = new File(reportOutputDirectory, "cobertura"); } else { this.outputDirectory = reportOutputDirectory; } } /** * Gets the resource bundle for the report text. * * @param locale The locale for the report, must not be <code>null</code>. * @return The resource bundle for the requested locale. */ private ResourceBundle getBundle(Locale locale) { return ResourceBundle.getBundle("cobertura-report", locale); } /** * Check whether the element is the last element of the list * * @param project element to check * @param mavenProjectList list of maven project * @return true if project is the last element of mavenProjectList list */ private boolean isLastProject(MavenProject project, List<MavenProject> mavenProjectList) { return project.equals(mavenProjectList.get(mavenProjectList.size() - 1)); } /** * Test if the project has pom packaging * * @param mavenProject Project to test * @return True if it has a pom packaging */ private boolean isMultiModule(MavenProject mavenProject) { return "pom".equals(mavenProject.getPackaging()); } /** * Generates various information needed for building aggregate reports. */ private void buildAggregateInfo() { if (projectChildren != null) { // already did this work return; } // build parent-child map projectChildren = new HashMap<MavenProject, List<MavenProject>>(); for (MavenProject proj : reactorProjects) { List<MavenProject> depList = projectChildren.get(proj.getParent()); if (depList == null) { depList = new ArrayList<MavenProject>(); projectChildren.put(proj.getParent(), depList); } depList.add(proj); } // attempt to determine where data files and output dir are relDataFileName = relativize(project.getBasedir(), getDataFile()); if (relDataFileName == null) { getLog().warn("Could not determine relative data file name, defaulting to 'cobertura/cobertura.ser'"); relDataFileName = "cobertura/cobertura.ser"; } relAggregateOutputDir = relativize(project.getBasedir(), outputDirectory); if (relAggregateOutputDir == null) { getLog().warn("Could not determine relative output dir name, defaulting to 'cobertura'"); relAggregateOutputDir = "cobertura"; } } /** * Returns a list containing all the recursive, non-pom children of the given project, never <code>null</code>. */ private List<MavenProject> getAllChildren(MavenProject parentProject) { List<MavenProject> children = projectChildren.get(parentProject); if (children == null) { return Collections.emptyList(); } List<MavenProject> result = new ArrayList<MavenProject>(); for (MavenProject child : children) { if (isMultiModule(child)) { result.addAll(getAllChildren(child)); } else { result.add(child); } } return result; } /** * Returns any existing cobertura data files from the given list of projects. */ private List<File> getOutputFiles(List<MavenProject> projects) { List<File> files = new ArrayList<File>(); for (MavenProject proj : projects) { if (isMultiModule(proj)) { continue; } File outputFile = new File(proj.getBasedir(), relDataFileName); if (outputFile.exists()) { files.add(outputFile); } } return files; } /** * Attempts to make the given childFile relative to the given parentFile. */ private String relativize(File parentFile, File childFile) { try { URI parentURI = parentFile.getCanonicalFile().toURI().normalize(); URI childURI = childFile.getCanonicalFile().toURI().normalize(); URI relativeURI = parentURI.relativize(childURI); if (relativeURI.isAbsolute()) { // child is not relative to parent return null; } String relativePath = relativeURI.getPath(); if (File.separatorChar != '/') { relativePath = relativePath.replace('/', File.separatorChar); } return relativePath; } catch (Exception e) { getLog().warn("Failed relativizing " + childFile + " to " + parentFile, e); } return null; } /** * Get the data file which is or will be generated by Cobertura, never <code>null</code>. * * @return the data file */ private File getDataFile() { return dataFile; } }