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    }