com.ottogroup.bi.spqr.websocket.kafka.KafkaTopicWebSocketEmitter.java Source code

Java tutorial

Introduction

Here is the source code for com.ottogroup.bi.spqr.websocket.kafka.KafkaTopicWebSocketEmitter.java

Source

/**
 * Copyright 2015 Otto (GmbH & Co KG)
 *
 * 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 com.ottogroup.bi.spqr.websocket.kafka;

import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;

import org.apache.log4j.Logger;

import uk.co.real_logic.queues.BlockingWaitStrategy;
import uk.co.real_logic.queues.MessageWaitStrategy;
import uk.co.real_logic.queues.OneToOneConcurrentArrayQueue3;

import com.ottogroup.bi.spqr.exception.RequiredInputMissingException;

/**
 * Receives a web socket connection and starts to emit incoming messages received from configured
 * kafka topic 
 * @author mnxfst
 * @since Apr 17, 2015
 */
public class KafkaTopicWebSocketEmitter implements Runnable {

    /** our faithful logging facility ... ;-) */
    private static final Logger logger = Logger.getLogger(KafkaTopicWebSocketEmitter.class);

    /** web socket channel to write content to */
    private final Channel websocketChannel;
    /** queue used for receiving kafka content */
    private final OneToOneConcurrentArrayQueue3<byte[]> messages;
    /** strategy to apply when waiting for messages from queue */
    private final MessageWaitStrategy<byte[]> messageWaitStrategy;
    /** runtime environment for topic stream consumers */
    private final ExecutorService executorService;
    /** consumer client establishing a connection with the Kafka server and spawning stream readers */
    private KafkaTopicConsumer consumer;
    /** connection string used for establishing a connection with zookeeper */
    private final String zookeeperConnect;
    /** group identifier assigned to kafka client when establishing a connection with kafka */
    private final String groupId;
    /** topic to consume data from */
    private final String topicId;
    /** indicates whether the emitter is running or halted */
    private boolean running = false;

    /**
     * Initializes the socket emitter using the provided input 
     * @param websocketChannel channel to write incoming messages to
     * @param messageQueueCapacity max. number of elements to be contained inside the queue used for internal data exchange
     * @param executorService runtime environment stream consumers will live in
     * @param zookeeperConnect zookeeper connection string, eg. localhost:2181
     * @param groupId group identifier used by client when connecting with Kafka 
     * @param topicId topic to consume data from
     */
    public KafkaTopicWebSocketEmitter(final Channel websocketChannel, final int messageQueueCapacity,
            final ExecutorService executorService, final String zookeeperConnect, final String groupId,
            final String topicId) {
        this.websocketChannel = websocketChannel;
        this.messages = new OneToOneConcurrentArrayQueue3<byte[]>(messageQueueCapacity);
        this.messageWaitStrategy = new BlockingWaitStrategy();
        this.executorService = executorService;

        this.zookeeperConnect = zookeeperConnect;
        this.groupId = groupId;
        this.topicId = topicId;

        if (logger.isDebugEnabled())
            logger.debug("kafka topic to websocket emitter initialized [topic=" + topicId + ", group=" + groupId
                    + ", zkConnect=" + zookeeperConnect + "]");
    }

    /**
     * @see java.lang.Runnable#run()
     */
    public void run() {

        // prepare topic consumer settings and initialize the client
        Map<String, String> settings = new HashMap<>();
        settings.put(KafkaTopicConsumer.CFG_OPT_KAFKA_GROUP_ID, groupId);
        settings.put(KafkaTopicConsumer.CFG_OPT_KAFKA_ZOOKEEPER_CONNECT, zookeeperConnect);
        settings.put(KafkaTopicConsumer.CFG_OPT_KAFKA_TOPIC, topicId);
        this.consumer = new KafkaTopicConsumer(this.messages, this.messageWaitStrategy, this.executorService);

        try {
            this.consumer.initialize(settings);
        } catch (RequiredInputMissingException e) {
            throw new RuntimeException(e);
        }

        // keep on running until externally halted (set running to 'false') 
        this.running = true;
        while (this.running) {

            // fetch message from queue via provided wait strategy
            byte[] message = null;
            try {
                message = this.messageWaitStrategy.waitFor(messages);
            } catch (InterruptedException e) {
                // 
            }

            // if the byte array contains anything, forward it to web socket, otherwise try to fetch a new message from queue
            if (message != null) {
                try {
                    websocketChannel.writeAndFlush(new TextWebSocketFrame(Unpooled.copiedBuffer(message)));
                } catch (Exception e) {
                    logger.error("Failed to write message to websocket. Error: " + e.getMessage());
                }
            }
        }

        // shut down consumer and websocket channel
        this.consumer.shutdown();
        this.websocketChannel.close();

        if (logger.isDebugEnabled())
            logger.debug("kafka topic to websocket emitter shut down");
    }

    /** 
     * Signle the emitter to halt
     */
    public void shutdown() {
        this.running = false;
    }

}