Added API to extract Agent Jar and create VM option from AgentOptions

Used in Ant, Eclipse and Maven
diff --git a/org.jacoco.agent/src/org/jacoco/agent/AgentJar.java b/org.jacoco.agent/src/org/jacoco/agent/AgentJar.java
index da978fd..c7a30e2 100644
--- a/org.jacoco.agent/src/org/jacoco/agent/AgentJar.java
+++ b/org.jacoco.agent/src/org/jacoco/agent/AgentJar.java
@@ -12,8 +12,12 @@
  *******************************************************************************/

 package org.jacoco.agent;

 

+import java.io.Closeable;

+import java.io.File;

+import java.io.FileOutputStream;

+import java.io.IOException;

 import java.io.InputStream;

-import java.net.URL;

+import java.io.OutputStream;

 

 /**

  * API to access the agent JAR file as a resource.

@@ -24,24 +28,59 @@
 public class AgentJar {

 

 	/**

-	 * Name of the agent JAR file resource within this bunde.

+	 * Name of the agent JAR file resource within this bundle.

 	 */

-	public static final String RESOURCE = "/jacocoagent.jar";

+	private static final String RESOURCE = "/jacocoagent.jar";

 

 	private AgentJar() {

 	}

 

 	/**

-	 * Returns a URL pointing to the JAR file.

+	 * Extract the JaCoCo agent jar from the classpath and put it into a

+	 * temporary location. This file should be deleted on exit, but may not if

+	 * the VM is terminated

 	 * 

-	 * @return URL of the JAR file

+	 * @return Location of the Agent Jar file in the local file system. The file

+	 *         should exist and be readable.

+	 * @throws IOException

+	 *             Unable to unpack agent jar

 	 */

-	public static URL getResource() {

-		final URL url = AgentJar.class.getResource(RESOURCE);

-		if (url == null) {

-			throw new RuntimeException(ERRORMSG);

+	public static File extractToTempLocation() throws IOException {

+		final File agentJar = File.createTempFile("jacocoagent", ".jar");

+		agentJar.deleteOnExit();

+

+		extractTo(agentJar);

+

+		return agentJar;

+	}

+

+	/**

+	 * Extract the JaCoCo agent jar from the classpath and put it into the

+	 * specified location.

+	 * 

+	 * @param destination

+	 *            Location to write JaCoCo Agent Jar to. Must be writeable

+	 * @throws IOException

+	 *             Unable to unpack agent jar

+	 */

+	public static void extractTo(File destination) throws IOException {

+		InputStream inputJarStream = getResourceAsStream();

+		OutputStream outputJarStream = null;

+

+		try {

+

+			outputJarStream = new FileOutputStream(destination);

+

+			final byte[] buffer = new byte[8192];

+

+			int bytesRead;

+			while ((bytesRead = inputJarStream.read(buffer)) != -1) {

+				outputJarStream.write(buffer, 0, bytesRead);

+			}

+		} finally {

+			safeClose(inputJarStream);

+			safeClose(outputJarStream);

 		}

-		return url;

 	}

 

 	/**

@@ -49,14 +88,29 @@
 	 * 

 	 * @return content of the JAR file

 	 */

-	public static InputStream getResourceAsStream() {

+	private static InputStream getResourceAsStream() throws IOException {

 		final InputStream stream = AgentJar.class.getResourceAsStream(RESOURCE);

 		if (stream == null) {

-			throw new RuntimeException(ERRORMSG);

+			throw new IOException(ERRORMSG);

 		}

 		return stream;

 	}

 

+	/**

+	 * Close a stream ignoring any error

+	 * 

+	 * @param closeable

+	 *            stream to be closed

+	 */

+	private static void safeClose(Closeable closeable) {

+		try {

+			if (closeable != null) {

+				closeable.close();

+			}

+		} catch (IOException e) {

+		}

+	}

+

 	private static final String ERRORMSG = String.format(

 			"The resource %s has not been found. Please see "

 					+ "/org.jacoco.agent/README.TXT for details.", RESOURCE);

diff --git a/org.jacoco.ant/src/org/jacoco/ant/AbstractCoverageTask.java b/org.jacoco.ant/src/org/jacoco/ant/AbstractCoverageTask.java
index 371f467..66fadcb 100644
--- a/org.jacoco.ant/src/org/jacoco/ant/AbstractCoverageTask.java
+++ b/org.jacoco.ant/src/org/jacoco/ant/AbstractCoverageTask.java
@@ -13,7 +13,9 @@
 package org.jacoco.ant;

 

 import java.io.File;

+import java.io.IOException;

 

+import org.apache.tools.ant.BuildException;

 import org.apache.tools.ant.Task;

 import org.jacoco.core.runtime.AgentOptions;

 

@@ -99,4 +101,18 @@
 		agentOptions.setExclClassloader(exclClassLoader);

 	}

 

+	/**

+	 * Creates JVM argument to launch with the specified JaCoCo agent jar and

+	 * the current options

+	 * 

+	 * @return JVM Argument to pass to new VM

+	 */

+	protected String getLaunchingArgument() {

+		try {

+			return getAgentOptions().getVMArgument(JaCoCoState.getAgentFile());

+		} catch (final IOException e) {

+			throw new BuildException("Unable to extract agent jar", e);

+		}

+	}

+

 }

diff --git a/org.jacoco.ant/src/org/jacoco/ant/AgentTask.java b/org.jacoco.ant/src/org/jacoco/ant/AgentTask.java
index bfc9085..c114f6f 100644
--- a/org.jacoco.ant/src/org/jacoco/ant/AgentTask.java
+++ b/org.jacoco.ant/src/org/jacoco/ant/AgentTask.java
@@ -57,8 +57,6 @@
 		if (property == null || property.length() == 0) {

 			throw new BuildException("Property is mandatory");

 		}

-		final JvmArgumentHelper jvmArgumentHelper = new JvmArgumentHelper();

-		getProject().setNewProperty(property,

-				jvmArgumentHelper.createJavaAgentParam(getAgentOptions()));

+		getProject().setNewProperty(property, getLaunchingArgument());

 	}

 }

diff --git a/org.jacoco.ant/src/org/jacoco/ant/CoverageTask.java b/org.jacoco.ant/src/org/jacoco/ant/CoverageTask.java
index 2a7d464..4cd9cb9 100644
--- a/org.jacoco.ant/src/org/jacoco/ant/CoverageTask.java
+++ b/org.jacoco.ant/src/org/jacoco/ant/CoverageTask.java
@@ -125,17 +125,14 @@
 		}

 

 		public void addJvmArgs(final UnknownElement task) {

-			final JvmArgumentHelper jvmArgumentHelper = new JvmArgumentHelper();

-			final String agentParam = jvmArgumentHelper

-					.createJavaAgentParam(getAgentOptions());

-

 			final UnknownElement el = new UnknownElement("jvmarg");

 			el.setTaskName("jvmarg");

 			el.setQName("jvmarg");

 

 			final RuntimeConfigurable runtimeConfigurableWrapper = el

 					.getRuntimeConfigurableWrapper();

-			runtimeConfigurableWrapper.setAttribute("value", agentParam);

+			runtimeConfigurableWrapper.setAttribute("value",

+					getLaunchingArgument());

 

 			task.getRuntimeConfigurableWrapper().addChild(

 					runtimeConfigurableWrapper);

diff --git a/org.jacoco.ant/src/org/jacoco/ant/JaCoCoState.java b/org.jacoco.ant/src/org/jacoco/ant/JaCoCoState.java
new file mode 100644
index 0000000..9109396
--- /dev/null
+++ b/org.jacoco.ant/src/org/jacoco/ant/JaCoCoState.java
@@ -0,0 +1,44 @@
+/*******************************************************************************

+ * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others

+ * All rights reserved. This program and the accompanying materials

+ * are made available under the terms of the Eclipse Public License v1.0

+ * which accompanies this distribution, and is available at

+ * http://www.eclipse.org/legal/epl-v10.html

+ *

+ * Contributors:

+ *    Brock Janiczak - initial API and implementation

+ *    

+ * $Id: $

+ *******************************************************************************/

+package org.jacoco.ant;

+

+import java.io.File;

+import java.io.IOException;

+

+import org.jacoco.agent.AgentJar;

+

+/**

+ * Static class for holding the state of the JaCoCo agent

+ * 

+ * @author Brock Janiczak

+ * @version $Revision: $

+ */

+public class JaCoCoState {

+	private static File agentFile;

+

+	/**

+	 * Gets the current location of the JaCoCo agent jar file. The agent jar may

+	 * be extracted to a temporary location

+	 * 

+	 * @return Physical location of the JaCoCo Agent Jar

+	 * @throws IOException

+	 *             Unable to extract the agent jar file

+	 */

+	public static synchronized File getAgentFile() throws IOException {

+		if (agentFile == null) {

+			agentFile = AgentJar.extractToTempLocation();

+		}

+

+		return agentFile;

+	}

+}

diff --git a/org.jacoco.ant/src/org/jacoco/ant/JvmArgumentHelper.java b/org.jacoco.ant/src/org/jacoco/ant/JvmArgumentHelper.java
deleted file mode 100644
index 5561883..0000000
--- a/org.jacoco.ant/src/org/jacoco/ant/JvmArgumentHelper.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*******************************************************************************

- * Copyright (c) 2009 Mountainminds GmbH & Co. KG and others

- * All rights reserved. This program and the accompanying materials

- * are made available under the terms of the Eclipse Public License v1.0

- * which accompanies this distribution, and is available at

- * http://www.eclipse.org/legal/epl-v10.html

- *

- * Contributors:

- *    Brock Janiczak - initial API and implementation

- *    

- * $Id: $

- *******************************************************************************/

-package org.jacoco.ant;

-

-import java.io.File;

-import java.io.FileOutputStream;

-import java.io.IOException;

-import java.io.InputStream;

-import java.io.OutputStream;

-

-import org.apache.tools.ant.BuildException;

-import org.apache.tools.ant.util.FileUtils;

-import org.jacoco.agent.AgentJar;

-import org.jacoco.core.runtime.AgentOptions;

-

-/**

- * Helper class to generate the JVM argument required to start a new JVM with a

- * code coverage agent

- * 

- * @author Brock Janiczak

- * @version $Revision: $

- */

-class JvmArgumentHelper {

-	private final File agentJar;

-

-	JvmArgumentHelper() {

-		final InputStream inputStream = AgentJar.getResourceAsStream();

-		try {

-			agentJar = extractAgentJar(inputStream);

-		} finally {

-			FileUtils.close(inputStream);

-		}

-	}

-

-	/**

-	 * Extract the JaCoCo agent jar from the classpath and put it into a

-	 * temporary location.

-	 * 

-	 * @param inputJarStream

-	 *            Open stream pointing to the JaCoCo jar

-	 * @return Local physical location of the JaCoCo agent jar. This file will

-	 *         be removed once the task has been executed

-	 */

-	private File extractAgentJar(final InputStream inputJarStream) {

-

-		if (inputJarStream == null) {

-			throw new BuildException("Unable to locate Agent Jar");

-		}

-

-		OutputStream outputJarStream = null;

-		try {

-			final File agentJar = File.createTempFile("jacocoagent", ".jar");

-			agentJar.deleteOnExit();

-

-			outputJarStream = new FileOutputStream(agentJar);

-

-			final byte[] buffer = new byte[8192];

-

-			int bytesRead;

-			while ((bytesRead = inputJarStream.read(buffer)) != -1) {

-				outputJarStream.write(buffer, 0, bytesRead);

-			}

-

-			return agentJar;

-		} catch (final IOException e) {

-			throw new BuildException("Unable to unpack Agent Jar", e);

-		} finally {

-			FileUtils.close(outputJarStream);

-		}

-	}

-

-	/**

-	 * Generate required JVM argument string based on current configuration and

-	 * agent jar location

-	 * 

-	 * @return Argument to pass to create new VM with coverage enabled

-	 */

-	String createJavaAgentParam(final AgentOptions agentOptions) {

-		final StringBuilder param = new StringBuilder();

-		param.append("-javaagent:");

-		param.append(agentJar.getAbsolutePath());

-		param.append("=");

-		param.append(agentOptions.toString());

-

-		return param.toString();

-	}

-}

diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java b/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java
index d30ee94..8e6dc69 100644
--- a/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java
+++ b/org.jacoco.core/src/org/jacoco/core/runtime/AgentOptions.java
@@ -14,6 +14,7 @@
 

 import static java.lang.String.format;

 

+import java.io.File;

 import java.util.Arrays;

 import java.util.Collection;

 import java.util.HashMap;

@@ -223,6 +224,26 @@
 	}

 

 	/**

+	 * Generate required JVM argument string based on current configuration and

+	 * supplied agent jar location

+	 * 

+	 * @param agentJarFile

+	 *            location of the JaCoCo Agent Jar

+	 * @return Argument to pass to create new VM with coverage enabled

+	 */

+	public String getVMArgument(final File agentJarFile) {

+		final StringBuilder param = new StringBuilder();

+		param.append('"');

+		param.append("-javaagent:");

+		param.append(agentJarFile.toString());

+		param.append("=");

+		param.append(this.toString());

+		param.append('"');

+

+		return param.toString();

+	}

+

+	/**

 	 * Creates a string representation that can be passed to the agent via the

 	 * command line. Might be the empty string, if no options are set.

 	 */