FilePool.java

/**
 * Powerunit - A JDK1.8 test framework
 * Copyright (C) 2014 Mathieu Boretti.
 *
 * This file is part of Powerunit
 *
 * Powerunit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Powerunit 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Powerunit. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.powerunit.extensions.async.impl;

import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.function.Predicate;

public final class FilePool implements Callable<Collection<WatchEvent<Path>>> {

	private static final Predicate<WatchEvent<?>> IGNORE_OVERFLOW = e -> !Objects.equals(e.kind(), OVERFLOW);

	@SuppressWarnings("unchecked")
	private static final Function<WatchEvent<?>, WatchEvent<Path>> COERCE_TO_PATH = e -> (WatchEvent<Path>) e;

	private final Path directory;
	private final WatchEvent.Kind<?> events[];

	private WatchService watcher; // Late init
	private WatchKey key; // Late init

	public FilePool(Path directory, WatchEvent.Kind<?>... events) {
		this.directory = directory;
		this.events = events;
	}

	@Override
	public Collection<WatchEvent<Path>> call() throws Exception {
		initIfNeeded();
		try {
			return key.pollEvents().stream().filter(IGNORE_OVERFLOW).map(COERCE_TO_PATH)
					.collect(collectingAndThen(toList(), Collections::unmodifiableList));
		} finally {
			key.reset();
		}
	}

	public void close() {
		Optional.ofNullable(watcher).ifPresent(FilePool::safeCloseWatchService);
	}

	@Override
	public String toString() {
		return "FilePool [directory=" + directory + ", events=" + Arrays.toString(events) + "]";
	}

	private void initIfNeeded() throws IOException {
		if (watcher == null) {
			watcher = directory.getFileSystem().newWatchService();
			key = directory.register(watcher, events);
		}
	}

	private static void safeCloseWatchService(WatchService watcher) {
		try {
			watcher.close();
		} catch (IOException e) {
			// ignore
		}
	}

}