001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.util.io; 016 017 import java.io.BufferedInputStream; 018 import java.io.BufferedOutputStream; 019 import java.io.ByteArrayInputStream; 020 import java.io.ByteArrayOutputStream; 021 import java.io.InputStream; 022 import java.io.ObjectInputStream; 023 import java.io.ObjectOutputStream; 024 import java.io.Serializable; 025 import java.util.zip.GZIPInputStream; 026 import java.util.zip.GZIPOutputStream; 027 028 import org.apache.commons.codec.binary.Base64; 029 import org.apache.hivemind.ApplicationRuntimeException; 030 import org.apache.hivemind.ClassResolver; 031 import org.apache.tapestry.services.DataSqueezer; 032 033 /** 034 * The most complicated of the adaptors, this one takes an arbitrary serializable object, serializes 035 * it to binary (possibly encoding it), and encodes it in a Base64 encoding. 036 * <p> 037 * Encoding and decoding of Base64 strings uses code adapted from work in the public domain 038 * originally written by Jonathan Knudsen and published in O'reilly's "Java Cryptography". Note that 039 * we use a <em>modified</em> form of Base64 encoding, with URL-safe characters to encode the 62 040 * and 63 values and the pad character. 041 * <p> 042 * TBD: Work out some class loader issues involved in deserializing. 043 * 044 * @author Howard Lewis Ship 045 */ 046 047 public class SerializableAdaptor implements SqueezeAdaptor 048 { 049 private ClassResolver _resolver; 050 051 private static final char BYTESTREAM_PREFIX = 'O'; 052 053 private static final char GZIP_BYTESTREAM_PREFIX = 'Z'; 054 055 // O is for an object stream rendered as MIME 056 // Z is for on object stream, compressed, rendered as MIME 057 058 private static final String PREFIX = "OZ"; 059 060 public String getPrefix() 061 { 062 return PREFIX; 063 } 064 065 public Class getDataClass() 066 { 067 return Serializable.class; 068 } 069 070 public String squeeze(DataSqueezer squeezer, Object data) 071 { 072 try 073 { 074 ByteArrayOutputStream bosPlain = new ByteArrayOutputStream(); 075 ByteArrayOutputStream bosCompressed = new ByteArrayOutputStream(); 076 077 GZIPOutputStream gos = new GZIPOutputStream(bosCompressed); 078 079 TeeOutputStream tos = new TeeOutputStream(bosPlain, gos); 080 081 ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(tos)); 082 083 oos.writeObject(data); 084 085 oos.close(); 086 087 boolean useCompressed = bosCompressed.size() < bosPlain.size(); 088 089 byte[] byteArray = useCompressed ? bosCompressed.toByteArray() : bosPlain.toByteArray(); 090 091 byte[] encoded = Base64.encodeBase64(byteArray); 092 093 String prefix = Character.toString(useCompressed ? GZIP_BYTESTREAM_PREFIX 094 : BYTESTREAM_PREFIX); 095 096 return prefix + new String(encoded); 097 } 098 catch (Exception ex) 099 { 100 throw new ApplicationRuntimeException(IoMessages.encodeFailure(data, ex), ex); 101 } 102 } 103 104 public Object unsqueeze(DataSqueezer squeezer, String encoded) 105 { 106 char prefix = encoded.charAt(0); 107 108 try 109 { 110 // Strip off the prefix, feed that in as a MIME stream. 111 112 byte[] mimeData = encoded.substring(1).getBytes(); 113 114 byte[] decoded = Base64.decodeBase64(mimeData); 115 116 InputStream is = new ByteArrayInputStream(decoded); 117 118 if (prefix == GZIP_BYTESTREAM_PREFIX) 119 is = new GZIPInputStream(is); 120 121 is = new BufferedInputStream(is); 122 123 ObjectInputStream ois = new ResolvingObjectInputStream(_resolver, is); 124 125 Object result = ois.readObject(); 126 127 ois.close(); 128 129 return result; 130 } 131 catch (Exception ex) 132 { 133 throw new ApplicationRuntimeException(IoMessages.decodeFailure(ex), ex); 134 } 135 } 136 137 public void setResolver(ClassResolver resolver) 138 { 139 _resolver = resolver; 140 } 141 142 }