Java tutorial
/* * 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; } } }