/*
* JBoss, Home of Professional Open Source Copyright 2005-2008, Red Hat
* Middleware LLC, and individual contributors by the @authors tag. See the
* copyright.txt in the distribution for a full listing of individual
* contributors.
*
* This is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this software; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
* site: http://www.fsf.org.
*/
package org.jboss.messaging.jms.client;
import org.jboss.messaging.core.client.ClientConsumer;
import org.jboss.messaging.core.client.ClientProducer;
import org.jboss.messaging.core.client.ClientSession;
import org.jboss.messaging.core.exception.MessagingException;
import org.jboss.messaging.core.logging.Logger;
import org.jboss.messaging.core.remoting.impl.wireformat.SessionBindingQueryResponseMessage;
import org.jboss.messaging.core.remoting.impl.wireformat.SessionQueueQueryResponseMessage;
import org.jboss.messaging.jms.JBossDestination;
import org.jboss.messaging.jms.JBossQueue;
import org.jboss.messaging.jms.JBossTemporaryQueue;
import org.jboss.messaging.jms.JBossTemporaryTopic;
import org.jboss.messaging.jms.JBossTopic;
import org.jboss.messaging.util.SimpleString;
import javax.jms.BytesMessage;
import javax.jms.Destination;
import javax.jms.IllegalStateException;
import javax.jms.InvalidClientIDException;
import javax.jms.InvalidDestinationException;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueBrowser;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.StreamMessage;
import javax.jms.TemporaryQueue;
import javax.jms.TemporaryTopic;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.jms.TransactionInProgressException;
import javax.jms.XAQueueSession;
import javax.jms.XASession;
import javax.jms.XATopicSession;
import javax.transaction.xa.XAResource;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
*
* Note that we *do not* support JMS ASF (Application Server Facilities) optional
* constructs such as ConnectionConsumer
*
* @author <a href="mailto:ovidiu@feodorov.com">Ovidiu Feodorov</a>
* @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a>
* @author <a href="mailto:ataylor@redhat.com">Andy Taylor</a>
*
* @version <tt>$Revision$</tt>
*
* $Id$
*/
public class JBossSession implements Session, XASession, QueueSession, XAQueueSession, TopicSession, XATopicSession
{
// Constants -----------------------------------------------------
public static final int TYPE_GENERIC_SESSION = 0;
public static final int TYPE_QUEUE_SESSION = 1;
public static final int TYPE_TOPIC_SESSION = 2;
// Static --------------------------------------------------------
private static final Logger log = Logger.getLogger(JBossSession.class);
// Attributes ----------------------------------------------------
private final JBossConnection connection;
private final ClientSession session;
private final int sessionType;
private final int ackMode;
private final boolean transacted;
private final boolean xa;
private boolean recoverCalled;
private final Set<JBossMessageConsumer> consumers = new HashSet<JBossMessageConsumer>();
// Constructors --------------------------------------------------
public JBossSession(final JBossConnection connection,
final boolean transacted,
final boolean xa,
final int ackMode,
final ClientSession session,
final int sessionType)
{
this.connection = connection;
this.ackMode = ackMode;
this.session = session;
this.sessionType = sessionType;
this.transacted = transacted;
this.xa = xa;
}
// Session implementation ----------------------------------------
public BytesMessage createBytesMessage() throws JMSException
{
checkClosed();
return new JBossBytesMessage(session);
}
public MapMessage createMapMessage() throws JMSException
{
checkClosed();
return new JBossMapMessage(session);
}
public Message createMessage() throws JMSException
{
checkClosed();
return new JBossMessage(session);
}
public ObjectMessage createObjectMessage() throws JMSException
{
checkClosed();
return new JBossObjectMessage(session);
}
public ObjectMessage createObjectMessage(final Serializable object) throws JMSException
{
checkClosed();
JBossObjectMessage jbm = new JBossObjectMessage(session);
jbm.setObject(object);
return jbm;
}
public StreamMessage createStreamMessage() throws JMSException
{
checkClosed();
return new JBossStreamMessage(session);
}
public TextMessage createTextMessage() throws JMSException
{
checkClosed();
return new JBossTextMessage(session);
}
public TextMessage createTextMessage(final String text) throws JMSException
{
checkClosed();
JBossTextMessage jbm = new JBossTextMessage(session);
jbm.setText(text);
return jbm;
}
public boolean getTransacted() throws JMSException
{
checkClosed();
return transacted;
}
public int getAcknowledgeMode() throws JMSException
{
checkClosed();
return ackMode;
}
public void commit() throws JMSException
{
if (!transacted)
{
throw new IllegalStateException("Cannot commit a non-transacted session");
}
if (xa)
{
throw new TransactionInProgressException("Cannot call commit on an XA session");
}
try
{
session.commit();
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public void rollback() throws JMSException
{
if (!transacted)
{
throw new IllegalStateException("Cannot rollback a non-transacted session");
}
if (xa)
{
throw new TransactionInProgressException("Cannot call rollback on an XA session");
}
try
{
session.rollback();
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public void close() throws JMSException
{
try
{
for (JBossMessageConsumer cons : new HashSet<JBossMessageConsumer>(consumers))
{
cons.close();
}
session.close();
connection.removeSession(this);
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public void recover() throws JMSException
{
if (transacted)
{
throw new IllegalStateException("Cannot recover a transacted session");
}
try
{
session.rollback();
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
recoverCalled = true;
}
public MessageListener getMessageListener() throws JMSException
{
checkClosed();
return null;
}
public void setMessageListener(final MessageListener listener) throws JMSException
{
checkClosed();
}
public void run()
{
}
public MessageProducer createProducer(final Destination destination) throws JMSException
{
if (destination != null && !(destination instanceof JBossDestination))
{
throw new InvalidDestinationException("Not a JBoss Destination:" + destination);
}
JBossDestination jbd = (JBossDestination) destination;
try
{
ClientProducer producer = session.createProducer(jbd == null ? null : jbd.getSimpleAddress());
return new JBossMessageProducer(connection, producer, jbd, session);
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public MessageConsumer createConsumer(final Destination destination) throws JMSException
{
return createConsumer(destination, null, false);
}
public MessageConsumer createConsumer(final Destination destination, final String messageSelector) throws JMSException
{
return createConsumer(destination, messageSelector, false);
}
public MessageConsumer createConsumer(final Destination destination,
final String messageSelector,
final boolean noLocal) throws JMSException
{
if (destination == null)
{
throw new InvalidDestinationException("Cannot create a consumer with a null destination");
}
if (!(destination instanceof JBossDestination))
{
throw new InvalidDestinationException("Not a JBossDestination:" + destination);
}
JBossDestination jbdest = (JBossDestination) destination;
JBossMessageConsumer consumer = createConsumer(jbdest, null, messageSelector, noLocal);
return consumer;
}
public Queue createQueue(final String queueName) throws JMSException
{
// As per spec. section 4.11
if (sessionType == TYPE_TOPIC_SESSION)
{
throw new IllegalStateException("Cannot create a queue using a TopicSession");
}
JBossQueue queue = new JBossQueue(queueName);
try
{
SessionQueueQueryResponseMessage response = session.queueQuery(queue.getSimpleAddress());
if (!response.isExists())
{
throw new JMSException("There is no queue with name " + queueName);
}
else
{
return queue;
}
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public Topic createTopic(final String topicName) throws JMSException
{
// As per spec. section 4.11
if (sessionType == TYPE_QUEUE_SESSION)
{
throw new IllegalStateException("Cannot create a topic on a QueueSession");
}
JBossTopic topic = new JBossTopic(topicName);
try
{
SessionBindingQueryResponseMessage response = session.bindingQuery(topic.getSimpleAddress());
if (!response.isExists())
{
throw new JMSException("There is no topic with name " + topicName);
}
else
{
return topic;
}
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public TopicSubscriber createDurableSubscriber(final Topic topic, final String name) throws JMSException
{
return createDurableSubscriber(topic, name, null, false);
}
public TopicSubscriber createDurableSubscriber(final Topic topic,
final String name,
String messageSelector,
final boolean noLocal) throws JMSException
{
// As per spec. section 4.11
if (sessionType == TYPE_QUEUE_SESSION)
{
throw new IllegalStateException("Cannot create a durable subscriber on a QueueSession");
}
if (topic == null)
{
throw new InvalidDestinationException("Cannot create a durable subscriber on a null topic");
}
if (!(topic instanceof JBossTopic))
{
throw new InvalidDestinationException("Not a JBossTopic:" + topic);
}
if ("".equals(messageSelector))
{
messageSelector = null;
}
JBossDestination jbdest = (JBossDestination) topic;
return createConsumer(jbdest, name, messageSelector, noLocal);
}
private JBossMessageConsumer createConsumer(final JBossDestination dest,
final String subscriptionName,
String selectorString,
final boolean noLocal) throws JMSException
{
try
{
selectorString = "".equals(selectorString) ? null : selectorString;
if (noLocal)
{
connection.setHasNoLocal();
String filter = JBossConnection.CONNECTION_ID_PROPERTY_NAME.toString() + "<>'" + connection.getUID() + "'";
if (selectorString != null)
{
selectorString += " AND " + filter;
}
else
{
selectorString = filter;
}
}
SimpleString coreFilterString = null;
if (selectorString != null)
{
coreFilterString = new SimpleString(SelectorTranslator.convertToJBMFilterString(selectorString));
}
ClientConsumer consumer;
SimpleString autoDeleteQueueName = null;
if (dest instanceof Queue)
{
SessionQueueQueryResponseMessage response = session.queueQuery(dest.getSimpleAddress());
if (!response.isExists())
{
throw new InvalidDestinationException("Queue " + dest.getName() + " does not exist");
}
consumer = session.createConsumer(dest.getSimpleAddress(), coreFilterString, false);
}
else
{
SessionBindingQueryResponseMessage response = session.bindingQuery(dest.getSimpleAddress());
if (!response.isExists())
{
throw new InvalidDestinationException("Topic " + dest.getName() + " does not exist");
}
SimpleString queueName;
if (subscriptionName == null)
{
// Non durable sub
queueName = new SimpleString(UUID.randomUUID().toString());
session.createQueue(dest.getSimpleAddress(), queueName, coreFilterString, false, true);
consumer = session.createConsumer(queueName, null, false);
autoDeleteQueueName = queueName;
}
else
{
// Durable sub
if (connection.getClientID() == null)
{
throw new InvalidClientIDException("Cannot create durable subscription - client ID has not been set");
}
if (dest.isTemporary())
{
throw new InvalidDestinationException("Cannot create a durable subscription on a temporary topic");
}
queueName = new SimpleString(JBossTopic.createQueueNameForDurableSubscription(connection.getClientID(),
subscriptionName));
SessionQueueQueryResponseMessage subResponse = session.queueQuery(queueName);
if (!subResponse.isExists())
{
session.createQueue(dest.getSimpleAddress(), queueName, coreFilterString, true, false);
}
else
{
// Already exists
if (subResponse.getConsumerCount() > 0)
{
throw new IllegalStateException("Cannot create a subscriber on the durable subscription since it already has subscriber(s)");
}
// From javax.jms.Session Javadoc (and also JMS 1.1 6.11.1):
// A client can change an existing durable subscription by
// creating a durable
// TopicSubscriber with the same name and a new topic and/or
// message selector.
// Changing a durable subscriber is equivalent to
// unsubscribing (deleting) the old
// one and creating a new one.
SimpleString oldFilterString = subResponse.getFilterString();
boolean selectorChanged = (coreFilterString == null && oldFilterString != null) || (oldFilterString == null && coreFilterString != null) ||
(oldFilterString != null && coreFilterString != null && !oldFilterString.equals(coreFilterString));
SimpleString oldTopicName = subResponse.getAddress();
boolean topicChanged = !oldTopicName.equals(dest.getSimpleAddress());
if (selectorChanged || topicChanged)
{
// Delete the old durable sub
session.deleteQueue(queueName);
// Create the new one
session.createQueue(dest.getSimpleAddress(), queueName, coreFilterString, true, false);
}
}
consumer = session.createConsumer(queueName, null, false);
}
}
JBossMessageConsumer jbc = new JBossMessageConsumer(this,
consumer,
noLocal,
dest,
selectorString,
autoDeleteQueueName);
consumers.add(jbc);
return jbc;
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public QueueBrowser createBrowser(final Queue queue) throws JMSException
{
return createBrowser(queue, null);
}
public QueueBrowser createBrowser(final Queue queue, String filterString) throws JMSException
{
// As per spec. section 4.11
if (sessionType == TYPE_TOPIC_SESSION)
{
throw new IllegalStateException("Cannot create a browser on a TopicSession");
}
if (queue == null)
{
throw new InvalidDestinationException("Cannot create a browser with a null queue");
}
if (!(queue instanceof JBossQueue))
{
throw new InvalidDestinationException("Not a JBossQueue:" + queue);
}
if ("".equals(filterString))
{
filterString = null;
}
JBossQueue jbq = (JBossQueue) queue;
try
{
SessionBindingQueryResponseMessage message = session.bindingQuery(new SimpleString(jbq.getAddress()));
if(!message.isExists())
{
throw new InvalidDestinationException(jbq.getAddress() + " does not exist");
}
}
catch (MessagingException e)
{
JMSExceptionHelper.convertFromMessagingException(e);
}
return new JBossQueueBrowser(jbq, filterString, session);
}
public TemporaryQueue createTemporaryQueue() throws JMSException
{
// As per spec. section 4.11
if (sessionType == TYPE_TOPIC_SESSION)
{
throw new IllegalStateException("Cannot create a temporary queue using a TopicSession");
}
String queueName = UUID.randomUUID().toString();
try
{
JBossTemporaryQueue queue = new JBossTemporaryQueue(this, queueName);
SimpleString simpleAddress = queue.getSimpleAddress();
session.createQueue(queue.getSimpleAddress(), queue.getSimpleAddress(), null, false, true);
session.addDestination(queue.getSimpleAddress(), false, true);
connection.addTemporaryAddress(simpleAddress);
connection.addTemporaryQueue(simpleAddress);
return queue;
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public TemporaryTopic createTemporaryTopic() throws JMSException
{
// As per spec. section 4.11
if (sessionType == TYPE_QUEUE_SESSION)
{
throw new IllegalStateException("Cannot create a temporary topic on a QueueSession");
}
String topicName = UUID.randomUUID().toString();
try
{
JBossTemporaryTopic topic = new JBossTemporaryTopic(this, topicName);
SimpleString simpleAddress = topic.getSimpleAddress();
session.addDestination(simpleAddress, false, true);
connection.addTemporaryAddress(simpleAddress);
return topic;
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public void unsubscribe(final String name) throws JMSException
{
// As per spec. section 4.11
if (sessionType == TYPE_QUEUE_SESSION)
{
throw new IllegalStateException("Cannot unsubscribe using a QueueSession");
}
SimpleString queueName = new SimpleString(JBossTopic.createQueueNameForDurableSubscription(connection.getClientID(),
name));
try
{
SessionQueueQueryResponseMessage response = session.queueQuery(queueName);
if (!response.isExists())
{
throw new InvalidDestinationException("Cannot unsubscribe, subscription with name " + name +
" does not exist");
}
if (response.getConsumerCount() != 0)
{
throw new IllegalStateException("Cannot unsubscribe durable subscription " + name +
" since it has active subscribers");
}
session.deleteQueue(queueName);
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
// XASession implementation
public Session getSession() throws JMSException
{
if (!xa)
{
throw new IllegalStateException("Isn't an XASession");
}
return this;
}
public XAResource getXAResource()
{
return session.getXAResource();
}
// QueueSession implementation
public QueueReceiver createReceiver(final Queue queue, final String messageSelector) throws JMSException
{
return (QueueReceiver) createConsumer(queue, messageSelector);
}
public QueueReceiver createReceiver(final Queue queue) throws JMSException
{
return (QueueReceiver) createConsumer(queue);
}
public QueueSender createSender(final Queue queue) throws JMSException
{
return (QueueSender) createProducer(queue);
}
// XAQueueSession implementation
public QueueSession getQueueSession() throws JMSException
{
return (QueueSession) getSession();
}
// TopicSession implementation
public TopicPublisher createPublisher(final Topic topic) throws JMSException
{
return (TopicPublisher) createProducer(topic);
}
public TopicSubscriber createSubscriber(final Topic topic, final String messageSelector, final boolean noLocal) throws JMSException
{
return (TopicSubscriber) createConsumer(topic, messageSelector, noLocal);
}
public TopicSubscriber createSubscriber(final Topic topic) throws JMSException
{
return (TopicSubscriber) createConsumer(topic);
}
// XATopicSession implementation
public TopicSession getTopicSession() throws JMSException
{
return (TopicSession) getSession();
}
// Public --------------------------------------------------------
public String toString()
{
return "JBossSession->" + session;
}
public ClientSession getCoreSession()
{
return session;
}
public boolean isRecoverCalled()
{
return recoverCalled;
}
public void setRecoverCalled(final boolean recoverCalled)
{
this.recoverCalled = recoverCalled;
}
public void deleteTemporaryTopic(final JBossTemporaryTopic tempTopic) throws JMSException
{
try
{
SessionBindingQueryResponseMessage response = session.bindingQuery(tempTopic.getSimpleAddress());
if (!response.isExists())
{
throw new InvalidDestinationException("Cannot delete temporary topic " + tempTopic.getName() +
" does not exist");
}
if (!response.getQueueNames().isEmpty())
{
throw new IllegalStateException("Cannot delete temporary topic " + tempTopic.getName() +
" since it has subscribers");
}
SimpleString address = tempTopic.getSimpleAddress();
session.removeDestination(address, false);
connection.removeTemporaryAddress(address);
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public void deleteTemporaryQueue(final JBossTemporaryQueue tempQueue) throws JMSException
{
try
{
SessionQueueQueryResponseMessage response = session.queueQuery(tempQueue.getSimpleAddress());
if (!response.isExists())
{
throw new InvalidDestinationException("Cannot delete temporary queue " + tempQueue.getName() +
" does not exist");
}
if (response.getConsumerCount() > 0)
{
throw new IllegalStateException("Cannot delete temporary queue " + tempQueue.getName() +
" since it has subscribers");
}
SimpleString address = tempQueue.getSimpleAddress();
session.removeDestination(address, false);
session.deleteQueue(address);
connection.removeTemporaryAddress(address);
connection.removeTemporaryQueue(address);
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public void deleteQueue(final SimpleString queueName) throws JMSException
{
try
{
session.deleteQueue(queueName);
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public void start() throws JMSException
{
try
{
session.start();
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public void stop() throws JMSException
{
try
{
session.stop();
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public void removeConsumer(final JBossMessageConsumer consumer)
{
consumers.remove(consumer);
}
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------
// Private -------------------------------------------------------
private void checkClosed() throws JMSException
{
if (session.isClosed())
{
throw new IllegalStateException("Session is closed");
}
}
// Inner classes -------------------------------------------------
}