--- /dev/null
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License, version 2.1 only,
+ * as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package org.lttng.ust.agent;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class LTTngAgent {
+
+ /* Domains */
+ static enum Domain {
+ JUL(3), LOG4J(4);
+ private int value;
+
+ private Domain(int value) {
+ this.value = value;
+ }
+
+ public int value() {
+ return value;
+ }
+ }
+
+ private static final int SEM_TIMEOUT = 3; /* Seconds */
+
+ private static LogFramework julUser;
+ private static LogFramework julRoot;
+ private static LogFramework log4jUser;
+ private static LogFramework log4jRoot;
+
+ /* Sessiond clients */
+ private static LTTngTCPSessiondClient julUserClient;
+ private static LTTngTCPSessiondClient julRootClient;
+ private static LTTngTCPSessiondClient log4jUserClient;
+ private static LTTngTCPSessiondClient log4jRootClient;
+
+ private static Thread sessiondThreadJULUser;
+ private static Thread sessiondThreadJULRoot;
+ private static Thread sessiondThreadLog4jUser;
+ private static Thread sessiondThreadLog4jRoot;
+
+ private boolean useJUL = false;
+ private boolean useLog4j = false;
+
+ /* Singleton agent object */
+ private static LTTngAgent curAgent = null;
+
+ /* Indicate if this object has been initialized. */
+ private static boolean initialized = false;
+
+ private static Semaphore registerSem;
+
+ /*
+ * Constructor is private. This is a singleton and a reference should be
+ * acquired using getLTTngAgent().
+ */
+ private LTTngAgent() {
+ initAgentJULClasses();
+
+ /* Since Log4j is a 3rd party JAR, we need to check if we can load any of its classes */
+ Boolean log4jLoaded = loadLog4jClasses();
+ if (log4jLoaded) {
+ initAgentLog4jClasses();
+ }
+
+ registerSem = new Semaphore(0, true);
+ }
+
+ private static Boolean loadLog4jClasses() {
+ Class<?> logging;
+
+ try {
+ logging = loadClass("org.apache.log4j.spi.LoggingEvent");
+ } catch (ClassNotFoundException e) {
+ /* Log4j classes not found, no need to create the relevant objects */
+ return false;
+ }
+
+ /*
+ * Detect capabilities of the log4j library. We only
+ * support log4j >= 1.2.15. The getTimeStamp() method
+ * was introduced in log4j 1.2.15, so verify that it
+ * is available.
+ *
+ * We can't rely on the getPackage().getImplementationVersion()
+ * call that would retrieves information from the manifest file
+ * found in the JAR since the manifest file shipped
+ * from upstream is known to be broken in several
+ * versions of the library.
+ *
+ * More info:
+ * https://issues.apache.org/bugzilla/show_bug.cgi?id=44370
+ */
+
+ try {
+ logging.getDeclaredMethod("getTimeStamp");
+ } catch (NoSuchMethodException e) {
+ System.err.println("Warning: The loaded log4j library is too old. Log4j tracing with LTTng will be disabled.");
+ return false;
+ } catch (NullPointerException e) {
+ /* Should never happen */
+ return false;
+ } catch (SecurityException e) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static Class<?> loadClass(String className) throws ClassNotFoundException {
+ ClassLoader loader;
+ Class<?> loadedClass;
+
+ try {
+ /* Try to load class using the current thread's context class loader */
+ loader = Thread.currentThread().getContextClassLoader();
+ loadedClass = loader.loadClass(className);
+ } catch (ClassNotFoundException e) {
+ /* Loading failed, try using the system class loader */
+ loader = ClassLoader.getSystemClassLoader();
+ loadedClass = loader.loadClass(className);
+ }
+
+ return loadedClass;
+ }
+
+ private void initAgentJULClasses() {
+ try {
+ Class<?> lttngJUL = loadClass("org.lttng.ust.agent.jul.LTTngJUL");
+ julUser = (LogFramework) lttngJUL.getDeclaredConstructor(new Class[] { Boolean.class }).newInstance(false);
+ julRoot = (LogFramework) lttngJUL.getDeclaredConstructor(new Class[] { Boolean.class }).newInstance(true);
+ this.useJUL = true;
+ } catch (ClassNotFoundException e) {
+ /* LTTng JUL classes not found, no need to create the relevant objects */
+ this.useJUL = false;
+ } catch (InstantiationException e) {
+ this.useJUL = false;
+ } catch (NoSuchMethodException e) {
+ this.useJUL = false;
+ } catch (IllegalAccessException e) {
+ this.useJUL = false;
+ } catch (InvocationTargetException e) {
+ this.useJUL = false;
+ }
+ }
+
+ private void initAgentLog4jClasses() {
+ try {
+ Class<?> lttngLog4j = loadClass("org.lttng.ust.agent.log4j.LTTngLog4j");
+ log4jUser = (LogFramework)lttngLog4j.getDeclaredConstructor(new Class[] {Boolean.class}).newInstance(false);
+ log4jRoot = (LogFramework)lttngLog4j.getDeclaredConstructor(new Class[] {Boolean.class}).newInstance(true);
+ this.useLog4j = true;
+ } catch (ClassNotFoundException e) {
+ /* LTTng Log4j classes not found, no need to create the relevant objects */
+ this.useLog4j = false;
+ } catch (InstantiationException e) {
+ this.useLog4j = false;
+ } catch (NoSuchMethodException e) {
+ this.useLog4j = false;
+ } catch (IllegalAccessException e) {
+ this.useLog4j = false;
+ } catch (InvocationTargetException e) {
+ this.useLog4j = false;
+ }
+ }
+
+ /*
+ * Public getter to acquire a reference to this singleton object.
+ */
+ public static synchronized LTTngAgent getLTTngAgent() throws IOException {
+ if (curAgent == null) {
+ curAgent = new LTTngAgent();
+ curAgent.init();
+ }
+
+ return curAgent;
+ }
+
+ private synchronized void init() throws SecurityException {
+ if (initialized) {
+ return;
+ }
+
+ Integer numJULThreads = 0;
+ Integer numLog4jThreads = 0;
+
+ if (this.useJUL) {
+ numJULThreads = initJULClientThreads();
+ }
+
+ if (this.useLog4j) {
+ numLog4jThreads = initLog4jClientThreads();
+ }
+
+ Integer numThreads = numJULThreads + numLog4jThreads;
+
+ /* Wait for each registration to end. */
+ try {
+ registerSem.tryAcquire(numThreads,
+ SEM_TIMEOUT,
+ TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ initialized = true;
+ }
+
+ private synchronized static Integer initJULClientThreads() {
+ Integer numThreads = 2;
+
+ /* Handle user session daemon if any. */
+ julUserClient = new LTTngTCPSessiondClient(Domain.JUL,
+ julUser,
+ registerSem);
+
+ String userThreadName = "LTTng UST agent JUL user thread";
+ sessiondThreadJULUser = new Thread(julUserClient, userThreadName);
+ sessiondThreadJULUser.setDaemon(true);
+ sessiondThreadJULUser.start();
+
+ /* Handle root session daemon. */
+ julRootClient = new LTTngTCPSessiondClient(Domain.JUL,
+ julRoot,
+ registerSem);
+
+ String rootThreadName = "LTTng UST agent JUL root thread";
+ sessiondThreadJULRoot = new Thread(julRootClient, rootThreadName);
+ sessiondThreadJULRoot.setDaemon(true);
+ sessiondThreadJULRoot.start();
+
+ return numThreads;
+ }
+
+ private synchronized static Integer initLog4jClientThreads() {
+ Integer numThreads = 2;
+
+ log4jUserClient = new LTTngTCPSessiondClient(Domain.LOG4J,
+ log4jUser,
+ registerSem);
+
+ String userThreadName = "LTTng UST agent Log4j user thread";
+ sessiondThreadLog4jUser = new Thread(log4jUserClient, userThreadName);
+ sessiondThreadLog4jUser.setDaemon(true);
+ sessiondThreadLog4jUser.start();
+
+ log4jRootClient = new LTTngTCPSessiondClient(Domain.LOG4J,
+ log4jRoot,
+ registerSem);
+
+ String rootThreadName = "LTTng UST agent Log4j root thread";
+ sessiondThreadLog4jRoot = new Thread(log4jRootClient,rootThreadName);
+ sessiondThreadLog4jRoot.setDaemon(true);
+ sessiondThreadLog4jRoot.start();
+
+ return numThreads;
+ }
+
+
+ public void dispose() throws IOException {
+ if (this.useJUL) {
+ julUserClient.destroy();
+ julRootClient.destroy();
+ julUser.reset();
+ julRoot.reset();
+ }
+
+ if (this.useLog4j) {
+ log4jUserClient.destroy();
+ log4jRootClient.destroy();
+ log4jUser.reset();
+ log4jRoot.reset();
+ }
+
+ try {
+ if (this.useJUL) {
+ sessiondThreadJULUser.join();
+ sessiondThreadJULRoot.join();
+ }
+
+ if (this.useLog4j) {
+ sessiondThreadLog4jUser.join();
+ sessiondThreadLog4jRoot.join();
+ }
+
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}