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.transport.tcp.protocols;
8   
9   import java.io.DataInputStream;
10  import java.io.DataOutputStream;
11  import java.io.IOException;
12  import java.io.InputStream;
13  import java.io.OutputStream;
14  
15  import org.apache.commons.logging.Log;
16  import org.apache.commons.logging.LogFactory;
17  
18  /**
19   * The LengthProtocol is an application level tcp protocol that can be used to
20   * transfer large amounts of data without risking some data to be loss. The protocol
21   * is defined by sending / reading an integer (the packet length) and then the data
22   * to be transferred.
23   *
24   * <p>Note that use of this protocol must be symmetric - both the sending and receiving
25   * connectors must use the same protocol.</p>
26   */
27  public class LengthProtocol extends DirectProtocol
28  {
29      
30      private static final Log logger = LogFactory.getLog(LengthProtocol.class);
31      // TODO - can we not get this from the API somewhere?
32      private static final int SIZE_INT = 4;
33      public static final int NO_MAX_LENGTH = -1;
34      private int maxMessageLength;
35  
36      public LengthProtocol()
37      {
38          this(NO_MAX_LENGTH);
39      }
40  
41      public LengthProtocol(int maxMessageLength)
42      {
43          super(NO_STREAM, SIZE_INT);
44          this.setMaxMessageLength(maxMessageLength);
45      }
46  
47      public Object read(InputStream is) throws IOException
48      {
49          // original comments indicated that we need to use read(byte[]) rather than readInt()
50          // to avoid socket timeouts - don't understand, but don't want to risk change.
51  
52          // first read the data necessary to know the length of the payload
53          DataInputStream dis = new DataInputStream(is);
54          dis.mark(SIZE_INT);
55          // this pulls through SIZE_INT bytes
56          if (null == super.read(dis, SIZE_INT))
57          {
58              return null; // eof
59          }
60  
61          // reset and read the integer
62          dis.reset();
63          int length = dis.readInt();
64          if (logger.isDebugEnabled())
65          {
66              logger.debug("length: " + length);
67          }
68  
69          if (length < 0 || (getMaxMessageLength() > 0 && length > getMaxMessageLength()))
70          {
71              throw new IOException("Length " + length + " exceeds limit: " + getMaxMessageLength());
72          }
73  
74          // finally read the rest of the data
75          byte[] buffer = new byte[length];
76          dis.readFully(buffer);
77          if (logger.isDebugEnabled())
78          {
79              logger.debug("length read: " + buffer.length);
80          }
81  
82          return buffer;
83      }
84  
85      @Override
86      protected void writeByteArray(OutputStream os, byte[] data) throws IOException
87      {
88          // Write the length and then the data.
89          DataOutputStream dos = new DataOutputStream(os);
90          dos.writeInt(data.length);
91          dos.write(data);
92          // DataOutputStream size is SIZE_INT + the byte length, due to the writeInt call
93          // this should fix EE-1494
94          if (dos.size() != data.length + SIZE_INT)
95          {
96              // only flush if the sizes don't match up
97              dos.flush();
98          }
99      }
100 
101     /**
102      * Read all four bytes for initial integer (limit is set in read)
103      *
104      * @param len Amount transferred last call (-1 on EOF or socket error)
105      * @param available Amount available
106      * @return true if the transfer should continue
107      */
108     @Override
109     protected boolean isRepeat(int len, int available)
110     {
111         return true;
112     }
113 
114     public int getMaxMessageLength()
115     {
116         return maxMessageLength;
117     }
118 
119     public void setMaxMessageLength(int maxMessageLength)
120     {
121         this.maxMessageLength = maxMessageLength;
122     }
123     
124 }