Turn ILttngAgentResponse into an abstract class
[lttng-ust.git] / liblttng-ust-java-agent / java / lttng-ust-agent-common / org / lttng / ust / agent / AbstractLttngAgent.java
CommitLineData
d60dfbe4
AM
1/*
2 * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
3 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
4 *
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License, version 2.1 only,
7 * as published by the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19package org.lttng.ust.agent;
20
21import java.util.HashSet;
22import java.util.LinkedList;
23import java.util.List;
24import java.util.Map;
25import java.util.NavigableMap;
26import java.util.Set;
27import java.util.concurrent.ConcurrentHashMap;
28import java.util.concurrent.ConcurrentSkipListMap;
29import java.util.concurrent.atomic.AtomicInteger;
30
31import org.lttng.ust.agent.client.LttngTcpSessiondClient;
32
33/**
34 * Base implementation of a {@link ILttngAgent}.
35 *
36 * @author Alexandre Montplaisir
37 * @param <T>
38 * The type of logging handler that should register to this agent
39 */
40public abstract class AbstractLttngAgent<T extends ILttngHandler> implements ILttngAgent<T> {
41
42 private static final String WILDCARD = "*";
43 private static final int INIT_TIMEOUT = 3; /* Seconds */
44
45 /** The handlers registered to this agent */
46 private final Set<T> registeredHandlers = new HashSet<T>();
47
48 /**
49 * The trace events currently enabled in the sessions.
50 *
51 * The key represents the event name, the value is the ref count (how many
52 * different sessions currently have this event enabled). Once the ref count
53 * falls to 0, this means we can avoid sending log events through JNI
54 * because nobody wants them.
55 *
56 * It uses a concurrent hash set", so that the {@link #isEventEnabled} and
57 * read methods do not need to take a synchronization lock.
58 */
59 private final Map<String, Integer> enabledEvents = new ConcurrentHashMap<String, Integer>();
60
61 /**
62 * The trace events prefixes currently enabled in the sessions, which means
63 * the event names finishing in *, like "abcd*". We track them separately
64 * from the standard event names, so that we can use {@link String#equals}
65 * and {@link String#startsWith} appropriately.
66 *
67 * We track the lone wildcard "*" separately, in {@link #enabledWildcards}.
68 */
69 private final NavigableMap<String, Integer> enabledEventPrefixes =
70 new ConcurrentSkipListMap<String, Integer>();
71
72 /** Number of sessions currently enabling the wildcard "*" event */
73 private final AtomicInteger enabledWildcards = new AtomicInteger(0);
74
75 /** Tracing domain. Defined by the sub-classes via the constructor. */
76 private final Domain domain;
77
78 /* Lazy-loaded sessiond clients and their thread objects */
79 private LttngTcpSessiondClient rootSessiondClient = null;
80 private LttngTcpSessiondClient userSessiondClient = null;
81 private Thread rootSessiondClientThread = null;
82 private Thread userSessiondClientThread = null;
83
84 /** Indicates if this agent has been initialized. */
85 private boolean initialized = false;
86
87 /**
88 * Constructor. Should only be called by sub-classes via super(...);
89 *
90 * @param domain
91 * The tracing domain of this agent.
92 */
93 protected AbstractLttngAgent(Domain domain) {
94 this.domain = domain;
95 }
96
97 @Override
98 public Domain getDomain() {
99 return domain;
100 }
101
102 @Override
103 public void registerHandler(T handler) {
104 synchronized (registeredHandlers) {
105 if (registeredHandlers.isEmpty()) {
106 /*
107 * This is the first handler that registers, we will initialize
108 * the agent.
109 */
110 init();
111 }
112 registeredHandlers.add(handler);
113 }
114 }
115
116 @Override
117 public void unregisterHandler(T handler) {
118 synchronized (registeredHandlers) {
119 registeredHandlers.remove(handler);
120 if (registeredHandlers.isEmpty()) {
121 /* There are no more registered handlers, close the connection. */
122 dispose();
123 }
124 }
125 }
126
127 private void init() {
128 /*
129 * Only called from a synchronized (registeredHandlers) block, should
130 * not need additional synchronization.
131 */
132 if (initialized) {
133 return;
134 }
135 String rootClientThreadName = "Root sessiond client started by agent: " + this.getClass().getSimpleName();
136
137 rootSessiondClient = new LttngTcpSessiondClient(this, true);
138 rootSessiondClientThread = new Thread(rootSessiondClient, rootClientThreadName);
139 rootSessiondClientThread.setDaemon(true);
140 rootSessiondClientThread.start();
141
142 String userClientThreadName = "User sessiond client started by agent: " + this.getClass().getSimpleName();
143
144 userSessiondClient = new LttngTcpSessiondClient(this, false);
145 userSessiondClientThread = new Thread(userSessiondClient, userClientThreadName);
146 userSessiondClientThread.setDaemon(true);
147 userSessiondClientThread.start();
148
149 /* Give the threads' registration a chance to end. */
150 if (!rootSessiondClient.waitForConnection(INIT_TIMEOUT)) {
151 userSessiondClient.waitForConnection(INIT_TIMEOUT);
152 }
153
154 initialized = true;
155 }
156
157 /**
158 * Dispose the agent
159 */
160 private void dispose() {
161 /*
162 * Only called from a synchronized (registeredHandlers) block, should
163 * not need additional synchronization.
164 */
165 rootSessiondClient.close();
166 userSessiondClient.close();
167
168 try {
169 rootSessiondClientThread.join();
170 userSessiondClientThread.join();
171
172 } catch (InterruptedException e) {
173 e.printStackTrace();
174 }
175 rootSessiondClient = null;
176 rootSessiondClientThread = null;
177 userSessiondClient = null;
178 userSessiondClientThread = null;
179
180 /* Reset all enabled event counts to 0 */
181 enabledEvents.clear();
182 enabledEventPrefixes.clear();
183 enabledWildcards.set(0);
184
185 initialized = false;
186
187 }
188
189 /**
190 * Callback for the TCP clients to notify the agent that a request for
191 * enabling an event was sent from the session daemon.
192 *
193 * @param eventName
194 * The name of the event that was requested to be enabled.
195 * @return Since we do not track individual sessions, right now this command
196 * cannot fail. It will always return true.
197 */
198 public boolean eventEnabled(String eventName) {
199 if (eventName.equals(WILDCARD)) {
200 enabledWildcards.incrementAndGet();
201 return true;
202 }
203
204 if (eventName.endsWith(WILDCARD)) {
205 /* Strip the "*" from the name. */
206 String prefix = eventName.substring(0, eventName.length() - 1);
207 return incrementEventCount(prefix, enabledEventPrefixes);
208 }
209
210 return incrementEventCount(eventName, enabledEvents);
211 }
212
213 /**
214 * Callback for the TCP clients to notify the agent that a request for
215 * disabling an event was sent from the session daemon.
216 *
217 * @param eventName
218 * The name of the event that was requested to be disabled.
219 * @return True if the command completed successfully, false if we should
220 * report an error (event was not enabled, etc.)
221 */
222 public boolean eventDisabled(String eventName) {
223 if (eventName.equals(WILDCARD)) {
224 int newCount = enabledWildcards.decrementAndGet();
225 if (newCount < 0) {
226 /* Event was not enabled, bring the count back to 0 */
227 enabledWildcards.incrementAndGet();
228 return false;
229 }
cbaa5167 230 return true;
d60dfbe4
AM
231 }
232
233 if (eventName.endsWith(WILDCARD)) {
234 /* Strip the "*" from the name. */
235 String prefix = eventName.substring(0, eventName.length() - 1);
236 return decrementEventCount(prefix, enabledEventPrefixes);
237 }
238
239 return decrementEventCount(eventName, enabledEvents);
240 }
241
242 @Override
243 public boolean isEventEnabled(String eventName) {
244 /* If at least one session enabled the "*" wildcard, send the event */
245 if (enabledWildcards.get() > 0) {
246 return true;
247 }
248
249 /* Check if at least one session wants this exact event name */
250 if (enabledEvents.containsKey(eventName)) {
251 return true;
252 }
253
254 /* Look in the enabled prefixes if one of them matches the event */
255 String potentialMatch = enabledEventPrefixes.floorKey(eventName);
256 if (potentialMatch != null && eventName.startsWith(potentialMatch)) {
257 return true;
258 }
259
260 return false;
261 }
262
263 @Override
264 public Iterable<String> listEnabledEvents() {
265 List<String> events = new LinkedList<String>();
266
267 if (enabledWildcards.get() > 0) {
268 events.add(WILDCARD);
269 }
270 for (String prefix : enabledEventPrefixes.keySet()) {
271 events.add(new String(prefix + WILDCARD));
272 }
273 events.addAll(enabledEvents.keySet());
274 return events;
275 }
276
277 private static boolean incrementEventCount(String eventName, Map<String, Integer> eventMap) {
278 synchronized (eventMap) {
279 Integer count = eventMap.get(eventName);
280 if (count == null) {
281 /* This is the first instance of this event being enabled */
282 eventMap.put(eventName, Integer.valueOf(1));
283 return true;
284 }
285 if (count.intValue() <= 0) {
286 /* It should not have been in the map in the first place! */
287 throw new IllegalStateException();
288 }
289 /* The event was already enabled, increment its refcount */
290 eventMap.put(eventName, Integer.valueOf(count.intValue() + 1));
291 return true;
292 }
293 }
294
295 private static boolean decrementEventCount(String eventName, Map<String, Integer> eventMap) {
296 synchronized (eventMap) {
297 Integer count = eventMap.get(eventName);
298 if (count == null || count.intValue() <= 0) {
299 /*
300 * The sessiond asked us to disable an event that was not
301 * enabled previously. Command error?
302 */
303 return false;
304 }
305 if (count.intValue() == 1) {
306 /*
307 * This is the last instance of this event being disabled,
308 * remove it from the map so that we stop sending it.
309 */
310 eventMap.remove(eventName);
311 return true;
312 }
313 /*
314 * Other sessions are still looking for this event, simply decrement
315 * its refcount.
316 */
317 eventMap.put(eventName, Integer.valueOf(count.intValue() - 1));
318 return true;
319 }
320 }
321}
322
This page took 0.034438 seconds and 4 git commands to generate.