/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.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>
 * @version $Revision: 1.1.4.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
     * @exception java.io.StreamCorruptedException The version or magic number are
     * incorrect.
     * @exception 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;
    }

    /**
     * Load the local class equivalent of the specified stream class description.
     *
     * Subclasses may implement this method to allow classes to be
     * fetched from an alternate source.
     *
     * 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>
     *
     * By default the class name is resolved relative to the class
     * that called readObject. <p>
     *
     * @param v  an instance of class ObjectStreamClass
     * @return a Class object corresponding to <code>v</code>
     * @exception java.io.IOException Any of the usual Input/Output exceptions.
     * @exception java.lang.ClassNotFoundException If class of
     * a serialized object cannot be found.
     */
    protected Class resolveClass(java.io.ObjectStreamClass v)
	throws java.io.IOException, ClassNotFoundException
    {
        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>This method is called exactly once for each unique proxy class
     * descriptor in the stream.
     *
     * <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>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>.
     *
     * @param	interfaces the list of interface names that were
     *		deserialized in the proxy class descriptor
     * @return  a proxy class for the specified interfaces
     * @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
     * @see java.io.ObjectOutputStream#annotateProxyClass(java.lang.Class)
     * @since	1.3
     */
    protected Class resolveProxyClass(String[] interfaces)
	throws java.io.IOException, ClassNotFoundException
    {
        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);
        }
    }
}
