Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
FunctionalTestComponent |
|
| 0.0;0 |
1 | /* | |
2 | * $Id: FunctionalTestComponent.java 19368 2010-09-05 05:19:34Z mike.schilling $ | |
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.tck.functional; | |
12 | ||
13 | import org.mule.RequestContext; | |
14 | import org.mule.api.MuleContext; | |
15 | import org.mule.api.MuleEventContext; | |
16 | import org.mule.api.MuleException; | |
17 | import org.mule.api.MuleMessage; | |
18 | import org.mule.api.context.MuleContextAware; | |
19 | import org.mule.api.lifecycle.Callable; | |
20 | import org.mule.api.lifecycle.Disposable; | |
21 | import org.mule.api.lifecycle.Initialisable; | |
22 | import org.mule.tck.exceptions.FunctionalTestException; | |
23 | import org.mule.util.NumberUtils; | |
24 | import org.mule.util.StringMessageUtils; | |
25 | import org.mule.util.SystemUtils; | |
26 | ||
27 | import java.util.List; | |
28 | ||
29 | import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList; | |
30 | ||
31 | import org.apache.commons.logging.Log; | |
32 | import org.apache.commons.logging.LogFactory; | |
33 | ||
34 | /** | |
35 | * <code>FunctionalTestComponent</code> is a service that can be used by | |
36 | * functional tests. This service accepts an EventCallback that can be used to | |
37 | * assert the state of the current event. | |
38 | * <p/> | |
39 | * Also, this service fires {@link org.mule.tck.functional.FunctionalTestNotification} via Mule for every message received. | |
40 | * Tests can register with Mule to receive these events by implementing | |
41 | * {@link org.mule.tck.functional.FunctionalTestNotificationListener}. | |
42 | * | |
43 | * @see EventCallback | |
44 | * @see FunctionalTestNotification | |
45 | * @see FunctionalTestNotificationListener | |
46 | */ | |
47 | // TODO This should really extend StaticComponent from mule-core as it is quite similar. | |
48 | 0 | public class FunctionalTestComponent implements Callable, Initialisable, Disposable, MuleContextAware, Receiveable |
49 | { | |
50 | 0 | protected transient Log logger = LogFactory.getLog(getClass()); |
51 | ||
52 | public static final int STREAM_SAMPLE_SIZE = 4; | |
53 | public static final int STREAM_BUFFER_SIZE = 4096; | |
54 | private EventCallback eventCallback; | |
55 | 0 | private Object returnData = null; |
56 | 0 | private boolean throwException = false; |
57 | 0 | private boolean enableMessageHistory = true; |
58 | 0 | private boolean enableNotifications = true; |
59 | 0 | private boolean doInboundTransform = true; |
60 | private String appendString; | |
61 | private Class<? extends Throwable> exceptionToThrow; | |
62 | 0 | private long waitTime = 0; |
63 | 0 | private boolean logMessageDetails = false; |
64 | private MuleContext muleContext; | |
65 | ||
66 | /** | |
67 | * Keeps a list of any messages received on this service. Note that only references | |
68 | * to the messages (objects) are stored, so any subsequent changes to the objects | |
69 | * will change the history. | |
70 | */ | |
71 | private List<Object> messageHistory; | |
72 | ||
73 | @SuppressWarnings("unchecked") | |
74 | public void initialise() | |
75 | { | |
76 | 0 | if (enableMessageHistory) |
77 | { | |
78 | 0 | messageHistory = new CopyOnWriteArrayList(); |
79 | } | |
80 | 0 | } |
81 | ||
82 | public void setMuleContext(MuleContext context) | |
83 | { | |
84 | 0 | this.muleContext = context; |
85 | 0 | } |
86 | ||
87 | public void dispose() | |
88 | { | |
89 | // nothing to do | |
90 | 0 | } |
91 | ||
92 | /** | |
93 | * {@inheritDoc} | |
94 | */ | |
95 | public Object onCall(MuleEventContext context) throws Exception | |
96 | { | |
97 | 0 | if (isThrowException()) |
98 | { | |
99 | 0 | throwException(); |
100 | } | |
101 | 0 | return process(getMessageFromContext(context), context); |
102 | } | |
103 | ||
104 | private Object getMessageFromContext(MuleEventContext context) throws MuleException | |
105 | { | |
106 | 0 | if (isDoInboundTransform()) |
107 | { | |
108 | 0 | Object o = context.getMessage().getPayload(); |
109 | 0 | if (getAppendString() != null && !(o instanceof String)) |
110 | { | |
111 | 0 | o = context.transformMessageToString(); |
112 | } | |
113 | 0 | return o; |
114 | } | |
115 | 0 | else if (getAppendString()!=null) |
116 | { | |
117 | 0 | return context.getMessageAsString(); |
118 | } | |
119 | else | |
120 | { | |
121 | 0 | return context.getMessage().getPayload(); |
122 | } | |
123 | } | |
124 | ||
125 | /** | |
126 | * This method is used by some WebServices tests where you don' want to be introducing the {@link org.mule.api.MuleEventContext} as | |
127 | * a complex type. | |
128 | * | |
129 | * @param data the event data received | |
130 | * @return the processed message | |
131 | * @throws Exception | |
132 | */ | |
133 | public Object onReceive(Object data) throws Exception | |
134 | { | |
135 | 0 | MuleEventContext context = RequestContext.getEventContext(); |
136 | ||
137 | 0 | if (isThrowException()) |
138 | { | |
139 | 0 | throwException(); |
140 | } | |
141 | 0 | return process(data, context); |
142 | } | |
143 | ||
144 | ||
145 | /** | |
146 | * Always throws a {@link org.mule.tck.exceptions.FunctionalTestException}. This methodis only called if | |
147 | * {@link #isThrowException()} is true. | |
148 | * | |
149 | * @throws FunctionalTestException or the exception specified in 'exceptionType | |
150 | */ | |
151 | protected void throwException() throws Exception | |
152 | { | |
153 | 0 | if (getExceptionToThrow() != null) |
154 | { | |
155 | 0 | throw (Exception)getExceptionToThrow().newInstance(); |
156 | } | |
157 | else | |
158 | { | |
159 | 0 | throw new FunctionalTestException(); |
160 | } | |
161 | } | |
162 | ||
163 | /** | |
164 | * Will append the value of {@link #getAppendString()} to the contents of the message. This has a side affect | |
165 | * that the inbound message will be converted to a string and the return payload will be a string. | |
166 | * Note that the value of {@link #getAppendString()} can contain expressions. | |
167 | * | |
168 | * @param contents the string vlaue of the current message payload | |
169 | * @param message the current message | |
170 | * @return a concatenated string of the current payload and the appendString | |
171 | */ | |
172 | protected String append(String contents, MuleMessage message) | |
173 | { | |
174 | 0 | return contents + muleContext.getExpressionManager().parse(appendString, message); |
175 | } | |
176 | ||
177 | /** | |
178 | * The service method that implements the test component logic. This method can be called publically through | |
179 | * either {@link #onCall(org.mule.api.MuleEventContext)} or {@link #onReceive(Object)} | |
180 | * | |
181 | * @param data The message payload | |
182 | * @param context the current {@link org.mule.api.MuleEventContext} | |
183 | * @return a new message payload according to the configuration of the component | |
184 | * @throws Exception if there is a general failure or if {@link #isThrowException()} is true. | |
185 | */ | |
186 | protected Object process(Object data, MuleEventContext context) throws Exception | |
187 | { | |
188 | // System.out.println(data + " at " + new java.util.Date()); | |
189 | 0 | if (enableMessageHistory) |
190 | { | |
191 | 0 | messageHistory.add(data); |
192 | } | |
193 | ||
194 | 0 | if (logger.isInfoEnabled()) |
195 | { | |
196 | 0 | String msg = StringMessageUtils.getBoilerPlate("Message Received in service: " |
197 | + context.getFlowConstruct().getName() + ". Content is: " | |
198 | + StringMessageUtils.truncate(data.toString(), 100, true), '*', 80); | |
199 | ||
200 | 0 | logger.info(msg); |
201 | } | |
202 | ||
203 | 0 | final MuleMessage message = context.getMessage(); |
204 | 0 | if (isLogMessageDetails() && logger.isInfoEnabled()) |
205 | { | |
206 | 0 | StringBuilder sb = new StringBuilder(); |
207 | ||
208 | 0 | sb.append("Full Message payload: ").append(SystemUtils.LINE_SEPARATOR); |
209 | 0 | sb.append(message.getPayload()).append(SystemUtils.LINE_SEPARATOR); |
210 | 0 | sb.append(StringMessageUtils.headersToString(message)); |
211 | 0 | logger.info(sb.toString()); |
212 | } | |
213 | ||
214 | 0 | if (eventCallback != null) |
215 | { | |
216 | 0 | eventCallback.eventReceived(context, this); |
217 | } | |
218 | ||
219 | Object replyMessage; | |
220 | 0 | if (returnData != null) |
221 | { | |
222 | 0 | if (returnData instanceof String && muleContext.getExpressionManager().isExpression(returnData.toString())) |
223 | { | |
224 | 0 | replyMessage = muleContext.getExpressionManager().parse(returnData.toString(), message); |
225 | } | |
226 | else | |
227 | { | |
228 | 0 | replyMessage = returnData; |
229 | } | |
230 | } | |
231 | else | |
232 | { | |
233 | 0 | if (appendString != null) |
234 | { | |
235 | 0 | replyMessage = append(data.toString(), message); |
236 | } | |
237 | else | |
238 | { | |
239 | 0 | replyMessage = data; |
240 | } | |
241 | } | |
242 | ||
243 | 0 | if (isEnableNotifications()) |
244 | { | |
245 | 0 | muleContext.fireNotification( |
246 | new FunctionalTestNotification(context, replyMessage, FunctionalTestNotification.EVENT_RECEIVED)); | |
247 | } | |
248 | ||
249 | //Time to wait before returning | |
250 | 0 | if (waitTime > 0) |
251 | { | |
252 | try | |
253 | { | |
254 | 0 | Thread.sleep(waitTime); |
255 | } | |
256 | 0 | catch (InterruptedException e) |
257 | { | |
258 | 0 | logger.info("FunctionalTestComponent waitTime was interrupted"); |
259 | 0 | } |
260 | } | |
261 | 0 | return replyMessage; |
262 | } | |
263 | ||
264 | /** | |
265 | * An event callback is called when a message is received by the service. | |
266 | * An MuleEvent callback isn't strictly required but it is usfal for performing assertions | |
267 | * on the current message being received. | |
268 | * Note that the FunctionalTestComponent should be made a singleton | |
269 | * when using MuleEvent callbacks | |
270 | * <p/> | |
271 | * Another option is to register a {@link org.mule.tck.functional.FunctionalTestNotificationListener} with Mule and this | |
272 | * will deleiver a {@link org.mule.tck.functional.FunctionalTestNotification} for every message received by this service | |
273 | * | |
274 | * @return the callback to call when a message is received | |
275 | * @see FunctionalTestNotification | |
276 | * @see FunctionalTestNotificationListener | |
277 | */ | |
278 | public EventCallback getEventCallback() | |
279 | { | |
280 | 0 | return eventCallback; |
281 | } | |
282 | ||
283 | /** | |
284 | * An event callback is called when a message is received by the service. | |
285 | * An MuleEvent callback isn't strictly required but it is usfal for performing assertions | |
286 | * on the current message being received. | |
287 | * Note that the FunctionalTestComponent should be made a singleton | |
288 | * when using MuleEvent callbacks | |
289 | * <p/> | |
290 | * Another option is to register a {@link org.mule.tck.functional.FunctionalTestNotificationListener} with Mule and this | |
291 | * will deleiver a {@link org.mule.tck.functional.FunctionalTestNotification} for every message received by this service | |
292 | * | |
293 | * @param eventCallback the callback to call when a message is received | |
294 | * @see FunctionalTestNotification | |
295 | * @see FunctionalTestNotificationListener | |
296 | */ | |
297 | public void setEventCallback(EventCallback eventCallback) | |
298 | { | |
299 | 0 | this.eventCallback = eventCallback; |
300 | 0 | } |
301 | ||
302 | /** | |
303 | * Often you will may want to return a fixed message payload to simulate and external system call. | |
304 | * This can be done using the 'returnData' property. Note that you can return complex objects by | |
305 | * using the <container-property> element in the Xml configuration. | |
306 | * | |
307 | * @return the message payload to always return from this service instance | |
308 | */ | |
309 | public Object getReturnData() | |
310 | { | |
311 | 0 | return returnData; |
312 | } | |
313 | ||
314 | /** | |
315 | * Often you will may want to return a fixed message payload to simulate and external system call. | |
316 | * This can be done using the 'returnData' property. Note that you can return complex objects by | |
317 | * using the <container-property> element in the Xml configuration. | |
318 | * | |
319 | * @param returnData the message payload to always return from this service instance | |
320 | */ | |
321 | public void setReturnData(Object returnData) | |
322 | { | |
323 | 0 | this.returnData = returnData; |
324 | 0 | } |
325 | ||
326 | /** | |
327 | * Sometimes you will want the service to always throw an exception, if this is the case you can | |
328 | * set the 'throwException' property to true. | |
329 | * | |
330 | * @return throwException true if an exception should always be thrown from this instance. | |
331 | * If the {@link #returnData} property is set and is of type | |
332 | * java.lang.Exception, that exception will be thrown. | |
333 | */ | |
334 | public boolean isThrowException() | |
335 | { | |
336 | 0 | return throwException; |
337 | } | |
338 | ||
339 | /** | |
340 | * Sometimes you will want the service to always throw an exception, if this is the case you can | |
341 | * set the 'throwException' property to true. | |
342 | * | |
343 | * @param throwException true if an exception should always be thrown from this instance. | |
344 | * If the {@link #returnData} property is set and is of type | |
345 | * java.lang.Exception, that exception will be thrown. | |
346 | */ | |
347 | public void setThrowException(boolean throwException) | |
348 | { | |
349 | 0 | this.throwException = throwException; |
350 | 0 | } |
351 | ||
352 | public boolean isEnableMessageHistory() | |
353 | { | |
354 | 0 | return enableMessageHistory; |
355 | } | |
356 | ||
357 | public void setEnableMessageHistory(boolean enableMessageHistory) | |
358 | { | |
359 | 0 | this.enableMessageHistory = enableMessageHistory; |
360 | 0 | } |
361 | ||
362 | /** | |
363 | * If enableMessageHistory = true, returns the number of messages received by this service. | |
364 | * @return -1 if no message history, otherwise the history size | |
365 | */ | |
366 | public int getReceivedMessagesCount() | |
367 | { | |
368 | 0 | if (messageHistory != null) |
369 | { | |
370 | 0 | return messageHistory.size(); |
371 | } | |
372 | else | |
373 | { | |
374 | 0 | return NumberUtils.INTEGER_MINUS_ONE.intValue(); |
375 | } | |
376 | } | |
377 | ||
378 | /** | |
379 | * If enableMessageHistory = true, returns a message received by the service in chronological order. | |
380 | * For example, getReceivedMessage(1) returns the first message received by the service, | |
381 | * getReceivedMessage(2) returns the second message received by the service, etc. | |
382 | */ | |
383 | public Object getReceivedMessage(int number) | |
384 | { | |
385 | 0 | Object message = null; |
386 | 0 | if (messageHistory != null) |
387 | { | |
388 | 0 | if (number <= messageHistory.size()) |
389 | { | |
390 | 0 | message = messageHistory.get(number - 1); |
391 | } | |
392 | } | |
393 | 0 | return message; |
394 | } | |
395 | ||
396 | /** | |
397 | * If enableMessageHistory = true, returns the last message received by the service in chronological order. | |
398 | */ | |
399 | public Object getLastReceivedMessage() | |
400 | { | |
401 | 0 | if (messageHistory != null) |
402 | { | |
403 | 0 | return messageHistory.get(messageHistory.size() - 1); |
404 | } | |
405 | else | |
406 | { | |
407 | 0 | return null; |
408 | } | |
409 | } | |
410 | ||
411 | public String getAppendString() | |
412 | { | |
413 | 0 | return appendString; |
414 | } | |
415 | ||
416 | public void setAppendString(String appendString) | |
417 | { | |
418 | 0 | this.appendString = appendString; |
419 | 0 | } |
420 | ||
421 | public boolean isEnableNotifications() | |
422 | { | |
423 | 0 | return enableNotifications; |
424 | } | |
425 | ||
426 | public void setEnableNotifications(boolean enableNotifications) | |
427 | { | |
428 | 0 | this.enableNotifications = enableNotifications; |
429 | 0 | } |
430 | ||
431 | public Class<? extends Throwable> getExceptionToThrow() | |
432 | { | |
433 | 0 | return exceptionToThrow; |
434 | } | |
435 | ||
436 | public void setExceptionToThrow(Class<? extends Throwable> exceptionToThrow) | |
437 | { | |
438 | 0 | this.exceptionToThrow = exceptionToThrow; |
439 | 0 | } |
440 | ||
441 | public long getWaitTime() | |
442 | { | |
443 | 0 | return waitTime; |
444 | } | |
445 | ||
446 | public void setWaitTime(long waitTime) | |
447 | { | |
448 | 0 | this.waitTime = waitTime; |
449 | 0 | } |
450 | ||
451 | public boolean isDoInboundTransform() | |
452 | { | |
453 | 0 | return doInboundTransform; |
454 | } | |
455 | ||
456 | public void setDoInboundTransform(boolean doInboundTransform) | |
457 | { | |
458 | 0 | this.doInboundTransform = doInboundTransform; |
459 | 0 | } |
460 | ||
461 | public boolean isLogMessageDetails() | |
462 | { | |
463 | 0 | return logMessageDetails; |
464 | } | |
465 | ||
466 | public void setLogMessageDetails(boolean logMessageDetails) | |
467 | { | |
468 | 0 | this.logMessageDetails = logMessageDetails; |
469 | 0 | } |
470 | } |