Philip Couling
Philip Couling

Reputation: 14913

How to share an InputStream or an OutputStream while using RMI?

I have a server side application to which I'm attempting to share access to via RMI. The application has an abstract representation of an "item" which is most commonly represents a file but could represent almost any arbitrary data including stream (streaming radio, streaming data from a sensor). Consequently any "item" is accessed through an InputStream (alongside a method to indicate the file size if it can be known).

While RMI is sufficient for the rest of the application, it really can't handle InputStreams.

It strikes me that the need to access InputStreams while using RMI must be a common problem. So I was wondering: What are the recommended solutions?

Note I need the client to both send and receive input streams and serializing to a byte array is not viable because some of them actually do represent a stream of data, not just a file.

Upvotes: 2

Views: 2780

Answers (3)

DRCB
DRCB

Reputation: 2121

I use RMIIO library to share streams over RMI.

See classes: com.healthmarketscience.rmiio.RemoteInputStream and com.healthmarketscience.rmiio.RemoteOutputStream

See also: Remote streaming reference with RMIIO

Upvotes: 4

Alex Stockinger
Alex Stockinger

Reputation: 3069

I just pushed a lightweight solution Serializable InputStream to Github for passing an InputStream over RMI that is conceptually very similar to RMIIO. This might solve your problem. With that library you can easily wrap your InputStream to make it serializable:

serializableStream = new SerializableInputStream(originalInputStream);

Basically it leverages the behavior of ObjectOutputStream to call special methods (if present) on classes that implement Serializable:

private void writeObject(java.io.ObjectOutputStream out) throws IOException;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

Using these methods, the SerializableInputStream forwards chunks read from the delegate InputStream into the processing ObjectOutputStream used by the RMI framework. These chunks then get deserialized on the other end (either into an in-memory cache or into a temp file - that's configurable and depends on your needs).

As for your requirement that...

"some of them actually do represent a stream of data, not just a file"

...I believe that either you need to further clarify your needs or reconsider if RMI is really the technique that suits your use case: I cannot see how sending (potentially never-ending) streams of data over a method-invocation-based interface could work out for you.

Upvotes: 0

Tom Anderson
Tom Anderson

Reputation: 47213

Rather than passing streams, can you pass a sort of 'stream handle'? That is, an object which is not a stream itself, but can produce a stream on demand. Something like:

public interface StreamHandle extends Serializable {
    public InputStream getStream();
}

public class WebStreamHandle implements StreamHandle {
    private URL url;
    public InputStream getStream() {
        return url.openStream();
    }
}

public class SocketStreamHandle implements StreamHandle {
    private String host;
    private int port;
    public InputStream getStream() {
        return new Socket(host, port).getInputStream();
    }
}

public class BufferStreamHandle implements StreamHandle {
    private byte[] buffer;
    public InputStream getStream() {
        return new ByteArrayInputStream(buffer);
    }
}

The key thing would be to ensure that the code was able to produce a stream independently of the machine it was executed on. So, you couldn't refer to a file (unless it was file you knew would be on every machine, or was a UNC path), but you can refer to resources on the network.

Then, you could pass the handles around, and produce the streams on demand.

Upvotes: 0

Related Questions