001/** 002 * =================================================== 003 * JCommon-Serializer : a free serialization framework 004 * =================================================== 005 * 006 * Project Info: http://reporting.pentaho.org/jcommon-serializer/ 007 * 008 * (C) Copyright 2006-2008, by Object Refinery Limited, Pentaho Corporation and Contributors. 009 * 010 * This library is free software; you can redistribute it and/or modify it under the terms 011 * of the GNU Lesser General Public License as published by the Free Software Foundation; 012 * either version 2.1 of the License, or (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 015 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 016 * See the GNU Lesser General Public License for more details. 017 * 018 * You should have received a copy of the GNU Lesser General Public License along with this 019 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 020 * Boston, MA 02111-1307, USA. 021 * 022 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 023 * in the United States and other countries.] 024 * 025 * ------------ 026 * SerializerHelper 027 * ------------ 028 */ 029 030package org.jfree.serializer; 031 032import java.io.IOException; 033import java.io.NotSerializableException; 034import java.io.ObjectInputStream; 035import java.io.ObjectOutputStream; 036import java.io.Serializable; 037import java.util.HashMap; 038import java.util.Iterator; 039 040import org.pentaho.reporting.libraries.base.config.Configuration; 041import org.pentaho.reporting.libraries.base.util.ObjectUtilities; 042import java.util.logging.Level; 043import java.util.logging.Logger; 044 045/** 046 * The SerializeHelper is used to make implementing custom serialization 047 * handlers easier. Handlers for certain object types need to be added to this 048 * helper before this implementation is usable. 049 * 050 * @author Thomas Morgner 051 */ 052public class SerializerHelper 053{ 054 private static Logger logger = Logger.getLogger(SerializerHelper.class.getName()); 055 /** 056 * The singleton instance of the serialize helper. 057 */ 058 private static SerializerHelper singleton; 059 060 /** 061 * Returns or creates a new SerializerHelper. When a new instance is created 062 * by this method, all known SerializeMethods are registered. 063 * 064 * @return the SerializerHelper singleton instance. 065 */ 066 public static synchronized SerializerHelper getInstance() 067 { 068 if (singleton == null) 069 { 070 singleton = new SerializerHelper(); 071 singleton.registerMethods(); 072 } 073 return singleton; 074 } 075 076 077 /** 078 * This method can be used to replace the singleton instance of this helper. 079 * 080 * @param helper the new instance of the serialize helper. 081 */ 082 protected static void setInstance(final SerializerHelper helper) 083 { 084 singleton = helper; 085 } 086 087 /** 088 * A collection of the serializer methods. 089 */ 090 private final HashMap methods; 091 092 /** 093 * A class comparator for searching the super class of an certain class. 094 */ 095 private final ClassComparator comparator; 096 097 /** 098 * Creates a new SerializerHelper. 099 */ 100 protected SerializerHelper() 101 { 102 this.comparator = new ClassComparator(); 103 this.methods = new HashMap(); 104 } 105 106 /** 107 * Registers a new SerializeMethod with this SerializerHelper. 108 * 109 * @param method the method that should be registered. 110 */ 111 public synchronized void registerMethod(final SerializeMethod method) 112 { 113 this.methods.put(method.getObjectClass(), method); 114 } 115 116 /** 117 * Traverses the configuration and registers all serialization handlers in this factory. 118 */ 119 protected void registerMethods() 120 { 121 final Configuration config = JCommonSerializerBoot.getInstance().getGlobalConfig(); 122 final Iterator sit = config.findPropertyKeys("org.jfree.serializer.handler."); 123 124 while (sit.hasNext()) 125 { 126 final String configkey = (String) sit.next(); 127 final String c = config.getConfigProperty(configkey); 128 final Object maybeModule = ObjectUtilities.loadAndInstantiate 129 (c, SerializerHelper.class, SerializeMethod.class); 130 if (maybeModule != null) 131 { 132 final SerializeMethod module = (SerializeMethod) maybeModule; 133 registerMethod(module); 134 } 135 else 136 { 137 logger.warning("Invalid SerializeMethod implementation: " + c); 138 } 139 } 140 } 141 142 /** 143 * Deregisters a new SerializeMethod with this SerializerHelper. 144 * 145 * @param method the method that should be deregistered. 146 */ 147 public synchronized void unregisterMethod(final SerializeMethod method) 148 { 149 this.methods.remove(method.getObjectClass()); 150 } 151 152 /** 153 * Returns the collection of all registered serialize methods. 154 * 155 * @return a collection of the registered serialize methods. 156 */ 157 protected HashMap getMethods() 158 { 159 return methods; 160 } 161 162 /** 163 * Returns the class comparator instance used to find correct super classes. 164 * 165 * @return the class comparator. 166 */ 167 protected ClassComparator getComparator() 168 { 169 return comparator; 170 } 171 172 /** 173 * Looks up the SerializeMethod for the given class or null if there is no 174 * SerializeMethod for the given class. 175 * 176 * @param c the class for which we want to lookup a serialize method. 177 * @return the method or null, if there is no registered method for the 178 * class. 179 */ 180 protected SerializeMethod getSerializer(final Class c) 181 { 182 final SerializeMethod sm = (SerializeMethod) methods.get(c); 183 if (sm != null) 184 { 185 return sm; 186 } 187 return getSuperClassObjectDescription(c); 188 } 189 190 /** 191 * Looks up the SerializeMethod for the given class or null if there is no 192 * SerializeMethod for the given class. This method searches all 193 * superclasses. 194 * 195 * @param d the class for which we want to lookup a serialize 196 * method. 197 * @return the method or null, if there is no registered method for the 198 * class. 199 */ 200 protected SerializeMethod getSuperClassObjectDescription 201 (final Class d) 202 { 203 SerializeMethod knownSuperClass = null; 204 final Iterator keys = methods.keySet().iterator(); 205 while (keys.hasNext()) 206 { 207 final Class keyClass = (Class) keys.next(); 208 if (keyClass.isAssignableFrom(d)) 209 { 210 final SerializeMethod od = (SerializeMethod) methods.get(keyClass); 211 if (knownSuperClass == null) 212 { 213 knownSuperClass = od; 214 } 215 else 216 { 217 if (comparator.isComparable 218 (knownSuperClass.getObjectClass(), od.getObjectClass())) 219 { 220 if (comparator.compare 221 (knownSuperClass.getObjectClass(), od.getObjectClass()) < 0) 222 { 223 knownSuperClass = od; 224 } 225 } 226 } 227 } 228 } 229 return knownSuperClass; 230 } 231 232 233 /** 234 * Writes a serializable object description to the given object output stream. 235 * This method selects the best serialize helper method for the given object. 236 * 237 * @param o the to be serialized object. 238 * @param out the outputstream that should receive the object. 239 * @throws IOException if an I/O error occured. 240 */ 241 public synchronized void writeObject(final Object o, 242 final ObjectOutputStream out) 243 throws IOException 244 { 245 if (o == null) 246 { 247 out.writeByte(0); 248 return; 249 } 250 if (o instanceof Serializable) 251 { 252 out.writeByte(1); 253 out.writeObject(o); 254 return; 255 } 256 257 final SerializeMethod m = getSerializer(o.getClass()); 258 if (m == null) 259 { 260 throw new NotSerializableException(o.getClass().getName()); 261 } 262 out.writeByte(2); 263 out.writeObject(m.getObjectClass()); 264 m.writeObject(o, out); 265 } 266 267 /** 268 * Reads the object from the object input stream. This object selects the best 269 * serializer to read the object. 270 * <p/> 271 * Make sure, that you use the same configuration (library and class versions, 272 * registered methods in the SerializerHelper) for reading as you used for 273 * writing. 274 * 275 * @param in the object input stream from where to read the serialized data. 276 * @return the generated object. 277 * @throws IOException if reading the stream failed. 278 * @throws ClassNotFoundException if serialized object class cannot be found. 279 */ 280 public synchronized Object readObject(final ObjectInputStream in) 281 throws IOException, ClassNotFoundException 282 { 283 final int type = in.readByte(); 284 if (type == 0) 285 { 286 return null; 287 } 288 if (type == 1) 289 { 290 return in.readObject(); 291 } 292 final Class c = (Class) in.readObject(); 293 final SerializeMethod m = getSerializer(c); 294 if (m == null) 295 { 296 throw new NotSerializableException(c.getName()); 297 } 298 return m.readObject(in); 299 } 300}