# DumpTask.java

    /*******************************************************************************
     * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors
     * 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
     *
     *******************************************************************************/
    package org.jacoco.ant;

    import static java.lang.String.format;

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.InetAddress;
    import java.net.Socket;

    import org.apache.tools.ant.BuildException;
    import org.apache.tools.ant.Task;
    import org.apache.tools.ant.util.FileUtils;
    import org.jacoco.core.data.ExecutionDataWriter;
    import org.jacoco.core.runtime.AgentOptions;
    import org.jacoco.core.runtime.RemoteControlReader;
    import org.jacoco.core.runtime.RemoteControlWriter;

    /**
     * Ant task for remotely controlling an application that is running with the
     * tcpserver output mode
     */
    public class DumpTask extends Task {

    	private static final OutputStream NUL = new OutputStream() {
    		@Override
    		public void write(final int b) throws IOException {
    			// nothing to do
    		}
    	};

    	private boolean dump = true;
    	private boolean reset = false;
    	private File destfile = null;
    	private String address = AgentOptions.DEFAULT_ADDRESS;
    	private int port = AgentOptions.DEFAULT_PORT;
    	private boolean append = true;

    	/**
    	 * Sets the location of the execution data file to write. This parameter is
    	 * required when dump is <code>true</code>. Default is
    	 * <code>jacoco.exec</code>
    	 *
    	 * @param destfile
    	 *            Location to write execution data to
    	 */
    	public void setDestfile(final File destfile) {
    		this.destfile = destfile;
    	}

    	/**
    	 * IP Address or hostname to connect to. Defaults to <code>localhost</code>
    	 *
    	 * @param address
    	 *            IP Address or hostname to connect to
    	 */
    	public void setAddress(final String address) {
    		this.address = address;
    	}

    	/**
    	 * Port number to connect to. Default is <code>6300</code>
    	 *
    	 * @param port
    	 *            Port to connect to
    	 */
    	public void setPort(final int port) {
    		this.port = port;
    	}

    	/**
    	 * <code>true</code> if the destination file it to be appended to.
    	 * <code>false</code> if the file is to be overwritten
    	 *
    	 * @param append
    	 *            <code>true</code> if the destination file should be appended
    	 *            to
    	 */
    	public void setAppend(final boolean append) {
    		this.append = append;
    	}

    	/**
    	 * Sets whether execution data should be downloaded from the remote host.
    	 * Defaults to <code>false</code>
    	 *
    	 * @param dump
    	 *            <code>true</code> to download execution data
    	 */
    	public void setDump(final boolean dump) {
    		this.dump = dump;
    	}

    	/**
    	 * Sets whether a reset command should be sent after the execution data has
    	 * been copied. Defaults to <code>false</code>
    	 *
    	 * @param reset
    	 *            <code>true</code> to reset execution data
    	 */
    	public void setReset(final boolean reset) {
    		this.reset = reset;
    	}

    	@Override
    	public void execute() throws BuildException {

    		if (port <= 0) {
    			throw new BuildException("Invalid port value", getLocation());
    		}
    		if (dump && destfile == null) {
    			throw new BuildException(
    					"Destination file is required when dumping execution data",
    					getLocation());
    		}

    		OutputStream output = null;

    		try {

    			// 1. Open socket connection
    			final Socket socket = new Socket(InetAddress.getByName(address),
    					port);
    			log(format("Connecting to %s", socket.getRemoteSocketAddress()));
    			final RemoteControlWriter remoteWriter = new RemoteControlWriter(
    					socket.getOutputStream());
    			final RemoteControlReader remoteReader = new RemoteControlReader(
    					socket.getInputStream());

    			// 2. Open file output
    			output = openOutputStream();
    			final ExecutionDataWriter outputWriter = new ExecutionDataWriter(
    					output);
    			remoteReader.setSessionInfoVisitor(outputWriter);
    			remoteReader.setExecutionDataVisitor(outputWriter);

    			// 3. Request dump
    			remoteWriter.visitDumpCommand(dump, reset);
    			remoteReader.read();

    			socket.close();

    		} catch (final IOException e) {
    			throw new BuildException("Unable to dump coverage data", e,
    					getLocation());
    		} finally {
    			FileUtils.close(output);
    		}
    	}

    	private OutputStream openOutputStream() throws IOException {
    		if (dump) {
    			log(format("Dumping execution data to %s",
    					destfile.getAbsolutePath()));
    			FileUtils.getFileUtils().createNewFile(destfile, true);
    			return new FileOutputStream(destfile, append);
    		} else {
    			return NUL;
    		}
    	}

    }