Introduce a new client listener interface for the Java agent
[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
3165c2f5 31import org.lttng.ust.agent.client.ILttngTcpClientListener;
d60dfbe4
AM
32import org.lttng.ust.agent.client.LttngTcpSessiondClient;
33
34/**
35 * Base implementation of a {@link ILttngAgent}.
36 *
37 * @author Alexandre Montplaisir
38 * @param <T>
39 * The type of logging handler that should register to this agent
40 */
3165c2f5
AM
41public abstract class AbstractLttngAgent<T extends ILttngHandler>
42 implements ILttngAgent<T>, ILttngTcpClientListener {
d60dfbe4
AM
43
44 private static final String WILDCARD = "*";
45 private static final int INIT_TIMEOUT = 3; /* Seconds */
46
47 /** The handlers registered to this agent */
48 private final Set<T> registeredHandlers = new HashSet<T>();
49
50 /**
51 * The trace events currently enabled in the sessions.
52 *
53 * The key represents the event name, the value is the ref count (how many
54 * different sessions currently have this event enabled). Once the ref count
55 * falls to 0, this means we can avoid sending log events through JNI
56 * because nobody wants them.
57 *
58 * It uses a concurrent hash set", so that the {@link #isEventEnabled} and
59 * read methods do not need to take a synchronization lock.
60 */
61 private final Map<String, Integer> enabledEvents = new ConcurrentHashMap<String, Integer>();
62
63 /**
64 * The trace events prefixes currently enabled in the sessions, which means
65 * the event names finishing in *, like "abcd*". We track them separately
66 * from the standard event names, so that we can use {@link String#equals}
67 * and {@link String#startsWith} appropriately.
68 *
69 * We track the lone wildcard "*" separately, in {@link #enabledWildcards}.
70 */
71 private final NavigableMap<String, Integer> enabledEventPrefixes =
72 new ConcurrentSkipListMap<String, Integer>();
73
74 /** Number of sessions currently enabling the wildcard "*" event */
75 private final AtomicInteger enabledWildcards = new AtomicInteger(0);
76
77 /** Tracing domain. Defined by the sub-classes via the constructor. */
78 private final Domain domain;
79
80 /* Lazy-loaded sessiond clients and their thread objects */
81 private LttngTcpSessiondClient rootSessiondClient = null;
82 private LttngTcpSessiondClient userSessiondClient = null;
83 private Thread rootSessiondClientThread = null;
84 private Thread userSessiondClientThread = null;
85
86 /** Indicates if this agent has been initialized. */
87 private boolean initialized = false;
88
89 /**
90 * Constructor. Should only be called by sub-classes via super(...);
91 *
92 * @param domain
93 * The tracing domain of this agent.
94 */
95 protected AbstractLttngAgent(Domain domain) {
96 this.domain = domain;
97 }
98
99 @Override
100 public Domain getDomain() {
101 return domain;
102 }
103
104 @Override
105 public void registerHandler(T handler) {
106 synchronized (registeredHandlers) {
107 if (registeredHandlers.isEmpty()) {
108 /*
109 * This is the first handler that registers, we will initialize
110 * the agent.
111 */
112 init();
113 }
114 registeredHandlers.add(handler);
115 }
116 }
117
118 @Override
119 public void unregisterHandler(T handler) {
120 synchronized (registeredHandlers) {
121 registeredHandlers.remove(handler);
122 if (registeredHandlers.isEmpty()) {
123 /* There are no more registered handlers, close the connection. */
124 dispose();
125 }
126 }
127 }
128
129 private void init() {
130 /*
131 * Only called from a synchronized (registeredHandlers) block, should
132 * not need additional synchronization.
133 */
134 if (initialized) {
135 return;
136 }
137 String rootClientThreadName = "Root sessiond client started by agent: " + this.getClass().getSimpleName();
138
3165c2f5 139 rootSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), true);
d60dfbe4
AM
140 rootSessiondClientThread = new Thread(rootSessiondClient, rootClientThreadName);
141 rootSessiondClientThread.setDaemon(true);
142 rootSessiondClientThread.start();
143
144 String userClientThreadName = "User sessiond client started by agent: " + this.getClass().getSimpleName();
145
3165c2f5 146 userSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), false);
d60dfbe4
AM
147 userSessiondClientThread = new Thread(userSessiondClient, userClientThreadName);
148 userSessiondClientThread.setDaemon(true);
149 userSessiondClientThread.start();
150
151 /* Give the threads' registration a chance to end. */
152 if (!rootSessiondClient.waitForConnection(INIT_TIMEOUT)) {
153 userSessiondClient.waitForConnection(INIT_TIMEOUT);
154 }
155
156 initialized = true;
157 }
158
159 /**
160 * Dispose the agent
161 */
162 private void dispose() {
163 /*
164 * Only called from a synchronized (registeredHandlers) block, should
165 * not need additional synchronization.
166 */
167 rootSessiondClient.close();
168 userSessiondClient.close();
169
170 try {
171 rootSessiondClientThread.join();
172 userSessiondClientThread.join();
173
174 } catch (InterruptedException e) {
175 e.printStackTrace();
176 }
177 rootSessiondClient = null;
178 rootSessiondClientThread = null;
179 userSessiondClient = null;
180 userSessiondClientThread = null;
181
182 /* Reset all enabled event counts to 0 */
183 enabledEvents.clear();
184 enabledEventPrefixes.clear();
185 enabledWildcards.set(0);
186
187 initialized = false;
188
189 }
190
3165c2f5 191 @Override
d60dfbe4
AM
192 public boolean eventEnabled(String eventName) {
193 if (eventName.equals(WILDCARD)) {
194 enabledWildcards.incrementAndGet();
195 return true;
196 }
197
198 if (eventName.endsWith(WILDCARD)) {
199 /* Strip the "*" from the name. */
200 String prefix = eventName.substring(0, eventName.length() - 1);
201 return incrementEventCount(prefix, enabledEventPrefixes);
202 }
203
204 return incrementEventCount(eventName, enabledEvents);
205 }
206
3165c2f5 207 @Override
d60dfbe4
AM
208 public boolean eventDisabled(String eventName) {
209 if (eventName.equals(WILDCARD)) {
210 int newCount = enabledWildcards.decrementAndGet();
211 if (newCount < 0) {
212 /* Event was not enabled, bring the count back to 0 */
213 enabledWildcards.incrementAndGet();
214 return false;
215 }
cbaa5167 216 return true;
d60dfbe4
AM
217 }
218
219 if (eventName.endsWith(WILDCARD)) {
220 /* Strip the "*" from the name. */
221 String prefix = eventName.substring(0, eventName.length() - 1);
222 return decrementEventCount(prefix, enabledEventPrefixes);
223 }
224
225 return decrementEventCount(eventName, enabledEvents);
226 }
227
3165c2f5
AM
228 @Override
229 public Iterable<String> listEnabledEvents() {
230 List<String> events = new LinkedList<String>();
231
232 if (enabledWildcards.get() > 0) {
233 events.add(WILDCARD);
234 }
235 for (String prefix : enabledEventPrefixes.keySet()) {
236 events.add(new String(prefix + WILDCARD));
237 }
238 events.addAll(enabledEvents.keySet());
239 return events;
240 }
241
d60dfbe4
AM
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
d60dfbe4
AM
263 private static boolean incrementEventCount(String eventName, Map<String, Integer> eventMap) {
264 synchronized (eventMap) {
265 Integer count = eventMap.get(eventName);
266 if (count == null) {
267 /* This is the first instance of this event being enabled */
268 eventMap.put(eventName, Integer.valueOf(1));
269 return true;
270 }
271 if (count.intValue() <= 0) {
272 /* It should not have been in the map in the first place! */
273 throw new IllegalStateException();
274 }
275 /* The event was already enabled, increment its refcount */
276 eventMap.put(eventName, Integer.valueOf(count.intValue() + 1));
277 return true;
278 }
279 }
280
281 private static boolean decrementEventCount(String eventName, Map<String, Integer> eventMap) {
282 synchronized (eventMap) {
283 Integer count = eventMap.get(eventName);
284 if (count == null || count.intValue() <= 0) {
285 /*
286 * The sessiond asked us to disable an event that was not
287 * enabled previously. Command error?
288 */
289 return false;
290 }
291 if (count.intValue() == 1) {
292 /*
293 * This is the last instance of this event being disabled,
294 * remove it from the map so that we stop sending it.
295 */
296 eventMap.remove(eventName);
297 return true;
298 }
299 /*
300 * Other sessions are still looking for this event, simply decrement
301 * its refcount.
302 */
303 eventMap.put(eventName, Integer.valueOf(count.intValue() - 1));
304 return true;
305 }
306 }
307}
308
This page took 0.046492 seconds and 4 git commands to generate.