Using OutputStream with multiple ObjectOutputStreams?

79 views Asked by At

To abstract from a specific serialization format I thought to define the following:

public interface TransportCodec {
  void write(OutputStream out, Object obj) throws IOException;      
  Object read(InputStream in) throws IOException;
}

A default implementation would use just Java object serialization like this:

public void write(OutputStream out, Object obj) throws IOException {
  ObjectOutputStream oout = new ObjectOutputStream(out);
  oout.writeObject(obj);
  oout.flush();
}

Obviously the oout.close() is missing, but for a reason: I want to be able write several objects into the same stream with independent calls to write. Looking at the source code of ObjectOutputStream (jdk 1.8), oout.close() closes the underlying stream, but also clears data structures that are part of ObjectOutputStream. But since I leave oout right to the garbage collector, I would not expect problems from not closing the stream.

Apart from the risk that a future JDK really needs the oout.close(), two questions:

  1. What do I loose in the current JDK when not closing the ObjectOutputStream above.
  2. First serializing into a ByteArrayOutputStream and then copying the bytes to out would allow to close oout. Are there better options?
1

There are 1 answers

0
Andreas On

Separate into two interfaces and make the implementation class "own" the underlying stream.

Advantages:

  • Underlying storage is no longer restricted to be an OutputStream / InputStream.

  • By making the two interfaces extend Closeable, they can now be used in a try-with-resources block.

  • Caller only need to carry one reference (e.g. TransportEncoder), and will no longer have to carry the stream too (e.g. OutputStream).

Interfaces

public interface TransportEncoder extends Closeable {
    void write(Object obj) throws IOException;
}
public interface TransportDecoder extends Closeable {
    Object read() throws IOException;
}

ObjectStream implementations

public final class ObjectStreamEncoder implements TransportEncoder {
    private final ObjectOutputStream stream;
    public ObjectStreamEncoder(OutputStream out) throws IOException {
        this.stream = new ObjectOutputStream(out);
    }
    @Override
    public void write(Object obj) throws IOException {
        this.stream.writeObject(obj);
    }
    @Override
    public void close() throws IOException {
        this.stream.close();
    }
}
public final class ObjectStreamDecoder implements TransportDecoder {
    private final ObjectInputStream stream;
    public ObjectStreamDecoder(InputStream in) throws IOException {
        this.stream = new ObjectInputStream(in);
    }
    @Override
    public Object read() throws IOException {
        try {
            return this.stream.readObject();
        } catch (ClassNotFoundException e) {
            throw new NoClassDefFoundError(e.getMessage());
        }
    }
    @Override
    public void close() throws IOException {
        this.stream.close();
    }
}