com.shopwiki.roger.event.MessagingManager.java Source code

Java tutorial

Introduction

Here is the source code for com.shopwiki.roger.event.MessagingManager.java

Source

/*
 * Copyright [2013] [ShopWiki]
 *
 * 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.shopwiki.roger.event;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.shopwiki.roger.RabbitConnector;
import com.shopwiki.roger.RabbitReconnector;
import com.shopwiki.roger.Route;
import com.shopwiki.roger.RabbitReconnector.ReconnectHandler;
import com.shopwiki.roger.RabbitReconnector.ReconnectLogger;
import com.shopwiki.roger.util.TimeUtil;

/**
 * The main entry point for creating & starting message workers using Roger.
 *
 * @author rstewart
 */
public class MessagingManager {

    public static final boolean DEBUG = Boolean.getBoolean("ROGER.EVENT.DEBUG");

    private final RabbitConnector connector;
    private final int numThreads;
    private final RabbitReconnector reconnector;
    private final Map<String, Object> queueArgs = Collections.emptyMap(); // TODO: Pass in queueArgs ???

    private final Map<MessageHandler<?>, Route> handlerToRoute = new ConcurrentHashMap<MessageHandler<?>, Route>();
    private volatile Connection conn = null;
    private volatile List<Channel> channels = null;

    public MessagingManager(RabbitConnector connector, int numThreads, int secondsBeforeReconnect) {
        this(connector, numThreads, secondsBeforeReconnect, null);
    }

    public MessagingManager(RabbitConnector connector, int numThreads, int secondsBeforeReconnect,
            ReconnectLogger reconnectLogger) {
        this.connector = connector;
        this.numThreads = numThreads;

        ReconnectHandler reconnectHandler = new ReconnectHandler() {
            @Override
            public boolean reconnect() throws Exception {
                return _start();
            }
        };

        reconnector = new RabbitReconnector(reconnectHandler, reconnectLogger, secondsBeforeReconnect);
    }

    /**
     * Adds a {@link MessageHandler} with the corresponding {@link Route}.
     * If already started, creates and starts a {@link MessageWorker}.
     *
     * @param handler
     * @param route
     * @throws IOException
     */
    public synchronized void add(MessageHandler<?> handler, Route route) throws IOException {
        handlerToRoute.put(handler, route);
        if (channels != null) {
            startWorker(handler, route);
        }
    }

    private void startWorker(MessageHandler<?> handler, Route route) throws IOException {
        MessageWorker worker = new MessageWorker(handler, channels, queueArgs, route);
        if (DEBUG) {
            System.out.println(TimeUtil.now() + " Starting MessageWorker for route: " + route);
        }
        worker.start();
    }

    /**
     * Creates {@link MessageWorker}s.
     * Starts each {@link MessageWorker} (declares queues & binds routing-keys).
     *
     * If this fails for any reason, it will periodically attempt to start in a background thread.
     */
    public void start() {
        if (_start() == false) {
            Executors.newSingleThreadExecutor().execute(reconnector);
        }
    }

    private synchronized boolean _start() {
        try {
            if (DEBUG) {
                System.out.print(TimeUtil.now() + " Starting MessagingManager: ");
            }
            conn = connector.newDaemonConnection(numThreads); // TODO: Should have the option for non-daemon ???
            channels = createChannels(conn, numThreads);
            if (DEBUG) {
                System.out.println(conn + " with " + channels.size() + " channels.");
            }

            for (MessageHandler<?> handler : handlerToRoute.keySet()) {
                Route route = handlerToRoute.get(handler);
                startWorker(handler, route);
            }

            conn.addShutdownListener(reconnector);
            return true;
        } catch (Throwable t) {
            System.err.println(TimeUtil.now() + " ERROR starting MessagingManager:");
            t.printStackTrace();
            channels = null;
            stop();
            return false;
        }
    }

    private static List<Channel> createChannels(Connection conn, int n) throws IOException {
        List<Channel> channels = Lists.newArrayList();
        for (int i = 0; i < n; i++) {
            Channel channel = conn.createChannel();
            channels.add(channel);
        }
        return ImmutableList.copyOf(channels);
    }

    /**
     * Closes the connection to RabbitMQ and prevents further automatic restarts.
     */
    public void stop() {
        System.out.println(TimeUtil.now() + " Stopping MessagingManager: " + conn);
        RabbitConnector.closeConnectionAndRemoveReconnector(conn, reconnector);
        conn = null;
    }
}