diff --git a/nexus-sdk/src/main/java/io/nexusrpc/handler/MethodExtension.java b/nexus-sdk/src/main/java/io/nexusrpc/handler/MethodExtension.java new file mode 100644 index 0000000..4775500 --- /dev/null +++ b/nexus-sdk/src/main/java/io/nexusrpc/handler/MethodExtension.java @@ -0,0 +1,52 @@ +package io.nexusrpc.handler; + +import io.nexusrpc.ServiceDefinition; +import java.lang.reflect.Method; +import java.util.Objects; +import org.jspecify.annotations.Nullable; + +/** + * Extension point for {@link ServiceImplInstance#fromInstance(Object, java.util.List)} that lets a + * framework recognize its own operation-handler annotation alongside {@link OperationImpl}. + * + *
For each method discovered during scanning of a {@link ServiceImpl}-annotated class, the + * extension's {@link #extract} is called once. If the method belongs to the extension's annotation, + * the extension returns a {@link Result} describing the operation name and handler; otherwise it + * returns {@code null}. Duplicate operation registrations (across both {@link OperationImpl} and + * any extension) are caught by the builder. + * + *
Extensions are responsible for their own validation of method shape (parameter types, return + * type, modifiers) and for translating the method into an {@link OperationHandler}. + */ +public interface MethodExtension { + + /** + * Inspect {@code method} on {@code instance}. Return a {@link Result} to register an operation + * handler, or {@code null} if the method is not claimed by this extension. + * + * @param instance the service implementation instance + * @param method the method being scanned + * @param serviceDefinition the resolved service definition for the {@code @ServiceImpl} + * @return a result describing the operation, or {@code null} to skip + */ + @Nullable Result extract(Object instance, Method method, ServiceDefinition serviceDefinition); + + /** Result returned by an extension that claims a method. */ + final class Result { + private final String operationName; + private final OperationHandler, ?> handler; + + public Result(String operationName, OperationHandler, ?> handler) { + this.operationName = Objects.requireNonNull(operationName, "operationName"); + this.handler = Objects.requireNonNull(handler, "handler"); + } + + public String getOperationName() { + return operationName; + } + + public OperationHandler, ?> getHandler() { + return handler; + } + } +} diff --git a/nexus-sdk/src/main/java/io/nexusrpc/handler/ServiceImplInstance.java b/nexus-sdk/src/main/java/io/nexusrpc/handler/ServiceImplInstance.java index 0ca25fb..eabb84c 100644 --- a/nexus-sdk/src/main/java/io/nexusrpc/handler/ServiceImplInstance.java +++ b/nexus-sdk/src/main/java/io/nexusrpc/handler/ServiceImplInstance.java @@ -18,6 +18,20 @@ public class ServiceImplInstance { * operations. */ public static ServiceImplInstance fromInstance(Object instance) { + return fromInstance(instance, Collections.emptyList()); + } + + /** + * Create a service instance from the given object instance, with additional {@link + * MethodExtension}s consulted for each scanned method. Extensions let frameworks recognize their + * own operation-handler annotations alongside {@link OperationImpl}. + * + *
For every method on the service class, {@link OperationImpl} is processed first; then each
+ * extension is invoked. Duplicate operation registrations across {@link OperationImpl} and any
+ * extension fail the build.
+ */
+ public static ServiceImplInstance fromInstance(
+ Object instance, List