/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.codehaus.stomp.jms;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.stomp.ProtocolException;
import org.codehaus.stomp.Stomp;
import org.codehaus.stomp.StompFrame;
/**
* Represents a logical session (a parallel unit of work) within a Stomp connection
*
* @version $Revision: 61 $
*/
public class StompSession {
private final ProtocolConverter protocolConverter;
private final Session session;
private MessageProducer producer;
private static Map<String, Destination> temporaryDestinations = new HashMap<String, Destination>();
private final Map<String, StompSubscription> subscriptions = new ConcurrentHashMap<String, StompSubscription>();
private List<String> created = new ArrayList<String>();
private Connection connection;
private InitialContext initialContext;
private static final Log log = LogFactory.getLog(StompSession.class);
public StompSession(InitialContext initialContext, ProtocolConverter protocolConverter, Session session,
Connection connection) throws JMSException {
this.initialContext = initialContext;
this.protocolConverter = protocolConverter;
this.session = session;
this.connection = connection;
this.producer = session.createProducer(null);
}
public ProtocolConverter getProtocolConverter() {
return protocolConverter;
}
public void close() throws JMSException {
Iterator<StompSubscription> iterator = subscriptions.values().iterator();
try {
while (iterator.hasNext()) {
iterator.next().close();
}
} finally {
subscriptions.clear();
connection.close();
}
}
public void sendToJms(StompFrame command) throws JMSException, ProtocolException, NamingException,
UnsupportedEncodingException {
Map headers = command.getHeaders();
String destinationName = (String) headers.remove(Stomp.Headers.Send.DESTINATION);
Message message = convertFrame(command);
Destination destination = convertDestination(destinationName, false);
int deliveryMode = getDeliveryMode(headers);
int priority = getPriority(headers);
long timeToLive = getTimeToLive(headers);
producer.send(destination, message, deliveryMode, priority, timeToLive);
log.debug("Sent to HQ: " + message.getJMSMessageID());
}
public void sendToStomp(Message message, String subscriptionID) throws JMSException, IOException {
log.debug("Sending to stomp");
StompFrame frame = convertMessage(message);
frame.getHeaders().put(Stomp.Headers.Message.SUBSCRIPTION, subscriptionID);
protocolConverter.sendToStomp(frame);
}
public Destination convertDestination(String name, boolean forceNew) throws ProtocolException, JMSException,
NamingException {
if (name == null) {
throw new ProtocolException("No destination is specified!");
} else if (name.startsWith("/queue/") || name.startsWith("/topic/")) {
return (Destination) initialContext.lookup("java:" + name);
// } else if (name.startsWith("/temp-queue/")) {
// String tempName = name.substring("/temp-queue/".length(), name.length());
// Destination answer = temporaryDestinations.get(tempName);
//
// if (forceNew || answer == null) {
// return temporaryDestination(tempName, session.createTemporaryQueue());
// } else {
// return answer;
// }
// } else if (name.startsWith("/temp-topic/")) {
// String tempName = name.substring("/temp-topic/".length(), name.length());
// Destination answer = temporaryDestinations.get(tempName);
// if (forceNew || answer == null) {
// return temporaryDestination(tempName, session.createTemporaryTopic());
// } else {
// return answer;
// }
} else {
throw new ProtocolException("Illegal destination name: [" + name + "] -- StompConnect destinations "
+ "must begine with one of: /queue/ /topic/ /temp-queue/ /temp-topic/");
}
}
protected String convertDestination(Destination d) throws JMSException {
if (d == null) {
return null;
}
StringBuffer buffer = new StringBuffer();
if (d instanceof Topic) {
Topic topic = (Topic) d;
// if (d instanceof TemporaryTopic) {
// buffer.append("/temp-topic/");
// temporaryDestination(topic.getTopicName(), d);
// } else {
buffer.append("/topic/");
// }
buffer.append(topic.getTopicName());
} else {
Queue queue = (Queue) d;
// if (d instanceof TemporaryQueue) {
// buffer.append("/temp-queue/");
// temporaryDestination(queue.getQueueName(), d);
// } else {
buffer.append("/queue/");
// }
buffer.append(queue.getQueueName());
}
return buffer.toString();
}
protected int getDeliveryMode(Map headers) throws JMSException {
Object o = headers.remove(Stomp.Headers.Send.PERSISTENT);
if (o != null) {
return "true".equals(o) ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT;
} else {
return producer.getDeliveryMode();
}
}
protected int getPriority(Map headers) throws JMSException {
Object o = headers.remove(Stomp.Headers.Send.PRIORITY);
if (o != null) {
return Integer.parseInt((String) o);
} else {
return producer.getPriority();
}
}
protected long getTimeToLive(Map headers) throws JMSException {
Object o = headers.remove(Stomp.Headers.Send.EXPIRATION_TIME);
if (o != null) {
return Long.parseLong((String) o);
} else {
return producer.getTimeToLive();
}
}
protected void copyStandardHeadersFromMessageToFrame(Message message, StompFrame command) throws JMSException {
final Map headers = command.getHeaders();
headers.put(Stomp.Headers.Message.DESTINATION, convertDestination(message.getJMSDestination()));
headers.put(Stomp.Headers.Message.MESSAGE_ID, message.getJMSMessageID());
if (message.getJMSCorrelationID() != null) {
headers.put(Stomp.Headers.Message.CORRELATION_ID, message.getJMSCorrelationID());
}
headers.put(Stomp.Headers.Message.EXPIRATION_TIME, "" + message.getJMSExpiration());
if (message.getJMSRedelivered()) {
headers.put(Stomp.Headers.Message.REDELIVERED, "true");
}
headers.put(Stomp.Headers.Message.PRORITY, "" + message.getJMSPriority());
if (message.getJMSReplyTo() != null) {
headers.put(Stomp.Headers.Message.REPLY_TO, convertDestination(message.getJMSReplyTo()));
}
headers.put(Stomp.Headers.Message.TIMESTAMP, "" + message.getJMSTimestamp());
if (message.getJMSType() != null) {
headers.put(Stomp.Headers.Message.TYPE, message.getJMSType());
}
// now lets add all the message headers
Enumeration names = message.getPropertyNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
Object value = message.getObjectProperty(name);
headers.put(name, value);
}
}
protected void copyStandardHeadersFromFrameToMessage(StompFrame command, Message msg) throws JMSException,
ProtocolException, NamingException {
final Map headers = new HashMap(command.getHeaders());
// the standard JMS headers
msg.setJMSCorrelationID((String) headers.remove(Stomp.Headers.Send.CORRELATION_ID));
Object o = headers.remove(Stomp.Headers.Send.TYPE);
if (o != null) {
msg.setJMSType((String) o);
}
o = headers.remove(Stomp.Headers.Send.REPLY_TO);
if (o != null) {
msg.setJMSReplyTo(convertDestination((String) o, false));
}
// now the general headers
for (Iterator iter = headers.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
String name = (String) entry.getKey();
Object value = entry.getValue();
msg.setObjectProperty(name, value);
}
}
protected Message convertFrame(StompFrame command) throws JMSException, UnsupportedEncodingException, ProtocolException,
NamingException {
final Map headers = command.getHeaders();
final Message msg;
if (headers.containsKey(Stomp.Headers.CONTENT_LENGTH)) {
headers.remove(Stomp.Headers.CONTENT_LENGTH);
BytesMessage bm = session.createBytesMessage();
bm.writeBytes(command.getContent());
msg = bm;
} else {
String text = new String(command.getContent(), StandardCharsets.UTF_8);
msg = session.createTextMessage(text);
}
copyStandardHeadersFromFrameToMessage(command, msg);
return msg;
}
protected StompFrame convertMessage(Message message) throws JMSException, UnsupportedEncodingException {
StompFrame command = new StompFrame();
command.setAction(Stomp.Responses.MESSAGE);
Map headers = new HashMap(25);
command.setHeaders(headers);
copyStandardHeadersFromMessageToFrame(message, command);
if (message instanceof TextMessage) {
TextMessage msg = (TextMessage) message;
command.setContent(msg.getText().getBytes(StandardCharsets.UTF_8));
} else if (message instanceof BytesMessage) {
BytesMessage msg = (BytesMessage) message;
byte[] data = new byte[(int) msg.getBodyLength()];
msg.readBytes(data);
headers.put(Stomp.Headers.CONTENT_LENGTH, "" + data.length);
command.setContent(data);
}
return command;
}
public Message receiveFromJms(String destinationName, Map headers) throws JMSException, ProtocolException, NamingException {
long ttl = getTimeToLive(headers);
log.trace("Consuming message - ttl=" + ttl);
Destination destination = convertDestination(destinationName, true);
MessageConsumer consumer = session.createConsumer(destination);
Message message;
if (ttl > 0) {
message = consumer.receive(ttl);
} else {
message = consumer.receive();
}
if (message != null) {
// As this is a dequeue, automatically acknowledge the message
message.acknowledge();
}
consumer.close();
log.trace("Received message: " + message);
return message;
}
public MessageConsumer createConsumer(Map headers) throws ProtocolException, JMSException, NamingException {
String selector = (String) headers.remove(Stomp.Headers.Subscribe.SELECTOR);
String destinationName = (String) headers.get(Stomp.Headers.Subscribe.DESTINATION);
Destination destination = convertDestination(destinationName, true);
MessageConsumer consumer;
if (destination instanceof Topic) {
boolean noLocal = false;
String value = (String) headers.get(Stomp.Headers.Subscribe.NO_LOCAL);
if (value != null && "true".equalsIgnoreCase(value)) {
noLocal = true;
}
String subscriberName = (String) headers.get(Stomp.Headers.Subscribe.DURABLE_SUBSCRIPTION_NAME);
if (subscriberName != null) {
consumer = session.createDurableSubscriber((Topic) destination, subscriberName, selector, noLocal);
} else {
consumer = session.createConsumer(destination, selector, noLocal);
}
} else {
consumer = session.createConsumer(destination, selector);
}
return consumer;
}
public StompSubscription subscribe(String subscriptionId, StompFrame command) throws ProtocolException, JMSException,
NamingException {
if (subscriptions.size() > 0) {
throw new ProtocolException("This connection already has a subscription");
}
StompSubscription subscription = (StompSubscription) subscriptions.get(subscriptionId);
if (subscription != null) {
throw new ProtocolException("There already is a subscription for: " + subscriptionId
+ ". Either use unique subscription IDs or do not create multiple subscriptions for the same destination");
} else {
subscription = new StompSubscription(this, subscriptionId, command);
subscriptions.put(subscriptionId, subscription);
}
return subscription;
}
public void unsubscribe(String subscriptionId) throws ProtocolException, JMSException {
StompSubscription subscription = (StompSubscription) subscriptions.remove(subscriptionId);
if (subscription == null) {
throw new ProtocolException("Cannot unsubscribe as mo subscription exists for id: " + subscriptionId);
}
subscription.close();
}
public void resume() throws JMSException {
log.debug("Resuming session: " + session);
Iterator<StompSubscription> iterator = subscriptions.values().iterator();
while (iterator.hasNext()) {
StompSubscription next = iterator.next();
next.resume();
}
}
public void recover() throws JMSException {
log.debug("Recovering session: " + session);
session.recover();
}
}