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