io.muserver.rest.ResourceClass Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mu-server Show documentation
Show all versions of mu-server Show documentation
A simple but powerful web server framework
package io.muserver.rest;
import io.muserver.Method;
import io.muserver.openapi.TagObject;
import javax.ws.rs.Consumes;
import javax.ws.rs.NameBinding;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ParamConverterProvider;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Parameter;
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
class ResourceClass {
final UriPattern pathPattern;
private final Class> resourceClass;
final Object resourceInstance;
final List produces;
final List consumes;
Set resourceMethods;
final String pathTemplate;
final TagObject tag;
final List> nameBindingAnnotations;
private ResourceClass(UriPattern pathPattern, String pathTemplate, Object resourceInstance, List consumes, List produces, TagObject tag, List> nameBindingAnnotations) {
this.pathPattern = pathPattern;
this.pathTemplate = pathTemplate;
this.resourceClass = resourceInstance.getClass();
this.resourceInstance = resourceInstance;
this.consumes = consumes;
this.produces = produces;
this.tag = tag;
this.nameBindingAnnotations = nameBindingAnnotations;
}
public boolean matches(URI uri) {
return pathPattern.matcher(uri).prefixMatches();
}
Set nonSubResourceMethods() {
return resourceMethods.stream().filter(resourceMethod -> !resourceMethod.isSubResource()).collect(Collectors.toSet());
}
Set subResourceMethods() {
return resourceMethods.stream().filter(ResourceMethod::isSubResource).collect(Collectors.toSet());
}
private void setupMethodInfo(List paramConverterProviders) {
if (resourceMethods != null) {
throw new IllegalStateException("Cannot call setupMethodInfo twice");
}
Set resourceMethods = new HashSet<>();
java.lang.reflect.Method[] methods = this.resourceClass.getMethods();
for (java.lang.reflect.Method restMethod : methods) {
java.lang.reflect.Method annotationSource = JaxMethodLocator.getMethodThatHasJaxRSAnnotations(restMethod);
Method httpMethod = ResourceMethod.getMuMethod(annotationSource);
if (httpMethod == null) {
continue;
}
restMethod.setAccessible(true);
Path methodPath = annotationSource.getAnnotation(Path.class);
List> methodNameBindingAnnotations = getNameBindingAnnotations(annotationSource);
UriPattern pathPattern = methodPath == null ? null : UriPattern.uriTemplateToRegex(methodPath.value());
List methodProduces = MediaTypeDeterminer.supportedProducesTypes(annotationSource);
List methodConsumes = MediaTypeDeterminer.supportedConsumesTypes(annotationSource);
List params = new ArrayList<>();
Parameter[] parameters = annotationSource.getParameters();
for (int i = 0; i < parameters.length; i++) {
Parameter p = parameters[i];
ResourceMethodParam resourceMethodParam = ResourceMethodParam.fromParameter(i, p, paramConverterProviders);
params.add(resourceMethodParam);
}
DescriptionData descriptionData = DescriptionData.fromAnnotation(restMethod, null);
String pathTemplate = methodPath == null ? null : methodPath.value();
boolean isDeprecated = annotationSource.isAnnotationPresent(Deprecated.class);
resourceMethods.add(new ResourceMethod(this, pathPattern, restMethod, params, httpMethod, pathTemplate, methodProduces, methodConsumes, descriptionData, isDeprecated, methodNameBindingAnnotations));
}
this.resourceMethods = Collections.unmodifiableSet(resourceMethods);
}
static List> getNameBindingAnnotations(AnnotatedElement annotationSource) {
return Stream.of(annotationSource.getAnnotations())
.filter(a -> a.annotationType().isAnnotationPresent(NameBinding.class))
.map(Annotation::annotationType)
.collect(toList());
}
static ResourceClass fromObject(Object restResource, List paramConverterProviders) {
Class> annotationSource = JaxClassLocator.getClassWithJaxRSAnnotations(restResource.getClass());
if (annotationSource == null) {
throw new IllegalArgumentException("The restResource class " + restResource.getClass().getName() + " must have a " + Path.class.getName() + " annotation to be eligible as a REST resource.");
}
// From section 3.6 of the spec:
// JAX-RS annotations MAY be used on the methods and method parameters of a super-class or an implemented interface.
// Such annotations are inherited by a corresponding sub-class or implementation class method provided that method
// and its parameters do not have any JAX-RS annotations of its own. Annotations on a super-class take precedence
// over those on an implemented interface. If a subclass or implementation method has any JAX-RS annotations then
// all of the annotations on the super class or interface method are ignored.
Path path = annotationSource.getDeclaredAnnotation(Path.class);
if (path == null) {
throw new IllegalArgumentException("The class " + annotationSource.getName() + " must specify a " + Path.class.getName()
+ " annotation because it has other JAX RS annotations declared. (Note that @Path cannot be inherited if there are other JAX RS annotations declared on this class.)");
}
UriPattern pathPattern = UriPattern.uriTemplateToRegex(path.value());
Produces produces = annotationSource.getAnnotation(Produces.class);
List producesList = MediaTypeHeaderDelegate.fromStrings(produces == null ? null : asList(produces.value()));
Consumes consumes = annotationSource.getAnnotation(Consumes.class);
List consumesList = MediaTypeHeaderDelegate.fromStrings(consumes == null ? null : asList(consumes.value()));
List> classLevelNameBindingAnnotations = getNameBindingAnnotations(annotationSource);
TagObject tag = DescriptionData.fromAnnotation(annotationSource, annotationSource.getSimpleName()).toTag();
ResourceClass resourceClass = new ResourceClass(pathPattern, path.value(), restResource, consumesList, producesList, tag, classLevelNameBindingAnnotations);
resourceClass.setupMethodInfo(paramConverterProviders);
return resourceClass;
}
@Override
public String toString() {
return "ResourceClass{" + resourceClassName() + '}';
}
String resourceClassName() {
return resourceClass.getName();
}
}