Home | History | Annotate | Download | only in maven
      1 /*******************************************************************************
      2  * Copyright (c) 2009, 2015 Mountainminds GmbH & Co. KG and Contributors
      3  * All rights reserved. This program and the accompanying materials
      4  * are made available under the terms of the Eclipse Public License v1.0
      5  * which accompanies this distribution, and is available at
      6  * http://www.eclipse.org/legal/epl-v10.html
      7  *
      8  * Contributors:
      9  *    Evgeny Mandrikov - initial API and implementation
     10  *
     11  *******************************************************************************/
     12 package org.jacoco.maven;
     13 
     14 import java.io.File;
     15 import java.util.Map;
     16 import java.util.Properties;
     17 
     18 import org.apache.maven.artifact.Artifact;
     19 import org.codehaus.plexus.util.StringUtils;
     20 import org.jacoco.core.runtime.AgentOptions;
     21 
     22 /**
     23  * Base class for preparing a property pointing to the JaCoCo runtime agent that
     24  * can be passed as a VM argument to the application under test.
     25  */
     26 public abstract class AbstractAgentMojo extends AbstractJacocoMojo {
     27 
     28 	/**
     29 	 * Name of the JaCoCo Agent artifact.
     30 	 */
     31 	static final String AGENT_ARTIFACT_NAME = "org.jacoco:org.jacoco.agent";
     32 	/**
     33 	 * Name of the property used in maven-osgi-test-plugin.
     34 	 */
     35 	static final String TYCHO_ARG_LINE = "tycho.testArgLine";
     36 	/**
     37 	 * Name of the property used in maven-surefire-plugin.
     38 	 */
     39 	static final String SUREFIRE_ARG_LINE = "argLine";
     40 	/**
     41 	 * Map of plugin artifacts.
     42 	 *
     43 	 * @parameter property="plugin.artifactMap"
     44 	 * @required
     45 	 * @readonly
     46 	 */
     47 	Map<String, Artifact> pluginArtifactMap;
     48 	/**
     49 	 * Allows to specify property which will contains settings for JaCoCo Agent.
     50 	 * If not specified, then "argLine" would be used for "jar" packaging and
     51 	 * "tycho.testArgLine" for "eclipse-test-plugin".
     52 	 *
     53 	 * @parameter property="jacoco.propertyName"
     54 	 */
     55 	String propertyName;
     56 	/**
     57 	 * If set to true and the execution data file already exists, coverage data
     58 	 * is appended to the existing file. If set to false, an existing execution
     59 	 * data file will be replaced.
     60 	 *
     61 	 * @parameter property="jacoco.append"
     62 	 */
     63 	Boolean append;
     64 	/**
     65 	 * A list of class loader names, that should be excluded from execution
     66 	 * analysis. The list entries are separated by a colon (:) and may use
     67 	 * wildcard characters (* and ?). This option might be required in case of
     68 	 * special frameworks that conflict with JaCoCo code instrumentation, in
     69 	 * particular class loaders that do not have access to the Java runtime
     70 	 * classes.
     71 	 *
     72 	 * @parameter property="jacoco.exclClassLoaders"
     73 	 */
     74 	String exclClassLoaders;
     75 	/**
     76 	 * Specifies whether also classes from the bootstrap classloader should be
     77 	 * instrumented. Use this feature with caution, it needs heavy
     78 	 * includes/excludes tuning.
     79 	 *
     80 	 * @parameter property="jacoco.inclBootstrapClasses"
     81 	 */
     82 	Boolean inclBootstrapClasses;
     83 	/**
     84 	 * A session identifier that is written with the execution data. Without
     85 	 * this parameter a random identifier is created by the agent.
     86 	 *
     87 	 * @parameter property="jacoco.sessionId"
     88 	 */
     89 	String sessionId;
     90 	/**
     91 	 * If set to true coverage data will be written on VM shutdown.
     92 	 *
     93 	 * @parameter property="jacoco.dumpOnExit"
     94 	 */
     95 	Boolean dumpOnExit;
     96 	/**
     97 	 * Output method to use for writing coverage data. Valid options are:
     98 	 * <ul>
     99 	 * <li>file: At VM termination execution data is written to a file.</li>
    100 	 * <li>tcpserver: The agent listens for incoming connections on the TCP port
    101 	 * specified by the {@link #address} and {@link #port}. Execution data is
    102 	 * written to this TCP connection.</li>
    103 	 * <li>tcpclient: At startup the agent connects to the TCP port specified by
    104 	 * the {@link #address} and {@link #port}. Execution data is written to this
    105 	 * TCP connection.</li>
    106 	 * <li>none: Do not produce any output.</li>
    107 	 * </ul>
    108 	 *
    109 	 * @parameter property="jacoco.output"
    110 	 */
    111 	String output;
    112 	/**
    113 	 * IP address or hostname to bind to when the output method is tcpserver or
    114 	 * connect to when the output method is tcpclient. In tcpserver mode the
    115 	 * value "*" causes the agent to accept connections on any local address.
    116 	 *
    117 	 * @parameter property="jacoco.address"
    118 	 */
    119 	String address;
    120 	/**
    121 	 * Port to bind to when the output method is tcpserver or connect to when
    122 	 * the output method is tcpclient. In tcpserver mode the port must be
    123 	 * available, which means that if multiple JaCoCo agents should run on the
    124 	 * same machine, different ports have to be specified.
    125 	 *
    126 	 * @parameter property="jacoco.port"
    127 	 */
    128 	Integer port;
    129 	/**
    130 	 * If a directory is specified for this parameter the JaCoCo agent dumps all
    131 	 * class files it processes to the given location. This can be useful for
    132 	 * debugging purposes or in case of dynamically created classes for example
    133 	 * when scripting engines are used.
    134 	 *
    135 	 * @parameter property="jacoco.classDumpDir"
    136 	 */
    137 	File classDumpDir;
    138 	/**
    139 	 * If set to true the agent exposes functionality via JMX.
    140 	 *
    141 	 * @parameter property="jacoco.jmx"
    142 	 */
    143 	Boolean jmx;
    144 
    145 	@Override
    146 	public void executeMojo() {
    147 		final String name = getEffectivePropertyName();
    148 		final Properties projectProperties = getProject().getProperties();
    149 		final String oldValue = projectProperties.getProperty(name);
    150 		final String newValue = createAgentOptions().prependVMArguments(
    151 				oldValue, getAgentJarFile());
    152 		getLog().info(name + " set to " + newValue);
    153 		projectProperties.setProperty(name, newValue);
    154 	}
    155 
    156 	@Override
    157 	protected void skipMojo() {
    158 		final String name = getEffectivePropertyName();
    159 		final Properties projectProperties = getProject().getProperties();
    160 		getLog().info(name + " set to empty");
    161 		projectProperties.setProperty(name, "");
    162 	}
    163 
    164 	File getAgentJarFile() {
    165 		final Artifact jacocoAgentArtifact = pluginArtifactMap
    166 				.get(AGENT_ARTIFACT_NAME);
    167 		return jacocoAgentArtifact.getFile();
    168 	}
    169 
    170 	AgentOptions createAgentOptions() {
    171 		final AgentOptions agentOptions = new AgentOptions();
    172 		agentOptions.setDestfile(getDestFile().getAbsolutePath());
    173 		if (append != null) {
    174 			agentOptions.setAppend(append.booleanValue());
    175 		}
    176 		if (getIncludes() != null && !getIncludes().isEmpty()) {
    177 			final String agentIncludes = StringUtils.join(getIncludes()
    178 					.iterator(), ":");
    179 			agentOptions.setIncludes(agentIncludes);
    180 		}
    181 		if (getExcludes() != null && !getExcludes().isEmpty()) {
    182 			final String agentExcludes = StringUtils.join(getExcludes()
    183 					.iterator(), ":");
    184 			agentOptions.setExcludes(agentExcludes);
    185 		}
    186 		if (exclClassLoaders != null) {
    187 			agentOptions.setExclClassloader(exclClassLoaders);
    188 		}
    189 		if (inclBootstrapClasses != null) {
    190 			agentOptions.setInclBootstrapClasses(inclBootstrapClasses
    191 					.booleanValue());
    192 		}
    193 		if (sessionId != null) {
    194 			agentOptions.setSessionId(sessionId);
    195 		}
    196 		if (dumpOnExit != null) {
    197 			agentOptions.setDumpOnExit(dumpOnExit.booleanValue());
    198 		}
    199 		if (output != null) {
    200 			agentOptions.setOutput(output);
    201 		}
    202 		if (address != null) {
    203 			agentOptions.setAddress(address);
    204 		}
    205 		if (port != null) {
    206 			agentOptions.setPort(port.intValue());
    207 		}
    208 		if (classDumpDir != null) {
    209 			agentOptions.setClassDumpDir(classDumpDir.getAbsolutePath());
    210 		}
    211 		if (jmx != null) {
    212 			agentOptions.setJmx(jmx.booleanValue());
    213 		}
    214 		return agentOptions;
    215 	}
    216 
    217 	String getEffectivePropertyName() {
    218 		if (isPropertyNameSpecified()) {
    219 			return propertyName;
    220 		}
    221 		if (isEclipseTestPluginPackaging()) {
    222 			return TYCHO_ARG_LINE;
    223 		}
    224 		return SUREFIRE_ARG_LINE;
    225 	}
    226 
    227 	boolean isPropertyNameSpecified() {
    228 		return propertyName != null && !"".equals(propertyName);
    229 	}
    230 
    231 	boolean isEclipseTestPluginPackaging() {
    232 		return "eclipse-test-plugin".equals(getProject().getPackaging());
    233 	}
    234 
    235 	/**
    236 	 * @return the destFile
    237 	 */
    238 	abstract File getDestFile();
    239 
    240 }
    241