Detailed implementation principles of Tomcat hot deployment

  • 2020-06-03 08:48:22
  • OfStack

Tomcat hot deployment mechanism

For Java applications, hot deployment means updating the Java class files at run time. Class loaders play an important role in hot deployment of Java-based application servers. Most ES7en-based application servers, including EJB servers and Servlet containers, support hot deployment. A class loader cannot reload an already loaded class, but it can reload a class into a running application with a new class loader instance.

As we know, most OF web servers now support hot deployment, but the implementation mechanism of hot deployment is not perfect enough. Let's explain how Tomcat's hot deployment implementation mechanism is implemented below:

Tomcat's container implementation for hot deployment USES two mechanisms:

Classloader rewrite to load the corresponding jsp compiled class into JVM by custom classloader. The modified class is re-loaded into JVM by dynamically modifying the bytecode in memory.

Classloader implements reloading of jsp

Tomcat through org. apache. jasper. servlet. jsp JasperLoader realized with load,

Here's a test:

1. Create a new web project, write an jsp page, and output classloader in the jsp page.

2. Start the web server, open the jsp page, we can see the background output, classloader of the jsp is an instance of JasperLoader.

3. Modify jsp, save and refresh the jsp page, and check the background output again. This instance of classloader is not the one just now, that is, tomcat loads the jsp again through a new classloader.

4. In fact, for each jsp page tomcat USES a separate classloader to load, and each time jsp is modified, tomcat USES a new classloader to load it.

About how to use custom classloader to load a class here is not to say, believe that you can find on the net, JSP belong to one sex consumption, each time the container will create a new instance, belongs to throw, the kind of finished but for this implementation is very difficult for other cases, such as now we many have used the singleton in engineering, especially spring engineering, in this case, use the new classloader to load the modified class is not realistic, singleton class could have multiple instances in memory, and in this way can change the current memory has instances of behavior, Of course, tomcat does not reload class files in this way.

Modify the byte code of class in memory by proxy

Tomcat class of documents is through org. apache. catalina. loader. WebappClassLoader loading, also we can do a test, the test process is similar to jsp test, test steps aside, only said 1 under the results:

In the case of hot deployment for the classloader load class file, its classloader is always with a WebappClassLoader, unless the container restart, believe you won't think tomcat finish this experiment is to use a new classloader to load the modified class, and for instance, there are state before the instance with the attributes and state will be saved, and the next execution has the new class logic, this is a mysterious quality of hot deployment (in fact, each instance just save the state of the instance attributes, We can see the state contained in the object by serializing the object, and the final logic still exists in the class file.

The following class redefinition is implemented through: ES106en.lang.instrument, please refer to the related documentation for details.

Let's take a look at how to modify the class bytecode in memory by proxy:

Here is a simple hot deployment agent implementation class (the code is rough and uncritical) :


package agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.util.Set;
import java.util.Timer;
import java.util.TreeSet;
public class HotAgent {
 
  protected static Set<String> clsnames=new TreeSet<String>();
 
  public static void premain(String agentArgs, Instrumentation inst) throws Exception {
    ClassFileTransformer transformer =new ClassTransform(inst);
    inst.addTransformer(transformer);
    System.out.println(" Support for class redefinition: "+inst.isRedefineClassesSupported());
    Timer timer=new Timer();
    timer.schedule(new ReloadTask(inst),2000,2000);
  }
}

package agent;
import java.lang.instrument.ClassFileTransformer;
importjava.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
 
public class ClassTransform. implements ClassFileTransformer {
  private Instrumentation inst;
 
  protected ClassTransform(Instrumentation inst){
    this.inst=inst;
  }
 
  /**
   *  In this method redefineClasses Is called at, or when it's first loaded class It's called when it's reloaded, 
   *  And we can do this dynamically class Bytecode to implement functions such as agents, specific methods can be used ASM or javasist . 
   *  You can modify the bytecode directly if you are familiar with it. 
   */
  public byte[] transform(ClassLoader loader, String className,
      Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
      byte[] classfileBuffer)throws IllegalClassFormatException {
    byte[] transformed = null;
    HotAgent.clsnames.add(className);
    return null;
  }
}

package agent;
import java.io.InputStream;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.util.TimerTask;
 
public class ReloadTask extends TimerTask {
  private Instrumentation inst;
 
  protected ReloadTask(Instrumentation inst){
    this.inst=inst;
  }
 
  @Override
  public void run() {
    try{
      ClassDefinition[] cd=new ClassDefinition[1];
      Class[] classes=inst.getAllLoadedClasses();
      for(Class cls:classes){
        if(cls.getClassLoader()==null||!cls.getClassLoader().getClass().getName().equals("sun.misc.Launcher$AppClassLoader"))
          continue;
        String name=cls.getName().replaceAll("\\.","/");
        cd[0]=new ClassDefinition(cls,loadClassBytes(cls,name+".class"));
        inst.redefineClasses(cd);
      }
    }catch(Exception ex){
      ex.printStackTrace();
    }
  }
 
  private byte[] loadClassBytes(Class cls,String clsname) throws Exception{
    System.out.println(clsname+":"+cls);
    InputStream is=cls.getClassLoader().getSystemClassLoader().getResourceAsStream(clsname);
    if(is==null)return null;
    byte[] bt=new byte[is.available()];
    is.read(bt);
    is.close();
    return bt;
  }
}

The above is the basic implementation code, and the required components are:

1. HotAgent (pre-loaded)

ClassTransform (you can modify the bytecode of class when loading class), not used in this example

3. ReloadTask (class timing loader, the above code is for reference only)

META-INF/ES133en-ES134en: (parameter 1: support class redefinition; Parameter 2: Pre-loaded class)

Can-Redefine-Classes: true Premain-Class: agent.HotAgent

5. Package the above components into an jar file (at this point, the component is complete; write the test class file below).

6. Create a new java project, write an java logical class, and write an Test class to call the methods of the logical class in the test class.

Take a look at the test class code:


package test.redefine;
 
public class Bean1 {
  public void test1(){
   System.out.println("============================");
  }
}

package test.redefine;
 
public class Test {
  public static void main(String[] args)throws InterruptedException {
 
    Bean1 c1=new Bean1();
    while(true){
      c1.test1();
      Thread.sleep(5000);
    }
  }
}

Run the test class:

java � javaagent: agent jar test. redefine. Test

In the test class, we used an endless loop that periodically called methods of the logical class. We can modify the implementation of the method in Bean1 to see different output at different times, but we don't have much to say about the technical details, I'm sure you can understand.

Tomcat hot deployment configuration


<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false"> 
  <Context docBase="CPCWeb" path="/CPCWeb" reloadable="true" source="org.<span class="wp_keywordlink"><a href="http://res.importnew.com/eclipse" title="Eclipse ImportNew The home page " target="_blank">Eclipse</a></span>.jst.j2ee.server:CPCWeb"/>
</Host>

autoDeploy= "true" - Automatic deployment reloadable= "true" - Automatic loading

Thank you for reading, I hope to help you, thank you for your support to this site!


Related articles: