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;
}
}