View Javadoc

1   /*
2    * $Id: AbstractSelectiveRouter.java 20592 2010-12-10 03:55:14Z mike.schilling $
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.routing;
12  
13  import java.util.ArrayList;
14  import java.util.Collection;
15  import java.util.Collections;
16  import java.util.List;
17  
18  import org.apache.commons.collections.ListUtils;
19  import org.mule.DefaultMuleEvent;
20  import org.mule.api.MuleContext;
21  import org.mule.api.MuleEvent;
22  import org.mule.api.MuleException;
23  import org.mule.api.MuleRuntimeException;
24  import org.mule.api.construct.FlowConstruct;
25  import org.mule.api.construct.FlowConstructAware;
26  import org.mule.api.context.MuleContextAware;
27  import org.mule.api.endpoint.OutboundEndpoint;
28  import org.mule.api.lifecycle.Disposable;
29  import org.mule.api.lifecycle.Initialisable;
30  import org.mule.api.lifecycle.InitialisationException;
31  import org.mule.api.lifecycle.Lifecycle;
32  import org.mule.api.lifecycle.Startable;
33  import org.mule.api.lifecycle.Stoppable;
34  import org.mule.api.processor.MessageProcessor;
35  import org.mule.api.routing.RoutePathNotFoundException;
36  import org.mule.api.routing.RouterResultsHandler;
37  import org.mule.api.routing.RouterStatisticsRecorder;
38  import org.mule.api.routing.SelectiveRouter;
39  import org.mule.api.routing.filter.Filter;
40  import org.mule.config.i18n.MessageFactory;
41  import org.mule.management.stats.RouterStatistics;
42  
43  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicBoolean;
44  
45  public abstract class AbstractSelectiveRouter
46      implements SelectiveRouter, RouterStatisticsRecorder, Lifecycle, FlowConstructAware, MuleContextAware
47  {
48      private final List<MessageProcessorFilterPair> conditionalMessageProcessors = new ArrayList<MessageProcessorFilterPair>();
49      private MessageProcessor defaultProcessor;
50      private final RouterResultsHandler resultsHandler = new DefaultRouterResultsHandler();
51      private RouterStatistics routerStatistics;
52  
53      final AtomicBoolean initialised = new AtomicBoolean(false);
54      final AtomicBoolean starting = new AtomicBoolean(false);
55      final AtomicBoolean started = new AtomicBoolean(false);
56      private FlowConstruct flowConstruct;
57      private MuleContext muleContext;
58  
59      public AbstractSelectiveRouter()
60      {
61          routerStatistics = new RouterStatistics(RouterStatistics.TYPE_OUTBOUND);
62      }
63  
64      public void setFlowConstruct(FlowConstruct flowConstruct)
65      {
66          this.flowConstruct = flowConstruct;
67      }
68  
69      public void setMuleContext(MuleContext context)
70      {
71          this.muleContext = context;
72      }
73  
74      public void initialise() throws InitialisationException
75      {
76          synchronized (conditionalMessageProcessors)
77          {
78              for (Object o : getLifecycleManagedObjects())
79              {
80                  if (o instanceof FlowConstructAware)
81                  {
82                      ((FlowConstructAware) o).setFlowConstruct(flowConstruct);
83                  }
84                  if (o instanceof MuleContextAware)
85                  {
86                      ((MuleContextAware) o).setMuleContext(muleContext);
87                  }
88                  if (o instanceof Initialisable)
89                  {
90                      ((Initialisable) o).initialise();
91                  }
92              }
93          }
94          initialised.set(true);
95      }
96  
97      public void start() throws MuleException
98      {
99          synchronized (conditionalMessageProcessors)
100         {
101             starting.set(true);
102             for (Object o : getLifecycleManagedObjects())
103             {
104                 if (o instanceof Startable)
105                 {
106                     ((Startable) o).start();
107                 }
108             }
109 
110             started.set(true);
111             starting.set(false);
112         }
113     }
114 
115     public void stop() throws MuleException
116     {
117         synchronized (conditionalMessageProcessors)
118         {
119             for (Object o : getLifecycleManagedObjects())
120             {
121                 if (o instanceof Stoppable)
122                 {
123                     ((Stoppable) o).stop();
124                 }
125             }
126 
127             started.set(false);
128         }
129     }
130 
131     public void dispose()
132     {
133         synchronized (conditionalMessageProcessors)
134         {
135             for (Object o : getLifecycleManagedObjects())
136             {
137                 if (o instanceof Disposable)
138                 {
139                     ((Disposable) o).dispose();
140                 }
141             }
142         }
143     }
144 
145     public void addRoute(MessageProcessor processor, Filter filter)
146     {
147         synchronized (conditionalMessageProcessors)
148         {
149             MessageProcessorFilterPair addedPair = new MessageProcessorFilterPair(processor, filter);
150             conditionalMessageProcessors.add(transitionLifecycleManagedObjectForAddition(addedPair));
151         }
152     }
153 
154     public void removeRoute(MessageProcessor processor)
155     {
156         updateRoute(processor, new RoutesUpdater()
157         {
158             public void updateAt(int index)
159             {
160                 MessageProcessorFilterPair removedPair = conditionalMessageProcessors.remove(index);
161 
162                 transitionLifecycleManagedObjectForRemoval(removedPair);
163             }
164         });
165     }
166 
167     public void updateRoute(final MessageProcessor processor, final Filter filter)
168     {
169         updateRoute(processor, new RoutesUpdater()
170         {
171             public void updateAt(int index)
172             {
173                 MessageProcessorFilterPair addedPair = new MessageProcessorFilterPair(processor, filter);
174 
175                 MessageProcessorFilterPair removedPair = conditionalMessageProcessors.set(index,
176                     transitionLifecycleManagedObjectForAddition(addedPair));
177 
178                 transitionLifecycleManagedObjectForRemoval(removedPair);
179             }
180         });
181     }
182 
183     public void setDefaultRoute(MessageProcessor processor)
184     {
185         defaultProcessor = processor;
186     }
187 
188     public MuleEvent process(MuleEvent event) throws MuleException
189     {
190         Collection<MessageProcessor> selectedProcessors = selectProcessors(event);
191 
192         if (!selectedProcessors.isEmpty())
193         {
194             return routeWithProcessors(selectedProcessors, event);
195         }
196 
197         if (defaultProcessor != null)
198         {
199             return routeWithProcessor(defaultProcessor, event);
200         }
201 
202         if (getRouterStatistics() != null  && getRouterStatistics().isEnabled())
203         {
204             getRouterStatistics().incrementNoRoutedMessage();
205         }
206 
207         throw new RoutePathNotFoundException(
208             MessageFactory.createStaticMessage("Can't process message because no route has been found matching any filter and no default route is defined"),
209             event, this);
210     }
211 
212     /**
213      * @return the processors selected according to the specific router strategy or
214      *         an empty collection (not null).
215      */
216     protected abstract Collection<MessageProcessor> selectProcessors(MuleEvent event);
217 
218     private Collection<?> getLifecycleManagedObjects()
219     {
220         if (defaultProcessor == null)
221         {
222             return conditionalMessageProcessors;
223         }
224 
225         return ListUtils.union(conditionalMessageProcessors, Collections.singletonList(defaultProcessor));
226     }
227 
228     private <O> O transitionLifecycleManagedObjectForAddition(O managedObject)
229     {
230         try
231         {
232             if ((flowConstruct != null) && (managedObject instanceof FlowConstructAware))
233             {
234                 ((FlowConstructAware) managedObject).setFlowConstruct(flowConstruct);
235             }
236 
237             if ((muleContext != null) && (managedObject instanceof MuleContextAware))
238             {
239                 ((MuleContextAware) managedObject).setMuleContext(muleContext);
240             }
241 
242             if ((initialised.get()) && (managedObject instanceof Initialisable))
243             {
244                 ((Initialisable) managedObject).initialise();
245             }
246 
247             if ((started.get()) && (managedObject instanceof Startable))
248             {
249                 ((Startable) managedObject).start();
250             }
251         }
252         catch (MuleException me)
253         {
254             throw new MuleRuntimeException(me);
255         }
256 
257         return managedObject;
258     }
259 
260     private <O> O transitionLifecycleManagedObjectForRemoval(O managedObject)
261     {
262         try
263         {
264             if (managedObject instanceof Stoppable)
265             {
266                 ((Stoppable) managedObject).stop();
267             }
268 
269             if (managedObject instanceof Disposable)
270             {
271                 ((Disposable) managedObject).dispose();
272             }
273         }
274         catch (MuleException me)
275         {
276             throw new MuleRuntimeException(me);
277         }
278 
279         return managedObject;
280     }
281 
282     private MuleEvent routeWithProcessor(MessageProcessor processor, MuleEvent event) throws MuleException
283     {
284         return routeWithProcessors(Collections.singleton(processor), event);
285     }
286 
287     private MuleEvent routeWithProcessors(Collection<MessageProcessor> processors, MuleEvent event)
288         throws MuleException
289     {
290         List<MuleEvent> results = new ArrayList<MuleEvent>();
291 
292         for (MessageProcessor processor : processors)
293         {
294             processEventWithProcessor(event, processor, results);
295         }
296 
297         return resultsHandler.aggregateResults(results, event, event.getMuleContext());
298     }
299 
300     private void processEventWithProcessor(MuleEvent event,
301                                            MessageProcessor processor,
302                                            List<MuleEvent> results) throws MuleException
303     {
304         MuleEvent processedEvent = event;
305 
306         if (processor instanceof OutboundEndpoint)
307         {
308             processedEvent = new DefaultMuleEvent(event.getMessage(), (OutboundEndpoint) processor,
309                 event.getSession());
310         }
311 
312         results.add(processor.process(processedEvent));
313 
314         if (getRouterStatistics() != null && getRouterStatistics().isEnabled())
315         {
316             getRouterStatistics().incrementRoutedMessage(event.getEndpoint());
317         }
318     }
319 
320     protected List<MessageProcessorFilterPair> getConditionalMessageProcessors()
321     {
322         return Collections.unmodifiableList(conditionalMessageProcessors);
323     }
324 
325     private interface RoutesUpdater
326     {
327         void updateAt(int index);
328     }
329 
330     private void updateRoute(MessageProcessor processor, RoutesUpdater routesUpdater)
331     {
332         synchronized (conditionalMessageProcessors)
333         {
334             for (int i = 0; i < conditionalMessageProcessors.size(); i++)
335             {
336                 if (conditionalMessageProcessors.get(i).getMessageProcessor().equals(processor))
337                 {
338                     routesUpdater.updateAt(i);
339                 }
340             }
341         }
342     }
343 
344     public RouterStatistics getRouterStatistics()
345     {
346         return routerStatistics;
347     }
348 
349     public void setRouterStatistics(RouterStatistics routerStatistics)
350     {
351         this.routerStatistics = routerStatistics;
352     }
353 
354     @Override
355     public String toString()
356     {
357         return String.format("%s [flow-construct=%s, started=%s]", getClass().getSimpleName(),
358             flowConstruct != null ? flowConstruct.getName() : null, started);
359     }
360 }