1 /* 2 * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.fs; 27 28 import java.nio.file.*; 29 import java.util.*; 30 31 /** 32 * Base implementation class for watch keys. 33 */ 34 35 abstract class AbstractWatchKey implements WatchKey { 36 37 /** 38 * Maximum size of event list (in the future this may be tunable) 39 */ 40 static final int MAX_EVENT_LIST_SIZE = 512; 41 42 /** 43 * Special event to signal overflow 44 */ 45 static final Event<Object> OVERFLOW_EVENT = 46 new Event<Object>(StandardWatchEventKinds.OVERFLOW, null); 47 48 /** 49 * Possible key states 50 */ 51 private static enum State { READY, SIGNALLED }; 52 53 // reference to watcher 54 private final AbstractWatchService watcher; 55 56 // reference to the original directory 57 private final Path dir; 58 59 // key state 60 private State state; 61 62 // pending events 63 private List<WatchEvent<?>> events; 64 65 // maps a context to the last event for the context (iff the last queued 66 // event for the context is an ENTRY_MODIFY event). 67 private Map<Object,WatchEvent<?>> lastModifyEvents; 68 AbstractWatchKey(Path dir, AbstractWatchService watcher)69 protected AbstractWatchKey(Path dir, AbstractWatchService watcher) { 70 this.watcher = watcher; 71 this.dir = dir; 72 this.state = State.READY; 73 this.events = new ArrayList<>(); 74 this.lastModifyEvents = new HashMap<>(); 75 } 76 watcher()77 final AbstractWatchService watcher() { 78 return watcher; 79 } 80 81 /** 82 * Return the original watchable (Path) 83 */ 84 @Override watchable()85 public Path watchable() { 86 return dir; 87 } 88 89 /** 90 * Enqueues this key to the watch service 91 */ signal()92 final void signal() { 93 synchronized (this) { 94 if (state == State.READY) { 95 state = State.SIGNALLED; 96 watcher.enqueueKey(this); 97 } 98 } 99 } 100 101 /** 102 * Adds the event to this key and signals it. 103 */ 104 @SuppressWarnings("unchecked") signalEvent(WatchEvent.Kind<?> kind, Object context)105 final void signalEvent(WatchEvent.Kind<?> kind, Object context) { 106 boolean isModify = (kind == StandardWatchEventKinds.ENTRY_MODIFY); 107 synchronized (this) { 108 int size = events.size(); 109 if (size > 0) { 110 // if the previous event is an OVERFLOW event or this is a 111 // repeated event then we simply increment the counter 112 WatchEvent<?> prev = events.get(size-1); 113 if ((prev.kind() == StandardWatchEventKinds.OVERFLOW) || 114 ((kind == prev.kind() && 115 Objects.equals(context, prev.context())))) 116 { 117 ((Event<?>)prev).increment(); 118 return; 119 } 120 121 // if this is a modify event and the last entry for the context 122 // is a modify event then we simply increment the count 123 if (!lastModifyEvents.isEmpty()) { 124 if (isModify) { 125 WatchEvent<?> ev = lastModifyEvents.get(context); 126 if (ev != null) { 127 assert ev.kind() == StandardWatchEventKinds.ENTRY_MODIFY; 128 ((Event<?>)ev).increment(); 129 return; 130 } 131 } else { 132 // not a modify event so remove from the map as the 133 // last event will no longer be a modify event. 134 lastModifyEvents.remove(context); 135 } 136 } 137 138 // if the list has reached the limit then drop pending events 139 // and queue an OVERFLOW event 140 if (size >= MAX_EVENT_LIST_SIZE) { 141 kind = StandardWatchEventKinds.OVERFLOW; 142 isModify = false; 143 context = null; 144 } 145 } 146 147 // non-repeated event 148 Event<Object> ev = 149 new Event<>((WatchEvent.Kind<Object>)kind, context); 150 if (isModify) { 151 lastModifyEvents.put(context, ev); 152 } else if (kind == StandardWatchEventKinds.OVERFLOW) { 153 // drop all pending events 154 events.clear(); 155 lastModifyEvents.clear(); 156 } 157 events.add(ev); 158 signal(); 159 } 160 } 161 162 @Override pollEvents()163 public final List<WatchEvent<?>> pollEvents() { 164 synchronized (this) { 165 List<WatchEvent<?>> result = events; 166 events = new ArrayList<>(); 167 lastModifyEvents.clear(); 168 return result; 169 } 170 } 171 172 @Override reset()173 public final boolean reset() { 174 synchronized (this) { 175 if (state == State.SIGNALLED && isValid()) { 176 if (events.isEmpty()) { 177 state = State.READY; 178 } else { 179 // pending events so re-queue key 180 watcher.enqueueKey(this); 181 } 182 } 183 return isValid(); 184 } 185 } 186 187 /** 188 * WatchEvent implementation 189 */ 190 private static class Event<T> implements WatchEvent<T> { 191 private final WatchEvent.Kind<T> kind; 192 private final T context; 193 194 // synchronize on watch key to access/increment count 195 private int count; 196 Event(WatchEvent.Kind<T> type, T context)197 Event(WatchEvent.Kind<T> type, T context) { 198 this.kind = type; 199 this.context = context; 200 this.count = 1; 201 } 202 203 @Override kind()204 public WatchEvent.Kind<T> kind() { 205 return kind; 206 } 207 208 @Override context()209 public T context() { 210 return context; 211 } 212 213 @Override count()214 public int count() { 215 return count; 216 } 217 218 // for repeated events increment()219 void increment() { 220 count++; 221 } 222 } 223 } 224