001: /*
002: * $Id: FunctionalTestComponent.java 10808 2008-02-14 20:36:57Z acooke $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.tck.functional;
012:
013: import org.mule.MuleServer;
014: import org.mule.RequestContext;
015: import org.mule.api.MuleContext;
016: import org.mule.api.MuleEventContext;
017: import org.mule.api.DefaultMuleException;
018: import org.mule.api.lifecycle.Callable;
019: import org.mule.api.lifecycle.Disposable;
020: import org.mule.api.lifecycle.Initialisable;
021: import org.mule.api.lifecycle.LifecycleTransitionResult;
022: import org.mule.config.i18n.MessageFactory;
023: import org.mule.tck.exceptions.FunctionalTestException;
024: import org.mule.util.NumberUtils;
025: import org.mule.util.StringMessageUtils;
026:
027: import java.util.List;
028:
029: import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;
030:
031: import org.apache.commons.logging.Log;
032: import org.apache.commons.logging.LogFactory;
033:
034: /**
035: * <code>FunctionalTestComponent</code> is a service that can be used by
036: * functional tests. This service accepts an EventCallback that can be used to
037: * assert the state of the current event.
038: * <p/>
039: * Also, this service fires {@link FunctionalTestNotification} via Mule for every message received.
040: * Tests can register with Mule to receive these events by implementing
041: * {@link FunctionalTestNotificationListener}.
042: *
043: * @see org.mule.tck.functional.EventCallback
044: * @see org.mule.tck.functional.FunctionalTestNotification
045: * @see org.mule.tck.functional.FunctionalTestNotificationListener
046: */
047:
048: public class FunctionalTestComponent implements Callable,
049: Initialisable, Disposable {
050: protected transient Log logger = LogFactory.getLog(getClass());
051:
052: public static final int STREAM_SAMPLE_SIZE = 4;
053: public static final int STREAM_BUFFER_SIZE = 4096;
054: private EventCallback eventCallback;
055: private Object returnMessage = null;
056: private boolean appendComponentName = false;
057: private boolean throwException = false;
058: private boolean enableMessageHistory = true;
059: private boolean addReceived = true;
060: private boolean asString = true;
061:
062: /**
063: * Keeps a list of any messages received on this service. Note that only references
064: * to the messages (objects) are stored, so any subsequent changes to the objects
065: * will change the history.
066: */
067: private List messageHistory;
068:
069: public LifecycleTransitionResult initialise() {
070: if (enableMessageHistory) {
071: messageHistory = new CopyOnWriteArrayList();
072: }
073: return LifecycleTransitionResult.OK;
074: }
075:
076: public void dispose() {
077: // nothing to do
078: }
079:
080: /** {@inheritDoc} */
081: public Object onCall(MuleEventContext context) throws Exception {
082: if (enableMessageHistory) {
083: messageHistory.add(context.transformMessage());
084: }
085:
086: String contents = context.transformMessageToString();
087: String msg = StringMessageUtils.getBoilerPlate(
088: "Message Received in service: "
089: + context.getService().getName()
090: + ". Content is: "
091: + StringMessageUtils.truncate(contents, 100,
092: true), '*', 80);
093:
094: logger.info(msg);
095:
096: if (eventCallback != null) {
097: eventCallback.eventReceived(context, this );
098: }
099:
100: Object replyMessage;
101: if (returnMessage != null) {
102: replyMessage = returnMessage;
103: } else {
104: if (isAsString()) {
105: replyMessage = (addReceived ? received(contents)
106: : contents)
107: + (appendComponentName ? " "
108: + context.getService().getName() : "");
109: } else {
110: replyMessage = context.getMessage().getPayload();
111: }
112: }
113:
114: MuleContext muleContext = context.getMuleContext();
115: if (muleContext == null) {
116: logger
117: .warn("No MuleContext available from MuleEventContext");
118: muleContext = MuleServer.getMuleContext();
119: }
120: muleContext.fireNotification(new FunctionalTestNotification(
121: context, replyMessage,
122: FunctionalTestNotification.EVENT_RECEIVED));
123:
124: if (throwException) {
125: throw new FunctionalTestException();
126: }
127:
128: return replyMessage;
129: }
130:
131: /**
132: * Append " Received" to contents. Exposed as static method so tests can call to
133: * construct string for comparison.
134: *
135: * @param contents
136: * @return Extended message
137: */
138: public static String received(String contents) {
139: return contents + " Received";
140: }
141:
142: /**
143: * This method duplicates much of the functionality for the {@link #onCall} method above. This method is currently
144: * used by some WebServices tests where you don' want to be introducing the {@link org.mule.api.MuleEventContext} as
145: * a complex type.
146: * TODO: It would be nice to remove this method or at least refactor the methods so there is little or no duplication
147: *
148: * @param data the event data received
149: * @return the processed message
150: * @throws Exception
151: */
152: public Object onReceive(Object data) throws Exception {
153: MuleEventContext context = RequestContext.getEventContext();
154: String contents = data.toString();
155: String msg = StringMessageUtils.getBoilerPlate(
156: "Message Received in service: "
157: + context.getService().getName()
158: + ". Content is: "
159: + StringMessageUtils.truncate(contents, 100,
160: true), '*', 80);
161:
162: logger.info(msg);
163:
164: if (eventCallback != null) {
165: eventCallback.eventReceived(context, this );
166: }
167:
168: Object replyMessage;
169: if (returnMessage != null) {
170: replyMessage = returnMessage;
171: } else {
172: replyMessage = contents + " Received";
173: }
174:
175: context.getMuleContext().fireNotification(
176: new FunctionalTestNotification(context, replyMessage,
177: FunctionalTestNotification.EVENT_RECEIVED));
178:
179: if (throwException) {
180: if (returnMessage != null
181: && returnMessage instanceof Exception) {
182: throw (Exception) returnMessage;
183: } else {
184: throw new DefaultMuleException(
185: MessageFactory
186: .createStaticMessage("Functional Test Service Exception"));
187: }
188: }
189:
190: return replyMessage;
191: }
192:
193: /**
194: * An event callback is called when a message is received by the service.
195: * An MuleEvent callback isn't strictly required but it is usfal for performing assertions
196: * on the current message being received.
197: * Note that the FunctionalTestComponent should be made a singleton
198: * {@link org.mule.api.UMODescriptor#setSingleton} when using MuleEvent callbacks
199: * <p/>
200: * Another option is to register a {@link FunctionalTestNotificationListener} with Mule and this
201: * will deleiver a {@link FunctionalTestNotification} for every message received by this service
202: *
203: * @return the callback to call when a message is received
204: * @see org.mule.api.UMODescriptor
205: * @see org.mule.tck.functional.FunctionalTestNotification
206: * @see org.mule.tck.functional.FunctionalTestNotificationListener
207: */
208: public EventCallback getEventCallback() {
209: return eventCallback;
210: }
211:
212: /**
213: * An event callback is called when a message is received by the service.
214: * An MuleEvent callback isn't strictly required but it is usfal for performing assertions
215: * on the current message being received.
216: * Note that the FunctionalTestComponent should be made a singleton
217: * {@link org.mule.api.UMODescriptor#setSingleton} when using MuleEvent callbacks
218: * <p/>
219: * Another option is to register a {@link FunctionalTestNotificationListener} with Mule and this
220: * will deleiver a {@link FunctionalTestNotification} for every message received by this service
221: *
222: * @param eventCallback the callback to call when a message is received
223: * @see org.mule.api.UMODescriptor
224: * @see org.mule.tck.functional.FunctionalTestNotification
225: * @see org.mule.tck.functional.FunctionalTestNotificationListener
226: */
227: public void setEventCallback(EventCallback eventCallback) {
228: this .eventCallback = eventCallback;
229: }
230:
231: /**
232: * Often you will may want to return a fixed message payload to simulate and external system call.
233: * This can be done using the 'returnMessage' property. Note that you can return complex objects by
234: * using the <container-property> element in the Xml configuration.
235: *
236: * @return the message payload to always return from this service instance
237: */
238: public Object getReturnMessage() {
239: return returnMessage;
240: }
241:
242: /**
243: * Often you will may want to return a fixed message payload to simulate and external system call.
244: * This can be done using the 'returnMessage' property. Note that you can return complex objects by
245: * using the <container-property> element in the Xml configuration.
246: *
247: * @param returnMessage the message payload to always return from this service instance
248: */
249: public void setReturnMessage(Object returnMessage) {
250: this .returnMessage = returnMessage;
251: }
252:
253: /**
254: * Sometimes you will want the service to always throw an exception, if this is the case you can
255: * set the 'throwException' property to true.
256: *
257: * @return throwException true if an exception should always be thrown from this instance.
258: * If the {@link #returnMessage} property is set and is of type
259: * java.lang.Exception, that exception will be thrown.
260: */
261: public boolean isThrowException() {
262: return throwException;
263: }
264:
265: /**
266: * Sometimes you will want the service to always throw an exception, if this is the case you can
267: * set the 'throwException' property to true.
268: *
269: * @param throwException true if an exception should always be thrown from this instance.
270: * If the {@link #returnMessage} property is set and is of type
271: * java.lang.Exception, that exception will be thrown.
272: */
273: public void setThrowException(boolean throwException) {
274: this .throwException = throwException;
275: }
276:
277: /**
278: * This will cause the service to append the compoent name to the end of the message
279: * returned from this service. This only works when processing String messages.
280: * This feature is useful when processing multiple messages using a pool of FunctionalTestComponents
281: * to determine who processed the resulting message
282: *
283: * @return true if the service name will be appended to the return message
284: */
285: public boolean isAppendComponentName() {
286: return appendComponentName;
287: }
288:
289: /**
290: * This will cause the service to append the compoent name to the end of the message
291: * returned from this service. This only works when processing String messages.
292: * This feature is useful when processing multiple messages using a pool of FunctionalTestComponents
293: * to determine who processed the resulting message
294: *
295: * @param appendComponentName true if the service name will be appended to the return message
296: */
297: public void setAppendComponentName(boolean appendComponentName) {
298: this .appendComponentName = appendComponentName;
299: }
300:
301: public boolean isEnableMessageHistory() {
302: return enableMessageHistory;
303: }
304:
305: public void setEnableMessageHistory(boolean enableMessageHistory) {
306: this .enableMessageHistory = enableMessageHistory;
307: }
308:
309: /** If enableMessageHistory = true, returns the number of messages received by this service. */
310: public int getReceivedMessages() {
311: if (messageHistory != null) {
312: return messageHistory.size();
313: } else {
314: return NumberUtils.INTEGER_MINUS_ONE.intValue();
315: }
316: }
317:
318: /**
319: * If enableMessageHistory = true, returns a message received by the service in chronological order.
320: * For example, getReceivedMessage(1) returns the first message received by the service,
321: * getReceivedMessage(2) returns the second message received by the service, etc.
322: */
323: public Object getReceivedMessage(int number) {
324: Object message = null;
325: if (messageHistory != null) {
326: if (number <= messageHistory.size()) {
327: message = messageHistory.get(number - 1);
328: }
329: }
330: return message;
331: }
332:
333: /** If enableMessageHistory = true, returns the last message received by the service in chronological order. */
334: public Object getLastReceivedMessage() {
335: if (messageHistory != null) {
336: return messageHistory.get(messageHistory.size() - 1);
337: } else {
338: return null;
339: }
340: }
341:
342: public boolean isAddReceived() {
343: return addReceived;
344: }
345:
346: public void setAddReceived(boolean addReceived) {
347: this .addReceived = addReceived;
348: }
349:
350: public boolean isAsString() {
351: return asString;
352: }
353:
354: public void setAsString(boolean asString) {
355: this.asString = asString;
356: }
357:
358: }
|