All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.muserver.rest.ResourceClass Maven / Gradle / Ivy

There is a newer version: 2.0.3
Show newest version
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();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy