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 030 package org.jfree.serializer; 031 032 import java.io.IOException; 033 import java.io.NotSerializableException; 034 import java.io.ObjectInputStream; 035 import java.io.ObjectOutputStream; 036 import java.io.Serializable; 037 import java.util.HashMap; 038 import java.util.Iterator; 039 040 import org.pentaho.reporting.libraries.base.config.Configuration; 041 import org.pentaho.reporting.libraries.base.util.ObjectUtilities; 042 import org.apache.commons.logging.Log; 043 import org.apache.commons.logging.LogFactory; 044 045 046 /** 047 * The SerializeHelper is used to make implementing custom serialization 048 * handlers easier. Handlers for certain object types need to be added to this 049 * helper before this implementation is usable. 050 * 051 * @author Thomas Morgner 052 */ 053 public class SerializerHelper 054 { 055 private static Log logger = LogFactory.getLog(SerializerHelper.class); 056 /** 057 * The singleton instance of the serialize helper. 058 */ 059 private static SerializerHelper singleton; 060 061 /** 062 * Returns or creates a new SerializerHelper. When a new instance is created 063 * by this method, all known SerializeMethods are registered. 064 * 065 * @return the SerializerHelper singleton instance. 066 */ 067 public static synchronized SerializerHelper getInstance() 068 { 069 if (singleton == null) 070 { 071 singleton = new SerializerHelper(); 072 singleton.registerMethods(); 073 } 074 return singleton; 075 } 076 077 078 /** 079 * This method can be used to replace the singleton instance of this helper. 080 * 081 * @param helper the new instance of the serialize helper. 082 */ 083 protected static void setInstance(final SerializerHelper helper) 084 { 085 singleton = helper; 086 } 087 088 /** 089 * A collection of the serializer methods. 090 */ 091 private final HashMap methods; 092 093 /** 094 * A class comparator for searching the super class of an certain class. 095 */ 096 private final ClassComparator comparator; 097 098 /** 099 * Creates a new SerializerHelper. 100 */ 101 protected SerializerHelper() 102 { 103 this.comparator = new ClassComparator(); 104 this.methods = new HashMap(); 105 } 106 107 /** 108 * Registers a new SerializeMethod with this SerializerHelper. 109 * 110 * @param method the method that should be registered. 111 */ 112 public synchronized void registerMethod(final SerializeMethod method) 113 { 114 this.methods.put(method.getObjectClass(), method); 115 } 116 117 /** 118 * Traverses the configuration and registers all serialization handlers in this factory. 119 */ 120 protected void registerMethods() 121 { 122 final Configuration config = JCommonSerializerBoot.getInstance().getGlobalConfig(); 123 final Iterator sit = config.findPropertyKeys("org.jfree.serializer.handler."); 124 125 while (sit.hasNext()) 126 { 127 final String configkey = (String) sit.next(); 128 final String c = config.getConfigProperty(configkey); 129 final Object maybeModule = ObjectUtilities.loadAndInstantiate 130 (c, SerializerHelper.class, SerializeMethod.class); 131 if (maybeModule != null) 132 { 133 final SerializeMethod module = (SerializeMethod) maybeModule; 134 registerMethod(module); 135 } 136 else 137 { 138 logger.warn("Invalid SerializeMethod implementation: " + c); 139 } 140 } 141 } 142 143 /** 144 * Deregisters a new SerializeMethod with this SerializerHelper. 145 * 146 * @param method the method that should be deregistered. 147 */ 148 public synchronized void unregisterMethod(final SerializeMethod method) 149 { 150 this.methods.remove(method.getObjectClass()); 151 } 152 153 /** 154 * Returns the collection of all registered serialize methods. 155 * 156 * @return a collection of the registered serialize methods. 157 */ 158 protected HashMap getMethods() 159 { 160 return methods; 161 } 162 163 /** 164 * Returns the class comparator instance used to find correct super classes. 165 * 166 * @return the class comparator. 167 */ 168 protected ClassComparator getComparator() 169 { 170 return comparator; 171 } 172 173 /** 174 * Looks up the SerializeMethod for the given class or null if there is no 175 * SerializeMethod for the given class. 176 * 177 * @param c the class for which we want to lookup a serialize method. 178 * @return the method or null, if there is no registered method for the 179 * class. 180 */ 181 protected SerializeMethod getSerializer(final Class c) 182 { 183 final SerializeMethod sm = (SerializeMethod) methods.get(c); 184 if (sm != null) 185 { 186 return sm; 187 } 188 return getSuperClassObjectDescription(c); 189 } 190 191 /** 192 * Looks up the SerializeMethod for the given class or null if there is no 193 * SerializeMethod for the given class. This method searches all 194 * superclasses. 195 * 196 * @param d the class for which we want to lookup a serialize 197 * method. 198 * @return the method or null, if there is no registered method for the 199 * class. 200 */ 201 protected SerializeMethod getSuperClassObjectDescription 202 (final Class d) 203 { 204 SerializeMethod knownSuperClass = null; 205 final Iterator keys = methods.keySet().iterator(); 206 while (keys.hasNext()) 207 { 208 final Class keyClass = (Class) keys.next(); 209 if (keyClass.isAssignableFrom(d)) 210 { 211 final SerializeMethod od = (SerializeMethod) methods.get(keyClass); 212 if (knownSuperClass == null) 213 { 214 knownSuperClass = od; 215 } 216 else 217 { 218 if (comparator.isComparable 219 (knownSuperClass.getObjectClass(), od.getObjectClass())) 220 { 221 if (comparator.compare 222 (knownSuperClass.getObjectClass(), od.getObjectClass()) < 0) 223 { 224 knownSuperClass = od; 225 } 226 } 227 } 228 } 229 } 230 return knownSuperClass; 231 } 232 233 234 /** 235 * Writes a serializable object description to the given object output stream. 236 * This method selects the best serialize helper method for the given object. 237 * 238 * @param o the to be serialized object. 239 * @param out the outputstream that should receive the object. 240 * @throws IOException if an I/O error occured. 241 */ 242 public synchronized void writeObject(final Object o, 243 final ObjectOutputStream out) 244 throws IOException 245 { 246 if (o == null) 247 { 248 out.writeByte(0); 249 return; 250 } 251 if (o instanceof Serializable) 252 { 253 out.writeByte(1); 254 out.writeObject(o); 255 return; 256 } 257 258 final SerializeMethod m = getSerializer(o.getClass()); 259 if (m == null) 260 { 261 throw new NotSerializableException(o.getClass().getName()); 262 } 263 out.writeByte(2); 264 out.writeObject(m.getObjectClass()); 265 m.writeObject(o, out); 266 } 267 268 /** 269 * Reads the object from the object input stream. This object selects the best 270 * serializer to read the object. 271 * <p/> 272 * Make sure, that you use the same configuration (library and class versions, 273 * registered methods in the SerializerHelper) for reading as you used for 274 * writing. 275 * 276 * @param in the object input stream from where to read the serialized data. 277 * @return the generated object. 278 * @throws IOException if reading the stream failed. 279 * @throws ClassNotFoundException if serialized object class cannot be found. 280 */ 281 public synchronized Object readObject(final ObjectInputStream in) 282 throws IOException, ClassNotFoundException 283 { 284 final int type = in.readByte(); 285 if (type == 0) 286 { 287 return null; 288 } 289 if (type == 1) 290 { 291 return in.readObject(); 292 } 293 final Class c = (Class) in.readObject(); 294 final SerializeMethod m = getSerializer(c); 295 if (m == null) 296 { 297 throw new NotSerializableException(c.getName()); 298 } 299 return m.readObject(in); 300 } 301 }