/******************************************************************************* * Copyright (c) 2009, 2017 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: * Marc R. Hoffmann - initial API and implementation * *******************************************************************************/ package org.jacoco.core.tools; import java.io.IOException; import java.io.InterruptedIOException; import java.net.InetAddress; import java.net.Socket; import org.jacoco.core.runtime.RemoteControlReader; import org.jacoco.core.runtime.RemoteControlWriter; /** * A client for remote execution data dumps. */ public class ExecDumpClient { private boolean dump; private boolean reset; private int retryCount; private long retryDelay; /** * New instance with the defaults dump==true, * reset==false, retryCount==0 and * retryDelay=1000. */ public ExecDumpClient() { this.dump = true; this.reset = false; this.retryCount = 0; this.setRetryDelay(1000); } /** * Specifies whether a dump should be requested * * @param dump * true if a dump should be requested */ public void setDump(final boolean dump) { this.dump = dump; } /** * Specifies whether execution data should be reset. * * @param reset * true if execution data should be reset */ public void setReset(final boolean reset) { this.reset = reset; } /** * Sets the number of retry attempts to connect to the target socket. This * allows to wait for a certain time until the target agent has initialized. * * @param retryCount * number of retries */ public void setRetryCount(final int retryCount) { this.retryCount = retryCount; } /** * Sets the delay time before between connection attempts. * * @param retryDelay * delay in milliseconds */ public void setRetryDelay(final long retryDelay) { this.retryDelay = retryDelay; } /** * Requests a dump from the given end-point. * * @param address * IP-Address to connect to * @param port * port to connect to * @return container for the dumped data * @throws IOException * in case the dump can not be requested */ public ExecFileLoader dump(final String address, final int port) throws IOException { return dump(InetAddress.getByName(address), port); } /** * Requests a dump from the given end-point. * * @param address * host name or IP-Address to connect to * @param port * port to connect to * @return container for the dumped data * @throws IOException * in case the dump can not be requested */ public ExecFileLoader dump(final InetAddress address, final int port) throws IOException { final ExecFileLoader loader = new ExecFileLoader(); final Socket socket = tryConnect(address, port); try { final RemoteControlWriter remoteWriter = new RemoteControlWriter( socket.getOutputStream()); final RemoteControlReader remoteReader = new RemoteControlReader( socket.getInputStream()); remoteReader.setSessionInfoVisitor(loader.getSessionInfoStore()); remoteReader .setExecutionDataVisitor(loader.getExecutionDataStore()); remoteWriter.visitDumpCommand(dump, reset); if (!remoteReader.read()) { throw new IOException("Socket closed unexpectedly."); } } finally { socket.close(); } return loader; } private Socket tryConnect(final InetAddress address, final int port) throws IOException { int count = 0; while (true) { try { onConnecting(address, port); return new Socket(address, port); } catch (final IOException e) { if (++count > retryCount) { throw e; } onConnectionFailure(e); sleep(); } } } private void sleep() throws InterruptedIOException { try { Thread.sleep(retryDelay); } catch (final InterruptedException e) { throw new InterruptedIOException(); } } /** * This method can be overwritten to get an event just before a connection * is made. * * @param address * target address * @param port * target port */ protected void onConnecting( @SuppressWarnings("unused") final InetAddress address, @SuppressWarnings("unused") final int port) { } /** * This method can be overwritten to get an event for connection failures * when another retry will be attempted. * * @param exception * connection error */ protected void onConnectionFailure( @SuppressWarnings("unused") final IOException exception) { } }