package io.github.proxyhotswap; import io.github.proxyhotswap.javassist.ClassPool; import io.github.proxyhotswap.javassist.CtClass; import io.github.proxyhotswap.javassist.CtMethod; import io.github.proxyhotswap.javassist.Modifier; import io.github.proxyhotswap.javassist.NotFoundException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.lang.instrument.ClassFileTransformer; import java.lang.reflect.Method; import java.security.ProtectionDomain; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * @author Erki Ehtla * */ public class ClassfileBufferSigantureTransformer implements ClassFileTransformer { private static Map<String, String> classSignatures = new ConcurrentHashMap<>(); protected static final ClassPool classPool = TransformationUtils.getClassPool(); public byte[] transform(ClassLoader loader, String className, final Class<?> classBeingRedefined, ProtectionDomain protectionDomain, final byte[] classfileBuffer) { if (classBeingRedefined == null) return null; CtClass cc = null; try { cc = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false); classSignatures.put(classBeingRedefined.getName(), getSignature(cc)); } catch (IOException | RuntimeException e) { TransformationUtils.logError(e); } return null; } private static String getSignature(CtClass cc) { StringBuilder strBuilder = new StringBuilder(); for (CtMethod method : cc.getDeclaredMethods()) { strBuilder.append(getMethodString(method)); } return strBuilder.toString(); } private static String getMethodString(CtMethod method) { try { return Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getName() + " " + method.getName() + getParams(method.getParameterTypes()) + ";"; } catch (NotFoundException e) { throw new RuntimeException(e); } } private static String getParams(CtClass[] parameterTypes) { StringBuilder strB = new StringBuilder("("); for (CtClass ctClass : parameterTypes) { strB.append(ctClass.getName()); strB.append(", "); } strB.append(")"); return strB.toString(); } public static boolean hasClassChanged(Class<?> clazz) { String classString = classSignatures.get(clazz.getName()); if (classString == null) return false; return !getSignature(clazz).equals(classString); } public static boolean hasSuperClassOrInterfaceChanged(Class<?> clazz1) { Class<?> superclass = clazz1.getSuperclass(); if (superclass != null && ClassfileBufferSigantureTransformer.hasClassChanged(superclass)) return true; Class<?>[] interfaces = clazz1.getInterfaces(); for (Class<?> clazz : interfaces) { if (ClassfileBufferSigantureTransformer.hasClassChanged(clazz)) return true; } return false; } private static String getSignature(Class<?> cc) { StringBuilder strBuilder = new StringBuilder(); for (Method method : cc.getDeclaredMethods()) { strBuilder.append(getMethodString(method)); } return strBuilder.toString(); } private static Object getMethodString(Method method) { return Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getName() + " " + method.getName() + getParams(method.getParameterTypes()) + ";"; } private static String getParams(Class<?>[] parameterTypes) { StringBuilder strB = new StringBuilder("("); for (Class<?> ctClass : parameterTypes) { strB.append(ctClass.getName()); strB.append(", "); } strB.append(")"); return strB.toString(); } }