B4J Library jFileWatcher - watch system file events

Roycefer

Well-Known Member
Licensed User
Longtime User
Version 1.3 adds a bunch of new functionality. Included are new file attributes, interactions with permissions on Posix systems, a recursive delete folder utility and the ability to list the root directories of the file system.
 

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
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

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

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
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

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

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.
 
Cookies are required to use this site. You must accept them to continue using the site. Learn more…