org.eclipse.hono.adapter.VertxBasedAdapterApplication.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.hono.adapter.VertxBasedAdapterApplication.java

Source

/**
 * Copyright (c) 2016 Red Hat
 *
 * 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:
 *    Red Hat - initial creation
 */

package org.eclipse.hono.adapter;

import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Verticle;
import io.vertx.core.Vertx;
import org.eclipse.hono.config.ServiceConfigProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Base class for any Hono protocol adapter
 * <p>
 * This class uses Spring Boot for configuring the adapter's properties. It requires that
 * the adapter logic is implemented as a Vert.x verticle.
 * By default there will be as many instances of the adapter 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>
 */
@ComponentScan(basePackages = "org.eclipse.hono")
@Configuration
@EnableAutoConfiguration
public abstract class VertxBasedAdapterApplication {

    protected final Logger LOG = LoggerFactory.getLogger(this.getClass());

    private Vertx vertx;
    private ServiceConfigProperties honoConfig = new ServiceConfigProperties();
    private AtomicBoolean running = new AtomicBoolean();

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

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

    @PostConstruct
    public void registerVerticles() {

        if (running.compareAndSet(false, true)) {
            final int instanceCount = honoConfig.getMaxInstances();

            try {
                final CountDownLatch latch = new CountDownLatch(1);
                final Future<Void> startFuture = Future.future();
                startFuture.setHandler(done -> {
                    if (done.succeeded()) {
                        latch.countDown();
                    } else {
                        LOG.error("could not start '{}' adapter", this.getName(), done.cause());
                    }
                });

                deployVerticle(instanceCount, startFuture);

                if (latch.await(honoConfig.getStartupTimeout(), TimeUnit.SECONDS)) {
                    LOG.info("'{}' adapter startup completed successfully", this.getName());
                } else {
                    LOG.error("startup timed out after {} seconds, shutting down ...",
                            honoConfig.getStartupTimeout());
                    shutdown();
                }
            } catch (InterruptedException e) {
                LOG.error("startup process has been interrupted, shutting down ...");
                Thread.currentThread().interrupt();
                shutdown();
            }
        }
    }

    private void deployVerticle(final int instanceCount, final Future<Void> resultHandler) {

        LOG.debug("starting up {} instances of '{}' adapter verticle", instanceCount, this.getName());
        @SuppressWarnings("rawtypes")
        List<Future> results = new ArrayList<>();
        for (int i = 1; i <= instanceCount; i++) {
            final int instanceId = i;
            final Future<String> result = Future.future();
            results.add(result);
            vertx.deployVerticle(this.getAdapter(), d -> {
                if (d.succeeded()) {
                    LOG.debug("verticle instance {} deployed", instanceId);
                    result.complete();
                } else {
                    LOG.debug("failed to deploy verticle instance {}", instanceId, d.cause());
                    result.fail(d.cause());
                }
            });
        }
        CompositeFuture.all(results).setHandler(done -> {
            if (done.succeeded()) {
                resultHandler.complete();
            } else {
                resultHandler.fail(done.cause());
            }
        });
    }

    @PreDestroy
    public void shutdown() {
        if (running.compareAndSet(true, false)) {
            this.shutdown(honoConfig.getStartupTimeout(), succeeded -> {
                // do nothing
            });
        }
    }

    public void shutdown(final long maxWaitTime, final Handler<Boolean> shutdownHandler) {

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

    /**
     * Return the Vert.x verticle which implements the adapter logic
     * @return  adapter as Vert.x verticle instance
     */
    protected abstract Verticle getAdapter();

    /**
     * Return the adapter name
     * @return  adapter name
     */
    protected abstract String getName();
}