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