1 /* ZNet - Java Compression Layer for a new Socket Factory
2 Copyright (C) 1999, Free Software Rulez
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18 The author of this program may be contacted at morgiaclaudio@yahoo.it. */
19
20 package java.net;
21
22 import java.lang;
23 import java.io;
24 import java.util.Arrays;
25
26 /**
27 * This class implements an object type suitable to be serialized in a non-standard way.
28 * To minimize the overhead that can occur in standard serialization, this class provides
29 * methods to serialize and deserialize objects minimizing the overhead.
30 * This class can represents compressed, uncompressed and ack objects.
31 * @author <a href="mailto:morgiaclaudio@yahoo.it">Claudio Morgia</a>
32 * @version 1.0
33 */
34 public class ZObject {
35 /**
36 * This static field represents the magic sequence that distinguishes serialized
37 * ZObject instances.
38 */
39 static byte[] magic={0xc,0xa,0xf,0xe};
40
41 /**
42 * The data payload this object carries. If it isn't an ack object, that buffer contains compressed
43 * data.
44 */
45 protected byte[] data;
46
47 /**
48 * This field can be one of the following type:<br>
49 * <ul>
50 * <li>ACK_TYPE for ack object,
51 * <li>COMPR_TYPE for compressed object,
52 * <li>UNCOMPR_TYPE for uncompressed object (actually nonsense).
53 * </ul>
54 */
55 protected byte type;
56
57 /**
58 * The sequence number that distinguishes that object.
59 * If it's an ack object, the sequence number is the same as the compressed object
60 * that <i>generated</i> it, otherwise is automagically generated (using the method
61 * {@link java.lang.Object#hashCode hashCode} applied to the data payload.
62 */
63 protected long seqnum;
64
65 public static byte ACK_TYPE=0,COMPR_TYPE=1,UNCOMPR_TYPE=2;
66
67 /**
68 * Constructor used to build an <b>ack</b> object with a well-defined sequence number.
69 * @param seqnum the sequence number to ack
70 */
71 public ZObject(long seqnum) {
72 this.seqnum=seqnum;
73 type=ACK_TYPE;
74 }
75
76 /**
77 * Constructor used to build an object by deserializing data coming from an input stream,
78 * tipically a socket input stream.
79 * @see #setData(InputStream)
80 * @throws java.net.AckException if the object is an ack object
81 * @throws java.io.IOException if the object is not a ZObject instance or something else goes wrong
82 */
83 public ZObject(InputStream in) throws IOException,AckException {
84 setData(in);
85 }
86
87 /**
88 * Constructor used to build an object by deserializing data coming from the buffer contained in
89 * the passed datagram.
90 * @see #setData(byte[])
91 * @throws java.net.AckException if the object is an ack object
92 * @throws java.net.NoMagicException if the object is not a ZObject instance
93 * @throws java.io.IOException if something goes wrong
94 */
95 public ZObject(DatagramPacket packet) throws AckException,NoMagicException,IOException {
96 setData(packet.getData());
97 }
98
99 /**
100 * This method is responsible for generic object creation and it's an utility method used by
101 * constructors.
102 * @param _data the data payload
103 * @param type the object instance's type
104 */
105 protected void create(byte[] _data,byte type) {
106 this.data=_data;
107 this.type=type;
108 seqnum=Math.abs(data.hashCode());
109 }
110
111 /**
112 * Constructor used to build a generic ZObject instance.
113 * @param _data the data payload to encode
114 * @param type the type of the instance
115 * @throws java.io.IOException never thrown (actually)
116 */
117 public ZObject(byte[] _data,byte type) throws IOException {
118 create(_data,type);
119 }
120
121 /**
122 * Constructor used to build a compressed object with a specific data payload.
123 * @param _data the data payload to encode
124 * @throws java.io.IOException never thrown (actually)
125 */
126 public ZObject(byte[] _data) throws IOException {
127 create(_data,COMPR_TYPE);
128 }
129
130 /**
131 * This method actually performs encoding on the internal object using a ByteArrayOutputStream
132 * as a container where internal fields get written into.
133 * @return the encoded object in form of a byte array
134 * @throws java.io.IOException if something goes wrong writing fields to the container
135 * @see #serialize(long) serialization shrinked to the <b>long</b> case
136 */
137 public byte[] prepareData() throws IOException {
138 ByteArrayOutputStream out=new ByteArrayOutputStream();
139 out.write(magic);
140 out.write(type);
141 out.write(serialize(seqnum));
142 if (type!=ACK_TYPE) {
143 out.write(serialize(data.length));
144 out.write(data);
145 }
146 return out.toByteArray();
147 }
148
149 /**
150 * This method returns the carried sequence number.
151 * @return the carried sequence number
152 */
153 public long getSeqNum() {
154 return seqnum;
155 }
156
157 /**
158 * This method returns the carried data payload.
159 * @return the data payload in form of a byte array
160 */
161 public byte[] getData() {
162 return data;
163 }
164
165 /**
166 * This method converts a passed <i>long</i> into a sequence of four bytes that can be written
167 * to an output stream.
168 * @param num the <i>long</i> to convert
169 * @return an array of four bytes representing the passed <i>long</i>
170 */
171 protected static byte[] serialize(long num) {
172 byte[] res=new byte[4];
173 for (int i=0; i<4 ; i++) {
174 res[i]=(byte)(num & 0xff);
175 num>>=8;
176 }
177 return res;
178 }
179
180 /**
181 * This method positivize the passed long using steps of value 256. This ensure the following relation
182 * always true:
183 * <code><result> mod 256==x mod 256</code>
184 */
185 protected static long positivize(long x) {
186 while (x<0)
187 x+=256;
188 return x;
189 }
190
191 /**
192 * This method decodes an array of four bytes into a long value.
193 * @param buf an array of four bytes
194 * @return the decoded long value
195 * @see #positivize(long)
196 */
197 protected static long deserialize(byte[] buf) {
198 int i;
199 long res=0;
200 for (i=3; i>0; i--) {
201 res+=positivize(buf[i]);
202 res<<=8;
203 }
204 res+=positivize(buf[i]);
205 return res;
206 }
207
208 /**
209 * This method decodes the passed byte array trying to deserialize a ZObject instance.
210 * @see #setData(InputStream)
211 * @param dat the byte array that contains data to decode
212 * @throws java.io.IOException actually never thrown
213 * @throws java.net.AckException if the decoded object is an ack-type obejct
214 */
215 public void setData(byte[] dat) throws IOException,AckException {
216 setData(new ByteArrayInputStream(dat));
217 }
218
219 public synchronized boolean dump(byte[] buf,int len) {
220 D.p("---------------------------");
221 for (int i=0;i<len;i++)
222 D.p("["+i+"]="+Long.toHexString(((long)buf[i])&0xff));
223 return false;
224 }
225
226 /**
227 * This method decodes data from the input stream trying to deserialize a ZObject instance.
228 * @param in the input stream from which data will be read
229 * @throws java.io.IOException if data cannot be read from the input stream or object is not a ZObject instance
230 * @throws java.net.AckException if the decoded object is an ack-type object
231 */
232 public void setData(InputStream in) throws IOException,AckException {
233 int dataLength;
234
235 byte[] buf=new byte[4];
236
237 if ((in.read(buf,0,4)==-1) || !Arrays.equals(buf,magic))
238 throw new NoMagicException("No magic");
239
240 if ((in.read(buf,0,1)==-1))
241 throw new IOException("Bad format");
242
243 type=buf[0];
244
245 buf=new byte[4];
246 if ((in.read(buf,0,4)==-1))
247 throw new IOException("Bad format in sequence number");
248
249 seqnum=deserialize(buf);
250
251 if (type==ACK_TYPE)
252 throw new AckException(seqnum);
253
254 if ((in.read(buf,0,4)==-1))
255 throw new IOException("No data before header");
256
257 dataLength=(int)deserialize(buf);
258 buf=new byte[1024];
259 int length;
260 ByteArrayOutputStream databuf=new ByteArrayOutputStream();
261 while ((length=in.read(buf,0,Math.min(dataLength,1024)))!=0) {
262 databuf.write(buf,0,length);
263 dataLength-=length;
264 }
265
266 data=databuf.toByteArray();
267 }
268
269 public String toString() {
270 try {
271 return new String(getData());
272 } catch(Exception e) {
273 return new String();
274 }
275 }
276 }