summaryrefslogtreecommitdiffstats
path: root/src/uscxml/plugins/invoker/filesystem
diff options
context:
space:
mode:
authorStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-01-21 23:47:54 (GMT)
committerStefan Radomski <radomski@tk.informatik.tu-darmstadt.de>2013-01-21 23:47:54 (GMT)
commit3be96d1aa3024c1acc129e587f5d3165c9434e48 (patch)
treefae65a932b899ed9424a5a76b9b98562d979fe40 /src/uscxml/plugins/invoker/filesystem
parent3bda299c6d2efce71d76b44dea8e732a073304f3 (diff)
downloaduscxml-3be96d1aa3024c1acc129e587f5d3165c9434e48.zip
uscxml-3be96d1aa3024c1acc129e587f5d3165c9434e48.tar.gz
uscxml-3be96d1aa3024c1acc129e587f5d3165c9434e48.tar.bz2
See detailed commitlog
- Started DirectoryMonitor invoker - Refactored Invoker / IOProcessor interface - Started with JavaScriptCore bindings - Embedding applications can now use setParentQueue to receive events sent to #_parent
Diffstat (limited to 'src/uscxml/plugins/invoker/filesystem')
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp197
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h58
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcher.cpp75
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcher.h141
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherImpl.h77
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherLinux.cpp165
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherLinux.h89
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp364
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h87
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherWin32.cpp244
-rw-r--r--src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherWin32.h83
11 files changed, 1580 insertions, 0 deletions
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp
new file mode 100644
index 0000000..8489d1d
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.cpp
@@ -0,0 +1,197 @@
+#include "DirMonInvoker.h"
+#include <glog/logging.h>
+
+#ifdef BUILD_AS_PLUGINS
+#include <Pluma/Connector.hpp>
+#endif
+
+#include <sys/stat.h>
+#ifndef WIN32
+#include <dirent.h>
+#else
+#include <strsafe.h>
+#endif
+
+namespace uscxml {
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_CONNECTOR
+bool connect(pluma::Host& host) {
+ host.add( new DirMonInvokerProvider() );
+ return true;
+}
+#endif
+
+DirMonInvoker::DirMonInvoker() : _reportExisting(false), _recurse(false), _thread(NULL) {
+}
+
+DirMonInvoker::~DirMonInvoker() {
+ _isRunning = false;
+ if (_thread)
+ _thread->join();
+};
+
+boost::shared_ptr<IOProcessorImpl> DirMonInvoker::create(Interpreter* interpreter) {
+ boost::shared_ptr<DirMonInvoker> invoker = boost::shared_ptr<DirMonInvoker>(new DirMonInvoker());
+ invoker->_interpreter = interpreter;
+ return invoker;
+}
+
+Data DirMonInvoker::getDataModelVariables() {
+ Data data;
+ return data;
+}
+
+void DirMonInvoker::send(const SendRequest& req) {
+}
+
+void DirMonInvoker::cancel(const std::string sendId) {
+}
+
+void DirMonInvoker::invoke(const InvokeRequest& req) {
+ if (req.params.find("dir") != req.params.end() && boost::iequals(req.params.find("reportexisting")->second, "true"))
+ _reportExisting = true;
+ if (req.params.find("recurse") != req.params.end() && boost::iequals(req.params.find("recurse")->second, "true"))
+ _recurse = true;
+ if (req.params.find("suffix") != req.params.end())
+ _suffix = req.params.find("suffix")->second;
+
+ std::multimap<std::string, std::string>::const_iterator dirIter = req.params.find("dir");
+ while(dirIter != req.params.upper_bound("dir")) {
+ URL url(dirIter->second);
+ if (!_interpreter->toAbsoluteURI(url) || !boost::iequals(url.scheme(), "file")) {
+ LOG(ERROR) << "Given directory '" << dirIter->second << "' cannot be transformed to absolute path";
+ } else {
+ _watchIds.insert(std::make_pair(url.path(), _fileWatcher.addWatch(url.path(), this, _recurse)));
+ }
+ dirIter++;
+ }
+ _isRunning = true;
+ _thread = new tthread::thread(DirMonInvoker::run, this);
+}
+
+void DirMonInvoker::run(void* instance) {
+ if (((DirMonInvoker*)instance)->_reportExisting)
+ ((DirMonInvoker*)instance)->reportExisting();
+
+ while(((DirMonInvoker*)instance)->_isRunning)
+ ((DirMonInvoker*)instance)->_fileWatcher.update();
+}
+
+void DirMonInvoker::reportExisting() {
+ std::multimap<std::string, FW::WatchID>::iterator watchIter = _watchIds.begin();
+ while(watchIter != _watchIds.end()) {
+ reportExistingIn(watchIter->first, watchIter->second);
+ watchIter++;
+ }
+}
+
+void DirMonInvoker::handleFileAction(FW::WatchID watchid, const FW::String& dir, const FW::String& filename, FW::Action action) {
+ if (!boost::algorithm::ends_with(filename, _suffix))
+ return;
+
+ struct stat fileStat;
+ if (stat(filename.c_str(), &fileStat) != 0) {
+ LOG(ERROR) << "Error with stat on directory entry " << filename << ": " << strerror(errno);
+ return;
+ }
+
+ Event event;
+ event.invokeid = _invokeId;
+ switch (action) {
+ case FW::Actions::Existing:
+ event.name = "file.existing";
+ break;
+ case FW::Actions::Add:
+ event.name = "file.added";
+ break;
+ case FW::Actions::Delete:
+ event.name = "file.deleted";
+ break;
+ case FW::Actions::Modified:
+ event.name = "file.modified";
+ break;
+
+ default:
+ break;
+ }
+
+ event.compound["file"].compound["name"] = Data(filename, Data::VERBATIM);
+ event.compound["file"].compound["dir"] = Data(dir, Data::VERBATIM);
+
+ event.compound["file"].compound["mtime"] = toStr(fileStat.st_mtime);
+ event.compound["file"].compound["ctime"] = toStr(fileStat.st_ctime);
+ event.compound["file"].compound["atime"] = toStr(fileStat.st_atime);
+ event.compound["file"].compound["size"] = toStr(fileStat.st_size);
+
+ returnEvent(event);
+}
+
+bool DirMonInvoker::filter(const std::string filename) {
+ return true;
+}
+
+void DirMonInvoker::reportExistingIn(const std::string dir, FW::WatchID watchid) {
+#ifndef WIN32
+ DIR *dp;
+ dp = opendir(dir.c_str());
+ if (dp == NULL) {
+ LOG(ERROR) << "Error opening directory " << dir << ": " << strerror(errno);
+ return;
+ }
+ // iterate all entries and see what changed
+ struct dirent* entry;
+ while((entry = readdir(dp))) {
+ std::string dname = entry->d_name;
+#else
+ WIN32_FIND_DATA ffd;
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+ TCHAR szDir[MAX_PATH];
+ StringCchCopy(szDir, MAX_PATH, dir.c_str());
+ StringCchCat(szDir, MAX_PATH, TEXT("\\*"));
+
+ hFind = FindFirstFile(szDir, &ffd);
+ do {
+ std::string dname = ffd.cFileName;
+#endif
+
+ if (boost::iequals(dname, ".") || boost::iequals(dname, ".."))
+ continue;
+
+ char* filename;
+ asprintf(&filename, "%s/%s", dir.c_str(), dname.c_str());
+
+ struct stat fileStat;
+ if (stat(filename, &fileStat) != 0) {
+ LOG(ERROR) << "Error with stat on directory entry " << filename << ": " << strerror(errno);
+ free(filename);
+ continue;
+ }
+
+ if (fileStat.st_mode & S_IFDIR) {
+ if (_recurse) {
+ reportExistingIn(filename, watchid);
+ } else {
+ free(filename);
+ continue;
+ }
+ }
+
+ if (!filter(dname)) {
+ free(filename);
+ continue;
+ }
+
+ handleFileAction(watchid, dir, filename, FW::Actions::Existing);
+#ifndef WIN32
+ }
+ closedir(dp);
+#else
+ }
+ while (FindNextFile(hFind, &ffd) != 0);
+ FindClose(hFind);
+#endif
+
+}
+
+} \ No newline at end of file
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h
new file mode 100644
index 0000000..1437759
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/DirMonInvoker.h
@@ -0,0 +1,58 @@
+#ifndef DIRMONINVOKER_H_W09J90F0
+#define DIRMONINVOKER_H_W09J90F0
+
+#include <uscxml/Interpreter.h>
+#include "FileWatcher/FileWatcher.h"
+#include <map>
+
+#ifdef BUILD_AS_PLUGINS
+#include "uscxml/plugins/Plugins.h"
+#endif
+
+namespace uscxml {
+
+class DirMonInvoker : public InvokerImpl, public FW::FileWatchListener {
+public:
+ DirMonInvoker();
+ virtual ~DirMonInvoker();
+ virtual boost::shared_ptr<IOProcessorImpl> create(Interpreter* interpreter);
+
+ virtual std::set<std::string> getNames() {
+ std::set<std::string> names;
+ names.insert("dirmon");
+ names.insert("DirectoryMonitor");
+ names.insert("http://uscxml.tk.informatik.tu-darmstadt.de/#dirmon");
+ return names;
+ }
+
+ virtual Data getDataModelVariables();
+ virtual void send(const SendRequest& req);
+ virtual void cancel(const std::string sendId);
+ virtual void invoke(const InvokeRequest& req);
+
+ void handleFileAction(FW::WatchID watchid, const FW::String& dir, const FW::String& filename, FW::Action action);
+ void reportExisting();
+ void reportExistingIn(const std::string dir, FW::WatchID watchid);
+ virtual bool filter(const std::string filename);
+
+ static void run(void* instance);
+
+protected:
+ bool _reportExisting;
+ bool _recurse;
+ std::string _suffix;
+
+ bool _isRunning;
+ tthread::thread* _thread;
+ FW::FileWatcher _fileWatcher;
+ std::multimap<std::string, FW::WatchID> _watchIds;
+};
+
+#ifdef BUILD_AS_PLUGINS
+PLUMA_INHERIT_PROVIDER(DirMonInvoker, Invoker);
+#endif
+
+}
+
+
+#endif /* end of include guard: DIRMONINVOKER_H_W09J90F0 */
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcher.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcher.cpp
new file mode 100644
index 0000000..9399495
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcher.cpp
@@ -0,0 +1,75 @@
+/**
+ Copyright (c) 2009 James Wynn (james@jameswynn.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include <FileWatcher/FileWatcher.h>
+#include <FileWatcher/FileWatcherImpl.h>
+
+#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_WIN32
+# include <FileWatcher/FileWatcherWin32.h>
+# define FILEWATCHER_IMPL FileWatcherWin32
+#elif FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_KQUEUE
+# include <FileWatcher/FileWatcherOSX.h>
+# define FILEWATCHER_IMPL FileWatcherOSX
+#elif FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_LINUX
+# include <FileWatcher/FileWatcherLinux.h>
+# define FILEWATCHER_IMPL FileWatcherLinux
+#endif
+
+namespace FW {
+
+//--------
+FileWatcher::FileWatcher() {
+ mImpl = new FILEWATCHER_IMPL();
+}
+
+//--------
+FileWatcher::~FileWatcher() {
+ delete mImpl;
+ mImpl = 0;
+}
+
+//--------
+WatchID FileWatcher::addWatch(const String& directory, FileWatchListener* watcher) {
+ return mImpl->addWatch(directory, watcher, false);
+}
+
+//--------
+WatchID FileWatcher::addWatch(const String& directory, FileWatchListener* watcher, bool recursive) {
+ return mImpl->addWatch(directory, watcher, recursive);
+}
+
+//--------
+void FileWatcher::removeWatch(const String& directory) {
+ mImpl->removeWatch(directory);
+}
+
+//--------
+void FileWatcher::removeWatch(WatchID watchid) {
+ mImpl->removeWatch(watchid);
+}
+
+//--------
+void FileWatcher::update() {
+ mImpl->update();
+}
+
+};//namespace FW
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcher.h b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcher.h
new file mode 100644
index 0000000..0e659b6
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcher.h
@@ -0,0 +1,141 @@
+/**
+ Main header for the FileWatcher class. Declares all implementation
+ classes to reduce compilation overhead.
+
+ @author James Wynn
+ @date 4/15/2009
+
+ Copyright (c) 2009 James Wynn (james@jameswynn.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef _FW_FILEWATCHER_H_
+#define _FW_FILEWATCHER_H_
+#pragma once
+
+#include <string>
+#include <stdexcept>
+
+namespace FW {
+/// Type for a string
+typedef std::string String;
+/// Type for a watch id
+typedef unsigned long WatchID;
+
+// forward declarations
+class FileWatcherImpl;
+class FileWatchListener;
+
+/// Base exception class
+/// @class Exception
+class Exception : public std::runtime_error {
+public:
+ Exception(const String& message)
+ : std::runtime_error(message)
+ {}
+};
+
+/// Exception thrown when a file is not found.
+/// @class FileNotFoundException
+class FileNotFoundException : public Exception {
+public:
+ FileNotFoundException()
+ : Exception("File not found")
+ {}
+
+ FileNotFoundException(const String& filename)
+ : Exception("File not found (" + filename + ")")
+ {}
+};
+
+/// Actions to listen for. Rename will send two events, one for
+/// the deletion of the old file, and one for the creation of the
+/// new file.
+namespace Actions {
+enum Action {
+ /// Sent when a file is created or renamed
+ Add = 1,
+ /// Sent when a file is deleted or renamed
+ Delete = 2,
+ /// Sent when a file is modified
+ Modified = 4,
+ /// Sent when an existing file is reported
+ Existing = 8
+};
+};
+typedef Actions::Action Action;
+
+/// Listens to files and directories and dispatches events
+/// to notify the parent program of the changes.
+/// @class FileWatcher
+class FileWatcher {
+public:
+ ///
+ ///
+ FileWatcher();
+
+ ///
+ ///
+ virtual ~FileWatcher();
+
+ /// Add a directory watch. Same as the other addWatch, but doesn't have recursive option.
+ /// For backwards compatibility.
+ /// @exception FileNotFoundException Thrown when the requested directory does not exist
+ WatchID addWatch(const String& directory, FileWatchListener* watcher);
+
+ /// Add a directory watch
+ /// @exception FileNotFoundException Thrown when the requested directory does not exist
+ WatchID addWatch(const String& directory, FileWatchListener* watcher, bool recursive);
+
+ /// Remove a directory watch. This is a brute force search O(nlogn).
+ void removeWatch(const String& directory);
+
+ /// Remove a directory watch. This is a map lookup O(logn).
+ void removeWatch(WatchID watchid);
+
+ /// Updates the watcher. Must be called often.
+ void update();
+
+private:
+ /// The implementation
+ FileWatcherImpl* mImpl;
+
+};//end FileWatcher
+
+
+/// Basic interface for listening for file events.
+/// @class FileWatchListener
+class FileWatchListener {
+public:
+ FileWatchListener() {}
+ virtual ~FileWatchListener() {}
+
+ /// Handles the action file action
+ /// @param watchid The watch id for the directory
+ /// @param dir The directory
+ /// @param filename The filename that was accessed (not full path)
+ /// @param action Action that was performed
+ virtual void handleFileAction(WatchID watchid, const String& dir, const String& filename, Action action) = 0;
+
+};//class FileWatchListener
+
+};//namespace FW
+
+#endif//_FW_FILEWATCHER_H_
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherImpl.h b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherImpl.h
new file mode 100644
index 0000000..08cfed1
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherImpl.h
@@ -0,0 +1,77 @@
+/**
+ Basic interface for the FileWatcher backend.
+
+ @author James Wynn
+ @date 5/11/2009
+
+ Copyright (c) 2009 James Wynn (james@jameswynn.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+#ifndef _FW_FILEWATCHERIMPL_H_
+#define _FW_FILEWATCHERIMPL_H_
+#pragma once
+
+#include "FileWatcher.h"
+
+#define FILEWATCHER_PLATFORM_WIN32 1
+#define FILEWATCHER_PLATFORM_LINUX 2
+#define FILEWATCHER_PLATFORM_KQUEUE 3
+
+#if defined(_WIN32)
+# define FILEWATCHER_PLATFORM FILEWATCHER_PLATFORM_WIN32
+#elif defined(__APPLE_CC__) || defined(BSD)
+# define FILEWATCHER_PLATFORM FILEWATCHER_PLATFORM_KQUEUE
+#elif defined(__linux__)
+# define FILEWATCHER_PLATFORM FILEWATCHER_PLATFORM_LINUX
+#endif
+
+namespace FW {
+struct WatchStruct;
+
+class FileWatcherImpl {
+public:
+ ///
+ ///
+ FileWatcherImpl() {}
+
+ ///
+ ///
+ virtual ~FileWatcherImpl() {}
+
+ /// Add a directory watch
+ /// @exception FileNotFoundException Thrown when the requested directory does not exist
+ virtual WatchID addWatch(const String& directory, FileWatchListener* watcher, bool recursive) = 0;
+
+ /// Remove a directory watch. This is a brute force lazy search O(nlogn).
+ virtual void removeWatch(const String& directory) = 0;
+
+ /// Remove a directory watch. This is a map lookup O(logn).
+ virtual void removeWatch(WatchID watchid) = 0;
+
+ /// Updates the watcher. Must be called often.
+ virtual void update() = 0;
+
+ /// Handles the action
+ virtual void handleAction(WatchStruct* watch, const String& filename, unsigned long action) = 0;
+
+};//end FileWatcherImpl
+};//namespace FW
+
+#endif//_FW_FILEWATCHERIMPL_H_
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherLinux.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherLinux.cpp
new file mode 100644
index 0000000..ecbaa32
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherLinux.cpp
@@ -0,0 +1,165 @@
+/**
+ Copyright (c) 2009 James Wynn (james@jameswynn.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ James Wynn james@jameswynn.com
+*/
+
+#include <FileWatcher/FileWatcherLinux.h>
+
+#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_LINUX
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/inotify.h>
+
+#define BUFF_SIZE ((sizeof(struct inotify_event)+FILENAME_MAX)*1024)
+
+namespace FW {
+
+struct WatchStruct {
+ WatchID mWatchID;
+ String mDirName;
+ FileWatchListener* mListener;
+};
+
+//--------
+FileWatcherLinux::FileWatcherLinux() {
+ mFD = inotify_init();
+ if (mFD < 0)
+ fprintf (stderr, "Error: %s\n", strerror(errno));
+
+ mTimeOut.tv_sec = 0;
+ mTimeOut.tv_usec = 0;
+
+ FD_ZERO(&mDescriptorSet);
+}
+
+//--------
+FileWatcherLinux::~FileWatcherLinux() {
+ WatchMap::iterator iter = mWatches.begin();
+ WatchMap::iterator end = mWatches.end();
+ for(; iter != end; ++iter) {
+ delete iter->second;
+ }
+ mWatches.clear();
+}
+
+//--------
+WatchID FileWatcherLinux::addWatch(const String& directory, FileWatchListener* watcher, bool recursive) {
+ int wd = inotify_add_watch (mFD, directory.c_str(),
+ IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE);
+ if (wd < 0) {
+ if(errno == ENOENT)
+ throw FileNotFoundException(directory);
+ else
+ throw Exception(strerror(errno));
+
+// fprintf (stderr, "Error: %s\n", strerror(errno));
+// return -1;
+ }
+
+ WatchStruct* pWatch = new WatchStruct();
+ pWatch->mListener = watcher;
+ pWatch->mWatchID = wd;
+ pWatch->mDirName = directory;
+
+ mWatches.insert(std::make_pair(wd, pWatch));
+
+ return wd;
+}
+
+//--------
+void FileWatcherLinux::removeWatch(const String& directory) {
+ WatchMap::iterator iter = mWatches.begin();
+ WatchMap::iterator end = mWatches.end();
+ for(; iter != end; ++iter) {
+ if(directory == iter->second->mDirName) {
+ removeWatch(iter->first);
+ return;
+ }
+ }
+}
+
+//--------
+void FileWatcherLinux::removeWatch(WatchID watchid) {
+ WatchMap::iterator iter = mWatches.find(watchid);
+
+ if(iter == mWatches.end())
+ return;
+
+ WatchStruct* watch = iter->second;
+ mWatches.erase(iter);
+
+ inotify_rm_watch(mFD, watchid);
+
+ delete watch;
+ watch = 0;
+}
+
+//--------
+void FileWatcherLinux::update() {
+ FD_SET(mFD, &mDescriptorSet);
+
+ int ret = select(mFD + 1, &mDescriptorSet, NULL, NULL, &mTimeOut);
+ if(ret < 0) {
+ perror("select");
+ } else if(FD_ISSET(mFD, &mDescriptorSet)) {
+ ssize_t len, i = 0;
+ char action[81+FILENAME_MAX] = {0};
+ char buff[BUFF_SIZE] = {0};
+
+ len = read (mFD, buff, BUFF_SIZE);
+
+ while (i < len) {
+ struct inotify_event *pevent = (struct inotify_event *)&buff[i];
+
+ WatchStruct* watch = mWatches[pevent->wd];
+ handleAction(watch, pevent->name, pevent->mask);
+ i += sizeof(struct inotify_event) + pevent->len;
+ }
+ }
+}
+
+//--------
+void FileWatcherLinux::handleAction(WatchStruct* watch, const String& filename, unsigned long action) {
+ if(!watch->mListener)
+ return;
+
+ if(IN_CLOSE_WRITE & action) {
+ watch->mListener->handleFileAction(watch->mWatchID, watch->mDirName, filename,
+ Actions::Modified);
+ }
+ if(IN_MOVED_TO & action || IN_CREATE & action) {
+ watch->mListener->handleFileAction(watch->mWatchID, watch->mDirName, filename,
+ Actions::Add);
+ }
+ if(IN_MOVED_FROM & action || IN_DELETE & action) {
+ watch->mListener->handleFileAction(watch->mWatchID, watch->mDirName, filename,
+ Actions::Delete);
+ }
+}
+
+};//namespace FW
+
+#endif//FILEWATCHER_PLATFORM_LINUX
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherLinux.h b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherLinux.h
new file mode 100644
index 0000000..b681e52
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherLinux.h
@@ -0,0 +1,89 @@
+/**
+ Implementation header file for Linux based on inotify.
+
+ @author James Wynn
+ @date 4/15/2009
+
+ Copyright (c) 2009 James Wynn (james@jameswynn.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+#ifndef _FW_FILEWATCHERLINUX_H_
+#define _FW_FILEWATCHERLINUX_H_
+#pragma once
+
+#include "FileWatcherImpl.h"
+
+#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_LINUX
+
+#include <map>
+#include <sys/types.h>
+
+namespace FW {
+/// Implementation for Linux based on inotify.
+/// @class FileWatcherLinux
+class FileWatcherLinux : public FileWatcherImpl {
+public:
+ /// type for a map from WatchID to WatchStruct pointer
+ typedef std::map<WatchID, WatchStruct*> WatchMap;
+
+public:
+ ///
+ ///
+ FileWatcherLinux();
+
+ ///
+ ///
+ virtual ~FileWatcherLinux();
+
+ /// Add a directory watch
+ /// @exception FileNotFoundException Thrown when the requested directory does not exist
+ WatchID addWatch(const String& directory, FileWatchListener* watcher, bool recursive);
+
+ /// Remove a directory watch. This is a brute force lazy search O(nlogn).
+ void removeWatch(const String& directory);
+
+ /// Remove a directory watch. This is a map lookup O(logn).
+ void removeWatch(WatchID watchid);
+
+ /// Updates the watcher. Must be called often.
+ void update();
+
+ /// Handles the action
+ void handleAction(WatchStruct* watch, const String& filename, unsigned long action);
+
+private:
+ /// Map of WatchID to WatchStruct pointers
+ WatchMap mWatches;
+ /// The last watchid
+ WatchID mLastWatchID;
+ /// inotify file descriptor
+ int mFD;
+ /// time out data
+ struct timeval mTimeOut;
+ /// File descriptor set
+ fd_set mDescriptorSet;
+
+};//end FileWatcherLinux
+
+};//namespace FW
+
+#endif//FILEWATCHER_PLATFORM_LINUX
+
+#endif//_FW_FILEWATCHERLINUX_H_
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp
new file mode 100644
index 0000000..e1634b0
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.cpp
@@ -0,0 +1,364 @@
+/**
+ Copyright (c) 2009 James Wynn (james@jameswynn.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ James Wynn james@jameswynn.com
+*/
+
+#include <FileWatcher/FileWatcherOSX.h>
+
+#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_KQUEUE
+
+#include <sys/event.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+
+// this is more suited:
+// https://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/FSEvents_ProgGuide/UsingtheFSEventsFramework/UsingtheFSEventsFramework.html
+
+namespace FW {
+
+#define MAX_CHANGE_EVENT_SIZE 2000
+
+typedef struct kevent KEvent;
+
+struct EntryStruct {
+ EntryStruct(const char* filename, time_t mtime = 0)
+ : mFilename(filename), mModifiedTime(mtime) {
+ }
+ ~EntryStruct() {
+ delete[] mFilename;
+ }
+ const char* mFilename;
+ time_t mModifiedTime;
+};
+
+int comparator(const void* ke1, const void* ke2) {
+ /*KEvent* kevent1 = (KEvent*) ke1;
+ KEvent* kevent2 = (KEvent*) ke2;
+
+ EntryStruct* event1 = (EntryStruct*)kevent1->udata;
+ EntryStruct* event2 = (EntryStruct*)kevent2->udata;
+ return strcmp(event1->mFilename, event2->mFilename);
+ */
+ return strcmp(((EntryStruct*)(((KEvent*)(ke1))->udata))->mFilename, ((EntryStruct*)(((KEvent*)(ke2))->udata))->mFilename);
+}
+
+struct WatchStruct {
+ WatchID mWatchID;
+ String mDirName;
+ FileWatchListener* mListener;
+
+ // index 0 is always the directory
+ KEvent mChangeList[MAX_CHANGE_EVENT_SIZE];
+ size_t mChangeListCount;
+
+ WatchStruct(WatchID watchid, const String& dirname, FileWatchListener* listener)
+ : mWatchID(watchid), mDirName(dirname), mListener(listener) {
+ mChangeListCount = 0;
+ addAll();
+ }
+
+ void addFile(const String& name, bool imitEvents = true) {
+ //fprintf(stderr, "ADDED: %s\n", name.c_str());
+
+ // create entry
+ struct stat attrib;
+ stat(name.c_str(), &attrib);
+
+ int fd = open(name.c_str(), O_RDONLY);
+
+ if(fd == -1)
+ throw FileNotFoundException(name);
+
+ ++mChangeListCount;
+
+ char* namecopy = new char[name.length() + 1];
+ strncpy(namecopy, name.c_str(), name.length());
+ namecopy[name.length()] = 0;
+ EntryStruct* entry = new EntryStruct(namecopy, attrib.st_mtime);
+
+ // set the event data at the end of the list
+ EV_SET(&mChangeList[mChangeListCount], fd, EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_ONESHOT,
+ NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
+ 0, (void*)entry);
+
+ // qsort
+ qsort(mChangeList + 1, mChangeListCount, sizeof(KEvent), comparator);
+
+ // handle action
+ if(imitEvents)
+ handleAction(name, Actions::Add);
+ }
+
+ void removeFile(const String& name, bool imitEvents = true) {
+ // bsearch
+ KEvent target;
+ EntryStruct tempEntry(name.c_str(), 0);
+ target.udata = &tempEntry;
+ KEvent* ke = (KEvent*)bsearch(&target, &mChangeList, mChangeListCount + 1, sizeof(KEvent), comparator);
+ if(!ke)
+ throw FileNotFoundException(name);
+
+ tempEntry.mFilename = 0;
+
+ // delete
+ close(ke->ident);
+ delete((EntryStruct*)ke->udata);
+ memset(ke, 0, sizeof(KEvent));
+
+ // move end to current
+ memcpy(ke, &mChangeList[mChangeListCount], sizeof(KEvent));
+ memset(&mChangeList[mChangeListCount], 0, sizeof(KEvent));
+ --mChangeListCount;
+
+ // qsort
+ qsort(mChangeList + 1, mChangeListCount, sizeof(KEvent), comparator);
+
+ // handle action
+ if(imitEvents)
+ handleAction(name, Actions::Delete);
+ }
+
+ // called when the directory is actually changed
+ // means a file has been added or removed
+ // rescans the watched directory adding/removing files and sending notices
+ void rescan() {
+ // if new file, call addFile
+ // if missing file, call removeFile
+ // if timestamp modified, call handleAction(filename, ACTION_MODIFIED);
+ DIR* dir = opendir(mDirName.c_str());
+ if(!dir)
+ return;
+
+ struct dirent* dentry;
+ KEvent* ke = &mChangeList[1];
+ EntryStruct* entry = 0;
+ struct stat attrib;
+
+ while((dentry = readdir(dir)) != NULL) {
+ String fname = mDirName + "/" + dentry->d_name;
+ stat(fname.c_str(), &attrib);
+ if(!S_ISREG(attrib.st_mode))
+ continue;
+
+ if(ke <= &mChangeList[mChangeListCount]) {
+ entry = (EntryStruct*)ke->udata;
+ int result = strcmp(entry->mFilename, fname.c_str());
+ //fprintf(stderr, "[%s cmp %s]\n", entry->mFilename, fname.c_str());
+ if(result == 0) {
+ stat(entry->mFilename, &attrib);
+ time_t timestamp = attrib.st_mtime;
+
+ if(entry->mModifiedTime != timestamp) {
+ entry->mModifiedTime = timestamp;
+ handleAction(entry->mFilename, Actions::Modified);
+ }
+ ke++;
+ } else if(result < 0) {
+ // f1 was deleted
+ removeFile(entry->mFilename);
+ ke++;
+ } else {
+ // f2 was created
+ addFile(fname);
+ ke++;
+ }
+ } else {
+ // just add
+ addFile(fname);
+ ke++;
+ }
+ }//end while
+
+ closedir(dir);
+ };
+
+ void handleAction(const String& filename, FW::Action action) {
+ mListener->handleFileAction(mWatchID, mDirName, filename, action);
+ }
+
+ void addAll() {
+ // add base dir
+ int fd = open(mDirName.c_str(), O_RDONLY);
+ EV_SET(&mChangeList[0], fd, EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_ONESHOT,
+ NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
+ 0, 0);
+
+ //fprintf(stderr, "ADDED: %s\n", mDirName.c_str());
+
+ // scan directory and call addFile(name, false) on each file
+ DIR* dir = opendir(mDirName.c_str());
+ if(!dir)
+ throw FileNotFoundException(mDirName);
+
+ struct dirent* entry;
+ struct stat attrib;
+ while((entry = readdir(dir)) != NULL) {
+ String fname = (mDirName + "/" + String(entry->d_name));
+ stat(fname.c_str(), &attrib);
+ if(S_ISREG(attrib.st_mode))
+ addFile(fname, false);
+ //else
+ // fprintf(stderr, "NOT ADDED: %s (%d)\n", fname.c_str(), attrib.st_mode);
+
+ }//end while
+
+ closedir(dir);
+ }
+
+ void removeAll() {
+ KEvent* ke = NULL;
+
+ // go through list removing each file and sending an event
+ for(int i = 0; i < mChangeListCount; ++i) {
+ ke = &mChangeList[i];
+ //handleAction(name, Action::Delete);
+ EntryStruct* entry = (EntryStruct*)ke->udata;
+
+ handleAction(entry->mFilename, Actions::Delete);
+
+ // delete
+ close(ke->ident);
+ delete((EntryStruct*)ke->udata);
+ }
+ }
+};
+
+void FileWatcherOSX::update() {
+ int nev = 0;
+ struct kevent event;
+
+ WatchMap::iterator iter = mWatches.begin();
+ WatchMap::iterator end = mWatches.end();
+ for(; iter != end; ++iter) {
+ WatchStruct* watch = iter->second;
+
+ while((nev = kevent(mDescriptor, (KEvent*)&(watch->mChangeList), watch->mChangeListCount + 1, &event, 1, &mTimeOut)) != 0) {
+ if(nev == -1)
+ perror("kevent");
+ else {
+ EntryStruct* entry = 0;
+ if((entry = (EntryStruct*)event.udata) != 0) {
+ //fprintf(stderr, "File: %s -- ", (char*)entry->mFilename);
+
+ if(event.fflags & NOTE_DELETE) {
+ //fprintf(stderr, "File deleted\n");
+ //watch->handleAction(entry->mFilename, Action::Delete);
+ watch->removeFile(entry->mFilename);
+ }
+ if(event.fflags & NOTE_EXTEND ||
+ event.fflags & NOTE_WRITE ||
+ event.fflags & NOTE_ATTRIB) {
+ //fprintf(stderr, "modified\n");
+ //watch->rescan();
+ struct stat attrib;
+ stat(entry->mFilename, &attrib);
+ entry->mModifiedTime = attrib.st_mtime;
+ watch->handleAction(entry->mFilename, FW::Actions::Modified);
+ }
+ } else {
+ //fprintf(stderr, "Dir: %s -- rescanning\n", watch->mDirName.c_str());
+ watch->rescan();
+ }
+ }
+ }
+ }
+}
+
+//--------
+FileWatcherOSX::FileWatcherOSX() {
+ mDescriptor = kqueue();
+ mTimeOut.tv_sec = 0;
+ mTimeOut.tv_nsec = 2000000;
+}
+
+//--------
+FileWatcherOSX::~FileWatcherOSX() {
+ WatchMap::iterator iter = mWatches.begin();
+ WatchMap::iterator end = mWatches.end();
+ for(; iter != end; ++iter) {
+ delete iter->second;
+ }
+ mWatches.clear();
+
+ close(mDescriptor);
+}
+
+//--------
+WatchID FileWatcherOSX::addWatch(const String& directory, FileWatchListener* watcher, bool recursive) {
+ /* int fd = open(directory.c_str(), O_RDONLY);
+ if(fd == -1)
+ perror("open");
+
+ EV_SET(&change, fd, EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_ONESHOT,
+ NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB,
+ 0, (void*)"testing");
+ */
+
+ WatchStruct* watch = new WatchStruct(++mLastWatchID, directory, watcher);
+ mWatches.insert(std::make_pair(mLastWatchID, watch));
+ return mLastWatchID;
+}
+
+//--------
+void FileWatcherOSX::removeWatch(const String& directory) {
+ WatchMap::iterator iter = mWatches.begin();
+ WatchMap::iterator end = mWatches.end();
+ for(; iter != end; ++iter) {
+ if(directory == iter->second->mDirName) {
+ removeWatch(iter->first);
+ return;
+ }
+ }
+}
+
+//--------
+void FileWatcherOSX::removeWatch(WatchID watchid) {
+ WatchMap::iterator iter = mWatches.find(watchid);
+
+ if(iter == mWatches.end())
+ return;
+
+ WatchStruct* watch = iter->second;
+ mWatches.erase(iter);
+
+ //inotify_rm_watch(mFD, watchid);
+
+ delete watch;
+ watch = 0;
+}
+
+//--------
+void FileWatcherOSX::handleAction(WatchStruct* watch, const String& filename, unsigned long action) {
+}
+
+};//namespace FW
+
+#endif//FILEWATCHER_PLATFORM_KQUEUE
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h
new file mode 100644
index 0000000..39f411c
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherOSX.h
@@ -0,0 +1,87 @@
+/**
+ Implementation header file for OSX based on KEvent.
+
+ @author James Wynn
+ @date 4/15/2009
+
+ Copyright (c) 2009 James Wynn (james@jameswynn.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+#ifndef _FW_FILEWATCHEROSX_H_
+#define _FW_FILEWATCHEROSX_H_
+#pragma once
+
+#include "FileWatcherImpl.h"
+
+#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_KQUEUE
+
+#include <map>
+#include <sys/types.h>
+
+namespace FW {
+/// Implementation for OSX based on kqueue.
+/// @class FileWatcherOSX
+class FileWatcherOSX : public FileWatcherImpl {
+public:
+ /// type for a map from WatchID to WatchStruct pointer
+ typedef std::map<WatchID, WatchStruct*> WatchMap;
+
+public:
+ ///
+ ///
+ FileWatcherOSX();
+
+ ///
+ ///
+ virtual ~FileWatcherOSX();
+
+ /// Add a directory watch
+ /// @exception FileNotFoundException Thrown when the requested directory does not exist
+ WatchID addWatch(const String& directory, FileWatchListener* watcher, bool recursive);
+
+ /// Remove a directory watch. This is a brute force lazy search O(nlogn).
+ void removeWatch(const String& directory);
+
+ /// Remove a directory watch. This is a map lookup O(logn).
+ void removeWatch(WatchID watchid);
+
+ /// Updates the watcher. Must be called often.
+ void update();
+
+ /// Handles the action
+ void handleAction(WatchStruct* watch, const String& filename, unsigned long action);
+
+private:
+ /// Map of WatchID to WatchStruct pointers
+ WatchMap mWatches;
+ /// The descriptor for the kqueue
+ int mDescriptor;
+ /// time out data
+ struct timespec mTimeOut;
+ /// WatchID allocator
+ int mLastWatchID;
+
+};//end FileWatcherOSX
+
+};//namespace FW
+
+#endif//__APPLE_CC__
+
+#endif//_FW_FILEWATCHEROSX_H_
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherWin32.cpp b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherWin32.cpp
new file mode 100644
index 0000000..ee5de7e
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherWin32.cpp
@@ -0,0 +1,244 @@
+/**
+ Copyright (c) 2009 James Wynn (james@jameswynn.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include <FileWatcher/FileWatcherWin32.h>
+
+#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_WIN32
+
+#define _WIN32_WINNT 0x0550
+#include <windows.h>
+
+#if defined(_MSC_VER)
+#pragma comment(lib, "comctl32.lib")
+#pragma comment(lib, "user32.lib")
+#pragma comment(lib, "ole32.lib")
+
+// disable secure warnings
+#pragma warning (disable: 4996)
+#endif
+
+namespace FW {
+/// Internal watch data
+struct WatchStruct {
+ OVERLAPPED mOverlapped;
+ HANDLE mDirHandle;
+ BYTE mBuffer[32 * 1024];
+ LPARAM lParam;
+ DWORD mNotifyFilter;
+ bool mStopNow;
+ FileWatcherImpl* mFileWatcher;
+ FileWatchListener* mFileWatchListener;
+ char* mDirName;
+ WatchID mWatchid;
+ bool mIsRecursive;
+};
+
+#pragma region Internal Functions
+
+// forward decl
+bool RefreshWatch(WatchStruct* pWatch, bool _clear = false);
+
+/// Unpacks events and passes them to a user defined callback.
+void CALLBACK WatchCallback(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) {
+ TCHAR szFile[MAX_PATH];
+ PFILE_NOTIFY_INFORMATION pNotify;
+ WatchStruct* pWatch = (WatchStruct*) lpOverlapped;
+ size_t offset = 0;
+
+ if(dwNumberOfBytesTransfered == 0)
+ return;
+
+ if (dwErrorCode == ERROR_SUCCESS) {
+ do {
+ pNotify = (PFILE_NOTIFY_INFORMATION) &pWatch->mBuffer[offset];
+ offset += pNotify->NextEntryOffset;
+
+# if defined(UNICODE)
+ {
+ lstrcpynW(szFile, pNotify->FileName,
+ min(MAX_PATH, pNotify->FileNameLength / sizeof(WCHAR) + 1));
+ }
+# else
+ {
+ int count = WideCharToMultiByte(CP_ACP, 0, pNotify->FileName,
+ pNotify->FileNameLength / sizeof(WCHAR),
+ szFile, MAX_PATH - 1, NULL, NULL);
+ szFile[count] = TEXT('\0');
+ }
+# endif
+
+ pWatch->mFileWatcher->handleAction(pWatch, szFile, pNotify->Action);
+
+ } while (pNotify->NextEntryOffset != 0);
+ }
+
+ if (!pWatch->mStopNow) {
+ RefreshWatch(pWatch);
+ }
+}
+
+/// Refreshes the directory monitoring.
+bool RefreshWatch(WatchStruct* pWatch, bool _clear) {
+ return ReadDirectoryChangesW(
+ pWatch->mDirHandle, pWatch->mBuffer, sizeof(pWatch->mBuffer), pWatch->mIsRecursive,
+ pWatch->mNotifyFilter, NULL, &pWatch->mOverlapped, _clear ? 0 : WatchCallback) != 0;
+}
+
+/// Stops monitoring a directory.
+void DestroyWatch(WatchStruct* pWatch) {
+ if (pWatch) {
+ pWatch->mStopNow = TRUE;
+
+ CancelIo(pWatch->mDirHandle);
+
+ RefreshWatch(pWatch, true);
+
+ if (!HasOverlappedIoCompleted(&pWatch->mOverlapped)) {
+ SleepEx(5, TRUE);
+ }
+
+ CloseHandle(pWatch->mOverlapped.hEvent);
+ CloseHandle(pWatch->mDirHandle);
+ delete pWatch->mDirName;
+ HeapFree(GetProcessHeap(), 0, pWatch);
+ }
+}
+
+/// Starts monitoring a directory.
+WatchStruct* CreateWatch(LPCTSTR szDirectory, bool recursive, DWORD mNotifyFilter) {
+ WatchStruct* pWatch;
+ size_t ptrsize = sizeof(*pWatch);
+ pWatch = static_cast<WatchStruct*>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptrsize));
+
+ pWatch->mDirHandle = CreateFile(szDirectory, FILE_LIST_DIRECTORY,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
+
+ if (pWatch->mDirHandle != INVALID_HANDLE_VALUE) {
+ pWatch->mOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ pWatch->mNotifyFilter = mNotifyFilter;
+ pWatch->mIsRecursive = recursive;
+
+ if (RefreshWatch(pWatch)) {
+ return pWatch;
+ } else {
+ CloseHandle(pWatch->mOverlapped.hEvent);
+ CloseHandle(pWatch->mDirHandle);
+ }
+ }
+
+ HeapFree(GetProcessHeap(), 0, pWatch);
+ return NULL;
+}
+
+#pragma endregion
+
+//--------
+FileWatcherWin32::FileWatcherWin32()
+ : mLastWatchID(0) {
+}
+
+//--------
+FileWatcherWin32::~FileWatcherWin32() {
+ WatchMap::iterator iter = mWatches.begin();
+ WatchMap::iterator end = mWatches.end();
+ for(; iter != end; ++iter) {
+ DestroyWatch(iter->second);
+ }
+ mWatches.clear();
+}
+
+//--------
+WatchID FileWatcherWin32::addWatch(const String& directory, FileWatchListener* watcher, bool recursive) {
+ WatchID watchid = ++mLastWatchID;
+
+ WatchStruct* watch = CreateWatch(directory.c_str(), recursive,
+ FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_FILE_NAME);
+
+ if(!watch)
+ throw FileNotFoundException(directory);
+
+ watch->mWatchid = watchid;
+ watch->mFileWatcher = this;
+ watch->mFileWatchListener = watcher;
+ watch->mDirName = new char[directory.length()+1];
+ strcpy(watch->mDirName, directory.c_str());
+
+ mWatches.insert(std::make_pair(watchid, watch));
+
+ return watchid;
+}
+
+//--------
+void FileWatcherWin32::removeWatch(const String& directory) {
+ WatchMap::iterator iter = mWatches.begin();
+ WatchMap::iterator end = mWatches.end();
+ for(; iter != end; ++iter) {
+ if(directory == iter->second->mDirName) {
+ removeWatch(iter->first);
+ return;
+ }
+ }
+}
+
+//--------
+void FileWatcherWin32::removeWatch(WatchID watchid) {
+ WatchMap::iterator iter = mWatches.find(watchid);
+
+ if(iter == mWatches.end())
+ return;
+
+ WatchStruct* watch = iter->second;
+ mWatches.erase(iter);
+
+ DestroyWatch(watch);
+}
+
+//--------
+void FileWatcherWin32::update() {
+ MsgWaitForMultipleObjectsEx(0, NULL, 0, QS_ALLINPUT, MWMO_ALERTABLE);
+}
+
+//--------
+void FileWatcherWin32::handleAction(WatchStruct* watch, const String& filename, unsigned long action) {
+ Action fwAction;
+
+ switch(action) {
+ case FILE_ACTION_RENAMED_NEW_NAME:
+ case FILE_ACTION_ADDED:
+ fwAction = Actions::Add;
+ break;
+ case FILE_ACTION_RENAMED_OLD_NAME:
+ case FILE_ACTION_REMOVED:
+ fwAction = Actions::Delete;
+ break;
+ case FILE_ACTION_MODIFIED:
+ fwAction = Actions::Modified;
+ break;
+ };
+
+ watch->mFileWatchListener->handleFileAction(watch->mWatchid, watch->mDirName, filename, fwAction);
+}
+
+};//namespace FW
+
+#endif//_WIN32
diff --git a/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherWin32.h b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherWin32.h
new file mode 100644
index 0000000..d1626b0
--- /dev/null
+++ b/src/uscxml/plugins/invoker/filesystem/dirmon/FileWatcher/FileWatcherWin32.h
@@ -0,0 +1,83 @@
+/**
+ Implementation for Windows. Uses ReadDirectoryChangesW to watch for
+ file system changes.
+
+ @author James Wynn
+ @date 4/15/2009
+
+ Copyright (c) 2009 James Wynn (james@jameswynn.com)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+#ifndef _FW_FILEWATCHERWIN32_H_
+#define _FW_FILEWATCHERWIN32_H_
+#pragma once
+
+#include "FileWatcherImpl.h"
+
+#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_WIN32
+
+#include <map>
+
+namespace FW {
+/// Implementation for Win32 based on ReadDirectoryChangesW.
+/// @class FileWatcherWin32
+class FileWatcherWin32 : public FileWatcherImpl {
+public:
+ /// type for a map from WatchID to WatchStruct pointer
+ typedef std::map<WatchID, WatchStruct*> WatchMap;
+
+public:
+ ///
+ ///
+ FileWatcherWin32();
+
+ ///
+ ///
+ virtual ~FileWatcherWin32();
+
+ /// Add a directory watch
+ /// @exception FileNotFoundException Thrown when the requested directory does not exist
+ WatchID addWatch(const String& directory, FileWatchListener* watcher, bool recursive);
+
+ /// Remove a directory watch. This is a brute force lazy search O(nlogn).
+ void removeWatch(const String& directory);
+
+ /// Remove a directory watch. This is a map lookup O(logn).
+ void removeWatch(WatchID watchid);
+
+ /// Updates the watcher. Must be called often.
+ void update();
+
+ /// Handles the action
+ void handleAction(WatchStruct* watch, const String& filename, unsigned long action);
+
+private:
+ /// Map of WatchID to WatchStruct pointers
+ WatchMap mWatches;
+ /// The last watchid
+ WatchID mLastWatchID;
+
+};//end FileWatcherWin32
+
+};//namespace FW
+
+#endif//FILEWATCHER_PLATFORM_WIN32
+
+#endif//_FW_FILEWATCHERWIN32_H_