Added ProtocolInfoProcessing and its components

This commit is contained in:
Tinglyyy
2025-09-29 18:27:03 +02:00
parent 846fa167bc
commit dae382d07d
5 changed files with 210 additions and 0 deletions

10
pom.xml
View File

@@ -127,5 +127,15 @@
<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>
</project>

View File

@@ -4,6 +4,7 @@ import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
import lombok.Getter;
import lombok.Setter;
import org.openautonomousconnection.protocol.annotations.ProtocolInfo;
import org.openautonomousconnection.protocol.annotations.processing.ProtocolInfoProcessing;
import org.openautonomousconnection.protocol.listeners.ClientListener;
import org.openautonomousconnection.protocol.listeners.DNSServerListener;
import org.openautonomousconnection.protocol.listeners.WebServerListener;
@@ -95,6 +96,8 @@ public final class ProtocolBridge {
@Setter
private ClassicHandlerClient classicHandlerClient;
private final ProtocolInfoProcessing protocolInfoProcessing;
/**
* Initialize the ProtocolBridge instance for the DNS server side
* @param protocolDNSServer The ProtocolDNSServer instance
@@ -120,6 +123,8 @@ public final class ProtocolBridge {
// Set the static instance to this instance
instance = this;
this.protocolInfoProcessing = new ProtocolInfoProcessing();
}
/**
@@ -147,6 +152,8 @@ public final class ProtocolBridge {
// Set the static instance to this instance
instance = this;
this.protocolInfoProcessing = new ProtocolInfoProcessing();
}
/**
@@ -174,6 +181,8 @@ public final class ProtocolBridge {
// Set the static instance to this instance
instance = this;
this.protocolInfoProcessing = new ProtocolInfoProcessing();
}
/**

View File

@@ -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());
//
// }
}
}
}

View File

@@ -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.IncompatibleProtocolException;
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 IncompatibleProtocolException(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) {
}
}

View File

@@ -0,0 +1,9 @@
package org.openautonomousconnection.protocol.exceptions;
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
public class IncompatibleProtocolException extends RuntimeException {
public IncompatibleProtocolException(ProtocolVersion.ProtocolSide callerSide, ProtocolVersion.ProtocolSide side) {
super(callerSide.name() + " is incompatible with called method of ProtocolSide " + side.name());
}
}