/*
 * Written by Dawid Kurzyniec and released to the public domain, as explained
 * at http://creativecommons.org/licenses/publicdomain
 */

package edu.emory.mathcs.util.remote.io;

import java.io.*;
import edu.emory.mathcs.util.remote.io.server.*;
import edu.emory.mathcs.util.remote.io.server.impl.*;

/**
 * Serializable input stream that reads from an input stream on a remote host,
 * using RMI (or RMIX, or Jini ERI, ...). Since RMI itself can be enabled over
 * various protocols and
 * socket factories, this class allows to tunnel byte streams through a variety
 * of protocols. For example, if used with RMIX, it is possible to tunnel
 * streams via SOAP/HTTP. Typical usage pattern is to create the instance
 * at the server side and then send it to the client via RMI:
 *
 * <pre>
 * InputStream getRemoteStream() throws RemoteException {
 *    InputStream source = ...;
 *    RemoteInputStreamSrv srv = new RemoteInputStreamSrvImpl(source);
 *    // the following line for standard RMI only (not RMIX or JERI)
 *    UnicastRemoteObject.exportObject(srv);
 *    return new RemoteInputStream(source);
 * }
 * </pre>
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */

public class RemoteInputStream extends InputStream implements Externalizable {
    RemoteInputStreamSrv server;

    /** for deserialization only */
    public RemoteInputStream() {}

    /**
     * Constructs a new serializable remote input stream reading from the
     * specified stream.
     *
     * @param in the source stream
     *
     * @deprecated this constructor does not work correctly with standard Java
     * RMI (it only works with RMIX). See this class javadoc for instantiation
     * instructions.
     */
    public RemoteInputStream(InputStream in) {
        this(new RemoteInputStreamSrvImpl(in));
    }

    /**
     * Constructs a new serializable remote input stream reading from the
     * specified stream handle. Use this constructor on the client side if
     * the RMI handle to the server has been already created.
     *
     * @param server RMI handle to the source stream
     */
    public RemoteInputStream(RemoteInputStreamSrv server) {
        if (server == null) {
            throw new NullPointerException("server");
        }
        this.server = server;
    }

    public int read() throws IOException {
        byte[] buf = server.read(1);
        if (buf == null) { // EOF
            return -1;
        }
        return buf[0];
    }

    public int read(byte[] b, int off, int len) throws IOException {
        byte[] buf = server.read(len);
        if (buf == null) { // EOF
            return -1;
        }
        System.arraycopy(buf, 0, b, off, buf.length);
        return buf.length;
    }

    public long skip(long n) throws IOException {
        return server.skip(n);
    }

    public int available() throws IOException {
        return server.available();
    }

    public void close() throws IOException {
        server.close();
    }

    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(server);
    }

    public void readExternal(ObjectInput in) throws IOException,
        ClassNotFoundException
    {
        server = (RemoteInputStreamSrv)in.readObject();
    }
}
