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

fathom.rest.controller.ControllerScanner Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (C) 2015 the original author or authors.
 *
 * 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 fathom.rest.controller;

import com.google.common.collect.Sets;
import fathom.conf.Settings;
import fathom.utils.ClassUtil;
import fathom.utils.RequireUtil;
import fathom.utils.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Base class for identifying annotated controllers.
 *
 * @author James Moger
 */
public abstract class ControllerScanner {

    private static final Logger log = LoggerFactory.getLogger(ControllerScanner.class);

    protected final Settings settings;

    protected final Set> httpMethodAnnotationClasses = Sets.newHashSet(DELETE.class, GET
            .class, HEAD.class, OPTIONS.class, PATCH.class, POST.class, PUT.class);


    protected ControllerScanner(Settings settings) {
        this.settings = settings;
    }

    /**
     * Discover Controller classes.
     *
     * @param packageNames
     * @return controller classes
     */
    protected Collection> discoverClasses(String... packageNames) {
        log.debug("Discovering annotated controller in package(s) '{}'", Arrays.toString(packageNames));
        Collection> classes = ClassUtil.getAnnotatedClasses(Path.class, packageNames);
        return classes;
    }

    /**
     * Discover Route methods.
     *
     * @param classes
     * @return discovered methods
     */
    protected Map> discoverMethods(Collection> classes) {
        // collect the allowed annotated methods
        Map> discoveredMethods = new LinkedHashMap<>();

        // discover all annotated controllers and methods
        for (Class controllerClass : classes) {
            for (Method method : controllerClass.getDeclaredMethods()) {
                if (RequireUtil.allowMethod(settings, method)) {
                    for (Annotation annotation : method.getAnnotations()) {
                        Class annotationClass = annotation.annotationType();
                        if (httpMethodAnnotationClasses.contains(annotationClass)) {
                            discoveredMethods.put(method, annotationClass);
                            break;
                        }
                    }
                }
            }
        }

        return discoveredMethods;
    }


    /**
     * Sort the methods by their preferred order, if specified.
     *
     * @param methods
     * @return a sorted list of methods
     */
    protected Collection sortMethods(Collection methods) {
        List list = new ArrayList<>(methods);
        Collections.sort(list, (m1, m2) -> {
            int o1 = Integer.MAX_VALUE;
            Order order1 = ClassUtil.getAnnotation(m1, Order.class);
            if (order1 != null) {
                o1 = order1.value();
            }

            int o2 = Integer.MAX_VALUE;
            Order order2 = ClassUtil.getAnnotation(m2, Order.class);
            if (order2 != null) {
                o2 = order2.value();
            }

            if (o1 == o2) {
                // same or unsorted, compare controller+method
                String s1 = Util.toString(m1);
                String s2 = Util.toString(m2);
                return s1.compareTo(s2);
            }

            if (o1 < o2) {
                return -1;
            } else {
                return 1;
            }
        });

        return list;
    }

    /**
     * Recursively builds the paths for the controller class.
     *
     * @param controllerClass
     * @return the paths for the controller
     */
    protected Set collectPaths(Class controllerClass) {
        Set parentPaths = Collections.emptySet();
        if (controllerClass.getSuperclass() != null) {
            parentPaths = collectPaths(controllerClass.getSuperclass());
        }

        Set paths = new LinkedHashSet<>();
        Path controllerPath = controllerClass.getAnnotation(Path.class);

        if (controllerPath != null && controllerPath.value().length > 0) {
            if (parentPaths.isEmpty()) {
                // add all controller paths
                paths.addAll(Arrays.asList(controllerPath.value()));
            } else {
                // create controller paths based on the parent paths
                for (String parentPath : parentPaths) {
                    for (String path : controllerPath.value()) {
                        paths.add(parentPath + path);
                    }
                }
            }
        } else {
            // add all parent paths
            paths.addAll(parentPaths);
        }

        return paths;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy