KickJava   Java API By Example, From Geeks To Geeks.

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


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.Session JavaDoc;
23 import javax.jms.TransactionRolledBackException JavaDoc;
24
25 import org.springframework.transaction.CannotCreateTransactionException;
26 import org.springframework.transaction.InvalidIsolationLevelException;
27 import org.springframework.transaction.TransactionDefinition;
28 import org.springframework.transaction.TransactionSystemException;
29 import org.springframework.transaction.UnexpectedRollbackException;
30 import org.springframework.transaction.support.AbstractPlatformTransactionManager;
31 import org.springframework.transaction.support.DefaultTransactionStatus;
32 import org.springframework.transaction.support.SmartTransactionObject;
33 import org.springframework.transaction.support.TransactionSynchronizationManager;
34 import org.springframework.transaction.support.ResourceTransactionManager;
35 import org.springframework.beans.factory.InitializingBean;
36
37 /**
38  * {@link org.springframework.transaction.PlatformTransactionManager} implementation
39  * for a single JMS {@link javax.jms.ConnectionFactory}. Binds a JMS
40  * Connection/Session pair from the specified ConnectionFactory to the thread,
41  * potentially allowing for one thread-bound Session per ConnectionFactory.
42  *
43  * <p>This local strategy is an alternative to executing JMS operations within
44  * JTA transactions. Its advantage is that it is able to work in any environment,
45  * for example a standalone application or a test suite, with any message broker
46  * as target. However, this strategy is <i>not</i> able to provide XA transactions,
47  * for example in order to share transactions between messaging and database access.
48  * A full JTA/XA setup is required for XA transactions, typically using Spring's
49  * {@link org.springframework.transaction.jta.JtaTransactionManager} as strategy.
50  *
51  * <p>Application code is required to retrieve the transactional JMS Session via
52  * {@link ConnectionFactoryUtils#getTransactionalSession} instead of a standard
53  * J2EE-style {@link ConnectionFactory#createConnection()} call with subsequent
54  * Session creation. Spring's {@link org.springframework.jms.core.JmsTemplate}
55  * will autodetect a thread-bound Session and automatically participate in it.
56  *
57  * <p>Alternatively, you can allow application code to work with the standard
58  * J2EE-style lookup pattern on a ConnectionFactory, for example for legacy code
59  * that is not aware of Spring at all. In that case, define a
60  * {@link TransactionAwareConnectionFactoryProxy} for your target ConnectionFactory,
61  * which will automatically participate in Spring-managed transactions.
62  *
63  * <p>This transaction strategy will typically be used in combination with
64  * {@link SingleConnectionFactory}, which uses a single JMS Connection for all
65  * JMS access in order to avoid the overhead of repeated Connection creation,
66  * typically in a standalone application. Each transaction will then share the
67  * same JMS Connection, while still using its own individual JMS Session.
68  *
69  * <p>Transaction synchronization is turned off by default, as this manager might
70  * be used alongside a datastore-based Spring transaction manager such as the
71  * JDBC {@link org.springframework.jdbc.datasource.DataSourceTransactionManager},
72  * which has stronger needs for synchronization.
73  *
74  * @author Juergen Hoeller
75  * @since 1.1
76  * @see ConnectionFactoryUtils#getTransactionalSession
77  * @see TransactionAwareConnectionFactoryProxy
78  * @see org.springframework.jms.core.JmsTemplate
79  */

80 public class JmsTransactionManager extends AbstractPlatformTransactionManager
81         implements ResourceTransactionManager, InitializingBean {
82
83     private ConnectionFactory JavaDoc connectionFactory;
84
85
86     /**
87      * Create a new JmsTransactionManager for bean-style usage.
88      * <p>Note: The ConnectionFactory has to be set before using the instance.
89      * This constructor can be used to prepare a JmsTemplate via a BeanFactory,
90      * typically setting the ConnectionFactory via setConnectionFactory.
91      * <p>Turns off transaction synchronization by default, as this manager might
92      * be used alongside a datastore-based Spring transaction manager like
93      * DataSourceTransactionManager, which has stronger needs for synchronization.
94      * Only one manager is allowed to drive synchronization at any point of time.
95      * @see #setConnectionFactory
96      * @see #setTransactionSynchronization
97      */

98     public JmsTransactionManager() {
99         setTransactionSynchronization(SYNCHRONIZATION_NEVER);
100     }
101
102     /**
103      * Create a new JmsTransactionManager, given a ConnectionFactory.
104      * @param connectionFactory the ConnectionFactory to obtain connections from
105      */

106     public JmsTransactionManager(ConnectionFactory JavaDoc connectionFactory) {
107         this();
108         setConnectionFactory(connectionFactory);
109         afterPropertiesSet();
110     }
111
112
113     /**
114      * Set the JMS ConnectionFactory that this instance should manage transactions for.
115      */

116     public void setConnectionFactory(ConnectionFactory JavaDoc cf) {
117         if (cf instanceof TransactionAwareConnectionFactoryProxy) {
118             // If we got a TransactionAwareConnectionFactoryProxy, we need to perform transactions
119
// for its underlying target ConnectionFactory, else JMS access code won't see
120
// properly exposed transactions (i.e. transactions for the target ConnectionFactory).
121
this.connectionFactory = ((TransactionAwareConnectionFactoryProxy) cf).getTargetConnectionFactory();
122         }
123         else {
124             this.connectionFactory = cf;
125         }
126     }
127
128     /**
129      * Return the JMS ConnectionFactory that this instance should manage transactions for.
130      */

131     public ConnectionFactory JavaDoc getConnectionFactory() {
132         return this.connectionFactory;
133     }
134
135     /**
136      * Make sure the ConnectionFactory has been set.
137      */

138     public void afterPropertiesSet() {
139         if (getConnectionFactory() == null) {
140             throw new IllegalArgumentException JavaDoc("Property 'connectionFactory' is required");
141         }
142     }
143
144
145     public Object JavaDoc getResourceFactory() {
146         return getConnectionFactory();
147     }
148
149     protected Object JavaDoc doGetTransaction() {
150         JmsTransactionObject txObject = new JmsTransactionObject();
151         txObject.setResourceHolder(
152                 (JmsResourceHolder) TransactionSynchronizationManager.getResource(getConnectionFactory()));
153         return txObject;
154     }
155
156     protected boolean isExistingTransaction(Object JavaDoc transaction) {
157         JmsTransactionObject txObject = (JmsTransactionObject) transaction;
158         return (txObject.getResourceHolder() != null);
159     }
160
161     protected void doBegin(Object JavaDoc transaction, TransactionDefinition definition) {
162         if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
163             throw new InvalidIsolationLevelException("JMS does not support an isolation level concept");
164         }
165         JmsTransactionObject txObject = (JmsTransactionObject) transaction;
166         Connection JavaDoc con = null;
167         Session JavaDoc session = null;
168         try {
169             con = createConnection();
170             session = createSession(con);
171             con.start();
172             if (logger.isDebugEnabled()) {
173                 logger.debug("Created JMS transaction on Session [" + session + "] from Connection [" + con + "]");
174             }
175             txObject.setResourceHolder(new JmsResourceHolder(getConnectionFactory(), con, session));
176             txObject.getResourceHolder().setSynchronizedWithTransaction(true);
177             int timeout = determineTimeout(definition);
178             if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
179                 txObject.getResourceHolder().setTimeoutInSeconds(timeout);
180             }
181             TransactionSynchronizationManager.bindResource(
182                     getConnectionFactory(), txObject.getResourceHolder());
183         }
184         catch (JMSException JavaDoc ex) {
185             if (session != null) {
186                 try {
187                     session.close();
188                 }
189                 catch (Throwable JavaDoc ex2) {
190                     // ignore
191
}
192             }
193             if (con != null) {
194                 try {
195                     con.close();
196                 }
197                 catch (Throwable JavaDoc ex2) {
198                     // ignore
199
}
200             }
201             throw new CannotCreateTransactionException("Could not create JMS transaction", ex);
202         }
203     }
204
205     protected Object JavaDoc doSuspend(Object JavaDoc transaction) {
206         JmsTransactionObject txObject = (JmsTransactionObject) transaction;
207         txObject.setResourceHolder(null);
208         return TransactionSynchronizationManager.unbindResource(getConnectionFactory());
209     }
210
211     protected void doResume(Object JavaDoc transaction, Object JavaDoc suspendedResources) {
212         JmsResourceHolder conHolder = (JmsResourceHolder) suspendedResources;
213         TransactionSynchronizationManager.bindResource(getConnectionFactory(), conHolder);
214     }
215
216     protected void doCommit(DefaultTransactionStatus status) {
217         JmsTransactionObject txObject = (JmsTransactionObject) status.getTransaction();
218         Session JavaDoc session = txObject.getResourceHolder().getSession();
219         try {
220             if (status.isDebug()) {
221                 logger.debug("Committing JMS transaction on Session [" + session + "]");
222             }
223             session.commit();
224         }
225         catch (TransactionRolledBackException JavaDoc ex) {
226             throw new UnexpectedRollbackException("JMS transaction rolled back", ex);
227         }
228         catch (JMSException JavaDoc ex) {
229             throw new TransactionSystemException("Could not commit JMS transaction", ex);
230         }
231     }
232
233     protected void doRollback(DefaultTransactionStatus status) {
234         JmsTransactionObject txObject = (JmsTransactionObject) status.getTransaction();
235         Session JavaDoc session = txObject.getResourceHolder().getSession();
236         try {
237             if (status.isDebug()) {
238                 logger.debug("Rolling back JMS transaction on Session [" + session + "]");
239             }
240             session.rollback();
241         }
242         catch (JMSException JavaDoc ex) {
243             throw new TransactionSystemException("Could not roll back JMS transaction", ex);
244         }
245     }
246
247     protected void doSetRollbackOnly(DefaultTransactionStatus status) {
248         JmsTransactionObject txObject = (JmsTransactionObject) status.getTransaction();
249         txObject.getResourceHolder().setRollbackOnly();
250     }
251
252     protected void doCleanupAfterCompletion(Object JavaDoc transaction) {
253         JmsTransactionObject txObject = (JmsTransactionObject) transaction;
254         TransactionSynchronizationManager.unbindResource(getConnectionFactory());
255         txObject.getResourceHolder().closeAll();
256         txObject.getResourceHolder().clear();
257     }
258
259
260     //-------------------------------------------------------------------------
261
// JMS 1.1 factory methods, potentially overridden for JMS 1.0.2
262
//-------------------------------------------------------------------------
263

264     /**
265      * Create a JMS Connection via this template's ConnectionFactory.
266      * <p>This implementation uses JMS 1.1 API.
267      * @return the new JMS Connection
268      * @throws javax.jms.JMSException if thrown by JMS API methods
269      */

270     protected Connection JavaDoc createConnection() throws JMSException JavaDoc {
271         return getConnectionFactory().createConnection();
272     }
273
274     /**
275      * Create a JMS Session for the given Connection.
276      * <p>This implementation uses JMS 1.1 API.
277      * @param con the JMS Connection to create a Session for
278      * @return the new JMS Session
279      * @throws javax.jms.JMSException if thrown by JMS API methods
280      */

281     protected Session JavaDoc createSession(Connection JavaDoc con) throws JMSException JavaDoc {
282         return con.createSession(true, Session.AUTO_ACKNOWLEDGE);
283     }
284
285
286     /**
287      * JMS transaction object, representing a JmsResourceHolder.
288      * Used as transaction object by JmsTransactionManager.
289      * @see JmsResourceHolder
290      */

291     private static class JmsTransactionObject implements SmartTransactionObject {
292
293         private JmsResourceHolder resourceHolder;
294
295         public void setResourceHolder(JmsResourceHolder resourceHolder) {
296             this.resourceHolder = resourceHolder;
297         }
298
299         public JmsResourceHolder getResourceHolder() {
300             return resourceHolder;
301         }
302
303         public boolean isRollbackOnly() {
304             return getResourceHolder().isRollbackOnly();
305         }
306     }
307
308 }
309
Popular Tags