Java tutorial
/* * $Id$ * -------------------------------------------------------------------------------------- * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.tck.functional; import org.mule.RequestContext; import org.mule.api.MuleContext; import org.mule.api.MuleEventContext; import org.mule.api.MuleException; import org.mule.api.MuleMessage; import org.mule.api.context.MuleContextAware; import org.mule.api.lifecycle.Callable; import org.mule.api.lifecycle.Disposable; import org.mule.api.lifecycle.Initialisable; import org.mule.api.lifecycle.Startable; import org.mule.api.lifecycle.Stoppable; import org.mule.tck.exceptions.FunctionalTestException; import org.mule.util.ClassUtils; import org.mule.util.NumberUtils; import org.mule.util.StringMessageUtils; import org.mule.util.SystemUtils; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * <code>FunctionalTestComponent</code> is a service that can be used by * functional tests. This service accepts an EventCallback that can be used to * assert the state of the current event. * <p/> * Also, this service fires {@link org.mule.tck.functional.FunctionalTestNotification} via Mule for every message received. * Tests can register with Mule to receive these events by implementing * {@link org.mule.tck.functional.FunctionalTestNotificationListener}. * * @see EventCallback * @see FunctionalTestNotification * @see FunctionalTestNotificationListener */ // TODO This should really extend StaticComponent from mule-core as it is quite similar. public class FunctionalTestComponent implements Callable, Initialisable, Disposable, MuleContextAware, Receiveable, Startable, Stoppable { protected transient Log logger = LogFactory.getLog(getClass()); public static final int STREAM_SAMPLE_SIZE = 4; public static final int STREAM_BUFFER_SIZE = 4096; private EventCallback eventCallback; private Object returnData = null; private boolean throwException = false; private Class<? extends Throwable> exceptionToThrow; private String exceptionText = ""; private boolean enableMessageHistory = true; private boolean enableNotifications = true; private boolean doInboundTransform = true; private String appendString; private long waitTime = 0; private boolean logMessageDetails = false; private String id = "<none>"; private MuleContext muleContext; private static List<LifecycleCallback> lifecycleCallbacks = new ArrayList<LifecycleCallback>(); /** * Keeps a list of any messages received on this service. Note that only references * to the messages (objects) are stored, so any subsequent changes to the objects * will change the history. */ private List<Object> messageHistory; @Override public void initialise() { if (enableMessageHistory) { messageHistory = new CopyOnWriteArrayList<Object>(); } for (LifecycleCallback callback : lifecycleCallbacks) { callback.onTransition(id, Initialisable.PHASE_NAME); } } @Override public void start() throws MuleException { for (LifecycleCallback callback : lifecycleCallbacks) { callback.onTransition(id, Startable.PHASE_NAME); } } public void setMuleContext(MuleContext context) { this.muleContext = context; } @Override public void stop() throws MuleException { for (LifecycleCallback callback : lifecycleCallbacks) { callback.onTransition(id, Stoppable.PHASE_NAME); } } public void dispose() { for (LifecycleCallback callback : lifecycleCallbacks) { callback.onTransition(id, Disposable.PHASE_NAME); } } /** * {@inheritDoc} */ @Override public Object onCall(MuleEventContext context) throws Exception { if (isThrowException()) { throwException(); } return process(getMessageFromContext(context), context); } private Object getMessageFromContext(MuleEventContext context) throws MuleException { if (isDoInboundTransform()) { Object o = context.getMessage().getPayload(); if (getAppendString() != null && !(o instanceof String)) { o = context.transformMessageToString(); } return o; } else if (getAppendString() != null) { return context.getMessageAsString(); } else { return context.getMessage().getPayload(); } } /** * This method is used by some WebServices tests where you don' want to be introducing the {@link org.mule.api.MuleEventContext} as * a complex type. * * @param data the event data received * @return the processed message * @throws Exception */ @Override public Object onReceive(Object data) throws Exception { MuleEventContext context = RequestContext.getEventContext(); if (isThrowException()) { throwException(); } return process(data, context); } /** * Always throws a {@link org.mule.tck.exceptions.FunctionalTestException}. This methodis only called if * {@link #isThrowException()} is true. * * @throws FunctionalTestException or the exception specified in 'exceptionType */ protected void throwException() throws Exception { if (getExceptionToThrow() != null) { if (StringUtils.isNotBlank(exceptionText)) { Throwable exception = ClassUtils.instanciateClass(getExceptionToThrow(), new Object[] { exceptionText }); throw (Exception) exception; } else { throw (Exception) getExceptionToThrow().newInstance(); } } else { if (StringUtils.isNotBlank(exceptionText)) { throw new FunctionalTestException(exceptionText); } else { throw new FunctionalTestException(); } } } /** * Will append the value of {@link #getAppendString()} to the contents of the message. This has a side affect * that the inbound message will be converted to a string and the return payload will be a string. * Note that the value of {@link #getAppendString()} can contain expressions. * * @param contents the string vlaue of the current message payload * @param message the current message * @return a concatenated string of the current payload and the appendString */ protected String append(String contents, MuleMessage message) { return contents + muleContext.getExpressionManager().parse(appendString, message); } /** * The service method that implements the test component logic. This method can be called publically through * either {@link #onCall(org.mule.api.MuleEventContext)} or {@link #onReceive(Object)} * * @param data The message payload * @param context the current {@link org.mule.api.MuleEventContext} * @return a new message payload according to the configuration of the component * @throws Exception if there is a general failure or if {@link #isThrowException()} is true. */ protected Object process(Object data, MuleEventContext context) throws Exception { // System.out.println(data + " at " + new java.util.Date()); if (enableMessageHistory) { messageHistory.add(data); } if (logger.isInfoEnabled()) { String msg = StringMessageUtils .getBoilerPlate( "Message Received in service: " + context.getFlowConstruct().getName() + ". Content is: " + StringMessageUtils.truncate(data.toString(), 100, true), '*', 80); logger.info(msg); } final MuleMessage message = context.getMessage(); if (isLogMessageDetails() && logger.isInfoEnabled()) { StringBuilder sb = new StringBuilder(); sb.append("Full Message payload: ").append(SystemUtils.LINE_SEPARATOR); sb.append(message.getPayload()).append(SystemUtils.LINE_SEPARATOR); sb.append(StringMessageUtils.headersToString(message)); logger.info(sb.toString()); } if (eventCallback != null) { eventCallback.eventReceived(context, this); } Object replyMessage; if (returnData != null) { if (returnData instanceof String && muleContext.getExpressionManager().isExpression(returnData.toString())) { replyMessage = muleContext.getExpressionManager().parse(returnData.toString(), message); } else { replyMessage = returnData; } } else { if (appendString != null) { replyMessage = append(data.toString(), message); } else { replyMessage = data; } } if (isEnableNotifications()) { muleContext.fireNotification(new FunctionalTestNotification(context, replyMessage, FunctionalTestNotification.EVENT_RECEIVED)); } //Time to wait before returning if (waitTime > 0) { try { Thread.sleep(waitTime); } catch (InterruptedException e) { logger.info("FunctionalTestComponent waitTime was interrupted"); } } return replyMessage; } /** * An event callback is called when a message is received by the service. * An MuleEvent callback isn't strictly required but it is usfal for performing assertions * on the current message being received. * Note that the FunctionalTestComponent should be made a singleton * when using MuleEvent callbacks * <p/> * Another option is to register a {@link org.mule.tck.functional.FunctionalTestNotificationListener} with Mule and this * will deleiver a {@link org.mule.tck.functional.FunctionalTestNotification} for every message received by this service * * @return the callback to call when a message is received * @see FunctionalTestNotification * @see FunctionalTestNotificationListener */ public EventCallback getEventCallback() { return eventCallback; } /** * An event callback is called when a message is received by the service. * An MuleEvent callback isn't strictly required but it is usfal for performing assertions * on the current message being received. * Note that the FunctionalTestComponent should be made a singleton * when using MuleEvent callbacks * <p/> * Another option is to register a {@link org.mule.tck.functional.FunctionalTestNotificationListener} with Mule and this * will deleiver a {@link org.mule.tck.functional.FunctionalTestNotification} for every message received by this service * * @param eventCallback the callback to call when a message is received * @see FunctionalTestNotification * @see FunctionalTestNotificationListener */ public void setEventCallback(EventCallback eventCallback) { this.eventCallback = eventCallback; } /** * Often you will may want to return a fixed message payload to simulate and external system call. * This can be done using the 'returnData' property. Note that you can return complex objects by * using the <container-property> element in the Xml configuration. * * @return the message payload to always return from this service instance */ public Object getReturnData() { return returnData; } /** * Often you will may want to return a fixed message payload to simulate and external system call. * This can be done using the 'returnData' property. Note that you can return complex objects by * using the <container-property> element in the Xml configuration. * * @param returnData the message payload to always return from this service instance */ public void setReturnData(Object returnData) { this.returnData = returnData; } /** * Sometimes you will want the service to always throw an exception, if this is the case you can * set the 'throwException' property to true. * * @return throwException true if an exception should always be thrown from this instance. * If the {@link #returnData} property is set and is of type * java.lang.Exception, that exception will be thrown. */ public boolean isThrowException() { return throwException; } /** * Sometimes you will want the service to always throw an exception, if this is the case you can * set the 'throwException' property to true. * * @param throwException true if an exception should always be thrown from this instance. * If the {@link #returnData} property is set and is of type * java.lang.Exception, that exception will be thrown. */ public void setThrowException(boolean throwException) { this.throwException = throwException; } public boolean isEnableMessageHistory() { return enableMessageHistory; } public void setEnableMessageHistory(boolean enableMessageHistory) { this.enableMessageHistory = enableMessageHistory; } /** * If enableMessageHistory = true, returns the number of messages received by this service. * @return -1 if no message history, otherwise the history size */ public int getReceivedMessagesCount() { if (messageHistory != null) { return messageHistory.size(); } else { return NumberUtils.INTEGER_MINUS_ONE.intValue(); } } /** * If enableMessageHistory = true, returns a message received by the service in chronological order. * For example, getReceivedMessage(1) returns the first message received by the service, * getReceivedMessage(2) returns the second message received by the service, etc. */ public Object getReceivedMessage(int number) { Object message = null; if (messageHistory != null) { if (number <= messageHistory.size()) { message = messageHistory.get(number - 1); } } return message; } /** * If enableMessageHistory = true, returns the last message received by the service in chronological order. */ public Object getLastReceivedMessage() { if (messageHistory != null) { return messageHistory.get(messageHistory.size() - 1); } else { return null; } } public String getAppendString() { return appendString; } public void setAppendString(String appendString) { this.appendString = appendString; } public boolean isEnableNotifications() { return enableNotifications; } public void setEnableNotifications(boolean enableNotifications) { this.enableNotifications = enableNotifications; } public Class<? extends Throwable> getExceptionToThrow() { return exceptionToThrow; } public void setExceptionToThrow(Class<? extends Throwable> exceptionToThrow) { this.exceptionToThrow = exceptionToThrow; } public long getWaitTime() { return waitTime; } public void setWaitTime(long waitTime) { this.waitTime = waitTime; } public boolean isDoInboundTransform() { return doInboundTransform; } public void setDoInboundTransform(boolean doInboundTransform) { this.doInboundTransform = doInboundTransform; } public boolean isLogMessageDetails() { return logMessageDetails; } public void setLogMessageDetails(boolean logMessageDetails) { this.logMessageDetails = logMessageDetails; } public String getExceptionText() { return exceptionText; } public void setExceptionText(String text) { exceptionText = text; } public void setId(String id) { this.id = id; } public static void addLifecycleCallback(LifecycleCallback callback) { lifecycleCallbacks.add(callback); } public static void removeLifecycleCallback(LifecycleCallback callback) { lifecycleCallbacks.remove(callback); } public interface LifecycleCallback { void onTransition(String name, String newPhase); } }