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