Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,24 @@
*/
package org.apache.karaf.diagnostic.core.internal;

import java.io.Closeable;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {
Closeable dumpHandler;
DumpHandler dumpHandler;

public void start(BundleContext context) throws Exception {
if (!isWindows()) {
ClassLoader cl = this.getClass().getClassLoader();
try {
Class<?> dumpHandlerClazz = cl.loadClass("org.apache.karaf.diagnostic.core.internal.DumpHandler");
dumpHandler = (Closeable) dumpHandlerClazz.getConstructor(BundleContext.class).newInstance(context);
} catch (Throwable e) {
// Will happen if sun.misc.SignalHandler is not available
dumpHandler = new DumpHandler(context);
} catch (Exception | LinkageError e) {
// Will happen if sun.misc.Signal is not available or cannot be initialized
}
}
}

public void stop(BundleContext context) throws Exception {
if (dumpHandler != null && !isWindows()) {
if (dumpHandler != null) {
dumpHandler.close();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;

Expand All @@ -27,29 +29,64 @@
import org.apache.karaf.diagnostic.core.common.ZipDumpDestination;
import org.osgi.framework.BundleContext;

import sun.misc.Signal;
import sun.misc.SignalHandler;
/**
* Creates a dump when the process receives SIGHUP. sun.misc.Signal is used reflectively, as in
* org.apache.karaf.main.Main, to avoid the compiler warning about internal proprietary API.
*/
public class DumpHandler implements Closeable {

public class DumpHandler implements SignalHandler, Closeable {
private static final String SIGNAL = "HUP";
private BundleContext context;
private SignalHandler previous;

public DumpHandler(BundleContext context) {
private final BundleContext context;
private final Method handleMethod;
private final Object signal;
private final Object previous;

public DumpHandler(BundleContext context) throws Exception {
this.context = context;
previous = sun.misc.Signal.handle(new Signal(SIGNAL), this);

final Class<?> signalClass = Class.forName("sun.misc.Signal");
final Class<?> signalHandlerClass = Class.forName("sun.misc.SignalHandler");

Object signalHandler = Proxy.newProxyInstance(getClass().getClassLoader(),
new Class<?>[] {
signalHandlerClass
},
(proxy, method, args) -> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proxy ignores method, so every call dispatched to this handler (including Object.equals/hashCode/toString) triggers a full diagnostic dump zip in the working directory.

I suggest the following guarding:

(proxy, method, args) -> {
  if ("handle".equals(method.getName())) {
    handle();
    return null;
  }
  return method.invoke(this, args); // Object methods
}

if ("handle".equals(method.getName())) {
handle();
return null;
}
// Object methods such as equals, hashCode and toString
return method.invoke(this, args);
}
);

handleMethod = signalClass.getMethod("handle", signalClass, signalHandlerClass);
signal = signalClass.getConstructor(String.class).newInstance(SIGNAL);
previous = handleMethod.invoke(null, signal, signalHandler);
}

public void handle(Signal signal) {
SimpleDateFormat dumpFormat = new SimpleDateFormat("yyyy-MM-dd_HHmmss-SSS");
String fileName = "dump-" + dumpFormat.format(new Date()) + ".zip";
DumpDestination destination = new ZipDumpDestination(new File(fileName));
Dump.dump(context, destination, false, false);

/**
* Creates the dump on a short-lived thread, so that the JVM signal dispatch thread is not
* blocked while everything is collected and zipped.
*/
private void handle() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dump.dump (collect and zip everything) runs synchronously on the JVM signal-dispatch thread here. Main.registerSignalHandler deliberately offloads its handler body to new Thread(...).

This is pre-existing behaviour, but since the method being rewritten anyway it would be a good moment to match Main and run the dump on a short-lived thread.

new Thread(() -> {
SimpleDateFormat dumpFormat = new SimpleDateFormat("yyyy-MM-dd_HHmmss-SSS");
String fileName = "dump-" + dumpFormat.format(new Date()) + ".zip";
DumpDestination destination = new ZipDumpDestination(new File(fileName));
Dump.dump(context, destination, false, false);
}, "karaf-diagnostic-dump").start();
}

@Override
public void close() throws IOException {
sun.misc.Signal.handle(new Signal(SIGNAL), previous);
try {
handleMethod.invoke(null, signal, previous);
} catch (Exception e) {
throw new IOException("Cannot restore the previous " + SIGNAL + " handler", e);
}
}

}
Loading