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.mulemq;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.lifecycle.InitialisationException;
11  import org.mule.config.ExceptionHelper;
12  import org.mule.transport.jms.JmsConnector;
13  import org.mule.transport.jms.JmsConstants;
14  import org.mule.transport.jms.i18n.JmsMessages;
15  import org.mule.util.ClassUtils;
16  
17  import java.lang.reflect.Method;
18  import java.util.Hashtable;
19  import java.util.Map;
20  
21  import javax.jms.ConnectionFactory;
22  import javax.jms.JMSException;
23  
24  public class MuleMQJmsConnector extends JmsConnector
25  {
26      public static final String MULEMQ_CONNECTION_FACTORY_CLASS = "com.pcbsys.nirvana.nJMS.ConnectionFactoryImpl";
27  
28      // The default values for the connection factory properties
29      public static final String DEFAULT_REALM_URL = "nsp://localhost:9000";
30      public static final String DEFAULT_BUFFER_OUTPUT = "queued";
31      public static final boolean DEFAULT_SYNC_WRITES = false;
32      public static final int DEFAULT_SYNC_BATCH_SIZE = 50;
33      public static final int DEFAULT_SYNC_TIME = 20;
34      public static final int DEFAULT_GLOBAL_STORE_CAPACITY = 5000;
35      public static final int DEFAULT_MAX_UNACKED_SIZE = 100;
36      public static final boolean DEFAULT_USE_JMS_ENGINE = true;
37      public static final int DEFAULT_QUEUE_WINDOW_SIZE = 100;
38      public static final int DEFAULT_AUTO_ACK_COUNT = 50;
39      public static final boolean DEFAULT_ENABLE_SHARED_DURABLE = false;
40      public static final boolean DEFAULT_RANDOMISE_R_NAMES = false;
41      public static final int DEFAULT_MAX_REDELIVERY = 100;
42      public static final int DEFAULT_MESSAGE_THREAD_POOL_SIZE = 30;
43      public static final boolean DEFAULT_DISC_ON_CLUSTER_FAILURE = true;
44      public static final int DEFAULT_INITIAL_RETRY_COUNT = 2;
45      public static final boolean DEFAULT_RETRY_COMMIT = false;
46      public static final boolean DEFAULT_ENABLE_MULTIPLEXED_CONNECTIONS = false;
47  
48      // properties to be set on the connector all initialised to their respective
49      // default value
50      private String realmURL = DEFAULT_REALM_URL;
51      private String bufferOutput = DEFAULT_BUFFER_OUTPUT;
52      private boolean syncWrites = DEFAULT_SYNC_WRITES;
53      private int syncBatchSize = DEFAULT_SYNC_BATCH_SIZE;
54      private int syncTime = DEFAULT_SYNC_TIME;
55      private int globalStoreCapacity = DEFAULT_GLOBAL_STORE_CAPACITY;
56      private int maxUnackedSize = DEFAULT_MAX_UNACKED_SIZE;
57      private boolean useJMSEngine = DEFAULT_USE_JMS_ENGINE;
58      private int queueWindowSize = DEFAULT_QUEUE_WINDOW_SIZE;
59      private int autoAckCount = DEFAULT_AUTO_ACK_COUNT;
60      private boolean enableSharedDurable = DEFAULT_ENABLE_SHARED_DURABLE;
61      private boolean randomiseRNames = DEFAULT_RANDOMISE_R_NAMES;
62      private int muleMqMaxRedelivery = DEFAULT_MAX_REDELIVERY;
63      private int messageThreadPoolSize = DEFAULT_MESSAGE_THREAD_POOL_SIZE;
64      private boolean discOnClusterFailure = DEFAULT_DISC_ON_CLUSTER_FAILURE;
65      private int initialRetryCount = DEFAULT_INITIAL_RETRY_COUNT;
66      private boolean retryCommit = DEFAULT_RETRY_COMMIT;
67      private boolean enableMultiplexedConnections = DEFAULT_ENABLE_MULTIPLEXED_CONNECTIONS;
68  
69      // property names
70      protected static final String BUFFER_OUTPUT = "BufferOutput";
71      protected static final String SYNC_WRITES = "nirvana.syncWrites";
72      protected static final String SYNC_BATCH_SIZE = "nirvana.syncBatchSize";
73      protected static final String SYNC_TIME = "nirvana.syncTime";
74      protected static final String GLOBAL_STORE_CAPACITY = "nirvana.globalStoreCapacity";
75      protected static final String MAX_UNACKED_SIZE = "nirvana.maxUnackedSize";
76      protected static final String USE_JMS_ENGINE = "nirvana.useJMSEngine";
77      protected static final String QUEUE_WINDOW_SIZE = "nirvana.queueWindowSize";
78      protected static final String AUTO_ACK_COUNT = "nirvana.autoAckCount";
79      protected static final String ENABLE_SHARED_DURABLE = "nirvana.enableSharedDurable";
80      protected static final String RANDOMISE_R_NAMES = "nirvana.randomiseRNames";
81      protected static final String MAX_REDELIVERY = "nirvana.maxRedelivery";
82      protected static final String MESSAGE_THREAD_POOL_SIZE = "nirvana.messageThreadPoolSize";
83      protected static final String DISC_ON_CLUSTER_FAILURE = "nirvana.discOnClusterFailure";
84      protected static final String INITIAL_RETRY_COUNT = "nirvana.initialRetryCount";
85      protected static final String RETRY_COMMIT = "nirvana.retryCommit";
86      protected static final String ENABLE_MULTIPLEXED_CONNECTIONS = "nirvana.enableMultiplexedConnections";
87  
88      public boolean supportJms102bSpec = false;
89  
90      private boolean inCluster = false;
91      
92      public MuleMQJmsConnector(MuleContext context)
93      {
94          super(context);
95          super.setSpecification(JmsConstants.JMS_SPECIFICATION_11);
96      }
97  
98      public boolean isSupportJms102bSpec()
99      {
100         return supportJms102bSpec;
101     }
102 
103     public void setSupportJms102bSpec(boolean supportJms102bSpec)
104     {
105         this.supportJms102bSpec = supportJms102bSpec;
106     }
107 
108     /*
109      * We need to default the specification to 1.1, the xsd defaults to 1.0.2b, we
110      * wouldn't have to do this here if the default was set in JmsConnector only, In
111      * that case setting the default in the constructor would have been sufficient.
112      */
113     @Override
114     public void setSpecification(String specification)
115     {
116         if (!isSupportJms102bSpec() && specification.equals(JmsConstants.JMS_SPECIFICATION_102B))
117         {
118             logger.warn(JmsMessages.errorMuleMqJmsSpecification());
119             specification = JmsConstants.JMS_SPECIFICATION_11;
120         }
121         super.setSpecification(specification);
122     }
123 
124     @Override
125     protected void doInitialise() throws InitialisationException
126     {
127         if (!isSupportJms102bSpec() && getSpecification().equals(JmsConstants.JMS_SPECIFICATION_102B))
128         {
129             throw new InitialisationException(JmsMessages.errorMuleMqJmsSpecification(), this);
130         }
131         super.doInitialise();
132     }
133 
134     @Override
135     protected ConnectionFactory getDefaultConnectionFactory() throws Exception
136     {
137         ConnectionFactory connectionFactory = (ConnectionFactory) ClassUtils.instanciateClass(
138             getMuleMQFactoryClass(), getRealmURL());
139         applyVendorSpecificConnectionFactoryProperties(connectionFactory);
140         return connectionFactory;
141     }
142 
143     private void applyVendorSpecificConnectionFactoryProperties(ConnectionFactory connectionFactory)
144     {
145         // set the properties first on the prop hash table
146         Hashtable<String, Object> props = new Hashtable<String, Object>();
147         props.put(BUFFER_OUTPUT, bufferOutput);
148         props.put(SYNC_WRITES, Boolean.toString(syncWrites));
149         props.put(SYNC_BATCH_SIZE, Integer.toString(syncBatchSize));
150         props.put(SYNC_TIME, Integer.toString(syncTime));
151         props.put(GLOBAL_STORE_CAPACITY, Integer.toString(globalStoreCapacity));
152         props.put(MAX_UNACKED_SIZE, Integer.toString(maxUnackedSize));
153         props.put(USE_JMS_ENGINE, Boolean.toString(useJMSEngine));
154         props.put(QUEUE_WINDOW_SIZE, Integer.toString(queueWindowSize));
155         props.put(AUTO_ACK_COUNT, Integer.toString(autoAckCount));
156         props.put(ENABLE_SHARED_DURABLE, Boolean.toString(enableSharedDurable));
157         props.put(RANDOMISE_R_NAMES, Boolean.toString(randomiseRNames));
158         props.put(MAX_REDELIVERY, Integer.toString(muleMqMaxRedelivery));
159         props.put(MESSAGE_THREAD_POOL_SIZE, Integer.toString(messageThreadPoolSize));
160         props.put(DISC_ON_CLUSTER_FAILURE, Boolean.toString(discOnClusterFailure));
161         props.put(INITIAL_RETRY_COUNT, Integer.toString(initialRetryCount));
162         props.put(RETRY_COMMIT, Boolean.toString(retryCommit));
163         props.put(ENABLE_MULTIPLEXED_CONNECTIONS, Boolean.toString(enableMultiplexedConnections));
164 
165         // if the user used the connectionFactoryProperties map, these will override
166         // the properties on the connector
167         Map<String, Object> connectionFactoryProperties = getConnectionFactoryProperties();
168         if (connectionFactoryProperties != null)
169         {
170             props.putAll(connectionFactoryProperties);
171         }
172 
173         try
174         {
175             // use reflection to set the properties on the connection factory
176             Method setPropertiesMethod = connectionFactory.getClass().getMethod("setProperties",
177                 Hashtable.class);
178             setPropertiesMethod.invoke(connectionFactory, props);
179         }
180         catch (Exception e)
181         {
182             logger.error("Can not set properties on the MuleMQ connection factory " + e);
183         }
184     }
185 
186     // returns the connection factory class name as a string. This method will be
187     // used by getDefaultConnectionFactory() to acquire the appropriate class to
188     // initialise
189     protected String getMuleMQFactoryClass()
190     {
191         return MULEMQ_CONNECTION_FACTORY_CLASS;
192     }
193 
194     public String getRealmURL()
195     {
196         return realmURL;
197     }
198 
199     public void setRealmURL(String realmURL)
200     {
201         this.realmURL = realmURL;
202         if (realmURL != null)
203         {
204             String[] realms = realmURL.split(",");
205             if (realms != null && realms.length > 1)
206             {
207                 this.setInCluster(true);
208             }
209         }
210     }
211 
212     public String getBufferOutput()
213     {
214         return bufferOutput;
215     }
216 
217     public void setBufferOutput(String bufferOutput)
218     {
219         this.bufferOutput = bufferOutput;
220     }
221 
222     public boolean isSyncWrites()
223     {
224         return syncWrites;
225     }
226 
227     public void setSyncWrites(boolean syncWrites)
228     {
229         this.syncWrites = syncWrites;
230     }
231 
232     public int getSyncBatchSize()
233     {
234         return syncBatchSize;
235     }
236 
237     public void setSyncBatchSize(int syncBatchSize)
238     {
239         this.syncBatchSize = syncBatchSize;
240     }
241 
242     public int getSyncTime()
243     {
244         return syncTime;
245     }
246 
247     public void setSyncTime(int syncTime)
248     {
249         this.syncTime = syncTime;
250     }
251 
252     public int getGlobalStoreCapacity()
253     {
254         return globalStoreCapacity;
255     }
256 
257     public void setGlobalStoreCapacity(int globalStoreCapacity)
258     {
259         this.globalStoreCapacity = globalStoreCapacity;
260     }
261 
262     public int getMaxUnackedSize()
263     {
264         return maxUnackedSize;
265     }
266 
267     public void setMaxUnackedSize(int maxUnackedSize)
268     {
269         this.maxUnackedSize = maxUnackedSize;
270     }
271 
272     public boolean isUseJMSEngine()
273     {
274         return useJMSEngine;
275     }
276 
277     public void setUseJMSEngine(boolean useJMSEngine)
278     {
279         this.useJMSEngine = useJMSEngine;
280     }
281 
282     public int getQueueWindowSize()
283     {
284         return queueWindowSize;
285     }
286 
287     public void setQueueWindowSize(int queueWindowSize)
288     {
289         this.queueWindowSize = queueWindowSize;
290     }
291 
292     public int getAutoAckCount()
293     {
294         return autoAckCount;
295     }
296 
297     public void setAutoAckCount(int autoAckCount)
298     {
299         this.autoAckCount = autoAckCount;
300     }
301 
302     public boolean isEnableSharedDurable()
303     {
304         return enableSharedDurable;
305     }
306 
307     public void setEnableSharedDurable(boolean enableSharedDurable)
308     {
309         this.enableSharedDurable = enableSharedDurable;
310     }
311 
312     public boolean isRandomiseRNames()
313     {
314         return randomiseRNames;
315     }
316 
317     public void setRandomiseRNames(boolean randomiseRNames)
318     {
319         this.randomiseRNames = randomiseRNames;
320     }
321 
322     public int getMessageThreadPoolSize()
323     {
324         return messageThreadPoolSize;
325     }
326 
327     public void setMessageThreadPoolSize(int messageThreadPoolSize)
328     {
329         this.messageThreadPoolSize = messageThreadPoolSize;
330     }
331 
332     public boolean isDiscOnClusterFailure()
333     {
334         return discOnClusterFailure;
335     }
336 
337     public void setDiscOnClusterFailure(boolean discOnClusterFailure)
338     {
339         this.discOnClusterFailure = discOnClusterFailure;
340     }
341 
342     public int getInitialRetryCount()
343     {
344         return initialRetryCount;
345     }
346 
347     public void setInitialRetryCount(int initialRetryCount)
348     {
349         this.initialRetryCount = initialRetryCount;
350     }
351 
352     public int getMuleMqMaxRedelivery()
353     {
354         return muleMqMaxRedelivery;
355     }
356 
357     public void setMuleMqMaxRedelivery(int mulqMqMaxRedelivery)
358     {
359         this.muleMqMaxRedelivery = mulqMqMaxRedelivery;
360     }
361 
362     public void setRetryCommit(boolean retryCommit)
363     {
364         this.retryCommit = retryCommit;
365     }
366 
367     public boolean isRetryCommit()
368     {
369         return retryCommit;
370     }
371 
372     public boolean isEnableMultiplexedConnections()
373     {
374         return enableMultiplexedConnections;
375     }
376 
377     public void setEnableMultiplexedConnections(boolean enableMultiplexedConnections)
378     {
379         this.enableMultiplexedConnections = enableMultiplexedConnections;
380     }
381 
382     public boolean isInCluster()
383     {
384         return inCluster;
385     }
386 
387     public void setInCluster(boolean inCluster)
388     {
389         this.inCluster = inCluster;
390     }
391 
392     public void onException(JMSException jmsException)
393     {
394         Throwable th = ExceptionHelper.getRootException(jmsException);
395         if (th == null) th = jmsException;
396         String errMsg = th.getMessage();
397 
398         if (errMsg.contains("Channel is full :"))
399         {
400             if(logger.isWarnEnabled())
401             {
402                 // TODO : externalize strings
403                 StringBuffer msg = new StringBuffer("MuleMQJmsConnector.onException() received exception: ");
404                 msg.append(th.getMessage());
405                 msg.append("Older Messages will be discarded by MULE MQ.To prevent message loss use transacted outbound-endpoint");
406                 msg.append("Refer to 'Queue Capacity' at http://www.mulesoft.org/display/MQ/Configuring+Mule+MQ#ConfiguringMuleMQ-ConfiguringQueues");
407                 // This error does not mean that connection has been closed. Log Capacity
408                 // is full warn and return.
409                 logger.warn(msg.toString(),th);
410             }
411         }
412         else if (this.isInCluster() && errMsg.contains("Disconnected from :"))
413         {
414             // TODO : externalize strings
415             StringBuffer msg = new StringBuffer("MuleMQJmsConnector.onException() received exception: ");
416             msg.append(th.getMessage());
417             msg.append("If using Mule MQ in a cluster Mule ESB will reconnect automatically in a few seconds");
418             // Nothing to do here, log error and return
419             logger.warn(msg.toString(),th);
420         }
421         else if (this.isInCluster() && errMsg.contains("Reconnected to :"))
422         {
423             // TODO : externalize strings
424             StringBuffer msg = new StringBuffer("MuleMQJmsConnector.onException() received exception: ");
425             msg.append(th.getMessage());
426             msg.append("If using Mule MQ in a cluster Mule ESB will reconnect automatically in a few seconds");
427             // Nothing to do here, log message and return
428             logger.warn(msg.toString(),th);
429         }
430         else
431         {
432             // This is connection error in a single node server. Follow regular
433             // connection error logic
434             super.onException(jmsException);
435         }
436     }
437 }