View Javadoc

1   /* (c) Copyright 2003 Caleigo AB, All rights reserved. 
2    * 
3    * This library is free software; you can redistribute it and/or
4    * modify it under the terms of the GNU Lesser General Public
5    * License as published by the Free Software Foundation; either
6    * version 2.1 of the License, or (at your option) any later version.
7    * 
8    * This library is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   * Lesser General Public License for more details.
12   * 
13   * You should have received a copy of the GNU Lesser General Public
14   * License along with this library; if not, write to the Free Software
15   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16   *  
17   */
18  
19  package org.caleigo.toolkit.tunnel;
20  
21  
22  import java.io.*;
23  import java.util.zip.*;
24  
25  import org.caleigo.toolkit.log.*;
26  import org.caleigo.toolkit.util.CircularByteBuffer;
27  
28  /***
29   *
30   * @author  Mattias Hagstrand
31   * @version 1.00
32   * 
33   *//* 
34   *
35   * WHEN        WHO               WHY & WHAT
36   * -----------------------------------------------------------------------------
37   * 2002-07-05  Mattias Hagstrand Creation
38   */
39  public class CRCPacker implements ITunnelPacker
40  {
41      // Constructors ------------------------------------------------------------
42      public CRCPacker()
43      {
44      }
45      
46      // ITunnelPacker implementation --------------------------------------------
47      
48      /***
49       * Creates and returns an InputStream that wrapps the provided InputStream.
50       * The returned InputStream can then be used to unpack messages. The returned
51       * InputStream must be stateless.
52       */
53      public InputStream createInputStream(InputStream in)
54      {
55          return new CRCPackerInputStream(in, 1024 * 64);
56      }
57      
58      /***
59       * Creates and returns an OutputStream that wrapps the provided OutputStream.
60       * The returned OutputStream can then be used to pack messages. The returned
61       * OutputStream must be stateless.
62       */
63      public OutputStream createOutputStream(OutputStream out)
64      {
65          return new CRCPackerOutputStream(out, 1024 * 64);
66      }
67      
68      // Nested classes ----------------------------------------------------------
69      protected class CRCPackerOutputStream extends FilterOutputStream
70      {
71          // Data members --------------------------------------------------------
72          protected byte[] mByteBuffer;
73          protected int mBufferSize;
74          protected int mCurrentPosition;
75          
76          protected CRC32 checker;
77          protected ObjectOutputStream mObjectOutputStream;
78          
79          // Constructors --------------------------------------------------------
80          public CRCPackerOutputStream(OutputStream out, int bufferSize)
81          {
82              super(out);
83              mByteBuffer = new byte[bufferSize];
84              mBufferSize = bufferSize;
85              checker = new CRC32();
86              try
87              {
88                  mObjectOutputStream = new ObjectOutputStream(out);
89              }
90              catch (IOException ioe)
91              {
92                  Log.printError(this, "Couldn't create object output stream", ioe);
93                  throw new IllegalArgumentException("Unable to create object output stream");
94              }
95          }
96          
97          // Superclass overrides ------------------------------------------------
98          public void write(int b)
99              throws IOException
100         {
101             // If the buffer is full increase the size of the buffer
102             if (mCurrentPosition >= mByteBuffer.length)
103             {
104                 byte[] newBuffer = new byte[mByteBuffer.length + mBufferSize / 2];
105                 System.arraycopy(mByteBuffer, 0, newBuffer, 0, mByteBuffer.length);
106                 mByteBuffer = newBuffer;
107             }
108                 
109             // Save the byte in the buffer
110             mByteBuffer[mCurrentPosition] = (byte) b;
111             mCurrentPosition++;
112         }
113         
114         public void flush()
115             throws IOException
116         {
117             checker.reset();
118             checker.update(mByteBuffer, 0, mCurrentPosition);
119             Log.print(this, "flush(): checksum=" + checker.getValue());
120             mObjectOutputStream.writeObject(new CRCDataPackage(mByteBuffer, 0, mCurrentPosition));
121             mObjectOutputStream.writeLong(checker.getValue());
122             mObjectOutputStream.flush();
123             mCurrentPosition = 0;
124         }
125     }
126     
127     protected class CRCPackerInputStream extends FilterInputStream
128     {
129         // Data members --------------------------------------------------------
130         protected byte[] mInByteBuffer;
131         protected byte[] mSingleByteBuffer = new byte[1];
132         protected CircularByteBuffer mCheckedByteBuffer;
133         
134         protected CRC32 checker;
135         protected ObjectInputStream mObjectInputStream;
136         
137         // Constructors --------------------------------------------------------
138         
139         /***
140          * Creates a CRCPackerInputStream that wrapps an InputStream.
141          *
142          * @param in   the InputStream that should be used to read the
143          *              data to from.
144          * @param bufferSize    the initial size of the internal buffer. The size
145          *                      of the buffer is increased as needed. Each time
146          *                      the buffer needs to be resized its size is increased
147          *                      by bufferSize / 2 bytes.
148          */
149         public CRCPackerInputStream(InputStream in, int bufferSize)
150         {
151             super(in);
152             mInByteBuffer = new byte[bufferSize];
153             mCheckedByteBuffer = new CircularByteBuffer(bufferSize);
154             checker = new CRC32();
155             try
156             {
157                 mObjectInputStream = new ObjectInputStream(in);
158             }
159             catch (IOException ioe)
160             {
161                 Log.printError(this, "Couldn't create object input stream", ioe);
162                 throw new IllegalArgumentException("Unable to create object input stream");
163             }
164         }
165         
166         // Superclass overrides ------------------------------------------------
167         public int available()
168             throws IOException
169         {
170             Log.print(this, "return from available(): " + mCheckedByteBuffer.getBufferSize());
171             return mCheckedByteBuffer.getBufferSize();
172         }
173         
174         public int read()
175             throws IOException
176         {
177             this.read(mSingleByteBuffer, 0, 1);
178             return mSingleByteBuffer[0];
179         }
180         
181         public int read(byte[] b, int off, int len)
182             throws IOException
183         {
184             while (mCheckedByteBuffer.getBufferSize() == 0)
185                 this.readFromCheckedStream();
186             
187             return mCheckedByteBuffer.getFromBuffer(b, off, len);
188         }
189         
190         // Help methods --------------------------------------------------------
191         protected void readFromCheckedStream()
192             throws IOException
193         {
194             CRCDataPackage dataPackage = null;
195             try
196             {
197                 dataPackage = (CRCDataPackage) mObjectInputStream.readObject();
198             }
199             catch (ClassNotFoundException cnfe)
200             {
201                 throw new IOException(cnfe.getMessage());
202             }
203             checker.reset();
204             checker.update(dataPackage.mData);
205             if (mObjectInputStream.readLong() != checker.getValue())
206                 throw new IOException("Checksum error");
207             mCheckedByteBuffer.addToBuffer(dataPackage.mData, 0, dataPackage.mData.length);
208         }
209     }
210     
211     protected static class CRCDataPackage implements Serializable
212     {
213         // Data members --------------------------------------------------------
214         protected byte[] mData;
215         
216         // Constructors --------------------------------------------------------
217         public CRCDataPackage(byte[] data, int offset, int length)
218         {
219             mData = new byte[length];
220             System.arraycopy(data, offset, mData, 0, length);
221         }
222     }
223 }