View Javadoc

1   /*
2    * $Id: SedaComponent.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.impl.model.seda;
12  
13  import org.mule.MuleManager;
14  import org.mule.MuleRuntimeException;
15  import org.mule.config.PoolingProfile;
16  import org.mule.config.QueueProfile;
17  import org.mule.config.ThreadingProfile;
18  import org.mule.config.i18n.CoreMessages;
19  import org.mule.impl.FailedToQueueEventException;
20  import org.mule.impl.MuleDescriptor;
21  import org.mule.impl.MuleEvent;
22  import org.mule.impl.model.AbstractComponent;
23  import org.mule.impl.model.DefaultMuleProxy;
24  import org.mule.impl.model.MuleProxy;
25  import org.mule.management.stats.ComponentStatistics;
26  import org.mule.management.stats.SedaComponentStatistics;
27  import org.mule.umo.ComponentException;
28  import org.mule.umo.UMOEvent;
29  import org.mule.umo.UMOException;
30  import org.mule.umo.UMOMessage;
31  import org.mule.umo.lifecycle.InitialisationException;
32  import org.mule.umo.lifecycle.LifecycleException;
33  import org.mule.umo.manager.UMOWorkManager;
34  import org.mule.util.ObjectPool;
35  import org.mule.util.queue.QueueSession;
36  
37  import java.util.ArrayList;
38  import java.util.List;
39  import java.util.NoSuchElementException;
40  
41  import javax.resource.spi.work.Work;
42  import javax.resource.spi.work.WorkEvent;
43  import javax.resource.spi.work.WorkException;
44  import javax.resource.spi.work.WorkListener;
45  import javax.resource.spi.work.WorkManager;
46  
47  /**
48   * A Seda component runs inside a Seda Model and is responsible for managing a Seda
49   * Queue and thread pool for a Mule sevice component. In Seda terms this is
50   * equivilent to a stage.
51   */
52  public class SedaComponent extends AbstractComponent implements Work, WorkListener
53  {
54      public static final String QUEUE_PROFILE_PROPERTY = "queueProfile";
55      public static final String POOLING_PROFILE_PROPERTY = "poolingProfile";
56      /**
57       * Serial version/
58       */
59      private static final long serialVersionUID = 7711976708670893015L;
60  
61      /**
62       * A pool of available Mule proxies. If component pooling has been disabled on the
63       * SEDAModel, this pool will be null and the 'componentProxy' will be used.
64       */
65      protected ObjectPool proxyPool;
66  
67      /**
68       * Is created only if component pooling is turned off on the SEDAModel. In this
69       * scenario all requests are serviced by this component, unless
70       * {@link #componentPerRequest} flag is set on the model.
71       */
72      protected MuleProxy componentProxy;
73  
74      protected UMOWorkManager workManager;
75  
76      protected String descriptorQueueName;
77  
78      /**
79       * The time out used for taking from the Seda Queue.
80       */
81      protected int queueTimeout = 0;
82  
83      /**
84       * Whether component objects should be pooled or a single instance should be
85       * used.
86       */
87      protected boolean enablePooling = true;
88  
89      /**
90       * If this is set to true a new component will be created for every request.
91       */
92      protected boolean componentPerRequest = false;
93  
94      /**
95       * The pooling configuration used when initialising the component described by
96       * this descriptor.
97       */
98      protected PoolingProfile poolingProfile;
99  
100     /**
101      * The queuing profile for events received for this component.
102      */
103     protected QueueProfile queueProfile;
104 
105     /**
106      * Creates a new SEDA component.
107      * 
108      * @param descriptor The descriptor of the component to creat
109      * @param model the model in which the component is registered
110      */
111     public SedaComponent(MuleDescriptor descriptor, SedaModel model)
112     {
113         super(descriptor, model);
114 
115         descriptorQueueName = descriptor.getName() + ".component";
116         queueTimeout = model.getQueueTimeout();
117         enablePooling = model.isEnablePooling();
118         componentPerRequest = model.isComponentPerRequest();
119         poolingProfile = model.getPoolingProfile();
120         queueProfile = model.getQueueProfile();
121     }
122 
123     /**
124      * Initialise the component. The component will first create a Mule UMO from the
125      * UMODescriptor and then initialise a pool based on the attributes in the
126      * UMODescriptor.
127      * 
128      * @throws org.mule.umo.lifecycle.InitialisationException if the component fails
129      *             to initialise
130      * @see org.mule.umo.UMODescriptor
131      */
132     public synchronized void doInitialise() throws InitialisationException
133     {
134         // Create thread pool
135         ThreadingProfile tp = descriptor.getThreadingProfile();
136         workManager = tp.createWorkManager(descriptor.getName());
137 
138         queueProfile = descriptor.getQueueProfile();
139         if (queueProfile == null)
140         {
141             queueProfile = ((SedaModel) model).getQueueProfile();
142         }
143 
144         poolingProfile = descriptor.getPoolingProfile();
145         if (poolingProfile == null)
146         {
147             poolingProfile = ((SedaModel) model).getPoolingProfile();
148         }
149 
150         try
151         {
152             // Setup event Queue (used for VM execution)
153             queueProfile.configureQueue(descriptor.getName());
154         }
155         catch (InitialisationException e)
156         {
157             throw e;
158         }
159         catch (Throwable e)
160         {
161             throw new InitialisationException(
162                 CoreMessages.objectFailedToInitialise("Component Queue"), e, this);
163         }
164     }
165 
166     protected ComponentStatistics createStatistics()
167     {
168         return new SedaComponentStatistics(getName(), descriptor.getThreadingProfile().getMaxThreadsActive(),
169             poolingProfile.getMaxActive());
170     }
171 
172     protected ObjectPool createPool() throws InitialisationException
173     {
174         return getPoolingProfile().getPoolFactory().createPool(descriptor, model,
175             getPoolingProfile());
176     }
177 
178     protected void initialisePool() throws InitialisationException
179     {
180         try
181         {
182             int initPolicy = getPoolingProfile().getInitialisationPolicy();
183             if (initPolicy == PoolingProfile.INITIALISE_ALL)
184             {
185                 int numToBorrow = getPoolingProfile().getMaxActive();
186                 List holderList = new ArrayList(numToBorrow);
187 
188                 try
189                 {
190                     for (int t = 0; t < numToBorrow; t++)
191                     {
192                         holderList.add(proxyPool.borrowObject());
193                     }
194                 }
195                 finally
196                 {
197                     for (int t = 0; t < holderList.size(); t++)
198                     {
199                         Object obj = holderList.get(t);
200                         if (obj != null)
201                         {
202                             try
203                             {
204                                 proxyPool.returnObject(obj);
205                             }
206                             finally
207                             {
208                                 // ignore - nothing we can do
209                             }
210                         }
211                     }
212                 }
213             }
214             else if (initPolicy == PoolingProfile.INITIALISE_ONE)
215             {
216                 Object obj = null;
217                 try
218                 {
219                     obj = proxyPool.borrowObject();
220                 }
221                 finally
222                 {
223                     if (obj != null)
224                     {
225                         proxyPool.returnObject(obj);
226                     }
227                 }
228             }
229 
230             poolInitialised.set(true);
231         }
232         catch (Exception e)
233         {
234             throw new InitialisationException(
235                 CoreMessages.objectFailedToInitialise("Proxy Pool"), e, this);
236         }
237     }
238 
239     protected MuleProxy createComponentProxy() throws InitialisationException
240     {
241         try
242         {
243             Object component = lookupComponent();
244             MuleProxy componentProxy = new DefaultMuleProxy(component, descriptor, model, null);
245             ((SedaComponentStatistics) getStatistics()).setComponentPoolSize(-1);
246             componentProxy.setStatistics(getStatistics());
247             componentProxy.start();
248             return componentProxy;
249         }
250         catch (UMOException e)
251         {
252             throw new InitialisationException(e, this);
253         }
254     }
255 
256     public void doForceStop() throws UMOException
257     {
258         doStop();
259     }
260 
261     public void doStop() throws UMOException
262     {
263         workManager.stop();
264         if (proxyPool != null)
265         {
266             try
267             {
268                 proxyPool.stop();
269                 proxyPool.clearPool();
270             }
271             catch (Exception e)
272             {
273                 // TODO MULE-863: If this is an error, do something about it
274                 logger.error("Failed to stop component pool: " + e.getMessage(), e);
275             }
276             poolInitialised.set(false);
277         }
278         else if (componentProxy != null)
279         {
280             componentProxy.stop();
281         }
282     }
283 
284     public void doStart() throws UMOException
285     {
286 
287         try
288         {
289             // Need to initialise the pool only after all listerner have
290             // been registered and initialised so we need to delay until now
291             if (!poolInitialised.get() && enablePooling)
292             {
293                 proxyPool = this.createPool();
294                 this.initialisePool();
295                 proxyPool.start();
296             }
297             else if (!componentPerRequest)
298             {
299                 componentProxy = createComponentProxy();
300             }
301             workManager.start();
302             workManager.scheduleWork(this, WorkManager.INDEFINITE, null, this);
303         }
304         catch (Exception e)
305         {
306             throw new LifecycleException(
307                 CoreMessages.failedToStart("Component: " + descriptor.getName()), e, this);
308         }
309     }
310 
311     protected void doDispose()
312     {
313 
314         try
315         {
316             // threadPool.awaitTerminationAfterShutdown();
317             if (workManager != null)
318             {
319                 workManager.dispose();
320             }
321         }
322         catch (Exception e)
323         {
324             // TODO MULE-863: So what are we going to do about it?
325             logger.error("Component Thread Pool did not close properly: " + e);
326         }
327         try
328         {
329             if (proxyPool != null)
330             {
331                 proxyPool.clearPool();
332             }
333             else if (componentProxy != null)
334             {
335                 componentProxy.dispose();
336             }
337         }
338         catch (Exception e)
339         {
340             // TODO MULE-863: So what are we going to do about it?
341             logger.error("Proxy Pool did not close properly: " + e);
342         }
343     }
344 
345     protected void doDispatch(UMOEvent event) throws UMOException
346     {
347         // Dispatching event to the component
348         if (stats.isEnabled())
349         {
350             stats.incReceivedEventASync();
351         }
352         if (logger.isDebugEnabled())
353         {
354             logger.debug("Component: " + descriptor.getName() + " has received asynchronous event on: "
355                             + event.getEndpoint().getEndpointURI());
356         }
357 
358         // Block until we can queue the next event
359         try
360         {
361             enqueue(event);
362             if (stats.isEnabled())
363             {
364                 stats.incQueuedEvent();
365             }
366         }
367         catch (Exception e)
368         {
369             FailedToQueueEventException e1 = 
370                 new FailedToQueueEventException(
371                     CoreMessages.interruptedQueuingEventFor(this.getName()), 
372                     event.getMessage(), this, e);
373             handleException(e1);
374         }
375 
376         if (logger.isTraceEnabled())
377         {
378             logger.trace("Event added to queue for: " + descriptor.getName());
379         }
380     }
381 
382     public UMOMessage doSend(UMOEvent event) throws UMOException
383     {
384         UMOMessage result = null;
385         MuleProxy proxy = null;
386         try
387         {
388             proxy = getProxy();
389             if (logger.isDebugEnabled())
390             {
391                 logger.debug(this + " : got proxy for " + event.getId() + " = " + proxy);
392             }
393             result = (UMOMessage) proxy.onCall(event);
394         }
395         catch (UMOException e)
396         {
397             throw e;
398         }
399         catch (Exception e)
400         {
401             throw new ComponentException(event.getMessage(), this, e);
402         }
403         finally
404         {
405             try
406             {
407                 if (proxy != null)
408                 {
409                     if (proxyPool != null)
410                     {
411                         proxyPool.returnObject(proxy);
412                     }
413                     else if (componentPerRequest)
414                     {
415                         proxy.dispose();
416                     }
417                 }
418             }
419             catch (Exception e)
420             {
421                 // noinspection ThrowFromFinallyBlock
422                 throw new ComponentException(event.getMessage(), this, e);
423             }
424 
425             if (proxyPool != null)
426             {
427                 ((SedaComponentStatistics) getStatistics()).setComponentPoolSize(proxyPool.getSize());
428             }
429         }
430         return result;
431     }
432 
433     /**
434      * @return the pool of Mule UMOs initialised in this component
435      */
436     ObjectPool getProxyPool()
437     {
438         return proxyPool;
439     }
440 
441     public int getQueueSize()
442     {
443         QueueSession queueSession = MuleManager.getInstance().getQueueManager().getQueueSession();
444         return queueSession.getQueue(descriptor.getName()).size();
445     }
446 
447     /**
448      * While the component isn't stopped this runs a continuous loop checking for new
449      * events in the queue.
450      */
451     public void run()
452     {
453         MuleEvent event = null;
454         MuleProxy proxy = null;
455         QueueSession queueSession = MuleManager.getInstance().getQueueManager().getQueueSession();
456 
457         while (!stopped.get())
458         {
459             try
460             {
461                 // Wait if the component is paused
462                 paused.whenFalse(null);
463 
464                 // If we're doing a draining stop, read all events from the queue
465                 // before stopping
466                 if (stopping.get())
467                 {
468                     if (queueSession == null || queueSession.getQueue(descriptorQueueName).size() == 0)
469                     {
470                         stopping.set(false);
471                         break;
472                     }
473                 }
474 
475                 event = (MuleEvent) dequeue();
476                 if (event != null)
477                 {
478                     if (stats.isEnabled())
479                     {
480                         stats.decQueuedEvent();
481                     }
482 
483                     if (logger.isDebugEnabled())
484                     {
485                         logger.debug("Component: " + descriptor.getName() + " dequeued event on: "
486                                         + event.getEndpoint().getEndpointURI());
487                     }
488 
489                     proxy = getProxy();
490                     proxy.start();
491                     proxy.onEvent(queueSession, event);
492                     workManager.scheduleWork(proxy, WorkManager.INDEFINITE, null, this);
493                 }
494             }
495             catch (Exception e)
496             {
497                 if (proxy != null && proxyPool != null)
498                 {
499                     try
500                     {
501                         proxyPool.returnObject(proxy);
502                     }
503                     catch (Exception e2)
504                     {
505                         // TODO MULE-863: What should we do here? Die?
506                         logger.info("Failed to return proxy to pool", e2);
507                     }
508                 }
509 
510                 if (e instanceof InterruptedException)
511                 {
512                     stopping.set(false);
513                     break;
514                 }
515                 else if (e instanceof NoSuchElementException)
516                 {
517                     handleException(new ComponentException(CoreMessages.proxyPoolTimedOut(),
518                         (event == null ? null : event.getMessage()), this, e));
519                 }
520                 else if (e instanceof UMOException)
521                 {
522                     handleException(e);
523                 }
524                 else if (e instanceof WorkException)
525                 {
526                     handleException(
527                         new ComponentException(
528                             CoreMessages.eventProcessingFailedFor(descriptor.getName()),
529                             (event == null ? null : event.getMessage()), this, e));
530                 }
531                 else
532                 {
533                     handleException(
534                         new ComponentException(
535                             CoreMessages.failedToGetPooledObject(),
536                             (event == null ? null : event.getMessage()), this, e));
537                 }
538             }
539             finally
540             {
541                 stopping.set(false);
542                 if (proxy != null && componentPerRequest)
543                 {
544                     proxy.dispose();
545                 }
546             }
547         }
548     }
549 
550     /**
551      * The proxy may be one of three types: 1. pooled 2. not pooled 3. per-request
552      */
553     protected MuleProxy getProxy() throws Exception
554     {
555         MuleProxy proxy;
556         if (proxyPool != null)
557         {
558             proxy = (MuleProxy) proxyPool.borrowObject();
559             ((SedaComponentStatistics) getStatistics()).setComponentPoolSize(proxyPool.getSize());
560         }
561         else if (componentPerRequest)
562         {
563             proxy = createComponentProxy();
564         }
565         else
566         {
567             proxy = componentProxy;
568         }
569         proxy.setStatistics(getStatistics());
570         return proxy;
571     }
572     
573     public void release()
574     {
575         stopping.set(false);
576     }
577 
578     protected void enqueue(UMOEvent event) throws Exception
579     {
580         QueueSession session = MuleManager.getInstance().getQueueManager().getQueueSession();
581         session.getQueue(descriptorQueueName).put(event);
582     }
583 
584     protected UMOEvent dequeue() throws Exception
585     {
586         // Wait until an event is available
587         QueueSession queueSession = MuleManager.getInstance().getQueueManager().getQueueSession();
588         return (UMOEvent) queueSession.getQueue(descriptorQueueName).poll(queueTimeout);
589     }
590 
591     public void workAccepted(WorkEvent event)
592     {
593         handleWorkException(event, "workAccepted");
594     }
595 
596     public void workRejected(WorkEvent event)
597     {
598         handleWorkException(event, "workRejected");
599     }
600 
601     public void workStarted(WorkEvent event)
602     {
603         handleWorkException(event, "workStarted");
604     }
605 
606     public void workCompleted(WorkEvent event)
607     {
608         handleWorkException(event, "workCompleted");
609     }
610 
611     protected void handleWorkException(WorkEvent event, String type)
612     {
613         Throwable e;
614 
615         if (event != null && event.getException() != null)
616         {
617             e = event.getException();
618         }
619         else
620         {
621             return;
622         }
623 
624         if (event.getException().getCause() != null)
625         {
626             e = event.getException().getCause();
627         }
628 
629         logger.error("Work caused exception on '" + type + "'. Work being executed was: "
630                         + event.getWork().toString());
631 
632         if (e instanceof Exception)
633         {
634             handleException((Exception) e);
635         }
636         else
637         {
638             throw new MuleRuntimeException(
639                 CoreMessages.componentCausedErrorIs(this.getName()), e);
640         }
641     }
642 
643     public PoolingProfile getPoolingProfile()
644     {
645         return poolingProfile;
646     }
647 
648     public void setPoolingProfile(PoolingProfile poolingProfile)
649     {
650         this.poolingProfile = poolingProfile;
651     }
652 
653     public QueueProfile getQueueProfile()
654     {
655         return queueProfile;
656     }
657 
658     public void setQueueProfile(QueueProfile queueProfile)
659     {
660         this.queueProfile = queueProfile;
661     }
662 }