1/******************************************************************************* 2 * Copyright (c) 2009, 2018 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 *******************************************************************************/ 12package org.jacoco.core.tools; 13 14import java.io.IOException; 15import java.io.InterruptedIOException; 16import java.net.InetAddress; 17import java.net.Socket; 18 19import org.jacoco.core.runtime.RemoteControlReader; 20import org.jacoco.core.runtime.RemoteControlWriter; 21 22/** 23 * A client for remote execution data dumps. 24 */ 25public 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 127 if (!remoteReader.read()) { 128 throw new IOException("Socket closed unexpectedly."); 129 } 130 131 } finally { 132 socket.close(); 133 } 134 return loader; 135 } 136 137 private Socket tryConnect(final InetAddress address, final int port) 138 throws IOException { 139 int count = 0; 140 while (true) { 141 try { 142 onConnecting(address, port); 143 return new Socket(address, port); 144 } catch (final IOException e) { 145 if (++count > retryCount) { 146 throw e; 147 } 148 onConnectionFailure(e); 149 sleep(); 150 } 151 } 152 } 153 154 private void sleep() throws InterruptedIOException { 155 try { 156 Thread.sleep(retryDelay); 157 } catch (final InterruptedException e) { 158 throw new InterruptedIOException(); 159 } 160 } 161 162 /** 163 * This method can be overwritten to get an event just before a connection 164 * is made. 165 * 166 * @param address 167 * target address 168 * @param port 169 * target port 170 */ 171 protected void onConnecting( 172 @SuppressWarnings("unused") final InetAddress address, 173 @SuppressWarnings("unused") final int port) { 174 } 175 176 /** 177 * This method can be overwritten to get an event for connection failures 178 * when another retry will be attempted. 179 * 180 * @param exception 181 * connection error 182 */ 183 protected void onConnectionFailure( 184 @SuppressWarnings("unused") final IOException exception) { 185 } 186 187} 188