diff --git a/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/JmsPooledConnection.java b/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/JmsPooledConnection.java index 8f4cfd2b35..27076515d6 100644 --- a/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/JmsPooledConnection.java +++ b/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/JmsPooledConnection.java @@ -35,7 +35,16 @@ import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; /** - * TODO Add Description + * Jms Pooled Connection. Tracks references to the connection to know when the + * connection can be released to the pool. Any exception will close pooled + * session instead of returning to the pool. The sessions are tracked in both + * active and available states. An available session can be reused by the next + * client. + * + * Synchronization Principle To prevent deadlocks: Chained sync blocks can only + * happen in a downward direction. A manager has a synchronized lock can make a + * call down to a wrapper, but not nice versa. Also a session inside a sync + * block can make a call down to a producer but not vice versa. * *
* @@ -44,8 +53,8 @@ import com.raytheon.uf.common.status.UFStatus.Priority; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Apr 15, 2011 rjpeter Initial creation - * Mar 08, 2012 194 njensen Improved safety of close() - * + * Mar 08, 2012 194 njensen Improved safety of close() + * Feb 21, 2013 1642 rjpeter Fix deadlock scenario ** * @author rjpeter @@ -57,35 +66,33 @@ public class JmsPooledConnection implements ExceptionListener { private final IUFStatusHandler statusHandler = UFStatus .getHandler(JmsPooledConnection.class); - private JmsPooledConnectionFactory connFactory = null; + private final JmsPooledConnectionFactory connFactory; - private Connection conn = null; + private volatile Connection conn = null; // keeps track of number of creates vs. closes to know when it can be // returned to the pool - List
* @@ -41,8 +48,8 @@ import com.raytheon.uf.common.status.UFStatus.Priority; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Apr 18, 2011 rjpeter Initial creation - * Mar 08, 2012 194 njensen Improved logging - * + * Mar 08, 2012 194 njensen Improved logging + * Feb 26, 2013 1642 rjpeter Removed lazy initialization ** * @author rjpeter @@ -55,34 +62,31 @@ public class JmsPooledConsumer { private final JmsPooledSession sess; - private final Destination destination; - - private final String messageSelector; - - private MessageConsumer consumer; + private final MessageConsumer consumer; private final String destKey; - private boolean exceptionOccurred = false; + private volatile boolean exceptionOccurred = false; - private Object stateLock = new Object(); + private final Object stateLock = new Object(); - private State state = State.InUse; + private volatile State state = State.InUse; /** * Technically a pooled consumer should only have 1 reference at a time. * Bullet proofing in case 3rd party ever tries to get multiple consumers to * the same destination. */ - private List
* @@ -48,8 +58,8 @@ import com.raytheon.uf.common.status.UFStatus.Priority; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Apr 15, 2011 rjpeter Initial creation - * Mar 08, 2012 194 njensen Improved logging - * + * Mar 08, 2012 194 njensen Improved logging + * Feb 21, 2013 1642 rjpeter Fix deadlock scenario ** * @author rjpeter @@ -65,27 +75,28 @@ public class JmsPooledSession { private final Session sess; + // The thread this session was most recently used by for tracking a pending + // session that is being reserved for a given thread. private String threadKey; - private boolean exceptionOccurred = false; + private volatile boolean exceptionOccurred = false; - private Throwable trappedExc = null; + private final Object stateLock = new Object(); - private Object stateLock = new Object(); - - private State state = State.InUse; + private volatile State state = State.InUse; // keeps track of number of creates vs. closes to know when it can be // returned to the pool - List
* diff --git a/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsConnectionWrapper.java b/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsConnectionWrapper.java index 754b753a2e..6a7de9ca0d 100644 --- a/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsConnectionWrapper.java +++ b/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsConnectionWrapper.java @@ -36,7 +36,9 @@ import javax.jms.Topic; import com.raytheon.uf.common.jms.JmsPooledConnection; /** - * TODO Add Description + * Wrapper class for jms connection pooling. Tracks wrapped sessions created + * from this wrapped connection to know when the connection can be returned to + * the pool. * ** @@ -45,7 +47,7 @@ import com.raytheon.uf.common.jms.JmsPooledConnection; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Apr 15, 2011 rjpeter Initial creation - * + * Feb 21, 2013 1642 rjpeter Added volatile references for better concurrency handling. ** * @author rjpeter @@ -53,18 +55,16 @@ import com.raytheon.uf.common.jms.JmsPooledConnection; */ public class JmsConnectionWrapper implements Connection { - private JmsPooledConnection mgr = null; + private final JmsPooledConnection mgr; - private boolean closed = false; + private volatile boolean closed = false; - private boolean exceptionOccurred = false; + private volatile boolean exceptionOccurred = false; - private Throwable trappedExc = null; - - private Listsessions = new ArrayList ( + private final List sessions = new ArrayList ( 1); - private String clientId = null; + private final String clientId = null; public JmsConnectionWrapper(JmsPooledConnection mgr) { this.mgr = mgr; @@ -76,20 +76,16 @@ public class JmsConnectionWrapper implements Connection { * * @return True if this wrapper hasn't been closed before, false otherwise. */ - public boolean closeInternal() { + public boolean closeWrapper() { synchronized (this) { if (!closed) { closed = true; - if (sessions != null) { - for (JmsSessionWrapper session : sessions) { - try { - session.close(); - } catch (JMSException e) { - - } + for (JmsSessionWrapper session : sessions) { + try { + session.close(); + } catch (JMSException e) { + // closing of wrapper doesn't throw an exception } - - sessions = null; } if (exceptionOccurred) { @@ -109,7 +105,7 @@ public class JmsConnectionWrapper implements Connection { */ @Override public void close() throws JMSException { - if (closeInternal()) { + if (closeWrapper()) { // remove this wrapper from the manager mgr.removeReference(this); @@ -169,9 +165,6 @@ public class JmsConnectionWrapper implements Connection { JmsSessionWrapper session = mgr.getSession(transacted, acknowledgeMode); if (session != null) { - if (sessions == null) { - sessions = new ArrayList (1); - } sessions.add(session); } else { throw new IllegalStateException("Underlying session is closed"); @@ -179,7 +172,6 @@ public class JmsConnectionWrapper implements Connection { return session; } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled connection"); exc.initCause(e); @@ -220,7 +212,6 @@ public class JmsConnectionWrapper implements Connection { return conn.getMetaData(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled connection"); exc.initCause(e); diff --git a/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsConsumerWrapper.java b/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsConsumerWrapper.java index 7aa960dd80..ec978fe7b8 100644 --- a/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsConsumerWrapper.java +++ b/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsConsumerWrapper.java @@ -28,7 +28,8 @@ import javax.jms.MessageListener; import com.raytheon.uf.common.jms.JmsPooledConsumer; /** - * TODO Add Description + * Wrapper class for jms consumer pooling. Helps track references to the pooled + * consumer to know when the consumer can be closed. * * * @@ -37,7 +38,7 @@ import com.raytheon.uf.common.jms.JmsPooledConsumer; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Apr 18, 2011 rjpeter Initial creation - * + * Feb 26, 2013 1642 rjpeter Added volatile references for better concurrency handling. ** * @author rjpeter @@ -45,11 +46,11 @@ import com.raytheon.uf.common.jms.JmsPooledConsumer; */ public class JmsConsumerWrapper implements MessageConsumer { - private JmsPooledConsumer mgr = null; + private final JmsPooledConsumer mgr; - private boolean exceptionOccurred = false; + private volatile boolean exceptionOccurred = false; - private boolean closed = false; + private volatile boolean closed = false; public JmsConsumerWrapper(JmsPooledConsumer mgr) { this.mgr = mgr; @@ -74,21 +75,20 @@ public class JmsConsumerWrapper implements MessageConsumer { * * @return True if this wrapper hasn't been closed before, false otherwise. */ - public boolean closeInternal() { - boolean close = false; - + public boolean closeWrapper() { synchronized (this) { if (!closed) { closed = true; - close = true; + + if (exceptionOccurred) { + mgr.setExceptionOccurred(true); + } + + return true; } } - if (close && exceptionOccurred) { - mgr.setExceptionOccurred(true); - } - - return close; + return false; } /* @@ -101,7 +101,7 @@ public class JmsConsumerWrapper implements MessageConsumer { */ @Override public void close() throws JMSException { - if (closeInternal()) { + if (closeWrapper()) { mgr.removeReference(this); if (exceptionOccurred) { diff --git a/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsProducerWrapper.java b/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsProducerWrapper.java index d2500fa400..1c9439d9c7 100644 --- a/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsProducerWrapper.java +++ b/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsProducerWrapper.java @@ -28,7 +28,8 @@ import javax.jms.MessageProducer; import com.raytheon.uf.common.jms.JmsPooledProducer; /** - * TODO Add Description + * Wrapper class for jms producer pooling. Helps track references to the pooled + * producer to know when the producer can be closed. * ** @@ -36,8 +37,8 @@ import com.raytheon.uf.common.jms.JmsPooledProducer; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Dec 8, 2011 rjpeter Initial creation - * + * Dec fi8, 2011 rjpeter Initial creation + * Feb 26, 2013 1642 rjpeter Added volatile references for better concurrency handling. ** * @author rjpeter @@ -45,11 +46,11 @@ import com.raytheon.uf.common.jms.JmsPooledProducer; */ public class JmsProducerWrapper implements MessageProducer { - private JmsPooledProducer mgr = null; + private final JmsPooledProducer mgr; - private boolean exceptionOccurred = false; + private volatile boolean exceptionOccurred = false; - private boolean closed = false; + private volatile boolean closed = false; public JmsProducerWrapper(JmsPooledProducer mgr) { this.mgr = mgr; @@ -61,21 +62,18 @@ public class JmsProducerWrapper implements MessageProducer { * * @return True if this wrapper hasn't been closed before, false otherwise. */ - public boolean closeInternal() { - boolean close = false; - + public boolean closeWrapper() { synchronized (this) { if (!closed) { closed = true; - close = true; + + if (exceptionOccurred) { + mgr.setExceptionOccurred(true); + } } } - if (close && exceptionOccurred) { - mgr.setExceptionOccurred(true); - } - - return close; + return false; } /* @@ -88,7 +86,7 @@ public class JmsProducerWrapper implements MessageProducer { */ @Override public void close() throws JMSException { - if (closeInternal()) { + if (closeWrapper()) { mgr.removeReference(this); if (exceptionOccurred) { diff --git a/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsSessionWrapper.java b/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsSessionWrapper.java index 5ffe5f2295..4af46d7ccb 100644 --- a/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsSessionWrapper.java +++ b/edexOsgi/com.raytheon.uf.common.jms/src/com/raytheon/uf/common/jms/wrapper/JmsSessionWrapper.java @@ -46,7 +46,9 @@ import javax.jms.TopicSubscriber; import com.raytheon.uf.common.jms.JmsPooledSession; /** - * TODO Add Description + * Wrapper class for jms session pooling. Tracks wrapped consumers/producers + * created from this wrapped session to know when the session can be returned to + * the pool. * ** @@ -55,7 +57,7 @@ import com.raytheon.uf.common.jms.JmsPooledSession; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Nov 30, 2011 rjpeter Initial creation - * + * Feb 26, 2013 1642 rjpeter Added volatile references for better concurrency handling. ** * @author rjpeter @@ -63,20 +65,18 @@ import com.raytheon.uf.common.jms.JmsPooledSession; */ public class JmsSessionWrapper implements Session { - private JmsPooledSession mgr = null; + private final JmsPooledSession mgr; - private boolean closed = false; + private volatile boolean closed = false; - private boolean exceptionOccurred = false; + private volatile boolean exceptionOccurred = false; - private Throwable trappedExc = null; + private final Listproducers = new ArrayList ( + 1);; - private List producers = null; + private final List consumers = new ArrayList ( + 1); - private List consumers = null; - - // TODO: needs to track the wrappers opened by this wrapped session so when - // wrapped session is closed, all underlying wrappers are closed. public JmsSessionWrapper(JmsPooledSession mgr) { this.mgr = mgr; } @@ -87,32 +87,24 @@ public class JmsSessionWrapper implements Session { * * @return True if this wrapper hasn't been closed before, false otherwise. */ - public boolean closeInternal() { + public boolean closeWrapper() { synchronized (this) { if (!closed) { closed = true; - if (consumers != null) { - for (JmsConsumerWrapper consumer : consumers) { - try { - consumer.close(); - } catch (JMSException e) { + for (JmsConsumerWrapper consumer : consumers) { + try { + consumer.close(); + } catch (JMSException e) { - } } - - consumers = null; } - if (producers != null) { - for (JmsProducerWrapper producer : producers) { - try { - producer.close(); - } catch (JMSException e) { + for (JmsProducerWrapper producer : producers) { + try { + producer.close(); + } catch (JMSException e) { - } } - - producers = null; } if (exceptionOccurred) { @@ -132,7 +124,7 @@ public class JmsSessionWrapper implements Session { */ @Override public void close() throws JMSException { - if (closeInternal()) { + if (closeWrapper()) { // remove this wrapper from the manager mgr.removeReference(this); @@ -164,7 +156,6 @@ public class JmsSessionWrapper implements Session { sess.commit(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -185,7 +176,6 @@ public class JmsSessionWrapper implements Session { return sess.createBrowser(queue); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -207,7 +197,6 @@ public class JmsSessionWrapper implements Session { return sess.createBrowser(queue, messageSelector); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -228,7 +217,6 @@ public class JmsSessionWrapper implements Session { return sess.createBytesMessage(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -256,20 +244,24 @@ public class JmsSessionWrapper implements Session { @Override public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException { - JmsConsumerWrapper consumer = mgr.getConsumer(destination, - messageSelector); + try { + JmsConsumerWrapper consumer = mgr.getConsumer(destination, + messageSelector); - if (consumer != null) { - if (consumers == null) { - consumers = new ArrayList (1); + if (consumer != null) { + consumers.add(consumer); + } else { + throw new IllegalStateException("Underlying consumer is closed"); } - consumers.add(consumer); - } else { - throw new IllegalStateException("Underlying consumer is closed"); + return consumer; + } catch (Throwable e) { + exceptionOccurred = true; + JMSException exc = new JMSException( + "Exception occurred on pooled session"); + exc.initCause(e); + throw exc; } - - return consumer; } /* @@ -290,7 +282,6 @@ public class JmsSessionWrapper implements Session { noLocal); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -317,7 +308,6 @@ public class JmsSessionWrapper implements Session { return sess.createDurableSubscriber(topic, name); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -342,7 +332,6 @@ public class JmsSessionWrapper implements Session { noLocal); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -363,7 +352,6 @@ public class JmsSessionWrapper implements Session { return sess.createMapMessage(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -384,7 +372,6 @@ public class JmsSessionWrapper implements Session { return sess.createMessage(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -405,7 +392,6 @@ public class JmsSessionWrapper implements Session { return sess.createObjectMessage(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -427,7 +413,6 @@ public class JmsSessionWrapper implements Session { return sess.createObjectMessage(obj); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -443,19 +428,23 @@ public class JmsSessionWrapper implements Session { @Override public MessageProducer createProducer(Destination destination) throws JMSException { - JmsProducerWrapper producer = mgr.getProducer(destination); + try { + JmsProducerWrapper producer = mgr.getProducer(destination); - if (producer != null) { - if (producers == null) { - producers = new ArrayList (1); + if (producer != null) { + producers.add(producer); + } else { + throw new IllegalStateException("Underlying producer is closed"); } - producers.add(producer); - } else { - throw new IllegalStateException("Underlying producer is closed"); + return producer; + } catch (Throwable e) { + exceptionOccurred = true; + JMSException exc = new JMSException( + "Exception occurred on pooled session"); + exc.initCause(e); + throw exc; } - - return producer; } /* @@ -471,7 +460,6 @@ public class JmsSessionWrapper implements Session { return sess.createQueue(queueName); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -492,7 +480,6 @@ public class JmsSessionWrapper implements Session { return sess.createStreamMessage(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -513,7 +500,6 @@ public class JmsSessionWrapper implements Session { return sess.createTemporaryQueue(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -534,7 +520,6 @@ public class JmsSessionWrapper implements Session { return sess.createTemporaryTopic(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -555,7 +540,6 @@ public class JmsSessionWrapper implements Session { return sess.createTextMessage(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -576,7 +560,6 @@ public class JmsSessionWrapper implements Session { return sess.createTextMessage(text); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -597,7 +580,6 @@ public class JmsSessionWrapper implements Session { return sess.createTopic(topicName); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -618,7 +600,6 @@ public class JmsSessionWrapper implements Session { return sess.getAcknowledgeMode(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -639,7 +620,6 @@ public class JmsSessionWrapper implements Session { return sess.getMessageListener(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -660,7 +640,6 @@ public class JmsSessionWrapper implements Session { return sess.getTransacted(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -681,7 +660,6 @@ public class JmsSessionWrapper implements Session { sess.recover(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -702,7 +680,6 @@ public class JmsSessionWrapper implements Session { sess.rollback(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -722,7 +699,6 @@ public class JmsSessionWrapper implements Session { sess.run(); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; RuntimeException exc = new RuntimeException( "Exception occurred on pooled session", e); throw exc; @@ -743,7 +719,6 @@ public class JmsSessionWrapper implements Session { sess.setMessageListener(listener); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e); @@ -764,7 +739,6 @@ public class JmsSessionWrapper implements Session { sess.unsubscribe(name); } catch (Throwable e) { exceptionOccurred = true; - trappedExc = e; JMSException exc = new JMSException( "Exception occurred on pooled session"); exc.initCause(e);