diff options
author | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-01-21 23:47:54 (GMT) |
---|---|---|
committer | Stefan Radomski <radomski@tk.informatik.tu-darmstadt.de> | 2013-01-21 23:47:54 (GMT) |
commit | 3be96d1aa3024c1acc129e587f5d3165c9434e48 (patch) | |
tree | fae65a932b899ed9424a5a76b9b98562d979fe40 /src/uscxml/plugins/invoker/filesystem | |
parent | 3bda299c6d2efce71d76b44dea8e732a073304f3 (diff) | |
download | uscxml-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')
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_
|