• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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