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

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);
}

Please check if the file represented by the path has a java object written to it.
From the ObjectInputStream API doc https://docs.oracle.com/javase/7/docs/api/java/io/ObjectInputStream.html
An ObjectInputStream deserializes primitive data and objects previously written using an ObjectOutputStream.
ObjectInputStream is used to recover those objects previously serialized.
If you are doing a this.ois.readObject(), and you are getting a -1, there are chances that the file does not contain an object in it.
Update: readObject returns an object and not an int. If you are using the read methods in ois, and you are getting a -1, then the file is empty.
Also, there are chances that your file contains -1 as its content ;)

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

The problem was that I was reading the ObjectInputStream the wrong way. It was like:
read.readExternal(ois);
but the correct way is:
read = (Product)ois.readObject();
And beacause of the Exceptions I was getting for doing so, I thought that the problem was with the construction of ObjectInputStream when using ByteArrayInputStream.
What a big mistake! :D
Thanks to everybody that tried to help.

Related

Can I only close fileoutputstream but the channel is living after I send file by Netty?

Few days ago, I struggled with how to access file sent by NettyClient without killing NettyServer. I got solution on StackOverFlow and the detail of question is here. The solution is that the client close channel after sending the file, and the server close the fileoutputstream in channelInactive method. The main code is below.
ClientHandler
public class FileClientHandler extends ChannelInboundHandlerAdapter {
private int readLength = 128;
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
sendFile(ctx.channel());
}
private void sendFile(Channel channel) throws IOException {
File file = new File("C:\\Users\\xxx\\Desktop\\1.png");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
ChannelFuture lastFuture = null;
for (;;) {
byte[] bytes = new byte[readLength];
int readNum = bis.read(bytes, 0, readLength);
if (readNum == -1) { // The end of the stream has been reached
bis.close();
fis.close();
lastFuture = sendToServer(bytes, channel, 0);
if(lastFuture == null) { // When our file is 0 bytes long, this is true
channel.close();
} else {
lastFuture.addListener(ChannelFutureListener.CLOSE);
}
return;
}
lastFuture = sendToServer(bytes, channel, readNum);
}
}
private ChannelFuture sendToServer(byte[] bytes, Channel channel, int length)
throws IOException {
return channel.writeAndFlush(Unpooled.copiedBuffer(bytes, 0, length));
}
}
ServerHandler
public class FileServerHandler extends ChannelInboundHandlerAdapter {
private File file = new File("C:\\Users\\xxx\\Desktop\\2.png");
private FileOutputStream fos;
public FileServerHandler() {
try {
if (!file.exists()) {
file.createNewFile();
} else {
file.delete();
file.createNewFile();
}
fos = new FileOutputStream(file);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("I want to close fileoutputstream!");
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
ByteBuf buf = (ByteBuf) msg;
try {
buf.readBytes(fos, buf.readableBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
buf.release(); // Should always be done, even if writing to the file fails
}
}
}
If now I need to send 10 thousands pictures but every picture is small like 1KB. I have to close and then establish channel frequently. It is a thing wasting many resources. How can I only close fileoutputstream but the channel is alive?
This is just an idea, and I have not tested it, but rather than sending each file in its own connection, you could start a stream where you send:
The number of files to be sent (once)
The file info and content (for each file)
The file size
The file name size
The file name
The file content (bytes)
The client would look something like this:
public void sendFiles(Channel channel, File...files) {
ByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
int fileCount = files.length;
// Send the file count
channel.write(allocator.buffer(4).writeInt(fileCount));
// For each file
Arrays.stream(files).forEach(f -> {
try {
// Get the file content
byte[] content = Files.readAllBytes(f.toPath());
byte[] fileName = f.getAbsolutePath().getBytes(UTF8);
// Write the content size, filename and the content
channel.write(allocator.buffer(4 + content.length + fileName.length)
.writeInt(content.length)
.writeInt(fileName.length)
.writeBytes(fileName)
.writeBytes(content)
);
} catch (IOException e) {
throw new RuntimeException(e); // perhaps do something better here.
}
});
// Flush the channel
channel.flush();
}
On the server side, you would need a slightly more sophisticated channel handler. I was thinking of a replaying decoder. (Example here)
In that example, the decoder will read all the files and then forward to the next handler which would receive a list of Upload instances, but you could send each upload up the pipeline after each received file so you don't allocate as much memory. But the intent is to send all your files in one stream rather than having to connect/disconnect for each file.

Java: PrintWriter not writing in file

I ran over some problem with PrintWriter. I wrote some code that simply takes some input from a file and outputs it to another one.
Though a file is created, the file remains empty. The wanted input can be easily printed out in the console, which means the FileInputStream is working correctly.
Why is PrintWriter not printing anything?
public static void writeInFile(File in, File out) throws FileNotFoundException {
PrintWriter outputStream = null
Scanner scanner = new Scanner(new FileInputStream(in));
outputStream = new PrintWriter(new FileOutputStream(out));
outputStream.print("test");
while(scanner.hasNext()) {
outputStream.print(scanner.nextLine() + "\n");
}
scanner.close();
}
Make sure you always close your OutputStreams:
while(scanner.hasNext()) {
String s = scanner.nextLine();
outputStream.print(s+"\n");
System.out.println("Test "+s); //d.h. das Problem liegt am outputstream
}
outputStream.close();
scanner.close();
Edit: When you close the outputStream it calls flush automatically, which writes the buffer to the file. Without closing it the buffer may never be emptied/written to the file, as was the case here.
Also see this answer.
When dealing with IO which requires cleanup, I prefer to use auto resource cleanup. This is all you need at the most basic level:
public static void writeInToOut(InputStream in, OutputStream out) {
try(PrintWriter outputStream = new PrintWriter(out);
Scanner scanner = new Scanner(in)) {
while(scanner.hasNext()) {
outputStream.print(scanner.nextLine()+"\n");
}
}
}
You can now overload this function in several ways:
public static void writeInToOut(File file, OutputStream out) {
try (InputStream in = new FileInputStream(file)) {
writeInToOut(in, out);
} catch (IOException e) {
Logger.getAnonymousLogger().log(Level.WARNING, "IOError", e);
}
}
public static void writeInToOut(File inFile, File outFile) {
try (InputStream in = new FileInputStream(inFile);
OutputStream out = new FileOutputStream(outFile)) {
writeInToOut(in, out);
} catch (IOException e) {
Logger.getAnonymousLogger().log(Level.WARNING, "IOError", e);
}
}
public static void writeStdInToFile(File file) {
try (OutputStream out = new FileOutputStream(file)) {
writeInToOut(System.in, out);
} catch (IOException e) {
Logger.getAnonymousLogger().log(Level.WARNING, "IOError", e);
}
}

Why this strange behaviour of ObjectOutputStream and ObjectInputStream throwing EOFException?

I wrote a custom serializing/de-serializing logic for persisting some of the data as Java default serialization turned out to be both time and memory expensive. For this purpose I wrote readObject(ObjectInput in) and writeObject(ObjectOutput out) methods for the class(es) that needs persisting. However I noticed that if I do not use any out.writeObject(obj) in writeObject(ObjectOutput out) method then it always throws EOFException.
Consider the following example:
Data.java
public class Data implements BaseData {
private String messageUID;
private String rawData;
private String data;
private Long type;
private Boolean processed = false;
private String processedMessage;
private String processedDetaildMessage;
// getter setter
public void readObject(ObjectInput in) throws IOException, ClassNotFoundException {
messageUID = in.readUTF();
rawData = in.readUTF();
data = in.readUTF();
type = in.readLong();
processed = in.readBoolean();
if (processed) {
processedMessage = in.readUTF();
processedDetaildMessage = in.readUTF();
}
}
public void writeObject(ObjectOutput out) throws IOException {
out.writeUTF(messageUID);
out.writeUTF(rawData);
out.writeUTF(data);
out.writeLong(type);
out.writeBoolean(processed);
if (processed) {
out.writeUTF(processedMessage);
String tempDetailsMessage[] = processedDetaildMessage.split(" more");
out.writeUTF(tempDetailsMessage[tempDetailsMessage.length - 1]);
}
}
However whenever I use above code the out stream is always missing some information at the end (from processedDetaildMessage field) and I get EOFException while reading it form in, stacktrace below (Data.java line 216 is processedDetaildMessage = in.readUTF());
java.io.EOFException
at java.io.ObjectInputStream$BlockDataInputStream.readByte(ObjectInputStream.java:2766)
at java.io.ObjectInputStream$BlockDataInputStream.readUTFChar(ObjectInputStream.java:3158)
at java.io.ObjectInputStream$BlockDataInputStream.readUTFBody(ObjectInputStream.java:3055)
at java.io.ObjectInputStream$BlockDataInputStream.readUTF(ObjectInputStream.java:2864)
at java.io.ObjectInputStream.readUTF(ObjectInputStream.java:1072)
at com.smartstream.common.Data.readObject(Data.java:216)
at com.smartstream.common.PerformanceTest.getObjectFromBytes(PerformanceTest.java:168)
at com.smartstream.common.PerformanceTest.access$0(PerformanceTest.java:160)
at com.smartstream.common.PerformanceTest$1.mapRow(PerformanceTest.java:119)
at com.smartstream.common.PerformanceTest$1.mapRow(PerformanceTest.java:1)
at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:92)
at org.springframework.jdbc.core.RowMapperResultSetExtractor.extractData(RowMapperResultSetExtractor.java:60)
at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:651)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:589)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:639)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:668)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:676)
at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:731)
at com.smartstream.common.PerformanceTest.readFromDb(PerformanceTest.java:109)
at com.smartstream.common.PerformanceTest.main(PerformanceTest.java:66)
so I though I would put some extra byte/s of information at the end after writing all required fields and will not read them so that I don't reach end of file while reading. I tried all of these out.writeByte(-1), out.writeInt(-1), out.writeLong(2342343l), out.writeUTF("END_OF_STREAM") but those make no difference. finally I did this out.writeObject(new String("END_OF_STREAM")) and it works fine. Can someone please explain as to why outputstream misses some information if none of the information is written using writeObject() method. Below is how I read and write to/from streams;
private byte[] getObjectAsBytes(Data data) {
byte[] byteArray = null;
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
// Use this for java default serialization
// oos.writeObject(data);
data.writeObject(oos);
byteArray = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.flush();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return byteArray;
}
private Data getObjectFromBytes(byte[] byteArray) {
Data data = new Data();
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
try {
bais = new ByteArrayInputStream(byteArray);
ois = new ObjectInputStream(bais);
// Use this for java default serialization
// data = (Data) ois.readObject();
data.readObject(ois);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return data;
}
If anyone is interested below is what is written in the streams;
persisted data with original code (throws EOFException and missing information) (don't confuse the stacktrace with original issue this stacktrace is persisted as field processedDetailedMessage)
¬í---z-------3507319347632941385----FEEDER-----1437052314954 ---This is a random string---N---þ%J---!this is message of processed dataÛ
Caused by: java.sql.SQLException: ORA-01691: unable to extend lob segment TLM_DBO.SYS_LOB0000076335C00008$$ by 8192 in tablespace WIN_SL_TABLE
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:439)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:395)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:802)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:436)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:186)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:521)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:205)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1008)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1307)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3449)
at oracle.jdbc.driver.OraclePre
persisted data after writing extra string at the end using writeObject method
¬í---z-------3507319347632941385----FEEDER-----1437052314954 ---This is a random string---N---þ%J---!this is message of processed dataÛ
Caused by: java.sql.SQLException: ORA-01691: unable to extend lob segment TLM_DBO.SYS_LOB0000076335C00008$$ by 8192 in tablespace WIN_SL_TABLE
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:439)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:395)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:802)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:436)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:186)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:521)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:205)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1008)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1307)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3449)
at oracle.jdbc.driver.OraclePrz-----NeparedStatement.execute(OraclePreparedStatement.java:3550)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.execute(OraclePreparedStatementWrapper.java:1374)
at com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.pmiExecute(WSJdbcPreparedStatement.java:975)
at com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.execute(WSJdbcPreparedStatement.java:642)
at com.smartstream.control.engine.config.dao.jdbc.ProcessExecutionAuditDetailDao$1.doInPreparedStatement(ProcessExecutionAuditDetailDao.java:115)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:586)
... 23t
END_OF_STREAM
PS ---- represents unreadable bytes
Your persisted data is incomplete because you are creating your byte array before flushing the ObjectOutputStream. In getObjectAsBytes(Data) move byteArray = bos.toByteArray(); after the finally block to make it work. Alternatively, the method could be written more succinctly as follows (requires Java 7+):
private byte[] getObjectAsBytes(Data data) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
data.writeObject(oos);
} catch (IOException e) {
e.printStackTrace();
}
return bos.toByteArray();
}
I tested both ways in my own program and they both prevent the EOFException from being thrown.
As far as why having a writeObject was working, that's because the underlying writeObject implementation toggles block data mode at the beginning and ending of the method, and changing the block data mode performs a drain which writes all data to the underlying OutputStream, which for a ByteArrayOutputStream is effectively the same as a flush.
This issue is caused because of the different implementations of writeObject method and some other non-generic write* methods i.e. writeUTF. The writeObject method toggles to data block mode at the start and at the end of the method which results all the data being written to underlying OutputStream, this has same affect as calling flush on outputStream. This means that you cannot create another byteArray before flushing the remaining data to the stream. It would be best if you stick with writeObject method for now; ie
public void writeObject(ObjectOutput out) throws IOException {
out.writeUTF(messageUID);
out.writeUTF(rawData);
out.writeUTF(data);
out.writeLong(type);
out.writeBoolean(processed);
if (processed) {
out.writeUTF(processedMessage);
String tempDetailsMessage[] = processedDetaildMessage.split(" more");
out.writeObject(tempDetailsMessage[tempDetailsMessage.length - 1]);
}
}

Serializing a game in Java?

Im trying to serialize a snake game in Java, in which the game has to have the option to "save" and "load". Im not getting any error but whenever I try to print out the lifes, time, etc. It just gives me 0 when the lifes and time are not supposed to be 0.
Heres is some of my code for the saving and loading part:
public void SaveGame() throws IOException {
PnlCentro pnlCentro = new PnlCentro();
FileOutputStream fileOut = new FileOutputStream(fileName);
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(pnlCentro);
out.close();
}
public void LoadGame() throws FileNotFoundException, IOException, ClassNotFoundException {
PnlCentro p = null;
FileInputStream fileIn = new FileInputStream(fileName);
ObjectInputStream in = new ObjectInputStream(fileIn);
p = (PnlCentro) in.readObject();
System.out.println("Body: " + p.vecBody);
System.out.println("Life: " + p.life);
System.out.println("Timer: " + p.getTime());
in.close();
fileIn.close();
}
I think your SaveGame() and LoadGame methods work perfectly, they just don't save or load any data from the current game session.
public void SaveGame() throws IOException {
PnlCentro pnlCentro = new PnlCentro(); //<-- Problem likely lies here!
FileOutputStream fileOut = new FileOutputStream(fileName);
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(pnlCentro);
out.close();
}
Notice the initialization line for pnlCentro in the SaveGame() method. The object is declared and instantiated with the default constructor. Unless you have overrode the default constructor to instantiate thepnlCentro object with the current game data, then the current game data is never set prior to being written out to disk.
Consider this:
public void SaveGame() throws IOException {
PnlCentro pnlCentro = new PnlCentro();
/* Set data prior to writing out */
pnlCentro.setLives(getThisGamesNumLives());
pnlCentro.setTime(getThisGamesTime());
FileOutputStream fileOut = new FileOutputStream(fileName);
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(pnlCentro);
out.close();
}
In the SaveGame method, you always create a new instance of PnlCentro before serializing, when you use the code:
PnlCentro pnlCentro = new PnlCentro();
There is no modification to the default values of the object plnCentro before the serialization and maybe that's why you are reading zeros after deserialization.

Appending objects to a binary file

Supose you have the following method:
public static void writeToBinary(Object obj, String filename)
{
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream(filename));
oos.writeObject(obj);
} catch (Exception e) {
e.printStackTrace();
} finally{
try{
if (oos != null) oos.close ();
}catch (Exception e){
e.printStackTrace();
}
}
}
As you can see, the method writes an object to a binary file.
But now you want to rewrite the same method to allow appending objects to the same file.
Ok, you look at the java documentation and you see that you have to add a parameter with value true to the FileOutputStream:
oos = new ObjectOutputStream(new FileOutputStream(filename, true));
You compile but, whoops!, it seems that it continues overriding the file.
Well, the problems begin. After searching in google you read that you have to use the SAME ObjectOutputStream to append objects to the same file. You want to have a function that every time you call it, it appends an object. I.e. :
writeToBinary("a", filename);
writeToBinary("b", filename);
But as I said before, you have to use the same ObjectOutputStream.
Solution 1:
ObjectOutputStream out = getOutputStream (filename);
writeToBinary("a", out);
writeToBinary("b", out);
writeToBinary("c", out);
out.close ();
This is very ugly because I want to hide the usage of streams.
Is there any other solution?
EDIT: The method is static. It is inside an utility class where all methods are static.
EDIT2: SOLVED! Appending to an ObjectOutputStream. See accepted answer to my question.
Thanks.
Solved.
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class Test{
private static String filename = "test";
public static void main(String[] args) {
writeToBinary (filename, "a", true);
writeToBinary (filename, "b", true);
writeToBinary (filename, "c", true);
readFromBinaryFile (filename);
}
public static void writeToBinary (String filename, Object obj, boolean append){
File file = new File (filename);
ObjectOutputStream out = null;
try{
if (!file.exists () || !append) out = new ObjectOutputStream (new FileOutputStream (filename));
else out = new AppendableObjectOutputStream (new FileOutputStream (filename, append));
out.writeObject(obj);
out.flush ();
}catch (Exception e){
e.printStackTrace ();
}finally{
try{
if (out != null) out.close ();
}catch (Exception e){
e.printStackTrace ();
}
}
}
public static void readFromBinaryFile (String filename){
File file = new File (filename);
if (file.exists ()){
ObjectInputStream ois = null;
try{
ois = new ObjectInputStream (new FileInputStream (filename));
while (true){
String s = (String)ois.readObject ();
System.out.println (s);
}
}catch (EOFException e){
}catch (Exception e){
e.printStackTrace ();
}finally{
try{
if (ois != null) ois.close();
}catch (IOException e){
e.printStackTrace ();
}
}
}
}
private static class AppendableObjectOutputStream extends ObjectOutputStream {
public AppendableObjectOutputStream(OutputStream out) throws IOException {
super(out);
}
#Override
protected void writeStreamHeader() throws IOException {}
}
}
oos = new ObjectOutputStream(new FileOutputStream(filename, true)); You
compile but, whoops!, it seems that it
continues overriding the file.
That does not make sense. The FileOutputStream is a streams that appends to the existing file, so it will not overwite the file. Check it.
The problem is that a stream cannot be closed and reopened to serialize several objects. Run the following and compare the resulting files to check it.
public class XX {
public static void writeToBinary(Object obj, String filename) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename,true));
oos.writeObject(obj);
oos.close();
}
public static void writeToBinary2(Object obj1, Object obj2,String filename) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename,true));
oos.writeObject(obj1);
oos.writeObject(obj2);
oos.close();
}
public static void main(String[] args) throws Exception {
String s1= "hi, just trying";
String s2= "bye bye cruel world";
String filename = "/temp/f.dat";
String filename2 = filename + ".2" ;
writeToBinary(s1, filename);
writeToBinary(s2, filename);
writeToBinary2(s1, s2,filename2);
ObjectInputStream fin = new ObjectInputStream(new FileInputStream(filename)); // oops... works with filename2
Object x1 = fin.readObject();
Object x2 = fin.readObject();
System.out.println(x1);
System.out.println(x2);
}
}
Write a helper class. In constructor it will instantiate an output stream for a particular file name. Then using some append() or writeToBinary() method it will append the data. on method close() there will be flush() and close() calls on the stream.
BinaryWriteHelper helper = new BinaryWriteHelper("test.dat");
helper.writeToBinary("1");
helper.writeToBinary(2);
helper.close();
in BinaryWriteHelper :
public BinaryWriteHelper(String filename) {
this.stream = new ObjectOutputStream(new FileOutputStream(filename));
}
public close() {
// the cleanup here
}
Try this approach:
Write the object to a ByteArrayOutputStream.
Append the the size and contents of the ByteArrayOutputStream to a RandomAccessFile.
To load an object from the file, read the bytes that represent an Object into a ByteArrayInputStream and initialize an ObjectInputStream on this. The size field that was prepends each object byte sequence will come handy here.

Categories

Resources