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