package com.yammer.telemetry.instrumentation;
import javassist.ByteArrayClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.Set;
public class TelemetryTransformer implements ClassFileTransformer {
private final Set<ClassInstrumentationHandler> handlers = new HashSet<>();
public void addHandler(ClassInstrumentationHandler handler) {
handlers.add(handler);
}
@Override
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
return transform(loader, className, classfileBuffer, ClassPool.getDefault());
}
/**
* Allows specifying the ClassPool, this allows tests to essentially 'reload' classes.
*
* @param loader
* @param className
* @param classfileBuffer
* @param cp
* @return
* @throws IllegalClassFormatException
*/
public byte[] transform(ClassLoader loader,
String className,
byte[] classfileBuffer,
ClassPool cp) throws IllegalClassFormatException {
try {
final String realClassName = className.replace('/', '.');
cp.insertClassPath(new LoaderClassPath(loader));
cp.insertClassPath(new ByteArrayClassPath(realClassName, classfileBuffer));
CtClass cc = cp.get(realClassName);
boolean classUpdated = false;
for (ClassInstrumentationHandler handler : handlers) {
if (classUpdated = handler.transformed(cc, cp)) {
break;
}
}
if (classUpdated) {
return cc.toBytecode();
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} catch (Throwable t) {
t.printStackTrace();
throw t;
}
}
}