org.jspare.forvertx.web.transporter.Transporter.java Source code

Java tutorial

Introduction

Here is the source code for org.jspare.forvertx.web.transporter.Transporter.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.
 */
/*
 * 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.
 */
/**
 * Copyright 2016 Senior Sistemas.
 *
 * Software sob Medida
 *
 */
package org.jspare.forvertx.web.transporter;

import static org.jspare.core.container.Environment.CONFIG;
import static org.jspare.core.container.Environment.my;
import static org.jspare.core.scanner.ComponentScanner.ALL_SCAN_QUOTE;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.DEFAULT_HTTP_OPTIONS_JSON_PATH;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.FILE_UPLOADS_PATH;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.ROUTES_PACKAGE;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.SERVER_PORT_AUTO_KEY;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.SERVER_PORT_DEFAULT;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.SERVER_PORT_KEY;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.SSL_ENABLE;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.SSL_KEYSTORE_KEY;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.SSL_KEYSTORE_PASSWORD;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.SSL_KEYSTORE_PASSWORD_KEY;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.SSL_KEYSTORE_PATH;
import static org.jspare.forvertx.web.commons.Definitions4Vertx.START_PORT_SCAN;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.jspare.core.loader.ResourceLoader;
import org.jspare.core.scanner.ComponentScanner;
import org.jspare.forvertx.VertxManager;
import org.jspare.forvertx.web.auth.AuthProvider;
import org.jspare.forvertx.web.collector.HandlerCollector;
import org.jspare.forvertx.web.collector.HandlerData;
import org.jspare.forvertx.web.commons.TransporterUtils;
import org.jspare.forvertx.web.exceptions.HttpServerListenException;
import org.jspare.forvertx.web.handler.BodyEndHandler;
import org.jspare.forvertx.web.handler.DefaultHandler;
import org.jspare.forvertx.web.handler.HandlerWrapper;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpServerOptionsConverter;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import lombok.Builder;
import lombok.Getter;
import lombok.Singular;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/** The Constant log. */
@Slf4j

/* (non-Javadoc)
 * @see java.lang.Object#toString()
 */
@Builder
public class Transporter extends AbstractVerticle {

    /**
     * Builder.
     *
     * @return the transporter builder
     */
    @SneakyThrows(IOException.class)
    public static TransporterBuilder builder() {

        // Prepare httpServerOptions default
        HttpServerOptions httpServerOptions = new HttpServerOptions().setTcpKeepAlive(true).setReuseAddress(true);
        if (my(ResourceLoader.class).exist(DEFAULT_HTTP_OPTIONS_JSON_PATH)) {

            String content = my(ResourceLoader.class).readFileToString(DEFAULT_HTTP_OPTIONS_JSON_PATH);
            if (StringUtils.isNotEmpty(content)) {

                HttpServerOptionsConverter.fromJson(Json.decodeValue(content, JsonObject.class), httpServerOptions);
            }
        }

        return new TransporterBuilder().httpServerOptions(httpServerOptions);
    }

    /**
     * Builder.
     *
     * @param source4convetions
     *            the source 4 convetions
     * @return the transporter builder
     */
    public static TransporterBuilder builder(Object source4convetions) {

        return builder().source4conventions(source4convetions);
    }

    /** The name. */

    /**
     * Gets the name.
     *
     * @return the name
     */
    @Getter
    private String name;

    /**
     * Gets the port used by http server.
     *
     * @return the port
     */
    @Getter
    private int port;

    /** The vertx. */
    /*
     * (non-Javadoc)
     *
     * @see io.vertx.core.AbstractVerticle#getVertx()
     */
    @Getter
    private Vertx vertx;

    /**
     * Gets the http server.
     *
     * @return the http server
     */
    @Getter
    private HttpServer httpServer;

    /**
     * Gets the router.
     *
     * @return the router
     */
    @Getter
    private Router router;

    /**
     * Gets the source 4 conventions.
     *
     * @return the source 4 conventions
     */
    @Getter
    private Object source4conventions;

    /**
     * Gets the http server options.
     *
     * @return the http server options
     */
    @Getter
    private HttpServerOptions httpServerOptions = new HttpServerOptions().setTcpKeepAlive(true)
            .setReuseAddress(true);

    /**
     * Gets the routes.
     *
     * @return the routes
     */
    @Getter
    @Singular("addRoute")
    private List<Class<?>> routes;

    /**
     * Gets the handlers. <br>
     * This handlers define the middleware before each call of route on http
     * server. For all requests this handlers will be called. <br>
     * Note: All of this handlers has setted with order MIN integer value, for
     * be called before any handler
     *
     * @return the handlers
     */
    @Getter
    @Singular("addHandler")
    private List<Handler<RoutingContext>> handlers;

    /** The route handler.
     *
     *   Define the default routeHandlerClass any request
     *
     * @return the route handler
     * */
    @Getter
    private Class<? extends Handler<RoutingContext>> routeHandlerClass;

    /**
     * Checks if is ignore body handler.
     *
     * @return true, if is ignore body handler
     */
    @Getter
    private boolean ignoreBodyHandler;

    /**
     * Gets the body handler.
     * 
     * Define the default bodyHandler for requests
     *
     * @return the body handler
     */
    @Getter
    private Handler<RoutingContext> bodyHandler;

    /**
     * Gets the default body end handlers.
     *
     * This attribute set for any request one defaultBodyHandler.
     *
     * @return the default body end handlers
     */
    @Getter
    @Singular("addDefaultBodyEndHandler")
    private List<BodyEndHandler> defaultBodyEndHandlers;

    /**
     * Gets the auth provider.
     *
     * @return the auth provider
     */
    @Getter
    private AuthProvider authProvider;

    /**
     * Instantiates a new transporter.
     *
     * @param name the name
     * @param port the port
     * @param vertx the vertx
     * @param httpServer the http server
     * @param router the router
     * @param source4conventions the source 4 conventions
     * @param httpServerOptions the http server options
     * @param routes the routes
     * @param handlers the handlers
     * @param routeHandlerClass the route handler
     * @param ignoreBodyHandler the ignore body handler
     * @param bodyHandler the body handler
     * @param defaultBodyEndHandlers the default body end handlers
     * @param authProvider the auth provider
     */
    public Transporter(String name, int port, Vertx vertx, HttpServer httpServer, Router router,
            Object source4conventions, HttpServerOptions httpServerOptions, List<Class<?>> routes,
            List<Handler<RoutingContext>> handlers, Class<? extends Handler<RoutingContext>> routeHandlerClass,
            boolean ignoreBodyHandler, Handler<RoutingContext> bodyHandler,
            List<BodyEndHandler> defaultBodyEndHandlers, AuthProvider authProvider) {
        super();
        this.name = name;
        this.port = port;
        this.vertx = vertx;
        this.httpServer = httpServer;
        this.router = router;
        this.source4conventions = source4conventions;
        this.httpServerOptions = httpServerOptions;
        this.routes = routes;
        this.handlers = handlers;
        this.routeHandlerClass = routeHandlerClass;
        this.ignoreBodyHandler = ignoreBodyHandler;
        this.bodyHandler = bodyHandler;
        this.defaultBodyEndHandlers = defaultBodyEndHandlers;
        this.authProvider = authProvider;
        build();
    }

    /**
     * Listen.
     */
    public void listen() {

        listen((server) -> {

            if (server.failed()) {

                log.error("Address port in use: [{}]", port);
                throw new HttpServerListenException(server.cause());
            }
            log.info(StringUtils.repeat("#", 50));
            log.info("# Tranposrter name [{}]", StringUtils.defaultIfEmpty(this.name, "DEFAULT"));
            log.info("# Vert.x httpServer started at: 127.0.0.1:{}", port);
            log.info(StringUtils.repeat("#", 50));
        });
    }

    /**
     * Listen.
     *
     * @param result
     *            the result
     */
    public void listen(Handler<AsyncResult<HttpServer>> result) {

        // Prepare http server to receive all routes
        httpServer.requestHandler(router::accept);

        // Start http server
        httpServer.listen(result);
    }

    /**
     * Builds the.
     */
    private void build() {

        // Initialize Vert.x if not setted
        if (vertx == null) {

            vertx = my(VertxManager.class).vertx();
        }

        // Set default port if not setted to transporter
        if (port == 0) {

            if (Boolean.valueOf(CONFIG.get(SERVER_PORT_AUTO_KEY, Boolean.FALSE))) {

                port = findForAvailablePort();
            } else {

                port = Integer.valueOf(CONFIG.get(SERVER_PORT_KEY, SERVER_PORT_DEFAULT));
            }
        }
        httpServerOptions.setPort(port);

        // Prepare SSL if available
        if (!httpServerOptions.isSsl() && Boolean.valueOf(CONFIG.get(SSL_ENABLE, Boolean.FALSE))) {

            httpServerOptions.setSsl(true)
                    .setKeyStoreOptions(new JksOptions().setPath(CONFIG.get(SSL_KEYSTORE_KEY, SSL_KEYSTORE_PATH))
                            .setPassword(CONFIG.get(SSL_KEYSTORE_PASSWORD_KEY, SSL_KEYSTORE_PASSWORD)));
        }

        // Initialize Router if not setted
        if (router == null) {

            router = Router.router(vertx);
        }

        //Create or define the defaut route handler
        if (this.routeHandlerClass == null) {

            this.routeHandlerClass = DefaultHandler.class;
        }

        // Create default Handler to handle file uploads and body
        if (this.bodyHandler == null) {

            this.bodyHandler = BodyHandler.create(FILE_UPLOADS_PATH);
        }

        if (!ignoreBodyHandler) {

            router.route().handler(bodyHandler);
        }

        // Collect all Middleware Handlers
        handlers.forEach(h -> HandlerWrapper.prepareHandler(router, h));

        // Collect all handlers adding to router
        collectHandlers().forEach(hd -> HandlerWrapper.prepareHandler(router, hd));

        // Initialize http server if not setted
        if (httpServer == null) {

            httpServer = vertx.createHttpServer(httpServerOptions);
        }

        // Deploy this verticle on Vert.x
        vertx.deployVerticle(this);
    }

    /**
     * Collect convention handlers.
     *
     * @return the list
     */
    private List<HandlerData> collectConventionHandlers() {

        List<HandlerData> handlers = new ArrayList<>();
        String cpackage = source4conventions.getClass().getPackage().getName().concat(ROUTES_PACKAGE)
                .concat(ALL_SCAN_QUOTE);
        my(ComponentScanner.class).scanAndExecute(cpackage, clazzName -> {

            try {

                Class<?> clazz = Class.forName(clazzName);
                handlers.addAll(HandlerCollector.collect(this, clazz));
            } catch (Exception e) {

                log.warn("Cannot collect route class [{}]", clazzName, e);
            }
        });

        return handlers;
    }

    /**
     * Collect handlers.
     *
     * @return the list
     */
    public List<HandlerData> collectHandlers() {

        List<HandlerData> handlers = new ArrayList<>();
        handlers.addAll(collectRoutesHandlers());
        if (source4conventions != null) {

            handlers.addAll(collectConventionHandlers());
        }
        return handlers;
    }

    /**
     * Collect routes handlers.
     *
     * @return the sets the
     */
    private Set<HandlerData> collectRoutesHandlers() {

        Set<HandlerData> handlers = new HashSet<>();
        routes.forEach(clazz -> handlers.addAll(HandlerCollector.collect(this, clazz)));
        return handlers;
    }

    /**
     * Find for available port.
     *
     * @return the int
     */
    private int findForAvailablePort() {

        int port = START_PORT_SCAN;
        boolean isAvailable = false;

        while (!isAvailable) {
            isAvailable = TransporterUtils.available(port);
            if (!isAvailable) {

                port++;
            }
        }
        return port;
    }
}