Home | History | Annotate | Download | only in fs
      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 
     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<WatchEvent<?>>();
     74         this.lastModifyEvents = new HashMap<Object,WatchEvent<?>>();
     75     }
     76 
     77     final AbstractWatchService watcher() {
     78         return watcher;
     79     }
     80 
     81     /**
     82      * Return the original watchable (Path)
     83      */
     84     @Override
     85     public Path watchable() {
     86         return dir;
     87     }
     88 
     89     /**
     90      * Enqueues this key to the watch service
     91      */
     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")
    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<Object>((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
    163     public final List<WatchEvent<?>> pollEvents() {
    164         synchronized (this) {
    165             List<WatchEvent<?>> result = events;
    166             events = new ArrayList<WatchEvent<?>>();
    167             lastModifyEvents.clear();
    168             return result;
    169         }
    170     }
    171 
    172     @Override
    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 
    197         Event(WatchEvent.Kind<T> type, T context) {
    198             this.kind = type;
    199             this.context = context;
    200             this.count = 1;
    201         }
    202 
    203         @Override
    204         public WatchEvent.Kind<T> kind() {
    205             return kind;
    206         }
    207 
    208         @Override
    209         public T context() {
    210             return context;
    211         }
    212 
    213         @Override
    214         public int count() {
    215             return count;
    216         }
    217 
    218         // for repeated events
    219         void increment() {
    220             count++;
    221         }
    222     }
    223 }
    224