View Javadoc

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