View Javadoc

1   /*
2    * $Id: ConnectionFactoryWrapper.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.providers.jms.xa;
12  
13  import java.lang.reflect.InvocationHandler;
14  import java.lang.reflect.InvocationTargetException;
15  import java.lang.reflect.Method;
16  import java.lang.reflect.Proxy;
17  
18  import javax.jms.Connection;
19  import javax.jms.ConnectionFactory;
20  import javax.jms.JMSException;
21  import javax.jms.MessageConsumer;
22  import javax.jms.MessageProducer;
23  import javax.jms.QueueConnection;
24  import javax.jms.QueueConnectionFactory;
25  import javax.jms.QueueReceiver;
26  import javax.jms.QueueSender;
27  import javax.jms.QueueSession;
28  import javax.jms.Session;
29  import javax.jms.TopicConnection;
30  import javax.jms.TopicConnectionFactory;
31  import javax.jms.TopicPublisher;
32  import javax.jms.TopicSession;
33  import javax.jms.TopicSubscriber;
34  import javax.jms.XAConnection;
35  import javax.jms.XAConnectionFactory;
36  import javax.jms.XAQueueConnection;
37  import javax.jms.XAQueueConnectionFactory;
38  import javax.jms.XAQueueSession;
39  import javax.jms.XASession;
40  import javax.jms.XATopicConnection;
41  import javax.jms.XATopicConnectionFactory;
42  import javax.jms.XATopicSession;
43  import javax.transaction.Transaction;
44  import javax.transaction.TransactionManager;
45  import javax.transaction.xa.XAResource;
46  
47  import org.apache.commons.logging.Log;
48  import org.apache.commons.logging.LogFactory;
49  
50  public class ConnectionFactoryWrapper
51      implements ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory
52  {
53      /**
54       * logger used by this class
55       */
56      protected static final Log logger = LogFactory.getLog(ConnectionFactoryWrapper.class);
57  
58      protected final Object factory;
59      protected final TransactionManager tm;
60  
61      public ConnectionFactoryWrapper(Object factory, TransactionManager tm)
62      {
63          this.factory = factory;
64          this.tm = tm;
65      }
66  
67      /*
68       * (non-Javadoc)
69       * 
70       * @see javax.jms.ConnectionFactory#createConnection()
71       */
72      public Connection createConnection() throws JMSException
73      {
74          XAConnection xac = ((XAConnectionFactory)factory).createXAConnection();
75          Connection proxy = (Connection)Proxy.newProxyInstance(Connection.class.getClassLoader(),
76              new Class[]{Connection.class}, new ConnectionInvocationHandler(xac));
77          return proxy;
78      }
79  
80      /*
81       * (non-Javadoc)
82       * 
83       * @see javax.jms.ConnectionFactory#createConnection(java.lang.String,
84       *      java.lang.String)
85       */
86      public Connection createConnection(String username, String password) throws JMSException
87      {
88          XAConnection xac = ((XAConnectionFactory)factory).createXAConnection(username, password);
89          Connection proxy = (Connection)Proxy.newProxyInstance(Connection.class.getClassLoader(),
90              new Class[]{Connection.class}, new ConnectionInvocationHandler(xac));
91          return proxy;
92      }
93  
94      /*
95       * (non-Javadoc)
96       * 
97       * @see javax.jms.QueueConnectionFactory#createQueueConnection()
98       */
99      public QueueConnection createQueueConnection() throws JMSException
100     {
101         XAQueueConnection xaqc = ((XAQueueConnectionFactory)factory).createXAQueueConnection();
102         QueueConnection proxy = (QueueConnection)Proxy.newProxyInstance(Connection.class.getClassLoader(),
103             new Class[]{QueueConnection.class}, new ConnectionInvocationHandler(xaqc));
104         return proxy;
105     }
106 
107     /*
108      * (non-Javadoc)
109      * 
110      * @see javax.jms.QueueConnectionFactory#createQueueConnection(java.lang.String,
111      *      java.lang.String)
112      */
113     public QueueConnection createQueueConnection(String username, String password) throws JMSException
114     {
115         XAQueueConnection xaqc = ((XAQueueConnectionFactory)factory).createXAQueueConnection(username,
116             password);
117         QueueConnection proxy = (QueueConnection)Proxy.newProxyInstance(Connection.class.getClassLoader(),
118             new Class[]{QueueConnection.class}, new ConnectionInvocationHandler(xaqc));
119         return proxy;
120     }
121 
122     /*
123      * (non-Javadoc)
124      * 
125      * @see javax.jms.TopicConnectionFactory#createTopicConnection()
126      */
127     public TopicConnection createTopicConnection() throws JMSException
128     {
129         XATopicConnection xatc = ((XATopicConnectionFactory)factory).createXATopicConnection();
130         TopicConnection proxy = (TopicConnection)Proxy.newProxyInstance(Connection.class.getClassLoader(),
131             new Class[]{TopicConnection.class}, new ConnectionInvocationHandler(xatc));
132         return proxy;
133     }
134 
135     /*
136      * (non-Javadoc)
137      * 
138      * @see javax.jms.TopicConnectionFactory#createTopicConnection(java.lang.String,
139      *      java.lang.String)
140      */
141     public TopicConnection createTopicConnection(String username, String password) throws JMSException
142     {
143         XATopicConnection xatc = ((XATopicConnectionFactory)factory).createXATopicConnection(username,
144             password);
145         TopicConnection proxy = (TopicConnection)Proxy.newProxyInstance(Connection.class.getClassLoader(),
146             new Class[]{TopicConnection.class}, new ConnectionInvocationHandler(xatc));
147         return proxy;
148     }
149 
150     public class ConnectionInvocationHandler implements InvocationHandler
151     {
152 
153         private Object xac;
154 
155         public ConnectionInvocationHandler(Object xac)
156         {
157             this.xac = xac;
158         }
159 
160         /**
161          * Can be one of 3 types.
162          * TODO check if we can portably cast it (JMS 1.1 vs 1.0.2b), see Jms102bSupport why
163          * @return underlying XAConnection instance
164          */
165         public Object getTargetObject()
166         {
167             return xac;
168         }
169 
170         /*
171          * (non-Javadoc)
172          * 
173          * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
174          *      java.lang.reflect.Method, java.lang.Object[])
175          */
176         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
177         {
178             if (logger.isDebugEnabled())
179             {
180                 logger.debug("Invoking " + method);
181             }
182             if (method.getName().equals("createSession"))
183             {
184                 XASession xas = ((XAConnection)xac).createXASession();
185                 return Proxy.newProxyInstance(Session.class.getClassLoader(), new Class[]{Session.class},
186                     new SessionInvocationHandler(xas.getSession(), xas.getXAResource()));
187             }
188             else if (method.getName().equals("createQueueSession"))
189             {
190                 XAQueueSession xaqs = ((XAQueueConnection)xac).createXAQueueSession();
191                 return Proxy.newProxyInstance(Session.class.getClassLoader(),
192                     new Class[]{QueueSession.class}, new SessionInvocationHandler(xaqs.getQueueSession(),
193                         xaqs.getXAResource()));
194             }
195             else if (method.getName().equals("createTopicSession"))
196             {
197                 XATopicSession xats = ((XATopicConnection)xac).createXATopicSession();
198                 return Proxy.newProxyInstance(Session.class.getClassLoader(),
199                     new Class[]{TopicSession.class}, new SessionInvocationHandler(xats.getTopicSession(),
200                         xats.getXAResource()));
201             }
202             else
203             {
204                 return method.invoke(xac, args);
205             }
206         }
207 
208         protected class SessionInvocationHandler implements InvocationHandler
209         {
210 
211             private Object session;
212             private Object xares;
213             private Transaction tx;
214 
215             public SessionInvocationHandler(Object session, Object xares)
216             {
217                 this.session = session;
218                 this.xares = xares;
219             }
220 
221             /*
222              * (non-Javadoc)
223              * 
224              * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
225              *      java.lang.reflect.Method, java.lang.Object[])
226              */
227             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
228             {
229                 if (logger.isDebugEnabled())
230                 {
231                     logger.debug("Invoking " + method);
232                 }
233                 Object result = method.invoke(session, args);
234 
235                 if (result instanceof TopicSubscriber)
236                 {
237                     result = Proxy.newProxyInstance(Session.class.getClassLoader(),
238                         new Class[]{TopicSubscriber.class}, new ConsumerProducerInvocationHandler(result));
239                 }
240                 else if (result instanceof QueueReceiver)
241                 {
242                     result = Proxy.newProxyInstance(Session.class.getClassLoader(),
243                         new Class[]{QueueReceiver.class}, new ConsumerProducerInvocationHandler(result));
244                 }
245                 else if (result instanceof MessageConsumer)
246                 {
247                     result = Proxy.newProxyInstance(Session.class.getClassLoader(),
248                         new Class[]{MessageConsumer.class}, new ConsumerProducerInvocationHandler(result));
249                 }
250                 else if (result instanceof TopicPublisher)
251                 {
252                     result = Proxy.newProxyInstance(Session.class.getClassLoader(),
253                         new Class[]{TopicPublisher.class}, new ConsumerProducerInvocationHandler(result));
254                 }
255                 else if (result instanceof QueueSender)
256                 {
257                     result = Proxy.newProxyInstance(Session.class.getClassLoader(),
258                         new Class[]{QueueSender.class}, new ConsumerProducerInvocationHandler(result));
259                 }
260                 else if (result instanceof MessageProducer)
261                 {
262                     result = Proxy.newProxyInstance(Session.class.getClassLoader(),
263                         new Class[]{MessageProducer.class}, new ConsumerProducerInvocationHandler(result));
264                 }
265                 return result;
266             }
267 
268             protected void enlist() throws Exception
269             {
270                 if (logger.isDebugEnabled())
271                 {
272                     logger.debug("Enlistment request: " + this);
273                 }
274                 if (tx == null && tm != null)
275                 {
276                     tx = tm.getTransaction();
277                     if (tx != null)
278                     {
279                         if (logger.isDebugEnabled())
280                         {
281                             logger.debug("Enlisting resource in xa transaction: " + xares);
282                         }
283                         XAResource xares = (XAResource)Proxy.newProxyInstance(XAResource.class
284                             .getClassLoader(), new Class[]{XAResource.class},
285                             new XAResourceInvocationHandler());
286                         tx.enlistResource(xares);
287                     }
288                 }
289             }
290 
291             protected class XAResourceInvocationHandler implements InvocationHandler
292             {
293 
294                 /*
295                  * (non-Javadoc)
296                  * 
297                  * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
298                  *      java.lang.reflect.Method, java.lang.Object[])
299                  */
300                 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
301                 {
302                     try
303                     {
304                         if (logger.isDebugEnabled())
305                         {
306                             logger.debug("Invoking " + method);
307                         }
308                         if (method.getName().equals("end"))
309                         {
310                             tx = null;
311                         }
312 
313                         /*
314                          * This has been added, since JOTM checks if the resource has
315                          * actually been enlisted & tries to compare two proxy
316                          * classes with eachother. Since the equals method is
317                          * proxied, it will effectivly compare a proxy with the
318                          * ConnectionFactory & this will fail. To solve this, if the
319                          * object passed as a parameter is actually another proxy,
320                          * call equals on the proxy passing this class as a
321                          * parameter, effictively we would be comparing the two
322                          * proxied classes.
323                          */
324                         if (method.getName().equals("equals"))
325                         {
326                             if (Proxy.isProxyClass(args[0].getClass()))
327                             {
328                                 return new Boolean(args[0].equals(this));
329                             }
330                             else
331                             {
332                                 return new Boolean(this.equals(args[0]));
333                             }
334                         }
335 
336                         return method.invoke(xares, args);
337                     }
338                     catch (InvocationTargetException e)
339                     {
340                         throw e.getCause();
341                     }
342                 }
343 
344             }
345 
346             protected class ConsumerProducerInvocationHandler implements InvocationHandler
347             {
348 
349                 private Object target;
350 
351                 public ConsumerProducerInvocationHandler(Object target)
352                 {
353                     this.target = target;
354                 }
355 
356                 /*
357                  * (non-Javadoc)
358                  * 
359                  * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
360                  *      java.lang.reflect.Method, java.lang.Object[])
361                  */
362                 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
363                 {
364                     if (logger.isDebugEnabled())
365                     {
366                         logger.debug("Invoking " + method);
367                     }
368                     if (!method.getName().equals("close"))
369                     {
370                         enlist();
371                     }
372                     return method.invoke(target, args);
373                 }
374             }
375 
376         }
377     }
378 
379 }