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

scouter.javassist.util.HotSwapAgent Maven / Gradle / Ivy

There is a newer version: 2.20.0
Show newest version
/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */
package scouter.javassist.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.management.ManagementFactory;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import com.sun.tools.attach.VirtualMachine;

import scouter.javassist.CannotCompileException;
import scouter.javassist.ClassPool;
import scouter.javassist.CtClass;
import scouter.javassist.NotFoundException;

/**
 * A utility class for dynamically adding a new method
 * or modifying an existing method body.
 * This class provides {@link #redefine(Class, CtClass)}
 * and {@link #redefine(Class[], CtClass[])}, which replace the
 * existing class definition with a new one.
 * These methods perform the replacement by
 * {@code java.lang.instrument.Instrumentation}.  For details
 * of acceptable modification,
 * see the {@code Instrumentation} interface.
 *
 * 

Before calling the {@code redefine} methods, the hotswap agent * has to be deployed.

* *

To create a hotswap agent, run {@link #createAgentJarFile(String)}. * For example, the following command creates an agent file named {@code hotswap.jar}. * *

 * $ jshell --class-path scouter.javassist.jar
 * jshell> scouter.javassist.util.HotSwapAgent.createAgentJarFile("hotswap.jar") 
 * 
* *

Then, run the JVM with the VM argument {@code -javaagent:hotswap.jar} * to deploy the hotswap agent. *

* *

If the {@code -javaagent} option is not given to the JVM, {@code HotSwapAgent} * attempts to automatically create and start the hotswap agent on demand. * This automated deployment may fail. If it fails, manually create the hotswap agent * and deploy it by {@code -javaagent}.

* *

The {@code HotSwapAgent} requires {@code tools.jar} as well as {@code scouter.javassist.jar}.

* *

The idea of this class was given by Adam Lugowski. * Shigeru Chiba wrote this class by referring * to his {@code RedefineClassAgent}. * For details, see this discussion. *

* * @see #redefine(Class, CtClass) * @see #redefine(Class[], CtClass[]) * @since 3.22 */ public class HotSwapAgent { private static Instrumentation instrumentation = null; /** * Obtains the {@code Instrumentation} object. * * @return null when it is not available. */ public Instrumentation instrumentation() { return instrumentation; } /** * The entry point invoked when this agent is started by {@code -javaagent}. */ public static void premain(String agentArgs, Instrumentation inst) throws Throwable { agentmain(agentArgs, inst); } /** * The entry point invoked when this agent is started after the JVM starts. */ public static void agentmain(String agentArgs, Instrumentation inst) throws Throwable { if (!inst.isRedefineClassesSupported()) throw new RuntimeException("this JVM does not support redefinition of classes"); instrumentation = inst; } /** * Redefines a class. */ public static void redefine(Class oldClass, CtClass newClass) throws NotFoundException, IOException, CannotCompileException { Class[] old = { oldClass }; CtClass[] newClasses = { newClass }; redefine(old, newClasses); } /** * Redefines classes. */ public static void redefine(Class[] oldClasses, CtClass[] newClasses) throws NotFoundException, IOException, CannotCompileException { startAgent(); ClassDefinition[] defs = new ClassDefinition[oldClasses.length]; for (int i = 0; i < oldClasses.length; i++) defs[i] = new ClassDefinition(oldClasses[i], newClasses[i].toBytecode()); try { instrumentation.redefineClasses(defs); } catch (ClassNotFoundException e) { throw new NotFoundException(e.getMessage(), e); } catch (UnmodifiableClassException e) { throw new CannotCompileException(e.getMessage(), e); } } /** * Ensures that the agent is ready. * It attempts to dynamically start the agent if necessary. */ private static void startAgent() throws NotFoundException { if (instrumentation != null) return; try { File agentJar = createJarFile(); String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName(); String pid = nameOfRunningVM.substring(0, nameOfRunningVM.indexOf('@')); VirtualMachine vm = VirtualMachine.attach(pid); vm.loadAgent(agentJar.getAbsolutePath(), null); vm.detach(); } catch (Exception e) { throw new NotFoundException("hotswap agent", e); } for (int sec = 0; sec < 10 /* sec */; sec++) { if (instrumentation != null) return; try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } throw new NotFoundException("hotswap agent (timeout)"); } /** * Creates an agent file for using {@code HotSwapAgent}. */ public static File createAgentJarFile(String fileName) throws IOException, CannotCompileException, NotFoundException { return createJarFile(new File(fileName)); } private static File createJarFile() throws IOException, CannotCompileException, NotFoundException { File jar = File.createTempFile("agent", ".jar"); jar.deleteOnExit(); return createJarFile(jar); } private static File createJarFile(File jar) throws IOException, CannotCompileException, NotFoundException { Manifest manifest = new Manifest(); Attributes attrs = manifest.getMainAttributes(); attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0"); attrs.put(new Attributes.Name("Premain-Class"), HotSwapAgent.class.getName()); attrs.put(new Attributes.Name("Agent-Class"), HotSwapAgent.class.getName()); attrs.put(new Attributes.Name("Can-Retransform-Classes"), "true"); attrs.put(new Attributes.Name("Can-Redefine-Classes"), "true"); JarOutputStream jos = null; try { jos = new JarOutputStream(new FileOutputStream(jar), manifest); String cname = HotSwapAgent.class.getName(); JarEntry e = new JarEntry(cname.replace('.', '/') + ".class"); jos.putNextEntry(e); ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(cname); jos.write(clazz.toBytecode()); jos.closeEntry(); } finally { if (jos != null) jos.close(); } return jar; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy