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  *    Marc R. Hoffmann - initial API and implementation
10  *
11  *******************************************************************************/
12 package org.jacoco.core.tools;
13 
14 import java.io.IOException;
15 import java.io.InterruptedIOException;
16 import java.net.InetAddress;
17 import java.net.Socket;
18 
19 import org.jacoco.core.runtime.RemoteControlReader;
20 import org.jacoco.core.runtime.RemoteControlWriter;
21 
22 /**
23  * A client for remote execution data dumps.
24  */
25 public class ExecDumpClient {
26 
27 	private boolean dump;
28 	private boolean reset;
29 	private int retryCount;
30 	private long retryDelay;
31 
32 	/**
33 	 * New instance with the defaults <code>dump==true</code>,
34 	 * <code>reset==false</code>, <code>retryCount==0</code> and
35 	 * <code>retryDelay=1000</code>.
36 	 */
ExecDumpClient()37 	public ExecDumpClient() {
38 		this.dump = true;
39 		this.reset = false;
40 		this.retryCount = 0;
41 		this.setRetryDelay(1000);
42 	}
43 
44 	/**
45 	 * Specifies whether a dump should be requested
46 	 *
47 	 * @param dump
48 	 *            <code>true</code> if a dump should be requested
49 	 */
setDump(final boolean dump)50 	public void setDump(final boolean dump) {
51 		this.dump = dump;
52 	}
53 
54 	/**
55 	 * Specifies whether execution data should be reset.
56 	 *
57 	 * @param reset
58 	 *            <code>true</code> if execution data should be reset
59 	 */
setReset(final boolean reset)60 	public void setReset(final boolean reset) {
61 		this.reset = reset;
62 	}
63 
64 	/**
65 	 * Sets the number of retry attempts to connect to the target socket. This
66 	 * allows to wait for a certain time until the target agent has initialized.
67 	 *
68 	 * @param retryCount
69 	 *            number of retries
70 	 */
setRetryCount(final int retryCount)71 	public void setRetryCount(final int retryCount) {
72 		this.retryCount = retryCount;
73 	}
74 
75 	/**
76 	 * Sets the delay time before between connection attempts.
77 	 *
78 	 * @param retryDelay
79 	 *            delay in milliseconds
80 	 */
setRetryDelay(final long retryDelay)81 	public void setRetryDelay(final long retryDelay) {
82 		this.retryDelay = retryDelay;
83 	}
84 
85 	/**
86 	 * Requests a dump from the given end-point.
87 	 *
88 	 * @param address
89 	 *            IP-Address to connect to
90 	 * @param port
91 	 *            port to connect to
92 	 * @return container for the dumped data
93 	 * @throws IOException
94 	 *             in case the dump can not be requested
95 	 */
dump(final String address, final int port)96 	public ExecFileLoader dump(final String address, final int port)
97 			throws IOException {
98 		return dump(InetAddress.getByName(address), port);
99 	}
100 
101 	/**
102 	 * Requests a dump from the given end-point.
103 	 *
104 	 * @param address
105 	 *            host name or IP-Address to connect to
106 	 * @param port
107 	 *            port to connect to
108 	 * @return container for the dumped data
109 	 * @throws IOException
110 	 *             in case the dump can not be requested
111 	 */
dump(final InetAddress address, final int port)112 	public ExecFileLoader dump(final InetAddress address, final int port)
113 			throws IOException {
114 		final ExecFileLoader loader = new ExecFileLoader();
115 		final Socket socket = tryConnect(address, port);
116 		try {
117 			final RemoteControlWriter remoteWriter = new RemoteControlWriter(
118 					socket.getOutputStream());
119 			final RemoteControlReader remoteReader = new RemoteControlReader(
120 					socket.getInputStream());
121 			remoteReader.setSessionInfoVisitor(loader.getSessionInfoStore());
122 			remoteReader
123 					.setExecutionDataVisitor(loader.getExecutionDataStore());
124 
125 			remoteWriter.visitDumpCommand(dump, reset);
126 			remoteReader.read();
127 
128 		} finally {
129 			socket.close();
130 		}
131 		return loader;
132 	}
133 
tryConnect(final InetAddress address, final int port)134 	private Socket tryConnect(final InetAddress address, final int port)
135 			throws IOException {
136 		int count = 0;
137 		while (true) {
138 			try {
139 				onConnecting(address, port);
140 				return new Socket(address, port);
141 			} catch (final IOException e) {
142 				if (++count > retryCount) {
143 					throw e;
144 				}
145 				onConnectionFailure(e);
146 				sleep();
147 			}
148 		}
149 	}
150 
sleep()151 	private void sleep() throws InterruptedIOException {
152 		try {
153 			Thread.sleep(retryDelay);
154 		} catch (final InterruptedException e) {
155 			throw new InterruptedIOException();
156 		}
157 	}
158 
159 	/**
160 	 * This method can be overwritten to get an event just before a connection
161 	 * is made.
162 	 *
163 	 * @param address
164 	 *            target address
165 	 * @param port
166 	 *            target port
167 	 */
onConnecting( @uppressWarnings"unused") final InetAddress address, @SuppressWarnings("unused") final int port)168 	protected void onConnecting(
169 			@SuppressWarnings("unused") final InetAddress address,
170 			@SuppressWarnings("unused") final int port) {
171 	}
172 
173 	/**
174 	 * This method can be overwritten to get an event for connection failures
175 	 * when another retry will be attempted.
176 	 *
177 	 * @param exception
178 	 *            connection error
179 	 */
onConnectionFailure( @uppressWarnings"unused") final IOException exception)180 	protected void onConnectionFailure(
181 			@SuppressWarnings("unused") final IOException exception) {
182 	}
183 
184 }
185