anyway to check if method readObject of class ObjectInputStream has finished reading file other than catching its thrown exceptions?
and if no. how can I make outNewmast.writeObject(accountRecord); statement reached in this case?
// read oldmast.ser
try {
while (true) {
accountRecord = (AccountRecord) inOldmast.readObject();
//read trans.ser
while (true) {
transactionRecord = (TransactionRecord) inTrans.readObject();
if (transactionRecord.getAccountNumber() == accountRecord.getAccount()) {
accountRecord.combine(transactionRecord);
}//end if
}//end inner while
outNewmast.writeObject(accountRecord);
}//end while
}//end try
catch (ClassNotFoundException e) {
System.err.println("Error reading file.");
System.exit(1);
}//end catch
catch (IOException e) {
System.err.println("Error reading file.");
System.exit(1);
}//end catch
The best idea would be to serialize the number of elements beforehand, so you could just do:
cnt = file.readInt();
for (int i=0;i<cnt;i++) {
file.readObject();
}
The method proposed by #ChrisCooper is not reliable, as stated in documentation. Some streams don't implement it, or return approximate result (in theory, it can even return 0 when there is still some data. Example - network stream).
Therefore, looking at same documentation, we find this particular block:
Any attempt to read object data which exceeds the boundaries of the
custom data written by the corresponding writeObject method will cause
an OptionalDataException to be thrown with an eof field value of true.
Non-object reads which exceed the end of the allotted data will
reflect the end of data in the same way that they would indicate the
end of the stream: bytewise reads will return -1 as the byte read or
number of bytes read, and primitive reads will throw EOFExceptions. If
there is no corresponding writeObject method, then the end of default
serialized data marks the end of the allotted data.
So, the best idea would be to catch an OptionalDataException and check it's eof field for true.
And to digest the answer even further, here's the method you want:
TransactionRecord readRecord(ObjectInputStream stream) throws OptionalDataException, IOException {
try {
transactionRecord = (TransactionRecord) stream.readObject();
} catch (OptionalDataException e) {
if (e.eof) {
return null;
} else {
throw e;
}
}
return transactionRecord;
}
.....
TransactionRecord record;
while ((record = readRecord(inTrans)) != null) {
doSomethingWithRecord(record);
}
endOfFile();
Yes, check the input stream to see if anything more is available:
http://docs.oracle.com/javase/6/docs/api/java/io/InputStream.html#available()
if (inOldmast.available() > 0) {
// read and process
} else {
// Close the stream and clean up
}
Related
I am making a voice chat program and I got the OptionalDataException error and I never had this problem with the code before I added voice. The voice communication is handles by a different socket so I don't see the problem.
Code:
public class Client implements Runnable { // CLIENT
private String msg;
public void run() {
try {
s1 = new Socket(ipAddress, port);
s2 = new Socket(ipAddress, 1210);
o1 = new ObjectOutputStream(s1.getOutputStream());
o1.writeObject(name);
serverListModel.addElement(name);
i1 = new ObjectInputStream(s1.getInputStream());
Thread voice = new Thread(new ClientAudio());
voice.start();
while(true) {
msg = (String) i1.readObject();
String[] namePart = msg.split("-");
if(namePart[0].equals("AddName") && !namePart[1].equals(name) && !serverListModel.contains(namePart[1])) {
serverListModel.addElement(namePart[1]);
}
if(namePart[0].equals("RemoveName") && !namePart[1].equals(name)) {
serverListModel.removeElement(namePart[1]);
}
if(!msg.equals(null) && !namePart[0].equals("AddName") && !namePart[0].equals("RemoveName")) {
chatWindow.append(msg+"\n");
}
}
} catch (IOException | ClassNotFoundException e) {
chatWindow.append("Server Closed");
e.printStackTrace();
try {
s1.close();
} catch (IOException e1) {
e1.printStackTrace();
}
mainWindow(true);
}
}
}
flag
it was thrown at msg = (String) i1.readObject(); and it says
java.io.OptionalDataException
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1361)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at client.chat$Client.run(chat.java:319)
at java.lang.Thread.run(Thread.java:745)
From Oracle:
Exception indicating the failure of an object read operation due to unread primitive data, or the end of data belonging to a serialized object in the stream. This exception may be thrown in two cases:
An attempt was made to read an object when the next element in the stream is primitive data. In this case, the OptionalDataException's length field is set to the number of bytes of primitive data immediately readable from the stream, and the eof field is set to false.
An attempt was made to read past the end of data consumable by a class-defined readObject or readExternal method. In this case, the OptionalDataException's eof field is set to true, and the length field is set to 0.
It would appear that the next object in the Stream is not a String.
Is the server code under your control? Or do you at least have the source? If so, verify that String objects are the only ones being transmitted, or adjust your code to handle the actual objects/primitives being sent.
Edit
From your other question Voice Server not working:
byte[] soundData =
//...
o.write(soundData, 0, bytesRead);
... It looks like you are not writing String objects to the ObjectOutputStream. Actually, not even writing an Object, but raw bytes. You have to read data the same way you write it; anything else just won't work.
Why in many codes written like as:
finally{
if(out!=null){
try{out.close();}
catch(){}
}}
But not:
finally{
try{out.close();}
catch(){}
}
Typically you want to create some kind of stream. This creation could fail, e.g. because a file is missing or an internet connection is not working. So you need to put it into a try-catch-block because it throws a check-exception which you need to handle:
Stream stream = null;
try {
stream = makeNewStream();
// more stuff
} catch(SomeException e) {
// do something with the exception
}
Now you want to be sure, that the stream is closed in the end, no matter what happens. So you add a finally block:
Stream stream = null;
try {
stream = makeNewStream();
// more stuff
} catch(SomeException e) {
} finally {
stream.close()
}
This block will be called in any case, even if your code fails (e.g. stream = makeNewStream(); throws an exception). But if stream = makeNewStream(); throws an exception, the variable stream will be null. So you need to check if stream is null (you cannot call a method on null ;):
finally {
if(stream != null)
stream.close()
}
Now, unfortunately, close() will also throw a checked-exception (which need to be handled), so you have to check this too. And you will end up with something like this:
Stream stream = null;
try {
stream = makeNewStream();
// more stuff
} catch(SomeException e) {
} finally {
if(stream != null) {
try {
stream.close()
} catch(ClosingException e) {
// ignore this
}
}
}
Most developers will just ignore the last exception in the finally block.
I'm writing a small program for an assignment and part of it involves reading from a file using ObjectInputStream. I've run into a brick wall because I keep getting errors when trying to close the file in the finally block as well as a NullPointerException but I cannot understand why. Any help is much appreciated! I have checked already and the file path is correct, so it is able to find the file.
Example file:
hello || apples, acai berry, bananas || shopping || 0.0005 || yes
public Disease[] readInCancers() {
Disease[] cancerList = null;
try {
FileInputStream fis = new FileInputStream(myData);
BufferedInputStream bis = new BufferedInputStream(fis);
ois = new ObjectInputStream(bis);
while(true) {
Disease disease = null;
try {
disease = (Disease)ois.readObject();
} catch (EOFException eofx) {
break;
}
if (cancerList == null || cancerList.length == 0) {
cancerList = new Disease[1];
cancerList[0] = disease;
} else {
Disease[] newList = new Disease[cancerList.length + 1];
System.arraycopy(cancerList, 0, newList, 0, cancerList.length);
newList[cancerList.length] = disease;
cancerList = newList;
}
}
} catch (FileNotFoundException fnfx) {
JOptionPane.showMessageDialog(null, "File could not be found");
} catch (IOException iox) {
JOptionPane.showMessageDialog(null, "Problem with reading from file");
} catch (ClassNotFoundException cnfx) {
JOptionPane.showMessageDialog(null, "Class could not be found");
} catch (NullPointerException npx) {
System.out.println("blah");
} finally {
try {
ois.close();
} catch (IOException iox) {
JOptionPane.showMessageDialog(null, "Problem with closing file");
}
}
return cancerList;
}
When I run the program, it gives a NullPointerException at ois.close() as well as an IOException that produces the pop-up "Problem with reading from file".
I have also tried changing how the file itself is structured, replaced the || (delimiters) with a word or even blank space but nothing changes.
Your FileInputStream is throwing an exception (I'm guessing from incorrect file permissions, but you'll have to look into this further); this occurs before you initialize your ObjectInputStream, so ois is still null when you reach your finally block which is resulting in a null pointer exception. It's usually a good idea to precede close statements in final blocks by null pointer checks for this reason.
When using an ObjectInputStream the input data is required to be in a byte format that can be read into a serialized object, Disease in this case. If the format is not in the expected format a StreamCorruptedException will be thrown. If you are changing the text file manually, chances are that this exception is being thrown but the exact message is not displayed as you are displaying a generic Problem with reading from file message.
Displaying the stack trace will help
iox.printStackTrace();
Ensure that you are writing the objects correctly to file. Alternatively you could use a text based file, and use Printwriter to write, Scanner to read. You can use || for as a Scanner delimiter.
I try to write a keyholder, and I want to write the passwords to a .dat file using ObjectOutputStream, and then read them using ObjectInputStream. This is my code for writing the objects:
public void toFile()
{
try
{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("passwords.dat"));
for(int i = 0; i<this.nrOfPW; i++)
{
if(this.PWlist[i] instanceof longPW)
{
oos.writeObject((longPW)this.PWlist[i]);
}
else
{
oos.writeObject((PinPW)this.PWlist[i]);
}
}
oos.close();
}
catch(IOException e)
{
e.getStackTrace();
}
}
This seems to work, but when I try to read the file again and put the objects in my PWlist array it says that PinPW isn't serializable, even though PinPW implements Serializable and it's imported. The base class of PinPW (Info) also implements Serializable and imports it. This is the code where I read the file:
public void fromFile()
{
try
{
ObjectInputStream objIn = new ObjectInputStream(new FileInputStream("passwords.dat"));
while(objIn.readObject() != null)
{
if(this.nrOfPW == this.PWlist.length)
{
expand(10);
}
if(objIn.readObject() instanceof PinPW)
{
this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject();
this.nrOfPW++;
}
else
{
this.PWlist[this.nrOfPW] = (longPW)objIn.readObject();
this.nrOfPW++;
}
}
objIn.close();
}
catch(EOFException e)
{
e.getStackTrace();
}
catch(IOException ex)
{
ex.printStackTrace();
}
catch(ClassNotFoundException ex)
{
ex.printStackTrace();
}
}
The PWlist array is a Info array, and PinPW and longPW extends Info.
What do I do to fix this problem?
Let's fix the "first bug, first" ...
In this code:
while(objIn.readObject() != null) // reads object, tests then *discards* it
{
...
if(objIn.readObject() instanceof PinPW) // reads object, tests then *discards* it
{
this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject(); // conditionally read an object
this.nrOfPW++;
}
else
{
this.PWlist[this.nrOfPW] = (longPW)objIn.readObject(); // conditionally read an object
this.nrOfPW++;
}
}
Each time around your loop iteration, you actually read 3 objects. The first time you read an object to check there was one in the stream, the next time you read one and determine it's type, then discard it. Then you read a third object and cast it based on what the type of the discarded object was.
In addition, as EJP correctly points out, the correct way to determine End of Stream for an ObjectInputStream is to catch the end of file exception.
You want to do this instead:
try
{
while (true)
{
final Object o = objIn.readObject(); // read the object from the stream
...
if (o instanceof PinPW)
{
this.PWlist[this.nrOfPW] = (PinPW) o; // cast to correct type
this.nrOfPW++;
}
else
{
this.PWlist[this.nrOfPW] = (longPW) o; // cast to correct type
this.nrOfPW++;
}
}
}
catch (EOFException e)
{
// end of stream reached ...
// ... close the file descriptor etc ...
}
You have a problem here.
while(objIn.readObject() != null)
{
if(this.nrOfPW == this.PWlist.length)
{
expand(10);
}
if(objIn.readObject() instanceof PinPW)
{
this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject();
this.nrOfPW++;
}
else
{
this.PWlist[this.nrOfPW] = (longPW)objIn.readObject();
this.nrOfPW++;
}
}
You are reading an object many times. Try to save it and then work with it.
if(objIn.readObject() instanceof PinPW) reads one, reads twice, this.PWlist[this.nrOfPW] = (PinPW)objIn.readObject(); reads three times, when it should be only once.
PS: use Greg Kopff syntax inside a while and without the final keyword because you want to save more objects in it.
I just wanted to point out the if-else block in the toFile() function is completely pointless. writeObject() takes an Object argument. It doesn't care what type of Object it is, as long as it's serializable.
While your fromFile() method is seriously flawed, it would not cause a NotSerializableException. I believe the exception you're referring to actually happened in toFile().
The cause is very simple: you didn't fully read and understand the documentation of ObjectOutputStream. To be specific, the Object and all its non-transient fields and all non-transient fields in its ancestor classes has to implement the Serializable. It must also have a public no-arg constructor.
I have a series of different object serialized into a binary file.How can I read the file until the end?
try {
ObjectInputStream reader = new ObjectInputStream(new FileInputStream(fname));
Object obj = reader.readObject();
if (obj instanceof Azienda) {
Azienda a = (Azienda) obj;
company.put(a.getCod(), a);
} else if (obj instanceof Privato) {
Privato p = (Privato) obj;
privato.put(p.getCod(), p);
}
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
} catch (FileNotFoundException ffe) {
System.err.println("Error: the file was not found!");
} catch (IOException ioe) {
ioe.printStackTrace();
}
in this way I read only once object per read.
when I read a text file I use null
try{
while(true) {
Object obj=reader.readObject()
// do sth with object
}
}catch(EOFException e){
//we expect it so ignore
}
there is no EOF check besides the exception when you read past it for ObjectInputStream so you'll have to use the code smell called exceptions for control flow
It seems that an EOFException is thrown when there is no more object to read from the stream. Unfortunately, it's not even documented. So, I see the following solutions:
you read in a loop until you get this exception,
you make it so that you know in advance the number of objects in the stream,
you make it so that there is a marker object which marks the last object of the stream,
you serialize (and unserialize) a unique object: a List<Object> containing all the objects. This last solution, of course prevents writing the objects on the fly to the stream, and forces you to have all the objects in memory before serializing them.
ObjectInputStream does not have a concrete method for checking for end-of-file.
But every read...() method of ObjectInputStream throws an EOFException when it tries to read past the end of file. Unfortunately this is not explicitly documented for readObject(), but it is for all the other methods (readInt() etc.)
http://docs.oracle.com/javase/7/docs/api/java/io/ObjectInputStream.html
ObjectInputStream reader = null;
try {
reader = new ObjectInputStream(new FileInputStream("sth"));
Object obj = null;
while ((obj = reader.readObject()) != null) {
System.out.println(obj);
}
} catch (EOFException e) {
System.out.println("finnished reading");
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
} catch (FileNotFoundException ex) {
System.err.println("Error: the file was not found!");
} catch (IOException ex) {
ex.printStackTrace();
} finally {
reader.close();
}