| package org.eclipse.osbp.xtext.signal.common; |
| |
| import static java.nio.file.LinkOption.NOFOLLOW_LINKS; |
| import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; |
| import static java.nio.file.StandardWatchEventKinds.OVERFLOW; |
| |
| import java.io.IOException; |
| import java.nio.file.FileSystems; |
| import java.nio.file.FileVisitResult; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.nio.file.SimpleFileVisitor; |
| import java.nio.file.StandardWatchEventKinds; |
| import java.nio.file.WatchEvent; |
| import java.nio.file.WatchEvent.Kind; |
| import java.nio.file.WatchKey; |
| import java.nio.file.WatchService; |
| import java.nio.file.attribute.BasicFileAttributes; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.osbp.xtext.signal.SignalActionEnum; |
| import org.eclipse.osbp.xtext.signal.SignalTypeEnum; |
| |
| public class OSBPSignalWatcher { |
| |
| /** the watcher service */ |
| private WatchService watcher; |
| /** list of all created keys and paths */ |
| private Map<WatchKey, Path> keys = null; |
| /** determines if the all tree of a directory has to be watched for changes*/ |
| private boolean recursive = false; |
| |
| // private static final long TIMEOUT = 10L; |
| // private static final TimeUnit TIMEUNIT = TimeUnit.SECONDS; |
| // for data-import |
| public static final String IMPORT_SUFFIX = "_imp"; |
| // normal print to local |
| public static final String PRINT_SUFFIX = "_prt"; |
| // pdf print in target directory |
| public static final String PDF_SUFFIX = "_pdf"; |
| // send mail |
| public static final String MAIL_SUFFIX = "_sd"; |
| // create notification |
| public static final String MESSAGE_SUFFIX = "_shw"; |
| |
| |
| public OSBPSignalWatcher() throws IOException { |
| this.watcher = FileSystems.getDefault().newWatchService(); |
| this.keys = new HashMap<WatchKey, Path>(); |
| } |
| |
| /** |
| * Process all events for keys queued to the watcher |
| */ |
| public void processEvents() { |
| for (;;) { |
| // wait for key to be signaled |
| WatchKey key; |
| try { |
| key = watcher.take(); |
| } catch (InterruptedException x) { |
| return; |
| } |
| |
| Path dir = keys.get(key); |
| if (dir == null) { |
| System.err.println("WatchKey not recognized!!"); |
| continue; |
| } |
| |
| for (WatchEvent<?> event : key.pollEvents()) { |
| Kind<?> kind = event.kind(); |
| |
| // handle the OVERFLOW event |
| if (kind == OVERFLOW) { |
| continue; |
| } |
| |
| // Context for directory entry event is the file name of entry |
| WatchEvent<Path> ev = cast(event); |
| Path name = ev.context(); |
| Path child = dir.resolve(name); |
| System.out.format("%s: %s\n", event.kind().name(), child); |
| |
| // depending on what the user decide to do before ending |
| // before reseting the event |
| handleEvent(ev); |
| |
| // if directory is created, and watching recursively, then |
| // register it and its sub-directories |
| if (recursive && (kind == ENTRY_CREATE)) { |
| try { |
| if (Files.isDirectory(child, NOFOLLOW_LINKS)) { |
| // TODO OSBP: this needs to be adjust here |
| // for now a single key for all kind of changes will |
| // be created |
| registerAll(child, null); |
| } |
| } catch (IOException x) { |
| // ignore to keep sample readbale |
| } |
| } |
| |
| } |
| |
| // reset key and remove from set if directory no longer accessible |
| boolean valid = key.reset(); |
| if (!valid) { |
| keys.remove(key); |
| |
| // all directories are inaccessible |
| if (keys.isEmpty()) { |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Handling method for the user to extend |
| * |
| * @param event |
| * the {@link WatchEvent} |
| */ |
| public void handleEvent(WatchEvent<Path> event) { |
| // put your logic here |
| } |
| |
| @SuppressWarnings("unchecked") |
| static <T> WatchEvent<T> cast(WatchEvent<?> event) { |
| return (WatchEvent<T>) event; |
| } |
| |
| /** |
| * Register the given directory, and all its sub-directories, with the |
| * WatchService. |
| * |
| * @param rootpath |
| * {@link Path} the root path. |
| * @param signals |
| * @throws IOException |
| */ |
| public void registerAll(Path rootpath, String signals) throws IOException { |
| // register directory and sub-directories |
| Files.walkFileTree(rootpath, new SimpleFileVisitor<Path>() { |
| @Override |
| public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { |
| registerPathToWatcher(dir, signals); |
| return FileVisitResult.CONTINUE; |
| } |
| }); |
| } |
| |
| /** |
| * Register the given directory with the WatchService |
| * |
| * @param directory |
| * @param signals |
| * @param combined |
| * true to create only one {@link WatchKey} for all event types, |
| * false to create separate key for each event type. |
| * @throws IOException |
| */ |
| public void registerPathToWatcher(Path directory, String signals) throws IOException { |
| //TODO enter the register logic here |
| // registerPathToWatcher(directory, signals); |
| // watcher = FileSystems.getDefault().newWatchService(); |
| // this.keys = new HashMap<WatchKey, Path>(); |
| // if (signals != null && !signals.isEmpty()) { |
| // boolean iscreate = |
| // signals.contains(StandardWatchEventKinds.ENTRY_CREATE.toString()); |
| // boolean isdelete = |
| // signals.contains(StandardWatchEventKinds.ENTRY_DELETE.toString()); |
| // boolean ismodify = |
| // signals.contains(StandardWatchEventKinds.ENTRY_MODIFY.toString()); |
| // // create one key for all kind of changes |
| // // and save the key for later use |
| // // TODO only applicable with xtext generation |
| // // keys.put(directory.register(watcher, iscreate ? |
| // StandardWatchEventKinds.ENTRY_CREATE : |
| // // null, |
| // // isdelete ? StandardWatchEventKinds.ENTRY_DELETE : null, ismodify ? |
| // StandardWatchEventKinds.ENTRY_MODIFY : null), |
| // // directory); |
| // keys.put(directory.register(watcher, |
| // StandardWatchEventKinds.ENTRY_CREATE, |
| // StandardWatchEventKinds.ENTRY_DELETE, |
| // StandardWatchEventKinds.ENTRY_MODIFY), directory); |
| // } else { |
| // // if none signals have been given by the user, |
| // // create a default key for all kind of changes |
| // // and save the key for later use |
| // keys.put(directory.register(watcher, |
| // StandardWatchEventKinds.ENTRY_CREATE, |
| // StandardWatchEventKinds.ENTRY_DELETE, |
| // StandardWatchEventKinds.ENTRY_MODIFY), directory); |
| // } |
| } |
| |
| /** |
| * Checks if the given {@link WatchKey} is valid. |
| * |
| * @return true if yes, false if not |
| */ |
| public boolean isKeyValid(WatchKey key) { |
| return key != null ? key.isValid() : false; |
| } |
| |
| /** |
| * |
| * @return |
| */ |
| public int getWatcherState() { |
| return watcher != null ? 1 : -1; |
| } |
| |
| /** |
| * Closes the watcher service |
| * |
| * @return true if yes or non existing, false if not |
| */ |
| public boolean closeWatcher() { |
| try { |
| if (watcher != null) { |
| watcher.close(); |
| } |
| return true; |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| return false; |
| } |
| |
| /** |
| * Get the list of registered directories. |
| * |
| * @return {@link Collection<Path>} {@link #directories} |
| */ |
| public Collection<Path> getRegisteredDirectories() { |
| return keys.values(); |
| } |
| |
| /** |
| * Gets the list of registered keys. |
| * |
| * @return {@link Set<WatchKey>} |
| */ |
| public Set<WatchKey> getRegisteredKeys() { |
| return keys.keySet(); |
| } |
| |
| /** |
| * Gets the list of registered keys and their paths. |
| * |
| * @return {@link Map<WatchKey,Path>} |
| */ |
| public Map<WatchKey, Path> getRegisteredKeysAndPaths() { |
| return keys; |
| } |
| |
| /** |
| * Gets the watcher. |
| * |
| * @return {@link WatchService} |
| */ |
| public WatchService getWatcher() { |
| return watcher; |
| } |
| |
| /** |
| * Register the path and trigger the processing event operation to poll key |
| * event. |
| * |
| * @throws IOException |
| */ |
| public void startWatcher(String path, SignalTypeEnum signal) throws IOException { |
| registerPathToWatcher(Paths.get(path), getAppropriateKey(signal)); |
| processEvents(); |
| } |
| |
| /** |
| * Convenience operation gives the apporpriate key set for |
| * |
| * @param type |
| * @return |
| */ |
| public String getAppropriateKey(SignalTypeEnum type) { |
| switch (type) { |
| case CREATESIGNALS: |
| return StandardWatchEventKinds.ENTRY_CREATE.toString(); |
| case DELETESIGNALS: |
| return StandardWatchEventKinds.ENTRY_DELETE.toString(); |
| case MODIFYSIGNALS: |
| return StandardWatchEventKinds.ENTRY_MODIFY.toString(); |
| case ALL: |
| return StandardWatchEventKinds.ENTRY_CREATE.toString() + StandardWatchEventKinds.ENTRY_DELETE.toString() |
| + StandardWatchEventKinds.ENTRY_MODIFY.toString(); |
| default: |
| return StandardWatchEventKinds.OVERFLOW.toString(); |
| } |
| } |
| |
| /** |
| * Provides an action based on the filename. |
| * |
| * @param filename |
| * filename |
| * @return |
| */ |
| public SignalActionEnum checkFileMask(String filename) { |
| if (filename != null && !filename.isEmpty()) { |
| if (filename.toLowerCase().contains(IMPORT_SUFFIX)) { |
| // persist data into a db |
| return SignalActionEnum.DATAIMPORT; |
| } else if (filename.toLowerCase().contains(PRINT_SUFFIX)) { |
| // print a report from a printer |
| return SignalActionEnum.PRINT; |
| } else if (filename.toLowerCase().contains(PDF_SUFFIX)) { |
| // create a pdf or report of a bpm task |
| return SignalActionEnum.PDFPRINT; |
| } else if (filename.toLowerCase().contains(MAIL_SUFFIX)) { |
| // send a mail |
| return SignalActionEnum.MAIL; |
| } else if (filename.toLowerCase().contains(MESSAGE_SUFFIX)) { |
| // show notification |
| return SignalActionEnum.MESSAGEPROMPT; |
| } |
| } |
| return SignalActionEnum.NONE; // do nothing |
| } |
| |
| } |