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.agent.rt.internal; 13 14 import java.io.ByteArrayOutputStream; 15 import java.io.IOException; 16 import java.net.InetAddress; 17 import java.util.concurrent.Callable; 18 19 import org.jacoco.agent.rt.IAgent; 20 import org.jacoco.agent.rt.internal.output.FileOutput; 21 import org.jacoco.agent.rt.internal.output.IAgentOutput; 22 import org.jacoco.agent.rt.internal.output.NoneOutput; 23 import org.jacoco.agent.rt.internal.output.TcpClientOutput; 24 import org.jacoco.agent.rt.internal.output.TcpServerOutput; 25 import org.jacoco.core.JaCoCo; 26 import org.jacoco.core.data.ExecutionDataWriter; 27 import org.jacoco.core.runtime.AbstractRuntime; 28 import org.jacoco.core.runtime.AgentOptions; 29 import org.jacoco.core.runtime.AgentOptions.OutputMode; 30 import org.jacoco.core.runtime.RuntimeData; 31 32 /** 33 * The agent manages the life cycle of JaCoCo runtime. 34 */ 35 public class Agent implements IAgent { 36 37 private static Agent singleton; 38 39 /** 40 * Returns a global instance which is already started. If the method is 41 * called the first time the instance is created with the given options. 42 * 43 * @param options 44 * options to configure the instance 45 * @return global instance 46 */ getInstance(final AgentOptions options)47 public static synchronized Agent getInstance(final AgentOptions options) { 48 if (singleton == null) { 49 final Agent agent = new Agent(options, IExceptionLogger.SYSTEM_ERR); 50 agent.startup(); 51 Runtime.getRuntime().addShutdownHook(new Thread() { 52 @Override 53 public void run() { 54 agent.shutdown(); 55 } 56 }); 57 singleton = agent; 58 } 59 return singleton; 60 } 61 62 /** 63 * Returns a global instance which is already started. If a agent has not 64 * been initialized before this method will fail. 65 * 66 * @return global instance 67 * @throws IllegalStateException 68 * if no Agent has been started yet 69 */ getInstance()70 public static synchronized Agent getInstance() throws IllegalStateException { 71 if (singleton == null) { 72 throw new IllegalStateException("JaCoCo agent not started."); 73 } 74 return singleton; 75 } 76 77 private final AgentOptions options; 78 79 private final IExceptionLogger logger; 80 81 private final RuntimeData data; 82 83 private IAgentOutput output; 84 85 private Callable<Void> jmxRegistration; 86 87 /** 88 * Creates a new agent with the given agent options. 89 * 90 * @param options 91 * agent options 92 * @param logger 93 * logger used by this agent 94 */ Agent(final AgentOptions options, final IExceptionLogger logger)95 Agent(final AgentOptions options, final IExceptionLogger logger) { 96 this.options = options; 97 this.logger = logger; 98 this.data = new RuntimeData(); 99 } 100 101 /** 102 * Returns the runtime data object created by this agent 103 * 104 * @return runtime data for this agent instance 105 */ getData()106 public RuntimeData getData() { 107 return data; 108 } 109 110 /** 111 * Initializes this agent. 112 * 113 */ startup()114 public void startup() { 115 try { 116 String sessionId = options.getSessionId(); 117 if (sessionId == null) { 118 sessionId = createSessionId(); 119 } 120 data.setSessionId(sessionId); 121 output = createAgentOutput(); 122 output.startup(options, data); 123 if (options.getJmx()) { 124 // BEGIN android-change 125 // jmxRegistration = new JmxRegistration(this); 126 // END android-change 127 } 128 } catch (final Exception e) { 129 logger.logExeption(e); 130 } 131 } 132 133 /** 134 * Shutdown the agent again. 135 */ shutdown()136 public void shutdown() { 137 try { 138 if (options.getDumpOnExit()) { 139 output.writeExecutionData(false); 140 } 141 output.shutdown(); 142 if (jmxRegistration != null) { 143 jmxRegistration.call(); 144 } 145 } catch (final Exception e) { 146 logger.logExeption(e); 147 } 148 } 149 150 /** 151 * Create output implementation as given by the agent options. 152 * 153 * @return configured controller implementation 154 */ createAgentOutput()155 IAgentOutput createAgentOutput() { 156 final OutputMode controllerType = options.getOutput(); 157 switch (controllerType) { 158 case file: 159 return new FileOutput(); 160 case tcpserver: 161 return new TcpServerOutput(logger); 162 case tcpclient: 163 return new TcpClientOutput(logger); 164 case none: 165 return new NoneOutput(); 166 default: 167 throw new AssertionError(controllerType); 168 } 169 } 170 createSessionId()171 private String createSessionId() { 172 String host; 173 try { 174 host = InetAddress.getLocalHost().getHostName(); 175 } catch (final Exception e) { 176 // Also catch platform specific exceptions (like on Android) to 177 // avoid bailing out here 178 host = "unknownhost"; 179 } 180 return host + "-" + AbstractRuntime.createRandomId(); 181 } 182 183 // === IAgent Implementation === 184 getVersion()185 public String getVersion() { 186 return JaCoCo.VERSION; 187 } 188 getSessionId()189 public String getSessionId() { 190 return data.getSessionId(); 191 } 192 setSessionId(final String id)193 public void setSessionId(final String id) { 194 data.setSessionId(id); 195 } 196 reset()197 public void reset() { 198 data.reset(); 199 } 200 getExecutionData(final boolean reset)201 public byte[] getExecutionData(final boolean reset) { 202 final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 203 try { 204 final ExecutionDataWriter writer = new ExecutionDataWriter(buffer); 205 data.collect(writer, writer, reset); 206 } catch (final IOException e) { 207 // Must not happen with ByteArrayOutputStream 208 throw new AssertionError(e); 209 } 210 return buffer.toByteArray(); 211 } 212 dump(final boolean reset)213 public void dump(final boolean reset) throws IOException { 214 output.writeExecutionData(reset); 215 } 216 217 } 218