checkTransaction
method makes sure the adapter follows the JCA
- * autocommit contract, namely all statements executed outside a container managed transaction
- * or a component managed transaction should be autocommitted. To avoid continually calling
- * setAutocommit(enable) before and after container managed transactions, we keep track of the state
- * and check it before each transactional method call.
- */
- void checkTransaction() throws SQLException
- {
- synchronized (stateLock)
- {
- if (inManagedTransaction)
- return;
+ Throwable connectionError(Throwable t)
+ {
+ if (t instanceof SQLException)
+ {
+ boolean stale = mcf.isStaleConnection((SQLException) t);
- // Check autocommit
- if (jdbcAutoCommit != underlyingAutoCommit)
- {
- con.setAutoCommit(jdbcAutoCommit);
- underlyingAutoCommit = jdbcAutoCommit;
- }
- }
+ if (stale)
+ {
+ t = new StaleConnectionException((SQLException) t);
- if (jdbcAutoCommit == false && inLocalTransaction.getAndSet(true) == false)
- {
- ArrayList copy;
- synchronized (cels)
- {
- copy = new ArrayList(cels);
- }
- ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.LOCAL_TRANSACTION_STARTED);
- for (int i = 0; i < copy.size(); ++i)
- {
- ConnectionEventListener cel = (ConnectionEventListener) copy.get(i);
- try
- {
- cel.localTransactionStarted(ce);
- }
- catch (Throwable t)
- {
- getLog().trace("Error notifying of connection committed for listener: " + cel, t);
- }
- }
- }
+ }
+ else
+ {
+ boolean fatalException = mcf.isExceptionFatal((SQLException) t);
- checkState();
- }
+ if (fatalException)
+ {
+ broadcastConnectionError(t);
- protected void checkState() throws SQLException
- {
- synchronized (stateLock)
- {
- // Check readonly
- if (jdbcReadOnly != underlyingReadOnly)
- {
- con.setReadOnly(jdbcReadOnly);
- underlyingReadOnly = jdbcReadOnly;
- }
- }
- }
+ }
+ }
+ }
+ else
+ {
+ broadcastConnectionError(t);
+ }
- boolean isJdbcAutoCommit()
- {
- return inManagedTransaction ? false : jdbcAutoCommit;
- }
+ return t;
+ }
- void setJdbcAutoCommit(final boolean jdbcAutoCommit) throws SQLException
- {
- synchronized (stateLock)
- {
- if (inManagedTransaction)
- throw new SQLException("You cannot set autocommit during a managed transaction!");
- this.jdbcAutoCommit = jdbcAutoCommit;
- }
+ protected void broadcastConnectionError(Throwable e)
+ {
+ synchronized (stateLock)
+ {
+ if (destroyed)
+ {
+ Logger log = getLog();
+ if (log.isTraceEnabled())
+ log.trace("Not broadcasting error, already destroyed " + this, e);
+ return;
+ }
+ }
- if (jdbcAutoCommit && inLocalTransaction.getAndSet(false))
- {
- ArrayList copy;
- synchronized (cels)
- {
- copy = new ArrayList(cels);
- }
- ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.LOCAL_TRANSACTION_COMMITTED);
- for (int i = 0; i < copy.size(); ++i)
- {
- ConnectionEventListener cel = (ConnectionEventListener) copy.get(i);
- try
- {
- cel.localTransactionCommitted(ce);
- }
- catch (Throwable t)
- {
- getLog().trace("Error notifying of connection committed for listener: " + cel, t);
- }
- }
- }
- }
+ // We need to unlock() before sending the connection error to the
+ // event listeners. Otherwise the lock won't be in sync once
+ // cleanup() is called
+ unlock();
- boolean isJdbcReadOnly()
- {
- return jdbcReadOnly;
- }
+ Exception ex = null;
+ if (e instanceof Exception)
+ ex = (Exception) e;
+ else
+ ex = new ResourceAdapterInternalException("Unexpected error", e);
+ ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_ERROR_OCCURRED, ex);
+ Collection copy = null;
+ synchronized (cels)
+ {
+ copy = new ArrayList(cels);
+ }
+ for (Iterator i = copy.iterator(); i.hasNext();)
+ {
+ ConnectionEventListener cel = (ConnectionEventListener) i.next();
+ try
+ {
+ cel.connectionErrorOccurred(ce);
+ }
+ catch (Throwable t)
+ {
+ getLog().warn("Error notifying of connection error for listener: " + cel, t);
+ }
+ }
+ }
- void setJdbcReadOnly(final boolean readOnly) throws SQLException
- {
- synchronized (stateLock)
- {
- if (inManagedTransaction)
- throw new SQLException("You cannot set read only during a managed transaction!");
- this.jdbcReadOnly = readOnly;
- }
- }
+ Connection getConnection() throws SQLException
+ {
+ if (con == null)
+ throw new SQLException("Connection has been destroyed!!!");
+ return con;
+ }
- int getJdbcTransactionIsolation()
- {
- return jdbcTransactionIsolation;
- }
+ PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
+ {
+ if (psCache != null)
+ {
+ PreparedStatementCache.Key key = new PreparedStatementCache.Key(sql, PreparedStatementCache.Key.PREPARED_STATEMENT, resultSetType,
+ resultSetConcurrency);
+ CachedPreparedStatement cachedps = (CachedPreparedStatement) psCache.get(key);
+ if (cachedps != null)
+ {
+ if (canUse(cachedps))
+ cachedps.inUse();
+ else
+ return doPrepareStatement(sql, resultSetType, resultSetConcurrency);
+ }
+ else
+ {
+ PreparedStatement ps = doPrepareStatement(sql, resultSetType, resultSetConcurrency);
+ cachedps = wrappedConnectionFactory.createCachedPreparedStatement(ps);
+ psCache.insert(key, cachedps);
+ }
+ return cachedps;
+ }
+ else
+ return doPrepareStatement(sql, resultSetType, resultSetConcurrency);
+ }
- void setJdbcTransactionIsolation(final int isolationLevel) throws SQLException
- {
- synchronized (stateLock)
- {
- this.jdbcTransactionIsolation = isolationLevel;
- con.setTransactionIsolation(jdbcTransactionIsolation);
- }
- }
+ PreparedStatement doPrepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
+ {
+ return con.prepareStatement(sql, resultSetType, resultSetConcurrency);
+ }
- void jdbcCommit() throws SQLException
- {
- synchronized (stateLock)
- {
- if (inManagedTransaction)
- throw new SQLException("You cannot commit during a managed transaction!");
- if (jdbcAutoCommit)
- throw new SQLException("You cannot commit with autocommit set!");
- }
- con.commit();
+ CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
+ {
+ if (psCache != null)
+ {
+ PreparedStatementCache.Key key = new PreparedStatementCache.Key(sql, PreparedStatementCache.Key.CALLABLE_STATEMENT, resultSetType,
+ resultSetConcurrency);
+ CachedCallableStatement cachedps = (CachedCallableStatement) psCache.get(key);
+ if (cachedps != null)
+ {
+ if (canUse(cachedps))
+ cachedps.inUse();
+ else
+ return doPrepareCall(sql, resultSetType, resultSetConcurrency);
+ }
+ else
+ {
+ CallableStatement cs = doPrepareCall(sql, resultSetType, resultSetConcurrency);
+ cachedps = wrappedConnectionFactory.createCachedCallableStatement(cs);
+ psCache.insert(key, cachedps);
+ }
+ return cachedps;
+ }
+ else
+ return doPrepareCall(sql, resultSetType, resultSetConcurrency);
+ }
- if (inLocalTransaction.getAndSet(false))
- {
- ArrayList copy;
- synchronized (cels)
- {
- copy = new ArrayList(cels);
- }
- ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.LOCAL_TRANSACTION_COMMITTED);
- for (int i = 0; i < copy.size(); ++i)
- {
- ConnectionEventListener cel = (ConnectionEventListener) copy.get(i);
- try
- {
- cel.localTransactionCommitted(ce);
- }
- catch (Throwable t)
- {
- getLog().trace("Error notifying of connection committed for listener: " + cel, t);
- }
- }
- }
- }
+ CallableStatement doPrepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
+ {
+ return con.prepareCall(sql, resultSetType, resultSetConcurrency);
+ }
- void jdbcRollback() throws SQLException
- {
- synchronized (stateLock)
- {
- if (inManagedTransaction)
- throw new SQLException("You cannot rollback during a managed transaction!");
- if (jdbcAutoCommit)
- throw new SQLException("You cannot rollback with autocommit set!");
- }
- con.rollback();
+ boolean canUse(CachedPreparedStatement cachedps)
+ {
+ // Nobody is using it so we are ok
+ if (cachedps.isInUse() == false)
+ return true;
- if (inLocalTransaction.getAndSet(false))
- {
- ArrayList copy;
- synchronized (cels)
- {
- copy = new ArrayList(cels);
- }
- ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK);
- for (int i = 0; i < copy.size(); ++i)
- {
- ConnectionEventListener cel = (ConnectionEventListener) copy.get(i);
- try
- {
- cel.localTransactionRolledback(ce);
- }
- catch (Throwable t)
- {
- getLog().trace("Error notifying of connection rollback for listener: " + cel, t);
- }
- }
- }
- }
+ // Cannot reuse prepared statements in auto commit mode
+ // if will close the previous usage of the PS
+ if (underlyingAutoCommit == true)
+ return false;
- void jdbcRollback(Savepoint savepoint) throws SQLException
- {
- synchronized (stateLock)
- {
- if (inManagedTransaction)
- throw new SQLException("You cannot rollback during a managed transaction!");
- if (jdbcAutoCommit)
- throw new SQLException("You cannot rollback with autocommit set!");
- }
- con.rollback(savepoint);
- }
+ // We have been told not to share
+ return mcf.sharePS;
+ }
- int getTrackStatements()
- {
- return mcf.trackStatements;
- }
+ protected Logger getLog()
+ {
+ return mcf.log;
+ }
- boolean isTransactionQueryTimeout()
- {
- return mcf.isTransactionQueryTimeout;
- }
+ private void checkIdentity(Subject subject, ConnectionRequestInfo cri) throws ResourceException
+ {
+ Properties newProps = mcf.getConnectionProperties(subject, cri);
+ if (!props.equals(newProps))
+ {
+ throw new JBossResourceException("Wrong credentials passed to getConnection!");
+ } // end of if ()
+ }
- int getQueryTimeout()
- {
- return mcf.getQueryTimeout();
- }
+ /**
+ * The checkTransaction
method makes sure the adapter follows
+ * the JCA autocommit contract, namely all statements executed outside a
+ * container managed transaction or a component managed transaction should
+ * be autocommitted. To avoid continually calling setAutocommit(enable)
+ * before and after container managed transactions, we keep track of the
+ * state and check it before each transactional method call.
+ */
+ void checkTransaction() throws SQLException
+ {
+ synchronized (stateLock)
+ {
+ if (inManagedTransaction)
+ return;
- protected void checkException(SQLException e) throws ResourceException
- {
- connectionError(e);
- throw new JBossResourceException("SQLException", e);
- }
-
- public Object listConnectionStats()
- {
- if(psCache != null)
- return psCache.toString();
- else
- return "-1"; //-1 indicates NoCache
- }
+ // Check autocommit
+ if (jdbcAutoCommit != underlyingAutoCommit)
+ {
+ con.setAutoCommit(jdbcAutoCommit);
+ underlyingAutoCommit = jdbcAutoCommit;
+ }
+ }
+
+ if (jdbcAutoCommit == false && inLocalTransaction.getAndSet(true) == false)
+ {
+ ArrayList copy;
+ synchronized (cels)
+ {
+ copy = new ArrayList(cels);
+ }
+ ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.LOCAL_TRANSACTION_STARTED);
+ for (int i = 0; i < copy.size(); ++i)
+ {
+ ConnectionEventListener cel = (ConnectionEventListener) copy.get(i);
+ try
+ {
+ cel.localTransactionStarted(ce);
+ }
+ catch (Throwable t)
+ {
+ getLog().trace("Error notifying of connection committed for listener: " + cel, t);
+ }
+ }
+ }
+
+ checkState();
+ }
+
+ protected void checkState() throws SQLException
+ {
+ synchronized (stateLock)
+ {
+ // Check readonly
+ if (jdbcReadOnly != underlyingReadOnly)
+ {
+ con.setReadOnly(jdbcReadOnly);
+ underlyingReadOnly = jdbcReadOnly;
+ }
+ }
+ }
+
+ boolean isJdbcAutoCommit()
+ {
+ return inManagedTransaction ? false : jdbcAutoCommit;
+ }
+
+ void setJdbcAutoCommit(final boolean jdbcAutoCommit) throws SQLException
+ {
+ synchronized (stateLock)
+ {
+ if (inManagedTransaction)
+ throw new SQLException("You cannot set autocommit during a managed transaction!");
+ this.jdbcAutoCommit = jdbcAutoCommit;
+ }
+
+ if (jdbcAutoCommit && inLocalTransaction.getAndSet(false))
+ {
+ ArrayList copy;
+ synchronized (cels)
+ {
+ copy = new ArrayList(cels);
+ }
+ ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.LOCAL_TRANSACTION_COMMITTED);
+ for (int i = 0; i < copy.size(); ++i)
+ {
+ ConnectionEventListener cel = (ConnectionEventListener) copy.get(i);
+ try
+ {
+ cel.localTransactionCommitted(ce);
+ }
+ catch (Throwable t)
+ {
+ getLog().trace("Error notifying of connection committed for listener: " + cel, t);
+ }
+ }
+ }
+ }
+
+ boolean isJdbcReadOnly()
+ {
+ return jdbcReadOnly;
+ }
+
+ void setJdbcReadOnly(final boolean readOnly) throws SQLException
+ {
+ synchronized (stateLock)
+ {
+ if (inManagedTransaction)
+ throw new SQLException("You cannot set read only during a managed transaction!");
+ this.jdbcReadOnly = readOnly;
+ }
+ }
+
+ int getJdbcTransactionIsolation()
+ {
+ return jdbcTransactionIsolation;
+ }
+
+ void setJdbcTransactionIsolation(final int isolationLevel) throws SQLException
+ {
+ synchronized (stateLock)
+ {
+ this.jdbcTransactionIsolation = isolationLevel;
+ con.setTransactionIsolation(jdbcTransactionIsolation);
+ }
+ }
+
+ void jdbcCommit() throws SQLException
+ {
+ synchronized (stateLock)
+ {
+ if (inManagedTransaction)
+ throw new SQLException("You cannot commit during a managed transaction!");
+ if (jdbcAutoCommit)
+ throw new SQLException("You cannot commit with autocommit set!");
+ }
+ con.commit();
+
+ if (inLocalTransaction.getAndSet(false))
+ {
+ ArrayList copy;
+ synchronized (cels)
+ {
+ copy = new ArrayList(cels);
+ }
+ ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.LOCAL_TRANSACTION_COMMITTED);
+ for (int i = 0; i < copy.size(); ++i)
+ {
+ ConnectionEventListener cel = (ConnectionEventListener) copy.get(i);
+ try
+ {
+ cel.localTransactionCommitted(ce);
+ }
+ catch (Throwable t)
+ {
+ getLog().trace("Error notifying of connection committed for listener: " + cel, t);
+ }
+ }
+ }
+ }
+
+ void jdbcRollback() throws SQLException
+ {
+ synchronized (stateLock)
+ {
+ if (inManagedTransaction)
+ throw new SQLException("You cannot rollback during a managed transaction!");
+ if (jdbcAutoCommit)
+ throw new SQLException("You cannot rollback with autocommit set!");
+ }
+ con.rollback();
+
+ if (inLocalTransaction.getAndSet(false))
+ {
+ ArrayList copy;
+ synchronized (cels)
+ {
+ copy = new ArrayList(cels);
+ }
+ ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.LOCAL_TRANSACTION_ROLLEDBACK);
+ for (int i = 0; i < copy.size(); ++i)
+ {
+ ConnectionEventListener cel = (ConnectionEventListener) copy.get(i);
+ try
+ {
+ cel.localTransactionRolledback(ce);
+ }
+ catch (Throwable t)
+ {
+ getLog().trace("Error notifying of connection rollback for listener: " + cel, t);
+ }
+ }
+ }
+ }
+
+ void jdbcRollback(Savepoint savepoint) throws SQLException
+ {
+ synchronized (stateLock)
+ {
+ if (inManagedTransaction)
+ throw new SQLException("You cannot rollback during a managed transaction!");
+ if (jdbcAutoCommit)
+ throw new SQLException("You cannot rollback with autocommit set!");
+ }
+ con.rollback(savepoint);
+ }
+
+ int getTrackStatements()
+ {
+ return mcf.trackStatements;
+ }
+
+ boolean isTransactionQueryTimeout()
+ {
+ return mcf.isTransactionQueryTimeout;
+ }
+
+ int getQueryTimeout()
+ {
+ return mcf.getQueryTimeout();
+ }
+
+ protected void checkException(SQLException e) throws ResourceException
+ {
+ connectionError(e);
+ throw new JBossResourceException("SQLException", e);
+ }
+
+ public Object listConnectionStats()
+ {
+ if (psCache != null)
+ return psCache.toString();
+ else
+ return "-1"; // -1 indicates NoCache
+ }
}
Added: branches/JBPAPP_5_0_1_GA_JBPAPP-11214/connector/src/main/org/jboss/resource/adapter/jdbc/ReentrantLock.java
===================================================================
--- branches/JBPAPP_5_0_1_GA_JBPAPP-11214/connector/src/main/org/jboss/resource/adapter/jdbc/ReentrantLock.java (rev 0)
+++ branches/JBPAPP_5_0_1_GA_JBPAPP-11214/connector/src/main/org/jboss/resource/adapter/jdbc/ReentrantLock.java 2015-02-13 05:36:48 UTC (rev 114834)
@@ -0,0 +1,59 @@
+/*
+ * 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.jdbc;
+
+import java.util.Collection;
+
+/**
+ * ReentrantLock override
+ * @author Jesper Pedersen
+ */
+public class ReentrantLock extends java.util.concurrent.locks.ReentrantLock
+{
+ /** Serial version uid */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor
+ * @param fair Fair locking
+ */
+ public ReentrantLock(boolean fair)
+ {
+ super(fair);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Thread getOwner()
+ {
+ return super.getOwner();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public CollectionEvery ManagedConnection will have a physical JMSConnection under the - * hood. This may leave out several session, as specifyed in 5.5.4 Multiple - * Connection Handles. Thread safe semantics is provided - * - *
Hm. If we are to follow the example in 6.11 this will not work. We would - * have to use the SAME session. This means we will have to guard against - * concurrent access. We use a stack, and only allowes the handle at the - * top of the stack to do things. - * - *
As to transactions we some fairly hairy alternatives to handle: - * XA - we get an XA. We may now only do transaction through the - * XAResource, since a XASession MUST throw exceptions in commit etc. But - * since XA support implies LocatTransaction support, we will have to use - * the XAResource in the LocalTransaction class. - * LocalTx - we get a normal session. The LocalTransaction will then work - * against the normal session api. - * - *
An invokation of JMS MAY BE DONE in none transacted context. What do we - * do then? How much should we leave to the user??? - * - *
One possible solution is to use transactions any way, but under the hood. - * If not LocalTransaction or XA has been aquired by the container, we have - * to do the commit in send and publish. (CHECK is the container required - * to get a XA every time it uses a managed connection? No its is not, only - * at creation!) - * - *
Does this mean that a session one time may be used in a transacted env, - * and another time in a not transacted. - * - *
Maybe we could have this simple rule: - * - *
If a user is going to use non trans: + * + *
+ * Every ManagedConnection will have a physical JMSConnection under the hood. + * This may leave out several session, as specifyed in 5.5.4 Multiple Connection + * Handles. Thread safe semantics is provided + * + *
+ * Hm. If we are to follow the example in 6.11 this will not work. We would have + * to use the SAME session. This means we will have to guard against concurrent + * access. We use a stack, and only allowes the handle at the top of the stack + * to do things. + * + *
+ * As to transactions we some fairly hairy alternatives to handle: XA - we get + * an XA. We may now only do transaction through the XAResource, since a + * XASession MUST throw exceptions in commit etc. But since XA support implies + * LocatTransaction support, we will have to use the XAResource in the + * LocalTransaction class. LocalTx - we get a normal session. The + * LocalTransaction will then work against the normal session api. + * + *
+ * An invokation of JMS MAY BE DONE in none transacted context. What do we do + * then? How much should we leave to the user??? + * + *
+ * One possible solution is to use transactions any way, but under the hood. If + * not LocalTransaction or XA has been aquired by the container, we have to do + * the commit in send and publish. (CHECK is the container required to get a XA + * every time it uses a managed connection? No its is not, only at creation!) + * + *
+ * Does this mean that a session one time may be used in a transacted env, and + * another time in a not transacted. + * + *
+ * Maybe we could have this simple rule: + * + *
+ * If a user is going to use non trans: *
From the JMS tutorial: - * "When you create a session in an enterprise bean, the container ignores - * the arguments you specify, because it manages all transactional - * properties for enterprise beans." - * - *
And further: - * "You do not specify a message acknowledgment mode when you create a - * message-driven bean that uses container-managed transactions. The - * container handles acknowledgment automatically." - * - *
On Session or Connection: - *
From Tutorial: - * "A JMS API resource is a JMS API connection or a JMS API session." But in - * the J2EE spec only connection is considered a resource. - * - *
Not resolved: connectionErrorOccurred: it is verry hard to know from the - * exceptions thrown if it is a connection error. Should we register an - * ExceptionListener and mark al handles as errounous? And then let them - * send the event and throw an exception? - * + * + *
+ * From the JMS tutorial: "When you create a session in an enterprise bean, the + * container ignores the arguments you specify, because it manages all + * transactional properties for enterprise beans." + * + *
+ * And further: "You do not specify a message acknowledgment mode when you + * create a message-driven bean that uses container-managed transactions. The + * container handles acknowledgment automatically." + * + *
+ * On Session or Connection: + *
+ * From Tutorial: + * "A JMS API resource is a JMS API connection or a JMS API session." But in the + * J2EE spec only connection is considered a resource. + * + *
+ * Not resolved: connectionErrorOccurred: it is verry hard to know from the + * exceptions thrown if it is a connection error. Should we register an + * ExceptionListener and mark al handles as errounous? And then let them send + * the event and throw an exception? + * * @author Peter Antman. * @author Jason Dillon * @author Adrian Brock * @version $Revision$ */ -public class JmsManagedConnection - implements ManagedConnection, ExceptionListener +public class JmsManagedConnection implements ManagedConnection, ExceptionListener { - private static final Logger log = Logger.getLogger(JmsManagedConnection.class); + private static final Logger log = Logger.getLogger(JmsManagedConnection.class); - private JmsManagedConnectionFactory mcf; - private JmsConnectionRequestInfo info; - private String user; - private String pwd; - private boolean isDestroyed; + private JmsManagedConnectionFactory mcf; + private JmsConnectionRequestInfo info; + private String user; + private String pwd; + private boolean isDestroyed; - private ReentrantLock lock = new ReentrantLock(); - - // Physical JMS connection stuff - private Connection con; - private Session session; - private TopicSession topicSession; - private QueueSession queueSession; - private XASession xaSession; - private XATopicSession xaTopicSession; - private XAQueueSession xaQueueSession; - private XAResource xaResource; - private boolean xaTransacted; + private ReentrantLock lock = new ReentrantLock(true); - /** Holds all current JmsSession handles. */ - private Set handles = Collections.synchronizedSet(new HashSet()); + // Physical JMS connection stuff + private Connection con; + private Session session; + private TopicSession topicSession; + private QueueSession queueSession; + private XASession xaSession; + private XATopicSession xaTopicSession; + private XAQueueSession xaQueueSession; + private XAResource xaResource; + private boolean xaTransacted; - /** The event listeners */ - private Vector listeners = new Vector(); + /** Holds all current JmsSession handles. */ + private Set handles = Collections.synchronizedSet(new HashSet()); - /** - * Create a JmsManagedConnection. - * - * @param mcf - * @param info - * @param user - * @param pwd - * - * @throws ResourceException - */ - public JmsManagedConnection(final JmsManagedConnectionFactory mcf, - final ConnectionRequestInfo info, - final String user, - final String pwd) - throws ResourceException - { - this.mcf = mcf; + /** The event listeners */ + private Vector listeners = new Vector(); - // seem like its asking for trouble here - this.info = (JmsConnectionRequestInfo)info; - this.user = user; - this.pwd = pwd; + /** + * Create a JmsManagedConnection. + * + * @param mcf + * @param info + * @param user + * @param pwd + * + * @throws ResourceException + */ + public JmsManagedConnection(final JmsManagedConnectionFactory mcf, final ConnectionRequestInfo info, final String user, final String pwd) + throws ResourceException + { + this.mcf = mcf; - try - { - setup(); - } - catch (Throwable t) - { - try - { - destroy(); - } - catch (Throwable ignored) - { - } - JBossResourceException.rethrowAsResourceException("Error during setup", t); - } - } + // seem like its asking for trouble here + this.info = (JmsConnectionRequestInfo) info; + this.user = user; + this.pwd = pwd; - //---- ManagedConnection API ---- + try + { + setup(); + } + catch (Throwable t) + { + try + { + destroy(); + } + catch (Throwable ignored) + { + } + JBossResourceException.rethrowAsResourceException("Error during setup", t); + } + } - /** - * Get the physical connection handler. - * - *
This bummer will be called in two situations: - *
It may also be called multiple time without a cleanup, to support - * connection sharing. - * - * @param subject - * @param info - * @return A new connection object. - * - * @throws ResourceException - */ - public Object getConnection(final Subject subject, - final ConnectionRequestInfo info) - throws ResourceException - { - // Check user first - JmsCred cred = JmsCred.getJmsCred(mcf,subject,info); + // ---- ManagedConnection API ---- - // Null users are allowed! - if (user != null && !user.equals(cred.name)) - throw new SecurityException - ("Password credentials not the same, reauthentication not allowed"); - if (cred.name != null && user == null) { - throw new SecurityException - ("Password credentials not the same, reauthentication not allowed"); - } + /** + * Get the physical connection handler. + * + *
+ * This bummer will be called in two situations: + *
+ * It may also be called multiple time without a cleanup, to support
+ * connection sharing.
+ *
+ * @param subject
+ * @param info
+ * @return A new connection object.
+ *
+ * @throws ResourceException
+ */
+ public Object getConnection(final Subject subject, final ConnectionRequestInfo info) throws ResourceException
+ {
+ // Check user first
+ JmsCred cred = JmsCred.getJmsCred(mcf, subject, info);
- user = cred.name; // Basically meaningless
+ // Null users are allowed!
+ if (user != null && !user.equals(cred.name))
+ throw new SecurityException("Password credentials not the same, reauthentication not allowed");
+ if (cred.name != null && user == null)
+ {
+ throw new SecurityException("Password credentials not the same, reauthentication not allowed");
+ }
- if (isDestroyed)
- throw new IllegalStateException("ManagedConnection already destroyd");
+ user = cred.name; // Basically meaningless
- // Create a handle
- JmsSession handle = new JmsSession(this, (JmsConnectionRequestInfo) info);
- handles.add(handle);
- return handle;
- }
+ if (isDestroyed)
+ throw new IllegalStateException("ManagedConnection already destroyd");
- /**
- * Destroy all handles.
- *
- * @throws ResourceException Failed to close one or more handles.
- */
- private void destroyHandles() throws ResourceException
- {
- try
- {
- if (con != null)
- con.stop();
- }
- catch (Throwable t)
- {
- log.trace("Ignored error stopping connection", t);
- }
-
- Iterator iter = handles.iterator();
- while (iter.hasNext())
- ((JmsSession)iter.next()).destroy();
+ // Create a handle
+ JmsSession handle = new JmsSession(this, (JmsConnectionRequestInfo) info);
+ handles.add(handle);
+ return handle;
+ }
- // clear the handles map
- handles.clear();
- }
+ /**
+ * Destroy all handles.
+ *
+ * @throws ResourceException
+ * Failed to close one or more handles.
+ */
+ private void destroyHandles() throws ResourceException
+ {
+ try
+ {
+ if (con != null)
+ con.stop();
+ }
+ catch (Throwable t)
+ {
+ log.trace("Ignored error stopping connection", t);
+ }
- /**
- * Destroy the physical connection.
- *
- * @throws ResourceException Could not property close the session and
- * connection.
- */
- public void destroy() throws ResourceException
- {
- if (isDestroyed || con == null) return;
+ Iterator iter = handles.iterator();
+ while (iter.hasNext())
+ ((JmsSession) iter.next()).destroy();
- isDestroyed = true;
+ // clear the handles map
+ handles.clear();
+ }
- try
- {
- con.setExceptionListener(null);
- }
- catch (JMSException e)
- {
- log.debug("Error unsetting the exception listener " + this, e);
- }
-
- // destory handles
- destroyHandles();
-
- try
- {
- // Close session and connection
- try
- {
- if (info.getType() == JmsConnectionFactory.TOPIC)
- {
- if (topicSession != null)
- topicSession.close();
- if (xaTransacted && xaTopicSession != null) {
- xaTopicSession.close();
- }
- }
- else if (info.getType() == JmsConnectionFactory.QUEUE)
- {
- if (queueSession != null)
- queueSession.close();
- if (xaTransacted && xaQueueSession != null)
- xaQueueSession.close();
- }
- else
- {
- if (session != null)
- session.close();
- if (xaTransacted && xaSession != null)
- xaSession.close();
- }
- }
- catch (JMSException e)
- {
- log.debug("Error closing session " +this, e);
- }
- con.close();
- }
- catch (Throwable e)
- {
- throw new JBossResourceException
- ("Could not properly close the session and connection", e);
- }
- }
+ /**
+ * Destroy the physical connection.
+ *
+ * @throws ResourceException
+ * Could not property close the session and connection.
+ */
+ public void destroy() throws ResourceException
+ {
+ if (isDestroyed || con == null)
+ return;
- /**
- * Cleans up the, from the spec
- * - The cleanup of ManagedConnection instance resets its client specific
- * state.
- *
- * Does that mean that autentication should be redone. FIXME
- */
- public void cleanup() throws ResourceException
- {
- if (isDestroyed)
- throw new IllegalStateException("ManagedConnection already destroyed");
+ isDestroyed = true;
- // destory handles
- destroyHandles();
+ try
+ {
+ con.setExceptionListener(null);
+ }
+ catch (JMSException e)
+ {
+ log.debug("Error unsetting the exception listener " + this, e);
+ }
- // I'm recreating the lock object when we return to the pool
- // because it looks too nasty to expect the connection handle
- // to unlock properly in certain race conditions
- // where the dissociation of the managed connection is "random".
- lock = new ReentrantLock();
- }
+ // destory handles
+ destroyHandles();
- /**
- * Move a handler from one mc to this one.
- *
- * @param obj An object of type JmsSession.
- *
- * @throws ResourceException Failed to associate connection.
- * @throws IllegalStateException ManagedConnection in an illegal state.
- */
- public void associateConnection(final Object obj)
- throws ResourceException
- {
- //
- // Should we check auth, ie user and pwd? FIXME
- //
+ try
+ {
+ // Close session and connection
+ try
+ {
+ if (info.getType() == JmsConnectionFactory.TOPIC)
+ {
+ if (topicSession != null)
+ topicSession.close();
+ if (xaTransacted && xaTopicSession != null)
+ {
+ xaTopicSession.close();
+ }
+ }
+ else if (info.getType() == JmsConnectionFactory.QUEUE)
+ {
+ if (queueSession != null)
+ queueSession.close();
+ if (xaTransacted && xaQueueSession != null)
+ xaQueueSession.close();
+ }
+ else
+ {
+ if (session != null)
+ session.close();
+ if (xaTransacted && xaSession != null)
+ xaSession.close();
+ }
+ }
+ catch (JMSException e)
+ {
+ log.debug("Error closing session " + this, e);
+ }
+ con.close();
+ }
+ catch (Throwable e)
+ {
+ throw new JBossResourceException("Could not properly close the session and connection", e);
+ }
+ }
- if (!isDestroyed && obj instanceof JmsSession)
- {
- JmsSession h = (JmsSession)obj;
- h.setManagedConnection(this);
- handles.add(h);
- }
- else
- throw new IllegalStateException
- ("ManagedConnection in an illegal state");
- }
+ /**
+ * Cleans up the, from the spec - The cleanup of ManagedConnection instance
+ * resets its client specific state.
+ *
+ * Does that mean that autentication should be redone. FIXME
+ */
+ public void cleanup() throws ResourceException
+ {
+ if (isDestroyed)
+ throw new IllegalStateException("ManagedConnection already destroyed");
- protected void lock()
- {
- lock.lock();
- }
+ // destory handles
+ destroyHandles();
- protected void tryLock() throws JMSException
- {
- int tryLock = mcf.getUseTryLock();
- if (tryLock <= 0)
- {
- lock();
- return;
- }
- try
- {
- if (lock.tryLock(tryLock, TimeUnit.SECONDS) == false)
- throw new ResourceAllocationException("Unable to obtain lock in " + tryLock + " seconds: " + this);
- }
- catch (InterruptedException e)
- {
- throw new ResourceAllocationException("Interrupted attempting lock: " + this);
- }
- }
-
- protected void unlock()
- {
- lock.unlock();
- }
+ boolean isActive = false;
- /**
- * Add a connection event listener.
- *
- * @param l The connection event listener to be added.
- */
- public void addConnectionEventListener(final ConnectionEventListener l)
- {
- listeners.addElement(l);
+ if (lock.hasQueuedThreads())
+ {
+ Collection