Rollback or Reset a BufferedWriter - java

A logic that handles the rollback of a write to a file is this possible?
From my understanding a BufferWriter only writes when a .close() or .flush() is invoked.
I would like to know is it possible to, rollback a write or undo any changes to a file when an error has occurred?
This means that the BufferWriter acts as a temporary storage to store the changes done to a file.

How big is what you're writing? If it isn't too big, then you could write to a ByteArrayOutputStream so you're writing in memory and not affecting the final file you want to write to. Only once you've written everything to memory and have done whatever you want to do to verify that everything is OK can you write to the output file. You can pretty much be guaranteed that if the file gets written to at all, it will get written to in its entirety (unless you run out of disk space.). Here's an example:
import java.io.*;
class Solution {
public static void main(String[] args) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
// Do whatever writing you want to do here. If this fails, you were only writing to memory and so
// haven't affected the disk in any way.
os.write("abcdefg\n".getBytes());
// Possibly check here to make sure everything went OK
// All is well, so write the output file. This should never fail unless you're out of disk space
// or you don't have permission to write to the specified location.
try (OutputStream os2 = new FileOutputStream("/tmp/blah")) {
os2.write(os.toByteArray());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
If you have to (or just want to) use Writers instead of OutputStreams, here's the equivalent example:
Writer writer = new StringWriter();
try {
// again, this represents the writing process that you worry might fail...
writer.write("abcdefg\n");
try (Writer os2 = new FileWriter("/tmp/blah2")) {
os2.write(writer.toString());
}
} catch (IOException e) {
e.printStackTrace();
}

It is impossible to rollback or undo changes already applied to files/streams,
but there are tons of alternatives to do so:
One simple trick is to clean the destination and redo the process again, to clean the file:
PrintWriter writer = new PrintWriter(FILE_PATH);
writer.print("");
// other operations
writer.close();
You can remove the content entirely and re-run again.
Or if you are sure the last line(s) are the problems, you may do remove last line actions for your purpose, such as rollback the line instead:
Delete last line in text file

Related

Issues with multiple FileOutputStreams to the same file in Java

I'm trying to write to a file using FileOutputStream. When the user selects what file to write to, the program tries to create a FileOutputStream using that file, to check if it works. If it does not, the user has to select a different file. If it does work, the FileOutputStream is closed.
After a file, for which a FOS can be opened, has been selected the program tries again to create another FOS, but this sometimes fails.
I know that you cannot write to a file when it is open on your computer. Could it be that the first FOS has not been "fully closed" and therefore the file is still considered open, so that the second FOS can not write to it?
In the following code, what happens is that the first creation and closing of the FOS does not throw an exception, while the second one somehow does. How can this be?
(I know the problem can be solved by simply using the first FOS and not creating a second, but I am interested in understanding why this particular code behaves the way it does.)
try {
FileOutputStream out1 = new FileOutputStream(file);
out1.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
FileOutputStream out2 = new FileOutputStream(file);
} catch (Exception e) {
e.printStackTrace();
}
Based on the symptoms, I surmise that you are using Windows, and the error you are getting in the second open is "file is in use".
Apparently under some circumstances, Windows does not immediately close a file when the application (in this case the JVM) closes the FileHandle:
FileStream.Close() is not closing the file handle instantly
Delay between CloseHandle function call and SMB Close request
This is not Java's doing, and I am not aware of a workaround (in Java) apart from waiting a bit and retrying.
(You could see what happens if you add a Thread.sleep(1000); before the 2nd attempt to create a FileOutputStream.)

Looking for an explanation regarding file.delete() method in try catch block

Trying to make a simple 'cut' program to move files across folders.
After it makes a copy it should delete the source file but it ignores the fileLocation.delete(); method in the try block. If I put it in the 'finally' block it works and also anywhere else in the program after it goes through copying the file but that makes no sense for it to work that way, even if something goes wrong the source will be deleted. My question is why does it ignore it, I was unable to find answers online. Thank you.
File fileLocation = new File("C:\\fileLocation\\picture.png");
File fileDestination = new File("C:\\fileDestination\\picture.png");
try(FileInputStream input = new FileInputStream(fileLocation);
FileOutputStream output = new FileOutputStream(fileDestination)) {
byte[] buffer = new byte[1024];
int length;
while((length = input.read(buffer)) > 0) {
output.write(buffer,0, length);
}
fileLocation.delete();
} catch(IOException exc) {
System.out.println(exc.getMessage());
}
try(FileInputStream input = new FileInputStream(fileLocation);
... ) {
// ..
fileLocation.delete();
}
At this point, input is still open, so you can't delete the file it refers to.
According to the definition of try-with-resources in the language spec, a finally block on a try-with-resources statement will be executed after the resource is closed. As such, putting the delete in the finally block means it can succeed.
Rather than putting it in the finally (which occurs whether or not an exception is thrown), you can split up the resources into two try-with-resources blocks, and delete once you're done with input:
try (FileOutputStream output = ...) {
try (FileInputStream input = new FileInputStream(fileLocation)) {
// ..
}
// input is now closed.
fileLocation.delete();
} catch(IOException exc) {
System.out.println(exc.getMessage());
}
Now, fileLocation is only deleted when no IOException is thrown from any preceding statement in the output try-with-resources block (including the input try-with-resources block).
Or, if you want not to delete it until output is closed: move the IOException catch into a surrounding try/catch (not try-with-resources) block:
try {
try (FileOutputStream output = ...;
FileInputStream input = ...) {
// ..
}
// input and output are now both closed.
fileLocation.delete();
} catch(IOException exc) {
System.out.println(exc.getMessage());
}
Of course, a better way to move a file would be to use the utility method to move files, e.g.
Files.move(fileLocation.toPath(), fileDestination.toPath(), CopyOption.REPLACE_EXISTING);
You're using the wrong API. File.delete() is known-bad API design.
This is what's bad about it, and why it explains your confusion: Unlike just about any other API, if delete() fails to delete, it does not throw any exceptions. Instead, it returns false. This is bad in 3 important ways:
It's un-java-like. Very few APIs do that; the vast majority of them throw something instead.
It is easy to 'forget'. Just writing x.foo(); on its own, where foo() is any method that returns something (i.e. has a non-void return type), is perfectly fine java. it's java-ese for: Run this method, then take the result and toss it in the garbage. You've done that here: Call delete() and ignore the result. For delete(), that's not actually okay unless you intended to write code that effectively means: "try to delete this path. Whether it succeeds or not, continue with the code". Which, usually, isn't what you want.
If something does go wrong, it is not possible for the delete() method to tell you any details other than 'I could not accomplish it'. No way to have a message or some sort of exception type to clear things up for you.
The solution is simple. Stop using this method. Put it on the banlist: This method should no longer ever be invoked in java code. If you are maintaining some 15 year old stuff, it's fine, I guess, but a quick refactor to get rid of it wouldn't go amiss.
Great! So what's the new one I should be using?
The path/files API in the java.nio.file package.
Replace:
File f = new File("a/b/c.txt");
f.delete();
with:
Path p = Paths.get("a/b/c.txt");
Files.delete(p);
Unlike file.delete(), Files.delete(path) WILL throw an exception if the deletion cannot be performed. This exception then contains suitable information about why. For example, because the file doesn't exist, or because you do not have write access to the underlying directory, or because the file system is mounted read only, etcetera.
The new File API is also vastly more capable. It can properly handle links or alternate file systems, for example. It also has more methods. For example, it has the Files.move method which may be of particular use here.
Just for reference, why is my delete operation failing?
Probably because your own process still has the file open. On some OS/filesystem combos (in particular, on windows and e.g. NTFS), you can't delete open files. Even if your own process is the one that still has the file open.
If you use Files.delete() you'll get an exception with a message that'll get you a lot closer to that conclusion than 'the delete() call returned false', fortunately.

Why I couldn't write all the characters to the file with java FileWriter? [duplicate]

I have a Java program that reads some text from a file, line by line, and writes new text to an output file. But not all the text I write to my BufferedWriter appears in the output file after the program has finished. Why is that?
The details: the program takes a CSV text document and converts it into SQL commands to insert the data into a table. The text file has more than 10000 lines which look similar to following:
2007,10,9,1,1,1006134,19423882
The program seems to work fine except it just stops in the file randomly half way through creating a new SQL statement having printed it into the SQL file. It looks something like:
insert into nyccrash values (2007, 1, 2, 1, 4, 1033092, 259916);
insert into nyccrash values (2007, 1, 1, 1, 1, 1020246, 197687);
insert into nyccrash values (2007, 10, 9, 1
This happens after about 10000 lines but several hundred lines before the end of the file. Where the break happens is between a 1 and a ,. However, the characters doesn't seem important because if I change the 1 to a 42 the last thing written to the new file is 4, which is cutting off the 2 from that integer. So it seems like the reader or writer must just be dying after writing/reading a certain amount.
My Java code is as follows:
import java.io.*;
public class InsertCrashData
{
public static void main (String args[])
{
try
{
//Open the input file.
FileReader istream = new FileReader("nyccrash.txt");
BufferedReader in = new BufferedReader(istream);
//Open the output file.
FileWriter ostream = new FileWriter("nyccrash.sql");
BufferedWriter out = new BufferedWriter(ostream);
String line, sqlstr;
sqlstr = "CREATE TABLE nyccrash (crash_year integer, accident_type integer, collision_type integer, weather_condition integer, light_condition integer, x_coordinate integer, y_coordinate integer);\n\n";
out.write(sqlstr);
while((line = in.readLine())!= null)
{
String[] esa = line.split(",");
sqlstr = "insert into nyccrash values ("+esa[0]+", "+esa[1]+", "+esa[2]+", "+esa[3]+", "+esa[4]+", "+esa[5]+", "+esa[6]+");\n";
out.write(sqlstr);
}
}
catch(Exception e)
{
System.out.println(e);
}
}
}
You need to close your OutputStream which will flush the remainder of your data:
out.close();
The default buffer size for BufferedWriter is 8192 characters, large enough to easily hold hundreds of lines of unwritten data.
You must close() your BufferedWriter. You must close() your BufferedWriter because it IS-A Writer and thus implements AutoCloseable, which means (emphasis added) it is
A resource that must be closed when it is no longer needed.
Some people say you must first call flush() for your BufferedWriter before calling close(). They are wrong. The documentation for BufferedWriter.close() notes that it "Closes the stream, flushing it first" (emphasis added).
The documented semantics of flushing (flush()) are
Flushes this stream by writing any buffered output to the underlying stream
So, you must close, and close will flush any buffered output.
Your output file does not include all the text you wrote to your BufferedWriter because it stored some of that text in a buffer. The BufferedWriter never emptied that buffer, passing it through to the file, because you never told it to do so.
Since Java 7, the best way to ensure an AutoCloseable resource, such as a BufferedWriter, is closed when it is not longer need is to use automatic resource management (ARM), also known as try-with-resources:
try (BufferedWriter out = new BufferedWriter(new FileWriter(file))) {
// writes to out here
} catch (IOException ex) {
// handle ex
}
You must also close your BufferedReader when it is no longer need, so you should have nested try-with-resources blocks:
try (BufferedReader in = new BufferedReader(new FileReader("nyccrash.txt")) {
try (BufferedWriter out = new BufferedWriter(new FileWriter("nyccrash.sql"))) {
// your reading and writing code here
}
} catch (IOException ex) {
// handle ex
}
Do not be tempted (as other answers here suggest) just to call close() at the end of your method, when your code has "finished" using the writer. That will not work if your writing code throws an exception, and in particular if it throws an IOException.
A resource that must be closed when it is no longer needed.
finally {
out.close();//this would resolve the issue
}
Some things to consider:
BufferedWriter.close() flushes the buffer to the underlying stream, so if you forget to flush() and don't close, your file may not have all the text you wrote to it.
BufferedWriter.close() also closes the wrapped Writer. When that's a FileWriter, this will ultimately close a FileOutputStream and tell the OS that you're done writing to the file.
The garbage collector will automatically call close(), not on the BufferedWriter or the wrapped FileWriter, but on the FileOuputStream. So the OS will be happy, but you have to wait for the GC.
However, you always want to release OS resources as soon as you no longer need them. This goes for open files, database connections, print queues ... anything. Trust me on this one.
BufferedWriter.close() does clear up the internal character buffer, so that memory will be available for garbage collection, even while the BufferedWriter itself remains in scope.
So, Always close your resources (not just files) when you're done with them.
If you really want a peek under the covers, most of the Java API's source is available. BufferedWriter is here.
Your code does not appear to be closing the writer after you've finished writing to it. Add an out.close() (preferably in a finally block) and it should work properly.
you dint close your BufferedWriter.close it inside a finally block
finally {
out.close();//this would resolve the issue
}
Always close your resources (not just files) when you're done with them.
finally {
out.close();//this would resolve the issue
}
There might be situations when you want to flush the buffer without closing the file. In these situations you can use the flush-method.
Since you're using BufferedWriter you can also flush the buffer when appropriate:
out.flush()
This will write the rest of the buffer to the actual file. Close-method also flushes the buffer and closes the file.
out.close()
There might be situations when you want to flush the buffer without closing the file. In these situations you can use the flush-method.
You can also use BuffredWriter's newline-method instead of adding \n to the end of the line. Newline-method uses system specific line separator so your code works on different platforms.
out.newLine()
According to documentation it is no use calling flush() method. If you intent to use FileWriter then flush() would help you out.
Basically in this case, you just need to close, BufferedWriter.close() only. This will flush the remainder of your data.
create finally block and put the close method inside so that it will put all data without missing.
finally {
out.close();
}

Write to the same file using BufferWriter from multiple classes

I want to output my result to a file. I use BufferWriter as below:
public class class1{
...
void print()
{
System.out.println("The name "+outName()+" Tel: "+outNumber());
try{
PrintWriter printWriter=new PrintWriter(new BufferedWriter(new FileWriter("myfile.txt", true)));
printWriter.println("The name "+outName()+" Tel: "+outNumber());
}catch (IOException e){}
}
}
However I have another class and main function also having their own print functions
public class class2{
...
void print()
{
System.out.println("The name "+outName()+" Tel: "+outNumber());
try{
PrintWriter printWriter=new PrintWriter(new BufferedWriter(new FileWriter("myfile.txt", true)));
printWriter.println("The name "+outName()+" Tel: "+outNumber());
}catch (IOException e){}
}
}
public static void main(String[] args) throws IOException{
try{
PrintWriter printWriter=new PrintWriter(new BufferedWriter(new FileWriter("myfile.txt", true)));
...
printWriter.println("something");
printWriter.close();
}catch(IOException e){ }
}
The code pass the compilation, but only the output from the main function appear in the output file. How to fix it please?
There are three (OK ... make that four, no five) significant problems with your code.
In class2 you don't close or flushthePrintWriter` after you have finished writing. That means that the data will never be written out to the file. That's why you never see the output.
This is the obvious bug. But the rest of the problems are also important. Arguably MUCH MORE important ... so keep reading.
The print() method in class2 leaks file descriptors (!). Each time you call it, it will open a file descriptor, write stuff ... and drop it on the floor. If you call print() repeatedly, the FileWriter constructor will fail. You need to close the file, and the cleanest way to ensure it always happens is to write the code like this:
try (PrintWriter printWriter =
new PrintWriter(new BufferedWriter(
new FileWriter("myfile.txt", true)))) {
printWriter.println(...);
}
This is a "try with resource" ... and it guarantees that the resource (printWriter) will be closed when the scope exits.
You are squashing exceptions.
try {
PrintWriter printWriter - ...
} catch (IOException e) {
// SQUASH!!!
}
This is really, really bad. Basically, you have written your code to ignore the exception. Pretend it never happened ... and throw away the information in the exception that would say why it happened.
You should only ever squash an exception if you are absolutely sure that you will only catch expected exceptions, and that ignoring them is absolutely correct. Here, it isn't. If an IOException is thrown here, you need to know why!
Opening multiple streams to write to the same file is a recipe for problems. The streams won't be synchronized, and you are likely to see the output interleaved in the output file in unexpected ways. If the output pipelines include buffering (like yours do), the problem is worse.
You have serious Java style issues:
A class name should always start with an uppercase letter. Always. Even in example code snippets ...
Code should be consistently indented. I recommend using SP characters rather than TAB characters because tabs don't display consistently.
There are style rules about where you should and should not put spaces and line breaks. For example, there should always be whitespace around a binary operator. Find a Java style guide, read it and format your code accordingly.
Always write your code so that >>other people<< can read it.
I think you need to call new class1().print() or new class2().print(), i.e. you need to instantiate the instances first.
Also please remember to close the file in each print() function.
It's because you're never closing anything exept in main(). You're also swallowing exceptions, so you're concealing the truth from yourself. Don't do that.
But it's poor practice. You should keep the file open and use the same FileWriter, BufferedWriter, PrintWriter, and synchronize access to them so you don't get interleaved data.
Not a good idea overall.

Cannot properly close the text file

I have this code that copies an array elements into a text file, and after copying the files I have a button which opens the file i-copied.
try
{
print = new PrintWriter("C:\\Users\\Jofrank\\workspace\\Java\\src\\payroll\\report.txt");
print.println("EMPLOYEES PAYROLL RECORD AS OF "+dateFormat.format(date));
print.println();
for(int x=0;x<department.length;x++)
{
print.println("DEPARTMENT: "+department[x].toUpperCase());
print.println("\tPAYROLL PERIOD\tEMPLOYEE NUMBER\tNAME\tPAY RATE\tHOURS WORKED\tSALARY");
print.println();
for(int y=0;y<trans.length;y++)
{
if(trans[y] == null)
{
continue;
}
if(trans[y].getDepartment().equals(department[x]))
{
print.println("\t"+trans[y].getPayrollPeriod()+"\t"+trans[y].getEmpNo()+"\t\t"+trans[y].getName()+"\t"+trans[y].getPayRate()+"\t\t"+trans[y].getHoursWorked()+"\t\t"+String.format("%,.2f", (trans[y].getPayRate()*trans[y].getHoursWorked())));
total+=(trans[y].getPayRate()*trans[y].getHoursWorked());
}
}
print.println("\t\t\t\t\t\t\t\t\tTOTAL:\t"+String.format("%,.2f", total));
print.println();
total=0;
}
print.close();
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
Unfortunately, My text file was not UPDATED unless i-close the system.
Is there a way that my text file will be updated automatically without CLOSING the system?
You can actually create a PrintWriter with autoFlush turned on:
print = new PrintWriter(new FileOutputStream
("C:\\Users\\Jofrank\\workspace\\Java\\src\\payroll\\report.txt"), true);
Here 2nd parameter is true. As per Javadoc:
autoFlush - A boolean; if true, the println, printf, or format
methods will flush the output buffer
That depens much on the system.
flush() usually should work, but this is all not garuanteed.
There are some embedded flash file system where you might call sync, too.
But for the first apporach try flush()
You need to empty your stream for it to be written to the file. As others have suggested, use .flush() to accomplish this without having to .close() your stream. Otherwise I believe .close() automagically calls .flush() for you to ensure your stream has been emptied and it's contents written to disk or wherever you are directing it.
In documentation it is said that PrintWriter does not flush lines automatically.
You may need to use different constructor for PrintWriter:
PrintWriter(File file)
but you have to open the file itself and then close it after writing is done.

Categories

Resources