View Javadoc

1   /*
2    * $Id: TransactionalQueueManager.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.util.queue;
12  
13  import org.mule.api.MuleContext;
14  import org.mule.api.context.MuleContextAware;
15  import org.mule.util.queue.QueuePersistenceStrategy.Holder;
16  import org.mule.util.xa.AbstractTransactionContext;
17  import org.mule.util.xa.AbstractXAResourceManager;
18  import org.mule.util.xa.ResourceManagerException;
19  import org.mule.util.xa.ResourceManagerSystemException;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.LinkedList;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.transaction.xa.XAResource;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  
33  /**
34   * The Transactional Queue Manager is responsible for creating and Managing
35   * transactional Queues. Queues can also be persistent by setting a persistence
36   * strategy on the manager. Default straties are provided for Memory, Jounaling,
37   * Cache and File.
38   */
39  public class TransactionalQueueManager extends AbstractXAResourceManager implements QueueManager, MuleContextAware
40  {
41  
42      private static Log logger = LogFactory.getLog(TransactionalQueueManager.class);
43  
44      private Map<String, QueueInfo> queues = new HashMap<String, QueueInfo>();
45  
46      private QueuePersistenceStrategy memoryPersistenceStrategy = new MemoryPersistenceStrategy();
47      private QueuePersistenceStrategy persistenceStrategy;
48  
49      private QueueConfiguration defaultQueueConfiguration = new QueueConfiguration(false);
50      private MuleContext muleContext;
51  
52      public void setMuleContext(MuleContext context)
53      {
54          this.muleContext = context;
55      }
56  
57      public MuleContext getMuleContext()
58      {
59          return muleContext;
60      }
61  
62      public synchronized QueueSession getQueueSession()
63      {
64          return new TransactionalQueueSession(this, this);
65      }
66  
67      public synchronized void setDefaultQueueConfiguration(QueueConfiguration config)
68      {
69          this.defaultQueueConfiguration = config;
70      }
71  
72      public synchronized void setQueueConfiguration(String queueName, QueueConfiguration config)
73      {
74          getQueue(queueName).config = config;
75      }
76  
77      protected synchronized QueueInfo getQueue(String name)
78      {
79          QueueInfo q = queues.get(name);
80          if (q == null)
81          {
82              q = new QueueInfo();
83              q.name = name;
84              q.list = new LinkedList();
85              q.config = defaultQueueConfiguration;
86              queues.put(name, q);
87          }
88          return q;
89      }
90  
91      protected Log getLogger()
92      {
93          return logger;
94      }
95  
96      protected void doStart() throws ResourceManagerSystemException
97      {
98          if (persistenceStrategy != null)
99          {
100             try
101             {
102                 persistenceStrategy.open();
103             }
104             catch (IOException e)
105             {
106                 throw new ResourceManagerSystemException(e);
107             }
108         }
109     }
110 
111     protected boolean shutdown(int mode, long timeoutMSecs)
112     {
113         try
114         {
115             if (persistenceStrategy != null)
116             {
117                 persistenceStrategy.close();
118             }
119         }
120         catch (IOException e)
121         {
122             // TODO MULE-863: What should we really do?
123             logger.error("Error closing persistent store", e);
124         }
125         // Clear queues on shutdown to avoid duplicate entries on warm restarts (MULE-3678)
126         synchronized (this)
127         {
128             queues.clear();
129         }
130         return super.shutdown(mode, timeoutMSecs);
131     }
132 
133     protected void recover() throws ResourceManagerSystemException
134     {
135         if (persistenceStrategy != null)
136         {
137             try
138             {
139                 List msgs = persistenceStrategy.restore();
140                 for (Object msg : msgs)
141                 {
142                     Holder h = (Holder) msg;
143                     getQueue(h.getQueue()).putNow(h.getId());
144                 }
145             }
146             catch (Exception e)
147             {
148                 throw new ResourceManagerSystemException(e);
149             }
150         }
151     }
152 
153     protected AbstractTransactionContext createTransactionContext(Object session)
154     {
155         return new QueueTransactionContext();
156     }
157 
158     protected void doBegin(AbstractTransactionContext context)
159     {
160         // Nothing special to do
161     }
162 
163     protected int doPrepare(AbstractTransactionContext context)
164     {
165         return XAResource.XA_OK;
166     }
167 
168     protected void doCommit(AbstractTransactionContext context) throws ResourceManagerException
169     {
170         QueueTransactionContext ctx = (QueueTransactionContext) context;
171         try
172         {
173             if (ctx.added != null)
174             {
175                 for (Object o : ctx.added.entrySet())
176                 {
177                     Map.Entry entry = (Map.Entry) o;
178                     QueueInfo queue = (QueueInfo) entry.getKey();
179                     List queueAdded = (List) entry.getValue();
180                     if (queueAdded != null && queueAdded.size() > 0)
181                     {
182                         for (Object object : queueAdded)
183                         {
184                             Object id = doStore(queue, object);
185                             queue.putNow(id);
186                         }
187                     }
188                 }
189             }
190             if (ctx.removed != null)
191             {
192                 for (Object o : ctx.removed.entrySet())
193                 {
194                     Map.Entry entry = (Map.Entry) o;
195                     QueueInfo queue = (QueueInfo) entry.getKey();
196                     List queueRemoved = (List) entry.getValue();
197                     if (queueRemoved != null && queueRemoved.size() > 0)
198                     {
199                         for (Object id : queueRemoved)
200                         {
201                             doRemove(queue, id);
202                         }
203                     }
204                 }
205             }
206         }
207         catch (Exception e)
208         {
209             // throw new ResourceManagerException("Could not commit
210             // transaction", e);
211             // TODO: add an i18n Message
212             throw new ResourceManagerException(e);
213         }
214         finally
215         {
216             ctx.added = null;
217             ctx.removed = null;
218         }
219     }
220 
221     protected Object doStore(QueueInfo queue, Object object) throws IOException
222     {
223         QueuePersistenceStrategy ps = (queue.config.persistent)
224                         ? persistenceStrategy : memoryPersistenceStrategy;
225         Object id = ps.store(queue.name, object);
226         return id;
227     }
228 
229     protected void doRemove(QueueInfo queue, Object id) throws IOException
230     {
231         QueuePersistenceStrategy ps = (queue.config.persistent)
232                         ? persistenceStrategy : memoryPersistenceStrategy;
233         ps.remove(queue.name, id);
234     }
235 
236     protected Object doLoad(QueueInfo queue, Object id) throws IOException
237     {
238         QueuePersistenceStrategy ps = (queue.config.persistent)
239                         ? persistenceStrategy : memoryPersistenceStrategy;
240         Object obj = ps.load(queue.name, id);
241         return obj;
242     }
243 
244     protected void doRollback(AbstractTransactionContext context) throws ResourceManagerException
245     {
246         QueueTransactionContext ctx = (QueueTransactionContext) context;
247         if (ctx.removed != null)
248         {
249             for (Object o : ctx.removed.entrySet())
250             {
251                 Map.Entry entry = (Map.Entry) o;
252                 QueueInfo queue = (QueueInfo) entry.getKey();
253                 List queueRemoved = (List) entry.getValue();
254                 if (queueRemoved != null && queueRemoved.size() > 0)
255                 {
256                     for (Object id : queueRemoved)
257                     {
258                         queue.putNow(id);
259                     }
260                 }
261             }
262         }
263         ctx.added = null;
264         ctx.removed = null;
265     }
266 
267     protected class QueueTransactionContext extends AbstractTransactionContext
268     {
269         protected Map added;
270         protected Map removed;
271 
272         @SuppressWarnings("unchecked")
273         public boolean offer(QueueInfo queue, Object item, long timeout) throws InterruptedException
274         {
275             readOnly = false;
276             if (added == null)
277             {
278                 added = new HashMap();
279             }
280             List queueAdded = (List) added.get(queue);
281             if (queueAdded == null)
282             {
283                 queueAdded = new ArrayList();
284                 added.put(queue, queueAdded);
285             }
286             // wait for enough room
287             if (queue.offer(null, queueAdded.size(), timeout))
288             {
289                 queueAdded.add(item);
290                 return true;
291             }
292             else
293             {
294                 return false;
295             }
296         }
297 
298         @SuppressWarnings("unchecked")
299         public Object poll(QueueInfo queue, long timeout) throws IOException, InterruptedException
300         {
301             readOnly = false;
302             if (added != null)
303             {
304                 List queueAdded = (List)added.get(queue);
305                 if (queueAdded != null)
306                 {
307                     return queueAdded.remove(queueAdded.size() - 1);
308                 }
309             }
310             Object o;
311             try
312             {
313                 o = queue.poll(timeout);
314             }
315             catch (InterruptedException e)
316             {
317                 if (muleContext.isStopping())
318                 {
319                     throw e;
320                 }
321                 // if disposing, ignore
322                 return null;
323             }
324             if (o != null)
325             {
326                 if (removed == null)
327                 {
328                     removed = new HashMap();
329                 }
330                 List queueRemoved = (List) removed.get(queue);
331                 if (queueRemoved == null)
332                 {
333                     queueRemoved = new ArrayList();
334                     removed.put(queue, queueRemoved);
335                 }
336                 queueRemoved.add(o);
337                 o = doLoad(queue, o);
338             }
339             return o;
340         }
341 
342         public Object peek(QueueInfo queue) throws IOException, InterruptedException
343         {
344             readOnly = false;
345             if (added != null)
346             {
347                 List queueAdded = (List) added.get(queue);
348                 if (queueAdded != null)
349                 {
350                     return queueAdded.get(queueAdded.size() - 1);
351                 }
352             }
353             Object o = queue.peek();
354             if (o != null)
355             {
356                 o = doLoad(queue, o);
357             }
358             return o;
359         }
360 
361         public int size(QueueInfo queue)
362         {
363             int sz = queue.list.size();
364             if (added != null)
365             {
366                 List queueAdded = (List) added.get(queue);
367                 if (queueAdded != null)
368                 {
369                     sz += queueAdded.size();
370                 }
371             }
372             return sz;
373         }
374 
375     }
376 
377     public QueuePersistenceStrategy getPersistenceStrategy()
378     {
379         return persistenceStrategy;
380     }
381 
382     public void setPersistenceStrategy(QueuePersistenceStrategy persistenceStrategy)
383     {
384         if (operationMode != OPERATION_MODE_STOPPED)
385         {
386             throw new IllegalStateException();
387         }
388         this.persistenceStrategy = persistenceStrategy;
389     }
390 
391     public QueuePersistenceStrategy getMemoryPersistenceStrategy()
392     {
393         return memoryPersistenceStrategy;
394     }
395 
396     public void setMemoryPersistenceStrategy(QueuePersistenceStrategy memoryPersistenceStrategy)
397     {
398         if (operationMode != OPERATION_MODE_STOPPED)
399         {
400             throw new IllegalStateException();
401         }
402         this.memoryPersistenceStrategy = memoryPersistenceStrategy;
403     }
404 
405 }