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