From 8a3643d63482e733109caed3b6097641147e7879 Mon Sep 17 00:00:00 2001 From: Brett Wooldridge Date: Fri, 18 Oct 2013 07:26:35 +0900 Subject: [PATCH] More work on class transformer --- pom.xml | 10 +- .../hikari/proxy/HikariClassTransformer.java | 175 +++++++++++++++++- 2 files changed, 183 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 9f5c789c..b54cb767 100644 --- a/pom.xml +++ b/pom.xml @@ -103,6 +103,13 @@ hsqldb 2.3.0 + + com.sun + tools + 1.6.0 + system + ${java.home}/../lib/tools.jar + @@ -118,8 +125,9 @@ HikariCP com.zaxxer.hikari - javassist.*,javax.management,javax.sql,javax.sql.rowset,,javax.sql.rowset.serial,,javax.sql.rowset.spi,org.slf4j + com.sun.tools.attach,javassist.*,javax.management,javax.sql,javax.sql.rowset,,javax.sql.rowset.serial,,javax.sql.rowset.spi,org.slf4j ${project.groupId}.${project.artifactId} + com.zaxxer.hikari.proxy.HikariClassTransformer diff --git a/src/main/java/com/zaxxer/hikari/proxy/HikariClassTransformer.java b/src/main/java/com/zaxxer/hikari/proxy/HikariClassTransformer.java index 4d2925e4..c7dd6475 100644 --- a/src/main/java/com/zaxxer/hikari/proxy/HikariClassTransformer.java +++ b/src/main/java/com/zaxxer/hikari/proxy/HikariClassTransformer.java @@ -16,9 +16,21 @@ package com.zaxxer.hikari.proxy; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.lang.management.ManagementFactory; +import java.net.URL; +import java.nio.ByteBuffer; import java.security.ProtectionDomain; +import java.util.HashMap; +import java.util.HashSet; + +import javassist.bytecode.ClassFile; + +import com.sun.tools.attach.VirtualMachine; /** * @@ -26,10 +38,171 @@ import java.security.ProtectionDomain; */ public class HikariClassTransformer implements ClassFileTransformer { + private String sniffPackage; + + public static void loadTransformerAgent(String sniffPackage) + { + String jarPath = getSelfJarPath(); + if (jarPath == null) + { + return; + } + + String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName(); + int p = nameOfRunningVM.indexOf('@'); + String pid = nameOfRunningVM.substring(0, p); + + try + { + VirtualMachine vm = VirtualMachine.attach(pid); + vm.loadAgent("/Users/brettw/Documents/dev/HikariCP/target/HikariCP-0.9-SNAPSHOT.jar", sniffPackage); + vm.detach(); + } + catch (Exception e) + { + return; + } + } + + public static void agentmain(String agentArgs, Instrumentation inst) + { + inst.addTransformer(new HikariClassTransformer(agentArgs), false); + } + + HikariClassTransformer(String sniffPackage) + { + this.sniffPackage = sniffPackage.replace('.', '/'); + } + /** {@inheritDoc} */ public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - return null; + if (!className.startsWith(sniffPackage)) + { + return classfileBuffer; + } + + try + { + ClassFile classFile = new ClassFile(new DataInputStream(new ByteArrayInputStream(classfileBuffer))); + String[] interfaces = classFile.getInterfaces(); + return classfileBuffer; + } + catch (Exception e) + { + return classfileBuffer; + } + } + + /** + * High-speed class file sniffer to determine if the class in question + * implements the specified interface. + * + * @param classFileBytes + * @return true if the + */ + private boolean sniffClass(byte[] classFileBytes) + { + ByteBuffer buffer = ByteBuffer.wrap(classFileBytes); + + buffer.getInt(); // 0xCAFEBABE + buffer.getInt(); // minor/major version + + HashMap stringPool = new HashMap(128); + HashMap classRefs = new HashMap(); + HashMap nameAndDescriptor = new HashMap(); + HashSet interfaceRefs = new HashSet(); + + int constantPoolSize = buffer.getShort(); + int slot = 1; + while (slot < constantPoolSize) + { + byte tag = buffer.get(); + switch (tag) + { + case 1: // UTF-8 String + short len = buffer.getShort(); + byte[] buf = new byte[len]; + buffer.get(buf); + stringPool.put(slot, new String(buf)); + slot++; + break; + case 3: // Integer: a signed 32-bit two's complement number in big-endian format + buffer.getInt(); + slot++; + break; + case 4: // Float: a 32-bit single-precision IEEE 754 floating-point number + buffer.getFloat(); + slot++; + break; + case 5: // Long: a signed 64-bit two's complement number in big-endian format (takes two slots in the constant pool table) + buffer.getLong(); + slot += 2; + break; + case 6: // Double: a 64-bit double-precision IEEE 754 floating-point number (takes two slots in the constant pool table) + buffer.getDouble(); + slot += 2; + break; + case 7: // Class reference: an index within the constant pool to a UTF-8 string containing the fully qualified class name + int index = buffer.getShort(); + classRefs.put(slot, index); + slot++; + break; + case 8: // String reference: an index within the constant pool to a UTF-8 string (big-endian too) + int sRef = buffer.getShort(); + slot++; + break; + case 9: // Field reference: two indexes within the constant pool, the first a Class reference, the second a Name and Type descriptor. + int fRef1 = buffer.getShort(); + int fRef2 = buffer.getShort(); + slot++; + break; + case 10: // Method reference: two indexes within the constant pool, ... + int mRef1 = buffer.getShort(); + int mRef2 = buffer.getShort(); + slot++; + break; + case 11: // Interface method reference: two indexes within the constant pool, ... + int iRef1 = buffer.getShort(); + int iRef2 = buffer.getShort(); + interfaceRefs.add(iRef1); + slot++; + break; + case 12: // Name and type descriptor: two indexes to UTF-8 strings within the constant pool, the first representing a name + // (identifier) and the second a specially encoded type descriptor. + int nameIndex = buffer.getShort(); + int descIndex = buffer.getShort(); + nameAndDescriptor.put(slot, nameIndex); + slot++; + break; + } + } + + return false; + } + + private static String getSelfJarPath() + { + URL resource = HikariClassTransformer.class.getResource('/' + HikariClassTransformer.class.getName().replace('.', '/') + ".class"); + if (resource == null) + { + return null; + } + + System.out.println(resource); + String jarPath = resource.toString(); + jarPath = jarPath.replace("file:", ""); + jarPath = jarPath.replace("jar:", ""); + if (jarPath.indexOf('!') > 0) + { + jarPath = jarPath.substring(0, jarPath.indexOf('!')); + } + else + { + jarPath = jarPath.substring(0, jarPath.lastIndexOf('/')); + } + + return jarPath; } }