001: package com.sun.portal.util;
002:
003: import java.io.*;
004: import java.util.*;
005: import java.util.zip.*;
006:
007: /**
008: * Static utility class for Base64 encoding/decoding a byte array or <code>
009: * Serializable</code> object with support for compression and escaped HTTP
010: * strings using a variant of Base64 encoding. The Base64 implementation
011: * relies on the <code>sun.misc.*</code> package included with Sun's
012: * implementation of the Java Platform; there is no guarantee that this
013: * implementation will be used in future versions of this class. The
014: * compression features use the <code>java.util.zip</code> package. This
015: * class provides static methods only and the default constructor is hidden.
016: * Base64 encoding essentially transforms binary data into ASCII; very helpful
017: * in cases where binary data needs to be stored in documents. In support of
018: * Html documents, a variant of Base64 encoding is used which ensures that
019: * encoded Strings can be stored in HTTP/HTML constructs; these strings are
020: * referred to as <b>Http64 strings</b> in this API.
021: *
022: * @see Http64Decoder Decoding Http64 strings
023: * @see Http64Encoder Encoding Http64 strings
024: *
025: * @author Todd Fast, todd.fast@sun.com
026: * @author Mike Frisino, michael.frisino@sun.com
027: * @version JATO/1.2.2 $Id: Encoder.java,v 1.1 2005/06/15 22:23:15 rt94277 Exp $
028: *
029: * This class is copied from JATO 1.2.2 util package
030: */
031: public class Encoder extends Object {
032: /**
033: *
034: *
035: */
036: private Encoder() {
037: super ();
038: }
039:
040: /**
041: * Encodes an array of bytes into a Http64 string <b>without</b>
042: * compression using a variant of Base64 encoding. Shortcut for calling
043: * <code>{@link #encodeHttp64(byte[],int) encodeHttp64(byte[],int)}</code>
044: *
045: * @param bytes binary data to be encoded, should <b>not</b> be null
046: * @return encoded Http64 string
047: */
048: public static String encode(byte[] bytes) {
049: return encodeHttp64(bytes, Integer.MAX_VALUE);
050: }
051:
052: /**
053: * Decodes an Http64 string to an array of bytes using a variant of Base64
054: * encoding. Shortcut for calling <code>{@link #decodeHttp64(String)
055: * decodeHttp64(String)}</code>
056: *
057: * @param s Http64 string to be decoded
058: * @return decoded array of bytes or <code>s.getBytes()</code> on exception during execution
059: */
060: public static byte[] decode(String s) {
061: return decodeHttp64(s);
062: }
063:
064: ////////////////////////////////////////////////////////////////////////////////
065: // Base64 methods
066: ////////////////////////////////////////////////////////////////////////////////
067:
068: /**
069: * Encodes an array of bytes into a Base64 string, without compression.
070: * Resulting string is uniform with no carriage returns.
071: *
072: * @param bytes binary data to be encoded
073: * @return Base64 encoded string
074: */
075: public static String encodeBase64(byte[] bytes) {
076: sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
077: String result = encoder.encodeBuffer(bytes);
078:
079: // Since the JavaSoft guys put in a carriage return at 57 bytes,
080: // we need to remove them so we have one long encoded string
081: result = StringTokenizer2.replace(result, "\r", "");
082: result = StringTokenizer2.replace(result, "\n", "");
083: return result;
084: }
085:
086: /**
087: * Decodes a string using Base64 decoding into an array of bytes
088: * without compression. Strings not previously Base64 encoded will
089: * succeed.
090: *
091: * @param s string to be decoded (assumed to be Base64 encoded string)
092: * @return decoded array of bytes or <code>s.getBytes()</code> on exception during execution
093: */
094: public static byte[] decodeBase64(String s) {
095: try {
096: sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
097: return decoder.decodeBuffer(s);
098: } catch (IOException e) {
099: e.printStackTrace();
100:
101: // Ignore, use the provided text directly
102: return s.getBytes();
103: }
104: }
105:
106: ////////////////////////////////////////////////////////////////////////////////
107: // Http64 methods
108: ////////////////////////////////////////////////////////////////////////////////
109:
110: /**
111: * Encodes an array of bytes into a compressed Http64 string using
112: * a variant of Base64 encoding, performing compression if the byte
113: * array exceeds the length specified by the threshold parameter.
114: *
115: * @param bytes binary data to be encoded, should <b>not</b> be null
116: * @param compressThreshold compression peformed when byte array exceeds this value
117: * @return encoded Http64 string
118: */
119: public static String encodeHttp64(byte[] bytes,
120: int compressThreshold) {
121: byte[] result = null;
122: if (bytes.length > compressThreshold)
123: result = compress(bytes);
124: else {
125: result = new byte[bytes.length + 1];
126: result[0] = 0;
127: System.arraycopy(bytes, 0, result, 1, bytes.length);
128: }
129:
130: Http64Encoder encoder = new Http64Encoder();
131: String encodedString = encoder.encodeBuffer(result);
132:
133: // Since the JavaSoft guys put in a carriage return at 57 bytes,
134: // we need to remove them so we have one long encoded string
135: encodedString = StringTokenizer2.replace(encodedString, "\r",
136: "");
137: encodedString = StringTokenizer2.replace(encodedString, "\n",
138: "");
139:
140: return encodedString;
141: }
142:
143: /**
144: * Decode an Http64 string to an array of bytes using a variant of Base64
145: * encoding.
146: *
147: * @param s Http64 string to be decoded
148: * @return decoded array of bytes or <code>s.getBytes()</code> on exception during execution
149: *
150: */
151: public static byte[] decodeHttp64(String s) {
152: try {
153: Http64Decoder decoder = new Http64Decoder();
154: byte[] result = decoder.decodeBuffer(s);
155: return decompress(result);
156: } catch (IOException e) {
157: e.printStackTrace();
158:
159: // Ignore, use the provided text directly
160: return s.getBytes();
161: }
162: }
163:
164: ////////////////////////////////////////////////////////////////////////////////
165: // Compression methods
166: ////////////////////////////////////////////////////////////////////////////////
167:
168: /**
169: * Compresses array of bytes. Handles cases where compression is
170: * counterproductive or when there is no compression yield.
171: *
172: * @param in array of bytes to be compressed (should <b>not</b> be null)
173: * @return array of bytes in ZLIB compression format
174: */
175: public static byte[] compress(byte[] in) {
176: if (in.length == 0)
177: return in;
178:
179: Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
180: byte[] result = new byte[in.length + 1];
181:
182: // Try to deflate within the size of the buffer, reserving leading byte
183:
184: deflater.setInput(in, 0, in.length);
185: deflater.finish();
186: int compressedLength = deflater.deflate(result, 1, in.length);
187:
188: // If def.finished() is false, it indicates that we wound up
189: // with a "compressed" buffer bigger than the original. Just
190: // send along the original with a zero leading byte to indicate
191: // no compression. The decompresser needs a buffer to decompress
192: // into, so we send along a size which will suffice in the
193: // first byte.
194:
195: if (compressedLength > 0 && deflater.finished()) {
196: result[0] = (byte) ((in.length + COMPRESS_BUFFER_SIZE - 1) / COMPRESS_BUFFER_SIZE);
197: //float ratio=((float)(nlen-len)/(float)len)*100F;
198: //System.out.println("# Compressed ("+len+" bytes -> "+nlen+" bytes, "+ratio+"%)");
199:
200: return subBuffer(result, 0, compressedLength + 1);
201: } else {
202: //System.out.println("# No compression");
203: result[0] = 0;
204: System.arraycopy(in, 0, result, 1, in.length);
205: }
206:
207: return result;
208: }
209:
210: /**
211: * Decompressed array of bytes using ZLIB.
212: *
213: * @param in array of bytes to be decompressed (should <b>not</b> be null)
214: * @return decompressed array of bytes
215: */
216: public static byte[] decompress(byte[] in) {
217: if (in.length < 2)
218: return in;
219:
220: if (in[0] == 0)
221: return subBuffer(in, 1, in.length - 1);
222:
223: int bufferSize = COMPRESS_BUFFER_SIZE * in[0];
224: byte[] buffer = new byte[bufferSize];
225:
226: try {
227: Inflater inflater = new Inflater();
228: inflater.setInput(in, 1, in.length - 1);
229: bufferSize = inflater.inflate(buffer, 0, bufferSize);
230:
231: if (bufferSize == 0 || !inflater.finished())
232: throw new RuntimeException("Decompression failed");
233: } catch (DataFormatException e) {
234: throw new RuntimeException(e);
235: }
236:
237: return subBuffer(buffer, 0, bufferSize);
238: }
239:
240: /**
241: *
242: *
243: */
244: private static byte[] subBuffer(byte[] in, int idx, int len) {
245: byte[] out = new byte[len];
246: System.arraycopy(in, idx, out, 0, len);
247: return out;
248: }
249:
250: ////////////////////////////////////////////////////////////////////////////////
251: // Serialization methods
252: ////////////////////////////////////////////////////////////////////////////////
253:
254: /**
255: * Serialize or 'flatten' an object to an array of bytes with
256: * optional compression.
257: *
258: * @param o object to be flattened (should be Serializable)
259: * @param compress flag indicating need to compress results
260: * @return serialized array of bytes from input object , optionally compressed,
261: */
262: public static byte[] serialize(Serializable o, boolean compress)
263: throws IOException {
264: ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
265: DeflaterOutputStream dos = null;
266: ObjectOutputStream oos = null;
267:
268: if (compress) {
269: dos = new DeflaterOutputStream(baos, new Deflater(
270: Deflater.BEST_COMPRESSION));
271: oos = new ObjectOutputStream(dos);
272: } else {
273: oos = new ObjectOutputStream(baos);
274: }
275:
276: oos.writeObject(o);
277:
278: oos.flush();
279: oos.close();
280:
281: if (dos != null) {
282: dos.finish();
283: dos.close();
284: }
285:
286: return baos.toByteArray();
287: }
288:
289: /**
290: * Deserialize or 'reconstitute' an object from an array of bytes,
291: * which may be have been previously compressed.
292: *
293: * @param b array of bytes representing previously serialized object
294: * @param compressed flag indicating need to decompress bytes first
295: * @return reconstituted object
296: */
297: public static Object deserialize(byte[] b, boolean compressed)
298: throws IOException, ClassNotFoundException {
299: ByteArrayInputStream bais = new ByteArrayInputStream(b);
300: InflaterInputStream iis = null;
301: ObjectInputStream ois = null;
302:
303: if (compressed) {
304: iis = new InflaterInputStream(bais);
305: ois = new ObjectInputStream(iis);
306: } else {
307: ois = new ObjectInputStream(bais);
308: }
309:
310: Object result = ois.readObject();
311: return result;
312: }
313:
314: /**
315: *
316: *
317: * /
318: public static String encodeObject(Object o)
319: throws IOException
320: {
321: String result=null;
322:
323: // if (o==null)
324: // result="";
325: // else
326: // if (o instanceof String)
327: // result=(String)o;
328: // else
329: // if (!(o instanceof Serializable))
330: // result=o.toString();
331: // else
332: {
333: ByteArrayOutputStream baos=new ByteArrayOutputStream();
334: ObjectOutputStream oos=new ObjectOutputStream(baos);
335: oos.writeObject(o);
336: oos.flush();
337: oos.close();
338:
339: // A leading asterisk indicates an encoded value
340: result=ENCODED_FLAG_PREFIX_STRING+encode(baos.toByteArray(),true);
341: baos.close();
342: }
343:
344: return result;
345: }
346:
347:
348: /**
349: *
350: *
351: * /
352: public static Object decodeObject(String s)
353: throws IOException, ClassNotFoundException
354: {
355: if (s==null || s.length()==0)
356: return null;
357: else
358: if (s.charAt(0)==ENCODED_FLAG_PREFIX_CHAR)
359: {
360: // A leading asterisk indicates an encoded value
361: ByteArrayInputStream bais=
362: new ByteArrayInputStream(decode(s.substring(1),true));
363: ObjectInputStream ois=new ObjectInputStream(bais);
364: return ois.readObject();
365: }
366: else
367: {
368: return s;
369: }
370: }
371:
372:
373:
374:
375: ////////////////////////////////////////////////////////////////////////////////
376: // Class variables
377: ////////////////////////////////////////////////////////////////////////////////
378:
379: /*
380: public static final String ENCODED_FLAG_PREFIX_STRING="*";
381: public static final char ENCODED_FLAG_PREFIX_CHAR='*';
382: */
383: private static final int COMPRESS_BUFFER_SIZE = 1024;
384:
385: ////////////////////////////////////////////////////////////////////////////////
386: // Member variables
387: ////////////////////////////////////////////////////////////////////////////////
388:
389: }
|