org.jspare.forvertx.web.collector.HandlerCollector.java Source code

Java tutorial

Introduction

Here is the source code for org.jspare.forvertx.web.collector.HandlerCollector.java

Source

/*
 * Copyright 2016 JSpare.org.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.jspare.forvertx.web.collector;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.lang.StringUtils;
import org.jspare.forvertx.web.mapping.authentication.Auth;
import org.jspare.forvertx.web.mapping.authentication.IgnoreAuth;
import org.jspare.forvertx.web.mapping.content.Consumes;
import org.jspare.forvertx.web.mapping.content.Produces;
import org.jspare.forvertx.web.mapping.documentation.Documentation;
import org.jspare.forvertx.web.mapping.handlers.BlockingHandler;
import org.jspare.forvertx.web.mapping.handlers.BodyEndHandler;
import org.jspare.forvertx.web.mapping.handlers.FailureHandler;
import org.jspare.forvertx.web.mapping.method.All;
import org.jspare.forvertx.web.mapping.subrouter.IgnoreSubRouter;
import org.jspare.forvertx.web.mapping.subrouter.SubRouter;
import org.jspare.forvertx.web.transporter.Transporter;

import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class HandlerCollector {

    public static Collection<HandlerData> collect(Transporter transporter, Class<?> clazz) {

        List<HandlerData> collectedHandlers = new ArrayList<>();
        List<Annotation> httpMethodsAnnotations = new ArrayList<>(getHttpMethodsPresents(clazz));
        boolean hasAuthClass = clazz.isAnnotationPresent(Auth.class);
        Auth authClass = clazz.getAnnotation(Auth.class);

        for (Method method : clazz.getDeclaredMethods()) {

            if (!isHandler(method)) {

                continue;
            }

            final List<Annotation> handlerHttpMethodsAnnotations = new ArrayList<>();
            handlerHttpMethodsAnnotations.addAll(httpMethodsAnnotations);

            String consumes = method.isAnnotationPresent(Consumes.class)
                    ? method.getAnnotation(Consumes.class).value()
                    : StringUtils.EMPTY;
            String produces = method.isAnnotationPresent(Produces.class)
                    ? method.getAnnotation(Produces.class).value()
                    : StringUtils.EMPTY;
            Class<? extends Handler<RoutingContext>> routeHandlerClass = transporter.getRouteHandlerClass();
            List<org.jspare.forvertx.web.handler.BodyEndHandler> bodyEndHandler = collectBodyEndHandlers(
                    transporter, method);
            boolean hasMethodAuth = false;
            boolean skipAuthorities = false;
            String autority = StringUtils.EMPTY;
            HandlerDocumentation hDocumentation = null;

            if (method.isAnnotationPresent(Documentation.class)) {
                Documentation documentation = method.getAnnotation(Documentation.class);
                hDocumentation = new HandlerDocumentation();
                hDocumentation.description(documentation.description());
                hDocumentation.status(Arrays.asList(documentation.responseStatus()).stream()
                        .map(s -> new HandlerDocumentation.ResponseStatus(s)).collect(Collectors.toList()));
                hDocumentation.queryParameters(Arrays.asList(documentation.queryParameters()).stream()
                        .map(q -> new HandlerDocumentation.QueryParameter(q)).collect(Collectors.toList()));
                hDocumentation.requestSchema(documentation.requestClass());
                hDocumentation.responseSchema(documentation.responseClass());
            }

            if (!method.isAnnotationPresent(IgnoreAuth.class)) {
                if (method.isAnnotationPresent(Auth.class)) {
                    Auth auth = method.getAnnotation(Auth.class);
                    hasMethodAuth = true;
                    skipAuthorities = auth.skipAuthorities();
                    autority = StringUtils.defaultIfEmpty(auth.value(), StringUtils.EMPTY);
                } else {
                    if (hasAuthClass) {
                        hasMethodAuth = true;
                        skipAuthorities = authClass.skipAuthorities();
                        autority = StringUtils.defaultIfEmpty(authClass.value(), StringUtils.EMPTY);
                    }
                }
            }

            HandlerData defaultHandlerData = new HandlerData().clazz(clazz).method(method).consumes(consumes)
                    .produces(produces).bodyEndHandler(bodyEndHandler).auth(hasMethodAuth)
                    .skipAuthorities(skipAuthorities).autority(autority).authProvider(transporter.getAuthProvider())
                    .routeHandler(routeHandlerClass).documentation(hDocumentation);

            if (hasHttpMethodsPresents(method)) {

                handlerHttpMethodsAnnotations.clear();
                handlerHttpMethodsAnnotations.addAll(getHttpMethodsPresents(method));
            }

            getHandlersPresents(method).forEach(handlerType -> {

                try {

                    // Extract order from Handler, all Hanlder having order()
                    // method
                    int order = annotationMethod(handlerType, "order");

                    HandlerData handlerData = (HandlerData) defaultHandlerData.clone();
                    handlerData.order(order);

                    if (isHandlerAnnotation(handlerType, org.jspare.forvertx.web.mapping.handlers.Handler.class)) {

                        handlerData.handlerType(HandlerType.HANDLER);
                    } else if (isHandlerAnnotation(handlerType, FailureHandler.class)) {

                        handlerData.handlerType(HandlerType.HANDLER);
                    } else if (isHandlerAnnotation(handlerType, BlockingHandler.class)) {

                        handlerData.handlerType(HandlerType.BLOCKING_HANDLER);
                    }

                    if (handlerHttpMethodsAnnotations.isEmpty()) {

                        collectedHandlers.add(handlerData);
                    } else {

                        collectedHandlers.addAll(collectByMethods(handlerData, handlerHttpMethodsAnnotations));
                    }

                } catch (Exception e) {

                    log.warn("Ignoring handler class {} method {} - {}", clazz.getName(), method.getName(),
                            e.getMessage());
                }
            });
        }
        return collectedHandlers;
    }

    @SuppressWarnings("unchecked")
    private static <T> T annotationMethod(Annotation annotation, String methodRef) throws NoSuchMethodException,
            SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method method = annotation.annotationType().getMethod(methodRef);
        return (T) method.invoke(annotation);
    }

    private static List<org.jspare.forvertx.web.handler.BodyEndHandler> collectBodyEndHandlers(
            Transporter transporter, Method method) {

        List<org.jspare.forvertx.web.handler.BodyEndHandler> handlers = new ArrayList<>();
        handlers.addAll(Optional.ofNullable(transporter.getDefaultBodyEndHandlers()).orElse(Arrays.asList()));
        if (method.isAnnotationPresent(BodyEndHandler.class)) {

            Arrays.asList(method.getAnnotation(BodyEndHandler.class).value()).forEach(c -> {
                try {
                    handlers.add(c.newInstance());
                } catch (InstantiationException | IllegalAccessException e) {

                    log.error("Cannot add bodyEndHandler [{}] to route instead of [{}]", c.getName(),
                            method.getName());
                }
            });
        }
        return handlers;
    }

    private static Collection<HandlerData> collectByMethods(HandlerData handlerSource,
            List<Annotation> httpMethodsAnnotations) {

        return httpMethodsAnnotations.stream().map(annotationHttpMethod -> {

            try {

                HandlerData handlerData = (HandlerData) handlerSource.clone();

                String prefix = StringUtils.EMPTY;
                String path = annotationMethod(annotationHttpMethod, "value");
                boolean isRegexPath = annotationMethod(annotationHttpMethod, "regex");

                if (handlerData.clazz().isAnnotationPresent(SubRouter.class)
                        && !handlerData.method().isAnnotationPresent(IgnoreSubRouter.class)) {

                    prefix = handlerData.clazz().getAnnotation(SubRouter.class).value();
                }
                handlerData.patch(String.format("%s%s", prefix, path));
                handlerData.pathRegex(isRegexPath);

                // For All, ignore set httpMethod
                if (All.class.equals(annotationHttpMethod.annotationType())) {

                    return handlerData;
                }

                handlerData.httpMethod(annotationHttpMethod.annotationType().getSimpleName().toUpperCase());
                return handlerData;
            } catch (Exception e) {

                log.warn("Ignoring handler class {} method {} - {}", handlerSource.clazz().getName(),
                        handlerSource.method().getName(), e);
            }
            return null;
        }).collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    private static List<Annotation> getHandlersPresents(Method method) {

        List<Class<?>> handlerClass = Arrays.asList(HandlerType.values()).stream().map(ht -> ht.getHandlerClass())
                .collect(Collectors.toList());
        return handlerClass.stream()
                .filter(handlerType -> method.isAnnotationPresent((Class<? extends Annotation>) handlerType))
                .map(handlerType -> method.getAnnotation((Class<? extends Annotation>) handlerType))
                .collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    private static List<Annotation> getHttpMethodsPresents(AnnotatedElement element) {

        List<Class<?>> filteredClazz = new ArrayList<>();
        filteredClazz.addAll(Arrays.asList(HttpMethodType.values()).stream().map(ht -> ht.getHttpMethodClass())
                .collect(Collectors.toList()));
        filteredClazz.removeIf(clazzHttpMethodType -> !element
                .isAnnotationPresent((Class<? extends Annotation>) clazzHttpMethodType));
        return filteredClazz.stream()
                .map(httpMethodClazz -> element.getAnnotation((Class<? extends Annotation>) httpMethodClazz))
                .collect(Collectors.toList());
    }

    private static boolean hasHttpMethodsPresents(Method method) {

        return getHttpMethodsPresents(method).stream()
                .filter(annotation -> method.isAnnotationPresent(annotation.annotationType())).count() >= 1;
    }

    private static boolean isHandler(Method method) {

        return getHandlersPresents(method).stream()
                .filter(handlerAnnotation -> method.isAnnotationPresent(handlerAnnotation.annotationType()))
                .count() >= 1;
    }

    protected static boolean isHandlerAnnotation(Annotation handlerType, Class<?> element) {
        return handlerType.annotationType().equals(element);
    }
}