Extensions to `java.io` for file and directory manipulation.

## Remote Input Streams

Remote interfaces cannot expose a `java.io.InputStream` because it is not a remote object and is not serializable. A variety of interfaces have been added to the com.sas.io package to enable the access of input streams from a remote virtual machine. Clients can use these interfaces to read from a stream across an RMI connection.

Objects that will expose both a local and remote input stream should extend the `com.sas.io.InputStreamProviderInterface`. Classes implement this interface if they provide one or more input streams.

`com.sas.io.RemoteInputStreamInterface` encapsulates a `java.io.InputStream` to allow access from a remote virtual machine. Method signatures and behavior of this class are identical to the encapsulated `InputStream` except for the addition of remote exception handling where necessary and a modification of the read method to make it compatible with a remotable interface.

### Sample Remote Object

`RemoteInputStream` can be used by any object that needs to expose `InputStream` functionality across a remotable interface. Objects that will expose both a local or remote input stream should extend the `InputStreamProviderInterface`. This interface provides input streams which may be remote:

      import com.sas.io.ChainedIOException;
      import java.io.ByteArrayInputStream;
      import java.rmi.RemoteException;
      import java.rmi.UnmarshalException;

      public class RemoteThing
        implements com.sas.io.InputStreamProviderInterface,
                   java.rmi.server.RemoteObject {

        java.io.InputStream is;
        com.sas.io.RemoteInputStream ris;
        static private ByteArrayInputStream dummyStream = new ByteArrayInputStream(new byte[1]);

        // method that creates the stream and its remotable wrapper
        void someMethod() {
          ...
          is = ...;
          ris = new com.sas.io.RemoteInputStream( is );
          ...
        }

        // get the local stream - will not work across a remote interface
        public java.io.InputStream getInputStream() throws UnmarshalException, ChainedIOException, RemoteException{
          return is;
        }

        // get the remotable wrapper for the stream
        public com.sas.io.RemoteInputStreamInterface getRemoteInputStream() throws ChainedIOException, RemoteException{
          return ris;
        }

        // returns non-null Object if local input stream exists;otherwise, throws UnmarshalException
        public Object hasInputStream() throws UnmarshalException, RemoteException
        {
           return dummyStream;
        }
      }

### Client Access to the RemoteInputStream

A client of `RemoteThing` can remotely access the stream using a static convenience method, `getInputStream()` on com.sas.io.RemoteInputStream. This convenience method obtains the local input stream if possible. Otherwise, if the provider is a remote object, the remote input stream is obtained and returned as an InputStream. The client can get the local or remote input stream with a single method call:

      RemoteThing obj = ...;
      Object streamID = ....;

     InputStream is = RemoteInputStream.getInputStream( obj, streamID );

## Remote Readers

Remote interfaces cannot expose a `java.io.Reader` because it is not a remote object and is not serializable. A variety of interfaces have been added to the com.sas.io package to enable the access of readers from a remote virtual machine. Clients can use these interfaces to read from a reader across an RMI connection.

Objects that will expose both a local and remote reader should extend the `com.sas.io.ReaderProviderInterface`. Classes implement this interface if they provide one or more readers.

`com.sas.io.RemoteReaderInterface` encapsulates a `java.io.Reader` to allow access from a remote virtual machine. Method signatures and behavior of this class are identical to the encapsulated `Reader` except for the addition of remote exception handling where necessary and a modification of the read method to make it compatible with a remotable interface.

### Sample Remote Object

`RemoteReader` can be used by any object that needs to expose `Reader` functionality across a remotable interface. Objects that will expose both a local or remote reader should extend the `ReaderProviderInterface`. This interface provides readers which may be remote:

      import com.sas.io.ChainedIOException;
      import java.io.CharArrayReader;
      import java.rmi.RemoteException;
      import java.rmi.UnmarshalException;

      public class RemoteThing
        implements java.rmi.server.RemoteObject,
                   com.sas.io.ReaderProviderInterface {

        java.io.Reader reader;
        com.sas.io.RemoteReader remoteReader;

         static private CharArrayReader dummyReader = new CharArrayReader(new char[1]);

        // method that creates the reader and its remotable wrapper
        void someMethod() {
          ...
          reader = ...;
          remoteReader = new com.sas.io.RemoteReader( remote );
          ...
        }

        // get the local reader - will not work on a remote object.
        java.io.Reader getReader(Object streamID) throws UnmarshalException, ChainedIOException, RemoteException {
           return reader;
        }

        // get the remote reader
        com.sas.io.RemoteReaderInterface getRemoteReader(Object streamID) throws ChainedIOException, RemoteException {
           return remoteReader;
         }

        // check if getReader will succeed
        Object hasReader() throws UnmarshalException, RemoteException{
           return dummyReader;
        }
      }

### Client Access to the RemoteReader

A client of `RemoteThing` can remotely access the reader using a static convenience method, `getReader()` on com.sas.io.RemoteReader. This convenience method obtains the local reader if possible. Otherwise, if the provider is a remote object, the remote reader is obtained and returned as a Reader. The client can get the local or remote reader with a single method call:

     RemoteThing obj = ...;
     Object streamID = ....;
     Reader reader = RemoteReader.getReader( obj, streamID );

## Remote Output Streams

Remote interfaces cannot expose a `java.io.OutputStream` because it is not a remote object and is not serializable. A variety of interfaces have been added to the com.sas.io package to enable the access of output streams from a remote virtual machine. Clients can use these interfaces to read from a stream across an RMI connection.

`com.sas.io.RemoteOutputStreamInterface` encapsulates a `java.io.OutputStream` to allow access from a remote virtual machine. Method signatures and behavior of this class are identical to the encapsulated `OutputStream` except for the addition of remote exception handling where necessary.

### Sample Remote Object

`RemoteOutputStream` can be used by any object that needs to expose `OutputStream` functionality across a remotable interface:

     public class RemoteThing
       implements java.rmi.server.RemoteObject {
         java.io.OutputStream os;
         com.sas.io.RemoteOutputStream ros;

        // method that creates the stream and its remotable wrapper
        void someMethod() {
          ...
          os = ...;
          ros = new com.sas.io.RemoteOutputStream( os );
          ...
        }

        // get the local stream - will not work across a remote interface
       public java.io.OutputStream getOutputStream() {
          return os;
        }

        // get the remotable wrapper for the stream
        public com.sas.io.RemoteOutputStreamInterface getRemoteOutputStream() {
          return ros;
        }
      }

### Client Access to the RemoteOutputStream

A client of `RemoteThing` can remotely access the stream using `RemoteOutputStreamProxy`:

      import com.sas.io.RemoteOutputStreamProxy;

      RemoteThing obj = ...;

      OutputStream os = new RemoteOutputStreamProxy( obj.getRemoteOutputStream() );

The above code will work whether `RemoteThing` is remote or local. In the local case we are adding an unnecessary level of indirection to access the `OutputStream`. This can be eliminated with an additional test:

      import com.sas.io.RemoteOutputStreamProxy;

      RemoteThing obj = ...;

      try {
        // try to get a local OutputStream
        OutputStream os = obj.getOutputStream();
      }
      catch ( NotSerializableException e ) {
        // obj is remote.  Get remote OutputStream
        os = new RemoteOutputStreamProxy( obj.getRemoteOutputStream() );
      }

A static convenience method can be added to `RemoteThing` to make this process slightly easier for the client:

      static OutputStream getOutputStream( RemoteThing obj ) {

        try {
          // try to get a local OutputStream
          return obj.getOutputStream();
        }
        catch ( NotSerializableException e ) {
          // obj is remote.  Get remote OutputStream
          return new RemoteOutputStreamProxy( obj.getRemoteOutputStream() );
        }
      }

The client of `RemoteThing` can then get the correct local or remote stream with single method call:

      RemoteThing obj = ...;

      OutputStream is = RemoteThing.getOutputStream( obj );