Commit | Line | Data |
---|---|---|
43e5396b DG |
1 | /* |
2 | * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com> | |
3 | * | |
4 | * This library is free software; you can redistribute it and/or modify it | |
5 | * under the terms of the GNU Lesser General Public License, version 2.1 only, | |
6 | * as published by the Free Software Foundation. | |
7 | * | |
8 | * This library is distributed in the hope that it will be useful, but WITHOUT | |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License | |
11 | * for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU Lesser General Public License | |
14 | * along with this library; if not, write to the Free Software Foundation, | |
15 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
16 | */ | |
17 | ||
18 | package org.lttng.ust.jul; | |
19 | ||
20 | import java.util.concurrent.Semaphore; | |
21 | import java.nio.ByteBuffer; | |
22 | import java.nio.ByteOrder; | |
23 | import java.lang.Integer; | |
24 | import java.io.IOException; | |
25 | import java.io.BufferedOutputStream; | |
26 | import java.io.ByteArrayOutputStream; | |
27 | import java.io.DataOutputStream; | |
28 | import java.io.DataInputStream; | |
29 | import java.net.*; | |
30 | import java.lang.management.ManagementFactory; | |
31 | import java.util.ArrayList; | |
529e6def | 32 | import java.util.HashMap; |
43e5396b DG |
33 | import java.util.List; |
34 | import java.util.Timer; | |
35 | import java.util.TimerTask; | |
529e6def | 36 | import java.util.logging.Logger; |
43e5396b DG |
37 | |
38 | class USTRegisterMsg { | |
39 | public static int pid; | |
40 | } | |
41 | ||
42 | public class LTTngTCPSessiondClient { | |
43 | /* Command header from the session deamon. */ | |
44 | private LTTngSessiondCmd2_4.sessiond_hdr headerCmd = | |
45 | new LTTngSessiondCmd2_4.sessiond_hdr(); | |
46 | ||
47 | private final String sessiondHost; | |
48 | private final int sessiondPort; | |
49 | private Socket sessiondSock; | |
50 | private boolean quit = false; | |
51 | ||
52 | private DataInputStream inFromSessiond; | |
53 | private DataOutputStream outToSessiond; | |
54 | ||
55 | private LTTngLogHandler handler; | |
56 | ||
57 | private Semaphore registerSem; | |
58 | ||
59 | private Timer eventTimer; | |
60 | private List<String> enabledEventList = new ArrayList<String>(); | |
529e6def DG |
61 | /* |
62 | * Map of Logger objects that have been enabled. They are indexed by name. | |
63 | */ | |
64 | private HashMap<String, Logger> enabledLoggers = new HashMap<String, Logger>(); | |
43e5396b DG |
65 | /* Timer delay at each 5 seconds. */ |
66 | private final static long timerDelay = 5 * 1000; | |
67 | private static boolean timerInitialized; | |
68 | ||
69 | public LTTngTCPSessiondClient(String host, int port, Semaphore sem) { | |
70 | this.sessiondHost = host; | |
71 | this.sessiondPort = port; | |
72 | this.registerSem = sem; | |
73 | this.eventTimer = new Timer(); | |
74 | this.timerInitialized = false; | |
75 | } | |
76 | ||
77 | private void setupEventTimer() { | |
78 | if (this.timerInitialized) { | |
79 | return; | |
80 | } | |
81 | ||
82 | this.eventTimer.scheduleAtFixedRate(new TimerTask() { | |
83 | @Override | |
84 | public void run() { | |
85 | /* | |
86 | * We have to make a copy here since it is possible that the | |
87 | * enabled event list is changed during an iteration on it. | |
88 | */ | |
89 | List<String> tmpList = new ArrayList<String>(enabledEventList); | |
90 | ||
91 | LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = new | |
92 | LTTngSessiondCmd2_4.sessiond_enable_handler(); | |
93 | for (String strEventName: tmpList) { | |
529e6def DG |
94 | /* |
95 | * Check if this Logger name has been enabled already. Note | |
96 | * that in the case of "*", it's never added in that hash | |
97 | * table thus the enable command does a lookup for each | |
98 | * logger name in that hash table for the * case in order | |
99 | * to make sure we don't enable twice the same logger | |
100 | * because JUL apparently accepts that the *same* | |
101 | * LogHandler can be added twice on a Logger object... | |
102 | * don't ask... | |
103 | */ | |
104 | if (enabledLoggers.get(strEventName) != null) { | |
105 | continue; | |
106 | } | |
107 | ||
43e5396b | 108 | enableCmd.name = strEventName; |
529e6def | 109 | if (enableCmd.execute(handler, enabledLoggers) == null) { |
43e5396b DG |
110 | enabledEventList.remove(strEventName); |
111 | } | |
112 | } | |
113 | } | |
114 | }, this.timerDelay, this.timerDelay); | |
115 | ||
116 | this.timerInitialized = true; | |
117 | } | |
118 | ||
119 | public void init(LTTngLogHandler handler) throws InterruptedException { | |
120 | this.handler = handler; | |
121 | ||
122 | for (;;) { | |
123 | if (this.quit) { | |
124 | break; | |
125 | } | |
126 | ||
127 | try { | |
128 | ||
129 | /* | |
130 | * Connect to the session daemon before anything else. | |
131 | */ | |
132 | connectToSessiond(); | |
133 | ||
134 | /* | |
135 | * Register to the session daemon as the Java component of the | |
136 | * UST application. | |
137 | */ | |
138 | registerToSessiond(); | |
139 | this.registerSem.release(); | |
140 | ||
141 | setupEventTimer(); | |
142 | ||
143 | /* | |
144 | * Block on socket receive and wait for command from the | |
145 | * session daemon. This will return if and only if there is a | |
146 | * fatal error or the socket closes. | |
147 | */ | |
148 | handleSessiondCmd(); | |
149 | } catch (UnknownHostException uhe) { | |
150 | this.registerSem.release(); | |
151 | System.out.println(uhe); | |
152 | } catch (IOException ioe) { | |
153 | this.registerSem.release(); | |
154 | Thread.sleep(3000); | |
155 | } catch (Exception e) { | |
156 | this.registerSem.release(); | |
157 | e.printStackTrace(); | |
158 | } | |
159 | } | |
160 | } | |
161 | ||
162 | public void destroy() { | |
163 | this.quit = true; | |
164 | this.eventTimer.cancel(); | |
165 | ||
166 | try { | |
167 | if (this.sessiondSock != null) { | |
168 | this.sessiondSock.close(); | |
169 | } | |
170 | } catch (Exception e) { | |
171 | e.printStackTrace(); | |
172 | } | |
173 | } | |
174 | ||
175 | /* | |
176 | * Receive header data from the session daemon using the LTTng command | |
177 | * static buffer of the right size. | |
178 | */ | |
179 | private void recvHeader() throws Exception { | |
180 | int read_len; | |
181 | byte data[] = new byte[this.headerCmd.SIZE]; | |
182 | ||
183 | read_len = this.inFromSessiond.read(data, 0, data.length); | |
184 | if (read_len != data.length) { | |
185 | throw new IOException(); | |
186 | } | |
187 | this.headerCmd.populate(data); | |
188 | } | |
189 | ||
190 | /* | |
191 | * Receive payload from the session daemon. This MUST be done after a | |
192 | * recvHeader() so the header value of a command are known. | |
193 | * | |
194 | * The caller SHOULD use isPayload() before which returns true if a payload | |
195 | * is expected after the header. | |
196 | */ | |
197 | private byte[] recvPayload() throws Exception { | |
198 | byte payload[] = new byte[(int) this.headerCmd.data_size]; | |
199 | ||
200 | /* Failsafe check so we don't waste our time reading 0 bytes. */ | |
201 | if (payload.length == 0) { | |
202 | return null; | |
203 | } | |
204 | ||
205 | this.inFromSessiond.read(payload, 0, payload.length); | |
206 | return payload; | |
207 | } | |
208 | ||
209 | /* | |
210 | * Handle session command from the session daemon. | |
211 | */ | |
212 | private void handleSessiondCmd() throws Exception { | |
213 | int ret_code; | |
214 | byte data[] = null; | |
215 | ||
216 | while (true) { | |
217 | /* Get header from session daemon. */ | |
218 | recvHeader(); | |
219 | ||
220 | if (headerCmd.data_size > 0) { | |
221 | data = recvPayload(); | |
222 | } | |
223 | ||
224 | switch (headerCmd.cmd) { | |
225 | case CMD_LIST: | |
226 | { | |
227 | LTTngSessiondCmd2_4.sessiond_list_logger listLoggerCmd = | |
228 | new LTTngSessiondCmd2_4.sessiond_list_logger(); | |
229 | listLoggerCmd.execute(this.handler); | |
230 | data = listLoggerCmd.getBytes(); | |
231 | break; | |
232 | } | |
233 | case CMD_ENABLE: | |
234 | { | |
235 | String event_name; | |
236 | LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = | |
237 | new LTTngSessiondCmd2_4.sessiond_enable_handler(); | |
238 | if (data == null) { | |
239 | enableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD; | |
240 | break; | |
241 | } | |
242 | enableCmd.populate(data); | |
529e6def | 243 | event_name = enableCmd.execute(this.handler, this.enabledLoggers); |
43e5396b DG |
244 | if (event_name != null) { |
245 | /* | |
246 | * Add the event to the list so it can be enabled if | |
247 | * the logger appears at some point in time. | |
248 | */ | |
529e6def DG |
249 | if (enabledEventList.contains(event_name) == false) { |
250 | enabledEventList.add(event_name); | |
251 | } | |
43e5396b DG |
252 | } |
253 | data = enableCmd.getBytes(); | |
254 | break; | |
255 | } | |
256 | case CMD_DISABLE: | |
257 | { | |
258 | LTTngSessiondCmd2_4.sessiond_disable_handler disableCmd = | |
259 | new LTTngSessiondCmd2_4.sessiond_disable_handler(); | |
260 | if (data == null) { | |
261 | disableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD; | |
262 | break; | |
263 | } | |
264 | disableCmd.populate(data); | |
265 | disableCmd.execute(this.handler); | |
266 | data = disableCmd.getBytes(); | |
267 | break; | |
268 | } | |
269 | default: | |
270 | { | |
271 | data = new byte[4]; | |
272 | ByteBuffer buf = ByteBuffer.wrap(data); | |
273 | buf.order(ByteOrder.BIG_ENDIAN); | |
274 | LTTngSessiondCmd2_4.lttng_jul_ret_code code = | |
275 | LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD; | |
276 | buf.putInt(code.getCode()); | |
277 | break; | |
278 | } | |
279 | } | |
280 | ||
281 | /* Send payload to session daemon. */ | |
282 | this.outToSessiond.write(data, 0, data.length); | |
283 | this.outToSessiond.flush(); | |
284 | } | |
285 | } | |
286 | ||
287 | private void connectToSessiond() throws Exception { | |
288 | this.sessiondSock = new Socket(this.sessiondHost, this.sessiondPort); | |
289 | this.inFromSessiond = new DataInputStream( | |
290 | sessiondSock.getInputStream()); | |
291 | this.outToSessiond = new DataOutputStream( | |
292 | sessiondSock.getOutputStream()); | |
293 | } | |
294 | ||
295 | private void registerToSessiond() throws Exception { | |
296 | byte data[] = new byte[4]; | |
297 | ByteBuffer buf = ByteBuffer.wrap(data); | |
298 | String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; | |
299 | ||
300 | buf.putInt(Integer.parseInt(pid)); | |
301 | this.outToSessiond.write(data, 0, data.length); | |
302 | this.outToSessiond.flush(); | |
303 | } | |
304 | } |