/*
* 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 java.util.HashSet;
import java.util.Set;
import javax.jms.Connection;
import javax.jms.ConnectionConsumer;
import javax.jms.ConnectionMetaData;
import javax.jms.Destination;
import javax.jms.ExceptionListener;
import javax.jms.IllegalStateException;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueSession;
import javax.jms.ServerSessionPool;
import javax.jms.Session;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicSession;
import javax.jms.XAConnection;
import javax.jms.XAQueueConnection;
import javax.jms.XAQueueSession;
import javax.jms.XASession;
import javax.jms.XATopicConnection;
import javax.jms.XATopicSession;
import org.jboss.messaging.core.client.ClientSession;
import org.jboss.messaging.core.client.ClientSessionFactory;
import org.jboss.messaging.core.exception.MessagingException;
import org.jboss.messaging.core.logging.Logger;
import org.jboss.messaging.core.remoting.FailureListener;
import org.jboss.messaging.core.version.Version;
import org.jboss.messaging.util.ConcurrentHashSet;
import org.jboss.messaging.util.SimpleString;
import org.jboss.messaging.util.UUIDGenerator;
import org.jboss.messaging.util.VersionLoader;
/**
* @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 JBossConnection implements
Connection, QueueConnection, TopicConnection,
XAConnection, XAQueueConnection, XATopicConnection
{
// Constants ------------------------------------------------------------------------------------
private static final Logger log = Logger.getLogger(JBossConnection.class);
public static final int TYPE_GENERIC_CONNECTION = 0;
public static final int TYPE_QUEUE_CONNECTION = 1;
public static final int TYPE_TOPIC_CONNECTION = 2;
public static final SimpleString CONNECTION_ID_PROPERTY_NAME = new SimpleString("__JBM_CID");
// Static ---------------------------------------------------------------------------------------
// Attributes -----------------------------------------------------------------------------------
private final int connectionType;
private final Set<JBossSession> sessions = new ConcurrentHashSet<JBossSession>();
private final Set<SimpleString> tempAddresses = new ConcurrentHashSet<SimpleString>();
private final Set<SimpleString> tempQueues = new ConcurrentHashSet<SimpleString>();
private volatile boolean hasNoLocal;
private volatile ExceptionListener exceptionListener;
private volatile boolean justCreated = true;
private volatile ConnectionMetaData metaData;
private volatile boolean closed;
private volatile boolean started;
private String clientID;
private final ClientSessionFactory sessionFactory;
private final SimpleString uid;
private final String username;
private final String password;
private final FailureListener listener = new JMSFailureListener();
private final Version thisVersion;
// Constructors ---------------------------------------------------------------------------------
public JBossConnection(final String username, final String password, final int connectionType,
final String clientID, final int dupsOKBatchSize,
final ClientSessionFactory sessionFactory)
{
this.username = username;
this.password = password;
this.connectionType = connectionType;
this.clientID = clientID;
this.sessionFactory = sessionFactory;
uid = UUIDGenerator.getInstance().generateSimpleStringUUID();
thisVersion = VersionLoader.getVersion();
}
// Connection implementation --------------------------------------------------------------------
public Session createSession(final boolean transacted, final int acknowledgeMode) throws JMSException
{
checkClosed();
return createSessionInternal(transacted, acknowledgeMode, false, TYPE_GENERIC_CONNECTION, false);
}
public String getClientID() throws JMSException
{
checkClosed();
justCreated = false;
return clientID;
}
public void setClientID(final String clientID) throws JMSException
{
checkClosed();
if (this.clientID != null)
{
throw new IllegalStateException("Client id has already been set");
}
if (!justCreated)
{
throw new IllegalStateException("setClientID can only be called directly after the connection is created");
}
this.clientID = clientID;
justCreated = false;
}
public ConnectionMetaData getMetaData() throws JMSException
{
checkClosed();
justCreated = false;
if (metaData == null)
{
metaData = new JBossConnectionMetaData(thisVersion);
}
return metaData;
}
public ExceptionListener getExceptionListener() throws JMSException
{
checkClosed();
justCreated = false;
return exceptionListener;
}
public void setExceptionListener(final ExceptionListener listener) throws JMSException
{
checkClosed();
exceptionListener = listener;
justCreated = false;
}
public void start() throws JMSException
{
checkClosed();
for (JBossSession session: sessions)
{
session.start();
}
justCreated = false;
started = true;
}
public void stop() throws JMSException
{
checkClosed();
for (JBossSession session: sessions)
{
session.stop();
}
justCreated = false;
started = false;
}
public synchronized void close() throws JMSException
{
if (closed)
{
return;
}
try
{
for (JBossSession session: new HashSet<JBossSession>(sessions))
{
session.close();
}
//TODO may be a better way of doing this that doesn't involve creating a new session
if (!tempAddresses.isEmpty() || !tempQueues.isEmpty())
{
ClientSession session = null;
try
{
session =
sessionFactory.createSession(username, password, false, true, true, false);
//Remove any temporary queues and addresses
for (SimpleString address: tempAddresses)
{
session.removeDestination(address, false);
}
for (SimpleString queueName: tempQueues)
{
session.deleteQueue(queueName);
}
}
finally
{
if (session != null)
{
session.close();
}
}
}
closed = true;
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
public ConnectionConsumer createConnectionConsumer(final Destination destination,
final String messageSelector,
final ServerSessionPool sessionPool,
final int maxMessages) throws JMSException
{
checkClosed();
return null;
}
public ConnectionConsumer createDurableConnectionConsumer(final Topic topic,
final String subscriptionName,
final String messageSelector,
final ServerSessionPool sessionPool,
final int maxMessages) throws JMSException
{
checkClosed();
// As spec. section 4.11
if (connectionType == TYPE_QUEUE_CONNECTION)
{
String msg = "Cannot create a durable connection consumer on a QueueConnection";
throw new javax.jms.IllegalStateException(msg);
}
//TODO
return null;
}
// QueueConnection implementation ---------------------------------------------------------------
public QueueSession createQueueSession(final boolean transacted,
final int acknowledgeMode) throws JMSException
{
checkClosed();
return createSessionInternal(transacted, acknowledgeMode, false,
JBossSession.TYPE_QUEUE_SESSION, false);
}
public ConnectionConsumer createConnectionConsumer(final Queue queue, final String messageSelector,
final ServerSessionPool sessionPool,
final int maxMessages) throws JMSException
{
checkClosed();
return null;
}
// TopicConnection implementation ---------------------------------------------------------------
public TopicSession createTopicSession(final boolean transacted,
final int acknowledgeMode) throws JMSException
{
checkClosed();
return createSessionInternal(transacted, acknowledgeMode, false,
JBossSession.TYPE_TOPIC_SESSION, false);
}
public ConnectionConsumer createConnectionConsumer(final Topic topic, final String messageSelector,
final ServerSessionPool sessionPool,
final int maxMessages) throws JMSException
{
checkClosed();
return null;
}
// XAConnection implementation ------------------------------------------------------------------
public XASession createXASession() throws JMSException
{
checkClosed();
return createSessionInternal(true, Session.SESSION_TRANSACTED, true,
JBossSession.TYPE_GENERIC_SESSION, false);
}
// XAQueueConnection implementation -------------------------------------------------------------
public XAQueueSession createXAQueueSession() throws JMSException
{
checkClosed();
return createSessionInternal(true, Session.SESSION_TRANSACTED, true,
JBossSession.TYPE_QUEUE_SESSION, false);
}
// XATopicConnection implementation -------------------------------------------------------------
public XATopicSession createXATopicSession() throws JMSException
{
checkClosed();
return createSessionInternal(true, Session.SESSION_TRANSACTED, true,
JBossSession.TYPE_TOPIC_SESSION, false);
}
// Public ---------------------------------------------------------------------------------------
public void addTemporaryAddress(final SimpleString tempAddress)
{
tempAddresses.add(tempAddress);
}
public void addTemporaryQueue(final SimpleString queueName)
{
tempQueues.add(queueName);
}
public void removeTemporaryAddress(final SimpleString tempAddress)
{
tempAddresses.remove(tempAddress);
}
public void removeTemporaryQueue(final SimpleString queueName)
{
tempQueues.remove(queueName);
}
public boolean hasNoLocal()
{
return hasNoLocal;
}
public void setHasNoLocal()
{
this.hasNoLocal = true;
}
public SimpleString getUID()
{
return uid;
}
// We provide some overloaded createSession methods to allow the value of cacheProducers to be specified
public Session createSession(final boolean transacted, final int acknowledgeMode,
final boolean cacheProducers) throws JMSException
{
return createSessionInternal(transacted, acknowledgeMode, false, TYPE_GENERIC_CONNECTION, cacheProducers);
}
public QueueSession createQueueSession(final boolean transacted,
final int acknowledgeMode, final boolean cacheProducers) throws JMSException
{
return createSessionInternal(transacted, acknowledgeMode, false,
JBossSession.TYPE_QUEUE_SESSION, cacheProducers);
}
public TopicSession createTopicSession(final boolean transacted,
final int acknowledgeMode, final boolean cacheProducers) throws JMSException
{
return createSessionInternal(transacted, acknowledgeMode, false,
JBossSession.TYPE_TOPIC_SESSION, cacheProducers);
}
public XASession createXASession(final boolean cacheProducers) throws JMSException
{
return createSessionInternal(true, Session.SESSION_TRANSACTED, true,
JBossSession.TYPE_GENERIC_SESSION, cacheProducers);
}
public XAQueueSession createXAQueueSession(final boolean cacheProducers) throws JMSException
{
return createSessionInternal(true, Session.SESSION_TRANSACTED, true,
JBossSession.TYPE_QUEUE_SESSION, cacheProducers);
}
public XATopicSession createXATopicSession(final boolean cacheProducers) throws JMSException
{
return createSessionInternal(true, Session.SESSION_TRANSACTED, true,
JBossSession.TYPE_TOPIC_SESSION, cacheProducers);
}
public void removeSession(final JBossSession session)
{
sessions.remove(session);
}
// Package protected ----------------------------------------------------------------------------
// Protected ------------------------------------------------------------------------------------
// In case the user forgets to close the connection manually
protected void finalize() throws Throwable
{
close();
}
protected JBossSession createSessionInternal(final boolean transacted, int acknowledgeMode,
final boolean isXA, final int type, final boolean cacheProducers) throws JMSException
{
if (transacted)
{
acknowledgeMode = Session.SESSION_TRANSACTED;
}
try
{
ClientSession session;
if (acknowledgeMode == Session.SESSION_TRANSACTED)
{
session =
sessionFactory.createSession(username, password, isXA, false, false, cacheProducers);
}
else if (acknowledgeMode == Session.AUTO_ACKNOWLEDGE)
{
session = sessionFactory.createSession(username, password, isXA, true, true, cacheProducers);
}
else if (acknowledgeMode == Session.DUPS_OK_ACKNOWLEDGE)
{
session = sessionFactory.createSession(username, password, isXA, true, true, cacheProducers);
}
else if (acknowledgeMode == Session.CLIENT_ACKNOWLEDGE)
{
session =
sessionFactory.createSession(username, password, isXA, true, false, cacheProducers);
}
else
{
throw new IllegalArgumentException("Invalid ackmode: " + acknowledgeMode);
}
justCreated = false;
//Setting multiple times on different sessions doesn't matter since RemotingConnection maintains
//a set (no duplicates)
session.addFailureListener(listener);
JBossSession jbs = new JBossSession(this, transacted, isXA, acknowledgeMode, session, type);
sessions.add(jbs);
if (started)
{
session.start();
}
return jbs;
}
catch (MessagingException e)
{
throw JMSExceptionHelper.convertFromMessagingException(e);
}
}
// Private --------------------------------------------------------------------------------------
private void checkClosed() throws JMSException
{
if (closed)
{
throw new IllegalStateException("Connection is closed");
}
}
// Inner classes --------------------------------------------------------------------------------
private class JMSFailureListener implements FailureListener
{
public void connectionFailed(final MessagingException me)
{
if (me == null)
{
return;
}
if (exceptionListener != null)
{
JMSException je = new JMSException(me.toString());
je.initCause(me);
exceptionListener.onException(je);
}
}
}
}