Agent.java revision 2a8b5339c422de268cae3a862a85666a9b0ac176
1/*******************************************************************************
2 * Copyright (c) 2009, 2013 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.net.UnknownHostException;
18
19import org.jacoco.agent.rt.IAgent;
20import org.jacoco.agent.rt.internal.controller.IAgentController;
21import org.jacoco.agent.rt.internal.controller.LocalController;
22import org.jacoco.agent.rt.internal.controller.MBeanController;
23import org.jacoco.agent.rt.internal.controller.TcpClientController;
24import org.jacoco.agent.rt.internal.controller.TcpServerController;
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 IAgentController controller;
82
83	private final RuntimeData data;
84
85	/**
86	 * Creates a new agent with the given agent options.
87	 *
88	 * @param options
89	 *            agent options
90	 * @param logger
91	 *            logger used by this agent
92	 */
93	public Agent(final AgentOptions options, final IExceptionLogger logger) {
94		this.options = options;
95		this.logger = logger;
96		this.data = new RuntimeData();
97	}
98
99	/**
100	 * Returns the runtime data object created by this agent
101	 *
102	 * @return runtime data for this agent instance
103	 */
104	public RuntimeData getData() {
105		return data;
106	}
107
108	/**
109	 * Initializes this agent.
110	 *
111	 */
112	public void startup() {
113		try {
114			String sessionId = options.getSessionId();
115			if (sessionId == null) {
116				sessionId = createSessionId();
117			}
118			data.setSessionId(sessionId);
119			controller = createAgentController();
120			controller.startup(options, data);
121		} catch (final Exception e) {
122			logger.logExeption(e);
123		}
124	}
125
126	/**
127	 * Shutdown the agent again.
128	 */
129	public void shutdown() {
130		try {
131			if (options.getDumpOnExit()) {
132				controller.writeExecutionData(false);
133			}
134			controller.shutdown();
135		} catch (final Exception e) {
136			logger.logExeption(e);
137		}
138	}
139
140	/**
141	 * Create controller implementation as given by the agent options.
142	 *
143	 * @return configured controller implementation
144	 */
145	IAgentController createAgentController() {
146		final OutputMode controllerType = options.getOutput();
147		switch (controllerType) {
148		case file:
149			return new LocalController();
150		case tcpserver:
151			return new TcpServerController(logger);
152		case tcpclient:
153			return new TcpClientController(logger);
154		case mbean:
155			return new MBeanController();
156		default:
157			throw new AssertionError(controllerType);
158		}
159	}
160
161	private String createSessionId() {
162		String host;
163		try {
164			host = InetAddress.getLocalHost().getHostName();
165		} catch (final UnknownHostException e) {
166			host = "unknownhost";
167		}
168		return host + "-" + AbstractRuntime.createRandomId();
169	}
170
171	// === IAgent Implementation ===
172
173	public String getVersion() {
174		return JaCoCo.VERSION;
175	}
176
177	public String getSessionId() {
178		return data.getSessionId();
179	}
180
181	public void setSessionId(final String id) {
182		data.setSessionId(id);
183	}
184
185	public void reset() {
186		data.reset();
187	}
188
189	public byte[] getExecutionData(final boolean reset) {
190		final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
191		try {
192			final ExecutionDataWriter writer = new ExecutionDataWriter(buffer);
193			data.collect(writer, writer, reset);
194		} catch (final IOException e) {
195			// Must not happen with ByteArrayOutputStream
196			throw new AssertionError(e);
197		}
198		return buffer.toByteArray();
199	}
200
201	public void dump(final boolean reset) throws IOException {
202		controller.writeExecutionData(reset);
203	}
204
205}
206