@@ -57,6 +57,8 @@ public static void start(final Instrumentation inst, final URL bootstrapURL) {
5757
5858 final boolean appUsingCustomLogManager = isAppUsingCustomLogManager ();
5959
60+ final boolean appUsingCustomJMXBuilder = isAppUsingCustomJMXBuilder ();
61+
6062 /*
6163 * java.util.logging.LogManager maintains a final static LogManager, which is created during class initialization.
6264 *
@@ -69,8 +71,15 @@ public static void start(final Instrumentation inst, final URL bootstrapURL) {
6971 *
7072 * Once we see the LogManager class loading, it's safe to start jmxfetch because the application is already setting
7173 * the global log manager and jmxfetch won't be able to touch it due to classloader locking.
74+ *
75+ * Likewise if a custom JMX builder is configured which is not on the system classpath then we delay starting
76+ * JMXFetch until we detect the custom MBeanServerBuilder is being used. This takes precedence over the custom
77+ * log manager check because any custom log manager will be installed before any custom MBeanServerBuilder.
7278 */
73- if (appUsingCustomLogManager ) {
79+ if (appUsingCustomJMXBuilder ) {
80+ log .debug ("Custom JMX builder detected. Delaying JMXFetch initialization." );
81+ registerMBeanServerBuilderCallback (new StartJmxCallback (bootstrapURL ));
82+ } else if (appUsingCustomLogManager ) {
7483 log .debug ("Custom logger detected. Delaying JMXFetch initialization." );
7584 registerLogManagerCallback (new StartJmxCallback (bootstrapURL ));
7685 } else {
@@ -114,6 +123,18 @@ private static void registerLogManagerCallback(final ClassLoadCallBack callback)
114123 }
115124 }
116125
126+ private static void registerMBeanServerBuilderCallback (final ClassLoadCallBack callback ) {
127+ try {
128+ final Class <?> agentInstallerClass =
129+ AGENT_CLASSLOADER .loadClass ("datadog.trace.agent.tooling.AgentInstaller" );
130+ final Method registerCallbackMethod =
131+ agentInstallerClass .getMethod ("registerClassLoadCallback" , String .class , Runnable .class );
132+ registerCallbackMethod .invoke (null , "javax.management.MBeanServerBuilder" , callback );
133+ } catch (final Exception ex ) {
134+ log .error ("Error registering callback for " + callback .getName (), ex );
135+ }
136+ }
137+
117138 protected abstract static class ClassLoadCallBack implements Runnable {
118139
119140 final URL bootstrapURL ;
@@ -508,6 +529,41 @@ private static boolean isAppUsingCustomLogManager() {
508529 return false ;
509530 }
510531
532+ /**
533+ * Search for java or datadog-tracer sysprops which indicate that a custom JMX builder will be
534+ * used.
535+ *
536+ * @return true if we detect a custom JMX builder being used.
537+ */
538+ private static boolean isAppUsingCustomJMXBuilder () {
539+ final String tracerCustomJMXBuilderSysprop = "dd.app.customjmxbuilder" ;
540+ final String customJMXBuilderProp = System .getProperty (tracerCustomJMXBuilderSysprop );
541+ final String customJMXBuilderEnv =
542+ System .getenv (tracerCustomJMXBuilderSysprop .replace ('.' , '_' ).toUpperCase ());
543+
544+ if (customJMXBuilderProp != null || customJMXBuilderEnv != null ) {
545+ log .debug ("Prop - customjmxbuilder: " + customJMXBuilderProp );
546+ log .debug ("Env - customjmxbuilder: " + customJMXBuilderEnv );
547+ // Allow setting to skip these automatic checks:
548+ return Boolean .parseBoolean (customJMXBuilderProp )
549+ || Boolean .parseBoolean (customJMXBuilderEnv );
550+ }
551+
552+ final String jmxBuilderProp = System .getProperty ("javax.management.builder.initial" );
553+ if (jmxBuilderProp != null ) {
554+ final boolean onSysClasspath =
555+ ClassLoader .getSystemResource (jmxBuilderProp .replaceAll ("\\ ." , "/" ) + ".class" ) != null ;
556+ log .debug ("Prop - javax.management.builder.initial: " + jmxBuilderProp );
557+ log .debug ("javax.management.builder.initial on system classpath: " + onSysClasspath );
558+ // Some applications set javax.management.builder.initial but never actually initialize JMX.
559+ // Check to see if the configured JMX builder is on the system classpath.
560+ // If so, it should be safe to initialize jmxfetch which will setup JMX.
561+ return !onSysClasspath ;
562+ }
563+
564+ return false ;
565+ }
566+
511567 private static boolean isJavaBefore9 () {
512568 return System .getProperty ("java.version" ).startsWith ("1." );
513569 }
0 commit comments