public abstract class WatchEvent<T> {
	/**
	 * Instances of this class act as tags to identify different kinds of
	 * events (like file creation or deletion)
	 * @param <T> The context this kind is intended for. In jpathwatch, only
	 * Path and Void are used as context types.
	 */
    public static interface Kind<T>
    {
		/**
		 * @return the name of this modifier
		 */
		String name();
		/**
		 * @return the type of the WatchEvent's context value.
		 */
		Class<T> type();
    }

	/**
	 * A modifier can be specified to {@link Watchable#register register} to
	 * change the way changes to a watchable are reported. jpathwatch
	 * defines a set of modifiers in the {@link ExtendedWatchEventModifier}
	 * class.
	 * @param <T> The context type for the modifier.
	 * @see ExtendedWatchEventModifier
	 */
    public static interface Modifier<T>
    {
		/**
		 * The modifier's name should be used for informative purposes
		 * only (like error reporting).
		 * @return	the modifier's name
		 */
		String name();
    }

    protected WatchEvent()
    {}

	/**
	 * @return the context of this event, which is usually a reference to the
	 * object that has changed. In the case of WatchEvents for Path, the
	 * context will be a <code>Path</code> to the file that this event
	 * refers to, relative to the watched <code>Path</code>
	 */
    public abstract T context();
	/**
	 * The number of times this event occurred, if it is cumulative. It
	 * is not specified how events cumulate, so use this value for informational
	 * purposes only.
	 * @return the number of times this event has occurred, in case events of
	 *	this kind have been aggregated into one WatchEvent instance.
	 */
    public abstract int count();

	/**
	 * @return the kind of event that occurred. This will indicate what
	 * actually happened, for instance, StandardWatchEventKind#ENTRY_CREATE
	 * indicates that a file has been created.
	 */
    public abstract WatchEvent.Kind<T> kind();
} public abstract class WatchKey {
protected 	WatchKey()
{}
/**
 * This cancels the registration with the WatchService that this WatchKey
 * was registered with. This means that no new events will be delivered to this
 * key any more. Events that are pending can still be retreived with
 * pollEvents(), and if the WatchKey is still marked as signalled a call to
 * WatchService's poll() or take() functions will still return it.
 */
public abstract  void 	cancel();

/**
 * @return if this watch key is valid. A watch key is valid if
 * <ul>
 * <li>It has not been canceled</li>
 * <li>The WatchService is not yet closed</li>
 * </ul>
 */
public abstract  boolean 	isValid();

/**
 * Returns the events that have occurred for this WatchKey. Just calling
 * this method will not reset the signalled state of this key; you'll have
 * to call #reset() to indicate to the WatchService that the the client is
 * ready to receive more events and that the key can be re-queued.
 * After the WatchService has determined that events have occurred for a
 * registered Watchable represented by a given WatchKey, it will return that
 * key when the client calls it's WatchService#take() or WatchService#poll()
 * methods.
 * @return	a list of events that have occurred since the last time that
 *	#pollEvents() was called.
 */
public abstract  List<WatchEvent<?>> 	pollEvents();

/**
 * Resets this {@link WatchKey} (marks it as non-signalled) so that it's
 * corresponding {@link WatchService} can report it again via it's
 * {@link WatchService#poll} and {@link WatchService#take} methods.
 *
 * @return <code>true</code> if the key could be reset, <code>false</code>
 * otherwise (typically if the corresponding {@link WatchService} has been closed
 * or if the the key was not signalled).
 */
public abstract  boolean 	reset();
} public abstract class WatchService implements Closeable {
protected 	WatchService()
{}
public abstract void close()throws IOException;
public abstract WatchKey poll() throws InterruptedException, ClosedWatchServiceException;
public abstract WatchKey poll(long timeout, TimeUnit unit) throws InterruptedException, ClosedWatchServiceException;
public abstract WatchKey take() throws InterruptedException, ClosedWatchServiceException;


    /**
     * Package-protected listener, for thread coordination during tests.
     */
    static interface WatchServiceStartListener {
        void startedWatching(WatchService service);
    }

    /**
     * The watch service start listeners.  Implementation note: Consider this as
     * a thread-safe linked list.
     */
    private final Collection<WatchServiceStartListener> listeners = new LinkedBlockingQueue<WatchServiceStartListener>();

    /**
     * Adds a new watch service start listener.
     * @param listener the listener
     */
    final void registerWatchServiceStartListener(final WatchServiceStartListener listener) {
        listeners.add(listener);
    }

    /**
     * Removes a watch service start listener.
     * @param listener the listener
     */
    final void unregisterWatchServiceStartListener(final WatchServiceStartListener listener) {
        listeners.remove(listener);
    }

    /**
     * Notifies listeners when the watch service begins watching.
     */
    protected final void notifyWatchServiceStarted() {
        for (WatchServiceStartListener listener : listeners) {
            listener.startedWatching(this);
        }
    }
} public class StandardWatchEventKind {
	/**
	 * Indicates that a file has been created under the watched path.
	 */
	public static final WatchEvent.Kind<Path> 	ENTRY_CREATE = new PathWatchEventKind("ENTRY_CREATE");
	/**
	 * Indicates that a file has been deleted under the watched path. Note that
	 * on file rename the old file name will be reported as deleted if no other
	 * (extended) watch event kinds are specified.
	 * @see ExtendedWatchEventKind
	 */
	public static final WatchEvent.Kind<Path> 	ENTRY_DELETE = new PathWatchEventKind("ENTRY_DELETE");
	/**
	 * Indicates that a file under the watched path has been modified. Note that
	 * modification can never be byte-accurate, which means that you won't
	 * receive a modification event for each byte written to a file. It is
	 * higly implementation dependent how many modification events are produced.
	 */
	public static final WatchEvent.Kind<Path> 	ENTRY_MODIFY = new PathWatchEventKind("ENTRY_MODIFY");

	/**
	 * Indicates queue overflow in the WatchService. If the event queue
	 * overflows (because, for example,the WatchService runs
	 * out of space to store events because they occur faster than the client
	 * code can retreives them from the designated watch keys), additional
	 * events are dropped, and this event is reported.
	 * Note that WatchKeys are always subscribed to this event, regardless
	 * of whether it is specified to <code>register()</code> or not.
	 * @see Watchable#register
	 */
	public static final WatchEvent.Kind<Void> 	OVERFLOW = new VoidWatchEventKind("OVERFLOW");
} public class ClosedWatchServiceException extends IllegalStateException {

} public abstract class FileSystems {
	private static FileSystem defaultfs = new FileSystem(){
		@Override
		public WatchService newWatchService() {
			return Bootstrapper.newWatchService();
		}

	};

	private FileSystems(){
	}

	/**
	 * Gets the default file system. jpathwatch provides this method
	 * as a means to get a {@link FileSystem} that in turn allows
	 * to create a {@link WatchService} instance.
	 * @return the default file system object.
	 */
	public static FileSystem getDefault(){
		return defaultfs;
	}
} public abstract class Path implements Watchable {

	protected Path(){
	}

	@Override
	public abstract boolean equals(Object obj);

	@Override
	public abstract int hashCode();

	public abstract WatchKey register(WatchService watcher, Kind<?>... events) throws IOException;

	public abstract WatchKey register(WatchService watcher, Kind<?>[] events, Modifier... modifiers) throws IOException;

	public abstract Path resolve(Path other);

	@Override
	public abstract String toString();

} public abstract class Paths {
	private Paths(){
	}
	
	/**
	 * Creates a new Path instance from the given file system path string.
	 * @param path	the path string
	 * @return	a new Path instance representing the path string
	 */
	public static Path get(String path){
		return Bootstrapper.newPath(new File(path));
	}

	// JDK7 also provides a method to convert a URI into a path, but
	// we won't support that for now (I can't see this being helpful, and
	// would just require more testing
/*	public static Path get(URI uri){
		return Bootstrapper.newPath(new File(uri));
	}
 */
} public class Bootstrapper {
	private static final int OSTYPE_UNKNOWN = 0;
	private static final int OSTYPE_LINUX = 1;
	private static final int OSTYPE_WINDOWS = 2;
	private static final int OSTYPE_BSD = 3;

	private static boolean forcePolling = false;
	private static long defaultPollingInterval = 2000;	// by default, polling WatchService implementation poll every 2 seconds

	private static final int ostype;
	static {
		String osName = System.getProperty("os.name");
		if(osName.contains("Windows"))
			ostype = OSTYPE_WINDOWS;
		else if(osName.equals("Linux"))
			ostype = OSTYPE_LINUX;
		else if(osName.equals("Mac OS X") || osName.equals("FreeBSD"))
			ostype = OSTYPE_BSD;
		else
			ostype = OSTYPE_UNKNOWN;
	}

	/**
	 * Creates a new WatchService. This is a shortcut for calling
	 * <code>FileSystems.getDefault().newWatchService()</code> and
	 * is not source-compatible to JDK7
	 * @return	a new WatchService implementation instance.
	 * @see name.pachler.nio.file.FileSystem#newWatchService()
	 * @see name.pachler.nio.file.FileSystems#getDefault()
	 */
    public static WatchService newWatchService(){
		WatchService ws = null;
		try {
			if(!forcePolling){
				switch(ostype){
					case OSTYPE_LINUX:
						ws = new LinuxPathWatchService();
						break;
					case OSTYPE_WINDOWS:
						ws = new WindowsPathWatchService();
						break;
					case OSTYPE_BSD:
						ws = new BSDPathWatchService();
						break;
				}
			}
		} catch(Throwable t){
			Logger.getLogger(Bootstrapper.class.getName()).log(Level.WARNING, null, t);
		} finally {
			// if for whatever reason we don't have a
			// WatchService, we'll create a polling one as fallback.
			if(ws == null)
				ws = new PollingPathWatchService();
			return ws;
		}
    }

	/**
	 * Creates a new Path instance for a given File.
	 * @param file	that a new Path is created for
	 * @return	a new Path() corresponding to the given File
	 */
	public static Path newPath(File file){
		return new PathImpl(file);
	}

	/**
	 * Gets the File that corresponds to the given path.
	 * @param path Path for with to retreive the corresponding File
	 * @return	The file which corresponds to the given Path instance.
	 */
	public static File pathToFile(Path path){
		return ((PathImpl)path).getFile();
	}

	/**
	 * @return whether polling is enforced.
	 */
	public static boolean isForcePollingEnabled(){
		return forcePolling;
	}

	/**
	 * When force polling is enabled, the Bootstrapper's {@link #newWatchService()}
	 * method will only produce polling watch services. This feature is mostly
	 * useful for testing and debugging (and not not much else really).
	 * @param forcePollingEnabled true to enable force polling
	 */
	public static void setForcePollingEnabled(boolean forcePollingEnabled){
		forcePolling = forcePollingEnabled;
	}
	
	/**
	 * <p>This method allows to set the default polling time interval for
	 * new WatchService implementations that use polling. Note that polling
	 * is only used on a few supported platforms when certain event kinds
	 * are used or on unsupported platforms (fallback implementation).</p>
	 * <p>The polling interval determines how often a thread that calls
	 * {@link WatchService#take() } will wake up to check if files in the
	 * watched directory have changed. Longer time intervals will make
	 * a polling service less accurate, but costs less in CPU and disk
	 * resources, while shorter time intervals lead to higher accuracy but
	 * more consumed resources (up to the point where polling takes longer than
	 * the set interval in which case the machine will become very slow).</p>
	 * @param pollInterval	the polling time interval in milliseconds
	 */
	public static void setDefaultPollingInterval(long pollInterval){
		if(pollInterval <= 0)
			throw new IllegalArgumentException("polling interval must be greater than zero");
		defaultPollingInterval = pollInterval;
	}
	
	/**
	 * Retrieves the default polling interval.
	 * @see setDefaultPollingInterval
	 * @return	the default polling interval, in milliseconds
	 */
	public static long getDefaultPollingInterval() {
		return defaultPollingInterval;
	}
} public class ExtendedWatchEventModifier {

	/**
	 * The {@link #FILE_TREE} modifier makes a WatchKey recursive. Without this modifier,
	 * a file watch is shallow: For a watched directory <code>foo</code>,
	 * WatchEvents are only generated for direct children such as
	 * <code>foo/bar</code> or <code>foo/oof</code>. For a changes to files in a
	 * subdirectory of <code>foo</code>, such as <code>foo/adir/file</code>
	 * will only be reported if the {@link #FILE_TREE} modifier is specified.
	 *
	 * Note that this modifier is only available on the Windows platform. If
	 * specified on other platforms, Path.register() will throw an
	 * UnsupportedOperationException.
	 *
	 */
	public static final WatchEvent.Modifier<Path> FILE_TREE = new PathWatchEventModifier("FILE_TREE");

	/**
	 * The {@link #ACCURATE} modifier indicates that the WatchKey may not miss events
	 * under any circumstances.<p/>
	 * If a {@link name.pachler.nio.file.WatchService WatchService} implementation is based on polling it may miss files
	 * that are created and deleted between two polls of the key (see below how
	 * this can happen). The {@link #ACCURATE} modifier demands that no events may be
	 * missed, however, not all platforms support this. <p/>
	 * Note that even without specifying ACCURATE events are still guaranteed to be
	 * reported as symmetric - if both {@link StandardWatchEventKind#ENTRY_CREATE ENTRY_CREATE}
	 * and {@link StandardWatchEventKind#ENTRY_DELETE ENTRY_DELETE} have been
	 * specified, only whole pairs of creation/deletion can be
	 * missed, not just one of them.<p/>
	 * On some platforms, ACCURATE is implicit as the underlying operating
	 * system guarantees to deliver every event, even without specifying
	 * {@link #ACCURATE} (however, the queue might still overflow, generating
	 * an event of the {@link name.pachler.nio.file.StandardWatchEventKind#OVERFLOW OVERFLOW} kind)<p/>
	 * Consider the following scenario in which an implementation which
	 * does not support ACCURATE can miss events:<br/>
	 * Say, at 0ms, the key polls, and detects no files in a directory. The
	 * key goes to sleep for 200ms. Say, at 50ms, a file is created. Then, at
	 * 100ms, the same file is deleted again. When the WatchKey wakes up after
	 * 200ms, it will see the same as it saw before: An empty directory.
	 */
	public static final WatchEvent.Modifier<Path> ACCURATE = new PathWatchEventModifier("ACCURATE");
} public class PollingPathWatchService extends PathWatchService {

	private Map<Path,PollingPathWatchKey> keys = new HashMap<Path, PollingPathWatchKey>();
	private Set<PathWatchKey> signalledKeys = new HashSet<PathWatchKey>();
	private Queue<PathWatchKey> pendingKeys = new LinkedList<PathWatchKey>();
	private final Object internalLock = new Object();
	private boolean closed = false;
	private long pollInterval;

	public PollingPathWatchService() {
		pollInterval = Bootstrapper.getDefaultPollingInterval();
	}


	@Override
	public synchronized PathWatchKey register(Path path, Kind<?>[] kinds, Modifier[] modifiers) throws IOException {
		PathImpl pathImpl = checkAndCastToPathImpl(path);

		int flags = makeFlagMask(kinds, modifiers);

		// check that user only provided supported flags and modifiers
		int supportedFlags = (FLAG_FILTER_ENTRY_CREATE | FLAG_FILTER_ENTRY_DELETE | FLAG_FILTER_ENTRY_MODIFY | FLAG_FILTER_KEY_INVALID );
		if((flags & ~supportedFlags) != 0)
			throw new UnsupportedOperationException("The given watch event kind or modifier is not supported by this WatchService");

		PollingPathWatchKey key = keys.get(pathImpl);

		if(key == null){
			key = new PollingPathWatchKey(this, path, flags);
			keys.put(path, key);
		} else {
			key.setFlags(flags);
		}

		// first poll to capture initial state
		key.poll();

		return key;
	}

	@Override
	synchronized void cancel(PathWatchKey pathWatchKey) {
		PathWatchKey key = keys.get(pathWatchKey.getPath());
		if(key != pathWatchKey)
			return;	// this is not one of our keys

		boolean eventAdded = cancelImpl(pathWatchKey);
		if(eventAdded)
			queueKey(pathWatchKey);
	}

	private boolean cancelImpl(PathWatchKey key){
		keys.remove(key.getPath());
		key.invalidate();

		boolean eventAdded = false;

		if((key.getFlags() & FLAG_FILTER_KEY_INVALID) != 0)
		{
			key.addWatchEvent(new VoidWatchEvent(ExtendedWatchEventKind.KEY_INVALID));
			eventAdded = true;
		}

		return eventAdded;
	}

	@Override
	public synchronized boolean reset(PathWatchKey pathWatchKey) {

		if(!pathWatchKey.isValid() || keys.get(pathWatchKey.getPath()) != pathWatchKey)
			return false;	// this is not one of our keys...

		if(pathWatchKey.hasPendingWatchEvents())
			pendingKeys.add(pathWatchKey);
		else
			signalledKeys.remove(pathWatchKey);

		return true;
	}

	@Override
	public synchronized void close() throws IOException {
		synchronized(internalLock){
			closed = true;
			internalLock.notifyAll();
		}

	}

	@Override
	public synchronized WatchKey poll() throws InterruptedException, ClosedWatchServiceException {
		if(closed)
			throw new ClosedWatchServiceException();

		// if we have pending keys, return them first. This will make the caller
		// take all keys from the queue first
		if(pendingKeys.size() > 0)
			return pendingKeys.remove();

		for(PollingPathWatchKey k : keys.values()){
			try {
				k.poll();
			} catch (FileNotFoundException ex) {
				cancelImpl(k);
			}
			if(k.hasPendingWatchEvents() && !signalledKeys.contains(k.getPath())){
				signalledKeys.add(k);
				pendingKeys.add(k);
			}
		}

		return pendingKeys.poll();
	}

	@Override
	public WatchKey poll(long timeout, TimeUnit unit) throws InterruptedException, ClosedWatchServiceException {
		// check first if there is a pending key - because if there is, there
		// is no need to wait. We'll also start the timer now, because
		// polling takes time, and this way we know how much we've spent
		long startTime = System.currentTimeMillis();

		WatchKey key = poll();
		if(key != null)
			return key;

		synchronized(internalLock){

			long pollDuration = System.currentTimeMillis() - startTime;
			long timeoutMillis = unit.toMillis(timeout);

			long millis = timeoutMillis - pollDuration;

			// if polling took longer than our timeout, we won't wait and
			// poll a second time.
			if(millis <= 0)
				return null;

			internalLock.wait(millis);
		}

		return poll();
	}

	@Override
	public WatchKey take() throws InterruptedException, ClosedWatchServiceException {
		for(;;){
			WatchKey key = poll(pollInterval, TimeUnit.MILLISECONDS);
			if(key != null)
				return key;
		}
	}

	private void queueKey(PathWatchKey key) {
		// if this key has pending events and is not signalled
		// yet, set it to signalled.
		if(key.hasPendingWatchEvents() && !signalledKeys.contains(key))
		{
			signalledKeys.add(key);
			pendingKeys.add(key);
		}
	}

	public long getPollInterval(){
		return pollInterval;
	}

	public void setPollInterval(long pollInterval){
		this.pollInterval = pollInterval;
	}

} public class StandardWatchEventKind {
	/**
	 * Indicates that a file has been created under the watched path.
	 */
	public static final WatchEvent.Kind<Path> 	ENTRY_CREATE = new PathWatchEventKind("ENTRY_CREATE");
	/**
	 * Indicates that a file has been deleted under the watched path. Note that
	 * on file rename the old file name will be reported as deleted if no other
	 * (extended) watch event kinds are specified.
	 * @see ExtendedWatchEventKind
	 */
	public static final WatchEvent.Kind<Path> 	ENTRY_DELETE = new PathWatchEventKind("ENTRY_DELETE");
	/**
	 * Indicates that a file under the watched path has been modified. Note that
	 * modification can never be byte-accurate, which means that you won't
	 * receive a modification event for each byte written to a file. It is
	 * higly implementation dependent how many modification events are produced.
	 */
	public static final WatchEvent.Kind<Path> 	ENTRY_MODIFY = new PathWatchEventKind("ENTRY_MODIFY");

	/**
	 * Indicates queue overflow in the WatchService. If the event queue
	 * overflows (because, for example,the WatchService runs
	 * out of space to store events because they occur faster than the client
	 * code can retreives them from the designated watch keys), additional
	 * events are dropped, and this event is reported.
	 * Note that WatchKeys are always subscribed to this event, regardless
	 * of whether it is specified to <code>register()</code> or not.
	 * @see Watchable#register
	 */
	public static final WatchEvent.Kind<Void> 	OVERFLOW = new VoidWatchEventKind("OVERFLOW");
} public class ExtendedWatchEventKind {
	/**
	 * Indicates the old file name of a renamed file. The
	 * {@link WatchEvent}'s {@link WatchEvent#context} method will return
	 * a Path that indicates the previous name that the file had.
	 */
	public static WatchEvent.Kind<Path> 	ENTRY_RENAME_FROM = new PathWatchEventKind("ENTRY_RENAME_FROM");
	/**
	 * Indicates the new file name of a renamed file. The
	 * {@link WatchEvent}'s {@link WatchEvent#context} method will return
	 * a Path that indicates the new name of the file.
	 */
	public static WatchEvent.Kind<Path> 	ENTRY_RENAME_TO = new PathWatchEventKind("ENTRY_RENAME_TO");

	/**
	 * Indicates that the given {@link WatchKey} has become invalid. This
	 * can happen for a number of reasons:
	 * <ul>
	 *	<li>The key has been invalidated programmatically by calling
	 *		{@link WatchKey#cancel}.</li>
	 *	<li>The {@link WatchService} has been closed.</li>
	 *	<li>The path that the key has been registered with has become
	 *		unavailable, e.g because it was deleted or the file system on which
	 *		it resides has been unmounted.</li>
	 * </ul>
	 * Note that not all operating systems will invalidate the WatchKey
	 * (and report KEY_INVALID) under the same circumstances. On most platforms,
	 * moving or renaming the directory that the key was registered with will
	 * invalidate it, however, on Windows it will not (only when it is
	 * deleted or unmounted).
	 */
	public static WatchEvent.Kind<Void> 	KEY_INVALID = new VoidWatchEventKind("KEY_INVALID");
}
import java.util.logging.Level;
import java.util.logging.Logger;
import name.pachler.nio.file.WatchEvent;
import name.pachler.nio.file.WatchKey;
import name.pachler.nio.file.WatchService;
import name.pachler.nio.file.StandardWatchEventKind;
import java.io.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import name.pachler.nio.file.ClosedWatchServiceException;
import name.pachler.nio.file.FileSystems;
import name.pachler.nio.file.Path;
import name.pachler.nio.file.Paths;
import name.pachler.nio.file.ext.Bootstrapper;
import name.pachler.nio.file.ext.ExtendedWatchEventModifier;
import name.pachler.nio.file.impl.PollingPathWatchService;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
import static name.pachler.nio.file.StandardWatchEventKind.*;
import static name.pachler.nio.file.ext.ExtendedWatchEventKind.*;
/*
 * Copyright 2008-2011 Uwe Pachler
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation. This particular file is
 * subject to the "Classpath" exception as provided in the LICENSE file
 * that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

package name.pachler.nio.file;


public class JDK7Test {

// enable this to test the PollingPathWatchService on platforms that would
// otherwise use their native implementation
/*
	static
	{
		Bootstrapper.setForcePollingEnabled(true);
		Bootstrapper.setDefaultPollingInterval(500);
	}
*/
	static class SingleWatchKeyTaker extends Thread{
		WatchService watcher;
		WatchKey watchKey;
		List<WatchEvent<?>> events;
		boolean noExceptions = false;
		boolean closedException = false;

		public SingleWatchKeyTaker(WatchService watcher) {
			this.watcher = watcher;
		}

		public SingleWatchKeyTaker(WatchService watcher, String threadName) {
			super(threadName);
			this.watcher = watcher;
		}

		@Override
		public void run() {
			try {
				watchKey = watcher.take();
				Assert.assertNotNull(watchKey);
				events = watchKey.pollEvents();
				Assert.assertTrue(watchKey.reset());

				noExceptions = true;
			} catch (InterruptedException ex) {
				Logger.getLogger(JDK7Test.class.getName()).log(Level.SEVERE, null, ex);
			} catch (ClosedWatchServiceException ex) {
				closedException = true;
			}
		}

	}

	static File createTempDir(String prefix) throws IOException{
		File file = File.createTempFile(prefix, "");
		file.deleteOnExit();
		File dir = new File(file.getParentFile(), file.getName()+".dir");
		dir.mkdir();
		dir.deleteOnExit();
		return dir;
	}

	static void appendBytesToFile(File file, byte[] bytes) throws IOException {
		FileOutputStream fos = new FileOutputStream(file, true);
		fos.write(bytes, 0, bytes.length);
		fos.close();
	}

	boolean listContainsEvent(List<WatchEvent<?>> list, WatchEvent.Kind kind, String fileName)
	{
		Path context = null;