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 }