Java tutorial
/* * Copyright 2013 David Tinker * * 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 io.qdb.server.output; import com.rabbitmq.client.*; import io.qdb.server.ExpectedIOException; import io.qdb.server.model.Output; import io.qdb.server.model.Queue; import java.io.IOException; import java.net.ConnectException; /** * Published messages to a RabbitMQ server. */ public class RabbitMQOutputHandler extends OutputHandlerAdapter implements ShutdownListener { public String exchange; public String[] queues; public int heartbeat = 30; public boolean persistentMessages; protected String outputPath; protected ConnectionFactory connectionFactory; protected Connection con; protected Channel channel; protected String exchangeType; protected boolean exchangeDurable; protected boolean[] queueDurable; protected AMQP.BasicProperties messageProperties; @SuppressWarnings("unchecked") @Override public void init(Queue q, Output output, String outputPath) throws Exception { this.outputPath = outputPath; if (output.getUrl() == null) throw new IllegalArgumentException("url is required"); if (queues != null && queues.length == 0) queues = null; if (exchange != null && exchange.length() == 0) exchange = null; if (queues == null) { if (exchange == null) throw new IllegalArgumentException("queues or exchange is required"); } else { queueDurable = new boolean[queues.length]; for (int i = 0; i < queues.length; i++) { String[] toks = queues[i].split("[\\s]*#[\\s]*"); queues[i] = toks[0]; queueDurable[i] = toks.length == 1 || "true".equals(toks[1]); if (queues[i].length() == 0) throw new IllegalArgumentException("empty queue name"); } if (exchange == null) { if (queues.length > 1) throw new IllegalArgumentException("exchange is required for multiple queues"); exchange = queues[0]; } } String[] toks = exchange.split("[\\s]*#[\\s]*"); exchange = toks[0]; exchangeType = toks.length >= 2 ? toks[1] : "fanout"; exchangeDurable = toks.length < 3 || "true".equals(toks[2]); if (persistentMessages) { messageProperties = new AMQP.BasicProperties(null, null, null, 2, 0, null, null, null, null, null, null, null, null, null); } connectionFactory = new ConnectionFactory(); connectionFactory.setUri(output.getUrl()); connectionFactory.setRequestedHeartbeat(heartbeat); ensureChannel(); } @Override public long processMessage(long messageId, String routingKey, long timestamp, byte[] payload) throws Exception { if (log.isDebugEnabled()) log.debug(outputPath + ": Publishing " + messageId); ensureChannel().basicPublish(exchange, routingKey, messageProperties, payload); return messageId; } @Override public void close() throws IOException { // this method cannot be synchronized or we get deadlock with shutdownCompleted if (con != null) { try { con.close(); } catch (AlreadyClosedException ignore) { } } } protected synchronized Channel ensureChannel() throws Exception { if (channel == null) { try { con = connectionFactory.newConnection(); } catch (ConnectException e) { throw new ExpectedIOException(e.getMessage() + ": " + getConnectionInfo()); } channel = con.createChannel(); if (log.isInfoEnabled()) log.info(outputPath + ": Connected to " + getConnectionInfo()); channel.addShutdownListener(this); initChannel(channel); } return channel; } protected void initChannel(Channel channel) throws IOException { channel.exchangeDeclare(exchange, exchangeType, exchangeDurable); if (queues != null) { for (int i = 0; i < queues.length; i++) { String q = queues[i]; channel.queueDeclare(q, queueDurable[i], false, false, null); channel.queueBind(q, exchange, ""); } } } @Override public synchronized void shutdownCompleted(ShutdownSignalException cause) { channel = null; if (!cause.isInitiatedByApplication()) { log.error(outputPath + ": Channel closed unexpectedly: " + cause.getMessage()); try { if (con.isOpen()) con.close(); } catch (Exception ignore) { } con = null; } } protected String getConnectionInfo() { if (connectionFactory == null) return "(null)"; String host = connectionFactory.getHost(); if (host.length() == 0) host = "mac-subs-009"; return "amqp" + (connectionFactory.isSSL() ? "s" : "") + "://" + connectionFactory.getUsername() + "@" + host + ":" + connectionFactory.getPort() + connectionFactory.getVirtualHost(); } }