Agent.java revision 283abfa148b749678924b5e75eabd35a2d58f9f8
1/******************************************************************************* 2 * Copyright (c) 2009, 2014 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.agent.rt.internal; 13 14import java.io.ByteArrayOutputStream; 15import java.io.IOException; 16import java.net.InetAddress; 17import java.util.concurrent.Callable; 18 19import org.jacoco.agent.rt.IAgent; 20import org.jacoco.agent.rt.internal.output.FileOutput; 21import org.jacoco.agent.rt.internal.output.IAgentOutput; 22import org.jacoco.agent.rt.internal.output.NoneOutput; 23import org.jacoco.agent.rt.internal.output.TcpClientOutput; 24import org.jacoco.agent.rt.internal.output.TcpServerOutput; 25import org.jacoco.core.JaCoCo; 26import org.jacoco.core.data.ExecutionDataWriter; 27import org.jacoco.core.runtime.AbstractRuntime; 28import org.jacoco.core.runtime.AgentOptions; 29import org.jacoco.core.runtime.AgentOptions.OutputMode; 30import org.jacoco.core.runtime.RuntimeData; 31 32/** 33 * The agent manages the life cycle of JaCoCo runtime. 34 */ 35public 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 */ 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 */ 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 */ 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 */ 106 public RuntimeData getData() { 107 return data; 108 } 109 110 /** 111 * Initializes this agent. 112 * 113 */ 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 jmxRegistration = new JmxRegistration(this); 125 } 126 } catch (final Exception e) { 127 logger.logExeption(e); 128 } 129 } 130 131 /** 132 * Shutdown the agent again. 133 */ 134 public void shutdown() { 135 try { 136 if (options.getDumpOnExit()) { 137 output.writeExecutionData(false); 138 } 139 output.shutdown(); 140 if (jmxRegistration != null) { 141 jmxRegistration.call(); 142 } 143 } catch (final Exception e) { 144 logger.logExeption(e); 145 } 146 } 147 148 /** 149 * Create output implementation as given by the agent options. 150 * 151 * @return configured controller implementation 152 */ 153 IAgentOutput createAgentOutput() { 154 final OutputMode controllerType = options.getOutput(); 155 switch (controllerType) { 156 case file: 157 return new FileOutput(); 158 case tcpserver: 159 return new TcpServerOutput(logger); 160 case tcpclient: 161 return new TcpClientOutput(logger); 162 case none: 163 return new NoneOutput(); 164 default: 165 throw new AssertionError(controllerType); 166 } 167 } 168 169 private String createSessionId() { 170 String host; 171 try { 172 host = InetAddress.getLocalHost().getHostName(); 173 } catch (final Exception e) { 174 // Also catch platform specific exceptions (like on Android) to 175 // avoid bailing out here 176 host = "unknownhost"; 177 } 178 return host + "-" + AbstractRuntime.createRandomId(); 179 } 180 181 // === IAgent Implementation === 182 183 public String getVersion() { 184 return JaCoCo.VERSION; 185 } 186 187 public String getSessionId() { 188 return data.getSessionId(); 189 } 190 191 public void setSessionId(final String id) { 192 data.setSessionId(id); 193 } 194 195 public void reset() { 196 data.reset(); 197 } 198 199 public byte[] getExecutionData(final boolean reset) { 200 final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 201 try { 202 final ExecutionDataWriter writer = new ExecutionDataWriter(buffer); 203 data.collect(writer, writer, reset); 204 } catch (final IOException e) { 205 // Must not happen with ByteArrayOutputStream 206 throw new AssertionError(e); 207 } 208 return buffer.toByteArray(); 209 } 210 211 public void dump(final boolean reset) throws IOException { 212 output.writeExecutionData(reset); 213 } 214 215} 216