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 * @key randomness 31 */ 32 33 import java.nio.file.*; 34 import static java.nio.file.StandardWatchEventKinds.*; 35 import java.util.*; 36 import java.util.concurrent.*; 37 38 public class MayFlies { 39 40 static volatile boolean stopped; 41 42 static volatile boolean failure; 43 44 /** 45 * Continuously creates short-lived files in a directory until {@code 46 * stopped} is set to {@code true}. 47 */ 48 static class MayFlyHatcher implements Runnable { 49 static final Random rand = new Random(); 50 51 private final Path dir; 52 private final String prefix; 53 54 private MayFlyHatcher(Path dir, String prefix) { 55 this.dir = dir; 56 this.prefix = prefix; 57 } 58 59 static void start(Path dir, String prefix) { 60 MayFlyHatcher hatcher = new MayFlyHatcher(dir, prefix); 61 new Thread(hatcher).start(); 62 } 63 64 public void run() { 65 try { 66 int n = 0; 67 while (!stopped) { 68 Path name = dir.resolve(prefix + (++n)); 69 Files.createFile(name); 70 if (rand.nextBoolean()) 71 Thread.sleep(rand.nextInt(500)); 72 Files.delete(name); 73 Thread.sleep(rand.nextInt(100)); 74 } 75 System.out.format("%d %ss hatched%n", n, prefix); 76 } catch (Exception x) { 77 failure = true; 78 x.printStackTrace(); 79 } 80 } 81 } 82 83 /** 84 * Test phases. 85 */ 86 static enum Phase { 87 /** 88 * Short-lived files are being created 89 */ 90 RUNNING, 91 /** 92 * Draining the final events 93 */ 94 FINISHING, 95 /** 96 * No more events or overflow detected 97 */ 98 FINISHED 99 }; 100 101 102 public static void main(String[] args) throws Exception { 103 104 // schedules file creation to stop after 10 seconds 105 ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor(); 106 pool.schedule( 107 new Runnable() { public void run() { stopped = true; }}, 108 10, TimeUnit.SECONDS); 109 110 Path dir = TestUtil.createTemporaryDirectory(); 111 112 Set<Path> entries = new HashSet<>(); 113 int nCreateEvents = 0; 114 boolean overflow = false; 115 116 try (WatchService watcher = FileSystems.getDefault().newWatchService()) { 117 WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE); 118 119 // start hatching Mayflies 120 MayFlyHatcher.start(dir, "clinger"); 121 MayFlyHatcher.start(dir, "crawler"); 122 MayFlyHatcher.start(dir, "burrower"); 123 MayFlyHatcher.start(dir, "swimmer"); 124 125 Phase phase = Phase.RUNNING; 126 while (phase != Phase.FINISHED) { 127 // during the running phase then poll for 1 second. 128 // once the file creation has stopped then move to the finishing 129 // phase where we do a long poll to ensure that all events have 130 // been read. 131 int time = (phase == Phase.RUNNING) ? 1 : 15; 132 key = watcher.poll(time, TimeUnit.SECONDS); 133 if (key == null) { 134 if (phase == Phase.RUNNING && stopped) 135 phase = Phase.FINISHING; 136 else if (phase == Phase.FINISHING) 137 phase = Phase.FINISHED; 138 } else { 139 // process events 140 for (WatchEvent<?> event: key.pollEvents()) { 141 if (event.kind() == ENTRY_CREATE) { 142 Path name = (Path)event.context(); 143 boolean added = entries.add(name); 144 if (!added) 145 throw new RuntimeException("Duplicate ENTRY_CREATE event"); 146 nCreateEvents++; 147 } else if (event.kind() == ENTRY_DELETE) { 148 Path name = (Path)event.context(); 149 boolean removed = entries.remove(name); 150 if (!removed) 151 throw new RuntimeException("ENTRY_DELETE event without ENTRY_CREATE event"); 152 } else if (event.kind() == OVERFLOW) { 153 overflow = true; 154 phase = Phase.FINISHED; 155 } else { 156 throw new RuntimeException("Unexpected event: " + event.kind()); 157 } 158 } 159 key.reset(); 160 } 161 } 162 163 System.out.format("%d ENTRY_CREATE events read%n", nCreateEvents); 164 165 // there should be a DELETE event for each CREATE event and so the 166 // entries set should be empty. 167 if (!overflow && !entries.isEmpty()) 168 throw new RuntimeException("Missed " + entries.size() + " DELETE event(s)"); 169 170 171 } finally { 172 try { 173 TestUtil.removeAll(dir); 174 } finally { 175 pool.shutdown(); 176 } 177 } 178 179 if (failure) 180 throw new RuntimeException("Test failed - see log file for details"); 181 } 182 }