tech.greenfield.vertx.irked.RouteConfigurationMethod Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of irked-vertx Show documentation
Show all versions of irked-vertx Show documentation
Opinionated framework for vertx-web route configuration and dispatch
package tech.greenfield.vertx.irked;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import tech.greenfield.vertx.irked.annotations.WebSocket;
import tech.greenfield.vertx.irked.exceptions.InvalidRouteConfiguration;
import tech.greenfield.vertx.irked.status.InternalServerError;
import tech.greenfield.vertx.irked.websocket.WebSocketMessage;
public class RouteConfigurationMethod extends RouteConfiguration {
private Method method;
private Class>[] params;
public RouteConfigurationMethod(Controller impl, Method m) throws InvalidRouteConfiguration {
super(impl, m.getAnnotations());
method = m;
if (!isValid())
return; // don't sanity check methods that aren't routing methods
//if ((m.getModifiers() & Modifier.PUBLIC) == 0)
// throw new InvalidRouteConfiguration("Method " + m.getName() + " is not public");
params = m.getParameterTypes();
if (isWebSocketHandler()) return; // satisfies
if (params.length < 1 || !RoutingContext.class.isAssignableFrom(params[0]))
throw new InvalidRouteConfiguration("Method " + m.getName() + " doesn't take a Vert.x RoutingContext as first parameter");
if (m.getParameterCount() > 1)
throw new InvalidRouteConfiguration("Method " + m.getName() + " requires more than one parameter which I don't know how to provide yet");
}
private boolean isWebSocketHandler() throws InvalidRouteConfiguration {
Map> types = Arrays.stream(annotations).collect(Collectors.partitioningBy((Annotation a) -> a.annotationType().equals(WebSocket.class)));
if (types.get(false).size() > 0 && types.get(true).size() > 0)
throw new InvalidRouteConfiguration("A WebSocket handler " + method + " cannot also be a request handler");
if (types.get(false).size() > 0)
return false;
if (params.length == 1 && WebSocketMessage.class.isAssignableFrom(params[0]))
return true;
if (params.length == 2 && Request.class.isAssignableFrom(params[0]) && WebSocketMessage.class.isAssignableFrom(params[1]))
return true;
throw new InvalidRouteConfiguration("A WebSocket handler " + method + " must accept (WebSocketMessage) or (Request,WebSocketMessage)");
}
@Override
protected T[] getAnnotation(Class anot) {
return method.getDeclaredAnnotationsByType(anot);
}
@Override
public boolean isController() {
return false; // a method is always a request handler and never a sub router
}
@Override
Controller getController() {
throw new RuntimeException("Not implemented");
}
@Override
protected String getName() {
return method.getName();
}
@Override
Handler super RoutingContext> getHandler() throws IllegalArgumentException, IllegalAccessException, InvalidRouteConfiguration {
method.setAccessible(true);
return new Handler() {
@Override
public void handle(RoutingContext r) {
// run time check for correct type
// we support working with methods that take specializations for Request, we'll rely on the specific implementation's
// getRequest() to provide the correct type
if (!params[0].isAssignableFrom(r.getClass())) {
r.fail(new InternalServerError("Invalid request handler " + this + " - can't handle request of type " + r.getClass()));
return;
}
try {
method.invoke(impl, r);
} catch (InvocationTargetException e) { // user exception
handleUserException(r, e.getCause(), "method " + method);
} catch (IllegalAccessException e) { // shouldn't happen because we setAccessible above
r.fail(new InternalServerError("Invalid request handler " + this + ": " + e, e));
} catch (IllegalArgumentException e) { // shouldn't happen because we checked the type before calling
r.fail(new InternalServerError("Mistyped request handler " + this + ": " + e, e));
}
}
@Override
public String toString() {
return method.getName() + "(" + Arrays.asList(method.getParameterTypes()).stream().map(Class::getSimpleName).collect(Collectors.joining(", ")) + ")";
}
};
}
@Override
Handler super WebSocketMessage> getMessageHandler() throws IllegalArgumentException, IllegalAccessException, InvalidRouteConfiguration {
method.setAccessible(true);
return new Handler() {
@Override
public void handle(WebSocketMessage m) {
Request req = m.request();
// run time check for correct type
// we support working with methods that take specializations for Request, we'll rely on the specific implementation's
// getRequest() to provide the correct type
if (!params[0].isAssignableFrom(m.getClass()) && !params[0].isAssignableFrom(req.getClass())) {
req.fail(new InternalServerError("Invalid request handler " + this + " - can't handle request of type " + req.getClass()));
return;
}
try {
if (params.length == 1)
method.invoke(impl, m);
else
method.invoke(impl, req, m);
} catch (InvocationTargetException e) { // user exception
handleUserException(m, e.getCause(), "method " + method);
} catch (IllegalAccessException | IllegalArgumentException e) {
// shouldn't happen because we setAccessible above and we checked the type before calling
handleUserException(m, e, "method " + method);
}
}
@Override
public String toString() {
return method.getName() + "(" + Arrays.asList(method.getParameterTypes()).stream().map(Class::getSimpleName).collect(Collectors.joining(", ")) + ")";
}
};
}
}