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 3044 2007-07-28 17:52:44Z tmorgner $
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    }