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