All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.polyglotted.common.web.HttpResourceModel Maven / Gradle / Ivy
package io.polyglotted.common.web;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import static io.polyglotted.common.util.ListBuilder.immutableList;
import static io.polyglotted.common.util.ListBuilder.immutableSet;
import static io.polyglotted.common.util.MapBuilder.immutableMap;
import static io.polyglotted.common.web.ParamConvertUtils.createPathParamConverter;
import static io.polyglotted.common.web.ParamConvertUtils.createQueryParamConverter;
import static io.polyglotted.common.web.WebException.internalServerException;
import static java.util.Objects.requireNonNull;
@SuppressWarnings({"WeakerAccess", "unchecked"})
@Accessors(fluent = true) @ToString(of = {"httpMethods", "path", "method"})
public final class HttpResourceModel {
private static final Set> SUPPORTED_PARAM_ANNOTATIONS = immutableSet(PathParam.class, QueryParam.class);
@Getter private final Set httpMethods;
@Getter private final String path;
private final Method method;
private final List, ParameterInfo>>> paramsInfo;
HttpResourceModel(Set httpMethods, String path, Method method) {
this.httpMethods = httpMethods;
this.path = path;
this.method = method;
this.paramsInfo = createParametersInfos(method);
}
private static List, ParameterInfo>>> createParametersInfos(Method method) {
if (method.getParameterTypes().length <= 2) { return ImmutableList.of(); }
ImmutableList.Builder, ParameterInfo>>> result = ImmutableList.builder();
Type[] parameterTypes = method.getGenericParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 2; i < parameterAnnotations.length; i++) {
Annotation[] annotations = parameterAnnotations[i];
Map, ParameterInfo>> paramAnnotations = Maps.newIdentityHashMap();
for (Annotation annotation : annotations) {
Class extends Annotation> annotationType = annotation.annotationType();
ParameterInfo> parameterInfo;
if (PathParam.class.isAssignableFrom(annotationType)) {
parameterInfo = new ParameterInfo<>(annotation, createPathParamConverter(parameterTypes[i]));
}
else if (QueryParam.class.isAssignableFrom(annotationType)) {
parameterInfo = new ParameterInfo<>(annotation, createQueryParamConverter(parameterTypes[i]));
}
else {
parameterInfo = new ParameterInfo<>(annotation, null);
}
paramAnnotations.put(annotationType, parameterInfo);
}
// Must have either @PathParam, @QueryParam or @HeaderParam, but not two or more.
if (Sets.intersection(SUPPORTED_PARAM_ANNOTATIONS, paramAnnotations.keySet()).size() != 1) {
throw new IllegalArgumentException(String.format("Must have exactly one annotation from %s " +
"for parameter %d in method %s", SUPPORTED_PARAM_ANNOTATIONS, i, method));
}
result.add(immutableMap(paramAnnotations));
}
return result.build();
}
void handle(AbstractHttpHandler handler, HttpRequest request, HttpResponder responder, Map groupValues) {
try {
Object[] args = new Object[paramsInfo.size() + 2];
args[0] = request; args[1] = responder;
int idx = 2;
for (Map, ParameterInfo>> info : paramsInfo) {
if (info.containsKey(PathParam.class)) { args[idx++] = getPathParamValue(info, groupValues); }
else if (info.containsKey(QueryParam.class)) { args[idx++] = getQueryParamValue(info, request); }
}
method.invoke(handler, args);
} catch (Throwable e) {
String deepMessage = e.getCause() == null ? e.getMessage() : e.getCause().getMessage();
throw internalServerException("Error in " + request.method + " " + request.uriPath + ": " + deepMessage);
}
}
private static Object getPathParamValue(Map, ParameterInfo>> annotations, Map groupValues) {
ParameterInfo info = (ParameterInfo) annotations.get(PathParam.class);
PathParam pathParam = info.getAnnotation();
String value = groupValues.get(pathParam.value());
return info.convert(requireNonNull(value, "Could not resolve value for parameter " + pathParam.value()));
}
private static Object getQueryParamValue(Map, ParameterInfo>> annotations, HttpRequest request) {
ParameterInfo> info = (ParameterInfo>) annotations.get(QueryParam.class);
QueryParam queryParam = info.getAnnotation();
List values = request.queryParams.get(queryParam.value());
return (values.isEmpty()) ? info.convert(defaultValue(annotations)) : info.convert(values);
}
private static List defaultValue(Map, ParameterInfo>> annotations) {
ParameterInfo> defaultInfo = annotations.get(DefaultValue.class);
return (defaultInfo != null) ? immutableList(defaultInfo.getAnnotation().value()) : immutableList();
}
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
private static final class ParameterInfo {
private final Annotation annotation;
private final Function converter;
@SuppressWarnings("unchecked") V getAnnotation() { return (V) annotation; }
Object convert(T input) { return (converter == null) ? null : converter.apply(input); }
}
}