views:

1413

answers:

4

Hi Everyone,

I have a piece of code that generates new data whenever there is new data available as InputStream . The same file is overwritten everytime. Sometimes the file becomes 0 kb before it gets written. A webservice reads these files at regular intervals. I need to avoid the case when the file is 0 bytes.

How do it do this? Will locks help in this case? If the browser comes in to read a file which is locked, will the browser continue to show old data from the cache until the lock is released and file is available to be read again.

try{
String outputFile = "output.html";     
FileWriter fWriter = new FileWriter(outputFile);
//write the data ...

fWriter .flush();


outputFile = "anotheroutput.html";     
fWriter = new FileWriter(outputFile);
//write the data ...

fWriter .flush();
fWriter.close();
}
catch(Exception e)
{
 e.prinStackTrace();
}
A: 
public class Data
{
  String fileName;
  protected  Data(String fileName)
  {
     this.fileName= fileName;
     return; // return from constructor often not needed.
  }

   /* above is in some class somehwere 
    *  then your code brings new info to the file
    */

  // 
  public synchronized accessFile(String data)
  {
    try
    {
       // File name to be class member.
       FileWriter fWriter = new FileWriter(fileName);
       //write the data ...
       fWriter.write(data);
       fWriter .flush();
       fWriter .close();
       return;
    }
    catch(Exception e)
    {
       e.prinStackTrace();
    }

this is not needed:

 outputFile = "anotheroutput.html";     
 fWriter = new FileWriter(outputFile);
 //write the data ...

fWriter .flush();
fWriter.close();

that's because work on the file is a method of class Data

Nicholas Jordan
!? you are assuming that all programs accessing the file are in the same JVM so that the "synchronized" keyword prevents more than one routine from using the file at the same time. what about processes outside the JVM? outside the computer? (if a file is shared via the network)
Jason S
Jason, I was trying to get something up that brings to op the notion of flattening the ( I don't know what it's called, I do it all the time ) where you sorta code by filling the copy-paste buffer and unrolling the loop, I would get to the sync() and locking after op noticed it - I'm busy with with JCE right at the moment, stay with op while he gets the zero-length nothing to do issue worked ... that's another like what you mention on my ... heh, site owners -> can we get a code editor in all the heavy scripting???..... really, really would help ( big time ! )
Nicholas Jordan
A: 

Your requirement is not very clear. Do you want to write a new name file every time or you want to append to the same file or you want to over write the same file? Anyway all three cases are easy and from the API you can manage it.

If the issue is that a web service is reading the file which is not yet complete i.e. is in writing phase. In your web service you should check if the file is read only, then only you read the file. In writing phase once writing is finished set the file to read only.

The 0Kb file happens because you are overwriting the same file again. Overwriting cleans up all the data and then start writing the new content.

Bhushan
+1  A: 

Try writing to a temporary file (in the same file system) and once the file write is complete move it into place using File.renameTo(). If you underlying file system supports atomic move operations (most do) then you should get the behaviour that you require. If you are running on windows you will have to make sure you close the file after reading otherwise the file move will fail.

public class Data
{
    private final File file;
    protected  Data(String fileName) {
        this.file = new File(filename);
    }

   /* above is in some class somehwere 
    *  then your code brings new info to the file
    */

   // 
   public synchronized accessFile(String data) {
       try {
           // Create temporary file
           String tempFilename = UUID.randomUUID().toString() + ".tmp";
           File tempFile = new File(tempFilename);

           //write the data ...
           FileWriter fWriter = new FileWriter(tempFile);
           fWriter.write(data);
           fWriter.flush();
           fWriter.close();

           // Move the new file in place
           if (!tempFile.renameTo(file)) {
               // You may want to retry if move fails?
               throw new IOException("Move Failed");
           }
       } catch(Exception e) {
           // Do something sensible with the exception.
           e.prinStackTrace();
       }
   }
}
Michael Barker
A: 

Thanks for the replies everyone!!

Michael, I am going to try out your solution, it looks like a good fix for my issue. I was also wondering if BufferedWriter would be beneficial in this case. The code is running on Unix so it should be atomic rename I guess. renameTo method has issues under Windows which are better avoided

Thanks for the explanation Bhushan, my problem is the last case you mentioned. I need to overwrite the same file everytime and avoid the 0 KB situation.

kelly
BufferedWriter won't make any real difference in this situation, although it won't hurt at all. It only help when you want a large number of small logical writes to be grouped into a fewer physical writes. If you need a flush() statement after every write then you shouldn't use a BufferedWriter as it will result in an unecessary copy operation.
Michael Barker