/**
* Copyright (c) 2011 CJSC Investment Company "Troika Dialog", http://troika.ru
*
* 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 hermes.ext.qpid;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.apache.log4j.Logger;
/**
* Hide qpid jms communication.
*
* Layer for qpid communication.
* @author Barys Ilyushonak
*/
public class QpidManager {
private static final String BROKER = "broker";
public static final String HOST = "host";
private static final String QMF2 = "qmf2";
private static final String X_AMQP_0_10_APP_ID = "x-amqp-0-10.app-id";
private static final String OBJECT = "OBJECT";
private static final String _WHAT = "_what";
private static final String QMF_OPCODE = "qmf.opcode";
private static final String _QUERY_REQUEST = "_query_request";
private static final String _SCHEMA_ID = "_schema_id";
private static final String _CLASS_NAME = "_class_name";
private static final int TIMEOUT = 10 * 1000;
private final Logger log = Logger.getLogger(QpidManager.class);
private Connection connection;
private Session session;
private MessageProducer sender;
private Destination responses;
private MessageConsumer receiver;
private String respQueueName;
/**
* Init connection with brocker.
*
* Heavily based on code from Gordon Sim's initial JMS QMF Example.
*
* @param env - jndi env settings
* @throws NamingException - if some goes wrong.
* @throws JMSException - if some goes wrong.
*/
public QpidManager(Hashtable<?, ?> env)
throws NamingException, JMSException {
Context ctx = new InitialContext(env);
ConnectionFactory factory = (ConnectionFactory) ctx.lookup(HOST);
Destination target = (Destination) ctx.lookup(BROKER);
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
sender = session.createProducer(target);
// In the 0.8 Qpid release, the broker incorrectly required
// the client's response queue to be bound to
// qmf.default.direct, requiring the following address:
respQueueName = UUID.randomUUID().toString();
String respQueueAddress = "BURL:management-direct://qmf.default.direct//"
+ respQueueName + "?autodelete='true'";
responses = session.createQueue(respQueueAddress);
// responses = session.createQueue("topic:/qmf.default.direct/" +
// UUID.randomUUID());
// However since the 0.10 release, the simpler approach
// commented out below would be preferred:
// Destination responses = session.createTemporaryQueue();
receiver = session.createConsumer(responses);
connection.start();
}
/**
* Looks up QMF classes and returns Lists of objects relating to those classes
*
* Heavily based on code from Gordon Sim's initial JMS QMF Example.
*
* @param <T> - type of list
* @param qmfSchema - the name of the QMF class being queried
* @return a List of QMF Objects describing that class
* @throws JMSException - if errors with qpid communication.
*/
public <T> List<T> getObjects(QmfTypes qmfSchema)
throws JMSException {
List<T> objects = Collections.emptyList();
MapMessage request = session.createMapMessage();
request.setJMSReplyTo(responses);
request.setStringProperty(X_AMQP_0_10_APP_ID, QMF2);
request.setStringProperty(QMF_OPCODE, _QUERY_REQUEST);
request.setString(_WHAT, OBJECT);
Map<String, Object> schemaId = new HashMap<String, Object>();
schemaId.put(_CLASS_NAME, qmfSchema.getValue());
request.setObject(_SCHEMA_ID, schemaId);
sender.send(request);
Message response = receiver.receive(TIMEOUT);
if (response != null) {
if (response instanceof BytesMessage) {
objects = decode((BytesMessage) response);
} else {
log.info("Received response in incorrect format: " + response);
}
} else {
log.info("No response received");
}
return objects;
}
@SuppressWarnings("unchecked")
/**
* JMS QMF returns amqp/list types as a BytesMessage this method decodes
* that into a Java List
*
* Taken from Gordon Sim's initial JMS QMF Example.
*/
private static <T> List<T> decode(BytesMessage msg)
throws JMSException {
// only handles responses up to 2^31-1 bytes long
byte[] data = new byte[(int) msg.getBodyLength()];
msg.readBytes(data);
//BBDecoder decoder = new BBDecoder();
//decoder.init(ByteBuffer.wrap(data));
//return (List<T>) decoder.readList();
return Collections.EMPTY_LIST ;
}
/**
* close used resources.
*
* @throws JMSException - if errors with qpid communication.
*/
public void close()
throws JMSException {
if (connection != null) {
connection.close();
log.debug("closed QPID connection");
}
}
public String getRespQueueName() {
return respQueueName;
}
}