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}