View Javadoc

1   /*
2    * $Id: ActiveMQJmsConnector.java 20321 2010-11-24 15:21:24Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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.transport.jms.activemq;
12  
13  import org.mule.api.MuleContext;
14  import org.mule.transport.ConnectException;
15  import org.mule.transport.jms.JmsConnector;
16  import org.mule.transport.jms.xa.TargetInvocationHandler;
17  import org.mule.util.ClassUtils;
18  
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  import java.lang.reflect.Proxy;
22  
23  import javax.jms.Connection;
24  import javax.jms.ConnectionFactory;
25  import javax.jms.JMSException;
26  
27  /**
28   * ActiveMQ 4.x-specific JMS connector.
29   */
30  public class ActiveMQJmsConnector extends JmsConnector
31  {
32      public static final String ACTIVEMQ_CONNECTION_FACTORY_CLASS = "org.apache.activemq.ActiveMQConnectionFactory";
33      public static final String DEFAULT_BROKER_URL = "vm://localhost?broker.persistent=false&broker.useJmx=false";
34  
35      private String brokerURL = DEFAULT_BROKER_URL;
36  
37      /**
38       * Constructs a new ActiveMQJmsConnector.
39       */
40      public ActiveMQJmsConnector(MuleContext context)
41      {
42          super(context);
43          setEagerConsumer(false);
44          // TODO MULE-1409 better support for ActiveMQ 4.x temp destinations
45      }
46  
47      protected ConnectionFactory getDefaultConnectionFactory() throws Exception
48      {
49          ConnectionFactory connectionFactory = (ConnectionFactory)
50                  ClassUtils.instanciateClass(ACTIVEMQ_CONNECTION_FACTORY_CLASS, getBrokerURL());
51          applyVendorSpecificConnectionFactoryProperties(connectionFactory);
52          return connectionFactory;
53      }
54  
55      protected void applyVendorSpecificConnectionFactoryProperties(ConnectionFactory connectionFactory)
56      {
57          try
58          {
59              Method getRedeliveryPolicyMethod = connectionFactory.getClass().getMethod("getRedeliveryPolicy");
60              Object redeliveryPolicy = getRedeliveryPolicyMethod.invoke(connectionFactory);
61              Method setMaximumRedeliveriesMethod = redeliveryPolicy.getClass().getMethod("setMaximumRedeliveries", Integer.TYPE);
62              // redelivery = deliveryCount - 1, but AMQ is considering the first delivery attempt as a redelivery (wrong!). adjust for it
63              setMaximumRedeliveriesMethod.invoke(redeliveryPolicy, getMaxRedelivery() + 1);
64          }
65          catch (Exception e)
66          {
67              logger.error("Can not set MaxRedelivery parameter to RedeliveryPolicy " + e);
68          }
69      }
70  
71      /**
72       * Will additionally try to cleanup the ActiveMq connection, otherwise there's a deadlock on shutdown.
73       */
74      protected void doDisconnect() throws ConnectException
75      {
76          try
77          {
78              Connection connection = getConnection();
79              if (connection == null)
80              {
81                  return;
82              }
83  
84              final Class clazz = connection.getClass();
85              Method cleanupMethod;
86              if (Proxy.isProxyClass(clazz))
87              {
88                  TargetInvocationHandler handler =
89                          (TargetInvocationHandler) Proxy.getInvocationHandler(connection);
90                  // this is really an XA connection, bypass the java.lang.reflect.Proxy as it
91                  // can't delegate to non-interfaced methods (like proprietary 'cleanup' one)
92                  // TODO check if CGlib will manage to enhance the AMQ connection class,
93                  // there are no final methods, but a number of private ones, though
94                  connection = (Connection) handler.getTargetObject();
95                  Class realConnectionClass = connection.getClass();
96                  cleanupMethod = realConnectionClass.getMethod("cleanup", (Class[])null);
97              }
98              else
99              {
100                 cleanupMethod = clazz.getMethod("cleanup", (Class[])null);
101             }
102 
103             try
104             {
105                 if (cleanupMethod != null)
106                 {
107                     cleanupMethod.invoke(connection, (Object[])null);
108                 }
109             }
110             catch (InvocationTargetException ex)
111             {
112                 logger.warn("Exception cleaning up JMS connection", ex);        
113             }
114             finally
115             {
116                 try
117                 {
118                     connection.close();
119                 }
120                 catch (JMSException ex)
121                 {
122                     logger.warn("Exception closing JMS connection", ex);
123                 }
124             }
125         }
126         catch (Exception e)
127         {
128             throw new ConnectException(e, this);
129         }
130         finally
131         {
132             //Is this necessary? It causes a NPE in certain situations
133             setConnection(null);
134         }
135     }
136 
137     public String getBrokerURL()
138     {
139         return brokerURL;
140     }
141 
142     public void setBrokerURL(String brokerURL)
143     {
144         this.brokerURL = brokerURL;
145     }
146 }