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
26package sun.nio.fs;
27
28import java.nio.file.*;
29import java.util.*;
30
31/**
32 * Base implementation class for watch keys.
33 */
34
35abstract 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<>();
74        this.lastModifyEvents = new HashMap<>();
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<>((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<>();
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