com.jbrisbin.groovy.mqdsl.RabbitMQBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.jbrisbin.groovy.mqdsl.RabbitMQBuilder.java

Source

/*
 * Copyright (c) 2010 by J. Brisbin <jon@jbrisbin.com>
 *
 * 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.jbrisbin.groovy.mqdsl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.HeadersExchange;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.util.StringUtils;

import com.rabbitmq.client.Channel;

import groovy.lang.Closure;
import groovy.lang.GString;
import groovy.lang.GroovyObjectSupport;
import groovy.util.BuilderSupport;

/**
 * Created by IntelliJ IDEA. User: jbrisbin Date: Mar 31, 2010 Time: 10:16:03 AM To change this template use File |
 * Settings | File Templates.
 */
@SuppressWarnings({ "unchecked" })
public class RabbitMQBuilder extends BuilderSupport {

    class Consume extends GroovyObjectSupport {

        SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer(connectionFactory);
        String eventName = null;
        Closure delegate = null;

        Consume() {
        }

        public Closure getDelegate() {
            return delegate;
        }

        public String getEventName() {
            return eventName;
        }

        public SimpleMessageListenerContainer getListenerContainer() {
            return listenerContainer;
        }

        public void setDelegate(Closure delegate) {
            this.delegate = delegate;
            listenerContainer.setMessageListener(new ClosureInvokingMessageListener(this));
        }

        public void setEventName(String eventName) {
            this.eventName = eventName;
            listenerContainer.setMessageListener(new EventInvokingMessageListener(this));
        }

        public void shutdown() {
            listenerContainer.shutdown();
            listenerContainers.remove(listenerContainer);
        }
    }

    class ClosureInvokingMessageListener implements MessageListener {

        Consume consume;
        Object lastResult = null;

        ClosureInvokingMessageListener(Consume consume) {
            this.consume = consume;
        }

        public Object getLastResult() {
            return lastResult;
        }

        @Override
        public void onMessage(Message message) {
            if (null != consume.getDelegate()) {
                lastResult = consume.getDelegate().call(message);
                if (null == lastResult
                        || (lastResult instanceof Boolean && !((Boolean) lastResult).booleanValue())) {
                    consume.shutdown();
                }
            }
        }
    }

    class EventInvokingMessageListener implements MessageListener {

        Consume consume;

        EventInvokingMessageListener(Consume consume) {
            this.consume = consume;
        }

        @Override
        public void onMessage(Message message) {
            if (null != consume.getEventName()) {
                dispatchEvent(consume.getEventName(), new Object[] { message });
                consume.shutdown();
            }
        }
    }

    class Publish extends GroovyObjectSupport {
        public Object call(Map<String, Object> params, Object bodyObj) throws IOException {
            String exchange = null != currentExchange ? currentExchange.getName() : null;
            if (null != params && params.containsKey(EXCHANGE)) {
                Object oexchange = params.get(EXCHANGE);
                if (null != oexchange && StringUtils.hasText(oexchange.toString())) {
                    exchange = oexchange.toString();
                }
            }
            String routingKey = currentRoutingKey;
            if (null != params && params.containsKey(ROUTING_KEY)) {
                Object oroutingKey = params.get(ROUTING_KEY);
                if (null != oroutingKey && StringUtils.hasText(oroutingKey.toString())) {
                    routingKey = oroutingKey.toString();
                }
            }
            return call(exchange, routingKey, params, bodyObj);
        }

        public Object call(String exchange, String routingKey, Map<String, Object> headers, Object bodyObj)
                throws IOException {
            Message msg = createMessage(headers, bodyObj);
            if (null == exchange && null != currentExchange) {
                exchange = currentExchange.getName();
            }
            if (null == routingKey && null != currentRoutingKey) {
                routingKey = currentRoutingKey;
            }

            dispatchEvent(BEFORE_PUBLISH, new Object[] { exchange, routingKey, msg });
            try {
                rabbitTemplate.send(exchange, routingKey, msg);
                dispatchEvent(AFTER_PUBLISH, new Object[] { exchange, routingKey, msg });
            } catch (Exception e) {
                dispatchError(e);
            }

            return this;
        }

        public Object call(Map<String, Object> params, Closure bodyClosure) throws IOException {
            String exchange = params.containsKey(EXCHANGE) ? params.remove(EXCHANGE).toString() : null;
            String routingKey = params.containsKey(ROUTING_KEY) ? params.remove(ROUTING_KEY).toString() : null;
            ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
            Object bodyObj = bodyClosure.call(bytesOut);
            bytesOut.flush();

            byte[] bytes = bytesOut.toByteArray();
            if (bytes.length > 0) {
                return call(exchange, routingKey, params, bytes);
            } else {
                return call(exchange, routingKey, params, bodyObj);
            }
        }

        private Message createMessage(Map<String, Object> params, Object bodyObj) throws IOException {
            MessageProperties msgProps = createProperties(params);
            if (bodyObj instanceof Closure) {
                // If it's a Closure, invoke it to get the value
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                Closure cl = (Closure) bodyObj;
                Object returnFromBodyClosure = cl.call(new Object[] { out });
                if (returnFromBodyClosure instanceof Message) {
                    return (Message) returnFromBodyClosure;
                } else if (returnFromBodyClosure instanceof String) {
                    return new Message(((String) returnFromBodyClosure).getBytes(), msgProps);
                } else {
                    out.flush();
                    return new Message(out.toByteArray(), msgProps);
                }
            } else if (bodyObj instanceof String || bodyObj instanceof GString) {
                // If it's sort of a String, toString() it
                return new Message(bodyObj.toString().getBytes(), msgProps);
            } else if (bodyObj instanceof byte[]) {
                // If it's raw bytes, don't do anything
                return new Message((byte[]) bodyObj, msgProps);
            } else {
                // Otherwise, write the object out using JDK serialization
                ByteArrayOutputStream bout = new ByteArrayOutputStream();
                ObjectOutputStream oout = new ObjectOutputStream(bout);
                oout.writeObject(bodyObj);
                oout.flush();
                oout.close();
                bout.flush();

                return new Message(bout.toByteArray(), msgProps);
            }
        }

        private MessageProperties createProperties(Map<String, Object> headers) {
            MessageProperties msgProps = new MessageProperties();
            for (Map.Entry<String, Object> entry : headers.entrySet()) {
                String key = entry.getKey();
                String value = null != entry.getValue() ? entry.getValue().toString() : null;
                if ("contentType".equals(key)) {
                    msgProps.setContentType(value);
                } else if ("correlationId".equals(key)) {
                    msgProps.setCorrelationId(value.getBytes());
                } else if ("replyTo".equals(key)) {
                    msgProps.setReplyTo(value);
                } else if ("contentEncoding".equals(key)) {
                    msgProps.setContentEncoding(value);
                } else {
                    msgProps.setHeader(key, value);
                }
            }
            return msgProps;
        }
    }

    static final String BEFORE_PUBLISH = "beforePublish";
    static final String AFTER_PUBLISH = "afterPublish";
    static final String EXCHANGE = "exchange";
    static final String ROUTING_KEY = "routingKey";
    static final String DURABLE = "durable";
    static final String AUTO_DELETE = "autoDelete";
    static final String EXCLUSIVE = "exclusive";
    static final String ARGUMENTS = "arguments";
    static final String NAME = "name";
    static final String TYPE = "type";
    static final String DIRECT = "direct";
    static final String TOPIC = "topic";
    static final String FANOUT = "fanout";
    static final String HEADERS = "headers";
    static final String ON = "on";
    static final String QUEUE = "queue";
    static final String CONSUME = "consume";
    static final String PUBLISH = "publish";
    static final String ON_MESSAGE = "onmessage";
    static final String ACK = "ack";
    static final String CALL = "call";
    static final String ERROR = "error";
    static final String INIT = "init";
    static final String HOST = "host";
    static final String PORT = "port";
    static final String VHOST = "vhost";
    static final String USERNAME = "username";
    static final String PASSWORD = "password";
    Logger log = LoggerFactory.getLogger(getClass());
    ConnectionFactory connectionFactory;
    ConcurrentLinkedQueue<SimpleMessageListenerContainer> listenerContainers = new ConcurrentLinkedQueue<SimpleMessageListenerContainer>();
    RabbitTemplate rabbitTemplate;
    RabbitAdmin rabbitAdmin;
    Exchange currentExchange;
    Queue currentQueue;
    String currentRoutingKey = null;
    List<Channel> activeChannels = new LinkedList<Channel>();
    Map<String, List<Closure>> eventHandlers = new LinkedHashMap<String, List<Closure>>();

    String arrayToString(Object[] o) {
        StringBuffer buff = new StringBuffer("[");
        for (int i = 0; i < o.length; i++) {
            if (i > 0) {
                buff.append(",");
            }
            buff.append(String.valueOf(o[i]));
        }
        buff.append("]");
        return buff.toString();
    }

    public void call(Closure cl) {
        Channel channel = connectionFactory.createConnection().createChannel(false);
        cl.call(new Object[] { channel });
        activeChannels.add(channel);
    }

    public void cancelAllConsumers() {
        for (SimpleMessageListenerContainer c : listenerContainers) {
            c.shutdown();
        }
    }

    public void close() {
        for (Channel channel : activeChannels) {
            try {
                channel.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        for (SimpleMessageListenerContainer c : listenerContainers) {
            c.shutdown();
        }
    }

    @Override
    protected Object createNode(Object o) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("createNode(Object o): %s", o));
        }
        if (o instanceof Consume || o instanceof Publish) {
            return o;
        }
        return null;
    }

    @Override
    protected Object createNode(Object node, Object arg) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("setParent(Object node, Object arg): %s, %s", node, arg));
        }
        if (arg instanceof Consume || arg instanceof Publish) {
            return arg;
        }

        return null;
    }

    @Override
    protected Object createNode(Object o, final Map params) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("createNode(Object o, Map params): %s, %s", o, params));
        }
        String node = o.toString();
        if (ON.equals(node)) {
            // Event handlers
            for (Map.Entry<String, Closure> entry : ((Map<String, Closure>) params).entrySet()) {
                String eventName = entry.getKey();
                List<Closure> handlers;
                if (eventHandlers.containsKey(eventName)) {
                    handlers = eventHandlers.get(eventName);
                } else {
                    handlers = new ArrayList<Closure>();
                    eventHandlers.put(eventName, handlers);
                }
                if (entry.getValue() instanceof List) {
                    for (Closure cl : (List<Closure>) entry.getValue()) {
                        handlers.add(cl);
                    }
                } else if (entry.getValue() instanceof Closure) {
                    Closure cl = entry.getValue();
                    cl.setProperty(NAME, eventName);
                    handlers.add(cl);
                }
            }
            return null;
        } else if (EXCHANGE.equals(node)) {
            boolean durable = params.containsKey(DURABLE) ? (Boolean) params.get(DURABLE) : false;
            boolean autoDelete = params.containsKey(AUTO_DELETE) ? (Boolean) params.get(AUTO_DELETE) : false;
            Map arguments = params.containsKey(ARGUMENTS) ? (Map) params.get(ARGUMENTS) : null;
            currentRoutingKey = null;

            Exchange exchange = null;
            String name = null;
            if (params.containsKey(NAME)) {
                name = params.get(NAME).toString();
            }
            if (params.containsKey(TYPE)) {
                String type = params.containsKey(TYPE) ? params.get(TYPE).toString() : DIRECT;
                if (DIRECT.equals(type)) {
                    exchange = new DirectExchange(name, durable, autoDelete, arguments);
                } else if (TOPIC.equals(type)) {
                    exchange = new TopicExchange(name, durable, autoDelete, arguments);
                } else if (FANOUT.equals(type)) {
                    exchange = new FanoutExchange(name, durable, autoDelete, arguments);
                } else if (HEADERS.equals(type)) {
                    exchange = new HeadersExchange(name, durable, autoDelete, arguments);
                } else {
                    exchange = new CustomExchange(name, type, durable, autoDelete, arguments);
                }
                currentExchange = exchange;

            } else {
                currentExchange = new DirectExchange(name);
            }
            try {
                rabbitTemplate.setExchange(name);
                //rabbitAdmin.declareExchange(currentExchange);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                dispatchError(e);
            }

            return exchange;
        } else if (QUEUE.equals(node)) {
            boolean durable = params.containsKey(DURABLE) ? (Boolean) params.get(DURABLE) : false;
            boolean autoDelete = params.containsKey(AUTO_DELETE) ? (Boolean) params.get(AUTO_DELETE) : true;
            String routingKey = params.containsKey(ROUTING_KEY) ? params.get(ROUTING_KEY).toString() : null;
            if (null != routingKey) {
                currentRoutingKey = routingKey;
            }
            boolean exclusive = params.containsKey(EXCLUSIVE) ? (Boolean) params.get(EXCLUSIVE) : false;
            Map arguments = params.containsKey(ARGUMENTS) ? (Map) params.get(ARGUMENTS) : null;

            Queue q = null;
            String name;
            if (params.containsKey(NAME)) {
                try {
                    if (null == params.get(NAME)) {
                        name = null;
                    } else {
                        name = params.get(NAME).toString();
                    }

                    if (name == null) {
                        q = rabbitAdmin.declareQueue();
                    } else {
                        rabbitTemplate.setQueue(name);
                        q = new Queue(name, durable, exclusive, autoDelete);
                        //rabbitAdmin.declareQueue(q);
                    }
                    currentQueue = q;

                    if (null != currentExchange) {
                        Binding binding = null;
                        if (currentExchange instanceof FanoutExchange) {
                            binding = BindingBuilder.bind(q).to((FanoutExchange) currentExchange);
                        } else if (currentExchange instanceof DirectExchange) {
                            binding = BindingBuilder.bind(q).to((DirectExchange) currentExchange).with(routingKey);
                        } else if (currentExchange instanceof HeadersExchange) {
                            binding = BindingBuilder.bind(q).to((HeadersExchange) currentExchange)
                                    .whereAll(arguments).match();
                        } else if (currentExchange instanceof TopicExchange) {
                            binding = BindingBuilder.bind(q).to((TopicExchange) currentExchange).with(routingKey);
                        } else if (currentExchange instanceof CustomExchange) {
                            binding = BindingBuilder.bind(q).to(currentExchange).with(routingKey).noargs();
                        }

                        if (null != binding) {
                            rabbitAdmin.declareBinding(binding);
                        }
                    }
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                    dispatchError(e);
                }
            }
            return q;
        } else if (INIT.equals(node)) {
            CachingConnectionFactory connectionFactory = new CachingConnectionFactory((String) params.get(HOST));
            connectionFactory.setPort(params.containsKey(PORT) ? (Integer) params.get(PORT) : 5672);
            connectionFactory.setUsername((String) params.get(USERNAME));
            connectionFactory.setPassword((String) params.get(PASSWORD));
            if (params.containsKey(VHOST)) {
                connectionFactory.setVirtualHost((String) params.get(VHOST));
            }
            setConnectionFactory(connectionFactory);
        }

        return null;
    }

    @Override
    protected Object createNode(Object o, Map map, Object o1) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("createNode(Object o, Map map, Object o1): %s, %s, %s", o, map, o1));
        }
        return null;
    }

    void dispatchError(Throwable t) {
        dispatchEvent(ERROR, new Object[] { t });
    }

    void dispatchEvent(String name, Object[] args) {
        if (log.isDebugEnabled()) {
            log.debug("Dispatching event " + name + " with args: " + arrayToString(args));
        }
        if (eventHandlers.containsKey(name)) {
            for (Closure cl : eventHandlers.get(name)) {
                cl.call(args);
            }
        }
    }

    public ConnectionFactory getConnectionFactory() {
        return connectionFactory;
    }

    @Override
    public Object invokeMethod(String methodName, Object args) {
        if (CONSUME.equals(methodName)) {
            Consume consume = new Consume();
            SimpleMessageListenerContainer listenerContainer = consume.getListenerContainer();
            Object[] params = (Object[]) args;
            for (Object param : params) {
                if (param instanceof Map) {
                    Map paramMap = (Map) param;

                    if (paramMap.containsKey(ON_MESSAGE)) {
                        Object onMessage = paramMap.get(ON_MESSAGE);
                        if (onMessage instanceof String) {
                            consume.setEventName((String) onMessage);
                        } else if (onMessage instanceof Closure) {
                            consume.setDelegate((Closure) onMessage);
                        } else if (onMessage instanceof MessageListener) {
                            listenerContainer.setMessageListener(onMessage);
                        } else {
                            listenerContainer.setMessageListener(new MessageListenerAdapter(onMessage));
                        }
                    }

                    if (paramMap.containsKey(ACK)) {
                        AcknowledgeMode mode = AcknowledgeMode.valueOf(paramMap.get(ACK).toString().toUpperCase());
                        listenerContainer.setAcknowledgeMode(mode);
                    } else {
                        listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
                    }

                } else if (param instanceof Closure) {
                    consume.setDelegate((Closure) param);
                }
            }
            listenerContainer.setQueues(currentQueue);
            listenerContainer.afterPropertiesSet();
            listenerContainer.start();
            listenerContainers.add(listenerContainer);

            return super.invokeMethod(methodName, consume);
        } else if (PUBLISH.equals(methodName)) {
            Publish publish = new Publish();
            publish.invokeMethod(CALL, args);
            return super.invokeMethod(methodName, publish);
        }

        return super.invokeMethod(methodName, args);
    }

    public boolean isActive() {
        for (SimpleMessageListenerContainer c : listenerContainers) {
            if (c.isActive()) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected void nodeCompleted(Object parent, Object node) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("nodeCompleted(Object parent, Object node): %s, %s", parent, node));
        }
        super.nodeCompleted(parent, node);
    }

    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
        this.rabbitTemplate = new RabbitTemplate(connectionFactory);
        this.rabbitAdmin = new RabbitAdmin(connectionFactory);
    }

    @Override
    protected void setParent(Object from, Object to) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("setParent(Object o, Object o1): %s, %s", from, to));
        }
        if (from instanceof Exchange) {
            currentExchange = (Exchange) from;
        }
        if (from instanceof Queue) {
            currentQueue = (Queue) from;
        }
    }

}