I am using Serialization to get persistent storage for my library managing app (I know it is not the right way, but it's the way my professor wants it).
I am using the following code inside my main();
controlador.getBiblioteca().getGestorMaterial().setListaLibros((Modelo.ColeccionLibros) controlador.getSerializador().abrirArchivo("libros.dat"));
My Serializador class has the abrirArchivo("FileName.dat") function (openFile in English).
That function looks like this:
public Object abrirArchivo(String nombreDelArchivo) {
Object retorno = null;
try {
lectorArchivos = new ObjectInputStream(new FileInputStream(
nombreDelArchivo));
retorno = lectorArchivos.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return retorno;
}
Now I am trying to check if the program gets a FileNotFoundException for any of the files. If it does, it should just not deserialize the file and go for the next one: that would just mean there is no book in my library yet.
The problem is, if the line runs, it seems to set my book list using setListaLibros() to null. And whenever I try to access that list, i get a NullPointerException. The list was already initialized as an empty list though, so I just need to leave it alone as long as the "libros.dat" file is not found.
What is the right way to get that done?
I don't know if I understand the problem well. However, as I can see in your code, when an exception FileNotFoundException happens, "retorno" will keep null. That's the reason why you get setListaLibros(null).
And then your list will became null. If you don't want that behavior, you should initialize "retorno" with an empty list instead of null.
You could add a line before this: controlador.getBiblioteca().getGestorMaterial().setListaLibros((Modelo.ColeccionLibros) controlador.getSerializador().abrirArchivo("libros.dat")); which checks if the file exists. If it does not, then it prompts the user. This way, the user knows something went wrong and can act accordingly.
Alternatively, you can make a change in your setListLibros method wherein, if the argument passed is null, then you do not do any assignment.
Personally, I would go with the first option.
As a side note, please break down your code, something like so: controlador.getBiblioteca().getGestorMaterial().setListaLibros((Modelo.ColeccionLibros) controlador.getSerializador().abrirArchivo("libros.dat")) can get hard to read and debug.
There is an aspect that the other answers are not mentioning: why are there no serialized objects when your library is empty?!
What I mean is: you could distinguish between "program runs the first time" (and obviously no serialized data exists) or "program ran before; and thus it fully configures itself from serialized data.
Meaning: "being empty" can be a valid state of a library, too. So another option would be to not use a "special value" (aka "no file with data") to represent that information ... but (de)serialize an empty list.
You could check if the file exist like:
String fileName;
File f1 = new File(fileName);
if (f1.exists()) {
//Do the work
}
Related
I'm creating a "File Manager" to practice my IO skills. If I'm creating a file, how would I handle an error if file#createNewFile() returns false?
#Override
public void run() {
File file = new File(src + "\\" + srcName);
try {
if (file.createNewFile()) {
MessageHandler.createSuccess(comp);
} else {
throw new IOException();
}
} catch (IOException e) {
MessageHandler.error500(comp);
}
}
Right now I'm simply throwing an IOException to be caught in the next block, but I'm not sure if that is the best idea. It should be fine on the client end but when debugging I would have no idea what went wrong internally.
It depends. I guess there is no single clear answer to this, so other members of the community might view this differently than I do.
According to the Javadoc, createNewFile returns false only if the file already exists.
Therefore, in cases where the file should not be present whatsoever, I also just throw an IOException.
If I have a reason to assume the file could be present, the user interacting with the program should be notified of this problem in some way. How exactly you do this depends on the UI of your program.
The code snippet you showed us does not make much sense. Throwing an exception, just to catch it immediately, isn't very performant (unless the clever Java optimizers eliminate the costly operations).
Your current run() method does:
MessageHandler.createSuccess() if the file didn't exist and could be created (file.createNewFile() returned true).
MessageHandler.error500() if the file didn't exist, but could not be created, e.g. because of not having the necessary permissions (file.createNewFile() threw an IOException).
MessageHandler.error500() if the file already existed (file.createNewFile() returned true). [Are you sure that already having the file is an error?]
For the case number 3, creating, throwing and catching an IOException does not serve any useful purpose (other than guiding the code into the catch clause).
I'd simply rewrite the code to something like
#Override
public void run() {
File file = new File(src + "\\" + srcName);
try {
if (file.createNewFile()) {
MessageHandler.createSuccess(comp);
} else {
MessageHandler.error500(comp);
}
} catch (IOException e) {
MessageHandler.error500(comp);
}
}
You might even find a result code different from 500, better representing the cause of the failure.
Exceptions are a very good way for one method to communicate to the other methods up its caller hierarchy that it wasn't able to do its job. Throwing and catching an exception within one method rarely is a good idea.
void appendFile() throws IOException{
FileWriter print = new FileWriter(f, true);
String info = GetData.getWord("Write desired info to File");
print.append(" "); //Write Data
print.append(info);
System.out.println("this executes");
print.flush();
print.close();
}
boolean fileExist(){
return f.exists();
}
try{
if (f.fileExist())
f.appendFile();
else {
throw new IOException();
}
}
catch(IOException e) {
e.printStackTrace();
}
I'm not sure if the ecxeption is well handeled? The FileWriter is not going to be created if there is a fileNotFoundException, therefore don't need to be closed. However, is it possible that this code throws another kind of IOException after the file was opened?, and in that case do I need a finally block to close the file.
No.
It doesn't safely close the resource
The general rule is, if you call a constructor of an object that represents a closable resource, or a method that returns one which documents that this counts as 'opening the resource', which often but not always includes new being part of the method name (examples: socket.getInputStream(), Files.newInputStream), then you are responsible for closing it.
The problem is, what if an exception occurs? So, you have to use try/finally, except that's a mouthful, so there's a convenient syntax for this.
The appendFile method should use it; it isn't, that makes it bad code. This is correct:
try (FileWriter print = new FileWriter(f, true)) {
String info = GetData.getWord("Write desired info to File");
print.append(" "); //Write Data
print.append(info);
System.out.println("this executes");
}
Not how it is not neccessary to flush before close (close implies flush), and in this case, not neccessary to close() - the try construct does it for you. It also does it for you if you get out of the {} of the try via a return statement, via control flow (break), or via an exception, or just by running to the } and exiting normally. No matter how - the resource is closed. As it should be.
It throws description-less exceptions
else throw new IOException(); is no good; add a description that explains why the exception happened. throw new IOException("File not found") is better, but throw new FileNotFoundException(f.toString()) is even better: The message should convey useful information and nothing more than that (in other words, throw new IOException("Something went wrong") is horrible, don't do that, that message adds no useful information), should not end in punctuation (throw new IOException("File not found!") is bad), and should throw a type that is most appropriate (if the file isn't there, FileNotFoundException, which is a subtype of IOException, is more appropriate, obviously).
It commits the capital offense
You should not ever write a catch block whose contents are just e.printStackTrace();. This is always wrong.
Here's what you do with a checked exception:
First, think of what the exception means and whether the nature of your method inherently implies that this exception can occur (vs. that it is an implementation detail). In this case, you didn't show us what the method containing that try/catch stuff even does. But let's say it is called appendFile, obviously a method that includes the text 'file' does I/O, and therefore, that method should be declared with throws IOException. It's not an implementation detail that a method named appendFile interacts with files. It is its nature.
This is somewhat in the eye of the beholder. Imagine a method named saveGame. That's less clear; perhaps the mechanism to save may involve a database instead, in which case SQLException would be normal. That's an example of a method where 'it interacts with the file system' is an implementation detail.
The problem that the exception signals is logical, but needs to be more abstract.
See the above: A save file system can obviously fail to save, but the exact nature of the error is abstract: If the save file system is DB-based, errors would show up in the form of an SQLException; if a file system, IOException, etcetera. However, the idea that saving may fail, and that the code that tried to save has a reasonable chance that it can somewhat recover from this, is obvious. If it's a game, there's a user interface; you should most absolutely TELL the player that saving failed, instead of shunting some stack trace into sysout which they probably never even look at! Telling the user that something failed is one way of handling things, not always the best, but here it applies).
In such cases, make your own exception type and throw that, using the wrapper constructor:
public class SaveException extends Exception {
public SaveException(Throwable cause) {
super(cause);
}
}
// and to use:
public void save() throws SaveException {
try {
try (OutputStream out = Files.newOutputStream(savePath)) {
game.saveTo(out);
}
} catch (IOException e) {
throw new SaveException(e);
}
}
If neither applies, then perhaps the exception is either essentially not handleable or not expectable or nearly always a bug. For example, writing to an outputstream that you know is a ByteArrayOutputStream (which can't throw), trying to load the UTF-8 charset (which is guaranteed by the JVM spec and therefore cannot possibly throw NoSuchCharsetException) - those are not expectable. Something like Pattern.compile("Some-regexp-here") can fail (not all strings are valid regexps), but as the vast majority of regexes in java are literals written by a programmer, any error in them is therefore neccessarily a bug. Those, too, are properly done as RuntimeExceptions (which are exceptions you don't have to catch or list in your throws line). Not handleables are mostly an application logic level thing. All fair game for runtimeexceptions. Make your own or use something that applies:
public void save(int saveSlot) {
if (saveSlot < 1 || saveSlot > 9) throw new IllegalArgumentException("Choose a saveslot from 1 to 9");
// ... rest of code.
}
This really feels like door number one: Whatever method this is in probably needs to be declared as throws IOException and do no catching or trying at all.
Minor nit: Uses old API
There's new API for file stuff in the java.nio.file package. It's 'better', in that the old API does a bunch of bad things, such as returning failure by way of a boolean flag instead of doing it right (by throwing an exception), and the new API has far more support for various bits and bobs of what file systems do, such as support for file links and creation timestamps.
I'm currently in a java course and trying to learn how to catch exceptions. In this case I have an input file that is only allowed to contain certains signs and must contain a goal and a start. I check all this in a seperate class constructor and want to insted of just telling the user something is wrong use a try/catch to tell the user that he did something wrong.
while(scanner.hasNext()){
temp = scanner.nextLine();
try {
testString(temp);
}
catch (/*Don't know what to catch*/){
System.out.println("Input file contains unvalid input");
}
mazeData.add(row, temp);
row++;
}
try{
containStartAndGoal();
}
catch(/*Dont know what to catch?*/){
System.out.println("Input file have either no goal or start!");
}
Currently this is my issue, i have tried to use a IOExecption but that does not seem to work. If I use just Execption i need to catch it every time i try to call this method. Which I do in both my test program and main program. I have tried to read as much as I can around here but don't seem to understand what I am suppose to do. Can I not try something if I don't throw it somewhere else? And what type of execption am I suppose to use when there is an input file that is incorrect. We didn't really get any information around how these work in school only that they exist.
I'm still working on the project I already needed a bit of help with:
JavaFX - TableView doesn't update items
Now I want to understand how this whole Serialization process in Java works, because unfortunately, I don't really get it now.
Before I go on, first of all, I'm a student, I'm not a professional. Second, I'm neither familiar with using DBs, nor XML or JSON, so I'd just like to find solution to my approach, no matter how inelegant it might be in the end, it just needs to work. So please don't feel offended if I just reject any advice in using other techniques.
So here's what I want:
Saving three different class objects to separate files BUT maintaining backward compatibility to each of it. The objects are Settings, Statistics and a "database" object, containing all words in a list added to it. In the future I may add more statistics or settings, means adding new variables, mostly type of IntegerProperty or DoubleProperty.
Now the question is: is it possible to load old version saved files and then during the process just initiate new variables not found in the old version with just null but keep the rest as it has been saved?
All I know is that the first thing to do so is not to alter the serialVersionUID.
Another thing would be saving the whole Model object (which contains the three objects mentioned before), so I just have to implement stuff for one class instead of three. But how would that work then concerning backward compatibility? I mean the class itself would not change but it's attributes in their own class structure.
Finally, what approach should I go for? And most of all, how do I do this and maintaning backward compatibilty at the same time? I do best with some concrete examples rather than plain theory.
Here are two example methods, if it's of any help. I already have methods for each class to write and read an object.
public static void saveModel(Model model, String destination) throws IOException
{
try
{
fileOutput = new FileOutputStream(destination);
objectOutput = new ObjectOutputStream(fileOutput);
objectOutput.writeObject(model);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (objectOutput != null)
try
{
objectOutput.close();
}
catch (IOException e) {}
if (fileOutput != null)
try
{
fileOutput.close();
}
catch (IOException e) {}
}
}
public static Settings readSettings(String destination) throws IOException, FileNotFoundException
{
Settings s = null;
try
{
fileInput = new FileInputStream(destination);
objectInput = new ObjectInputStream(fileInput);
Object obj = objectInput.readObject();
if (obj instanceof Settings)
{
s = (Settings)obj;
}
}
catch (IOException e)
{
e.printStackTrace();
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
finally
{
if (objectInput != null) try { objectInput.close(); } catch (IOException e) {}
if (fileInput != null) try { fileInput.close(); } catch (IOException e) {}
}
return s;
}
Tell me if you need more of my current code.
Thank you in advance!
... you must be this tall
Best advice for Serialisation is to avoid it for application persistence, especially if backwards compatibility is desired property in your application.
Answers
Is it possible to load old version saved files and then during the process just initiate new variables not found in the old version with just null but keep the rest as it has been saved?
Yes. Deserialising objects saved using previous versions of the class into a new version of this class will work only if:
fully qualified name of the class has not changed (same name and package)
previous and current class have exactly the same serialVersionUID; if one of the versions is missing it, it will be calculated as a 'hash' of all fields and methods and upon a mismatch deserialisation will fail.
inheritance hierarchy has not changed for that class (the same ancestors)
no fields have been removed in the new version of the class
no fields have become static
no fields have become transient
I just have to implement stuff for one class instead of three. But how would that work then concerning backward compatibility?
Yes. Providing that all classes of all fields of Model and Model class itself adhere to the rules above.
Finally, what approach should I go for? And most of all, how do I do this and maintaning backward compatibilty at the same time?
Yes, as long as you can guarantee all of the above rules forever, you will be backwards compatible.
I am sure you can appreciate that forever, or even for next year can be very hard to guarantee, especially in software.
This is why people do application persistence using more robust data exchange formats, than binary representation of serialised Java objects.
Raw data for the table, could be saved using anything from CSV file to JSON docs stored as files or as documents in NoSQL database.
For settings / config have a look at Java's Properties class which could store and load properties to and from *.properties or *.xml files or separately have a look at YAML.
Finally for backwards compatibility, have a look at FlatBuffers
The field of application persistence is very rich and ripe, so happy exploring.
after creating a file and populating it with that with a thread if the file is in a USB java can't delete it, when I try on disk it deletes the file ok !
Here is the part of the code that create and after an exception when try to delete the file.
if(canExport && fileCreated)
{
//Create the file
this.file.createNewFile();
//Export the data
this.run();
if(possible == false){ // in case writing fails delete the file created.
file.delete();
Export novaTentativa = new Export(plan);
novaTentativa.fileCreator(plan);
}
}
The file is created when the this.file.createNewFile() acts.
When this.run() runs, there is a lot of methods to populate the data and handle exceptions, if one exception is caught it sets the global variable possible to false so I know the file is created but empty in the USB, after that I try to delete it with file.delete();
You mention that you're trying to delete the file "after an exception" - consequently, your approach is on the wrong track and isn't going to work as-is.
If an exception is thrown by earlier methods (e.g. the createNewFile() call), then that exception will immediately propagate upwards, so your file.delete() call won't get a chance to execute. You'd need to wrap the earlier statements in a try block, and put the delete call in the corresponding catch or finally block in order for it to execute when an exception was thrown.
Here's an example of what you might try to do:
if(canExport && fileCreated)
{
//Create the file
this.file.createNewFile();
try
{
this.run();
}
catch (IOException e)
{
try
{
file.delete();
}
catch (IOException ignore) {} // don't want to mask the real exception
// Rethrow the actual exception from run() so callers can handle it
throw e;
}
}
An alternative approach rather than catching IOExceptions would be to have a finally block (which is always run) and then check a condition there, such as your possible flag.
Note as well that I start the try block after the call to createNewFile() - if an exception is thrown in the create file call then the file won't exist to delete at all!
As a file note, adding "a lot of code that asks for the thread to start over" in your error-handling block is probably not the best design. It would be more appropriate to simply consider recovering from IO situations here, and let the exception bubble up to the top and cause the thread/runnable to die. The logic around restarting tasks and/or resurrecting threads would be better positioned with the class that started the threads in the first place (e.g. a thread pool/task executor/etc.). Scattering the logic throughout the code will make it harder to see what any individual class is doing (not to mention that having a class marshall resources to resurrect itself just seems wrong from an OO standpoint).
Try explicitly stating the drive letter, path and folder to access the USB device to create write and read or delete the file. If that does not work then it is possible only a specific operating system utility or proprietory utility can delete the file.
How certain are you that you closed the file when the write failed? I'll bet money that you are missing a finally block somewhere in this.run(). That would result in exactly the behavior you describe - delete() will fail if the file is open (you should check it's return code - File.delete() doesn't throw exceptions if it is unable to delete the file).
If you want to test this, replace this.run() with a super, crazy simple implementation that writes 100 bytes to the file, sets 'possible' to false, then returns. If the file still won't delete, post the code you are using for this simplified version of run() and maybe someone can spot what's going on.