/** * 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.vm.agent; import groovy.lang.GroovyClassLoader; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicLong; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; import org.codehaus.groovy.runtime.GeneratedClosure; /** * <p>Title: ByteCodeCollectingClassFileTransformer</p> * <p>Description: </p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.vm.agent.ByteCodeCollectingClassFileTransformer</code></p> */ public class ByteCodeCollectingClassFileTransformer implements ClassFileTransformer { /** A map of bytecode byte arrays keyed by the class */ private final WeakHashMap<Class<?>, byte[]> byteCode = new WeakHashMap<Class<?>, byte[]>(); /** A map of bytecode byte arrays keyed by the class name */ private final WeakHashMap<String, byte[]> byteCodeByName = new WeakHashMap<String, byte[]>(); /** The notification broadcaster fired when targetted classes are loaded */ private final NotificationBroadcasterSupport broadcaster; /** Notification sequence number */ private static final AtomicLong sequence = new AtomicLong(0L); /** * Creates a new ByteCodeCollectingClassFileTransformer * @param broadcaster The notification broadcaster fired when targetted classes are loaded */ public ByteCodeCollectingClassFileTransformer(NotificationBroadcasterSupport broadcaster) { super(); this.broadcaster = broadcaster; } /** * {@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<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if(className.toLowerCase().contains("closure")) { System.out.println("Transform on Closure Class [" + className + "]"); } byte[] bytecode = classfileBuffer; if(loader instanceof GroovyClassLoader) { System.out.println("Transform on Groovy Class [" + className + "]"); Notification n = new Notification("Load", AgentInstrumentation.AGENT_INSTR_ON, sequence.incrementAndGet()); Map<String, Object> classInfo = new HashMap<String, Object>(); classInfo.put("ClassLoader", loader); classInfo.put("ClassName", className); classInfo.put("ClassBytes", classfileBuffer); n.setUserData(classInfo); byteCodeByName.put(className, bytecode); System.out.println("Stored [" + bytecode.length + "] Bytes for class [" + className + "]"); broadcaster.sendNotification(n); } if(classBeingRedefined!=null) { System.out.println("Redefine on Closure Class [" + className + "] with ClassLoader [" + loader + "]:[" + loader.getClass().getName() + "]"); if(GeneratedClosure.class.isAssignableFrom(classBeingRedefined) || (loader instanceof GroovyClassLoader.InnerLoader)) { System.out.println("Transform on Groovy Class [" + className + "]"); Notification n = new Notification("Redefine", AgentInstrumentation.AGENT_INSTR_ON, sequence.incrementAndGet()); Map<String, Object> classInfo = new HashMap<String, Object>(); classInfo.put("Class", classBeingRedefined); classInfo.put("ClassName", className); classInfo.put("ClassBytes", classfileBuffer); n.setUserData(classInfo); byteCode.put(classBeingRedefined, bytecode); System.out.println("Stored [" + bytecode.length + "] Bytes for class [" + className + "]"); broadcaster.sendNotification(n); } } return bytecode; } /** * Retrieves the byte code array for the passed class * @param clazz The class to get the byte code for * @return A byte array or null if the byte code was not found. */ public byte[] getByteCode(Class<?> clazz) { if(clazz==null) throw new IllegalArgumentException("The passed class was null", new Throwable()); byte[] bytecode = null; synchronized(byteCode) { bytecode = byteCode.get(clazz); } if(bytecode==null) { bytecode = byteCodeByName.get(clazz.getName()); } return bytecode; } }