KickJava   Java API By Example, From Geeks To Geeks.

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


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 java.lang.reflect.InvocationHandler JavaDoc;
20 import java.lang.reflect.InvocationTargetException JavaDoc;
21 import java.lang.reflect.Method JavaDoc;
22 import java.lang.reflect.Proxy JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.List JavaDoc;
25
26 import javax.jms.Connection JavaDoc;
27 import javax.jms.ConnectionFactory JavaDoc;
28 import javax.jms.JMSException JavaDoc;
29 import javax.jms.QueueConnection JavaDoc;
30 import javax.jms.QueueConnectionFactory JavaDoc;
31 import javax.jms.QueueSession JavaDoc;
32 import javax.jms.Session JavaDoc;
33 import javax.jms.TopicConnection JavaDoc;
34 import javax.jms.TopicConnectionFactory JavaDoc;
35 import javax.jms.TopicSession JavaDoc;
36 import javax.jms.TransactionInProgressException JavaDoc;
37
38 import org.springframework.util.Assert;
39
40 /**
41  * Proxy for a target JMS {@link javax.jms.ConnectionFactory}, adding awareness of
42  * Spring-managed transactions. Similar to a transactional JNDI ConnectionFactory
43  * as provided by a J2EE server.
44  *
45  * <p>Messaging code that should remain unaware of Spring's JMS support can work
46  * with this proxy to seamlessly participate in Spring-managed transactions.
47  * Note that the transaction manager, for example {@link JmsTransactionManager},
48  * still needs to work with the underlying ConnectionFactory, <i>not</i> with
49  * this proxy.
50  *
51  * <p><b>Make sure that TransactionAwareConnectionFactoryProxy is the outermost
52  * ConnectionFactory of a chain of ConnectionFactory proxies/adapters.</b>
53  * TransactionAwareConnectionFactoryProxy can delegate either directly to the
54  * target factory or to some intermediary adapter like
55  * {@link UserCredentialsConnectionFactoryAdapter}.
56  *
57  * <p>Delegates to {@link ConnectionFactoryUtils} for automatically participating
58  * in thread-bound transactions, for example managed by {@link JmsTransactionManager}.
59  * <code>createSession</code> calls and <code>close</code> calls on returned Sessions
60  * will behave properly within a transaction, that is, always work on the transactional
61  * Session. If not within a transaction, normal ConnectionFactory behavior applies.
62  *
63  * <p>Note that transactional JMS Sessions will be registered on a per-Connection
64  * basis. To share the same JMS Session across a transaction, make sure that you
65  * operate on the same JMS Connection handle - either through reusing the handle
66  * or through configuring a {@link SingleConnectionFactory} underneath.
67  *
68  * <p>Returned transactional Session proxies will implement the {@link SessionProxy}
69  * interface to allow for access to the underlying target Session. This is only
70  * intended for accessing vendor-specific Session API or for testing purposes
71  * (e.g. to perform manual transaction control). For typical application purposes,
72  * simply use the standard JMS Session interface.
73  *
74  * @author Juergen Hoeller
75  * @since 2.0
76  * @see UserCredentialsConnectionFactoryAdapter
77  * @see SingleConnectionFactory
78  */

79 public class TransactionAwareConnectionFactoryProxy
80         implements ConnectionFactory JavaDoc, QueueConnectionFactory JavaDoc, TopicConnectionFactory JavaDoc {
81
82     private boolean synchedLocalTransactionAllowed = false;
83
84     private ConnectionFactory JavaDoc targetConnectionFactory;
85
86
87     /**
88      * Create a new TransactionAwareConnectionFactoryProxy.
89      */

90     public TransactionAwareConnectionFactoryProxy() {
91     }
92
93     /**
94      * Create a new TransactionAwareConnectionFactoryProxy.
95      * @param targetConnectionFactory the target ConnectionFactory
96      */

97     public TransactionAwareConnectionFactoryProxy(ConnectionFactory JavaDoc targetConnectionFactory) {
98         setTargetConnectionFactory(targetConnectionFactory);
99     }
100
101
102     /**
103      * Set the target ConnectionFactory that this ConnectionFactory should delegate to.
104      */

105     public final void setTargetConnectionFactory(ConnectionFactory JavaDoc targetConnectionFactory) {
106         Assert.notNull(targetConnectionFactory, "targetConnectionFactory must not be nul");
107         this.targetConnectionFactory = targetConnectionFactory;
108     }
109
110     /**
111      * Return the target ConnectionFactory that this ConnectionFactory should delegate to.
112      */

113     protected ConnectionFactory JavaDoc getTargetConnectionFactory() {
114         return this.targetConnectionFactory;
115     }
116
117     /**
118      * Set whether to allow for a local JMS transaction that is synchronized with a
119      * Spring-managed transaction (where the main transaction might be a JDBC-based
120      * one for a specific DataSource, for example), with the JMS transaction committing
121      * right after the main transaction. If not allowed, the given ConnectionFactory
122      * needs to handle transaction enlistment underneath the covers.
123      * <p>Default is "false": If not within a managed transaction that encompasses
124      * the underlying JMS ConnectionFactory, standard Sessions will be returned.
125      * Turn this flag on to allow participation in any Spring-managed transaction,
126      * with a local JMS transaction synchronized with the main transaction.
127      */

128     public void setSynchedLocalTransactionAllowed(boolean synchedLocalTransactionAllowed) {
129         this.synchedLocalTransactionAllowed = synchedLocalTransactionAllowed;
130     }
131
132     /**
133      * Return whether to allow for a local JMS transaction that is synchronized
134      * with a Spring-managed transaction.
135      */

136     protected boolean isSynchedLocalTransactionAllowed() {
137         return this.synchedLocalTransactionAllowed;
138     }
139
140
141     public Connection JavaDoc createConnection() throws JMSException JavaDoc {
142         Connection JavaDoc targetConnection = this.targetConnectionFactory.createConnection();
143         return getTransactionAwareConnectionProxy(targetConnection);
144     }
145
146     public Connection JavaDoc createConnection(String JavaDoc username, String JavaDoc password) throws JMSException JavaDoc {
147         Connection JavaDoc targetConnection = this.targetConnectionFactory.createConnection(username, password);
148         return getTransactionAwareConnectionProxy(targetConnection);
149     }
150
151     public QueueConnection JavaDoc createQueueConnection() throws JMSException JavaDoc {
152         if (!(this.targetConnectionFactory instanceof QueueConnectionFactory JavaDoc)) {
153             throw new javax.jms.IllegalStateException JavaDoc("'targetConnectionFactory' is no QueueConnectionFactory");
154         }
155         QueueConnection JavaDoc targetConnection =
156                 ((QueueConnectionFactory JavaDoc) this.targetConnectionFactory).createQueueConnection();
157         return (QueueConnection JavaDoc) getTransactionAwareConnectionProxy(targetConnection);
158     }
159
160     public QueueConnection JavaDoc createQueueConnection(String JavaDoc username, String JavaDoc password) throws JMSException JavaDoc {
161         if (!(this.targetConnectionFactory instanceof QueueConnectionFactory JavaDoc)) {
162             throw new javax.jms.IllegalStateException JavaDoc("'targetConnectionFactory' is no QueueConnectionFactory");
163         }
164         QueueConnection JavaDoc targetConnection =
165                 ((QueueConnectionFactory JavaDoc) this.targetConnectionFactory).createQueueConnection(username, password);
166         return (QueueConnection JavaDoc) getTransactionAwareConnectionProxy(targetConnection);
167     }
168
169     public TopicConnection JavaDoc createTopicConnection() throws JMSException JavaDoc {
170         if (!(this.targetConnectionFactory instanceof TopicConnectionFactory JavaDoc)) {
171             throw new javax.jms.IllegalStateException JavaDoc("'targetConnectionFactory' is no TopicConnectionFactory");
172         }
173         TopicConnection JavaDoc targetConnection =
174                 ((TopicConnectionFactory JavaDoc) this.targetConnectionFactory).createTopicConnection();
175         return (TopicConnection JavaDoc) getTransactionAwareConnectionProxy(targetConnection);
176     }
177
178     public TopicConnection JavaDoc createTopicConnection(String JavaDoc username, String JavaDoc password) throws JMSException JavaDoc {
179         if (!(this.targetConnectionFactory instanceof TopicConnectionFactory JavaDoc)) {
180             throw new javax.jms.IllegalStateException JavaDoc("'targetConnectionFactory' is no TopicConnectionFactory");
181         }
182         TopicConnection JavaDoc targetConnection =
183                 ((TopicConnectionFactory JavaDoc) this.targetConnectionFactory).createTopicConnection(username, password);
184         return (TopicConnection JavaDoc) getTransactionAwareConnectionProxy(targetConnection);
185     }
186
187
188     /**
189      * Wrap the given Connection with a proxy that delegates every method call to it
190      * but handles Session lookup in a transaction-aware fashion.
191      * @param target the original Connection to wrap
192      * @return the wrapped Connection
193      */

194     private Connection JavaDoc getTransactionAwareConnectionProxy(Connection JavaDoc target) {
195         List JavaDoc classes = new ArrayList JavaDoc(3);
196         classes.add(Connection JavaDoc.class);
197         if (target instanceof QueueConnection JavaDoc) {
198             classes.add(QueueConnection JavaDoc.class);
199         }
200         if (target instanceof TopicConnection JavaDoc) {
201             classes.add(TopicConnection JavaDoc.class);
202         }
203         return (Connection JavaDoc) Proxy.newProxyInstance(
204                 getClass().getClassLoader(),
205                 (Class JavaDoc[]) classes.toArray(new Class JavaDoc[classes.size()]),
206                 new TransactionAwareConnectionInvocationHandler(target));
207     }
208
209
210     /**
211      * Invocation handler that exposes transactional Sessions for the underlying Connection.
212      */

213     private class TransactionAwareConnectionInvocationHandler implements InvocationHandler JavaDoc {
214
215         private final Connection JavaDoc target;
216
217         public TransactionAwareConnectionInvocationHandler(Connection JavaDoc target) {
218             this.target = target;
219         }
220
221         public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc {
222             // Invocation on ConnectionProxy interface coming in...
223

224             if (Session JavaDoc.class.equals(method.getReturnType())) {
225                 Session JavaDoc session = ConnectionFactoryUtils.getTransactionalSession(
226                         getTargetConnectionFactory(), this.target, isSynchedLocalTransactionAllowed());
227                 if (session != null) {
228                     return getCloseSuppressingSessionProxy(session);
229                 }
230             }
231             else if (QueueSession JavaDoc.class.equals(method.getReturnType())) {
232                 QueueSession JavaDoc session = ConnectionFactoryUtils.getTransactionalQueueSession(
233                         (QueueConnectionFactory JavaDoc) getTargetConnectionFactory(), (QueueConnection JavaDoc) this.target,
234                         isSynchedLocalTransactionAllowed());
235                 if (session != null) {
236                     return getCloseSuppressingSessionProxy(session);
237                 }
238             }
239             else if (TopicSession JavaDoc.class.equals(method.getReturnType())) {
240                 TopicSession JavaDoc session = ConnectionFactoryUtils.getTransactionalTopicSession(
241                         (TopicConnectionFactory JavaDoc) getTargetConnectionFactory(), (TopicConnection JavaDoc) this.target,
242                         isSynchedLocalTransactionAllowed());
243                 if (session != null) {
244                     return getCloseSuppressingSessionProxy(session);
245                 }
246             }
247             else if (method.getName().equals("equals")) {
248                 // Only consider equal when proxies are identical.
249
return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
250             }
251             else if (method.getName().equals("hashCode")) {
252                 // Use hashCode of Connection proxy.
253
return new Integer JavaDoc(hashCode());
254             }
255
256             // Invoke method on target Connection.
257
try {
258                 return method.invoke(this.target, args);
259             }
260             catch (InvocationTargetException JavaDoc ex) {
261                 throw ex.getTargetException();
262             }
263         }
264
265         private Session JavaDoc getCloseSuppressingSessionProxy(Session JavaDoc target) {
266             List JavaDoc classes = new ArrayList JavaDoc(3);
267             classes.add(SessionProxy.class);
268             if (target instanceof QueueSession JavaDoc) {
269                 classes.add(QueueSession JavaDoc.class);
270             }
271             if (target instanceof TopicSession JavaDoc) {
272                 classes.add(TopicSession JavaDoc.class);
273             }
274             return (Session JavaDoc) Proxy.newProxyInstance(
275                     SessionProxy.class.getClassLoader(),
276                     (Class JavaDoc[]) classes.toArray(new Class JavaDoc[classes.size()]),
277                     new CloseSuppressingSessionInvocationHandler(target));
278         }
279     }
280
281
282     /**
283      * Invocation handler that suppresses close calls for a transactional JMS Session.
284      */

285     private static class CloseSuppressingSessionInvocationHandler implements InvocationHandler JavaDoc {
286
287         private final Session JavaDoc target;
288
289         public CloseSuppressingSessionInvocationHandler(Session JavaDoc target) {
290             this.target = target;
291         }
292
293         public Object JavaDoc invoke(Object JavaDoc proxy, Method JavaDoc method, Object JavaDoc[] args) throws Throwable JavaDoc {
294             // Invocation on SessionProxy interface coming in...
295

296             if (method.getName().equals("getTargetSession")) {
297                 // Handle getTargetSession method: return underlying Session.
298
return this.target;
299             }
300             else if (method.getName().equals("equals")) {
301                 // Only consider equal when proxies are identical.
302
return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
303             }
304             else if (method.getName().equals("hashCode")) {
305                 // Use hashCode of Connection proxy.
306
return new Integer JavaDoc(hashCode());
307             }
308             else if (method.getName().equals("commit")) {
309                 throw new TransactionInProgressException JavaDoc("Commit call not allowed within a managed transaction");
310             }
311             else if (method.getName().equals("rollback")) {
312                 throw new TransactionInProgressException JavaDoc("Rollback call not allowed within a managed transaction");
313             }
314             else if (method.getName().equals("close")) {
315                 // Handle close method: not to be closed within a transaction.
316
return null;
317             }
318
319             // Invoke method on target Session.
320
try {
321                 return method.invoke(this.target, args);
322             }
323             catch (InvocationTargetException JavaDoc ex) {
324                 throw ex.getTargetException();
325             }
326         }
327     }
328
329 }
330
Popular Tags