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