Is there a way to terminate/force close an async thread created from a tag event?
No, you just need to be careful not to hang yourself. If you spawned off an async infinite loop you need to restart your gateway now.
No, I have a java file watching service in the thread. I have a getStatus() function that returns the thread Id, if it is alive, and if it is interrupted. Sometimes it stops working despite it still being alive and not interrupted.
while trying to get it to work, I spawned a second thread by accident.
Looks like I will have to interrupt() the thread then join() it with the other thread to get rid of it without a gateway start.
That kind of async thread should be checking a memory tag as part of its otherwise infinite loop. .interrupt() response is somewhat voluntary–only checked by java for you on certain operations.
In my async while loop I check it every iteration using the isInterrupted()
method.
Do the other operations in the loop have timeouts to ensure .isInterrupted() is checked reliably?
while self.thread.isAlive() and not self.thread.isInterrupted():
k = self.watcher.poll(5,TimeUnit.SECONDS)
if k:
for e in k.pollEvents():
kind = e.kind()
if kind == swek.ENTRY_DELETE:
filePath = p.resolve(e.context()).toString()
system.tag.write('Cutting/watchService/deletedFile',filePath)
elif kind == swek.ENTRY_CREATE or kind == swek.ENTRY_MODIFY:
filePath = p.resolve(e.context()).toString()
system.tag.write('Cutting/watchService/changedFile',filePath)
k.reset()
Not sure what the problem is. But two recommendations:
- Don’t bother checking for alive within the thread–it will never be false.
- Consider using the static method
Thread.interrupted()
instead of the instance method.
I think my problem is coming from the fact that the directory I’m watching is a mapped drive. I think I read that the WatchService API
is not suited for this and I should use FileAlterationMonitor
instead.
I’ve started to port a Java example over to Jython, but I’m getting tripped up at the @overide
parts as I don’t understand how to register the file events.
What I have so far
from java.io import File
#from org.apache.commons.io import FileDeleteStrategy,FileUtils
from org.apache.commons.io.filefilter import FileFilterUtils
from org.apache.commons.io.monitor import FileAlterationListenerAdaptor
from org.apache.commons.io.monitor import FileAlterationMonitor
from org.apache.commons.io.monitor import FileAlterationObserver
class FileMonitor():
isRunning = False
def __init__(self):
self.monitor = None
self.thread = None
self.run()
def _doAsync(self):
self.isRunning = True
# Directory to monitor for changes
directory = File("L:\Files\CFC MINIPLOTS").toPath()
# Only monitor directory for changes on pdf file extensions
fileFilter = FileFilterUtils.suffixFileFilter("pdf")
# Create a new file alteration observer
observer = FileAlterationObserver(directory, fileFilter)
# Add listner to the observer
observer.addListner(FileAlterationListenerAdaptor())
# Create file alteration monitor at 1 second polling rate
self.monitor = fileAlterationMonitor(1000,observer)
# Start monitoring for file alterations
self.monitor.start()
logger = system.util.getLogger("File Alteration Monitoring")
while self.isRunning and not self.thread.interrupted():
logger.debug('In While Loop')
def run(self): # Create and run the File Monitor in an Aysnc Thread
self.thread = system.util.invokeAsynchronous(self._doAsync)
def stop(self):
self.isRunning = False
self.monitor.stop()
'''
Tutorials Point Java Example Code
usingFileAlterationMonitor() throws IOException {
FileAlterationObserver observer = new
FileAlterationObserver(parentDirectory);
observer.addListener(new FileAlterationListenerAdaptor(){
@Override
public void onDirectoryCreate(File file) {
System.out.println("Folder created: " + file.getName());
}
@Override
public void onDirectoryDelete(File file) {
System.out.println("Folder deleted: " + file.getName());
}
@Override
public void onFileCreate(File file) {
System.out.println("File created: " + file.getName());
}
@Override
public void onFileDelete(File file) {
System.out.println("File deleted: " + file.getName());
}
});
//create a monitor to check changes after every 500 ms
FileAlterationMonitor monitor = new FileAlterationMonitor(500, observer);
try {
monitor.start();
//create a new directory
File newFolder = new File("test");
File newFile = new File("test1");
newFolder.mkdirs();
Thread.sleep(1000);
newFile.createNewFile();
Thread.sleep(1000);
FileDeleteStrategy.NORMAL.delete(newFolder);
Thread.sleep(1000);
FileDeleteStrategy.NORMAL.delete(newFile);
Thread.sleep(1000);
monitor.stop(10000);
} catch(IOException e) {
System.out.println(e.getMessage());
} catch(InterruptedException e) {
System.out.println(e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
To subclass a Java class in Jython, you make a new class that’s a subclass of the Java one, call the Java constructor (if necessary) by defining an __init__
method that takes the same parameters and calls super.__init__
, then override the Java methods by defining a method with the same arity plus the self
argument:
class NetworkListenerAdapter(FileAlterationListenerAdaptor):
def __init__(self):
# do any initialization; superclass has no parameters
pass
def onDirectoryCreate(self, file):
pass
def onDirectoryDelete(self, file):
pass
I made some changes and was on this track, but I didn’t quite have it correct. Thanks for the example, makes more sense now.
from java.io import File
#from org.apache.commons.io import FileDeleteStrategy,FileUtils
from org.apache.commons.io.filefilter import FileFilterUtils
from org.apache.commons.io.monitor import FileAlterationListenerAdaptor
from org.apache.commons.io.monitor import FileAlterationMonitor
from org.apache.commons.io.monitor import FileAlterationObserver
class FileMonitor():
isRunning = False
def __init__(self):
self.monitor = None
self.thread = None
self.run()
def _doAsync(self):
self.isRunning = True
# Directory to monitor for changes
directory = File("L:\Files\CFC MINIPLOTS").toPath()
# Only monitor directory for changes on pdf file extensions
fileFilter = FileFilterUtils.suffixFileFilter(".pdf")
# Create a new file alteration observer
observer = FileAlterationObserver(directory, fileFilter)
# Add listner to the observer with overridden class methods
observer.addListner(NetworkListenerAdapter())
# Create file alteration monitor at 1 second polling rate
self.monitor = fileAlterationMonitor(1000,observer)
# Start monitoring for file alterations
self.monitor.start()
logger = system.util.getLogger("File Alteration Monitoring")
logger.debug('Thread Started')
def run(self): # Create and run the File Monitor in an Aysnc Thread
self.thread = system.util.invokeAsynchronous(self._doAsync)
def stop(self):
self.isRunning = False
self.monitor.stop()
class NetworkListenerAdapter(FileAlterationListenerAdaptor):
def __init__(self):
self.logger = system.util.getLogger("File Alteration Monitoring")
def onFileChange(self, file):
self.logger.debug('File Change Detected')
system.tag.write('Cutting/watchService/changedFile',filePath)
def onFileCreate(self, file):
self.logger.debug('File Creation Detected')
system.tag.write('Cutting/watchService/changedFile',filePath)
def onfileDelete(self, file):
self.logger.debug('File Deletion Detected')
system.tag.write('Cutting/watchService/deletedFile',filePath)
Okay, I’ve debugged some typos in my script. Now, when I start the file monitor, the thread state is Runnable
but I don’t see any file events being logged. Any idea what I’m missing?
from java.io import File
#from org.apache.commons.io import FileDeleteStrategy,FileUtils
from org.apache.commons.io.filefilter import FileFilterUtils
from org.apache.commons.io.monitor import FileAlterationListenerAdaptor
from org.apache.commons.io.monitor import FileAlterationMonitor
from org.apache.commons.io.monitor import FileAlterationObserver
from java.lang import Exception
class FileMonitor():
isRunning = False
def __init__(self):
self.monitor = None
self.thread = None
self.run()
def _doAsync(self):
self.isRunning = True
logger = system.util.getLogger("File Alteration Monitoring")
# Directory to monitor for changes
directory = str(File("C:\Users\dhayes\Desktop\TestFolder").toPath())
# Only monitor directory for changes on pdf file extensions
fileFilter = FileFilterUtils.suffixFileFilter(".pdf")
# Create a new file alteration observer
observer = FileAlterationObserver(directory, fileFilter)
# Add listner to the observer with overridden class methods
observer = observer.addListener(NetworkListenerAdapter())
# Create file alteration monitor at 1 second polling rate
self.monitor = FileAlterationMonitor(1000,observer)
# Start monitoring for file alterations
self.monitor.start()
logger.debug('Thread Started')
print 'Thread Started'
def run(self): # Create and run the File Monitor in an Aysnc Thread
self.thread = system.util.invokeAsynchronous(self._doAsync)
def stop(self):
self.isRunning = False
self.monitor.stop()
class NetworkListenerAdapter(FileAlterationListenerAdaptor):
def __init__(self):
self.logger = system.util.getLogger("File Alteration Monitoring")
def onFileChange(self, file):
self.logger.debug('File Change Detected')
system.tag.write('Cutting/watchService/changedFile',filePath)
def onFileCreate(self, file):
self.logger.debug('File Creation Detected')
system.tag.write('Cutting/watchService/changedFile',filePath)
def onfileDelete(self, file):
self.logger.debug('File Deletion Detected')
system.tag.write('Cutting/watchService/deletedFile',filePath)
Here’s my problem, not sure what this means…
14:30:41.868 [AWT-EventQueue-0] INFO designer.update-and-save - Save finished in 40ms.
<type 'org.apache.commons.io.monitor.FileAlterationObserver'>
14:30:55.773 [AWT-EventQueue-0] ERROR com.inductiveautomation.ignition.client.util.gui.ErrorUtil - <HTML>Error running function from <code>fpmi.system.invokeAsynchronous</code>
com.inductiveautomation.ignition.common.script.JythonExecException: Traceback (most recent call last):
File "<module:shared.fileMonitor>", line 32, in _doAsync
TypeError: org.apache.commons.io.monitor.FileAlterationMonitor(): 2nd arg can't be coerced to org.apache.commons.io.monitor.FileAlterationObserver[]
Looks like you need to supply a list/array of observers.
I just figured it out. I had to do this before passing the observer to the FAM
observer.initialize()
So I have the file monitor working, but in order to keep the thread Runnable
, I had to add this while loop
while self.isRunning and not self.thread.interrupted():
observer.checkAndNotify()
This is not good, as you would put it, “This is a horrible CPU burning script.” It runs at 24% CPU by itself lol. I can’t get the file events to register unless I manually call the checkAndNotify()
method. Is there a way to make the check poll()
instead of loop out of control?
Why do you want it “Runnable”? That explicitly means it will burn CPU. Waiting is the correct state when literally waiting on a notification.
It is Runnable
by default? That is its state when I call it, not sure how to make it Timed_Waiting
. You think it would be that since FileAlterationMonitor
is being feed a 1000 ms poll rate. However, if I don’t have a while loop, the thread goes immediately into a Terminated
state.
Weird thing is the Monitor has two methods run()
and start()
. Maybe I should call run()
then start()
instead of just start()
?
Interesting…
run
public void run()
Runs this monitor.
Specified by:
run in interface Runnable