Added annotation processing from "dev"-Branche
This commit is contained in:
10
pom.xml
10
pom.xml
@@ -127,6 +127,16 @@
|
||||
<version>6.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy-agent</artifactId>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@@ -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 {
|
||||
|
||||
/**
|
||||
|
@@ -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<A extends Annotation> extends GenericReflectClass<A> {
|
||||
private static final AtomicReference<Class<? extends Annotation>> 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<CallInterceptor> 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());
|
||||
//
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
@@ -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<ProtocolInfo> {
|
||||
|
||||
private final CallTracker<ProtocolInfo> tracker;
|
||||
|
||||
private final AtomicReference<Set<Method>> methodReferences = new AtomicReference<>();
|
||||
private final AtomicReference<Set<Class<?>>> 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) {
|
||||
}
|
||||
}
|
@@ -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());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user