Skip to content

Commit

Permalink
fix: handle JVM deserialization of generic types
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Dec 20, 2024
1 parent 2041cb0 commit 2244764
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.regex.Pattern;

Expand All @@ -36,7 +35,6 @@

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.quarkus.arc.processor.DotNames;
import xyz.block.ftl.Config;
Expand Down Expand Up @@ -73,7 +71,6 @@
import xyz.block.ftl.schema.v1.TypeAlias;
import xyz.block.ftl.schema.v1.Unit;
import xyz.block.ftl.schema.v1.Verb;
import xyz.block.ftl.v1.CallRequest;

public class ModuleBuilder {

Expand Down Expand Up @@ -190,7 +187,7 @@ public void registerVerbMethod(MethodInfo method, String className,
boolean exported, BodyType bodyType, Consumer<Verb.Builder> metadataCallback) {
try {
List<Class<?>> parameterTypes = new ArrayList<>();
List<BiFunction<ObjectMapper, CallRequest, Object>> paramMappers = new ArrayList<>();
List<VerbRegistry.ParameterSupplier> paramMappers = new ArrayList<>();
org.jboss.jandex.Type bodyParamType = null;
Nullability bodyParamNullability = Nullability.MISSING;

Expand All @@ -199,7 +196,9 @@ public void registerVerbMethod(MethodInfo method, String className,
MetadataCalls.Builder callsMetadata = MetadataCalls.newBuilder();
MetadataConfig.Builder configMetadata = MetadataConfig.newBuilder();
MetadataSecrets.Builder secretMetadata = MetadataSecrets.newBuilder();
var pos = -1;
for (var param : method.parameters()) {
pos++;
if (param.hasAnnotation(Secret.class)) {
Class<?> paramType = ModuleBuilder.loadClass(param.type());
parameterTypes.add(paramType);
Expand Down Expand Up @@ -248,7 +247,7 @@ public void registerVerbMethod(MethodInfo method, String className,
Class<?> paramType = ModuleBuilder.loadClass(param.type());
parameterTypes.add(paramType);
//TODO: map and list types
paramMappers.add(new VerbRegistry.BodySupplier(paramType));
paramMappers.add(new VerbRegistry.BodySupplier(pos));
} else {
throw new RuntimeException("Unknown parameter type " + param.type() + " on FTL method: "
+ method.declaringClass().name() + "." + method.name());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.function.BiFunction;

import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
import org.jboss.resteasy.reactive.server.core.parameters.ParameterExtractor;
Expand All @@ -27,7 +26,7 @@ public class FTLRecorder {
public static final String X_FTL_VERB = "X-ftl-verb";

public void registerVerb(String module, String verbName, String methodName, List<Class<?>> parameterTypes,
Class<?> verbHandlerClass, List<BiFunction<ObjectMapper, CallRequest, Object>> paramMappers,
Class<?> verbHandlerClass, List<VerbRegistry.ParameterSupplier> paramMappers,
boolean allowNullReturn) {
//TODO: this sucks
try {
Expand Down Expand Up @@ -67,11 +66,11 @@ public void registerEnum(Class<?> ennum, List<Class<?>> variants) {
}
}

public BiFunction<ObjectMapper, CallRequest, Object> topicSupplier(String className, String callingVerb) {
public VerbRegistry.ParameterSupplier topicSupplier(String className, String callingVerb) {
try {
var cls = Thread.currentThread().getContextClassLoader().loadClass(className.replace("/", "."));
var topic = cls.getDeclaredConstructor(String.class).newInstance(callingVerb);
return new BiFunction<ObjectMapper, CallRequest, Object>() {
return new VerbRegistry.ParameterSupplier() {
@Override
public Object apply(ObjectMapper mapper, CallRequest callRequest) {
return topic;
Expand All @@ -82,11 +81,11 @@ public Object apply(ObjectMapper mapper, CallRequest callRequest) {
}
}

public BiFunction<ObjectMapper, CallRequest, Object> verbClientSupplier(String className) {
public VerbRegistry.ParameterSupplier verbClientSupplier(String className) {
try {
var cls = Thread.currentThread().getContextClassLoader().loadClass(className.replace("/", "."));
var client = cls.getDeclaredConstructor().newInstance();
return new BiFunction<ObjectMapper, CallRequest, Object>() {
return new VerbRegistry.ParameterSupplier() {
@Override
public Object apply(ObjectMapper mapper, CallRequest callRequest) {
return client;
Expand All @@ -97,8 +96,8 @@ public Object apply(ObjectMapper mapper, CallRequest callRequest) {
}
}

public BiFunction<ObjectMapper, CallRequest, Object> leaseClientSupplier() {
return new BiFunction<ObjectMapper, CallRequest, Object>() {
public VerbRegistry.ParameterSupplier leaseClientSupplier() {
return new VerbRegistry.ParameterSupplier() {

@Override
public Object apply(ObjectMapper mapper, CallRequest callRequest) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
Expand All @@ -16,6 +17,7 @@
import org.jboss.resteasy.reactive.server.core.parameters.ParameterExtractor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.google.protobuf.ByteString;

import io.quarkus.arc.Arc;
Expand All @@ -37,7 +39,7 @@ public VerbRegistry(ObjectMapper mapper) {
}

public void register(String module, String name, InstanceHandle<?> verbHandlerClass, Method method,
List<BiFunction<ObjectMapper, CallRequest, Object>> paramMappers, boolean allowNullReturn) {
List<ParameterSupplier> paramMappers, boolean allowNullReturn) {
verbs.put(new Key(module, name), new AnnotatedEndpointHandler(verbHandlerClass, method, paramMappers, allowNullReturn));
}

Expand All @@ -61,15 +63,18 @@ private record Key(String module, String name) {
private class AnnotatedEndpointHandler implements VerbInvoker {
final InstanceHandle<?> verbHandlerClass;
final Method method;
final List<BiFunction<ObjectMapper, CallRequest, Object>> parameterSuppliers;
final List<ParameterSupplier> parameterSuppliers;
final boolean allowNull;

private AnnotatedEndpointHandler(InstanceHandle<?> verbHandlerClass, Method method,
List<BiFunction<ObjectMapper, CallRequest, Object>> parameterSuppliers, boolean allowNull) {
List<ParameterSupplier> parameterSuppliers, boolean allowNull) {
this.verbHandlerClass = verbHandlerClass;
this.method = method;
this.parameterSuppliers = parameterSuppliers;
this.allowNull = allowNull;
for (ParameterSupplier parameterSupplier : parameterSuppliers) {
parameterSupplier.init(method);
}
}

public CallResponse handle(CallRequest in) {
Expand Down Expand Up @@ -106,19 +111,36 @@ public CallResponse handle(CallRequest in) {
}
}

public record BodySupplier(Class<?> inputClass) implements BiFunction<ObjectMapper, CallRequest, Object> {
public static class BodySupplier implements ParameterSupplier {

final int parameterIndex;
volatile Type inputClass;

public BodySupplier(int parameterIndex) {
this.parameterIndex = parameterIndex;
}

public void init(Method method) {
inputClass = method.getGenericParameterTypes()[parameterIndex];
}

@Override
public Object apply(ObjectMapper mapper, CallRequest in) {
try {
return mapper.createParser(in.getBody().newInput()).readValueAs(inputClass);
ObjectReader reader = mapper.reader();
return reader.forType(reader.getTypeFactory().constructType(inputClass))
.readValue(in.getBody().newInput());
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public int getParameterIndex() {
return parameterIndex;
}
}

public static class SecretSupplier implements BiFunction<ObjectMapper, CallRequest, Object>, ParameterExtractor {
public static class SecretSupplier implements ParameterSupplier, ParameterExtractor {

final String name;
final Class<?> inputClass;
Expand Down Expand Up @@ -153,7 +175,7 @@ public Object extractParameter(ResteasyReactiveRequestContext context) {
}
}

public static class ConfigSupplier implements BiFunction<ObjectMapper, CallRequest, Object>, ParameterExtractor {
public static class ConfigSupplier implements ParameterSupplier, ParameterExtractor {

final String name;
final Class<?> inputClass;
Expand Down Expand Up @@ -187,4 +209,12 @@ public String getName() {
}
}

public interface ParameterSupplier extends BiFunction<ObjectMapper, CallRequest, Object> {

// TODO: this is pretty yuck, but it lets us avoid a whole heap of nasty stuff to get the generic type
default void init(Method method) {

}

}
}

0 comments on commit 2244764

Please sign in to comment.