1e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera/* 2e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. 3e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * 5e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * This code is free software; you can redistribute it and/or modify it 6e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * under the terms of the GNU General Public License version 2 only, as 7e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * published by the Free Software Foundation. Oracle designates this 8e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * particular file as subject to the "Classpath" exception as provided 9e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * by Oracle in the LICENSE file that accompanied this code. 10e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * 11e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * This code is distributed in the hope that it will be useful, but WITHOUT 12e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * version 2 for more details (a copy is included in the LICENSE file that 15e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * accompanied this code). 16e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * 17e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * You should have received a copy of the GNU General Public License version 18e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * 2 along with this work; if not, write to the Free Software Foundation, 19e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * 21e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * or visit www.oracle.com if you need additional information or have any 23e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * questions. 24e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera */ 25e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 26e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmerapackage sun.nio.fs; 27e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 28e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraimport java.nio.file.*; 29e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraimport java.nio.file.attribute.*; 30e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraimport java.security.AccessController; 31e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraimport java.security.PrivilegedAction; 32e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraimport java.security.PrivilegedExceptionAction; 33e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraimport java.security.PrivilegedActionException; 34e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraimport java.io.IOException; 35e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraimport java.util.*; 36e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraimport java.util.concurrent.*; 37e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraimport com.sun.nio.file.SensitivityWatchEventModifier; 38e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 39e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera/** 40e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * Simple WatchService implementation that uses periodic tasks to poll 41e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * registered directories for changes. This implementation is for use on 42e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * operating systems that do not have native file change notification support. 43e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera */ 44e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 45e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmeraclass PollingWatchService 46e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera extends AbstractWatchService 47e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera{ 48e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // map of registrations 49e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private final Map<Object,PollingWatchKey> map = 50e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera new HashMap<Object,PollingWatchKey>(); 51e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 52e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // used to execute the periodic tasks that poll for changes 53e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private final ScheduledExecutorService scheduledExecutor; 54e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 55e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera PollingWatchService() { 56e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // TBD: Make the number of threads configurable 57e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera scheduledExecutor = Executors 58e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera .newSingleThreadScheduledExecutor(new ThreadFactory() { 59e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera @Override 60e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera public Thread newThread(Runnable r) { 61e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Thread t = new Thread(r); 62e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera t.setDaemon(true); 63e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return t; 64e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera }}); 65e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 66e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 67e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera /** 68e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * Register the given file with this watch service 69e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera */ 70e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera @Override 71e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera WatchKey register(final Path path, 72e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera WatchEvent.Kind<?>[] events, 73e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera WatchEvent.Modifier... modifiers) 74e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throws IOException 75e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera { 76e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // check events - CCE will be thrown if there are invalid elements 77e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera final Set<WatchEvent.Kind<?>> eventSet = 78e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera new HashSet<WatchEvent.Kind<?>>(events.length); 79e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera for (WatchEvent.Kind<?> event: events) { 80e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // standard events 81e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (event == StandardWatchEventKinds.ENTRY_CREATE || 82e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera event == StandardWatchEventKinds.ENTRY_MODIFY || 83e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera event == StandardWatchEventKinds.ENTRY_DELETE) 84e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera { 85e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera eventSet.add(event); 86e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera continue; 87e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 88e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 89e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // OVERFLOW is ignored 90e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (event == StandardWatchEventKinds.OVERFLOW) { 91e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera continue; 92e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 93e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 94e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // null/unsupported 95e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (event == null) 96e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw new NullPointerException("An element in event set is 'null'"); 97e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw new UnsupportedOperationException(event.name()); 98e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 99e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (eventSet.isEmpty()) 100e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw new IllegalArgumentException("No events to register"); 101e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 102e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // A modifier may be used to specify the sensitivity level 103e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera SensitivityWatchEventModifier sensivity = SensitivityWatchEventModifier.MEDIUM; 104e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (modifiers.length > 0) { 105e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera for (WatchEvent.Modifier modifier: modifiers) { 106e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (modifier == null) 107e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw new NullPointerException(); 108e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (modifier instanceof SensitivityWatchEventModifier) { 109e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera sensivity = (SensitivityWatchEventModifier)modifier; 110e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera continue; 111e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 112e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw new UnsupportedOperationException("Modifier not supported"); 113e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 114e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 115e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 116e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // check if watch service is closed 117e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (!isOpen()) 118e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw new ClosedWatchServiceException(); 119e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 120e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // registration is done in privileged block as it requires the 121e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // attributes of the entries in the directory. 122e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera try { 123e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera final SensitivityWatchEventModifier s = sensivity; 124e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return AccessController.doPrivileged( 125e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera new PrivilegedExceptionAction<PollingWatchKey>() { 126e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera @Override 127e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera public PollingWatchKey run() throws IOException { 128e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return doPrivilegedRegister(path, eventSet, s); 129e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 130e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera }); 131e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } catch (PrivilegedActionException pae) { 132e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Throwable cause = pae.getCause(); 133e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (cause != null && cause instanceof IOException) 134e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw (IOException)cause; 135e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw new AssertionError(pae); 136e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 137e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 138e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 139e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // registers directory returning a new key if not already registered or 140e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // existing key if already registered 141e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private PollingWatchKey doPrivilegedRegister(Path path, 142e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Set<? extends WatchEvent.Kind<?>> events, 143e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera SensitivityWatchEventModifier sensivity) 144e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throws IOException 145e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera { 146e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // check file is a directory and get its file key if possible 147e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class); 148e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (!attrs.isDirectory()) { 149e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw new NotDirectoryException(path.toString()); 150e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 151e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Object fileKey = attrs.fileKey(); 152e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (fileKey == null) 153e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw new AssertionError("File keys must be supported"); 154e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 155e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // grab close lock to ensure that watch service cannot be closed 156e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera synchronized (closeLock()) { 157e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (!isOpen()) 158e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw new ClosedWatchServiceException(); 159e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 160e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera PollingWatchKey watchKey; 161e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera synchronized (map) { 162e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera watchKey = map.get(fileKey); 163e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (watchKey == null) { 164e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // new registration 165e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera watchKey = new PollingWatchKey(path, this, fileKey); 166e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera map.put(fileKey, watchKey); 167e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } else { 168e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // update to existing registration 169e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera watchKey.disable(); 170e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 171e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 172e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera watchKey.enable(events, sensivity.sensitivityValueInSeconds()); 173e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return watchKey; 174e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 175e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 176e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 177e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 178e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera @Override 179e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera void implClose() throws IOException { 180e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera synchronized (map) { 181e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera for (Map.Entry<Object,PollingWatchKey> entry: map.entrySet()) { 182e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera PollingWatchKey watchKey = entry.getValue(); 183e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera watchKey.disable(); 184e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera watchKey.invalidate(); 185e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 186e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera map.clear(); 187e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 188e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera AccessController.doPrivileged(new PrivilegedAction<Void>() { 189e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera @Override 190e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera public Void run() { 191e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera scheduledExecutor.shutdown(); 192e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return null; 193e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 194e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera }); 195e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 196e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 197e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera /** 198e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * Entry in directory cache to record file last-modified-time and tick-count 199e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera */ 200e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private static class CacheEntry { 201e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private long lastModified; 202e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private int lastTickCount; 203e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 204e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera CacheEntry(long lastModified, int lastTickCount) { 205e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera this.lastModified = lastModified; 206e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera this.lastTickCount = lastTickCount; 207e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 208e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 209e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera int lastTickCount() { 210e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return lastTickCount; 211e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 212e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 213e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera long lastModified() { 214e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return lastModified; 215e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 216e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 217e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera void update(long lastModified, int tickCount) { 218e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera this.lastModified = lastModified; 219e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera this.lastTickCount = tickCount; 220e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 221e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 222e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 223e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera /** 224e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * WatchKey implementation that encapsulates a map of the entries of the 225e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * entries in the directory. Polling the key causes it to re-scan the 226e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * directory and queue keys when entries are added, modified, or deleted. 227e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera */ 228e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private class PollingWatchKey extends AbstractWatchKey { 229e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private final Object fileKey; 230e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 231e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // current event set 232e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private Set<? extends WatchEvent.Kind<?>> events; 233e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 234e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // the result of the periodic task that causes this key to be polled 235e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private ScheduledFuture<?> poller; 236e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 237e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // indicates if the key is valid 238e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private volatile boolean valid; 239e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 240e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // used to detect files that have been deleted 241e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private int tickCount; 242e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 243e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // map of entries in directory 244e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera private Map<Path,CacheEntry> entries; 245e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 246e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera PollingWatchKey(Path dir, PollingWatchService watcher, Object fileKey) 247e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throws IOException 248e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera { 249e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera super(dir, watcher); 250e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera this.fileKey = fileKey; 251e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera this.valid = true; 252e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera this.tickCount = 0; 253e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera this.entries = new HashMap<Path,CacheEntry>(); 254e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 255e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // get the initial entries in the directory 256e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) { 257e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera for (Path entry: stream) { 258e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // don't follow links 259e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera long lastModified = 260e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Files.getLastModifiedTime(entry, LinkOption.NOFOLLOW_LINKS).toMillis(); 261e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera entries.put(entry.getFileName(), new CacheEntry(lastModified, tickCount)); 262e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 263e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } catch (DirectoryIteratorException e) { 264e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera throw e.getCause(); 265e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 266e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 267e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 268e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Object fileKey() { 269e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return fileKey; 270e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 271e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 272e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera @Override 273e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera public boolean isValid() { 274e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return valid; 275e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 276e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 277e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera void invalidate() { 278e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera valid = false; 279e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 280e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 281e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // enables periodic polling 282e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera void enable(Set<? extends WatchEvent.Kind<?>> events, long period) { 283e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera synchronized (this) { 284e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // update the events 285e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera this.events = events; 286e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 287e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // create the periodic task 288e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Runnable thunk = new Runnable() { public void run() { poll(); }}; 289e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera this.poller = scheduledExecutor 290e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera .scheduleAtFixedRate(thunk, period, period, TimeUnit.SECONDS); 291e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 292e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 293e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 294e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // disables periodic polling 295e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera void disable() { 296e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera synchronized (this) { 297e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (poller != null) 298e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera poller.cancel(false); 299e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 300e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 301e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 302e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera @Override 303e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera public void cancel() { 304e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera valid = false; 305e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera synchronized (map) { 306e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera map.remove(fileKey()); 307e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 308e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera disable(); 309e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 310e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 311e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera /** 312e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * Polls the directory to detect for new files, modified files, or 313e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera * deleted files. 314e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera */ 315e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera synchronized void poll() { 316e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (!valid) { 317e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return; 318e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 319e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 320e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // update tick 321e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera tickCount++; 322e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 323e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // open directory 324e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera DirectoryStream<Path> stream = null; 325e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera try { 326e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera stream = Files.newDirectoryStream(watchable()); 327e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } catch (IOException x) { 328e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // directory is no longer accessible so cancel key 329e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera cancel(); 330e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera signal(); 331e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera return; 332e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 333e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 334e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // iterate over all entries in directory 335e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera try { 336e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera for (Path entry: stream) { 337e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera long lastModified = 0L; 338e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera try { 339e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera lastModified = 340e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Files.getLastModifiedTime(entry, LinkOption.NOFOLLOW_LINKS).toMillis(); 341e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } catch (IOException x) { 342e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // unable to get attributes of entry. If file has just 343e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // been deleted then we'll report it as deleted on the 344e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // next poll 345e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera continue; 346e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 347e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 348e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // lookup cache 349e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera CacheEntry e = entries.get(entry.getFileName()); 350e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (e == null) { 351e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // new file found 352e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera entries.put(entry.getFileName(), 353e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera new CacheEntry(lastModified, tickCount)); 354e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 355e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // queue ENTRY_CREATE if event enabled 356e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (events.contains(StandardWatchEventKinds.ENTRY_CREATE)) { 357e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera signalEvent(StandardWatchEventKinds.ENTRY_CREATE, entry.getFileName()); 358e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera continue; 359e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } else { 360e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // if ENTRY_CREATE is not enabled and ENTRY_MODIFY is 361e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // enabled then queue event to avoid missing out on 362e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // modifications to the file immediately after it is 363e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // created. 364e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) { 365e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, entry.getFileName()); 366e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 367e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 368e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera continue; 369e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 370e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 371e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // check if file has changed 372e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (e.lastModified != lastModified) { 373e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) { 374e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, 375e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera entry.getFileName()); 376e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 377e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 378e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // entry in cache so update poll time 379e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera e.update(lastModified, tickCount); 380e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 381e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 382e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } catch (DirectoryIteratorException e) { 383e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // ignore for now; if the directory is no longer accessible 384e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // then the key will be cancelled on the next poll 385e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } finally { 386e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 387e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // close directory stream 388e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera try { 389e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera stream.close(); 390e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } catch (IOException x) { 391e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // ignore 392e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 393e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 394e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera 395e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // iterate over cache to detect entries that have been deleted 396e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Iterator<Map.Entry<Path,CacheEntry>> i = entries.entrySet().iterator(); 397e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera while (i.hasNext()) { 398e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Map.Entry<Path,CacheEntry> mapEntry = i.next(); 399e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera CacheEntry entry = mapEntry.getValue(); 400e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (entry.lastTickCount() != tickCount) { 401e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera Path name = mapEntry.getKey(); 402e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera // remove from map and queue delete event (if enabled) 403e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera i.remove(); 404e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera if (events.contains(StandardWatchEventKinds.ENTRY_DELETE)) { 405e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera signalEvent(StandardWatchEventKinds.ENTRY_DELETE, name); 406e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 407e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 408e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 409e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 410e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera } 411e6bac4bf9c85c2454ce22c91da6c654552c268e0Shubham Ajmera} 412