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.source;
12  
13  import org.mule.DefaultMuleEvent;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleException;
16  import org.mule.api.construct.FlowConstruct;
17  import org.mule.api.construct.FlowConstructAware;
18  import org.mule.api.endpoint.OutboundEndpoint;
19  import org.mule.api.lifecycle.Disposable;
20  import org.mule.api.lifecycle.Initialisable;
21  import org.mule.api.lifecycle.InitialisationException;
22  import org.mule.api.lifecycle.Lifecycle;
23  import org.mule.api.lifecycle.LifecycleException;
24  import org.mule.api.lifecycle.Startable;
25  import org.mule.api.lifecycle.Stoppable;
26  import org.mule.api.processor.MessageProcessor;
27  import org.mule.api.source.CompositeMessageSource;
28  import org.mule.api.source.MessageSource;
29  import org.mule.config.i18n.CoreMessages;
30  import org.mule.util.ObjectUtils;
31  
32  import java.util.ArrayList;
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.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  
41  /**
42   * Implementation of {@link CompositeMessageSource} that propagates both injection of {@link FlowConstruct}
43   * and lifecycle to nested {@link MessageSource}s.
44   * <p>
45   * <li>This message source cannot be started without a listener set.
46   * <li>If sources are added when this composie is started they will be started as well.
47   * <li>If a {@link MessageSource} is started in isolation when composite is stopped then messages will be
48   * lost.
49   * <li>Message will only be received from endpoints if the connector is also started.
50   */
51  public class StartableCompositeMessageSource
52      implements CompositeMessageSource, Lifecycle, FlowConstructAware
53  {
54      protected static final Log log = LogFactory.getLog(StartableCompositeMessageSource.class);
55  
56      protected MessageProcessor listener;
57      protected AtomicBoolean initialised = new AtomicBoolean(false);
58      protected AtomicBoolean started = new AtomicBoolean(false);
59      protected final List<MessageSource> sources = Collections.synchronizedList(new ArrayList<MessageSource>());
60      protected AtomicBoolean starting = new AtomicBoolean(false);
61      protected FlowConstruct flowConstruct;
62      private final MessageProcessor internalListener = new InternalMessageProcessor();
63  
64      public void addSource(MessageSource source) throws MuleException
65      {
66          synchronized (sources)
67          {
68              sources.add(source);
69          }
70          source.setListener(internalListener);
71          if (initialised.get())
72          {
73              if (source instanceof FlowConstructAware)
74              {
75                  ((FlowConstructAware) source).setFlowConstruct(flowConstruct);
76              }
77              if (source instanceof Initialisable)
78              {
79                  ((Initialisable) source).initialise();
80              }
81          }
82          if (started.get() && source instanceof Startable)
83          {
84              ((Startable) source).start();
85          }
86      }
87  
88      public void removeSource(MessageSource source) throws MuleException
89      {
90          if (started.get())
91          {
92              if (source instanceof Stoppable)
93              {
94                  ((Stoppable) source).stop();
95              }
96              if (source instanceof Disposable)
97              {
98                  ((Disposable) source).dispose();
99              }
100         }
101         synchronized (sources)
102         {
103             sources.remove(source);
104         }
105     }
106     
107     public void setMessageSources(List<MessageSource> sources) throws MuleException
108     {
109         this.sources.clear();
110         for (MessageSource messageSource : sources)
111         {
112             addSource(messageSource);
113         }
114     }
115     
116     public void initialise() throws InitialisationException
117     {
118         if (listener == null)
119         {
120             throw new InitialisationException(CoreMessages.objectIsNull("listener"), this);
121         }
122         synchronized (sources)
123         {
124             for (MessageSource source : sources)
125             {
126                 if (source instanceof FlowConstructAware)
127                 {
128                     ((FlowConstructAware) source).setFlowConstruct(flowConstruct);
129                 }
130                 if (source instanceof Initialisable)
131                 {
132                     ((Initialisable) source).initialise();
133                 }
134             }
135         }
136         initialised.set(true);
137     }
138 
139     public void start() throws MuleException
140     {
141         if (listener == null)
142         {
143             throw new LifecycleException(CoreMessages.objectIsNull("listener"), this);
144         }
145 
146         synchronized (sources)
147         {
148             starting.set(true);
149             for (MessageSource source : sources)
150             {
151                 if (source instanceof Startable)
152                 {
153                     ((Startable) source).start();
154                 }
155             }
156 
157             started.set(true);
158             starting.set(false);
159         }
160     }
161 
162     public void stop() throws MuleException
163     {
164         synchronized (sources)
165         {
166             for (MessageSource source : sources)
167             {
168                 if (source instanceof Stoppable)
169                 {
170                     ((Stoppable) source).stop();
171                 }
172             }
173 
174             started.set(false);
175         }
176     }
177     
178     public void dispose()
179     {
180         synchronized (sources)
181         {
182             for (MessageSource source : sources)
183             {
184                 if (source instanceof Disposable)
185                 {
186                     ((Disposable) source).dispose();
187                 }
188             }
189         }
190     }
191 
192     public void setListener(MessageProcessor listener)
193     {
194         this.listener = listener;
195     }
196 
197     public void setFlowConstruct(FlowConstruct pattern)
198     {
199         this.flowConstruct = pattern;
200 
201     }
202 
203     @Override
204     public String toString()
205     {
206         return String.format("%s [listener=%s, sources=%s, started=%s]", getClass().getSimpleName(),
207             listener, sources, started);
208     }
209 
210     private class InternalMessageProcessor implements MessageProcessor
211     {
212         public InternalMessageProcessor()
213         {
214             super();
215         }
216 
217         public MuleEvent process(MuleEvent event) throws MuleException
218         {
219             if (started.get() || starting.get())
220             {
221                 // If the next message processor is an outbound router then create outbound event
222                 if (listener instanceof OutboundEndpoint)
223                 {
224                     event = new DefaultMuleEvent(event.getMessage(), (OutboundEndpoint) listener, event.getSession());
225                 }
226                 return listener.process(event);
227             }
228             else
229             {
230                 log.warn(String.format(
231                     "A message was receieved from MessageSource, but message source is stopped. Message will be discarded.%n"
232                                     + "  Message: %s%n" + "  MessageSource:%s", event, this));
233                 return null;
234             }
235 
236         }
237 
238         @Override
239         public String toString()
240         {
241             return ObjectUtils.toString(this);
242         }
243     }
244 }