Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions client/src/main/java/dev/restate/client/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
// https://github.com/restatedev/sdk-java/blob/main/LICENSE
package dev.restate.client;

import static dev.restate.common.reflections.ReflectionUtils.mustHaveAnnotation;

import dev.restate.common.Output;
import dev.restate.common.Request;
import dev.restate.common.Target;
Expand Down Expand Up @@ -556,7 +554,7 @@ default Response<Output<Res>> getOutput() throws IngressException {
*/
@org.jetbrains.annotations.ApiStatus.Experimental
default <SVC> SVC service(Class<SVC> clazz) {
mustHaveAnnotation(clazz, Service.class);
ReflectionUtils.mustHaveServiceAnnotation(clazz);
var serviceName = ReflectionUtils.extractServiceName(clazz);
return ProxySupport.createProxy(
clazz,
Expand Down Expand Up @@ -607,7 +605,7 @@ default <SVC> SVC service(Class<SVC> clazz) {
*/
@org.jetbrains.annotations.ApiStatus.Experimental
default <SVC> ClientServiceHandle<SVC> serviceHandle(Class<SVC> clazz) {
mustHaveAnnotation(clazz, Service.class);
ReflectionUtils.mustHaveServiceAnnotation(clazz);
return new ClientServiceHandleImpl<>(this, clazz, null);
}

Expand Down Expand Up @@ -635,7 +633,7 @@ default <SVC> ClientServiceHandle<SVC> serviceHandle(Class<SVC> clazz) {
*/
@org.jetbrains.annotations.ApiStatus.Experimental
default <SVC> SVC virtualObject(Class<SVC> clazz, String key) {
mustHaveAnnotation(clazz, VirtualObject.class);
ReflectionUtils.mustHaveVirtualObjectAnnotation(clazz);
var serviceName = ReflectionUtils.extractServiceName(clazz);
return ProxySupport.createProxy(
clazz,
Expand Down Expand Up @@ -687,7 +685,7 @@ default <SVC> SVC virtualObject(Class<SVC> clazz, String key) {
*/
@org.jetbrains.annotations.ApiStatus.Experimental
default <SVC> ClientServiceHandle<SVC> virtualObjectHandle(Class<SVC> clazz, String key) {
mustHaveAnnotation(clazz, VirtualObject.class);
ReflectionUtils.mustHaveVirtualObjectAnnotation(clazz);
return new ClientServiceHandleImpl<>(this, clazz, key);
}

Expand Down Expand Up @@ -715,7 +713,7 @@ default <SVC> ClientServiceHandle<SVC> virtualObjectHandle(Class<SVC> clazz, Str
*/
@org.jetbrains.annotations.ApiStatus.Experimental
default <SVC> SVC workflow(Class<SVC> clazz, String key) {
mustHaveAnnotation(clazz, Workflow.class);
ReflectionUtils.mustHaveWorkflowAnnotation(clazz);
var serviceName = ReflectionUtils.extractServiceName(clazz);
return ProxySupport.createProxy(
clazz,
Expand Down Expand Up @@ -767,7 +765,7 @@ default <SVC> SVC workflow(Class<SVC> clazz, String key) {
*/
@org.jetbrains.annotations.ApiStatus.Experimental
default <SVC> ClientServiceHandle<SVC> workflowHandle(Class<SVC> clazz, String key) {
mustHaveAnnotation(clazz, Workflow.class);
ReflectionUtils.mustHaveWorkflowAnnotation(clazz);
return new ClientServiceHandleImpl<>(this, clazz, key);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@

public class ReflectionUtils {

private static final @Nullable Class<? extends Annotation> RESTATE_SPRING_SERVICE_ANNOTATION =
tryLoadClass("dev.restate.sdk.springboot.RestateService");
private static final @Nullable Class<? extends Annotation>
RESTATE_SPRING_VIRTUAL_OBJECT_ANNOTATION =
tryLoadClass("dev.restate.sdk.springboot.RestateVirtualObject");
private static final @Nullable Class<? extends Annotation> RESTATE_SPRING_WORKFLOW_ANNOTATION =
tryLoadClass("dev.restate.sdk.springboot.RestateWorkflow");

/** Record containing handler information extracted from annotations. */
public record HandlerInfo(String name, boolean shared) {}

Expand Down Expand Up @@ -163,16 +171,17 @@ private static String inferRestateNameFromHierarchy(Class<?> type) {
}

// Check if the type has any of the Restate component annotations
var restateServiceAnnotation = type.getAnnotation(Service.class);
if (restateServiceAnnotation != null) {
return extractNameFromAnnotations(type);
}
var restateVirtualObjectAnnotation = type.getAnnotation(VirtualObject.class);
if (restateVirtualObjectAnnotation != null) {
return extractNameFromAnnotations(type);
}
var restateWorkflowAnnotation = type.getAnnotation(Workflow.class);
if (restateWorkflowAnnotation != null) {
var isRestateAnnotated =
type.getAnnotation(Service.class) != null
|| type.getAnnotation(VirtualObject.class) != null
|| type.getAnnotation(Workflow.class) != null
|| (RESTATE_SPRING_SERVICE_ANNOTATION != null
&& type.getAnnotation(RESTATE_SPRING_SERVICE_ANNOTATION) != null)
|| (RESTATE_SPRING_VIRTUAL_OBJECT_ANNOTATION != null
&& type.getAnnotation(RESTATE_SPRING_VIRTUAL_OBJECT_ANNOTATION) != null)
|| (RESTATE_SPRING_WORKFLOW_ANNOTATION != null
&& type.getAnnotation(RESTATE_SPRING_WORKFLOW_ANNOTATION) != null);
if (isRestateAnnotated) {
return extractNameFromAnnotations(type);
}

Expand Down Expand Up @@ -200,17 +209,49 @@ private static String extractNameFromAnnotations(Class<?> type) {
return type.getSimpleName();
}

public static <A extends Annotation> A mustHaveAnnotation(
Class<?> clazz, Class<A> annotationClazz) {
A annotation = findAnnotation(clazz, annotationClazz);
if (annotation == null) {
public static boolean hasServiceAnnotation(Class<?> clazz) {
return findAnnotation(clazz, Service.class) != null
|| (RESTATE_SPRING_SERVICE_ANNOTATION != null
&& findAnnotation(clazz, RESTATE_SPRING_SERVICE_ANNOTATION) != null);
}

public static void mustHaveServiceAnnotation(Class<?> clazz) {
if (!hasServiceAnnotation(clazz)) {
throw new IllegalArgumentException(
"The given class "
+ clazz.getName()
+ " is not annotated with the Restate service annotation");
}
}

public static boolean hasVirtualObjectAnnotation(Class<?> clazz) {
return findAnnotation(clazz, VirtualObject.class) != null
|| (RESTATE_SPRING_VIRTUAL_OBJECT_ANNOTATION != null
&& findAnnotation(clazz, RESTATE_SPRING_VIRTUAL_OBJECT_ANNOTATION) != null);
}

public static void mustHaveVirtualObjectAnnotation(Class<?> clazz) {
if (!hasVirtualObjectAnnotation(clazz)) {
throw new IllegalArgumentException(
"The given class "
+ clazz.getName()
+ " is not annotated with the Restate virtualObject annotation");
}
}

public static boolean hasWorkflowAnnotation(Class<?> clazz) {
return findAnnotation(clazz, Workflow.class) != null
|| (RESTATE_SPRING_WORKFLOW_ANNOTATION != null
&& findAnnotation(clazz, RESTATE_SPRING_WORKFLOW_ANNOTATION) != null);
}

public static void mustHaveWorkflowAnnotation(Class<?> clazz) {
if (!hasWorkflowAnnotation(clazz)) {
throw new IllegalArgumentException(
"The given class "
+ clazz.getName()
+ " is not annotated with @"
+ annotationClazz.getSimpleName());
+ " is not annotated with the Restate workflow annotation");
}
return annotation;
}

public static HandlerInfo mustHaveHandlerAnnotation(@NonNull Method method) {
Expand Down Expand Up @@ -308,6 +349,15 @@ public static boolean isKotlinClass(Class<?> clazz) {
.anyMatch(annotation -> annotation.annotationType().getName().equals("kotlin.Metadata"));
}

@SuppressWarnings("unchecked")
private static @Nullable <T> Class<T> tryLoadClass(String className) {
try {
return (Class<T>) Class.forName(className);
} catch (ClassNotFoundException e) {
return null;
}
}

// From Spring's ReflectionUtils
// License Apache 2.0

Expand Down
14 changes: 6 additions & 8 deletions sdk-api/src/main/java/dev/restate/sdk/Restate.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
// https://github.com/restatedev/sdk-java/blob/main/LICENSE
package dev.restate.sdk;

import static dev.restate.common.reflections.ReflectionUtils.mustHaveAnnotation;

import dev.restate.common.Request;
import dev.restate.common.Slice;
import dev.restate.common.Target;
Expand Down Expand Up @@ -430,7 +428,7 @@ public static AwakeableHandle awakeableHandle(String id) {
*/
@org.jetbrains.annotations.ApiStatus.Experimental
public static <SVC> SVC service(Class<SVC> clazz) {
mustHaveAnnotation(clazz, Service.class);
ReflectionUtils.mustHaveServiceAnnotation(clazz);
String serviceName = ReflectionUtils.extractServiceName(clazz);
return ProxySupport.createProxy(
clazz,
Expand Down Expand Up @@ -481,7 +479,7 @@ public static <SVC> SVC service(Class<SVC> clazz) {
*/
@org.jetbrains.annotations.ApiStatus.Experimental
public static <SVC> ServiceHandle<SVC> serviceHandle(Class<SVC> clazz) {
mustHaveAnnotation(clazz, Service.class);
ReflectionUtils.mustHaveServiceAnnotation(clazz);
return new ServiceHandleImpl<>(clazz, null);
}

Expand All @@ -506,7 +504,7 @@ public static <SVC> ServiceHandle<SVC> serviceHandle(Class<SVC> clazz) {
*/
@org.jetbrains.annotations.ApiStatus.Experimental
public static <SVC> SVC virtualObject(Class<SVC> clazz, String key) {
mustHaveAnnotation(clazz, VirtualObject.class);
ReflectionUtils.mustHaveVirtualObjectAnnotation(clazz);
String serviceName = ReflectionUtils.extractServiceName(clazz);
return ProxySupport.createProxy(
clazz,
Expand Down Expand Up @@ -558,7 +556,7 @@ public static <SVC> SVC virtualObject(Class<SVC> clazz, String key) {
*/
@org.jetbrains.annotations.ApiStatus.Experimental
public static <SVC> ServiceHandle<SVC> virtualObjectHandle(Class<SVC> clazz, String key) {
mustHaveAnnotation(clazz, VirtualObject.class);
ReflectionUtils.mustHaveVirtualObjectAnnotation(clazz);
return new ServiceHandleImpl<>(clazz, key);
}

Expand All @@ -583,7 +581,7 @@ public static <SVC> ServiceHandle<SVC> virtualObjectHandle(Class<SVC> clazz, Str
*/
@org.jetbrains.annotations.ApiStatus.Experimental
public static <SVC> SVC workflow(Class<SVC> clazz, String key) {
mustHaveAnnotation(clazz, Workflow.class);
ReflectionUtils.mustHaveWorkflowAnnotation(clazz);
String serviceName = ReflectionUtils.extractServiceName(clazz);
return ProxySupport.createProxy(
clazz,
Expand Down Expand Up @@ -635,7 +633,7 @@ public static <SVC> SVC workflow(Class<SVC> clazz, String key) {
*/
@org.jetbrains.annotations.ApiStatus.Experimental
public static <SVC> ServiceHandle<SVC> workflowHandle(Class<SVC> clazz, String key) {
mustHaveAnnotation(clazz, Workflow.class);
ReflectionUtils.mustHaveWorkflowAnnotation(clazz);
return new ServiceHandleImpl<>(clazz, key);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,9 @@ public ServiceDefinition create(

Class<?> serviceClazz = serviceInstance.getClass();

boolean hasServiceAnnotation =
ReflectionUtils.findAnnotation(serviceClazz, Service.class) != null;
boolean hasVirtualObjectAnnotation =
ReflectionUtils.findAnnotation(serviceClazz, VirtualObject.class) != null;
boolean hasWorkflowAnnotation =
ReflectionUtils.findAnnotation(serviceClazz, Workflow.class) != null;
boolean hasServiceAnnotation = ReflectionUtils.hasServiceAnnotation(serviceClazz);
boolean hasVirtualObjectAnnotation = ReflectionUtils.hasVirtualObjectAnnotation(serviceClazz);
boolean hasWorkflowAnnotation = ReflectionUtils.hasWorkflowAnnotation(serviceClazz);

boolean hasAnyAnnotation =
hasServiceAnnotation || hasVirtualObjectAnnotation || hasWorkflowAnnotation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@

import dev.restate.sdk.annotation.Handler;
import dev.restate.sdk.annotation.Name;
import dev.restate.sdk.annotation.Service;
import dev.restate.sdk.springboot.RestateComponent;
import dev.restate.sdk.springboot.RestateService;
import org.springframework.beans.factory.annotation.Value;

@Service
@RestateComponent
@RestateService
@Name("greeterNewApi")
public class GreeterNewApi {

Expand Down