Search code examples
javaserializationinputstreamobjectinputstreambytearrayinputstream

ObjectInputStream doesn't have available bytes after being constructed with a ByteArrayInputStream


I'm constructing a class that is handling a Binary De/Serialization. The method open() receives an InputStream and a OutputStream. Those are created by another open() method that receives a path as argument. The InputStream is actually a ByteArrayInputStream. I already did some tests to prove that the InputStream is arriving at the open() method with content - and it actually is. But when I try to set a ObjectInputStream using it, it doesn't work. No exceptions are thrown, but when I try to read bytes from it, it always gives me -1.

BinaryStrategy class

public class BinaryStrategy implements SerializableStrategy{
  public BinaryStrategy(){
    try{
        open("products.ser");
    }catch(IOException ioe){

    }
  } 
  @Override
  public void open(InputStream input, OutputStream output) throws IOException  {
    try{
        this.ois = new ObjectInputStream(input);
    }catch(Exception ioe){
        System.out.println(ioe);
    }
    this.oos = new ObjectOutputStream(output);
  }
  @Override
  public void writeObject(fpt.com.Product obj) throws IOException {
    oos.writeObject(obj);
    oos.flush();
  }
  @Override
  public Product readObject() throws IOException {
    Product read = new Product();
    try{
        read.readExternal(ois);
    }catch(IOException | ClassNotFoundException exc){
        System.out.println(exc);
    }
    return read;
  }
}

interface SerializableStrategy (just the default method)

    default void open(Path path) throws IOException {
    if (path != null) {
        ByteArrayInputStream in = null;
        if (Files.exists(path)) {
            byte[] data = Files.readAllBytes(path);
            in = new ByteArrayInputStream(data);
        }
        OutputStream out = Files.newOutputStream(path);
        open(in, out);
    }

Product class

public class Product implements java.io.Externalizable {
    @Override
public void writeExternal(ObjectOutput out) throws IOException {
    out.writeLong(getId());
    out.writeObject(getName());
    out.writeObject(getPrice());
    out.writeObject(getQuantity());
}

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    this.setId((Long)in.readLong());
    this.setName((String) in.readObject());
    this.setPrice((Double) in.readObject());
    this.setQuantity((Integer) in.readObject());
}

I had to personalize it because the attributes are SimplePropertys

At public void open(InputStream input, OutputStream output) I tried to do some stuff as follow to test:

    public void open(InputStream input, OutputStream output) throws IOException {
    try{
        System.out.println(input.available() + " " + input.read() + " " + input.read());
        //is gives me: 181 172 237
        //181 is the exact size of the file I have, so i think that the Output is ok
        //172 237 - just some chars that are in the file
        //I know that for now on it is going to give me an excepetion because
        // of the position of the index that is reading. I did it just to test
        this.ois = new ObjectInputStream(input);
    }catch(Exception ioe){
        System.out.println(ioe);
    }
    this.oos = new ObjectOutputStream(output);
}

And then the other test:

public void open(InputStream input, OutputStream output) throws IOException {
    try{
        this.ois = new ObjectInputStream(input);
        System.out.println(ois.available() + " " + ois.read());
        //here is where I am receiving -1 and 0 available bytes!
        //so something is going wrong right here.
        //i tried to just go on and try to read the object,
        //but I got a EOFException, in other words, -1.
    }catch(Exception ioe){
        System.out.println(ioe);
    }
    this.oos = new ObjectOutputStream(output);
}

Solution

  • ObjectInputStream, internally uses a BlockDataInputStream perform its read operations. This reads a block of data and not just a byte as we expect, when you call a read. It reads a byte only if it falls as a "block"

    The output is not what I was expecting either. But, if you look at the code of ObjectInputStream.read(), it makes sense.

    So, in your case it makes sense to use only readObject to restore your objects' state.

    Heres your code again...

    class SimpleJava {
    
        public static void open(InputStream input, OutputStream output) throws IOException {
    
            try {
                ObjectInputStream ois = new ObjectInputStream(input);
                System.out.println(ois.available());// 0
                System.out.println(ois.available() + " " + ois.read() + " " + ois.read());// 0 -1 -1
                // Reads the object even if the available returned 0 
                // and ois.read() returned -1
                System.out.println("object:" + ois.readObject());// object:abcd
            }
            catch (Exception ioe) {
                ioe.printStackTrace();
            }
        }
    
        static void open(Path path) throws IOException {
    
            if (path != null) {
                ByteArrayInputStream in = null;
                if (Files.exists(path)) {
                    byte[] data = Files.readAllBytes(path);
                    in = new ByteArrayInputStream(data);
                }
                OutputStream out = Files.newOutputStream(path);
                open(in, out);
            }
        }
    
        public static void main(String[] args) throws Exception {
    
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("/home/pradhan/temp.object")));
            oos.writeObject("abcd");//writes a string object for us to read later
            oos.close();
            //
            open(FileSystems.getDefault().getPath("/home/user/temp.object"));
        }
    }
    

    Heres the output...

    0
    0 -1 -1
    object:abcd