1 /* 2 * Copyright (c) 2012, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* @test 25 * @bug 7164570 7191467 26 * @summary Test that CREATE and DELETE events are paired for very 27 * short lived files 28 * @library .. 29 * @run main MayFlies 30 */ 31 32 import java.nio.file.*; 33 import static java.nio.file.StandardWatchEventKinds.*; 34 import java.util.*; 35 import java.util.concurrent.*; 36 37 public class MayFlies { 38 39 static volatile boolean stopped; 40 41 static volatile boolean failure; 42 43 /** 44 * Continuously creates short-lived files in a directory until {@code 45 * stopped} is set to {@code true}. 46 */ 47 static class MayFlyHatcher implements Runnable { 48 static final Random rand = new Random(); 49 50 private final Path dir; 51 private final String prefix; 52 53 private MayFlyHatcher(Path dir, String prefix) { 54 this.dir = dir; 55 this.prefix = prefix; 56 } 57 58 static void start(Path dir, String prefix) { 59 MayFlyHatcher hatcher = new MayFlyHatcher(dir, prefix); 60 new Thread(hatcher).start(); 61 } 62 63 public void run() { 64 try { 65 int n = 0; 66 while (!stopped) { 67 Path name = dir.resolve(prefix + (++n)); 68 Files.createFile(name); 69 if (rand.nextBoolean()) 70 Thread.sleep(rand.nextInt(500)); 71 Files.delete(name); 72 Thread.sleep(rand.nextInt(100)); 73 } 74 System.out.format("%d %ss hatched%n", n, prefix); 75 } catch (Exception x) { 76 failure = true; 77 x.printStackTrace(); 78 } 79 } 80 } 81 82 /** 83 * Test phases. 84 */ 85 static enum Phase { 86 /** 87 * Short-lived files are being created 88 */ 89 RUNNING, 90 /** 91 * Draining the final events 92 */ 93 FINISHING, 94 /** 95 * No more events or overflow detected 96 */ 97 FINISHED 98 }; 99 100 101 public static void main(String[] args) throws Exception { 102 103 // schedules file creation to stop after 10 seconds 104 ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor(); 105 pool.schedule( 106 new Runnable() { public void run() { stopped = true; }}, 107 10, TimeUnit.SECONDS); 108 109 Path dir = TestUtil.createTemporaryDirectory(); 110 111 Set<Path> entries = new HashSet<>(); 112 int nCreateEvents = 0; 113 boolean overflow = false; 114 115 try (WatchService watcher = FileSystems.getDefault().newWatchService()) { 116 WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE); 117 118 // start hatching Mayflies 119 MayFlyHatcher.start(dir, "clinger"); 120 MayFlyHatcher.start(dir, "crawler"); 121 MayFlyHatcher.start(dir, "burrower"); 122 MayFlyHatcher.start(dir, "swimmer"); 123 124 Phase phase = Phase.RUNNING; 125 while (phase != Phase.FINISHED) { 126 // during the running phase then poll for 1 second. 127 // once the file creation has stopped then move to the finishing 128 // phase where we do a long poll to ensure that all events have 129 // been read. 130 int time = (phase == Phase.RUNNING) ? 1 : 15; 131 key = watcher.poll(time, TimeUnit.SECONDS); 132 if (key == null) { 133 if (phase == Phase.RUNNING && stopped) 134 phase = Phase.FINISHING; 135 else if (phase == Phase.FINISHING) 136 phase = Phase.FINISHED; 137 } else { 138 // process events 139 for (WatchEvent<?> event: key.pollEvents()) { 140 if (event.kind() == ENTRY_CREATE) { 141 Path name = (Path)event.context(); 142 boolean added = entries.add(name); 143 if (!added) 144 throw new RuntimeException("Duplicate ENTRY_CREATE event"); 145 nCreateEvents++; 146 } else if (event.kind() == ENTRY_DELETE) { 147 Path name = (Path)event.context(); 148 boolean removed = entries.remove(name); 149 if (!removed) 150 throw new RuntimeException("ENTRY_DELETE event without ENTRY_CREATE event"); 151 } else if (event.kind() == OVERFLOW) { 152 overflow = true; 153 phase = Phase.FINISHED; 154 } else { 155 throw new RuntimeException("Unexpected event: " + event.kind()); 156 } 157 } 158 key.reset(); 159 } 160 } 161 162 System.out.format("%d ENTRY_CREATE events read%n", nCreateEvents); 163 164 // there should be a DELETE event for each CREATE event and so the 165 // entries set should be empty. 166 if (!overflow && !entries.isEmpty()) 167 throw new RuntimeException("Missed " + entries.size() + " DELETE event(s)"); 168 169 170 } finally { 171 try { 172 TestUtil.removeAll(dir); 173 } finally { 174 pool.shutdown(); 175 } 176 } 177 178 if (failure) 179 throw new RuntimeException("Test failed - see log file for details"); 180 } 181 }