From f9ec00add4468ef6d157c61a09ed319262d2c21b Mon Sep 17 00:00:00 2001 From: Finn Date: Mon, 29 Sep 2025 18:45:18 +0200 Subject: [PATCH] Added annotation processing from "dev"-Branche --- pom.xml | 10 ++ .../protocol/annotations/ProtocolInfo.java | 4 + .../annotations/processing/CallTracker.java | 83 ++++++++++++++++ .../processing/ProtocolInfoProcessing.java | 99 +++++++++++++++++++ .../IncompatibleProtocolSideException.java | 12 +++ 5 files changed, 208 insertions(+) create mode 100644 src/main/java/org/openautonomousconnection/protocol/annotations/processing/CallTracker.java create mode 100644 src/main/java/org/openautonomousconnection/protocol/annotations/processing/ProtocolInfoProcessing.java create mode 100644 src/main/java/org/openautonomousconnection/protocol/exceptions/IncompatibleProtocolSideException.java diff --git a/pom.xml b/pom.xml index 9e848cb..3a0dbf7 100644 --- a/pom.xml +++ b/pom.xml @@ -127,6 +127,16 @@ 6.1.0 provided + + net.bytebuddy + byte-buddy + LATEST + + + net.bytebuddy + byte-buddy-agent + LATEST + diff --git a/src/main/java/org/openautonomousconnection/protocol/annotations/ProtocolInfo.java b/src/main/java/org/openautonomousconnection/protocol/annotations/ProtocolInfo.java index e908cf3..d357f02 100644 --- a/src/main/java/org/openautonomousconnection/protocol/annotations/ProtocolInfo.java +++ b/src/main/java/org/openautonomousconnection/protocol/annotations/ProtocolInfo.java @@ -2,9 +2,13 @@ package org.openautonomousconnection.protocol.annotations; import org.openautonomousconnection.protocol.versions.ProtocolVersion; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Annotation to provide metadata about protocol handlers or classes. */ +@Retention(RetentionPolicy.RUNTIME) public @interface ProtocolInfo { /** diff --git a/src/main/java/org/openautonomousconnection/protocol/annotations/processing/CallTracker.java b/src/main/java/org/openautonomousconnection/protocol/annotations/processing/CallTracker.java new file mode 100644 index 0000000..eeb0be9 --- /dev/null +++ b/src/main/java/org/openautonomousconnection/protocol/annotations/processing/CallTracker.java @@ -0,0 +1,83 @@ +package org.openautonomousconnection.protocol.annotations.processing; + +import dev.unlegitdqrk.unlegitlibrary.reflections.GenericReflectClass; +import net.bytebuddy.agent.ByteBuddyAgent; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.asm.Advice; + +import javax.annotation.Nullable; +import java.lang.annotation.Annotation; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +import static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; + +public class CallTracker extends GenericReflectClass { + private static final AtomicReference> atomicClass = new AtomicReference<>(); + + public CallTracker(CallInterceptor interceptor) { + super(); + + atomicClass.set(this.persistentClass); + } + + public static void premain(String agentArgs, Instrumentation inst) { + ByteBuddyAgent.install(); + + new AgentBuilder.Default() + .type(any()) // instrument all classes, you can restrict here + .transform((builder, type, classLoader, module, protectionDomain) -> + builder.visit(Advice.to(CallInterceptor.class).on(isMethod())) + ).installOn(inst); + } + + public abstract static class CallInterceptor { + private static Set interceptors = new HashSet<>(); + + public CallInterceptor() { + interceptors.add(this); + } + + /** + * Code executed on any method call + */ + public abstract void onCall(Method method, @Nullable StackTraceElement callerMethod); + + @Advice.OnMethodEnter + static void intercept(@Advice.Origin Method method) { + + for(CallInterceptor interceptor : interceptors) { + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + + if (stack.length <= 3) + return; + + StackTraceElement caller = stack[3]; + + interceptor.onCall(method, caller); + } + +// if (method.isAnnotationPresent(atomicClass.get())) { +// StackTraceElement[] stack = Thread.currentThread().getStackTrace(); +// // stack[0] = getStackTrace +// // stack[1] = intercept +// // stack[2] = Advice dispatcher +// // stack[3+] = your actual caller +// if (stack.length <= 3) +// return; +// +// +// +// StackTraceElement caller = stack[3]; +// +// System.out.println("Annotated method " + method.getName() +// + " was called by " + caller.getClassName() + "." + caller.getMethodName()); +// +// } + } + } +} diff --git a/src/main/java/org/openautonomousconnection/protocol/annotations/processing/ProtocolInfoProcessing.java b/src/main/java/org/openautonomousconnection/protocol/annotations/processing/ProtocolInfoProcessing.java new file mode 100644 index 0000000..4e32ace --- /dev/null +++ b/src/main/java/org/openautonomousconnection/protocol/annotations/processing/ProtocolInfoProcessing.java @@ -0,0 +1,99 @@ +// Author: maple +// date: 9/29/25 + +package org.openautonomousconnection.protocol.annotations.processing; + +import dev.unlegitdqrk.unlegitlibrary.reflections.annotation.processing.AnnotationProcessor; +import org.openautonomousconnection.protocol.annotations.ProtocolInfo; +import org.openautonomousconnection.protocol.exceptions.IncompatibleProtocolSideException; +import org.openautonomousconnection.protocol.versions.ProtocolVersion; + +import javax.annotation.Nullable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Process ProtocolInfo annotation and throw exception on mismatching annotation and ProtocolSide + */ +public class ProtocolInfoProcessing extends AnnotationProcessor { + + private final CallTracker tracker; + + private final AtomicReference> methodReferences = new AtomicReference<>(); + private final AtomicReference>> typeReferences = new AtomicReference<>(); + + public ProtocolInfoProcessing() { + super("org.openautonomousconnection.protocol"); + + this.process(); + + this.methodReferences.set(this.annotatedMethods); + + this.typeReferences.set(this.annotatedTypes); + + this.tracker = new CallTracker<>(new CallTracker.CallInterceptor() { + @Override + public void onCall(Method method, @Nullable StackTraceElement callerMethod) { + ProtocolVersion.ProtocolSide side, callerSide; + + Object o; + + if((o = methodGetByName(callerMethod.getMethodName())) != null) + callerSide = ((Method) o).getAnnotation(ProtocolInfo.class).protocolSide(); + else if((o = typeHasAnnotation(callerMethod.getClassName())) != null) + callerSide = ((Class) o).getAnnotation(ProtocolInfo.class).protocolSide(); + else + return; + + + if(methodReferences.get().contains(method)) + side = method.getAnnotation(ProtocolInfo.class).protocolSide(); + + else if(typeReferences.get().contains(method.getDeclaringClass())) + side = method.getDeclaringClass().getAnnotation(ProtocolInfo.class).protocolSide(); + else + return; + + if(callerSide.equals(ProtocolVersion.ProtocolSide.CLIENT) && + !side.equals(callerSide)) + throw new IncompatibleProtocolSideException(callerSide, side); + + + + } + }); + } + + private Method methodGetByName(String methodName) { + for(Method method : this.annotatedMethods) + if(method.getName().equals(methodName)) + return method; + return null; + } + + private Class typeHasAnnotation(String typeName) { + for(Class type : this.annotatedTypes) + if(type.getName().equals(typeName)) + return type; + return null; + } + + @Override + protected void processType(Class type) { + } + + @Override + protected void processMethod(Method method) { + } + + @Override + protected void processField(Field field) { + } + + @Override + protected void processConstructor(Constructor constructor) { + } +} diff --git a/src/main/java/org/openautonomousconnection/protocol/exceptions/IncompatibleProtocolSideException.java b/src/main/java/org/openautonomousconnection/protocol/exceptions/IncompatibleProtocolSideException.java new file mode 100644 index 0000000..e871ef3 --- /dev/null +++ b/src/main/java/org/openautonomousconnection/protocol/exceptions/IncompatibleProtocolSideException.java @@ -0,0 +1,12 @@ +package org.openautonomousconnection.protocol.exceptions; + +import org.openautonomousconnection.protocol.versions.ProtocolVersion; + +/** + * Exception thrown when an unsupported protocol side method is called. + */ +public class IncompatibleProtocolSideException extends RuntimeException { + public IncompatibleProtocolSideException(ProtocolVersion.ProtocolSide callerSide, ProtocolVersion.ProtocolSide side) { + super(callerSide.name() + " is incompatible with called method of ProtocolSide " + side.name()); + } +}