KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > jms > connection > ConnectionFactoryUtils


1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.jms.connection;
18
19 import javax.jms.Connection JavaDoc;
20 import javax.jms.ConnectionFactory JavaDoc;
21 import javax.jms.JMSException JavaDoc;
22 import javax.jms.QueueConnection JavaDoc;
23 import javax.jms.QueueConnectionFactory JavaDoc;
24 import javax.jms.QueueSession JavaDoc;
25 import javax.jms.Session JavaDoc;
26 import javax.jms.TopicConnection JavaDoc;
27 import javax.jms.TopicConnectionFactory JavaDoc;
28 import javax.jms.TopicSession JavaDoc;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32
33 import org.springframework.transaction.support.TransactionSynchronizationAdapter;
34 import org.springframework.transaction.support.TransactionSynchronizationManager;
35 import org.springframework.util.Assert;
36
37 /**
38  * Helper class for managing a JMS {@link javax.jms.ConnectionFactory}, in particular
39  * for obtaining transactional JMS resources for a given ConnectionFactory.
40  *
41  * <p>Mainly for internal use within the framework. Used by
42  * {@link org.springframework.jms.core.JmsTemplate} as well as
43  * {@link org.springframework.jms.listener.DefaultMessageListenerContainer}.
44  *
45  * @author Juergen Hoeller
46  * @since 2.0
47  * @see SmartConnectionFactory
48  */

49 public abstract class ConnectionFactoryUtils {
50
51     private static final Log logger = LogFactory.getLog(ConnectionFactoryUtils.class);
52
53
54     /**
55      * Release the given Connection, stopping it (if necessary) and eventually closing it.
56      * <p>Checks {@link SmartConnectionFactory#shouldStop}, if available.
57      * This is essentially a more sophisticated version of
58      * {@link org.springframework.jms.support.JmsUtils#closeConnection}.
59      * @param con the Connection to release
60      * (if this is <code>null</code>, the call will be ignored)
61      * @param cf the ConnectionFactory that the Connection was obtained from
62      * (may be <code>null</code>)
63      * @param started whether the Connection might have been started by the application
64      * @see SmartConnectionFactory#shouldStop
65      * @see org.springframework.jms.support.JmsUtils#closeConnection
66      */

67     public static void releaseConnection(Connection JavaDoc con, ConnectionFactory JavaDoc cf, boolean started) {
68         if (con == null) {
69             return;
70         }
71         if (started && cf instanceof SmartConnectionFactory && ((SmartConnectionFactory) cf).shouldStop(con)) {
72             try {
73                 con.stop();
74             }
75             catch (Throwable JavaDoc ex) {
76                 logger.debug("Could not stop JMS Connection before closing it", ex);
77             }
78         }
79         try {
80             con.close();
81         }
82         catch (Throwable JavaDoc ex) {
83             logger.debug("Could not close JMS Connection", ex);
84         }
85     }
86
87     /**
88      * Determine whether the given JMS Session is transactional, that is,
89      * bound to the current thread by Spring's transaction facilities.
90      * @param session the JMS Session to check
91      * @param cf the JMS ConnectionFactory that the Session originated from
92      * @return whether the Session is transactional
93      */

94     public static boolean isSessionTransactional(Session JavaDoc session, ConnectionFactory JavaDoc cf) {
95         if (session == null || cf == null) {
96             return false;
97         }
98         JmsResourceHolder resourceHolder = (JmsResourceHolder) TransactionSynchronizationManager.getResource(cf);
99         return (resourceHolder != null && resourceHolder.containsSession(session));
100     }
101
102
103     /**
104      * Obtain a JMS Session that is synchronized with the current transaction, if any.
105      * @param cf the ConnectionFactory to obtain a Session for
106      * @param existingCon the existing JMS Connection to obtain a Session for
107      * (may be <code>null</code>)
108      * @param synchedLocalTransactionAllowed whether to allow for a local JMS transaction
109      * that is synchronized with a Spring-managed transaction (where the main transaction
110      * might be a JDBC-based one for a specific DataSource, for example), with the JMS
111      * transaction committing right after the main transaction. If not allowed, the given
112      * ConnectionFactory needs to handle transaction enlistment underneath the covers.
113      * @return the transactional Session, or <code>null</code> if none found
114      * @throws JMSException in case of JMS failure
115      */

116     public static Session JavaDoc getTransactionalSession(
117             final ConnectionFactory JavaDoc cf, final Connection JavaDoc existingCon, final boolean synchedLocalTransactionAllowed)
118             throws JMSException JavaDoc {
119
120         return doGetTransactionalSession(cf, new ResourceFactory() {
121             public Session JavaDoc getSession(JmsResourceHolder holder) {
122                 return holder.getSession(Session JavaDoc.class, existingCon);
123             }
124             public Connection JavaDoc getConnection(JmsResourceHolder holder) {
125                 return (existingCon != null ? existingCon : holder.getConnection());
126             }
127             public Connection JavaDoc createConnection() throws JMSException JavaDoc {
128                 return cf.createConnection();
129             }
130             public Session JavaDoc createSession(Connection JavaDoc con) throws JMSException JavaDoc {
131                 return con.createSession(synchedLocalTransactionAllowed, Session.AUTO_ACKNOWLEDGE);
132             }
133             public boolean isSynchedLocalTransactionAllowed() {
134                 return synchedLocalTransactionAllowed;
135             }
136         });
137     }
138
139     /**
140      * Obtain a JMS QueueSession that is synchronized with the current transaction, if any.
141      * <p>Mainly intended for use with the JMS 1.0.2 API.
142      * @param cf the ConnectionFactory to obtain a Session for
143      * @param existingCon the existing JMS Connection to obtain a Session for
144      * (may be <code>null</code>)
145      * @param synchedLocalTransactionAllowed whether to allow for a local JMS transaction
146      * that is synchronized with a Spring-managed transaction (where the main transaction
147      * might be a JDBC-based one for a specific DataSource, for example), with the JMS
148      * transaction committing right after the main transaction. If not allowed, the given
149      * ConnectionFactory needs to handle transaction enlistment underneath the covers.
150      * @return the transactional Session, or <code>null</code> if none found
151      * @throws JMSException in case of JMS failure
152      */

153     public static QueueSession JavaDoc getTransactionalQueueSession(
154             final QueueConnectionFactory JavaDoc cf, final QueueConnection JavaDoc existingCon, final boolean synchedLocalTransactionAllowed)
155             throws JMSException JavaDoc {
156
157         return (QueueSession JavaDoc) doGetTransactionalSession(cf, new ResourceFactory() {
158             public Session JavaDoc getSession(JmsResourceHolder holder) {
159                 return holder.getSession(QueueSession JavaDoc.class, existingCon);
160             }
161             public Connection JavaDoc getConnection(JmsResourceHolder holder) {
162                 return (existingCon != null ? existingCon : holder.getConnection(QueueConnection JavaDoc.class));
163             }
164             public Connection JavaDoc createConnection() throws JMSException JavaDoc {
165                 return cf.createQueueConnection();
166             }
167             public Session JavaDoc createSession(Connection JavaDoc con) throws JMSException JavaDoc {
168                 return ((QueueConnection JavaDoc) con).createQueueSession(synchedLocalTransactionAllowed, Session.AUTO_ACKNOWLEDGE);
169             }
170             public boolean isSynchedLocalTransactionAllowed() {
171                 return synchedLocalTransactionAllowed;
172             }
173         });
174     }
175
176     /**
177      * Obtain a JMS TopicSession that is synchronized with the current transaction, if any.
178      * <p>Mainly intended for use with the JMS 1.0.2 API.
179      * @param cf the ConnectionFactory to obtain a Session for
180      * @param existingCon the existing JMS Connection to obtain a Session for
181      * (may be <code>null</code>)
182      * @param synchedLocalTransactionAllowed whether to allow for a local JMS transaction
183      * that is synchronized with a Spring-managed transaction (where the main transaction
184      * might be a JDBC-based one for a specific DataSource, for example), with the JMS
185      * transaction committing right after the main transaction. If not allowed, the given
186      * ConnectionFactory needs to handle transaction enlistment underneath the covers.
187      * @return the transactional Session, or <code>null</code> if none found
188      * @throws JMSException in case of JMS failure
189      */

190     public static TopicSession JavaDoc getTransactionalTopicSession(
191             final TopicConnectionFactory JavaDoc cf, final TopicConnection JavaDoc existingCon, final boolean synchedLocalTransactionAllowed)
192             throws JMSException JavaDoc {
193
194         return (TopicSession JavaDoc) doGetTransactionalSession(cf, new ResourceFactory() {
195             public Session JavaDoc getSession(JmsResourceHolder holder) {
196                 return holder.getSession(TopicSession JavaDoc.class, existingCon);
197             }
198             public Connection JavaDoc getConnection(JmsResourceHolder holder) {
199                 return (existingCon != null ? existingCon : holder.getConnection(TopicConnection JavaDoc.class));
200             }
201             public Connection JavaDoc createConnection() throws JMSException JavaDoc {
202                 return cf.createTopicConnection();
203             }
204             public Session JavaDoc createSession(Connection JavaDoc con) throws JMSException JavaDoc {
205                 return ((TopicConnection JavaDoc) con).createTopicSession(synchedLocalTransactionAllowed, Session.AUTO_ACKNOWLEDGE);
206             }
207             public boolean isSynchedLocalTransactionAllowed() {
208                 return synchedLocalTransactionAllowed;
209             }
210         });
211     }
212
213     /**
214      * Obtain a JMS Session that is synchronized with the current transaction, if any.
215      * @param connectionFactory the JMS ConnectionFactory to bind for
216      * (used as TransactionSynchronizationManager key)
217      * @param resourceFactory the ResourceFactory to use for extracting or creating
218      * JMS resources
219      * @return the transactional Session, or <code>null</code> if none found
220      * @throws JMSException in case of JMS failure
221      */

222     public static Session JavaDoc doGetTransactionalSession(
223             ConnectionFactory JavaDoc connectionFactory, ResourceFactory resourceFactory)
224             throws JMSException JavaDoc {
225
226         Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
227         Assert.notNull(resourceFactory, "ResourceFactory must not be null");
228
229         JmsResourceHolder resourceHolder =
230                 (JmsResourceHolder) TransactionSynchronizationManager.getResource(connectionFactory);
231         if (resourceHolder != null) {
232             Session JavaDoc session = resourceFactory.getSession(resourceHolder);
233             if (session != null || resourceHolder.isFrozen()) {
234                 return session;
235             }
236         }
237         if (!TransactionSynchronizationManager.isSynchronizationActive()) {
238             return null;
239         }
240         JmsResourceHolder resourceHolderToUse = resourceHolder;
241         if (resourceHolderToUse == null) {
242             resourceHolderToUse = new JmsResourceHolder(connectionFactory);
243         }
244         Connection JavaDoc con = resourceFactory.getConnection(resourceHolderToUse);
245         Session JavaDoc session = null;
246         try {
247             boolean isExistingCon = (con != null);
248             if (!isExistingCon) {
249                 con = resourceFactory.createConnection();
250                 resourceHolderToUse.addConnection(con);
251             }
252             session = resourceFactory.createSession(con);
253             resourceHolderToUse.addSession(session, con);
254             if (!isExistingCon) {
255                 con.start();
256             }
257         }
258         catch (JMSException JavaDoc ex) {
259             if (session != null) {
260                 try {
261                     session.close();
262                 }
263                 catch (Throwable JavaDoc ex2) {
264                     // ignore
265
}
266             }
267             if (con != null) {
268                 try {
269                     con.close();
270                 }
271                 catch (Throwable JavaDoc ex2) {
272                     // ignore
273
}
274             }
275             throw ex;
276         }
277         if (resourceHolderToUse != resourceHolder) {
278             TransactionSynchronizationManager.registerSynchronization(
279                     new JmsResourceSynchronization(
280                             connectionFactory, resourceHolderToUse, resourceFactory.isSynchedLocalTransactionAllowed()));
281             resourceHolderToUse.setSynchronizedWithTransaction(true);
282             TransactionSynchronizationManager.bindResource(connectionFactory, resourceHolderToUse);
283         }
284         return session;
285     }
286
287
288     /**
289      * Callback interface for resource creation.
290      * Serving as argument for the <code>doGetTransactionalSession</code> method.
291      */

292     public interface ResourceFactory {
293
294         /**
295          * Fetch an appropriate Session from the given JmsResourceHolder.
296          * @param holder the JmsResourceHolder
297          * @return an appropriate Session fetched from the holder,
298          * or <code>null</code> if none found
299          */

300         Session JavaDoc getSession(JmsResourceHolder holder);
301
302         /**
303          * Fetch an appropriate Connection from the given JmsResourceHolder.
304          * @param holder the JmsResourceHolder
305          * @return an appropriate Connection fetched from the holder,
306          * or <code>null</code> if none found
307          */

308         Connection JavaDoc getConnection(JmsResourceHolder holder);
309
310         /**
311          * Create a new JMS Connection for registration with a JmsResourceHolder.
312          * @return the new JMS Connection
313          * @throws JMSException if thrown by JMS API methods
314          */

315         Connection JavaDoc createConnection() throws JMSException JavaDoc;
316
317         /**
318          * Create a new JMS Session for registration with a JmsResourceHolder.
319          * @param con the JMS Connection to create a Session for
320          * @return the new JMS Session
321          * @throws JMSException if thrown by JMS API methods
322          */

323         Session JavaDoc createSession(Connection JavaDoc con) throws JMSException JavaDoc;
324
325         /**
326          * Return whether to allow for a local JMS transaction that is synchronized with
327          * a Spring-managed transaction (where the main transaction might be a JDBC-based
328          * one for a specific DataSource, for example), with the JMS transaction
329          * committing right after the main transaction.
330          * @return whether to allow for synchronizing a local JMS transaction
331          */

332         boolean isSynchedLocalTransactionAllowed();
333     }
334
335
336     /**
337      * Callback for resource cleanup at the end of a non-native JMS transaction
338      * (e.g. when participating in a JtaTransactionManager transaction).
339      * @see org.springframework.transaction.jta.JtaTransactionManager
340      */

341     private static class JmsResourceSynchronization extends TransactionSynchronizationAdapter {
342
343         private final Object JavaDoc resourceKey;
344
345         private final JmsResourceHolder resourceHolder;
346
347         private final boolean transacted;
348
349         private boolean holderActive = true;
350
351         public JmsResourceSynchronization(Object JavaDoc resourceKey, JmsResourceHolder resourceHolder, boolean transacted) {
352             this.resourceKey = resourceKey;
353             this.resourceHolder = resourceHolder;
354             this.transacted = transacted;
355         }
356
357         public void suspend() {
358             if (this.holderActive) {
359                 TransactionSynchronizationManager.unbindResource(this.resourceKey);
360             }
361         }
362
363         public void resume() {
364             if (this.holderActive) {
365                 TransactionSynchronizationManager.bindResource(this.resourceKey, this.resourceHolder);
366             }
367         }
368
369         public void beforeCompletion() {
370             TransactionSynchronizationManager.unbindResource(this.resourceKey);
371             this.holderActive = false;
372             if (!this.transacted) {
373                 this.resourceHolder.closeAll();
374             }
375         }
376
377         public void afterCommit() {
378             if (this.transacted) {
379                 try {
380                     this.resourceHolder.commitAll();
381                 }
382                 catch (JMSException JavaDoc ex) {
383                     throw new SynchedLocalTransactionFailedException("Local JMS transaction failed to commit", ex);
384                 }
385             }
386         }
387
388         public void afterCompletion(int status) {
389             if (this.transacted) {
390                 this.resourceHolder.closeAll();
391             }
392         }
393     }
394
395 }
396
Popular Tags