/** * Helios, OpenSource Monitoring * Brought to you by the Helios Development Group * * Copyright 2007, Helios Development Group and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.helios.gmx.util; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; import java.security.ProtectionDomain; import java.util.HashMap; import java.util.Map; import org.helios.gmx.classloading.ByteCodeRepository; /** * <p>Title: ByteCodeNet</p> * <p>Description: A class file transformer used to trap the bytecode of a target class</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.gmx.util.ByteCodeNet</code></p> */ public class ByteCodeNet implements ClassFileTransformer { /** The class to get the byte code for */ private final String targetClassName; /** The captured bytecode */ private final ThreadLocal<Map<String, byte[]>> byteCodeMap = new ThreadLocal<Map<String, byte[]>>() { /** * {@inheritDoc} * @see java.lang.ThreadLocal#initialValue() */ @Override protected Map<String, byte[]> initialValue() { return null; } }; /** The instrumentation instance */ private static volatile Instrumentation instrumentation = null; /** * Creates a new ByteCodeNet * @param targetClass The class to get the byte code for * @return the bytecode of the class */ public static Map<String, byte[]> getClassBytes(Class<?> targetClass) { if(instrumentation==null) { instrumentation = ByteCodeRepository.getInstance().getInstrumentation(); } if(targetClass==null) throw new IllegalArgumentException("The passed class was null", new Throwable()); ByteCodeNet bcn = new ByteCodeNet(targetClass.getName().replace('.', '/')); bcn.byteCodeMap.set(new HashMap<String, byte[]>()); try { if(!instrumentation.isModifiableClass(targetClass)) { throw new RuntimeException("The class [" + targetClass.getName() + "] is not modifiable", new Throwable()); } instrumentation.addTransformer(bcn, true); try { instrumentation.retransformClasses(targetClass); } catch (Exception e) { throw new RuntimeException("Failed to retransform class [" + targetClass.getName() + "]", e); } Map<String, byte[]> map = bcn.byteCodeMap.get(); return map; } finally { bcn.byteCodeMap.remove(); instrumentation.removeTransformer(bcn); } } /** * Creates a new ByteCodeNet * @param targetClassName The class to get the byte code for */ private ByteCodeNet(String targetClassName) { super(); this.targetClassName = targetClassName; } /** * {@inheritDoc} * @see java.lang.instrument.ClassFileTransformer#transform(java.lang.ClassLoader, java.lang.String, java.lang.Class, java.security.ProtectionDomain, byte[]) */ @Override public byte[] transform(ClassLoader loader, String className, Class<?> redefineClass, ProtectionDomain protectionDomain, byte[] classBytes) throws IllegalClassFormatException { Map<String, byte[]> map = byteCodeMap.get(); if(map!=null) { map.put(className, classBytes); System.out.println("Added class [" + className + "]"); } return classBytes; } }