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 org.apache.commons.logging.Log;
043import 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 */
053public 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}