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