/*
* JBoss, Home of Professional Open Source.
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file 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.resource.adapter.jms;
import org.jboss.logging.Logger;
import javax.jms.*;
import javax.jms.IllegalStateException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.resource.Referenceable;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ManagedConnectionFactory;
import javax.transaction.Status;
import javax.transaction.TransactionSynchronizationRegistry;
import java.util.HashSet;
import java.util.Iterator;
//import org.jboss.resource.connectionmanager.JTATransactionChecker;
/**
* Implements the JMS Connection API and produces {@link JmsSession} objects.
*
* @author <a href="mailto:peter.antman@tim.se">Peter Antman</a>.
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
* @author <a href="mailto:adrian@jboss.com">Adrian Brock</a>
*/
public class JmsSessionFactoryImpl implements JmsSessionFactory, Referenceable {
private static final Logger log = Logger.getLogger(JmsSessionFactoryImpl.class);
/**
* Are we closed?
*/
private boolean closed = false;
/**
* Whether trace is enabled
*/
private boolean trace = log.isTraceEnabled();
private Reference reference;
// Used from JmsConnectionFactory
private String userName;
private String password;
private String clientID;
private int type;
/* Whether we are started */
private boolean started = false;
/**
* JmsRa own factory
*/
private JmsManagedConnectionFactory mcf;
/**
* Hook to the appserver
*/
private ConnectionManager cm;
/**
* The sessions
*/
private HashSet sessions = new HashSet();
/**
* The temporary queues
*/
private HashSet tempQueues = new HashSet();
/**
* The temporary topics
*/
private HashSet tempTopics = new HashSet();
public JmsSessionFactoryImpl(final ManagedConnectionFactory mcf,
final ConnectionManager cm,
final int type) {
this.mcf = (JmsManagedConnectionFactory) mcf;
this.cm = cm;
if (cm == null)
// This is standalone usage, no appserver
this.cm = new JmsConnectionManager();
else
this.cm = cm;
this.type = type;
if (trace)
log.trace("mcf=" + mcf + ", cm=" + cm + ", type=" + type);
}
public void setReference(final Reference reference) {
this.reference = reference;
}
public Reference getReference() {
return reference;
}
// --- API for JmsConnectionFactoryImpl
public void setUserName(final String name) {
userName = name;
}
public void setPassword(final String password) {
this.password = password;
}
//---- QueueConnection ---
public QueueSession createQueueSession(final boolean transacted,
final int acknowledgeMode)
throws JMSException {
checkClosed();
if (type == JmsConnectionFactory.TOPIC)
throw new IllegalStateException("Can not get a queue session from a topic connection");
return allocateConnection(transacted, acknowledgeMode, type);
}
public ConnectionConsumer createConnectionConsumer
(Queue queue,
String messageSelector,
ServerSessionPool sessionPool,
int maxMessages)
throws JMSException {
throw new IllegalStateException(ISE);
}
//--- TopicConnection ---
public TopicSession createTopicSession(final boolean transacted,
final int acknowledgeMode)
throws JMSException {
checkClosed();
if (type == JmsConnectionFactory.QUEUE)
throw new IllegalStateException("Can not get a topic session from a queue connection");
return allocateConnection(transacted, acknowledgeMode, type);
}
public ConnectionConsumer createConnectionConsumer
(Topic topic,
String messageSelector,
ServerSessionPool sessionPool,
int maxMessages)
throws JMSException {
throw new IllegalStateException(ISE);
}
public ConnectionConsumer createDurableConnectionConsumer(
Topic topic,
String subscriptionName,
String messageSelector,
ServerSessionPool sessionPool,
int maxMessages)
throws JMSException {
throw new IllegalStateException(ISE);
}
//--- All the Connection methods
public String getClientID() throws JMSException {
checkClosed();
if (clientID == null)
return mcf.getClientID();
return clientID;
}
public void setClientID(String cID) throws JMSException {
if (mcf.isStrict())
throw new IllegalStateException(ISE);
checkClosed();
if (clientID != null)
throw new IllegalStateException("Cannot change client id");
clientID = cID;
}
public ConnectionMetaData getMetaData() throws JMSException {
checkClosed();
return mcf.getMetaData();
}
public ExceptionListener getExceptionListener() throws JMSException {
throw new IllegalStateException(ISE);
}
public void setExceptionListener(ExceptionListener listener)
throws JMSException {
throw new IllegalStateException(ISE);
}
public void start() throws JMSException {
checkClosed();
if (trace)
log.trace("start() " + this);
synchronized (sessions) {
if (started)
return;
started = true;
for (Iterator i = sessions.iterator(); i.hasNext(); ) {
JmsSession session = (JmsSession) i.next();
session.start();
}
}
}
public void stop() throws JMSException {
if (mcf.isStrict())
throw new IllegalStateException(ISE);
checkClosed();
if (trace)
log.trace("stop() " + this);
synchronized (sessions) {
if (started == false)
return;
started = true;
for (Iterator i = sessions.iterator(); i.hasNext(); ) {
JmsSession session = (JmsSession) i.next();
session.stop();
}
}
}
public void close() throws JMSException {
if (closed)
return;
closed = true;
if (trace)
log.trace("close() " + this);
synchronized (sessions) {
for (Iterator i = sessions.iterator(); i.hasNext(); ) {
JmsSession session = (JmsSession) i.next();
try {
session.closeSession();
} catch (Throwable t) {
log.trace("Error closing session", t);
}
i.remove();
}
}
if (mcf.isDeleteTemporaryDestinations()) {
synchronized (tempQueues) {
for (Iterator i = tempQueues.iterator(); i.hasNext(); ) {
TemporaryQueue temp = (TemporaryQueue) i.next();
try {
if (trace)
log.trace("Closing temporary queue " + temp + " for " + this);
temp.delete();
} catch (Throwable t) {
log.trace("Error deleting temporary queue", t);
}
i.remove();
}
}
synchronized (tempTopics) {
for (Iterator i = tempTopics.iterator(); i.hasNext(); ) {
TemporaryTopic temp = (TemporaryTopic) i.next();
try {
if (trace)
log.trace("Closing temporary topic " + temp + " for " + this);
temp.delete();
} catch (Throwable t) {
log.trace("Error deleting temporary topic", t);
}
i.remove();
}
}
}
}
public void closeSession(JmsSession session) throws JMSException {
synchronized (sessions) {
sessions.remove(session);
}
}
public void addTemporaryQueue(TemporaryQueue temp) {
synchronized (tempQueues) {
tempQueues.add(temp);
}
}
public void addTemporaryTopic(TemporaryTopic temp) {
synchronized (tempTopics) {
tempTopics.add(temp);
}
}
// -- JMS 1.1
public ConnectionConsumer createConnectionConsumer(Destination destination, ServerSessionPool pool, int maxMessages) throws JMSException {
throw new IllegalStateException(ISE);
}
public ConnectionConsumer createConnectionConsumer(Destination destination, String name, ServerSessionPool pool, int maxMessages) throws JMSException {
throw new IllegalStateException(ISE);
}
public Session createSession(boolean transacted, int acknowledgeMode)
throws JMSException {
checkClosed();
return allocateConnection(transacted, acknowledgeMode, type);
}
protected JmsSession allocateConnection(boolean transacted, int acknowledgeMode, int sessionType) throws JMSException {
try {
synchronized (sessions) {
if (mcf.isStrict() && sessions.isEmpty() == false)
throw new IllegalStateException("Only allowed one session per connection. See the J2EE spec, e.g. J2EE1.4 Section 6.6");
if (isTransactionActive()) {
transacted = true;
acknowledgeMode = Session.SESSION_TRANSACTED;
}
JmsConnectionRequestInfo info = new JmsConnectionRequestInfo(transacted, acknowledgeMode, sessionType);
info.setUserName(userName);
info.setPassword(password);
info.setClientID(clientID);
info.setDefaults(mcf.getProperties());
if (trace)
log.trace("Allocating session for " + this + " with request info=" + info);
JmsSession session = (JmsSession) cm.allocateConnection(mcf, info);
try {
if (trace)
log.trace("Allocated " + this + " session=" + session);
session.setJmsSessionFactory(this);
if (started)
session.start();
sessions.add(session);
return session;
} catch (Throwable t) {
try {
session.close();
} catch (Throwable ignored) {
}
if (t instanceof Exception)
throw (Exception) t;
else
throw new RuntimeException("Unexpected error: ", t);
}
}
} catch (Exception e) {
log.error("could not create session", e);
JMSException je = new JMSException
("Could not create a session: " + e);
je.setLinkedException(e);
throw je;
}
}
protected void checkClosed() throws IllegalStateException {
if (closed)
throw new IllegalStateException("The connection is closed");
}
/**
* Check whether a transaction is active
*/
private boolean isTransactionActive() throws IllegalStateException {
if (cm == null)
throw new IllegalStateException("No connection manager");
InitialContext context = null;
try {
context = new InitialContext();
String name = mcf.getTransactionSynchronizationRegistryLookup();
if (name == null) {
// consider the transaction active if we are not able to look up
// the TransactionSynchronizationRegistry
return true;
}
TransactionSynchronizationRegistry registry = (TransactionSynchronizationRegistry) context.lookup(name);
return (registry.getTransactionStatus() == Status.STATUS_ACTIVE);
} catch (NamingException e) {
IllegalStateException ex = new IllegalStateException("Transaction not active");
ex.initCause(e);
ex.setLinkedException(e);
throw ex;
} finally {
if (context != null) {
try {
context.close();
} catch (NamingException e) {
}
}
}
}
}