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 015package org.apache.tapestry.util.io; 016 017import java.io.BufferedInputStream; 018import java.io.BufferedOutputStream; 019import java.io.ByteArrayInputStream; 020import java.io.ByteArrayOutputStream; 021import java.io.InputStream; 022import java.io.ObjectInputStream; 023import java.io.ObjectOutputStream; 024import java.io.Serializable; 025import java.util.zip.GZIPInputStream; 026import java.util.zip.GZIPOutputStream; 027 028import org.apache.commons.codec.binary.Base64; 029import org.apache.hivemind.ApplicationRuntimeException; 030import org.apache.hivemind.ClassResolver; 031import 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 compressing the stream along the way), and encodes it in a Base64 036 * encoding. The first character of the squeezed stream indicates whether it is or is not encoded. 037 * 038 * @author Howard Lewis Ship 039 */ 040 041public class SerializableAdaptor implements SqueezeAdaptor 042{ 043 private ClassResolver _resolver; 044 045 private static final char BYTESTREAM_PREFIX = 'O'; 046 047 private static final char GZIP_BYTESTREAM_PREFIX = 'Z'; 048 049 // O is for an object stream rendered as MIME 050 // Z is for on object stream, compressed, rendered as MIME 051 052 private static final String PREFIX = "OZ"; 053 054 public String getPrefix() 055 { 056 return PREFIX; 057 } 058 059 public Class getDataClass() 060 { 061 return Serializable.class; 062 } 063 064 public String squeeze(DataSqueezer squeezer, Object data) 065 { 066 try 067 { 068 ByteArrayOutputStream bosPlain = new ByteArrayOutputStream(); 069 ByteArrayOutputStream bosCompressed = new ByteArrayOutputStream(); 070 071 GZIPOutputStream gos = new GZIPOutputStream(bosCompressed); 072 073 TeeOutputStream tos = new TeeOutputStream(bosPlain, gos); 074 075 ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(tos)); 076 077 oos.writeObject(data); 078 079 oos.close(); 080 081 boolean useCompressed = bosCompressed.size() < bosPlain.size(); 082 083 byte[] byteArray = useCompressed ? bosCompressed.toByteArray() : bosPlain.toByteArray(); 084 085 byte[] encoded = Base64.encodeBase64(byteArray); 086 087 String prefix = Character.toString(useCompressed ? GZIP_BYTESTREAM_PREFIX 088 : BYTESTREAM_PREFIX); 089 090 return prefix + new String(encoded); 091 } 092 catch (Exception ex) 093 { 094 throw new ApplicationRuntimeException(IoMessages.encodeFailure(data, ex), ex); 095 } 096 } 097 098 public Object unsqueeze(DataSqueezer squeezer, String encoded) 099 { 100 char prefix = encoded.charAt(0); 101 102 try 103 { 104 // Strip off the prefix, feed that in as a MIME stream. 105 106 byte[] mimeData = encoded.substring(1).getBytes(); 107 108 byte[] decoded = Base64.decodeBase64(mimeData); 109 110 InputStream is = new ByteArrayInputStream(decoded); 111 112 if (prefix == GZIP_BYTESTREAM_PREFIX) 113 is = new GZIPInputStream(is); 114 115 is = new BufferedInputStream(is); 116 117 ObjectInputStream ois = new ResolvingObjectInputStream(_resolver, is); 118 119 Object result = ois.readObject(); 120 121 ois.close(); 122 123 return result; 124 } 125 catch (Exception ex) 126 { 127 throw new ApplicationRuntimeException(IoMessages.decodeFailure(ex), ex); 128 } 129 } 130 131 public void setResolver(ClassResolver resolver) 132 { 133 _resolver = resolver; 134 } 135 136}