From b480922b15876f44e2d2373ea93e3c21d628a6eb Mon Sep 17 00:00:00 2001 From: Tiago Freitas Date: Sat, 28 Jun 2014 13:32:55 +0100 Subject: [PATCH] - Classroom file watcher was not working as expected because lastModifiedTime of a directory is not updated when a file is changed inside it. - Added functions watchDir/unwatchDir which is responsible for watching all files and subdirectories changes - Callback is called once for each detected change - Changed classroom to check for the last "refresh" made to avoid multiple refreshes without changes - Changed refresh time to 3s because it is much more comfortable - I don't think this would be an issue because checking for lastModifiedTime should be very fast, perhaps even 3s is too much time - Tested in Windows --- src/main/js/modules/utils/utils.js | 135 ++++++++++++++++++++- src/main/js/plugins/classroom/classroom.js | 20 +-- 2 files changed, 145 insertions(+), 10 deletions(-) diff --git a/src/main/js/modules/utils/utils.js b/src/main/js/modules/utils/utils.js index a47f387..4308032 100644 --- a/src/main/js/modules/utils/utils.js +++ b/src/main/js/modules/utils/utils.js @@ -517,15 +517,67 @@ utils.watchFile( 'test.txt', function( file ) { ``` ***/ var filesWatched = {}; +var dirsWatched = {}; + exports.watchFile = function( file, callback ) { if ( typeof file == 'string' ) { file = new File(file); } + //console.log("Watching file " + file); filesWatched[file.canonicalPath] = { callback: callback, lastModified: file.lastModified() }; }; + +/************************************************************************ +### utils.watchDir() function + +Watches for changes to the given directory and calls the function provided +when the directory changes. It works by calling watchFile/watchDir for each +file/subdirectory. + +#### Parameters + + * Dir - the file to watch (can be a file or directory) + * Callback - The callback to invoke when the directory has changed. + The callback takes the changed file as a parameter. + For each change inside the directory the callback will also + be called. + +#### Example + +```javascript +var utils = require('utils'); +utils.watchDir( 'players/_ial', function( dir ) { + console.log( dir + ' has changed'); +}); +``` +***/ + +exports.watchDir = function( dir, callback ) { + if ( typeof dir == 'string' ) { + dir = new File(dir); + } + //console.log("Watching dir " + dir); + dirsWatched[dir.canonicalPath] = { + callback: callback, + lastModified: dir.lastModified() + }; + + var files = dir.listFiles(),file; + if ( !files ) { + return; + } + for ( var i = 0; i < files.length; i++ ) { + file = files[i]; + if (file.isDirectory( )) { + exports.watchDir(file,callback); + }else{ + exports.watchFile(file,callback); + } + } +}; /************************************************************************ ### utils.unwatchFile() function @@ -542,21 +594,98 @@ exports.unwatchFile = function( file, callback ) { if ( typeof file == 'string' ) { file = new File(file); } + //console.log("Unwatching file " + file); delete filesWatched[file.canonicalPath]; }; -function fileWatcher() { +/************************************************************************ +### utils.unwatchDir() function + +Removes a directory from the watch list and all files inside the directory +are also "unwatched" + +#### Example +```javascript +var utils = require('utils'); +utils.unwatchDir ('players/_ial'); +``` +Would cause also +```javascript +utils.unwatchFile (file); +``` +for each file inside directory (and unwatchDir for each directory inside it) + +***/ +exports.unwatchDir = function( dir, callback ) { + if ( typeof dir == 'string' ) { + dir = new File(dir); + } + //console.log("Unwatching dir " + dir); + delete dirsWatched[dir.canonicalPath]; + + var files = dir.listFiles(),file; + if ( !files ) { + return; + } + for ( var i = 0; i < files.length; i++ ) { + file = files[i]; + if (file.isDirectory( )) { + exports.unwatchDir(file,callback); + }else{ + exports.unwatchFile(file,callback); + } + } +}; + +function fileWatcher(calledCallbacks) { for (var file in filesWatched) { var fileObject = new File(file); var lm = fileObject.lastModified(); if ( lm != filesWatched[file].lastModified ) { + //console.log("Change found in " + file); filesWatched[file].lastModified = lm; filesWatched[file].callback(fileObject); + if (!fileObject.exists()) { + //console.log("File " + file + " was removed."); + exports.unwatchFile(file,filesWatched[file].callback); + } } } - setTimeout( fileWatcher, 5000 ); }; -setTimeout( fileWatcher, 5000 ); + + +//monitors directories for time change +//when a change is detected watchFiles are invoked for each of the files in directory +//and callback is called +function dirWatcher(calledCallbacks) { + for (var dir in dirsWatched) { + var dirObject = new File(dir); + var lm = dirObject.lastModified(); + var dw = dirsWatched[dir]; + if ( lm != dirsWatched[dir].lastModified ) { + //console.log("Change found in " + dir); + dirsWatched[dir].lastModified = lm; + dirsWatched[dir].callback(dirObject); + + exports.unwatchDir(dir, dw.callback); + //causes all files to be rewatched + if (dirObject.exists()) { + exports.watchDir(dir, dw.callback); + } else { + //console.log("Directory " + dir + " was removed."); + } + } + } +}; + +//guarantees that a callback is only called once for each change +function monitorDirAndFiles() { + fileWatcher (); + dirWatcher (); + setTimeout( monitorDirAndFiles, 3000 ); +}; + +setTimeout( monitorDirAndFiles, 3000 ); /************************************************************************** ### utils.array() function diff --git a/src/main/js/plugins/classroom/classroom.js b/src/main/js/plugins/classroom/classroom.js index fa4edfc..d0ebf2a 100644 --- a/src/main/js/plugins/classroom/classroom.js +++ b/src/main/js/plugins/classroom/classroom.js @@ -2,8 +2,8 @@ var utils = require('utils'), autoload = require('plugin').autoload, logger = __plugin.logger, foreach = utils.foreach, - watchFile = utils.watchFile, - unwatchFile = utils.unwatchFile, + watchDir = utils.watchDir, + unwatchDir = utils.unwatchDir, playersDir = __dirname + '/../../players/', serverAddress = utils.serverAddress(); @@ -104,9 +104,9 @@ function revokeScripting ( player ) { var playerName = '' + player.name; playerName = playerName.replace(/[^a-zA-Z0-9_\-]/g,''); var playerDir = new File( playersDir + playerName ); - unwatchFile( playerDir ); + unwatchDir( playerDir ); } - +exports.classroomAutoloadTime = 0; function grantScripting( player ) { console.log('Enabling scripting for player ' + player.name); var playerName = '' + player.name; @@ -117,9 +117,15 @@ function grantScripting( player ) { var playerContext = {}; autoload( playerContext, playerDir, logger, { cache: false }); global[playerName] = playerContext; - - watchFile( playerDir, function( changedDir ){ - autoload(playerContext, playerDir, logger, { cache: false }); + watchDir( playerDir, function( changedDir ){ + var currentTime = new java.util.Date().getTime(); + //this check is here because this callback might get called multiple times for the watch interval + //one call for the file change and another for directory change + //(this happens only in Linux because in Windows the folder lastModifiedTime is not changed) + if(currentTime-exports.classroomAutoloadTime>1000) { + autoload(playerContext, playerDir, logger, { cache: false }); + } + exports.classroomAutoloadTime = currentTime; }); /*