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; | |
32 | import java.util.List; | |
33 | import java.util.Timer; | |
34 | import java.util.TimerTask; | |
35 | ||
36 | class USTRegisterMsg { | |
37 | public static int pid; | |
38 | } | |
39 | ||
40 | public class LTTngTCPSessiondClient { | |
41 | /* Command header from the session deamon. */ | |
42 | private LTTngSessiondCmd2_4.sessiond_hdr headerCmd = | |
43 | new LTTngSessiondCmd2_4.sessiond_hdr(); | |
44 | ||
45 | private final String sessiondHost; | |
46 | private final int sessiondPort; | |
47 | private Socket sessiondSock; | |
48 | private boolean quit = false; | |
49 | ||
50 | private DataInputStream inFromSessiond; | |
51 | private DataOutputStream outToSessiond; | |
52 | ||
53 | private LTTngLogHandler handler; | |
54 | ||
55 | private Semaphore registerSem; | |
56 | ||
57 | private Timer eventTimer; | |
58 | private List<String> enabledEventList = new ArrayList<String>(); | |
59 | /* Timer delay at each 5 seconds. */ | |
60 | private final static long timerDelay = 5 * 1000; | |
61 | private static boolean timerInitialized; | |
62 | ||
63 | public LTTngTCPSessiondClient(String host, int port, Semaphore sem) { | |
64 | this.sessiondHost = host; | |
65 | this.sessiondPort = port; | |
66 | this.registerSem = sem; | |
67 | this.eventTimer = new Timer(); | |
68 | this.timerInitialized = false; | |
69 | } | |
70 | ||
71 | private void setupEventTimer() { | |
72 | if (this.timerInitialized) { | |
73 | return; | |
74 | } | |
75 | ||
76 | this.eventTimer.scheduleAtFixedRate(new TimerTask() { | |
77 | @Override | |
78 | public void run() { | |
79 | /* | |
80 | * We have to make a copy here since it is possible that the | |
81 | * enabled event list is changed during an iteration on it. | |
82 | */ | |
83 | List<String> tmpList = new ArrayList<String>(enabledEventList); | |
84 | ||
85 | LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = new | |
86 | LTTngSessiondCmd2_4.sessiond_enable_handler(); | |
87 | for (String strEventName: tmpList) { | |
88 | enableCmd.name = strEventName; | |
89 | if (enableCmd.execute(handler) == null) { | |
90 | enabledEventList.remove(strEventName); | |
91 | } | |
92 | } | |
93 | } | |
94 | }, this.timerDelay, this.timerDelay); | |
95 | ||
96 | this.timerInitialized = true; | |
97 | } | |
98 | ||
99 | public void init(LTTngLogHandler handler) throws InterruptedException { | |
100 | this.handler = handler; | |
101 | ||
102 | for (;;) { | |
103 | if (this.quit) { | |
104 | break; | |
105 | } | |
106 | ||
107 | try { | |
108 | ||
109 | /* | |
110 | * Connect to the session daemon before anything else. | |
111 | */ | |
112 | connectToSessiond(); | |
113 | ||
114 | /* | |
115 | * Register to the session daemon as the Java component of the | |
116 | * UST application. | |
117 | */ | |
118 | registerToSessiond(); | |
119 | this.registerSem.release(); | |
120 | ||
121 | setupEventTimer(); | |
122 | ||
123 | /* | |
124 | * Block on socket receive and wait for command from the | |
125 | * session daemon. This will return if and only if there is a | |
126 | * fatal error or the socket closes. | |
127 | */ | |
128 | handleSessiondCmd(); | |
129 | } catch (UnknownHostException uhe) { | |
130 | this.registerSem.release(); | |
131 | System.out.println(uhe); | |
132 | } catch (IOException ioe) { | |
133 | this.registerSem.release(); | |
134 | Thread.sleep(3000); | |
135 | } catch (Exception e) { | |
136 | this.registerSem.release(); | |
137 | e.printStackTrace(); | |
138 | } | |
139 | } | |
140 | } | |
141 | ||
142 | public void destroy() { | |
143 | this.quit = true; | |
144 | this.eventTimer.cancel(); | |
145 | ||
146 | try { | |
147 | if (this.sessiondSock != null) { | |
148 | this.sessiondSock.close(); | |
149 | } | |
150 | } catch (Exception e) { | |
151 | e.printStackTrace(); | |
152 | } | |
153 | } | |
154 | ||
155 | /* | |
156 | * Receive header data from the session daemon using the LTTng command | |
157 | * static buffer of the right size. | |
158 | */ | |
159 | private void recvHeader() throws Exception { | |
160 | int read_len; | |
161 | byte data[] = new byte[this.headerCmd.SIZE]; | |
162 | ||
163 | read_len = this.inFromSessiond.read(data, 0, data.length); | |
164 | if (read_len != data.length) { | |
165 | throw new IOException(); | |
166 | } | |
167 | this.headerCmd.populate(data); | |
168 | } | |
169 | ||
170 | /* | |
171 | * Receive payload from the session daemon. This MUST be done after a | |
172 | * recvHeader() so the header value of a command are known. | |
173 | * | |
174 | * The caller SHOULD use isPayload() before which returns true if a payload | |
175 | * is expected after the header. | |
176 | */ | |
177 | private byte[] recvPayload() throws Exception { | |
178 | byte payload[] = new byte[(int) this.headerCmd.data_size]; | |
179 | ||
180 | /* Failsafe check so we don't waste our time reading 0 bytes. */ | |
181 | if (payload.length == 0) { | |
182 | return null; | |
183 | } | |
184 | ||
185 | this.inFromSessiond.read(payload, 0, payload.length); | |
186 | return payload; | |
187 | } | |
188 | ||
189 | /* | |
190 | * Handle session command from the session daemon. | |
191 | */ | |
192 | private void handleSessiondCmd() throws Exception { | |
193 | int ret_code; | |
194 | byte data[] = null; | |
195 | ||
196 | while (true) { | |
197 | /* Get header from session daemon. */ | |
198 | recvHeader(); | |
199 | ||
200 | if (headerCmd.data_size > 0) { | |
201 | data = recvPayload(); | |
202 | } | |
203 | ||
204 | switch (headerCmd.cmd) { | |
205 | case CMD_LIST: | |
206 | { | |
207 | LTTngSessiondCmd2_4.sessiond_list_logger listLoggerCmd = | |
208 | new LTTngSessiondCmd2_4.sessiond_list_logger(); | |
209 | listLoggerCmd.execute(this.handler); | |
210 | data = listLoggerCmd.getBytes(); | |
211 | break; | |
212 | } | |
213 | case CMD_ENABLE: | |
214 | { | |
215 | String event_name; | |
216 | LTTngSessiondCmd2_4.sessiond_enable_handler enableCmd = | |
217 | new LTTngSessiondCmd2_4.sessiond_enable_handler(); | |
218 | if (data == null) { | |
219 | enableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD; | |
220 | break; | |
221 | } | |
222 | enableCmd.populate(data); | |
223 | event_name = enableCmd.execute(this.handler); | |
224 | if (event_name != null) { | |
225 | /* | |
226 | * Add the event to the list so it can be enabled if | |
227 | * the logger appears at some point in time. | |
228 | */ | |
229 | enabledEventList.add(event_name); | |
230 | } | |
231 | data = enableCmd.getBytes(); | |
232 | break; | |
233 | } | |
234 | case CMD_DISABLE: | |
235 | { | |
236 | LTTngSessiondCmd2_4.sessiond_disable_handler disableCmd = | |
237 | new LTTngSessiondCmd2_4.sessiond_disable_handler(); | |
238 | if (data == null) { | |
239 | disableCmd.code = LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD; | |
240 | break; | |
241 | } | |
242 | disableCmd.populate(data); | |
243 | disableCmd.execute(this.handler); | |
244 | data = disableCmd.getBytes(); | |
245 | break; | |
246 | } | |
247 | default: | |
248 | { | |
249 | data = new byte[4]; | |
250 | ByteBuffer buf = ByteBuffer.wrap(data); | |
251 | buf.order(ByteOrder.BIG_ENDIAN); | |
252 | LTTngSessiondCmd2_4.lttng_jul_ret_code code = | |
253 | LTTngSessiondCmd2_4.lttng_jul_ret_code.CODE_INVALID_CMD; | |
254 | buf.putInt(code.getCode()); | |
255 | break; | |
256 | } | |
257 | } | |
258 | ||
259 | /* Send payload to session daemon. */ | |
260 | this.outToSessiond.write(data, 0, data.length); | |
261 | this.outToSessiond.flush(); | |
262 | } | |
263 | } | |
264 | ||
265 | private void connectToSessiond() throws Exception { | |
266 | this.sessiondSock = new Socket(this.sessiondHost, this.sessiondPort); | |
267 | this.inFromSessiond = new DataInputStream( | |
268 | sessiondSock.getInputStream()); | |
269 | this.outToSessiond = new DataOutputStream( | |
270 | sessiondSock.getOutputStream()); | |
271 | } | |
272 | ||
273 | private void registerToSessiond() throws Exception { | |
274 | byte data[] = new byte[4]; | |
275 | ByteBuffer buf = ByteBuffer.wrap(data); | |
276 | String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; | |
277 | ||
278 | buf.putInt(Integer.parseInt(pid)); | |
279 | this.outToSessiond.write(data, 0, data.length); | |
280 | this.outToSessiond.flush(); | |
281 | } | |
282 | } |