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