org.eclipse.hono.application.HonoApplication.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.hono.application.HonoApplication.java

Source

/**
 * Copyright (c) 2016,2017 Bosch Software Innovations GmbH.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Bosch Software Innovations GmbH - initial creation
 */
package org.eclipse.hono.application;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.eclipse.hono.authentication.AuthenticationService;
import org.eclipse.hono.config.ServiceConfigProperties;
import org.eclipse.hono.server.HonoServer;
import org.eclipse.hono.server.HonoServerFactory;
import org.eclipse.hono.service.authorization.AuthorizationService;
import org.eclipse.hono.service.credentials.CredentialsService;
import org.eclipse.hono.service.registration.RegistrationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;

/**
 * The Hono server main application class.
 * <p>
 * This class configures and wires up Hono's components (Verticles).
 * By default there will be as many instances of each verticle created as there are CPU cores
 * available. The {@code hono.maxinstances} config property can be used to set the maximum number
 * of instances to create. This may be useful for executing tests etc.
 * </p>
 */
public class HonoApplication {

    private static final Logger LOG = LoggerFactory.getLogger(HonoApplication.class);

    private ServiceConfigProperties honoConfig;
    private Vertx vertx;
    private RegistrationService registrationService;
    private CredentialsService credentialsService;
    private AuthenticationService authenticationService;
    private AuthorizationService authorizationService;
    private HonoServerFactory serverFactory;

    /**
     * @param honoConfig the honoConfig to set
     */
    @Autowired
    public final void setHonoConfig(final ServiceConfigProperties honoConfig) {
        this.honoConfig = honoConfig;
    }

    public final ServiceConfigProperties getHonoConfig() {
        return honoConfig;
    }

    /**
     * @param vertx the vertx to set
     */
    @Autowired
    public final void setVertx(final Vertx vertx) {
        this.vertx = vertx;
    }

    public final Vertx getVertx() {
        return vertx;
    }

    /**
     * @param credentialsService the credentialsService to set
     */
    @Autowired
    public final void setCredentialsService(final CredentialsService credentialsService) {
        this.credentialsService = credentialsService;
    }

    public final CredentialsService getCredentialsService() {
        return credentialsService;
    }

    /**
     * @param registrationService the registrationService to set
     */
    @Autowired
    public final void setRegistrationService(final RegistrationService registrationService) {
        this.registrationService = registrationService;
    }

    public final RegistrationService getRegistrationService() {
        return registrationService;
    }

    /**
     * @param authenticationService the authenticationService to set
     */
    @Autowired
    public final void setAuthenticationService(final AuthenticationService authenticationService) {
        this.authenticationService = authenticationService;
    }

    public final AuthenticationService getAuthenticationService() {
        return authenticationService;
    }

    /**
     * @param authorizationService the authorizationService to set
     */
    @Autowired
    public final void setAuthorizationService(final AuthorizationService authorizationService) {
        this.authorizationService = authorizationService;
    }

    public final AuthorizationService getAuthorizationService() {
        return authorizationService;
    }

    /**
     * @param serverFactory the serverFactory to set
     */
    @Autowired
    public final void setServerFactory(final HonoServerFactory serverFactory) {
        this.serverFactory = serverFactory;
    }

    public final HonoServerFactory getServerFactory() {
        return serverFactory;
    }

    /**
     * Starts the Hono server.
     */
    @PostConstruct
    public final void startup() {
        boolean startupSuccessful = registerVerticles();
        if (startupSuccessful) {
            postRegisterVerticles();
        }
    }

    /**
     * Deploys all verticles the Hono server consists of.
     * 
     * @return true if deployment was successful
     */
    private boolean registerVerticles() {

        if (vertx == null) {
            throw new IllegalStateException("no Vert.x instance has been configured");
        }

        final CountDownLatch startupLatch = new CountDownLatch(1);
        final AtomicBoolean startupSucceeded = new AtomicBoolean(false);

        // without creating a first instance here, deployment of the HonoServer verticles fails
        // TODO: find out why
        HonoServer firstInstance = serverFactory.getHonoServer();

        final int instanceCount = honoConfig.getMaxInstances();

        Future<Void> started = Future.future();
        started.setHandler(ar -> {
            if (ar.failed()) {
                LOG.error("cannot start up HonoServer", ar.cause());
            } else {
                startupSucceeded.set(true);
            }
            startupLatch.countDown();
        });

        CompositeFuture.all(deployAuthenticationService(), // we only need 1 authentication service
                deployAuthorizationService(), // we only need 1 authorization service
                deployCredentialsService(), deployRegistrationService()).setHandler(ar -> {
                    if (ar.succeeded()) {
                        deployServer(firstInstance, instanceCount, started);
                    } else {
                        started.fail(ar.cause());
                    }
                });

        try {
            if (startupLatch.await(honoConfig.getStartupTimeout(), TimeUnit.SECONDS)) {
                if (startupSucceeded.get()) {
                    LOG.info("Hono startup completed successfully");
                } else {
                    shutdown();
                }
            } else {
                LOG.error("startup timed out after {} seconds, shutting down ...", honoConfig.getStartupTimeout());
                shutdown();
                startupSucceeded.set(false);
            }
        } catch (InterruptedException e) {
            LOG.error("startup process has been interrupted, shutting down ...");
            Thread.currentThread().interrupt();
            shutdown();
            startupSucceeded.set(false);
        }
        return startupSucceeded.get();
    }

    /**
     * Invoked after the Hono verticles have been registered as part of Hono startup.
     * May be overridden to provide additional startup logic.
     */
    protected void postRegisterVerticles() {
        // empty
    }

    private Future<String> deployRegistrationService() {
        LOG.info("Starting registration service {}", registrationService);
        Future<String> result = Future.future();
        vertx.deployVerticle(registrationService, result.completer());
        return result;
    }

    private Future<String> deployCredentialsService() {
        LOG.info("Starting credentials service {}", credentialsService);
        Future<String> result = Future.future();
        vertx.deployVerticle(credentialsService, result.completer());
        return result;
    }

    private Future<String> deployAuthenticationService() {
        LOG.info("Starting authentication service {}", authenticationService);
        Future<String> result = Future.future();
        vertx.deployVerticle(authenticationService, result.completer());
        return result;
    }

    private Future<String> deployAuthorizationService() {
        LOG.info("Starting authorization service {}", authorizationService);
        Future<String> result = Future.future();
        vertx.deployVerticle(authorizationService, result.completer());
        return result;
    }

    private void deployServer(final HonoServer firstInstance, final int instanceCount, Future<Void> startFuture) {
        @SuppressWarnings("rawtypes")
        List<Future> results = new ArrayList<>();
        deployServerInstance(firstInstance, results);
        for (int i = 1; i < instanceCount; i++) {
            HonoServer server = serverFactory.getHonoServer();
            deployServerInstance(server, results);
        }
        CompositeFuture.all(results).setHandler(ar -> {
            if (ar.failed()) {
                startFuture.fail(ar.cause());
            } else {
                startFuture.complete();
            }
        });
    }

    @SuppressWarnings("rawtypes")
    private void deployServerInstance(final HonoServer instance, final List<Future> deployResults) {
        Future<String> result = Future.future();
        vertx.deployVerticle(instance, result.completer());
        deployResults.add(result);
    }

    /**
     * Stops the Hono server in a controlled fashion.
     */
    @PreDestroy
    public final void shutdown() {
        shutdown(honoConfig.getStartupTimeout(), succeeded -> {
            // do nothing
        });
    }

    /**
     * Stops the Hono server in a controlled fashion.
     * 
     * @param maxWaitTime The maximum time to wait for the server to shut down (in seconds).
     * @param shutdownHandler The handler to invoke with the result of the shutdown attempt.
     */
    public final void shutdown(final long maxWaitTime, final Handler<Boolean> shutdownHandler) {

        try {
            preShutdown();
            final CountDownLatch latch = new CountDownLatch(1);
            if (vertx != null) {
                LOG.debug("shutting down Hono server...");
                vertx.close(r -> {
                    if (r.failed()) {
                        LOG.error("could not shut down Hono cleanly", r.cause());
                    }
                    latch.countDown();
                });
            }
            if (latch.await(maxWaitTime, TimeUnit.SECONDS)) {
                LOG.info("Hono server has been shut down successfully");
                shutdownHandler.handle(Boolean.TRUE);
            } else {
                LOG.error("shut down of Hono server timed out, aborting...");
                shutdownHandler.handle(Boolean.FALSE);
            }
        } catch (InterruptedException e) {
            LOG.error("shut down of Hono server has been interrupted, aborting...");
            Thread.currentThread().interrupt();
            shutdownHandler.handle(Boolean.FALSE);
        }
    }

    /**
     * Invoked before Hono shutdown is initiated.
     * May be overridden to provide additional shutdown handling.
     */
    protected void preShutdown() {
        // empty
    }
}