From 21ccf7423233f49a62063228bcc88d8c9eb3e2ca Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Mon, 12 Jan 2026 22:42:49 +0100 Subject: [PATCH] Small usability improvements to client proxy Check annotated method is public --- .../proxysupport/ByteBuddyProxyFactory.java | 26 +++++++++++++------ .../ReflectionServiceDefinitionFactory.java | 12 +++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/bytebuddy-proxy-support/src/main/java/dev/restate/bytebuddy/proxysupport/ByteBuddyProxyFactory.java b/bytebuddy-proxy-support/src/main/java/dev/restate/bytebuddy/proxysupport/ByteBuddyProxyFactory.java index f2252d3a..738210de 100644 --- a/bytebuddy-proxy-support/src/main/java/dev/restate/bytebuddy/proxysupport/ByteBuddyProxyFactory.java +++ b/bytebuddy-proxy-support/src/main/java/dev/restate/bytebuddy/proxysupport/ByteBuddyProxyFactory.java @@ -8,7 +8,13 @@ // https://github.com/restatedev/sdk-java/blob/main/LICENSE package dev.restate.bytebuddy.proxysupport; +import static net.bytebuddy.matcher.ElementMatchers.*; + import dev.restate.common.reflections.ProxyFactory; +import dev.restate.sdk.annotation.Exclusive; +import dev.restate.sdk.annotation.Handler; +import dev.restate.sdk.annotation.Shared; +import dev.restate.sdk.annotation.Workflow; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -17,7 +23,6 @@ import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.dynamic.scaffold.TypeValidation; import net.bytebuddy.implementation.InvocationHandlerAdapter; -import net.bytebuddy.matcher.ElementMatchers; import org.jspecify.annotations.Nullable; import org.objenesis.Objenesis; import org.objenesis.ObjenesisStd; @@ -42,7 +47,7 @@ public final class ByteBuddyProxyFactory implements ProxyFactory { public @Nullable T createProxy(Class clazz, MethodInterceptor interceptor) { // Cannot proxy final classes if (Modifier.isFinal(clazz.getModifiers())) { - return null; + throw new IllegalArgumentException("Class " + clazz + " is final, cannot be proxied."); } try { @@ -61,15 +66,13 @@ public final class ByteBuddyProxyFactory implements ProxyFactory { interceptorField.set(proxyInstance, interceptor); return proxyInstance; - } catch (Exception e) { - // Could not create or instantiate the proxy - return null; + throw new IllegalArgumentException("Cannot create proxy for class " + clazz, e); } } private Class generateProxyClass(Class clazz) { - ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.DISABLED); + ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.ENABLED); var builder = clazz.isInterface() @@ -81,7 +84,13 @@ private Class generateProxyClass(Class clazz) { // Add a field to store the interceptor .defineField(INTERCEPTOR_FIELD_NAME, MethodInterceptor.class, Visibility.PUBLIC) // Intercept all methods - .method(ElementMatchers.any()) + .method( + isMethod() + .and( + isAnnotatedWith(Handler.class) + .or(isAnnotatedWith(Exclusive.class)) + .or(isAnnotatedWith(Shared.class)) + .or(isAnnotatedWith(Workflow.class)))) .intercept( InvocationHandlerAdapter.of( (proxy, method, args) -> { @@ -91,7 +100,8 @@ private Class generateProxyClass(Class clazz) { MethodInterceptor interceptor = (MethodInterceptor) field.get(proxy); if (interceptor == null) { - throw new IllegalStateException("Interceptor not set on proxy instance"); + throw new IllegalStateException( + "Interceptor not set on proxy instance. This is a bug, please contact the developers."); } MethodInvocation invocation = diff --git a/sdk-api/src/main/java/dev/restate/sdk/internal/ReflectionServiceDefinitionFactory.java b/sdk-api/src/main/java/dev/restate/sdk/internal/ReflectionServiceDefinitionFactory.java index 6b716b36..d9028509 100644 --- a/sdk-api/src/main/java/dev/restate/sdk/internal/ReflectionServiceDefinitionFactory.java +++ b/sdk-api/src/main/java/dev/restate/sdk/internal/ReflectionServiceDefinitionFactory.java @@ -19,6 +19,7 @@ import dev.restate.serde.provider.DefaultSerdeFactoryProvider; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.text.MessageFormat; import java.util.*; import java.util.stream.Collectors; @@ -119,6 +120,17 @@ public ServiceDefinition create( var genericParameterTypes = method.getGenericParameterTypes(); var parameterCount = method.getParameterCount(); + if (!Modifier.isPublic(method.getModifiers())) { + throw new MalformedRestateServiceException( + serviceName, + "Handler method '" + + handlerName + + "' MUST be public, but method '" + + method.getName() + + "' has modifiers: " + + Modifier.toString(method.getModifiers())); + } + if ((parameterCount == 1 || parameterCount == 2) && (genericParameterTypes[0].equals(Context.class) || genericParameterTypes[0].equals(SharedObjectContext.class)