/*
* COMSAT
* Copyright (c) 2013-2016, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.comsat.tomcat;
import co.paralleluniverse.fibers.instrument.Log;
import co.paralleluniverse.fibers.instrument.LogLevel;
import co.paralleluniverse.fibers.instrument.MethodDatabase;
import co.paralleluniverse.fibers.instrument.QuasarInstrumentor;
import java.util.Arrays;
import org.apache.catalina.loader.ResourceEntry;
import org.apache.catalina.loader.WebappClassLoader;
/**
* See:
* http://tomcat.apache.org/tomcat-7.0-doc/config/loader.html
* http://tomcat.apache.org/tomcat-8.0-doc/config/loader.html
*
* @author pron
*/
public final class QuasarWebAppClassLoader extends WebappClassLoader {
@Override
protected final synchronized boolean filter(String name, boolean isClassName) {
// Don't re-load the instrumentation logic, including the `SuspendableClassifier` interface,
// else implementations in the webapp classloader will have trouble loading when running in
// a standalone servlet container
return
isClassName &&
name.startsWith("co.paralleluniverse.common.") ||
name.startsWith("co.paralleluniverse.fibers.instrument.") ||
name.startsWith("jsr166e") ||
name.startsWith("co.paralleluniverse.asm.");
}
private QuasarInstrumentor instrumentor;
public QuasarWebAppClassLoader() {}
public QuasarWebAppClassLoader(ClassLoader parent) {
super(parent);
}
private QuasarInstrumentor newInstrumentor() {
final QuasarInstrumentor inst = new QuasarInstrumentor(); // must be called *after* construction has completed
inst.setLog(new Log() {
@Override
public final void log(LogLevel level, String msg, Object... args) {
System.err.println("[quasar] " + level + ": " + String.format(msg, args));
}
@Override
public final void error(String msg, Throwable exc) {
System.err.println("[quasar] ERROR: " + msg);
exc.printStackTrace(System.err);
}
});
inst.setVerbose(false);
inst.setDebug(false);
return inst;
}
private synchronized void initInstrumentor() {
if (instrumentor == null)
instrumentor = newInstrumentor();
}
@Override
protected final ResourceEntry findResourceInternal(String name, String path) {
initInstrumentor();
final ResourceEntry entry = super.findResourceInternal(name, path);
if (name != null && path != null && path.endsWith(CLASS_SUFFIX) && entry != null && entry.binaryContent != null) {
final int nameLen = name.length();
final String className = name.substring(0, name.endsWith(CLASS_SUFFIX) ? nameLen - CLASS_SUFFIX_LENGTH : nameLen);
try {
final byte[] res = instrumentor.instrumentClass(this, className, entry.binaryContent);
if (res != null)
entry.binaryContent = res;
} catch (final Exception ex) {
if (MethodDatabase.isProblematicClass(className))
instrumentor.log(LogLevel.INFO, "Skipping problematic class instrumentation %s - %s %s", className, ex, Arrays.toString(ex.getStackTrace()));
else
instrumentor.error("Unable to instrument " + className, ex);
}
}
return entry;
}
private static final String CLASS_SUFFIX = ".class";
private static final int CLASS_SUFFIX_LENGTH = CLASS_SUFFIX.length();
}