org.zeroturnaround.javarebel.integration.resin.WatchdogProcessCBP Maven / Gradle / Ivy
The newest version!
package org.zeroturnaround.javarebel.integration.resin;
import java.io.File;
import java.util.List;
import java.util.ListIterator;
import org.zeroturnaround.bundled.javassist.CannotCompileException;
import org.zeroturnaround.bundled.javassist.ClassPool;
import org.zeroturnaround.bundled.javassist.CtClass;
import org.zeroturnaround.bundled.javassist.NotFoundException;
import org.zeroturnaround.bundled.javassist.expr.ExprEditor;
import org.zeroturnaround.bundled.javassist.expr.MethodCall;
import org.zeroturnaround.javarebel.Logger;
import org.zeroturnaround.javarebel.LoggerFactory;
import org.zeroturnaround.javarebel.integration.support.JavassistClassBytecodeProcessor;
import org.zeroturnaround.javarebel.integration.util.AgentUtil;
/**
* Processes com.caucho.boot.ResinWatchdog
* or com.caucho.boot.WatchdogProcess
.
*
*
* Starting from Resin 3.1 a so called "WatchDog" forks a new JVM with a custom system class loader.
* Since JavaRebel cannot instrument the system class loader and it's dependencies directly
* the necessary classes are transformed and written into a special JAR file which is
* prepended to the class path of the forked JVM.
*
*
* Tested on Resin 3.1.3, 3.1.9, 3.2.1 and 4.0.0.
*
* @author Rein Raudjärv
*
* @see ResinWatchdogCBP
*/
public class WatchdogProcessCBP extends JavassistClassBytecodeProcessor {
private static final Logger log = LoggerFactory.getInstance();
/**
* Class path separator character.
*/
private static final char CLASSPATH_SEPARATOR = File.pathSeparatorChar;
/**
* >-noverify
JVM option.
*/
private static final String NO_VERIFY = "-noverify";
/**
* -javaagent
JVM option of JavaRebel.
*/
private static final String JAVA_AGENT = "-javaagent:" + AgentUtil.getRebelJarPath();
private static final String REBEL_LOG_FILE = "rebel.log.file";
private static final String REBEL_LOG_FILE_PREFIX = "-D" + REBEL_LOG_FILE + "=";
private static final String REBEL_LOG_FILE_SUFFIX = "-fork";
public void process(ClassPool cp, ClassLoader cl, CtClass ctClass) throws Exception {
cp.importPackage("java.io");
cp.importPackage("java.net");
cp.importPackage("org.zeroturnaround.javarebel");
cp.importPackage("org.zeroturnaround.javarebel.integration.resin");
processConnectToChild(ctClass);
processCreateProcess(cl, ctClass);
}
private void processConnectToChild(CtClass ctClass) throws CannotCompileException {
try {
ctClass.getDeclaredMethod("connectToChild").instrument(new ExprEditor() {
public void edit(MethodCall m) throws CannotCompileException {
if ("java.net.ServerSocket".equals(m.getClassName()) && "accept".equals(m.getMethodName())) {
m.replace(
"{" +
" Logger log = LoggerFactory.getInstance();" +
" if (log.isEnabled())" +
" log.log(\"JRebel-Resin: Listening for connections on port \" + $0.getLocalPort());" +
" Socket result = $proceed($$);" +
" $_ = result;" +
" if (log.isEnabled())" +
" log.log(\"JRebel-Resin: Accepted connection \" + result + \" on port \" + $0.getLocalPort());" +
"}");
}
}
});
} catch (NotFoundException e) {
// Missing up to Resin 3.1.4, available since Resin 3.1.5
}
}
private void processCreateProcess(ClassLoader cl, CtClass ctClass) throws Exception {
final File jar = ResinPatchHelper.createResinPatch(cl);
ctClass.getDeclaredMethod("createProcess").instrument(new ExprEditor() {
public void edit(MethodCall m) throws CannotCompileException {
String className = m.getClassName();
String methodName = m.getMethodName();
/*
* Process class path of the forked JVM.
*/
if ((className.equals("com.caucho.boot.ResinWatchdogManager") // Resin 3.1.x
|| className.equals("com.caucho.boot.WatchdogArgs")) // Resin 3.2.x
&& methodName.equals("calculateClassPath")) {
m.replace(
"{" +
" String result = $proceed($$);" +
" Logger log = LoggerFactory.getInstance();" +
" if (log.isEnabled())" +
" log.log(\"JRebel-Resin: Resin original classpath: \" + result);" +
" result = \"" + escape(jar.getPath()) + CLASSPATH_SEPARATOR + "\" + result;" +
" if (log.isEnabled())" +
" log.log(\"JRebel-Resin: Resin modified classpath: \" + result);" +
" $_ = result;" +
"}");
}
/*
* Add JVM options for JavaRebel.
*/
else if ((className.equals("com.caucho.boot.Boot") && methodName.equals("exec"))
|| (className.equals("java.lang.ProcessBuilder") && methodName.equals("command"))) {
m.replace(
"{" +
" WatchdogProcessCBP.processArguments($1);" +
" $_ = $proceed($$);" +
"}");
}
}
});
}
public static void processArguments(List commandArgs) {
try {
commandArgs.add(1, NO_VERIFY);
commandArgs.add(1, JAVA_AGENT);
// Modify -Drebel.log.file value
String path = System.getProperty(REBEL_LOG_FILE);
if (path != null) {
for (ListIterator it = commandArgs.listIterator(); it.hasNext();) {
String arg = (String) it.next();
if (arg.startsWith(REBEL_LOG_FILE_PREFIX)) {
int i = arg.lastIndexOf('.');
if (i < REBEL_LOG_FILE_PREFIX.length())
arg += REBEL_LOG_FILE_SUFFIX;
else
arg = arg.substring(0, i) + REBEL_LOG_FILE_SUFFIX + arg.substring(i);
it.set(arg);
}
}
}
if (log.isEnabled())
log.log("JRebel-Resin: Exec args:" + commandArgs);
} catch (Throwable t) {
log.error(t);
}
}
/**
* Escape a String to be used in Javassist code.
*/
private static String escape(String str) {
StringBuffer sb = new StringBuffer(str.length());
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
// Add additional backslash
if (c == '\"' || c == '\\')
sb.append('\\');
sb.append(c);
}
return sb.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy