How do I update a File created by openFileOutput - java

I'm currently building an application where the user will generate data over time and, should he/she has an internet connection, transmit it to the web. However, if he doesn't have web access, I need to store this data in the phone until the user recovers his access, when I'll need to recover this data to be transmitted. However, I'm facing lots of troubles to do this, as per below.
Note: before anything, I'm using a local java-created file because I know no other way to save/restore this data on the device. If you happen to know any other way to store/access this data from within the device please feel free to comment here.
Just for reference,
phantoms is an ArrayList containing objects with the data I need to
store,
Arquivador is the class that I'm using to make my data persistent and to recover it,
Funcionario is the class with the data generated by the program (just a few strings and numbers)
I am able to write a file to the file system through the code below, on my Activity:
try {
arq = new Arquivador();
arq.addFirstObjectInFile(
openFileOutput("dados.jlog", MODE_WORLD_WRITEABLE),
phantoms.get(0));
phantoms.remove(phantoms.get(0));
for (Funcionario func : phantoms) {
arq.addObjectInFile(openFileOutput("dados.jlog", MODE_APPEND),
func);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
}
Here is the code inside Arquivador that adds the data to a file:
public void addObjectInFile(FileOutputStream arquivo,
Object objetoAAdicionar) {
try {
ObjectOutputStream aoos = new ObjectOutputStream(arquivo);
aoos.writeObject(objetoAAdicionar);
aoos.close();
} catch (IOException ioe) {
Log.d(TAG_NAME, "Erro no Appendable OOS.");
}
}
public void addFirstObjectInFile(FileOutputStream arquivo,
Object objetoAAdicionar) {
try {
AppendableObjectOutputStream aoos = new AppendableObjectOutputStream(
arquivo);
aoos.writeObject(objetoAAdicionar);
aoos.close();
} catch (IOException ioe) {
Log.d(TAG_NAME, "Erro no Appendable OOS.");
}
}
You will notice that I'm adding data to persistence in 2 steps, the first Object and the rest of them. This was an idea I saw on this post, here in StackOverflow, to allow appending data to a Java generated file. I have no problem with this code, it works perfectly.
Later on, back on my Activity, the internet connection is detected and I try to recover the file saved on the disk:
phantoms = new ArrayList<Funcionario>();
Object obj = arq.readObjectFromFile(openFileInput("dados.jlog"));
Funcionario func = null;
if (obj instanceof Funcionario) {
func = (Funcionario) obj;
}
while (func != null) {
phantoms.add(func);
arq.removeObjectFromFile(openFileInput("dados.jlog"), func,
getApplicationContext());
func = (Funcionario) arq
.readObjectFromFile(openFileInput("dados.jlog"));
}
The original idea was to read 1 object at a time, then attempt to transmit it and, if successful, erase the object from the file (so it didn't get retransmitted). However, I was having too many error messages with this. Instead, I decided to load all the objects at once, one by one, to see where my problem was more clearly.
Back to the Arquivador class:
public Object readObjectFromFile(FileInputStream arquivo) {
Object retorno = null;
if (arquivo.equals(null)) {
Log.e(TAG_NAME, "FIS is null!");
}
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(arquivo);
retorno = ois.readObject();
} catch (IOException ioex) {
} catch (ClassNotFoundException e) {
} finally {
try {
if (ois != null) ois.close();
} catch (IOException e) {
}
}
return retorno;
}
public void removeObjectFromFile(FileInputStream arqPrincipal,
Object objetoARemover, Context contexto) {
try {
// Construct the new file that will later be renamed to the original
// filename.
ObjectOutputStream oos = new ObjectOutputStream(
contexto.openFileOutput("dados.jlog.temp",
contexto.MODE_APPEND));
ObjectInputStream ois = new ObjectInputStream(arqPrincipal);
Object obj = null;
// Read from the original file and write to the new
// unless content matches data to be removed.
try {
while ((obj = ois.readObject()) != null) {
if (!(objetoARemover.equals(obj))) {
oos.writeObject(obj);
oos.flush();
}
}
} catch (EOFException eof) {
} finally {
oos.close();
ois.close();
// Delete the original file
File aDeletar = contexto.getFileStreamPath("dados.jlog");
File aRenomear = contexto.getFileStreamPath("dados.jlog.tmp");
if (!aDeletar.delete()) {
return;
} else {
// Rename the new file to the filename the original file
// had.
if (!aRenomear.renameTo(aDeletar)) Log.d(TAG_NAME,
"Error renaming file");
else Log.d(TAG_NAME, "Renaming successful");
}
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
Log.d(TAG_NAME, "Arquivo não encontrado");
} catch (IOException ex) {
ex.printStackTrace();
Log.d(TAG_NAME, "Erro de entrada/saída");
} catch (ClassNotFoundException e) {
Log.d(TAG_NAME, "Classe Não Encontrada.");
}
}
The method readObjectFromFile() seems to work just fine. I can even convert the read Object to Funcionario class and read its data.
My problems appear when I use removeObjectFromFile(). The idea is to create a temporary file to store objects from "dados.jlog" file other than the one that has been already loaded in the main program, then once this temp file is created the file "dados.jlog" should be deleted and the temporary file should be renamed to replace it.
The first thing I found out to be strange here is that the ois.readobject() keeps throwing an EOFException. While this makes sense, the tutorial I read on the internet doesn't mention this error. In fact, their code indicates that when the readObject() method reaches the EOF, it would return a reference to null, but instead this class throws this EOFException. I handled this exception in the code - though I'm not sure if this would be the right way to do it.
Another thing I find strange is the fact that this code fails to recognize the object that it should NOT copy. When I compare the object read from the file to the one received as argument, no matter what I try ( == , equals(), etc) they seem different objects to the compiler. Funcionario class is serializable has a serialversionUID, so the object read from the file should be identical to the one I stored. Worse than this, these 2 Objects being compared are read from the same file. They should be identical, right?
After creating the temporary file, I try to delete the original file and rename the temporary file. Though this seems to be working, once the removeObjectFromFile() ends the first time, the program is unable to read the data from the file "dados.jlog" again. I can't read the remaining data from the file and the program enters on an endless loop - since the 1st object is never removed from the list in the file.
Please enlighten me with this matter.

Personally I'd use an SQLLite database. Store each object in a row in the database. Once you've successfully transmitted you can remove the row from the database.
You can even reuse most of your code that you've already done. The easiest way to get there from where you are is to use a separate file for each object and store only the filename of the object in the database. You can then iterate over the rows in the database. Each time you transmit an object to your server simply delete that row from the database (and remove the file from the filesystem!). No rows in the database means no objects remain to be transmitted.

Related

Objects not read completely when reading from a File in Java

I'm saving objects from two array lists to a file and if I restart the application I have called a read method at the startup which will read the data from the file and add them to the array lists
But when reading from the file only the first object is been read and added to the list even when I add several objects to the array list and save to the file when reading only the first object is read
My Method to Read Objects from the File:
void readData() throws IOException{
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("systemData.txt"))) {
doctorList.add((Doctor) in.readObject());
consultations.add((Consultation) in.readObject());
} catch (EOFException ignored){
} catch (IOException | ClassNotFoundException e ) {
e.printStackTrace();
}catch (ClassCastException ignored) {
}
}
My Method to Save Objects to the File:
#Override
public void saveFile() {
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("systemData.txt"))) {
for (Doctor doctor : doctorList) {
out.writeObject(doctor);
System.out.println("Doctor data saved to the file");
}
for (Consultation consultation: consultations){
out.writeObject(consultation);
System.out.println("Consultation data saved to the file");
}
} catch (IOException e) {
e.printStackTrace();
}
}
As #Slaw hints at you need to inform the read method how many many Doctors to read, then the number of Consulations. In short readObject() does not read the collection, only a single item... that's because you wrote item by item.
Think of it like this:
Imagining stuffing 4 apples and 6 oranges into a legwarmer.
When you write/put in, you call writeObject() 4+6 times. When you read you are pulling things out of the leg warmer from the other end (that's why I chose a leg warmer and not a sock!) and you they come out in exactly the order you put them in. But just like a leg warmer/sock you cannot immediately tell what is going to come out as the objects have similar size...
Certainly your code is quite wrote since you invoke readObject() just twice. So you read just one of the Doctors into the doctors collection then what happens? Well, the second readObject() reads a Doctor but your code tries to cast it into a Consultation which will raise a ClassCastException. Your code catches this but then swallows the Exception so you don't know that that happened! (Hence why it is always bad to have empty catch blocks.
With this low level approach, people develop their own protocols to define how to know what is coming out of the leg-warmer, like #Slaw suggests,. In rough code (I didn't check this compiled) it would be:
out.writeObject(doctorList.size());
for (Doctor doctor : doctorList) {
out.writeObject(doctor);
}
out.writeObject(consultations.size());
for (Consultation consultation: consultations){
out.writeObject(consultation);
}
out.flush();
then read
Integer expectedDoctors = (Integer) in.readObject();
for(int i=0; i<expectedDoctors; i++) {
doctorList.add((Doctor) in.readObject());
}
Integer expected Consultations = (Integer) in.readObject();
for(int i=0; i<Consultations; i++) {
consultations.add((Consultation) in.readObject());
}
A simpler approach would be not to write Doctor by Doctor but the whole collection in one go:
out.writeObject(doctorList);
out.writeObject(consultations;
out.flush();
then read
doctorList.addAll((List) in.readObject());
consultations.addAll((List) in.readObject());
Now you don't need the "here comes" counter. Note addAll() and the changed cast.
The other things you must do is call out.flush(); to ensure all the data has been written - essential if you were to wrap the output stream with a Buffer.

Deserializing returns null object Java

Currently writing an application like Booking and I am in the stage where i want to store my infomation in files. I have created a Serializable Database class which has a field with a pathname and 2 methods for read/write . I have about 8 other classes which extend the Database and each holds a Hashmap and some querying methods. Naturally I read the Databases from my files before i start the application and i write before exiting, but I ran into a problem where the objects I am reading are all null. I've been at it for 2 hours now and I need a second opinion. This is the database's read/write methods:
public void write() {
try {
File temp = new File(this.filename);
temp.createNewFile(); // create file if not present
FileOutputStream fileOut = new FileOutputStream(this.filename);
ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
objectOut.writeObject(this);
objectOut.close();
System.out.println("The Object was successfully written to a file");
} catch (Exception ex) {
ex.printStackTrace();
}
}
public Object read() {
Object obj = null;
try {
File temp = new File(this.filename);
temp.createNewFile(); // create file if not present
FileInputStream fileIn = new FileInputStream(this.filename);
ObjectInputStream objectIn = new ObjectInputStream(fileIn);
obj = objectIn.readObject();
System.out.println("The Object was successfully read from the file");
objectIn.close();
} catch (EOFException ex) {
return obj;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
And here is the way I load them (Which is probably the problem) in my Application class
private void loadData() {
accommodationReviewsDatabase = (AccommodationReviews) accommodationReviewsDatabase.read();
brokerAccommodationsDatabase = (BrokerAccommodations) brokerAccommodationsDatabase.read();
credentialsUserDatabase = (CredentialsUser) credentialsUserDatabase.read();
customerReviewsDatabase = (CustomerReviews) customerReviewsDatabase.read();
userConfirmationsDatabase = (UserConfirmations) userConfirmationsDatabase.read();
userMessagesDatabase = (UserMessages) userMessagesDatabase.read();
}
private void writeData() {
accommodationReviewsDatabase.write();
brokerAccommodationsDatabase.write();
credentialsUserDatabase.write();
customerReviewsDatabase.write();
userConfirmationsDatabase.write();
userMessagesDatabase.write();
}
Some extra information that may be asked :
All my classes that I am storing are serializable
The files I am storing the databases are all *.ser (Thats the extension I found)
The files are stored inside the project
If your read() method completes without an EOFException, it ends with return null;. You should return obj;, the object you read.
You should not expect that EOFException will be thrown if your read succeeds. The EOFException would indicate that it ran out of data while it was trying to read your object, and could not complete successfully.
If you do get an EOFException, it is probably a good idea to give some indication instead of silently returning. Silent catch blocks deny you information that could be useful for debugging.

How can I increment a number in a file by an amount?

I have got a .txt file which stores a players' money. I need this file to increment or detriment a certain amount depending on if the player kills something or if they buy something from the shop.
The issue is that I do not know how to actually increment or detriment the contents. I can delete/recreate the .txt file with the new money, however because multiple threads will be accessing the file, then there is the risk that the file may not exist due to it being deleted and not regenerated yet.
Just to clarify, there will only be one thread at a time modifying the file. Other threads will only be reading the file.
So how would I do this without deleting the data/file first?
Here is the code ,read file first and then increment it and store again -
BufferedWriter out = null;
try {
// Read File Contents - score
BufferedReader br = new BufferedReader(new FileReader("c:\\a.txt"));
String storedScore="0";
int storedScoreNumber = 0;
while ((storedScore = br.readLine()) != null) {
storedScoreNumber=(Integer.parseInt(storedScore==null?"0":storedScore));
}
// Write File Contents - incremented socre
out = new BufferedWriter(new FileWriter("c:\\a.txt", false));
out.write(String.valueOf(storedScoreNumber+1));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Have a singleton data accessor with a queue so that it is the only one manipulating the file. If necessary acknowledge to client threads after the write.

Read/Write File in blackberry

I am writing to a file using this code.
protected void writeFile(String text) {
DataOutputStream os = null;
FileConnection fconn = null;
try {
fconn = (FileConnection) Connector.open("file:///store/home/user/documents/file.txt", Connector.READ_WRITE);
if (!fconn.exists())
fconn.create();
os = fconn.openDataOutputStream();
os.write(text.getBytes());
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
try {
if (null != os)
os.close();
if (null != fconn)
fconn.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}}
the code is working fine.
My problem is if I write first time "Banglore" and when I read it, I get "Banglore".
But, second time when I write "India" and when I read it, I get, "Indialore".
so, basically its content is not changing according the text , I am giving.
Please tell me how to fix this.
writing in a file doesn't remove the content but it just replaces the content, so writing 'india' over 'Bangalore' will replace the 'Banga' with 'India' and the rest would remain the same. If you want to completely remove old content with newer one, you need to truncate()
the file from where the newer data ends. truncate(text.getBytes().length)

Strange input error, I have used this code before

After a very bad attempt at doing my homework, I decided it would be faster to abandon everything and start from scratch. Well not everything ... I copied this part since it worked perfectly so I saw no need to modify it. While maybe not perfect, it worked.
However now, when I compile just to test it out, I get an unexpected error:
Input error: java.io.EOFException.
Notice that "Input error" is from my catch(IOException ioe).
The file (fileName) is completely empty. Nothing in it. What could cause this. Is there a way to tell the ObjectInputStream to do nothing if the file is empty ?
Also I tested this out with an empty file on my other "iteration", didn't have this problem. I even named my file the same.
public Repository (String fileName) throws FileNotFoundException,
IOException,
SecurityException,
ClassNotFoundException {
this.fileName = fileName;
this.clients = new ArrayList<Client> ();
FileInputStream fileIn = null;
ObjectInputStream in = null;
try {
fileIn = new FileInputStream(this.fileName);
in = new ObjectInputStream(fileIn);
this.clients = (ArrayList<Client>) in.readObject();
} catch (FileNotFoundException fnfe) {
System.out.println("File not found, error: " + fnfe);
} catch (IOException ioe) {
System.out.println("Input error: " + ioe);
} catch (ClassNotFoundException cnfe) {
System.out.println("Class not found, error: " + cnfe);
} catch (SecurityException se) {
System.out.println(
"You do not have permission to access this file, error: "
+ se);
} finally {
if (fileIn != null)
fileIn.close();
if (in != null)
in.close();
}
Surely before
in = new ObjectInputStream(fileIn);
this.clients = (ArrayList<Client>) in.readObject();
you want to check the file size via File.length().
I assume if it's empty then you'd want to return an empty array list. You can't do that via deserialising an empty file. After all, even an empty array list has a non-zero size (and would need to identify itself as an array list via serialised attributes)
The file (fileName) is completely empty. Nothing in it.
That's exactly the problem. You cannot read an object (or an array) from an empty file. It will not find any data and throw an End-of-file-Exception (EOFException).
Even an empty array - when serialized to a file - will produce some data because the object stream will write the type (ArrayList) and the size of the array (0) to the file. When you try to read it, it will expect to find this data.

Categories