All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.epam.deltix.util.io.FileSystemWatcher Maven / Gradle / Ivy

/*
 * Copyright 2021 EPAM Systems, Inc
 *
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership. Licensed under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.epam.deltix.util.io;

import com.epam.deltix.gflog.api.Log;
import com.epam.deltix.gflog.api.LogFactory;
import com.epam.deltix.util.lang.Util;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 

Allows to scan and watch the file system folders.

* An example of using: *
 *    final FileSystemWatcher.EventHandler h = new FileSystemWatcher.EventHandler() {
 *           {@literal @}Override
 *           public void onEvent(File file, FileSystemWatcher.EventType event) {
 * 
 *               System.out.println(file + " " + event);
 *               
 *               // subscribe again for recursive monitoring
 *               if (file.isDirectory() &&
 *                      (event == FileSystemWatcher.EventType.SCANNED ||
 *                       event == FileSystemWatcher.EventType.CREATED)) {
 *                     try {
 *                         FileSystemWatcher.getInstance().subscribe(this, file, FileSystemWatcher.EventType.SCANNED, FileSystemWatcher.EventType.CREATED, FileSystemWatcher.EventType.MODIFIED, FileSystemWatcher.EventType.DELETED);
 *                     } catch (IOException e) {                            
 *                         e.printStackTrace();
 *                     }
 *               }
 *           }
 *       };
 * 
 *       FileSystemWatcher.getInstance().subscribe(h, new java.io.File("/home/user/projects/qs/main/custom"), FileSystemWatcher.EventType.SCANNED, FileSystemWatcher.EventType.CREATED, FileSystemWatcher.EventType.MODIFIED, FileSystemWatcher.EventType.DELETED);
 *       
 *       // ...do something...
 *       
 *       FileSystemWatcher.getInstance().unsubscribe(h); 
 * 
*/ public class FileSystemWatcher { public enum EventType { /** * A file/folder exists in the target folder by the moment of subscription. */ SCANNED, /** * A new file/folder is created in the target folder. */ CREATED, /** * A file/folder is modified in the target folder. */ MODIFIED, /** * A file/folder is deleted from the target folder. */ DELETED; public boolean isInto(EventType... events) { for (EventType event : events) { if (this == event) { return true; } } return false; } private static WatchEvent.Kind[] toWatchEventKind(EventType... events) { final List result = new ArrayList<>(); for (EventType event : events) { switch (event) { case CREATED: result.add(StandardWatchEventKinds.ENTRY_CREATE); break; case MODIFIED: result.add(StandardWatchEventKinds.ENTRY_MODIFY); break; case DELETED: result.add(StandardWatchEventKinds.ENTRY_DELETE); break; } } return result.toArray(new WatchEvent.Kind[result.size()]); } private static EventType toEventType(WatchEvent.Kind event) { switch (event.name().charAt(6)) { case 'C': //StandardWatchEventKinds.ENTRY_CREATE: return CREATED; case 'M': //StandardWatchEventKinds.ENTRY_MODIFY: return MODIFIED; case 'D': //StandardWatchEventKinds.ENTRY_DELETE: return DELETED; } return null; } } public interface EventHandler { void onEvent(File file, EventType event); } private final static FileSystemWatcher INSTANCE = new FileSystemWatcher(); private final static Log LOGGER = LogFactory.getLog(FileSystemWatcher.class.getName()); public static FileSystemWatcher getInstance() { return INSTANCE; } private final Object lock = new Object(); private WatchingThread watcher; private FileSystemWatcher() { } public synchronized void subscribe(EventHandler handler, File folder, EventType... events) throws IOException { if (!folder.exists()) { throw new IOException(folder + " doesn't exist."); } if (!folder.isDirectory()) { throw new IOException(folder + " isn't a folder."); } if (watcher == null) { watcher = new WatchingThread(); watcher.start(); } watcher.subscribe(handler, folder, events); } public synchronized void unsubscribe(EventHandler handler) throws IOException { if (watcher == null) { return; } watcher.unsubscribe(handler); if (watcher.isEmpty()) { watcher.interrupt(); try { watcher.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } watcher = null; } } public Object getLock() { return lock; } private class WatchingThread extends Thread { private final WatchService watcher; private final Map> pathMap = new HashMap<>(); private final Map keyMap = new HashMap<>(); private WatchingThread() { super(FileSystemWatcher.class.getSimpleName()); setDaemon(true); try { //System.out.println("Preparing WatchingThread"); watcher = FileSystems.getDefault().newWatchService(); } catch (IOException e) { throw new RuntimeException(e); } } private boolean isEmpty() { synchronized (lock) { return pathMap.isEmpty(); } } private void subscribe(final EventHandler handler, final File folder, EventType... events) throws IOException { final Path path = folder.toPath(); final WatchEvent.Kind[] stdEvents = EventType.toWatchEventKind(events); synchronized (lock) { if (stdEvents.length > 0) { final boolean newPath = !pathMap.containsKey(path); final List pathLtns; if (newPath) { pathLtns = new ArrayList<>(); pathMap.put(path, pathLtns); } else { pathLtns = pathMap.get(path); } if (!pathLtns.contains(handler)) { pathLtns.add(handler); } if (newPath) { keyMap.put(path.register(watcher, stdEvents), path); } } if (EventType.SCANNED.isInto(events)) { for (File file : folder.listFiles()) { handler.onEvent(file, EventType.SCANNED); } } } } private void unsubscribe(EventHandler handler) { synchronized (lock) { final List paths = new ArrayList<>(); for (Map.Entry> kv : pathMap.entrySet()) { if (!kv.getValue().contains(handler)) { continue; } for (Map.Entry kv2 : keyMap.entrySet()) { if (!kv2.getValue().toAbsolutePath().equals(kv.getKey())) { continue; } paths.add(kv2.getValue()); } } for (Path path : paths) { final List pathLtns = pathMap.get(path); if (pathLtns == null) { continue; } pathLtns.remove(handler); if (pathLtns.isEmpty()) { for (Map.Entry kv : new HashMap<>(keyMap).entrySet()) { if (!kv.getValue().equals(path)) { continue; } final WatchKey key = kv.getKey(); key.cancel(); keyMap.remove(key); } pathMap.remove(path); } } } } @Override @SuppressWarnings("unchecked") public void run() { while (!isInterrupted()) { final WatchKey key; try { key = watcher.take(); } catch (InterruptedException x) { break; } for (WatchEvent event : key.pollEvents()) { final WatchEvent.Kind kind = event.kind(); // This key is registered only // for ENTRY_CREATE events, // but an OVERFLOW event can // occur regardless if events // are lost or discarded. if (kind == StandardWatchEventKinds.OVERFLOW) { continue; } final EventType eventType = EventType.toEventType(kind); if (eventType == null) { continue; } final WatchEvent ev = (WatchEvent) event; final Path file = ev.context(); synchronized (lock) { final Path path = keyMap.get(key); if (path == null) { continue; } for (EventHandler handler : pathMap.get(path.toAbsolutePath())) { try { handler.onEvent(path.resolve(file).toFile(), eventType); } catch (Throwable t) { LOGGER.warn().append("An error while event processing.").append(t).commit(); } } } } key.reset(); } Util.close(watcher); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy