WatchService issues - logfile doesn't refresh on modification - java

I use a (third-party) Windows10 application, which generates .txt log files. In my own application I wrote a class, which uses WatchService and observes the folder for these .txt changes. I know my class works correctly, becuase I tested with other files/JUnit. During testing changes are picked up right away, all correct.
In case of the .txt logs nothing gets picked up.
I played around and noticed, that my application pickes up the updates only if I go to the explorer window and hit F5 (refresh). Size of the file is refreshed and also my WatchService fires an update.
Any idea why this weird behaviour would happen? It probably is on the log application level or Windows itself, but I wonder if anyone can come up with a solution to this with Java?

Because there is no suggestion I will share what I did, solution I don't particularly like, but perhaps it will be helpfull to someone.
In order to refresh the folder I simply created a loop, which checks for file sizes. The below code checks sizes of all files, but if you know the exact name of the file, you could also implement this for that particular file.
File folder = new File("C:\\path\\to\\logs\\");
while (true)
{
Thread.sleep(5000);
File[] listOfFiles = folder.listFiles();
for (File f : listOfFiles)
{
System.out.println("\tFile size: " + f.length());
}
}
You can also change the sleep value from 5000 (5seconds) if you need it checked more or less often.

Related

Reading current and new files from a directory using Java

I have written a program to process files in a directory. At start up it reads the current files in a directory, and then it uses a monitor to discover new files. Once it has processed a file,the program deletes the file. The problem is that there is a time gap, no matter how slight, between reading the files in a directory at startup and then starting the listener. A file created in that gap would be missed. One possible solution would be to repeatedly read the files in a directory (newDirectoryStream), but that doesn't seem as elegant or possibly efficient as using a monitor. The code uses the Apache Commons monitor and looks something like:
// Read Current files
stream = Files.newDirectoryStream(listenDir);
processFile(file);
// Process New files
FileAlterationObserver observer = new
FileAlterationObserver(listenDir.toAbsolutePath().toString(),filter);
FileAlterationMonitor monitor = new FileAlterationMonitor(POLL_INTERVAL);
FileAlterationListener listener = new FileAlterationListenerAdaptor() {
#Override
public void onFileCreate(File file) {
processFile( file.toPath());
}
};
observer.addListener(listener);
monitor.addObserver(observer);
monitor.start();
Simply flip it: First set up the listener and then obtain a directory stream. Go through a concurrent set which lets you do a 'only once ever' layout (the one that added the file name to the set and got the return value indicating 'you actually are the one that added it, you're not merely re-applying something that was already in there' - then you handle the file, otherwise you keep going). This way, if the file is added right in the 'sweet spot', both the dirstream and the observer would get it, but still only one will process it.

How to prevent file wipe if an error occurs while writing to it?

This is an issue I have had in many applications.
I want to change the information inside a file, which has an outdated version.
In this instance, I am updating the file that records playlists after adding a song to a playlist. (For reference, I am creating an app for android.)
The problem is if I run this code:
FileOutputStream output = new FileOutputStream(file);
output.write(data.getBytes());
output.close();
And if an IOException occurs while trying to write to the file, the data is lost (since creating an instance of FileOutputStream empties the file). Is there a better method to do this, so if an IOException occurs, the old data remains intact? Or does this error only occur when the file is read-only, so I just need to check for that?
My only "work around" is to inform the user of the error, and give said user the correct data, which the user has to manually update. While this might work for a developer, there is a lot of issues that could occur if this happens. Additionally, in this case, the user doesn't have permission to edit the file themselves, so the "work around" doesn't work at all.
Sorry if someone else has asked this. I couldn't find a result when searching.
Thanks in advance!
One way you could ensure that you do not wipe the file is by creating a new file with a different name first. If writing that file succeeds, you could delete the old file and rename the new one.
There is the possibility that renaming fails. To be completely safe from that, your files could be named according to the time at which they are created. For instance, if your file is named save.dat, you could add the time at which the file was saved (from System.currentTimeMillis()) to the end of the file's name. Then, no matter what happens later (including failure to delete the old file or rename the new one), you can recover the most recent successful save. I have included a sample implementation below which represents the time as a 16-digit zero-padded hexadecimal number appended to the file extension. A file named save.dat will be instead saved as save.dat00000171ed431353 or something similar.
// name includes the file extension (i.e. "save.dat").
static File fileToSave(File directory, String name) {
return new File(directory, name + String.format("%016x", System.currentTimeMillis()));
}
// return the entire array if you need older versions for which deletion failed. This could be useful for attempting to purge any unnecessary older versions for instance.
static File fileToLoad(File directory, String name) {
File[] files = directory.listFiles((dir, n) -> n.startsWith(name));
Arrays.sort(files, Comparator.comparingLong((File file) -> Long.parseLong(file.getName().substring(name.length()), 16)).reversed());
return files[0];
}

Java program can't use file write after it's written

The thing is this, I am creating a file, that an XML resource uses, right after creation. When the program is done executing, the file should be deleted. This is what happens:
I run the program, file does not yet exist...
File should be created using FileWriter:
File file = new File("src/main/resources/org/avalin/optaplanner/solver/employeeShiftsScoreRules.drl");
try (FileWriter fileWriter = new FileWriter(file))
{
fileWriter.write("Content...");
fileWriter.flush();
fileWriter.close();
}
catch(IOException e)
{
e.printStackTrace();
}
And then I have this code:
private static synchronized Solver buildSolver()
{
SolverFactory solverFactory =
SolverFactory.createFromXmlResource(SOLVER_CONFIG_XML);
return solverFactory.buildSolver();
}
The filewriting is also wrapped in a synchronized method, but I assume that since they're not directly accessing the same variable, they have no effect what so ever. The file is being read from the SOLVER_CONFIG_XML seen above.
When the program ends, it deletes the file on the path given, so that when it runs next time it will be created accordingly to parameters given to the program.
Now this is what happens...
The first time I run the program I get an error, saying the file isn't written.
Exception in thread "main" java.lang.IllegalArgumentException: The scoreDrl (org/avalin/optaplanner/solver/employeeShiftsScoreRules.drl) does not exist as a classpath resource in the classLoader
I can make prints right after filewriting, that concludes the method HAS run through the first time around, but for some reason, the file is not "created" anyway, before the program ends executing the first time around...
The second time, program runs fine, as the file was created before it gets to the exception?
Is there a way to make sure the file is "completely written" before the next part of my program executes? The file differs in length each time, as it is dynamically created from what the user inputs, so I can't check on that. I would assume it would be completely written as it did execute the prints I made after fileWriter.close() but apparently not so.
It looks like you are writing your file to the src/main/resources folder, which is a standard location for resource sources - that is, where your build system reads files from, and not where your running program does.
Although it's possible to add your source folders to the classpath of your running problem, it is bad practice - try to find out where your build system writes its output to (probably separate folders for class files and copied resources) and write your file there.

java renameTo method not working

I know this has been probably answered a million times on here but everything I have looked at has not helped me. Here is my code:
for(File g: f.listFiles()){
for(File h : g.listFiles()){
try{
Scanner s = new Scanner(h);
String timestamp = s.next().split("[?]")[4];
File z = new File(h.getAbsolutePath().split("[.]")[0] + timestamp + h.getAbsolutePath().split("[.]")[1]);
boolean q = h.renameTo(z);
}catch(Exception e){
}
}
}
I have checked to see if File z exists and it doesnt. I have checked if File h exists and it does. I have doublechecked that h is an absolute path. If I print the absolute path of z, I get the correct path. None of the directories in f or files in g are open. The files denoted by h are not open. Could there be some flag set or something on the file where windows is not allowing my program to rename it?
My guess is that you are having a similar problem to one I had here File deletion/moving failing
Try using FileinputStreams for the Scanner
FileInputStream fin = new FileInputStream(h);
fin.open()
Scanner s = new Scanner(fin);
//do work
fin.close()
and closing the stream before renaming
The behavior of renameTo varies from platform to platform. Operations that succeed on one platform may fail on another. For example, on my local development workstation (OS X), everything worked as expected. On a production system (Solaris), renameTo failed consistently. I finally determined that it failed when the files were located on different partitions. Obviously that is not the case here, but it illustrates that the method can behave in unexpected ways.
To get consistent behavior, copy the data to a new file, then delete the original.
I had a almost same issue. Some of rename cases succeeded, some failed. For those failed cases, I found, the source file path and destination file path are not on in same file system. In my cases, the NTFS mounted another file system which the destination file would be moved to. Since the rename function's original purpose simply rename a name, not to move the data of the concerned file. If both source file path and destination file path are in different file system, some version of JVM will fail on certain platforms. Actually, it is a bug in java.io and Solaris has fixed this bug in new versions.
Good Luck!
HappyForever,

Java: Efficient way to scan a folder for a particular file

I am contacting an external services with my Java app.
The flow is as follow: ->I generate an XML file, and put it in an folder, then the service processes the file and return another file with the same name having an extension .out
Right now after I put the file in the folder I start with a loop, until I get that file back so I can read the result.
Here is the code:
fileName += ".out";
File f = new File(fileName);
do
{
f = new File(fileName);
} while (!f.exists());
response = readResponse(fileName); // got the response now read it
My question comes here, am I doing it in the right way, is there a better/more efficient way to wait for the file?
Some info: I run my app on WinXP, usually it takes the external service less than a second to respond with a file, I send around 200 request per day to this services. The path to the folder with the result file is always the same.
All suggestions are welcome.
Thank you for your time.
There's no reason to recreate the File object. It just represents the file location, whether the file exists or not. Also you probably don't want a loop without at least a short delay, otherwise it'll just max out a processor until the file exists. You probably want something like this instead:
File file = new File(filename);
while (!file.exists()) {
Thread.sleep(100);
}
Edit: Ingo makes a great point in the comments. The file might not be completely there just because it exists. One way to guarantee that it's ready is have the first process create a second file after the first is completely written. Then have the Java program detect that second file, delete it and then safely read the first one.

Categories