fathom.rest.RestService.java Source code

Java tutorial

Introduction

Here is the source code for fathom.rest.RestService.java

Source

/*
 * 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;

import com.codahale.metrics.MetricRegistry;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import fathom.Constants;
import fathom.Service;
import fathom.conf.Settings;
import fathom.exception.FathomException;
import fathom.exception.RedirectException;
import fathom.exception.StatusCodeException;
import fathom.rest.controller.ControllerHandler;
import fathom.utils.ClassUtil;
import fathom.utils.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ro.pippo.core.Application;
import ro.pippo.core.ContentTypeEngine;
import ro.pippo.core.ContentTypeEngines;
import ro.pippo.core.TemplateEngine;
import ro.pippo.core.route.Route;
import ro.pippo.core.route.Router;
import ro.pippo.metrics.MetricsDispatchListener;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author James Moger
 */
@Singleton
class RestService implements Service {

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

    private static final String ROUTES_CLASS = "conf.Routes";

    private static final String SETTING_ENGINES_LOG = "rest.engines.log";

    private static final String SETTING_ROUTES_LOG = "rest.routes.log";

    private static final String SETTING_ROUTES_MAX_LINE_LENGTH = "rest.routes.maxLineLength";

    private static final String REST_ROUTES_LOG_HANDLERS = "rest.routes.logHandlers";

    @Inject
    Injector injector;

    @Inject
    Settings settings;

    @Inject
    Application application;

    @Inject
    Router router;

    @Inject
    MetricRegistry metricRegistry;

    private boolean isRunning;

    @Override
    public int getPreferredStartOrder() {
        return 100;
    }

    @Override
    public void start() {

        initializeApplication();

        String border = Strings.padEnd("", Constants.MIN_BORDER_LENGTH, '-');
        Optional<String> applicationPackage = Optional.fromNullable(settings.getApplicationPackage());

        log.info(border);
        log.info("Registered engines");
        log.info(border);
        logEngines();

        log.debug(border);
        log.debug("Initializing router");
        log.debug(border);
        initializeRouter(applicationPackage);

        log.info("");
        log.info(border);
        log.info("RESTful routes ({}) served on base path '{}'", router.getRoutes().size(),
                Strings.isNullOrEmpty(router.getApplicationPath()) ? "/" : router.getApplicationPath());
        log.info(border);
        logRoutes(router);

        isRunning = true;
    }

    @Override
    public boolean isRunning() {
        return isRunning;
    }

    @Override
    public void stop() {
        application.destroy();
    }

    private void initializeApplication() {

        // hook-up the Metrics dispatch listener
        MetricsDispatchListener metricsDispatchListener = new MetricsDispatchListener(metricRegistry);
        application.getRoutePreDispatchListeners().add(metricsDispatchListener);
        application.getRoutePostDispatchListeners().add(metricsDispatchListener);

        // set the RedirectException handler
        application.getErrorHandler().setExceptionHandler(RedirectException.class, (exception, ctx) -> {
            RedirectException redirectException = (RedirectException) exception;

            if (!Strings.isNullOrEmpty(redirectException.getSessionUrlAttribute())) {
                String requestUri = ctx.getRequest().getApplicationUriWithQuery();
                ctx.setSession(redirectException.getSessionUrlAttribute(), requestUri);
            }
            ctx.redirect(redirectException.getPath());
        });

        // set the StatusCodeException handler
        application.getErrorHandler().setExceptionHandler(StatusCodeException.class, (exception, ctx) -> {
            StatusCodeException statusCodeException = (StatusCodeException) exception;
            ctx.setLocal("message", statusCodeException.getMessage());
            application.getErrorHandler().handle(statusCodeException.getStatusCode(), ctx);
        });

    }

    private void logEngines() {
        if (!settings.getBoolean(SETTING_ENGINES_LOG, true)) {
            return;
        }

        TemplateEngine templateEngine = application.getTemplateEngine();
        ContentTypeEngines engines = application.getContentTypeEngines();

        List<String> contentTypes = new ArrayList<>(engines.getContentTypes());
        Collections.sort(contentTypes);

        int maxContentTypeLen = 0;
        int maxTemplateEngineLen = templateEngine == null ? 0 : templateEngine.getClass().getName().length();

        for (String contentType : contentTypes) {

            ContentTypeEngine engine = engines.getContentTypeEngine(contentType);

            maxContentTypeLen = Math.max(maxContentTypeLen, contentType.length());
            maxTemplateEngineLen = Math.max(maxTemplateEngineLen, engine.getClass().getName().length());

        }

        if (templateEngine != null) {
            log.info("{}  =>  {}", Strings.padEnd("templates", maxContentTypeLen, ' '),
                    templateEngine.getClass().getName());
        }

        for (String contentType : contentTypes) {

            ContentTypeEngine engine = engines.getContentTypeEngine(contentType);
            log.info("{}  =>  {}", Strings.padEnd(contentType, maxContentTypeLen, ' '),
                    engine.getClass().getName());

        }

    }

    private void initializeRouter(Optional<String> applicationPackage) {
        String routesClassName = ClassUtil.buildClassName(applicationPackage, ROUTES_CLASS);
        if (ClassUtil.doesClassExist(routesClassName)) {
            Class<?> routesClass = ClassUtil.getClass(routesClassName);
            if (RoutesModule.class.isAssignableFrom(routesClass)) {
                RoutesModule routes = (RoutesModule) injector.getInstance(routesClass);
                routes.init();
            } else {
                throw new FathomException("Your Routes class '{}' does not subclass '{}'!", routesClassName,
                        RoutesModule.class.getName());
            }
        } else {
            log.debug("Did not find '{}' in your application!", routesClassName);
        }
    }

    private void logRoutes(Router router) {
        if (!settings.getBoolean(SETTING_ROUTES_LOG, true)) {
            return;
        }

        // determine the width of the columns
        int maxMethodLen = 0;
        int maxPathLen = 0;
        int maxControllerLen = 0;

        if (router.getRoutes().isEmpty()) {
            log.info("no routes found");
            return;
        }

        for (Route route : router.getRoutes()) {

            maxMethodLen = Math.max(maxMethodLen, route.getRequestMethod().length());
            maxPathLen = Math.max(maxPathLen, route.getUriPattern().length());

            if (route.getRouteHandler() instanceof ControllerHandler) {

                ControllerHandler handler = (ControllerHandler) route.getRouteHandler();
                int controllerLen = Util.toString(handler.getControllerMethod()).length();
                maxControllerLen = Math.max(maxControllerLen, controllerLen);

            } else if (route.getName() != null) {
                maxControllerLen = Math.max(maxControllerLen, route.getName().length());
            }

        }

        // log the routing table
        final int maxLineLength = settings.getInteger(SETTING_ROUTES_MAX_LINE_LENGTH, 120);
        final boolean logHandlers = settings.getBoolean(REST_ROUTES_LOG_HANDLERS, true);
        final boolean oneLine = (maxMethodLen + maxPathLen + maxControllerLen
                + 11 /* whitespace */) <= maxLineLength;
        if (!oneLine || !logHandlers) {
            maxPathLen = 0;
        }

        for (Route route : router.getRoutes()) {
            if (route.getRouteHandler() instanceof ControllerHandler) {

                ControllerHandler handler = (ControllerHandler) route.getRouteHandler();
                if (oneLine) {
                    if (logHandlers) {
                        log.info("{} {}  =>  {}()", Strings.padEnd(route.getRequestMethod(), maxMethodLen, ' '),
                                Strings.padEnd(route.getUriPattern(), maxPathLen, ' '),
                                Util.toString(handler.getControllerMethod()));
                    } else {
                        log.info("{} {}", Strings.padEnd(route.getRequestMethod(), maxMethodLen, ' '),
                                Strings.padEnd(route.getUriPattern(), maxPathLen, ' '));
                    }
                } else {
                    log.info("{} {}", Strings.padEnd(route.getRequestMethod(), maxMethodLen, ' '),
                            Strings.padEnd(route.getUriPattern(), maxPathLen, ' '));
                    if (logHandlers) {
                        log.info("{} {}()", Strings.padEnd("", maxMethodLen, ' '),
                                Util.toString(handler.getControllerMethod()));
                    }
                }

            } else if (route.getName() != null) {
                if (oneLine) {
                    if (logHandlers) {
                        log.info("{} {}  =>  {}", Strings.padEnd(route.getRequestMethod(), maxMethodLen, ' '),
                                Strings.padEnd(route.getUriPattern(), maxPathLen, ' '), route.getName());
                    } else {
                        log.info("{} {}", Strings.padEnd(route.getRequestMethod(), maxMethodLen, ' '),
                                Strings.padEnd(route.getUriPattern(), maxPathLen, ' '));
                    }
                } else {
                    log.info("{} {}", Strings.padEnd(route.getRequestMethod(), maxMethodLen, ' '),
                            Strings.padEnd(route.getUriPattern(), maxPathLen, ' '));
                    if (logHandlers) {
                        log.info("{} {}", Strings.padEnd("", maxMethodLen, ' '), route.getName());
                    }
                }
            } else {
                log.info("{} {}", Strings.padEnd(route.getRequestMethod(), maxMethodLen, ' '),
                        route.getUriPattern());
            }
        }

    }

}