Skip to content

Commit

Permalink
Fix WebSphere 8.5 compatibility
Browse files Browse the repository at this point in the history
Closes #270
  • Loading branch information
trask committed Sep 30, 2017
1 parent 1ad77dc commit b43de44
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class EmbeddedAgentModule {
final boolean h2MemDb = Boolean.parseBoolean(properties.get("glowroot.internal.h2.memdb"));

// need to perform jrebel workaround prior to loading any jackson classes
JRebelWorkaround.performWorkaroundIfNeeded();
JRebelWorkaround.perform();
PluginCache pluginCache = PluginCache.create(pluginsDir, false);
if (offline) {
viewerAgentModule = new ViewerAgentModule(pluginsDir, confDir);
Expand Down
42 changes: 34 additions & 8 deletions agent/core/src/main/java/org/glowroot/agent/init/AgentModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ public class AgentModule {
private static final long ROLLUP_0_INTERVAL_MILLIS =
Long.getLong("glowroot.internal.rollup.0.intervalMillis", MINUTES.toMillis(1));

// java.util.logging is shaded to org.glowroot.agent.jul
private static final String SHADE_PROOF_JUL_LOGGER_CLASS_NAME =
"_java.util.logging.Logger".substring(1);

@OnlyUsedByTests
public static final ThreadLocal</*@Nullable*/ IsolatedWeavingClassLoader> isolatedWeavingClassLoader =
new ThreadLocal</*@Nullable*/ IsolatedWeavingClassLoader>();
Expand Down Expand Up @@ -154,7 +158,7 @@ public AgentModule(Clock clock, @Nullable Ticker nullableTicker, final PluginCac
instrumentation.addTransformer(transformer);
jvmRetransformClassesSupported = false;
}
logRunnableCallableClassWarningIfNeeded(instrumentation);
logJavaClassAlreadyLoadedWarningIfNeeded(instrumentation);
}

// now that instrumentation is set up, it is safe to create scheduled executor
Expand Down Expand Up @@ -255,22 +259,33 @@ public LiveJvmServiceImpl getLiveJvmService() {
return liveJvmService;
}

private static void logRunnableCallableClassWarningIfNeeded(Instrumentation instrumentation) {
private static void logJavaClassAlreadyLoadedWarningIfNeeded(Instrumentation instrumentation) {
List<String> runnableCallableClasses = Lists.newArrayList();
boolean julLoggerLoaded = false;
for (Class<?> clazz : instrumentation.getAllLoadedClasses()) {
if (clazz.isInterface()) {
continue;
}
if (!clazz.getName().startsWith("java.util.concurrent.")) {
continue;
}
if (Runnable.class.isAssignableFrom(clazz) || Callable.class.isAssignableFrom(clazz)) {
String className = clazz.getName();
if (className.startsWith("java.util.concurrent.")
&& (Runnable.class.isAssignableFrom(clazz)
|| Callable.class.isAssignableFrom(clazz))) {
runnableCallableClasses.add(clazz.getName());
}
if (className.equals(SHADE_PROOF_JUL_LOGGER_CLASS_NAME)) {
julLoggerLoaded = true;
}
}
if (!runnableCallableClasses.isEmpty()) {
logRunnableCallableClassWarning(runnableCallableClasses);
}
if (runnableCallableClasses.isEmpty()) {
return;
if (julLoggerLoaded && isShaded()) {
logger.warn("java.util.logging.Logger was loaded before Glowroot instrumentation could"
+ " be applied to it. This may prevent Glowroot from capturing JUL logging.");
}
}

private static void logRunnableCallableClassWarning(List<String> runnableCallableClasses) {
List<String> nonGlowrootAgents = Lists.newArrayList();
for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
if (jvmArg.startsWith("-javaagent:") && !jvmArg.endsWith("glowroot.jar")
Expand Down Expand Up @@ -316,6 +331,17 @@ private static void initPlugins(List<PluginDescriptor> pluginDescriptors) {
}
}

private static boolean isShaded() {
try {
Class.forName("org.glowroot.agent.shaded.slf4j.Logger");
return true;
} catch (ClassNotFoundException e) {
// log exception at trace level
logger.trace(e.getMessage(), e);
return false;
}
}

@OnlyUsedByTests
public void close() throws Exception {
immedateTraceStoreWatcher.cancel();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,10 @@
*/
package org.glowroot.agent.init;

import java.lang.management.ManagementFactory;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.glowroot.agent.util.ThreadFactories;

// this is needed for JRebel 6.5.0+
// otherwise get JsonMappingException: "No serializer found for class
// org.glowroot.agent.shaded.glowroot.common.repo.MutableTimer"
Expand All @@ -37,14 +29,15 @@ public class JRebelWorkaround {

private JRebelWorkaround() {}

public static void performWorkaroundIfNeeded() {
if (!isJrebel()) {
return;
}
public static void perform() {
try {
ExecutorService singleUseExecutor = Executors.newSingleThreadExecutor(
ThreadFactories.create("Glowroot-Init-JRebel-Workaround"));
Future<?> future = singleUseExecutor.submit(new Runnable() {
// cannot check ManagementFactory.getRuntimeMXBean().getInputArguments() here because
// that can trigger java.util.logging.Logger to be loaded (e.g. on WebSphere 8.5) before
// weaving is put in place (preventing the logger plugin from weaving JUL Logger)
//
// also cannot use ExecutorService here before weaving is put in place (preventing the
// executor plugin from weaving ExecutorService)
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
boolean shouldBeTrue;
Expand All @@ -60,20 +53,11 @@ public void run() {
}
}
});
future.get();
singleUseExecutor.shutdown();
thread.setName("Glowroot-Init-JRebel-Workaround");
thread.start();
thread.join();
} catch (Exception e) {
startupLogger.error(e.getMessage(), e);
}
}

private static boolean isJrebel() {
for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
if (jvmArg.startsWith("-agentpath:")
&& jvmArg.toLowerCase(Locale.ENGLISH).contains("jrebel")) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public void init(@Nullable File pluginsDir, final File confDir,
Clock clock = Clock.systemClock();

// need to perform jrebel workaround prior to loading any jackson classes
JRebelWorkaround.performWorkaroundIfNeeded();
JRebelWorkaround.perform();
final PluginCache pluginCache = PluginCache.create(pluginsDir, false);
final ConfigService configService =
ConfigService.create(confDir, pluginCache.pluginDescriptors());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import javax.annotation.Nullable;

Expand All @@ -32,17 +33,11 @@
import org.glowroot.agent.plugin.api.weaving.OnAfter;
import org.glowroot.agent.plugin.api.weaving.OnBefore;
import org.glowroot.agent.plugin.api.weaving.Pointcut;
import org.glowroot.agent.plugin.api.weaving.Shim;

public class JavaLoggingAspect {

private static final String TIMER_NAME = "logging";

@Shim("java.util.logging.Logger")
public interface Logger {
boolean isLoggable(Level level);
}

@Pointcut(className = "java.util.logging.Logger", methodName = "log",
methodParameterTypes = {"java.util.logging.LogRecord"}, nestingGroup = "logging",
timerName = TIMER_NAME)
Expand All @@ -52,14 +47,17 @@ public static class LogAdvice {

private static final Formatter formatter = new DummyFormatter();

// cannot use java.util.logging.Logger in the signature of this method because that triggers
// java.util.logging.Logger to be loaded before weaving is put in place (from inside
// org.glowroot.agent.weaving.AdviceBuilder)
@OnBefore
public static @Nullable LogAdviceTraveler onBefore(ThreadContext context,
@BindParameter @Nullable LogRecord record, @BindReceiver Logger logger) {
@BindParameter @Nullable LogRecord record, @BindReceiver Object logger) {
if (record == null) {
return null;
}
Level level = record.getLevel();
if (!logger.isLoggable(level)) {
if (!((Logger) logger).isLoggable(level)) {
// Logger.log(LogRecord) was called directly
return null;
}
Expand Down

0 comments on commit b43de44

Please sign in to comment.