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