Interface Pipe

All Superinterfaces:
AutoCloseable, ByteChannel, Channel, Closeable, DataInput, DataOutput, Flushable, Link, ObjectInput, ObjectOutput, ReadableByteChannel, WritableByteChannel

public interface Pipe extends Closeable, Flushable, ObjectInput, ObjectOutput, Link, ByteChannel
A pipe is a bidirectional stream which supports basic object serialization. Only simple types and collections can be serialized, and the original classes aren't necessarily preserved. Graph structure isn't preserved unless reference tracking is enabled.

Pipes are only partially thread-safe. Reading and writing is concurrent, but at most one thread can be reading, and at most one thread can be writing.

Pipes are fully buffered, and closing the pipe directly discards any buffered writes. Closing the OutputStream will attempt to flush the buffer first, although it can only be called from the thread which is allowed to perform writes. Closing either stream has the side effect of also fully closing the pipe.

Here's an example remote method declaration which uses a pipe:

Pipe uploadFile(String name, Pipe pipe) throws RemoteException;
The remote method declaration requires the return type to be a pipe, and one parameter must also be a pipe. The client-side invocation of the remote method simply passes null for the pipe parameter, and the server-side implementation returns null instead of a pipe. Example client call:
    Pipe pipe = server.uploadFile("notes.txt", null);
    byte[] notes = ...
    pipe.writeInt(notes.length);
    pipe.write(notes);
    pipe.flush();
    pipe.readByte(); // read ack
    pipe.recycle();
The remote method implementation might look like this:
@Override
public Pipe uploadFile(String name, Pipe pipe) {
    byte[] notes = new byte[pipe.readInt()];
    pipe.readFully(notes);
    pipe.writeByte(1); // ack
    pipe.flush();
    pipe.recycle();
    ...
    return null;
}
When using a pipe, writes must be explicitly flushed. When a client calls a piped method, the flush method must be called to ensure that the method name and parameters are actually sent to the remote endpoint. Care must be taken when recycling pipes. There must not be any pending input or unflushed output, and the pipe must not be used again directly. Closing the pipe is safer, although it might force a new pipe connection to be established.

Note: The pipe implementation isn't strictly compatible with DataInput and DataOutput. The contract for those interfaces specifies a modified UTF-8 encoding, but pipes adhere to the standard UTF-8 format. Also, floating point values are written in their "raw" form, preserving non-canonical NaN values.

  • Method Details

    • enableReferences

      void enableReferences()
      Enables tracking of object references as they are written, for correctly serializing object graphs, and to potentially reduce the overall encoding size. This mode has higher memory overhead because each object flowing through the pipe must be remembered.

      This method counts the number of times it's invoked, and a matching number of calls to disableReferences() is required to fully disable the mode.

    • disableReferences

      boolean disableReferences()
      Disables tracking of object references. Memory isn't freed on the remote side until it reads another object.
      Returns:
      true if fully disabled
      Throws:
      IllegalStateException - if not currently enabled
    • inputStream

      InputStream inputStream()
      Returns the pipe's InputStream, which also implements ObjectInput. Closing the stream is equivalent to closing the pipe.
    • outputStream

      OutputStream outputStream()
      Returns the pipe's OutputStream, which also implements ObjectOutput. Closing the stream is equivalent to closing the pipe.
    • recycle

      void recycle() throws IOException
      Attempt to recycle the connection instead of closing it. The caller must ensure that the pipe has no pending input or unflushed output.

      When the org.cojen.dirmi.Pipe.RECYCLE_CLOSE system propery is set to true, calling recycle on a client-side pipe closes it instead. This feature is intended to help diagnose issues caused by incorrect pipe recycling.

      Throws:
      IllegalStateException - if it's detected that the pipe isn't in a recyclable state
      IOException
    • readObject

      Object readObject() throws IOException
      Read and return an object. Unlike the inherited method, reading from a pipe never throws a ClassNotFoundException.
      Specified by:
      readObject in interface ObjectInput
      Throws:
      IOException
    • readThrowable

      Object readThrowable() throws IOException
      Read and return an object, and if it's a Throwable instance, a local stack trace is stitched in.
      Throws:
      IOException
    • skipNBytes

      void skipNBytes(long n) throws IOException
      Skips over and discards exactly n bytes of data from this pipe. If n is less than or equal to zero, then no bytes are skipped.
      Throws:
      ClosedException - if the pipe is closed by the remote endpoint before n bytes are skipped
      IOException
    • skipObject

      void skipObject(Consumer<Object> remoteConsumer) throws IOException
      Skip an object instead of reading it. If references are enabled, the object is still read and remembered, in case it's referenced later.
      Parameters:
      remoteConsumer - receives all client-side remote objects, which aren't truly skipped; can pass null to do nothing with them
      Throws:
      IOException
    • writeObject

      void writeObject(Object obj) throws IOException
      Write an object (or null) to the pipe.
      Specified by:
      writeObject in interface ObjectOutput
      Throws:
      IOException
    • writeNull

      void writeNull() throws IOException
      Write a null object reference.
      Throws:
      IOException
    • transferTo

      long transferTo(OutputStream out, long n) throws IOException
      Reads up to n bytes from this pipe and writes them into the given stream. Fewer than n bytes are written if the pipe has no more input to read.
      Returns:
      the number of bytes transferred
      Throws:
      IOException
    • readDecode

      <T> T readDecode(T object, int length, Pipe.Decoder<T> decoder) throws IOException
      Read a complex object from the pipe by invoking a decoder, which can be more efficient than reading from the pipe multiple times.
      Parameters:
      object - passed directly to the decoder
      length - exact buffer length to pass to the decoder
      decoder - called to perform decoding against a buffer
      Throws:
      IOException
    • writeEncode

      <T> void writeEncode(T object, int length, Pipe.Encoder<T> encoder) throws IOException
      Write a complex object to the pipe by invoking an encoder, which can be more efficient than writing to the pipe multiple times.
      Parameters:
      object - passed directly to the encoder
      length - minimum buffer length to pass to the encoder
      encoder - called to perform encoding against a buffer
      Throws:
      IOException