View Javadoc

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