Turn ISessiondCommand 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
68a1ef73 21import java.util.Collection;
d60dfbe4 22import java.util.HashSet;
d60dfbe4
AM
23import java.util.Map;
24import java.util.NavigableMap;
25import java.util.Set;
26import java.util.concurrent.ConcurrentHashMap;
27import java.util.concurrent.ConcurrentSkipListMap;
28import java.util.concurrent.atomic.AtomicInteger;
29
3165c2f5 30import org.lttng.ust.agent.client.ILttngTcpClientListener;
d60dfbe4 31import org.lttng.ust.agent.client.LttngTcpSessiondClient;
4fb826b1 32import org.lttng.ust.agent.filter.FilterChangeNotifier;
3daf5cba 33import org.lttng.ust.agent.session.EventRule;
d60dfbe4
AM
34
35/**
36 * Base implementation of a {@link ILttngAgent}.
37 *
38 * @author Alexandre Montplaisir
39 * @param <T>
40 * The type of logging handler that should register to this agent
41 */
3165c2f5
AM
42public abstract class AbstractLttngAgent<T extends ILttngHandler>
43 implements ILttngAgent<T>, ILttngTcpClientListener {
d60dfbe4
AM
44
45 private static final String WILDCARD = "*";
46 private static final int INIT_TIMEOUT = 3; /* Seconds */
47
48 /** The handlers registered to this agent */
49 private final Set<T> registeredHandlers = new HashSet<T>();
50
51 /**
52 * The trace events currently enabled in the sessions.
53 *
54 * The key represents the event name, the value is the ref count (how many
55 * different sessions currently have this event enabled). Once the ref count
56 * falls to 0, this means we can avoid sending log events through JNI
57 * because nobody wants them.
58 *
3daf5cba 59 * It uses a concurrent hash map, so that the {@link #isEventEnabled} and
d60dfbe4
AM
60 * read methods do not need to take a synchronization lock.
61 */
62 private final Map<String, Integer> enabledEvents = new ConcurrentHashMap<String, Integer>();
63
64 /**
65 * The trace events prefixes currently enabled in the sessions, which means
66 * the event names finishing in *, like "abcd*". We track them separately
67 * from the standard event names, so that we can use {@link String#equals}
68 * and {@link String#startsWith} appropriately.
69 *
70 * We track the lone wildcard "*" separately, in {@link #enabledWildcards}.
71 */
72 private final NavigableMap<String, Integer> enabledEventPrefixes =
73 new ConcurrentSkipListMap<String, Integer>();
74
75 /** Number of sessions currently enabling the wildcard "*" event */
76 private final AtomicInteger enabledWildcards = new AtomicInteger(0);
77
78 /** Tracing domain. Defined by the sub-classes via the constructor. */
79 private final Domain domain;
80
81 /* Lazy-loaded sessiond clients and their thread objects */
82 private LttngTcpSessiondClient rootSessiondClient = null;
83 private LttngTcpSessiondClient userSessiondClient = null;
84 private Thread rootSessiondClientThread = null;
85 private Thread userSessiondClientThread = null;
86
87 /** Indicates if this agent has been initialized. */
88 private boolean initialized = false;
89
90 /**
91 * Constructor. Should only be called by sub-classes via super(...);
92 *
93 * @param domain
94 * The tracing domain of this agent.
95 */
96 protected AbstractLttngAgent(Domain domain) {
97 this.domain = domain;
98 }
99
100 @Override
101 public Domain getDomain() {
102 return domain;
103 }
104
105 @Override
106 public void registerHandler(T handler) {
107 synchronized (registeredHandlers) {
108 if (registeredHandlers.isEmpty()) {
109 /*
110 * This is the first handler that registers, we will initialize
111 * the agent.
112 */
113 init();
114 }
115 registeredHandlers.add(handler);
116 }
117 }
118
119 @Override
120 public void unregisterHandler(T handler) {
121 synchronized (registeredHandlers) {
122 registeredHandlers.remove(handler);
123 if (registeredHandlers.isEmpty()) {
124 /* There are no more registered handlers, close the connection. */
125 dispose();
126 }
127 }
128 }
129
130 private void init() {
131 /*
132 * Only called from a synchronized (registeredHandlers) block, should
133 * not need additional synchronization.
134 */
135 if (initialized) {
136 return;
137 }
138 String rootClientThreadName = "Root sessiond client started by agent: " + this.getClass().getSimpleName();
139
3165c2f5 140 rootSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), true);
d60dfbe4
AM
141 rootSessiondClientThread = new Thread(rootSessiondClient, rootClientThreadName);
142 rootSessiondClientThread.setDaemon(true);
143 rootSessiondClientThread.start();
144
145 String userClientThreadName = "User sessiond client started by agent: " + this.getClass().getSimpleName();
146
3165c2f5 147 userSessiondClient = new LttngTcpSessiondClient(this, getDomain().value(), false);
d60dfbe4
AM
148 userSessiondClientThread = new Thread(userSessiondClient, userClientThreadName);
149 userSessiondClientThread.setDaemon(true);
150 userSessiondClientThread.start();
151
152 /* Give the threads' registration a chance to end. */
153 if (!rootSessiondClient.waitForConnection(INIT_TIMEOUT)) {
154 userSessiondClient.waitForConnection(INIT_TIMEOUT);
155 }
156
157 initialized = true;
158 }
159
160 /**
161 * Dispose the agent
162 */
163 private void dispose() {
164 /*
165 * Only called from a synchronized (registeredHandlers) block, should
166 * not need additional synchronization.
167 */
168 rootSessiondClient.close();
169 userSessiondClient.close();
170
171 try {
172 rootSessiondClientThread.join();
173 userSessiondClientThread.join();
174
175 } catch (InterruptedException e) {
176 e.printStackTrace();
177 }
178 rootSessiondClient = null;
179 rootSessiondClientThread = null;
180 userSessiondClient = null;
181 userSessiondClientThread = null;
182
183 /* Reset all enabled event counts to 0 */
184 enabledEvents.clear();
185 enabledEventPrefixes.clear();
186 enabledWildcards.set(0);
187
188 initialized = false;
189
190 }
191
3165c2f5 192 @Override
3daf5cba 193 public boolean eventEnabled(EventRule eventRule) {
4fb826b1
AM
194 /* Notify the filter change manager of the command */
195 FilterChangeNotifier.getInstance().addEventRule(eventRule);
196
3daf5cba
AM
197 String eventName = eventRule.getEventName();
198
d60dfbe4
AM
199 if (eventName.equals(WILDCARD)) {
200 enabledWildcards.incrementAndGet();
201 return true;
202 }
d60dfbe4
AM
203 if (eventName.endsWith(WILDCARD)) {
204 /* Strip the "*" from the name. */
205 String prefix = eventName.substring(0, eventName.length() - 1);
206 return incrementEventCount(prefix, enabledEventPrefixes);
207 }
208
209 return incrementEventCount(eventName, enabledEvents);
210 }
211
3165c2f5 212 @Override
d60dfbe4 213 public boolean eventDisabled(String eventName) {
4fb826b1
AM
214 /* Notify the filter change manager of the command */
215 FilterChangeNotifier.getInstance().removeEventRules(eventName);
216
d60dfbe4
AM
217 if (eventName.equals(WILDCARD)) {
218 int newCount = enabledWildcards.decrementAndGet();
219 if (newCount < 0) {
220 /* Event was not enabled, bring the count back to 0 */
221 enabledWildcards.incrementAndGet();
222 return false;
223 }
cbaa5167 224 return true;
d60dfbe4
AM
225 }
226
227 if (eventName.endsWith(WILDCARD)) {
228 /* Strip the "*" from the name. */
229 String prefix = eventName.substring(0, eventName.length() - 1);
230 return decrementEventCount(prefix, enabledEventPrefixes);
231 }
232
233 return decrementEventCount(eventName, enabledEvents);
234 }
235
68a1ef73
AM
236 /*
237 * Implementation of this method is domain-specific.
238 */
3165c2f5 239 @Override
68a1ef73 240 public abstract Collection<String> listAvailableEvents();
3165c2f5 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.03579 seconds and 4 git commands to generate.