Skip to content

Commit 9641fa7

Browse files
Raw handlers (#303)
1 parent 1bbc34f commit 9641fa7

File tree

41 files changed

+1183
-381
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1183
-381
lines changed

sdk-api-gen-common/src/main/java/dev/restate/sdk/gen/model/Handler.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,25 @@
99
package dev.restate.sdk.gen.model;
1010

1111
import java.util.Objects;
12+
import org.jspecify.annotations.Nullable;
1213

1314
public class Handler {
1415

1516
private final CharSequence name;
1617
private final HandlerType handlerType;
18+
private final @Nullable String inputAccept;
1719
private final PayloadType inputType;
1820
private final PayloadType outputType;
1921

2022
public Handler(
21-
CharSequence name, HandlerType handlerType, PayloadType inputType, PayloadType outputType) {
23+
CharSequence name,
24+
HandlerType handlerType,
25+
@Nullable String inputAccept,
26+
PayloadType inputType,
27+
PayloadType outputType) {
2228
this.name = name;
2329
this.handlerType = handlerType;
30+
this.inputAccept = inputAccept;
2431
this.inputType = inputType;
2532
this.outputType = outputType;
2633
}
@@ -33,6 +40,10 @@ public HandlerType getHandlerType() {
3340
return handlerType;
3441
}
3542

43+
public String getInputAccept() {
44+
return inputAccept;
45+
}
46+
3647
public PayloadType getInputType() {
3748
return inputType;
3849
}
@@ -48,6 +59,7 @@ public static Builder builder() {
4859
public static class Builder {
4960
private CharSequence name;
5061
private HandlerType handlerType;
62+
private String inputAccept;
5163
private PayloadType inputType;
5264
private PayloadType outputType;
5365

@@ -61,6 +73,11 @@ public Builder withHandlerType(HandlerType handlerType) {
6173
return this;
6274
}
6375

76+
public Builder withInputAccept(String inputAccept) {
77+
this.inputAccept = inputAccept;
78+
return this;
79+
}
80+
6481
public Builder withInputType(PayloadType inputType) {
6582
this.inputType = inputType;
6683
return this;
@@ -96,7 +113,11 @@ public Handler validateAndBuild() {
96113
}
97114

98115
return new Handler(
99-
Objects.requireNonNull(name), Objects.requireNonNull(handlerType), inputType, outputType);
116+
Objects.requireNonNull(name),
117+
Objects.requireNonNull(handlerType),
118+
inputAccept,
119+
inputType,
120+
outputType);
100121
}
101122
}
102123
}

sdk-api-gen-common/src/main/java/dev/restate/sdk/gen/template/HandlebarsTemplateEngine.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,12 @@ private ServiceTemplateModel(
107107

108108
this.handlers =
109109
inner.getMethods().stream()
110-
.map(h -> new HandlerTemplateModel(h, handlerNamesToPrefix))
110+
.map(
111+
h ->
112+
new HandlerTemplateModel(
113+
h,
114+
this.generatedClassSimpleNamePrefix + "Definitions.Serde",
115+
handlerNamesToPrefix))
111116
.collect(Collectors.toList());
112117
}
113118
}
@@ -126,14 +131,18 @@ static class HandlerTemplateModel {
126131
public final String inputSerdeDecl;
127132
public final String boxedInputFqcn;
128133
public final String inputSerdeFieldName;
134+
public final String inputAcceptContentType;
135+
public final String inputSerdeRef;
129136

130137
public final boolean outputEmpty;
131138
public final String outputFqcn;
132139
public final String outputSerdeDecl;
133140
public final String boxedOutputFqcn;
134141
public final String outputSerdeFieldName;
142+
public final String outputSerdeRef;
135143

136-
private HandlerTemplateModel(Handler inner, Set<String> handlerNamesToPrefix) {
144+
private HandlerTemplateModel(
145+
Handler inner, String definitionsClass, Set<String> handlerNamesToPrefix) {
137146
this.name = inner.getName().toString();
138147
this.methodName = (handlerNamesToPrefix.contains(this.name) ? "_" : "") + this.name;
139148
this.handlerType = inner.getHandlerType().toString();
@@ -146,13 +155,16 @@ private HandlerTemplateModel(Handler inner, Set<String> handlerNamesToPrefix) {
146155
this.inputFqcn = inner.getInputType().getName();
147156
this.inputSerdeDecl = inner.getInputType().getSerdeDecl();
148157
this.boxedInputFqcn = inner.getInputType().getBoxed();
149-
this.inputSerdeFieldName = "SERDE_" + this.name.toUpperCase() + "_INPUT";
158+
this.inputSerdeFieldName = this.name.toUpperCase() + "_INPUT";
159+
this.inputAcceptContentType = inner.getInputAccept();
160+
this.inputSerdeRef = definitionsClass + "." + this.inputSerdeFieldName;
150161

151162
this.outputEmpty = inner.getOutputType().isEmpty();
152163
this.outputFqcn = inner.getOutputType().getName();
153164
this.outputSerdeDecl = inner.getOutputType().getSerdeDecl();
154165
this.boxedOutputFqcn = inner.getOutputType().getBoxed();
155-
this.outputSerdeFieldName = "SERDE_" + this.name.toUpperCase() + "_OUTPUT";
166+
this.outputSerdeFieldName = this.name.toUpperCase() + "_OUTPUT";
167+
this.outputSerdeRef = definitionsClass + "." + this.outputSerdeFieldName;
156168
}
157169
}
158170
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) 2023 - Restate Software, Inc., Restate GmbH
2+
//
3+
// This file is part of the Restate Java SDK,
4+
// which is released under the MIT license.
5+
//
6+
// You can find a copy of the license in file LICENSE in the root
7+
// directory of this repository or package, or at
8+
// https://github.com/restatedev/sdk-java/blob/main/LICENSE
9+
package dev.restate.sdk.gen.utils;
10+
11+
import java.lang.annotation.Annotation;
12+
import java.util.Objects;
13+
14+
public class AnnotationUtils {
15+
public static Object getAnnotationDefaultValue(
16+
Class<? extends Annotation> annotation, String name) {
17+
try {
18+
return Objects.requireNonNull(annotation.getMethod(name).getDefaultValue());
19+
} catch (NoSuchMethodException e) {
20+
throw new RuntimeException(e);
21+
}
22+
}
23+
}

sdk-api-gen/src/main/java/dev/restate/sdk/gen/ElementConverter.java

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,31 @@
1111
import dev.restate.sdk.Context;
1212
import dev.restate.sdk.ObjectContext;
1313
import dev.restate.sdk.SharedObjectContext;
14-
import dev.restate.sdk.annotation.Exclusive;
15-
import dev.restate.sdk.annotation.Shared;
16-
import dev.restate.sdk.annotation.Workflow;
14+
import dev.restate.sdk.annotation.*;
1715
import dev.restate.sdk.common.ServiceType;
1816
import dev.restate.sdk.gen.model.*;
17+
import dev.restate.sdk.gen.model.Handler;
18+
import dev.restate.sdk.gen.model.Service;
19+
import dev.restate.sdk.gen.utils.AnnotationUtils;
1920
import dev.restate.sdk.workflow.WorkflowContext;
2021
import dev.restate.sdk.workflow.WorkflowSharedContext;
2122
import java.util.List;
22-
import java.util.Objects;
2323
import java.util.regex.Pattern;
2424
import java.util.stream.Collectors;
2525
import javax.annotation.processing.Messager;
26-
import javax.lang.model.element.ElementKind;
27-
import javax.lang.model.element.ExecutableElement;
28-
import javax.lang.model.element.Modifier;
29-
import javax.lang.model.element.TypeElement;
26+
import javax.lang.model.element.*;
3027
import javax.lang.model.type.TypeKind;
3128
import javax.lang.model.type.TypeMirror;
3229
import javax.lang.model.util.Elements;
3330
import javax.lang.model.util.Types;
3431
import javax.tools.Diagnostic;
32+
import org.jspecify.annotations.Nullable;
3533

3634
public class ElementConverter {
3735

3836
private static final PayloadType EMPTY_PAYLOAD =
3937
new PayloadType(true, "", "Void", "dev.restate.sdk.common.CoreSerdes.VOID");
38+
private static final String RAW_SERDE = "dev.restate.sdk.common.CoreSerdes.RAW";
4039

4140
private final Messager messager;
4241
private final Elements elements;
@@ -197,18 +196,12 @@ private Handler fromExecutableElement(ServiceType serviceType, ExecutableElement
197196
return new Handler.Builder()
198197
.withName(element.getSimpleName())
199198
.withHandlerType(handlerType)
200-
.withInputType(
201-
element.getParameters().size() > 1
202-
? payloadFromType(element.getParameters().get(1).asType())
203-
: EMPTY_PAYLOAD)
204-
.withOutputType(
205-
!element.getReturnType().getKind().equals(TypeKind.VOID)
206-
? payloadFromType(element.getReturnType())
207-
: EMPTY_PAYLOAD)
199+
.withInputAccept(inputAcceptFromParameterList(element.getParameters()))
200+
.withInputType(inputPayloadFromParameterList(element.getParameters()))
201+
.withOutputType(outputPayloadFromExecutableElement(element))
208202
.validateAndBuild();
209203
} catch (Exception e) {
210-
messager.printMessage(
211-
Diagnostic.Kind.ERROR, "Error when building handler: " + e.getMessage(), element);
204+
messager.printMessage(Diagnostic.Kind.ERROR, "Error when building handler: " + e, element);
212205
return null;
213206
}
214207
}
@@ -280,13 +273,89 @@ private void validateFirstParameterType(Class<?> clazz, ExecutableElement elemen
280273
}
281274
}
282275

283-
private PayloadType payloadFromType(TypeMirror typeMirror) {
284-
Objects.requireNonNull(typeMirror);
285-
return new PayloadType(
286-
false, typeMirror.toString(), boxedType(typeMirror), serdeDecl(typeMirror));
276+
private String inputAcceptFromParameterList(List<? extends VariableElement> element) {
277+
if (element.size() <= 1) {
278+
return null;
279+
}
280+
281+
Accept accept = element.get(1).getAnnotation(Accept.class);
282+
if (accept == null) {
283+
return null;
284+
}
285+
return accept.value();
286+
}
287+
288+
private PayloadType inputPayloadFromParameterList(List<? extends VariableElement> element) {
289+
if (element.size() <= 1) {
290+
return EMPTY_PAYLOAD;
291+
}
292+
293+
Element parameterElement = element.get(1);
294+
return payloadFromTypeMirrorAndAnnotations(
295+
parameterElement.asType(),
296+
parameterElement.getAnnotation(Json.class),
297+
parameterElement.getAnnotation(Raw.class),
298+
parameterElement);
299+
}
300+
301+
private PayloadType outputPayloadFromExecutableElement(ExecutableElement element) {
302+
return payloadFromTypeMirrorAndAnnotations(
303+
element.getReturnType(),
304+
element.getAnnotation(Json.class),
305+
element.getAnnotation(Raw.class),
306+
element);
307+
}
308+
309+
private PayloadType payloadFromTypeMirrorAndAnnotations(
310+
TypeMirror ty, @Nullable Json jsonAnnotation, @Nullable Raw rawAnnotation, Element element) {
311+
if (ty.getKind().equals(TypeKind.VOID)) {
312+
if (rawAnnotation != null || jsonAnnotation != null) {
313+
messager.printMessage(
314+
Diagnostic.Kind.ERROR, "Unexpected annotation for void type.", element);
315+
}
316+
return EMPTY_PAYLOAD;
317+
}
318+
// Some validation
319+
if (rawAnnotation != null && jsonAnnotation != null) {
320+
messager.printMessage(
321+
Diagnostic.Kind.ERROR,
322+
"A parameter cannot be annotated both with @Raw and @Json.",
323+
element);
324+
}
325+
if (rawAnnotation != null
326+
&& !types.isSameType(ty, types.getArrayType(types.getPrimitiveType(TypeKind.BYTE)))) {
327+
messager.printMessage(
328+
Diagnostic.Kind.ERROR,
329+
"A parameter annotated with @Raw MUST be of type byte[], was " + ty,
330+
element);
331+
}
332+
333+
String serdeDecl = rawAnnotation != null ? RAW_SERDE : jsonSerdeDecl(ty);
334+
if (rawAnnotation != null
335+
&& !rawAnnotation
336+
.contentType()
337+
.equals(AnnotationUtils.getAnnotationDefaultValue(Raw.class, "contentType"))) {
338+
serdeDecl = contentTypeDecoratedSerdeDecl(serdeDecl, rawAnnotation.contentType());
339+
}
340+
if (jsonAnnotation != null
341+
&& !jsonAnnotation
342+
.contentType()
343+
.equals(AnnotationUtils.getAnnotationDefaultValue(Json.class, "contentType"))) {
344+
serdeDecl = contentTypeDecoratedSerdeDecl(serdeDecl, jsonAnnotation.contentType());
345+
}
346+
347+
return new PayloadType(false, ty.toString(), boxedType(ty), serdeDecl);
348+
}
349+
350+
private static String contentTypeDecoratedSerdeDecl(String serdeDecl, String contentType) {
351+
return "dev.restate.sdk.common.Serde.withContentType(\""
352+
+ contentType
353+
+ "\", "
354+
+ serdeDecl
355+
+ ")";
287356
}
288357

289-
private static String serdeDecl(TypeMirror ty) {
358+
private static String jsonSerdeDecl(TypeMirror ty) {
290359
switch (ty.getKind()) {
291360
case BOOLEAN:
292361
return "dev.restate.sdk.common.CoreSerdes.JSON_BOOLEAN";

sdk-api-gen/src/main/java/dev/restate/sdk/gen/ServiceProcessor.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
@SupportedSourceVersion(SourceVersion.RELEASE_11)
3636
public class ServiceProcessor extends AbstractProcessor {
3737

38+
private HandlebarsTemplateEngine definitionsCodegen;
3839
private HandlebarsTemplateEngine bindableServiceFactoryCodegen;
3940
private HandlebarsTemplateEngine bindableServiceCodegen;
4041
private HandlebarsTemplateEngine clientCodegen;
@@ -47,6 +48,18 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
4748

4849
FilerTemplateLoader filerTemplateLoader = new FilerTemplateLoader(processingEnv.getFiler());
4950

51+
this.definitionsCodegen =
52+
new HandlebarsTemplateEngine(
53+
"Definitions",
54+
filerTemplateLoader,
55+
Map.of(
56+
ServiceType.WORKFLOW,
57+
"templates/Definitions.hbs",
58+
ServiceType.SERVICE,
59+
"templates/Definitions.hbs",
60+
ServiceType.VIRTUAL_OBJECT,
61+
"templates/Definitions.hbs"),
62+
RESERVED_METHOD_NAMES);
5063
this.bindableServiceFactoryCodegen =
5164
new HandlebarsTemplateEngine(
5265
"BindableServiceFactory",
@@ -108,6 +121,7 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
108121
try {
109122
ThrowingFunction<String, Writer> fileCreator =
110123
name -> filer.createSourceFile(name, e.getKey()).openWriter();
124+
this.definitionsCodegen.generate(fileCreator, e.getValue());
111125
this.bindableServiceFactoryCodegen.generate(fileCreator, e.getValue());
112126
this.bindableServiceCodegen.generate(fileCreator, e.getValue());
113127
this.clientCodegen.generate(fileCreator, e.getValue());

0 commit comments

Comments
 (0)