1
2
3
4
5
6
7
8
9
10
11 package org.mule.processor;
12
13 import org.mule.DefaultMuleEvent;
14 import org.mule.api.MessagingException;
15 import org.mule.api.MuleContext;
16 import org.mule.api.MuleEvent;
17 import org.mule.api.MuleException;
18 import org.mule.api.NamedObject;
19 import org.mule.api.context.WorkManager;
20 import org.mule.api.context.WorkManagerSource;
21 import org.mule.api.exception.MessagingExceptionHandler;
22 import org.mule.api.exception.SystemExceptionHandler;
23 import org.mule.api.lifecycle.InitialisationException;
24 import org.mule.api.lifecycle.Lifecycle;
25 import org.mule.api.lifecycle.LifecycleException;
26 import org.mule.api.lifecycle.LifecycleState;
27 import org.mule.api.processor.MessageProcessor;
28 import org.mule.api.service.FailedToQueueEventException;
29 import org.mule.config.QueueProfile;
30 import org.mule.config.i18n.CoreMessages;
31 import org.mule.config.i18n.MessageFactory;
32 import org.mule.management.stats.QueueStatistics;
33 import org.mule.service.Pausable;
34 import org.mule.util.concurrent.WaitableBoolean;
35 import org.mule.util.queue.Queue;
36 import org.mule.util.queue.QueueSession;
37 import org.mule.work.AbstractMuleEventWork;
38 import org.mule.work.MuleWorkManager;
39
40 import java.text.MessageFormat;
41
42 import javax.resource.spi.work.Work;
43 import javax.resource.spi.work.WorkException;
44 import javax.resource.spi.work.WorkListener;
45
46
47
48
49
50 public class SedaStageInterceptingMessageProcessor extends OptionalAsyncInterceptingMessageProcessor
51 implements WorkListener, Work, Lifecycle
52 {
53 protected static final String QUEUE_NAME_PREFIX = "seda.queue";
54
55 protected QueueProfile queueProfile;
56 protected int queueTimeout;
57 protected LifecycleState lifecycleState;
58 protected QueueStatistics queueStatistics;
59 protected MuleContext muleContext;
60 protected String name;
61
62 protected Queue queue;
63 private WaitableBoolean queueDraining = new WaitableBoolean(false);
64
65 public SedaStageInterceptingMessageProcessor(String name,
66 QueueProfile queueProfile,
67 int queueTimeout,
68 WorkManagerSource workManagerSource,
69 LifecycleState lifecycleState,
70 QueueStatistics queueStatistics,
71 MuleContext muleContext)
72 {
73 super(workManagerSource);
74 this.name = name;
75 this.queueProfile = queueProfile;
76 this.queueTimeout = queueTimeout;
77 this.lifecycleState = lifecycleState;
78 this.queueStatistics = queueStatistics;
79 this.muleContext = muleContext;
80 }
81
82 @Deprecated
83 public SedaStageInterceptingMessageProcessor(String name,
84 QueueProfile queueProfile,
85 int queueTimeout,
86 WorkManagerSource workManagerSource,
87 boolean doThreading,
88 LifecycleState lifecycleState,
89 QueueStatistics queueStatistics,
90 MuleContext muleContext)
91 {
92 this(name, queueProfile, queueTimeout, workManagerSource, lifecycleState, queueStatistics,
93 muleContext);
94 this.doThreading = doThreading;
95 }
96
97 @Override
98 protected void processNextAsync(MuleEvent event) throws MuleException
99 {
100 try
101 {
102 if (isStatsEnabled())
103 {
104 queueStatistics.incQueuedEvent();
105 }
106 enqueue(event);
107 }
108 catch (Exception e)
109 {
110 throw new FailedToQueueEventException(
111 CoreMessages.interruptedQueuingEventFor(getStageDescription()), event, e);
112 }
113
114 if (logger.isTraceEnabled())
115 {
116 logger.trace("MuleEvent added to queue for: " + getStageDescription());
117 }
118 }
119
120 protected boolean isStatsEnabled()
121 {
122 return queueStatistics != null && queueStatistics.isEnabled();
123 }
124
125 protected void enqueue(MuleEvent event) throws Exception
126 {
127 if (logger.isDebugEnabled())
128 {
129 logger.debug(MessageFormat.format("{1}: Putting event on queue {2}", queue.getName(),
130 getStageDescription(), event));
131 }
132 queue.put(event);
133 }
134
135 protected MuleEvent dequeue() throws Exception
136 {
137 if (queue == null)
138 {
139 return null;
140 }
141 if (logger.isTraceEnabled())
142 {
143 logger.trace(MessageFormat.format("{0}: Polling queue {1}, timeout = {2}", getStageName(),
144 getStageDescription(), queueTimeout));
145 }
146
147 MuleEvent event = (MuleEvent)queue.poll(queueTimeout);
148
149
150 if(event!=null && lifecycleState.isPhaseComplete(Pausable.PHASE_NAME))
151 {
152 queue.untake(event);
153 return null;
154 }
155 return event;
156 }
157
158 private class SedaStageWorker extends AbstractMuleEventWork
159 {
160 public SedaStageWorker(MuleEvent event)
161 {
162 super(event);
163 }
164
165 @Override
166 protected void doRun()
167 {
168 try
169 {
170 processNextTimed(event);
171 }
172 catch (Exception e)
173 {
174 event.getSession().setValid(false);
175 MessagingExceptionHandler exceptionListener = event.getFlowConstruct().getExceptionListener();
176 if (e instanceof MessagingException)
177 {
178 exceptionListener.handleException(e, event);
179 }
180 else
181 {
182 exceptionListener.handleException(new MessagingException(
183 CoreMessages.eventProcessingFailedFor(getStageDescription()), event, e), event);
184 }
185 }
186 }
187 }
188
189
190
191
192
193 public void run()
194 {
195 DefaultMuleEvent event = null;
196 QueueSession queueSession = muleContext.getQueueManager().getQueueSession();
197
198 while (!lifecycleState.isStopped())
199 {
200 try
201 {
202
203 if (lifecycleState.isPhaseComplete(Pausable.PHASE_NAME))
204 {
205 waitIfPaused();
206
207
208 if (lifecycleState.isStopping())
209 {
210 queueDraining.set(true);
211 if (!isQueuePersistent() && (queueSession != null && getQueueSize() > 0))
212 {
213
214
215 logger.warn(CoreMessages.stopPausedSedaStageNonPeristentQueueMessageLoss(
216 getQueueSize(), getQueueName()));
217 }
218 queueDraining.set(false);
219 break;
220 }
221 }
222
223
224
225 if (lifecycleState.isStopping())
226 {
227 if (isQueuePersistent() || queueSession == null || getQueueSize() <= 0)
228 {
229 queueDraining.set(false);
230 break;
231 }
232 }
233
234 event = (DefaultMuleEvent) dequeue();
235 }
236 catch (InterruptedException ie)
237 {
238 queueDraining.set(false);
239 break;
240 }
241 catch (Exception e)
242 {
243 SystemExceptionHandler exceptionListener = muleContext.getExceptionListener();
244 if (e instanceof MuleException)
245 {
246 exceptionListener.handleException(e);
247 }
248 else
249 {
250 exceptionListener.handleException(new MessagingException(
251 CoreMessages.eventProcessingFailedFor(getStageDescription()),
252 event, e));
253 }
254 }
255
256 if (event != null)
257 {
258 try
259 {
260 if (isStatsEnabled())
261 {
262 queueStatistics.decQueuedEvent();
263 }
264
265 if (logger.isDebugEnabled())
266 {
267 logger.debug(MessageFormat.format("{0}: Dequeued event from {1}",
268 getStageDescription(), getQueueName()));
269 }
270 Work work = new SedaStageWorker(event);
271 if (doThreading)
272 {
273 workManagerSource.getWorkManager().scheduleWork(work, WorkManager.INDEFINITE, null,
274 this);
275 }
276 else
277 {
278 work.run();
279 }
280 }
281 catch (Exception e)
282 {
283 event.getFlowConstruct().getExceptionListener().handleException(e, event);
284 }
285 }
286 }
287 }
288
289
290 protected boolean isQueuePersistent()
291 {
292 return queueProfile.isPersistent();
293 }
294
295 public int getQueueSize()
296 {
297 return queue.size();
298 }
299
300 protected String getQueueName()
301 {
302 return String.format("%s(%s)", QUEUE_NAME_PREFIX, getStageName());
303 }
304
305 protected String getStageName()
306 {
307 if (name != null)
308 {
309 return name;
310 }
311 else if (next instanceof NamedObject)
312 {
313 return ((NamedObject) next).getName();
314 }
315 else
316 {
317 return String.format("%s.%s", next.getClass().getName(), next.hashCode());
318 }
319 }
320
321 protected String getStageDescription()
322 {
323 return "SEDA Stage " + getStageName();
324 }
325
326 protected void waitIfPaused() throws InterruptedException
327 {
328 if (logger.isDebugEnabled() && lifecycleState.isPhaseComplete(Pausable.PHASE_NAME))
329 {
330 logger.debug(getStageDescription() + " is paused. Polling halted until resumed is called");
331 }
332 while (lifecycleState.isPhaseComplete(Pausable.PHASE_NAME) && !lifecycleState.isStopping())
333 {
334 Thread.sleep(500);
335 }
336 }
337
338 public void release()
339 {
340 queueDraining.set(false);
341 }
342
343 public void initialise() throws InitialisationException
344 {
345 if (next == null)
346 {
347 throw new IllegalStateException(
348 "Next message processor cannot be null with this InterceptingMessageProcessor");
349 }
350
351 queueProfile.configureQueue(getQueueName(), muleContext.getQueueManager());
352 queue = muleContext.getQueueManager().getQueueSession().getQueue(getQueueName());
353 if (queue == null)
354 {
355 throw new InitialisationException(MessageFactory.createStaticMessage("Queue not created for "
356 + getStageDescription()),
357 this);
358 }
359 }
360
361 public void start() throws MuleException
362 {
363 if (queue == null)
364 {
365 throw new IllegalStateException("Not initialised");
366 }
367 try
368 {
369 workManagerSource.getWorkManager().scheduleWork(this, WorkManager.INDEFINITE, null, this);
370 }
371 catch (WorkException e)
372 {
373 throw new LifecycleException(CoreMessages.failedToStart(getStageDescription()), e, this);
374
375 }
376 }
377
378 public void stop() throws MuleException
379 {
380 if (queue != null && queue.size() > 0)
381 {
382 try
383 {
384 queueDraining.whenFalse(null);
385 }
386 catch (InterruptedException e)
387 {
388
389 }
390 }
391 }
392
393 public void dispose()
394 {
395 queue = null;
396 }
397
398 }