/*
 * 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 output stream that writes to an output stream on a remote host,
 * using RMI. 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>
 * OutputStream getRemoteStream() throws RemoteException {
 *    OutputStream sink = ...;
 *    RemoteOutputStreamSrv srv = new RemoteOutputStreamSrvImpl(sink);
 *    // the following line for standard RMI only (not RMIX or JERI)
 *    UnicastRemoteObject.exportObject(srv);
 *    return new RemoteOutputStream(srv);
 * }
 * </pre>
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */

public class RemoteOutputStream extends OutputStream implements Externalizable {
    RemoteOutputStreamSrv server;

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

    /**
     * Constructs a new serializable remote output stream that writes to the
     * specified stream.
     *
     * @deprecated this constructor does not work correctly with standard Java
     * RMI (it only works with RMIX). See this class javadoc for instantiation
     * instructions.
     * @param out the sink stream
     */
    public RemoteOutputStream(OutputStream out) {
        this(new RemoteOutputStreamSrvImpl(out));
    }

    /**
     * Constructs a new serializable remote output stream that writes to 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 RemoteOutputStream(RemoteOutputStreamSrv server) {
        if (server == null) {
            throw new NullPointerException("server");
        }
        this.server = server;
    }

    public void write(int b) throws java.io.IOException {
        server.write(new byte[] { (byte)b });
    }

    public void write(byte[] buf) throws IOException {
        server.write(buf);
    }

    public void write(byte[] buf, int off, int len) throws IOException {
        byte[] data = new byte[len];
        System.arraycopy(buf, off, data, 0, len);
        server.write(data);
    }

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

    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 = (RemoteOutputStreamSrv)in.readObject();
    }
}
