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