B4J Library jFileWatcher - watch system file events

This library wraps many capabilities from the java.nio.file package and a few from the java.io.File package. It allows you to set certain directories to be watched, raising events when files or folders within those directories are created, deleted or modified. Additionally, this library enables you to get and set last access time, last modified time, creation time, read-only state and hiddenness of files. Beware, some operating systems won't support all of those attributes.

On Windows, this library performs quite well, using the system's native file events and consuming only a minuscule amount of system resources. The latency was also extremely low. On systems that don't have native file events, the library will poll the file system for changes, periodically. This might introduce a latency in receiving file change events and might consume more resources, though I haven't tested it on any such systems.

The watching is performed off the main thread however events are raised on the main thread.

Example code:
B4X:
Sub Process_Globals
    Dim fw As FileWatcher
    Dim t as Timer
End Sub

Sub AppStart(Args() as String)
     t.Initialize("t", 60*1000)   'Stop file watching in 1 minute
     fw.Initialize("fw")     'Initialize with the event name
     fw.SetWatchList(Array as String(File.DirApp))   'Set the current dir to be watched
     fw.StartWatching       'Start Watching
     t.Enabled = true        'Start the timer

     Log("creation time: " & fw.GetCreationTime("C:\Test\test.txt"))   'Log creation time of a file
     fw.SetCreationTime("C:\Test\test.txt", DateTime.Now)    'Set the creation time to now
     Log("creation time: " & fw.GetCreationTime("C:\Test\test.txt"))   'Log the new creation time of file
     StartMessageLoop
End Sub

Sub fw_CreationDetected(FileName As String)
    Log("CreationDetected: " & FileName)    'Logs the creation of a new file or folder
End Sub

Sub fw_DeletionDetected(FileName As String)
    Log("DeletionDetected: " & FileName)   'Logs the deletion of a file or folder
End Sub

Sub fw_ModificationDetected(FileName As String)
    Log("ModificationDetected: " & FileName)   'Logs the modification of a file or folder
End Sub

Sub fw_WatchingTerminated   'Logs the termination of the FileWatcher
    Log("WatchingTerminated")   'The FileWatcher can still be used, it just has to be started again.
    StopMessageLoop     'With the main message loop ended and the FileWatcher
                               'watcher thread ended, this application will close now.
End Sub

Sub t_Tick
     fw.StopWatching
     Log("Timer Ticked, watching stopped")
End Sub

This library was compiled against jdk 8.

EDIT(26JUL2015): Updated to version 1.1. See post #3 for details.
EDIT(2AUG2015): Updated to version 1.2. See post #8 for details.
EDIT(06MAR2018): Updated to version 1.3. See post #21 for details.
EDIT(23APR2020): Updated to version 1.4. See post #28 for details.
 

Attachments

  • jFileWatcher.zip
    5.7 KB · Views: 643
  • jFileWatcher1.1.zip
    5.8 KB · Views: 521
  • jFileWatcher1.2.zip
    5.9 KB · Views: 771
  • jFileWatcher1.3.zip
    8 KB · Views: 587
  • jFileWatcher1.4.zip
    8.1 KB · Views: 532
Last edited:

Jmu5667

Well-Known Member
Licensed User
Longtime User
Hello Roycefer

I know this thread is old, but I think you may have a problem with thread disposal when you stop and start watching. I discover this issue in a B4J server app. While testing the internal restarter method of the server I noticed that there was a thread leak. I disabled the use of JFilewatcher and the problem seems to have gone away. I had noticed multiple thread references in a thread dump so that is what steered me in that direction. As I said MAY have a problem, I cannot be sure, but it is worth a look.

Regards

John.
 

wl

Well-Known Member
Licensed User
Longtime User
It's entirely possible. I'll look into it.

Hi, any news on this. I plan to start using the watcher in a (j)server application, and a resource like would be a pity in that case.

Thanks
 

Jmu5667

Well-Known Member
Licensed User
Longtime User
Hi, any news on this. I plan to start using the watcher in a (j)server application, and a resource like would be a pity in that case.

Thanks

A simple class in b4j, you can modify it to suite your own needs, and add it as a background worker so it runs on it own thread. You could pass the an array of files to the initialise sub and off you go.

B4X:
Sub Class_Globals
    
    Private lm             As Long
    Private tmrHook        As Timer
    Private threadName    As String
    
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize()

    tmrHook.Initialize("t",2000)
    tmrHook.Enabled = True
    threadName = getcurrentThreadName
    
    Main.filewatcher1 = Me
    
    writelog($"Thread Initialized, Watching ${Main.SettingsFile}"$)
    StartMessageLoop 
    
End Sub

Public Sub Stop

    writelog($"Thread Stopped"$)
    tmrHook.Enabled = False
    StopMessageLoop
    
End Sub

Sub t_Tick
    
    Dim d As Long
    
    If Main.restartingServer Then
        Return
    End If
    
    If File.Exists(Main.appPath, Main.SettingsFile) Then
        If lm = 0 Then
            lm = File.LastModified(Main.appPath, Main.SettingsFile)
        End If
        d = File.LastModified(Main.appPath, Main.SettingsFile)
        If lm <> d Then
            lm = d
            writelog(Main.SettingsFile & " has been modified, reload settings.")
            mod_settings.load_settings
            ' // do we have the restart key
            If Main.SettingsMap.ContainsKey("restart_server") Then
                Dim v As Int = Main.SettingsMap.GetDefault("restart_server",0)
                If v = 1 Then
                    writelog("Restarting Server ... ")
                    ' // reset the value
                    Main.SettingsMap.Put("restart_server",0)
                    ' // update the settings file
                    File.WriteMap(Main.appPath, Main.SettingsFile, Main.SettingsMap)
                    ' // call the restart
                    CallSubDelayed2(Main,"RestartServer",True)
                End If
            End If
        End If
    End If
    
    
End Sub


Sub getcurrentThreadName As String
    
    Dim t As JavaObject
    Return t.InitializeStatic("java.lang.Thread").RunMethodJO("currentThread", Null).RunMethod("getName", Null)
    
End Sub

Sub writelog(pData As String)
    
    Try
        CallSubDelayed3(Main.logger1,"writelog",DateTime.Now,threadName & ":INFO:filewatcher│" & pData)
    Catch
        Log(threadName & ":INFO:filewatcher│" & pData)
    End Try
    
End Sub

Regards

John.
 

wl

Well-Known Member
Licensed User
Longtime User
Thanks John,

I have bee using your library in the past succesfully. Great library. I'm now implementing a webserver with jBasicLib scripting on the backend (and websockets). As a way to have notifications between requests etc I would like to use the FileWatcher.

It would mean that multiple websocket instances might register a FileSystelmWatcher to get notified for updates. I have no control on how many that might be; so this is why it is important for me to know whether there are known resource (eg: memory) leaks as the server could be running 24/7.

Thanks
 

Jmu5667

Well-Known Member
Licensed User
Longtime User
Thanks John,

I have bee using your library in the past succesfully. Great library. I'm now implementing a webserver with jBasicLib scripting on the backend (and websockets). As a way to have notifications between requests etc I would like to use the FileWatcher.

It would mean that multiple websocket instances might register a FileSystelmWatcher to get notified for updates. I have no control on how many that might be; so this is why it is important for me to know whether there are known resource (eg: memory) leaks as the server could be running 24/7.

Thanks
Hi, it's not my Lib, I was using it,but wrote the class as an alternative. I guess you can create a class instance in each of your web sockets if you want and monitor what ever files you want.

Regards,

John.
 
Hello,
looking for a solution for a multi-directory watch. In the project I will have to check around 100 directories per instance. How do I know the directory in which the change was made? (I checked V1.2. V1.3 V1.4)
A
Idea:
Sub fw_ModificationDetected (Foldername as String, FileName As String)
would be the solution.


B4J: Foldername:
    fw.Initialize("fw") 
    fw.SetWatchList(Array As String("c:/Test","c:/Test2","C:/Test3"))   'multiple folder watch
    Log ("Watch:" & fw.GetWatchList)
    fw.StartWatching       'Start Watching

    Sub fw_ModificationDetected(FileName As String)
        Log("ModificationDetected: " & FileName)   'Logs the modification of a file !  Where is the folderinfomation?
    End Sub
 
Last edited:

MathiasM

Active Member
Licensed User
Hello,
looking for a solution for a multi-directory watch. In the project I will have to check around 100 directories per instance. How do I know the directory in which the change was made? (I checked V1.2. V1.3 V1.4)
A
Idea:
Sub fw_ModificationDetected (Foldername as String, FileName As String)
would be the solution.


B4J: Foldername:
    fw.Initialize("fw")
    fw.SetWatchList(Array As String("c:/Test","c:/Test2","C:/Test3"))   'multiple folder watch
    Log ("Watch:" & fw.GetWatchList)
    fw.StartWatching       'Start Watching

    Sub fw_ModificationDetected(FileName As String)
        Log("ModificationDetected: " & FileName)   'Logs the modification of a file !  Where is the folderinfomation?
    End Sub

I had exactly the same 'problem'. I fixed it by creating a new CustomFileWatcher (a composition which holds a real FileWatcher class), which can only hold 1 folder to watch, and rethrow the events via Ducktyping.
Now I just spawn 100 CustomFileWatchers and that works.
 

imbault

Well-Known Member
Licensed User
Longtime User
I had exactly the same 'problem'. I fixed it by creating a new CustomFileWatcher (a composition which holds a real FileWatcher class), which can only hold 1 folder to watch, and rethrow the events via Ducktyping.
Now I just spawn 100 CustomFileWatchers and that works.

Good, can we see your solution, Mathias?
 

MathiasM

Active Member
Licensed User
Good, can we see your solution, Mathias?

Sure thing!

This is the class I created (very simple) I named this class "CustomFileWatcher"

B4X:
#Event: CreationDetected(PathToWatch As String, Filename As String)
#Event: DeletionDetected(PathToWatch As String, Filename As String)
#Event: ModificationDetected(PathToWatch As String, Filename As String)
Sub Class_Globals
    Private FWatcher As FileWatcher
    Private mEventName As String
    Private mCallbackModule As Object
    Private mPathToWatch As String
End Sub

'Initialze the object
'Pass the path you want to watch, the module you want to be called, the event you want to be called
Public Sub Initialize(PathToWatch As String, CallbackModule As Object, EventName As String)
    mEventName = EventName
    mCallbackModule = CallbackModule
    mPathToWatch = PathToWatch
    FWatcher.Initialize("fwEvent")
    FWatcher.SetWatchList(Array As String(mPathToWatch))
    FWatcher.StartWatching
End Sub

Private Sub fwEvent_CreationDetected(FileName As String)
    CallSub3(mCallbackModule, mEventName & "_CreationDetected", mPathToWatch, FileName)
End Sub

Private Sub fwEvent_DeletionDetected(FileName As String)
    CallSub3(mCallbackModule, mEventName & "_DeletionDetected", mPathToWatch, FileName)
End Sub

Private Sub fwEvent_ModificationDetected(FileName As String)
    CallSub3(mCallbackModule, mEventName & "_ModificationDetected", mPathToWatch, FileName)
End Sub

You use it just like the normal filewatcher class. Here is small example:
B4X:
Sub Process_Globals
    Dim cfw As CustomFileWatcher
End Sub

Sub AppStart (Args() As String)
    cfw.Initialize("C:", Me, "CustomFileWatcherEvent")
    StartMessageLoop
End Sub

Sub CustomFileWatcherEvent_CreationDetected(PathToWatch As String, Filename As String)
    Log("New file/folder created: " & PathToWatch & Filename)
End Sub

Note: I don't know if this is the best method to do this. Maybe someone with more experience can elaborate on this.
 

amorosik

Expert
Licensed User
I am using the jFileWatcher library to create a spooler for the printouts sent from the pos program to the printer
However, it is not possible to understand when the created file is complete
In the sense that I am able to intercept the event of the file creation, then the subsequent changes
But I can't 'understand' when the text file has been completed and closed by the management program
At that point the spooler should send the file to print
Assuming that the creation and subsequent modifications of the single text file is not instantaneous, is there any way to tell when the file is 'closed' by the pos program?
 

Jmu5667

Well-Known Member
Licensed User
Longtime User
I am using the jFileWatcher library to create a spooler for the printouts sent from the pos program to the printer
However, it is not possible to understand when the created file is complete
In the sense that I am able to intercept the event of the file creation, then the subsequent changes
But I can't 'understand' when the text file has been completed and closed by the management program
At that point the spooler should send the file to print
Assuming that the creation and subsequent modifications of the single text file is not instantaneous, is there any way to tell when the file is 'closed' by the pos program?
Maybe consider using a temp filename first, dump your data in, close it, then rename it to the filename you are watching for.
 

Roycefer

Well-Known Member
Licensed User
Longtime User
It has been a while since I've worked on this library but if my memory serves me, you can get a variety of different attributes of a file like LastModifiedTime. You might try starting a Timer when the CreationDetected event is raised. In that timer_Tick event you can check the LastModifiedTime. After N number of ticks with the same LastModifiedTime value, you might consider the file as no longer being written to by the pos program. You'll have to experiment to find a good value for N.
 

amorosik

Expert
Licensed User
It has been a while since I've worked on this library but if my memory serves me, you can get a variety of different attributes of a file like LastModifiedTime. You might try starting a Timer when the CreationDetected event is raised. In that timer_Tick event you can check the LastModifiedTime. After N number of ticks with the same LastModifiedTime value, you might consider the file as no longer being written to by the pos program. You'll have to experiment to find a good value for N.

Thank you for your help
But even though this is also a possibility, I do not think that waiting for a definite time is the definitive solution, in the sense that there may be cases where the necessary time is longer than expected.
And vice versa, every time a file arrives, the system would wait the expected time even if it was not needed
At this moment, having control of the software that produces the file, I think the best solution is to rename it so the file is born as receipt1.temp until it is completed, then after being closed it is renamed in receipt1.txt
And the jFileWatcher will be set up to monitor the * .txt
Whether the file is completed and renamed in 0.1 sec or in 2 seconds, it will be served immediately to the printer
 

MathiasM

Active Member
Licensed User
Don't rely on the fact that renaming is fast, it might not be in some cases.
Just try to read it, if it is read, check for a termination character or something else reliable.
If you can read it, the writing program must be finished, or else the file will be locked. Keep trying to read it in a loop.

Even better, if you control the writing program, terminate the file with a certain (on or more) character which you can check.
 
noob here. i was using file.exists in a loop and that worked fine but jfilewatch seemed to be better so i switched over. but there seems to be a flaw with it and i'm wondering if theres anything you can do about it. i use it more as a network communications sort of thing. my watcher program looks for a file to be created from another program across the network, if it has been created then do something programmically (in my case if the file gate168.txt is created then change gateway address to 192.168.168.168 if file gate167.txt is created then change gateway address to 192.168.168.167) . watcher program works unless it goes to sleep, while asleep if the file is created, when the watcher program wakes up it seems to have missed the creation event. so i end up back to having file exists in a loop again. not sure at all if you can do anything about it, but i read somewhere that on some os systems jfilewatcher polls instead of watching for the file creation event, can you make it where it somethings polls in windows ?
 

John Naylor

Active Member
Licensed User
Longtime User
noob here. i was using file.exists in a loop and that worked fine but jfilewatch seemed to be better so i switched over. but there seems to be a flaw with it and i'm wondering if theres anything you can do about it. i use it more as a network communications sort of thing. my watcher program looks for a file to be created from another program across the network, if it has been created then do something programmically (in my case if the file gate168.txt is created then change gateway address to 192.168.168.168 if file gate167.txt is created then change gateway address to 192.168.168.167) . watcher program works unless it goes to sleep, while asleep if the file is created, when the watcher program wakes up it seems to have missed the creation event. so i end up back to having file exists in a loop again. not sure at all if you can do anything about it, but i read somewhere that on some os systems jfilewatcher polls instead of watching for the file creation event, can you make it where it somethings polls in windows ?

Turn off sleep / hybernate mode in your power settings. Have the screen turn off by all means.
 
"Turn off sleep / hybernate mode" that would work same as my file.exists in a loop in a timer would work. i can program around the problem. i'm more asking if the problem could be fixed at the library level in the first place. thats why i asked on this old thread, if it can be fixed thats great if it can't be fixed then anyone (like me) who wants to use this library should be aware that if your watching for a file event to occur on another computer across the network and your watching computer can go to sleep, be aware that it will be missing file events. if i had known that i would not have used the lib. remember i did say "noob here". hoping roycefer will weight in here. maybe i could beg for source code ?? as if noob could do something.
 
Top