2 * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
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.
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
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
18 package org
.lttng
.ust
.jul
;
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
;
30 import java
.lang
.management
.ManagementFactory
;
31 import java
.util
.ArrayList
;
32 import java
.util
.HashMap
;
33 import java
.util
.List
;
34 import java
.util
.Timer
;
35 import java
.util
.TimerTask
;
36 import java
.util
.logging
.Logger
;
38 class USTRegisterMsg
{
39 public static int pid
;
42 public class LTTngTCPSessiondClient
{
43 /* Command header from the session deamon. */
44 private LTTngSessiondCmd2_4
.sessiond_hdr headerCmd
=
45 new LTTngSessiondCmd2_4
.sessiond_hdr();
47 private final String sessiondHost
;
48 private final int sessiondPort
;
49 private Socket sessiondSock
;
50 private boolean quit
= false;
52 private DataInputStream inFromSessiond
;
53 private DataOutputStream outToSessiond
;
55 private LTTngLogHandler handler
;
57 private Semaphore registerSem
;
59 private Timer eventTimer
;
60 private List
<String
> enabledEventList
= new ArrayList
<String
>();
62 * Map of Logger objects that have been enabled. They are indexed by name.
64 private HashMap
<String
, Logger
> enabledLoggers
= new HashMap
<String
, Logger
>();
65 /* Timer delay at each 5 seconds. */
66 private final static long timerDelay
= 5 * 1000;
67 private static boolean timerInitialized
;
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;
77 private void setupEventTimer() {
78 if (this.timerInitialized
) {
82 this.eventTimer
.scheduleAtFixedRate(new TimerTask() {
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.
89 List
<String
> tmpList
= new ArrayList
<String
>(enabledEventList
);
91 LTTngSessiondCmd2_4
.sessiond_enable_handler enableCmd
= new
92 LTTngSessiondCmd2_4
.sessiond_enable_handler();
93 for (String strEventName
: tmpList
) {
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...
104 if (enabledLoggers
.get(strEventName
) != null) {
108 enableCmd
.name
= strEventName
;
109 if (enableCmd
.execute(handler
, enabledLoggers
) == null) {
110 enabledEventList
.remove(strEventName
);
114 }, this.timerDelay
, this.timerDelay
);
116 this.timerInitialized
= true;
119 public void init(LTTngLogHandler handler
) throws InterruptedException
{
120 this.handler
= handler
;
130 * Connect to the session daemon before anything else.
135 * Register to the session daemon as the Java component of the
138 registerToSessiond();
139 this.registerSem
.release();
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.
149 } catch (UnknownHostException uhe
) {
150 this.registerSem
.release();
151 System
.out
.println(uhe
);
152 } catch (IOException ioe
) {
153 this.registerSem
.release();
155 } catch (Exception e
) {
156 this.registerSem
.release();
162 public void destroy() {
164 this.eventTimer
.cancel();
167 if (this.sessiondSock
!= null) {
168 this.sessiondSock
.close();
170 } catch (Exception e
) {
176 * Receive header data from the session daemon using the LTTng command
177 * static buffer of the right size.
179 private void recvHeader() throws Exception
{
181 byte data
[] = new byte[this.headerCmd
.SIZE
];
183 read_len
= this.inFromSessiond
.read(data
, 0, data
.length
);
184 if (read_len
!= data
.length
) {
185 throw new IOException();
187 this.headerCmd
.populate(data
);
191 * Receive payload from the session daemon. This MUST be done after a
192 * recvHeader() so the header value of a command are known.
194 * The caller SHOULD use isPayload() before which returns true if a payload
195 * is expected after the header.
197 private byte[] recvPayload() throws Exception
{
198 byte payload
[] = new byte[(int) this.headerCmd
.data_size
];
200 /* Failsafe check so we don't waste our time reading 0 bytes. */
201 if (payload
.length
== 0) {
205 this.inFromSessiond
.read(payload
, 0, payload
.length
);
210 * Handle session command from the session daemon.
212 private void handleSessiondCmd() throws Exception
{
217 /* Get header from session daemon. */
220 if (headerCmd
.data_size
> 0) {
221 data
= recvPayload();
224 switch (headerCmd
.cmd
) {
227 LTTngSessiondCmd2_4
.sessiond_list_logger listLoggerCmd
=
228 new LTTngSessiondCmd2_4
.sessiond_list_logger();
229 listLoggerCmd
.execute(this.handler
);
230 data
= listLoggerCmd
.getBytes();
236 LTTngSessiondCmd2_4
.sessiond_enable_handler enableCmd
=
237 new LTTngSessiondCmd2_4
.sessiond_enable_handler();
239 enableCmd
.code
= LTTngSessiondCmd2_4
.lttng_jul_ret_code
.CODE_INVALID_CMD
;
242 enableCmd
.populate(data
);
243 event_name
= enableCmd
.execute(this.handler
, this.enabledLoggers
);
244 if (event_name
!= null) {
246 * Add the event to the list so it can be enabled if
247 * the logger appears at some point in time.
249 if (enabledEventList
.contains(event_name
) == false) {
250 enabledEventList
.add(event_name
);
253 data
= enableCmd
.getBytes();
258 LTTngSessiondCmd2_4
.sessiond_disable_handler disableCmd
=
259 new LTTngSessiondCmd2_4
.sessiond_disable_handler();
261 disableCmd
.code
= LTTngSessiondCmd2_4
.lttng_jul_ret_code
.CODE_INVALID_CMD
;
264 disableCmd
.populate(data
);
265 disableCmd
.execute(this.handler
);
266 data
= disableCmd
.getBytes();
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());
281 /* Send payload to session daemon. */
282 this.outToSessiond
.write(data
, 0, data
.length
);
283 this.outToSessiond
.flush();
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());
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];
300 buf
.putInt(Integer
.parseInt(pid
));
301 this.outToSessiond
.write(data
, 0, data
.length
);
302 this.outToSessiond
.flush();