KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tapestry > util > io > SerializableAdaptor


1 // Copyright 2004, 2005 The Apache Software Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15 package org.apache.tapestry.util.io;
16
17 import java.io.ByteArrayInputStream JavaDoc;
18 import java.io.ByteArrayOutputStream JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21 import java.io.ObjectInputStream JavaDoc;
22 import java.io.ObjectOutputStream JavaDoc;
23 import java.io.OutputStream JavaDoc;
24 import java.io.Serializable JavaDoc;
25 import java.util.zip.GZIPInputStream JavaDoc;
26 import java.util.zip.GZIPOutputStream JavaDoc;
27
28 import org.apache.tapestry.Tapestry;
29 import org.apache.tapestry.services.DataSqueezer;
30
31 /**
32  * The most complicated of the adaptors, this one takes an arbitrary serializable
33  * object, serializes it to binary, and encodes it in a Base64 encoding.
34  *
35  * <p>Encoding and decoding of Base64 strings uses code adapted from work in the public
36  * domain originally written by Jonathan Knudsen and published in
37  * O'reilly's "Java Cryptography". Note that we use a <em>modified</em> form of Base64 encoding,
38  * with URL-safe characters to encode the 62 and 63 values and the pad character.
39  *
40  * <p>TBD: Work out some class loader issues involved in deserializing.
41  *
42  * @author Howard Lewis Ship
43  *
44  **/

45
46 class SerializableAdaptor implements ISqueezeAdaptor
47 {
48     private static final String JavaDoc PREFIX = "O";
49
50     /**
51      * The PAD character, appended to the end of the string to make things
52      * line up. In normal Base64, this is the character '='.
53      *
54      **/

55
56     private static final char PAD = '.';
57
58     /**
59      * Representation for the 6-bit code 63, normally '+' in Base64.
60      *
61      **/

62
63     private static final char CH_62 = '-';
64
65     /**
66      * Representation for the 6-bit code 64, normally '/' in Base64.
67      *
68      **/

69
70     private static final char CH_63 = '_';
71
72     public String JavaDoc squeeze(DataSqueezer squeezer, Object JavaDoc data) throws IOException JavaDoc
73     {
74         ByteArrayOutputStream JavaDoc bos = null;
75         GZIPOutputStream JavaDoc gos = null;
76         ObjectOutputStream JavaDoc oos = null;
77         byte[] byteData = null;
78
79         try
80         {
81             bos = new ByteArrayOutputStream JavaDoc();
82             gos = new GZIPOutputStream JavaDoc(bos);
83             oos = new ObjectOutputStream JavaDoc(gos);
84
85             oos.writeObject(data);
86             oos.close();
87         }
88         finally
89         {
90             close(oos);
91             close(gos);
92             close(bos);
93         }
94
95         byteData = bos.toByteArray();
96
97         StringBuffer JavaDoc encoded = new StringBuffer JavaDoc(2 * byteData.length);
98         char[] base64 = new char[4];
99
100         encoded.append(PREFIX);
101
102         for (int i = 0; i < byteData.length; i += 3)
103         {
104             encodeBlock(byteData, i, base64);
105             encoded.append(base64);
106         }
107
108         return encoded.toString();
109     }
110
111     private void close(OutputStream JavaDoc stream)
112     {
113         if (stream != null)
114         {
115             try
116             {
117                 stream.close();
118             }
119             catch (IOException JavaDoc ex)
120             {
121                 // Ignore.
122
}
123         }
124     }
125
126     private void close(InputStream JavaDoc stream)
127     {
128         if (stream != null)
129         {
130             try
131             {
132                 stream.close();
133             }
134             catch (IOException JavaDoc ex)
135             {
136                 // Ignore.
137
}
138         }
139     }
140     public Object JavaDoc unsqueeze(DataSqueezer squeezer, String JavaDoc string) throws IOException JavaDoc
141     {
142         ByteArrayInputStream JavaDoc bis = null;
143         GZIPInputStream JavaDoc gis = null;
144         ObjectInputStream JavaDoc ois = null;
145         byte[] byteData;
146
147         // Strip off the first character and decode the rest.
148

149         byteData = decode(string.substring(1));
150
151         try
152         {
153             bis = new ByteArrayInputStream JavaDoc(byteData);
154             gis = new GZIPInputStream JavaDoc(bis);
155             ois = new ResolvingObjectInputStream(squeezer.getResolver(), gis);
156
157             return ois.readObject();
158         }
159         catch (ClassNotFoundException JavaDoc ex)
160         {
161             // The message is the name of the class.
162

163             throw new IOException JavaDoc(
164                 Tapestry.format("SerializableAdaptor.class-not-found", ex.getMessage()));
165         }
166         finally
167         {
168             close(ois);
169             close(gis);
170             close(bis);
171         }
172     }
173
174     public void register(DataSqueezer squeezer)
175     {
176         squeezer.register(PREFIX, Serializable JavaDoc.class, this);
177     }
178
179     private static void encodeBlock(byte[] raw, int offset, char[] base64) throws IOException JavaDoc
180     {
181         int block = 0;
182         int slack = raw.length - offset - 1;
183         int end = (slack >= 2) ? 2 : slack;
184
185         for (int i = 0; i <= end; i++)
186         {
187             byte b = raw[offset + i];
188             int neuter = (b < 0) ? b + 256 : b;
189             block += neuter << (8 * (2 - i));
190         }
191
192         for (int i = 0; i < 4; i++)
193         {
194             int sixbit = (block >>> (6 * (3 - i))) & 0x3f;
195             base64[i] = getChar(sixbit);
196         }
197
198         if (slack < 1)
199             base64[2] = PAD;
200
201         if (slack < 2)
202             base64[3] = PAD;
203     }
204
205     protected static char getChar(int sixBit) throws IOException JavaDoc
206     {
207         if (sixBit >= 0 && sixBit <= 25)
208             return (char) ('A' + sixBit);
209
210         if (sixBit >= 26 && sixBit <= 51)
211             return (char) ('a' + (sixBit - 26));
212
213         if (sixBit >= 52 && sixBit <= 61)
214             return (char) ('0' + (sixBit - 52));
215
216         if (sixBit == 62)
217             return CH_62;
218
219         if (sixBit == 63)
220             return CH_63;
221
222         throw new IOException JavaDoc(
223             Tapestry.format("SerializableAdaptor.unable-to-convert", Integer.toString(sixBit)));
224     }
225
226     public static byte[] decode(String JavaDoc string) throws IOException JavaDoc
227     {
228         int pad = 0;
229         char[] base64 = string.toCharArray();
230
231         for (int i = base64.length - 1; base64[i] == PAD; i--)
232             pad++;
233
234         int length = base64.length * 6 / 8 - pad;
235         byte[] raw = new byte[length];
236         int rawIndex = 0;
237
238         for (int i = 0; i < base64.length; i += 4)
239         {
240             int block =
241                 (getValue(base64[i]) << 18)
242                     + (getValue(base64[i + 1]) << 12)
243                     + (getValue(base64[i + 2]) << 6)
244                     + (getValue(base64[i + 3]));
245
246             for (int j = 0; j < 3 && rawIndex + j < raw.length; j++)
247                 raw[rawIndex + j] = (byte) ((block >> (8 * (2 - j))) & 0xff);
248
249             rawIndex += 3;
250         }
251
252         return raw;
253     }
254
255     private static int getValue(char c) throws IOException JavaDoc
256     {
257         if (c >= 'A' && c <= 'Z')
258             return c - 'A';
259
260         if (c >= 'a' && c <= 'z')
261             return c - 'a' + 26;
262
263         if (c >= '0' && c <= '9')
264             return c - '0' + 52;
265
266         if (c == CH_62)
267             return 62;
268
269         if (c == CH_63)
270             return 63;
271
272         // Pad character
273

274         if (c == PAD)
275             return 0;
276
277         throw new IOException JavaDoc(
278             Tapestry.format(
279                 "SerializableAdaptor.unable-to-interpret-char",
280                 new String JavaDoc(new char[] { c })));
281     }
282
283 }
Popular Tags