/***************************************
 *                                     *
 *  JBoss: The OpenSource J2EE WebOS   *
 *                                     *
 *  Distributable under LGPL license.  *
 *  See terms of license at gnu.org.   *
 *                                     *
 ***************************************/
package org.jboss.remoting.loading;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.StreamCorruptedException;


/**
 * ObjectInputStreamWithClassLoader
 *
 * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
 * @author <a href="mailto:tom@jboss.org">Tom Elrod</a>
 * @version $Revision: 1.1.14.1 $
 */
public class ObjectInputStreamWithClassLoader extends ObjectInputStream
{
   private ClassLoader cl;

   /**
    * Create an ObjectInputStream that reads from the specified InputStream.
    * The stream header containing the magic number and version number
    * are read from the stream and verified. This method will block
    * until the corresponding ObjectOutputStream has written and flushed the
    * header.
    *
    * @param in the underlying <code>InputStream</code> from which to read
    * @throws java.io.StreamCorruptedException
    *                             The version or magic number are
    *                             incorrect.
    * @throws java.io.IOException An exception occurred in the underlying stream.
    */
   public ObjectInputStreamWithClassLoader(java.io.InputStream in, ClassLoader cl)
         throws IOException, StreamCorruptedException
   {
      super(in);
      this.cl = cl;
   }

   /**
    * Set the classloader that the stream will used when deserializing class.  This will
    * allow plugging in of classloaders based on invocation context.
    *
    * @param cl
    */
   public void setClassLoader(ClassLoader cl)
   {
      this.cl = cl;
   }

   /**
    * Gets the pluggable classloader that will be used for classloading when deserializing
    * objects.
    *
    * @return
    */
   public ClassLoader getClassLoader()
   {
      return cl;
   }

   /**
    * Load the local class equivalent of the specified stream class description.
    * <p/>
    * Subclasses may implement this method to allow classes to be
    * fetched from an alternate source.
    * <p/>
    * The corresponding method in ObjectOutputStream is
    * annotateClass.  This method will be invoked only once for each
    * unique class in the stream.  This method can be implemented by
    * subclasses to use an alternate loading mechanism but must
    * return a Class object.  Once returned, the serialVersionUID of the
    * class is compared to the serialVersionUID of the serialized class.
    * If there is a mismatch, the deserialization fails and an exception
    * is raised. <p>
    * <p/>
    * By default the class name is resolved relative to the class
    * that called readObject. <p>
    * <p/>
    * Will use the classloader explicitly set if it exists.  If it does not exist,
    * will used its default classloader (that loader this instance).<p>
    *
    * @param v an instance of class ObjectStreamClass
    * @return a Class object corresponding to <code>v</code>
    * @throws java.io.IOException Any of the usual Input/Output exceptions.
    * @throws java.lang.ClassNotFoundException
    *                             If class of
    *                             a serialized object cannot be found.
    */
   protected Class resolveClass(java.io.ObjectStreamClass v)
         throws java.io.IOException, ClassNotFoundException
   {
      if(cl == null)
      {
         return super.resolveClass(v);
      }
      else
      {
         return cl.loadClass(v.getName());
      }
   }

   /**
    * Returns a proxy class that implements the interfaces named in a
    * proxy class descriptor; subclasses may implement this method to
    * read custom data from the stream along with the descriptors for
    * dynamic proxy classes, allowing them to use an alternate loading
    * mechanism for the interfaces and the proxy class.
    * <p/>
    * <p>This method is called exactly once for each unique proxy class
    * descriptor in the stream.
    * <p/>
    * <p>The corresponding method in <code>ObjectOutputStream</code> is
    * <code>annotateProxyClass</code>.  For a given subclass of
    * <code>ObjectInputStream</code> that overrides this method, the
    * <code>annotateProxyClass</code> method in the corresponding
    * subclass of <code>ObjectOutputStream</code> must write any data or
    * objects read by this method.
    * <p/>
    * <p>The default implementation of this method in
    * <code>ObjectInputStream</code> returns the result of calling
    * <code>Proxy.getProxyClass</code> with the list of
    * <code>Class</code> objects for the interfaces that are named in
    * the <code>interfaces</code> parameter.  The <code>Class</code>
    * object for each interface name <code>i</code> is the value
    * returned by calling
    * <pre>
    *     Class.forName(i, false, loader)
    * </pre>
    * where <code>loader</code> is that of the first non-null class
    * loader up the execution stack, or <code>null</code> if no non-null
    * class loaders are on the stack (the same class loader choice used
    * by the <code>resolveClass</code> method).  This same value of
    * <code>loader</code> is also the class loader passed to
    * <code>Proxy.getProxyClass</code>.  If <code>Proxy.getProxyClass</code>
    * throws an <code>IllegalArgumentException</code>,
    * <code>resolveProxyClass</code> will throw a
    * <code>ClassNotFoundException</code> containing the
    * <code>IllegalArgumentException</code>.
    *
    * @return a proxy class for the specified interfaces
    * @param    interfaces the list of interface names that were
    * deserialized in the proxy class descriptor
    * @throws   java.io.IOException any exception thrown by the underlying
    * <code>InputStream</code>
    * @throws   java.lang.ClassNotFoundException if the proxy class or any of the
    * named interfaces could not be found
    * @since    1.3
    * @see java.io.ObjectOutputStream#annotateProxyClass(java.lang.Class)
    */
   protected Class resolveProxyClass(String[] interfaces)
         throws java.io.IOException, ClassNotFoundException
   {
      if(cl == null)
      {
         return super.resolveProxyClass(interfaces);
      }
      else
      {
         Class[] classObjs = new Class[interfaces.length];
         for(int i = 0; i < interfaces.length; i++)
         {
            classObjs[i] = cl.loadClass(interfaces[i]);
         }
         try
         {
            return java.lang.reflect.Proxy.getProxyClass(cl, classObjs);
         }
         catch(IllegalArgumentException e)
         {
            throw new ClassNotFoundException(null, e);
         }
      }
   }
}