View Javadoc

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