All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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