View Javadoc
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.api.MuleEventContext;
10  import org.mule.api.lifecycle.Callable;
11  import org.mule.transformer.types.DataTypeFactory;
12  import org.mule.util.ClassUtils;
13  import org.mule.util.StringMessageUtils;
14  
15  import java.io.IOException;
16  import java.io.InputStream;
17  
18  import edu.emory.mathcs.backport.java.util.concurrent.atomic.AtomicInteger;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  
23  /**
24   * A service that can be used by streaming functional tests. This service accepts an
25   * EventCallback that can be used to assert the state of the current event.  To access the
26   * service when embedded in an (XML) model, make sure that the descriptor sets the
27   * singleton attribute true - see uses in TCP and FTP.
28   *
29   * Note that although this implements the full StreamingService interface, nothing is
30   * written to the output stream - this is intended as a final sink.
31   *
32   * @see org.mule.tck.functional.EventCallback
33   */
34  
35  public class FunctionalStreamingTestComponent implements Callable
36  {
37      protected transient Log logger = LogFactory.getLog(getClass());
38  
39      private static AtomicInteger count = new AtomicInteger(0);
40      private int number = count.incrementAndGet();
41  
42      public static final int STREAM_SAMPLE_SIZE = 4;
43      public static final int STREAM_BUFFER_SIZE = 4096;
44      private EventCallback eventCallback;
45      private String summary = null;
46      private long targetSize = -1;
47  
48      public FunctionalStreamingTestComponent()
49      {
50          logger.debug("creating " + toString());
51      }
52  
53      public void setEventCallback(EventCallback eventCallback, long targetSize)
54      {
55          logger.debug("setting callback: " + eventCallback + " in " + toString());
56          this.eventCallback = eventCallback;
57          this.targetSize = targetSize;
58      }
59  
60      public String getSummary()
61      {
62          return summary;
63      }
64   
65      public int getNumber()
66      {
67          return number;
68      }
69  
70      public Object onCall(MuleEventContext context) throws Exception
71      {
72          InputStream in = context.getMessage().getPayload(DataTypeFactory.create(InputStream.class));
73          try
74          {
75              logger.debug("arrived at " + toString());
76              byte[] startData = new byte[STREAM_SAMPLE_SIZE];
77              long startDataSize = 0;
78              byte[] endData = new byte[STREAM_SAMPLE_SIZE]; // ring buffer
79              long endDataSize = 0;
80              long endRingPointer = 0;
81              long streamLength = 0;
82              byte[] buffer = new byte[STREAM_BUFFER_SIZE];
83  
84              // throw data on the floor, but keep a record of size, start and end values
85              long bytesRead = 0;
86              while (bytesRead >= 0)
87              {
88                  bytesRead = read(in, buffer);
89                  if (bytesRead > 0)
90                  {
91                      if (logger.isDebugEnabled())
92                      {
93                          logger.debug("read " + bytesRead + " bytes");
94                      }
95                      
96                      streamLength += bytesRead;
97                      long startOfEndBytes = 0;
98                      for (long i = 0; startDataSize < STREAM_SAMPLE_SIZE && i < bytesRead; ++i)
99                      {
100                         startData[(int) startDataSize++] = buffer[(int) i];
101                         ++startOfEndBytes; // skip data included in startData
102                     }
103                     startOfEndBytes = Math.max(startOfEndBytes, bytesRead - STREAM_SAMPLE_SIZE);
104                     for (long i = startOfEndBytes; i < bytesRead; ++i)
105                     {
106                         ++endDataSize;
107                         endData[(int) (endRingPointer++ % STREAM_SAMPLE_SIZE)] = buffer[(int) i];
108                     }
109                     if (streamLength >= targetSize)
110                     {
111                         doCallback(startData, startDataSize,
112                                 endData, endDataSize, endRingPointer,
113                                 streamLength, context);
114                     }
115                 }
116             }
117 
118             in.close();
119         }
120         catch (Exception e)
121         {
122             in.close();
123             
124             e.printStackTrace();
125             if (logger.isDebugEnabled())
126             {
127                 logger.debug(e);
128             }
129             throw e;
130         }
131         
132         return null;
133     }
134 
135     protected int read(InputStream in, byte[] buffer) throws IOException
136     {
137         return in.read(buffer);
138     }
139 
140     private void doCallback(byte[] startData, long startDataSize,
141                             byte[] endData, long endDataSize, long endRingPointer,
142                             long streamLength, MuleEventContext context) throws Exception
143     {
144         // make a nice summary of the data
145         StringBuffer result = new StringBuffer("Received stream");
146         result.append("; length: ");
147         result.append(streamLength);
148         result.append("; '");
149 
150         for (long i = 0; i < startDataSize; ++i)
151         {
152             result.append((char) startData[(int) i]);
153         }
154 
155         long endSize = Math.min(endDataSize, STREAM_SAMPLE_SIZE);
156         if (endSize > 0)
157         {
158             result.append("...");
159             for (long i = 0; i < endSize; ++i)
160             {
161                 result.append((char) endData[(int) ((endRingPointer + i) % STREAM_SAMPLE_SIZE)]);
162             }
163         }
164         result.append("'");
165 
166         summary = result.toString();
167 
168         String msg = StringMessageUtils.getBoilerPlate("Message Received in service: "
169                 + context.getFlowConstruct().getName() + ". " + summary
170                 + "\n callback: " + eventCallback,
171                 '*', 80);
172 
173         logger.info(msg);
174 
175         if (eventCallback != null)
176         {
177             eventCallback.eventReceived(context, this);
178         }
179     }
180 
181     @Override
182     public String toString()
183     {
184         return ClassUtils.getSimpleName(getClass()) + "/" + number;
185     }
186 
187 }