package org.rakam.server.http;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import org.rakam.server.http.util.Lambda;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static java.lang.String.format;
import static org.rakam.server.http.HttpServer.returnError;
public class JsonBeanRequestHandler implements HttpRequestHandler {
private final ObjectMapper mapper;
private final JavaType jsonClazz;
private final boolean isAsync;
private final HttpService service;
private final List<RequestPreprocessor> requestPreprocessors;
private final List<ResponsePostProcessor> postProcessors;
private final HttpServer httpServer;
private final BiFunction function;
public JsonBeanRequestHandler(HttpServer httpServer, ObjectMapper mapper, Method method,
List<RequestPreprocessor> requestPreprocessors,
List<ResponsePostProcessor> postProcessors,
HttpService service) {
this.httpServer = httpServer;
this.mapper = mapper;
this.service = service;
this.postProcessors = postProcessors;
function = Lambda.produceLambdaForBiFunction(method);
isAsync = CompletionStage.class.isAssignableFrom(method.getReturnType());
jsonClazz = mapper.constructType(method.getParameters()[0].getParameterizedType());
this.requestPreprocessors = requestPreprocessors;
}
@Override
public void handle(RakamHttpRequest request) {
request.bodyHandler(o -> {
Object json;
try {
json = mapper.readValue(o, jsonClazz);
} catch (UnrecognizedPropertyException e) {
returnError(request, "Unrecognized field: " + e.getPropertyName(), BAD_REQUEST);
return;
} catch (InvalidFormatException e) {
returnError(request, format("Field value couldn't validated: %s ", e.getOriginalMessage()), BAD_REQUEST);
return;
} catch (JsonMappingException e) {
returnError(request, e.getCause() != null ? e.getCause().getClass().getName() + ": " +e.getCause().getMessage() : e.getMessage(), BAD_REQUEST);
return;
} catch (JsonParseException e) {
returnError(request, format("Couldn't parse json: %s ", e.getOriginalMessage()), BAD_REQUEST);
return;
}catch (DateTimeParseException e) {
returnError(request, format("Couldn't parse date value '%s' in json: %s ", e.getParsedString(), e.getMessage()), BAD_REQUEST);
return;
} catch (IOException e) {
returnError(request, format("Error while mapping json: ", e.getMessage()), BAD_REQUEST);
return;
}
try {
if(!requestPreprocessors.isEmpty()) {
for (RequestPreprocessor preprocessor : requestPreprocessors) {
preprocessor.handle(request, null);
}
}
} catch (Throwable e) {
httpServer.requestError(e, request, postProcessors);
return;
}
if (isAsync) {
CompletionStage apply;
try {
apply = (CompletionStage) function.apply(service, json);
} catch (Throwable e) {
httpServer.requestError(e, request, postProcessors);
return;
}
httpServer.handleAsyncJsonRequest(mapper, request, apply, postProcessors);
} else {
httpServer.handleJsonRequest(mapper, service, request, (service) -> function.apply(service, json), postProcessors);
}
});
}
}