WaitFile.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.lang;
import static ch.powerunit.extensions.async.lang.WaitResult.callableWithToString;
import static ch.powerunit.extensions.async.lang.WaitResult.of;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.Callable;
import ch.powerunit.extensions.async.impl.FilePool;
/**
* This class provides methods to wait for fileSystem events.
* <p>
* The goal is to wait for filesystem event, by using the
* {@link java.nio.file.WatchService} functionality.
*
* @since 1.1.0
* @see java.nio.file.WatchService
*/
public final class WaitFile {
private WaitFile() {
}
/**
* Wait for a folder to have some event.
* <p>
* The wait starts at the first try to get the result.
* <p>
* For example :
*
* <pre>
* CompletableFuture<Optional<Collection<WatchEvent<Path>>>> wait = WaitFile
* .eventIn(test, StandardWatchEventKinds.ENTRY_CREATE)
* .expecting(l -> l.stream().map(WatchEvent::context).map(Path::getFileName).map(Path::toString)
* .anyMatch(n -> n.equals("test")))
* .repeat(3).every(Duration.ofMillis(250)).usingDefaultExecutor().asyncExec();
* </pre>
*
* Defines a 3 tries with a wait time of 250ms, for a creation event, containing
* at least one event with last part of a path named test.
*
* @param directory
* the directory to be verified.
* @param events
* the events to wait for.
* @return {@link WaitResultBuilder1} the next step of the builder.
*/
@SafeVarargs
public static WaitResultBuilder1<Collection<WatchEvent<Path>>> eventIn(Path directory, Kind<Path>... events) {
requireNonNull(directory, "directory can't be null");
FilePool filePool = new FilePool(directory, events);
return of(filePool, filePool::close);
}
/**
* Wait for a folder to contains new entry.
* <p>
* The wait starts at the first try to get the result.
* <p>
* For example :
*
* <pre>
* CompletableFuture<Optional<Collection<Path>>> wait = WaitFile.newFileIn(test)
* .expecting(l -> l.stream().map(Path::getFileName).map(Path::toString).anyMatch(n -> n.equals("test")))
* .repeat(3).every(Duration.ofMillis(250)).usingDefaultExecutor().asyncExec();
* </pre>
*
* Defines a 3 tries with a wait time of 250ms, for a list of new file,
* containing at least one file with last part of a path named test.
*
*
* @param directory
* the directory to be verified.
* @return {@link WaitResultBuilder1} the next step of the builder.
*/
public static WaitResultBuilder1<Collection<Path>> newFileIn(Path directory) {
requireNonNull(directory, "directory can't be null");
FilePool filePool = new FilePool(directory, ENTRY_CREATE);
return of(callableWithToString(toPathCollection(filePool), () -> "new file in " + filePool), filePool::close);
}
/**
* Wait for a folder to contains new entry based on his name.
* <p>
* The wait starts at the first try to get the result.
* <p>
* For example :
*
* <pre>
* CompletableFuture<Optional<Path>> wait = WaitFile.newFileNamedIn(test, "test").expectingNotNull().repeat(3)
* .every(Duration.ofMillis(250)).usingDefaultExecutor().asyncExec();
* </pre>
*
* Defines a 3 tries with a wait time of 25ms, for a file named "test".
*
* @param directory
* the directory to be verified.
* @param name
* the expected name
* @return {@link WaitResultBuilder1} the next step of the builder.
*/
public static WaitResultBuilder1<Path> newFileNamedIn(Path directory, String name) {
requireNonNull(directory, "directory can't be null");
requireNonNull(name, "name can't be null");
FilePool filePool = new FilePool(directory, ENTRY_CREATE);
return of(callableWithToString(toPathByName(toPathCollection(filePool), name),
() -> String.format("New file named %s in %s", name, filePool)), filePool::close);
}
/**
* Wait for a folder to have entry removed.
* <p>
* The wait starts at the first try to get the result.
*
* @param directory
* the directory to be verified.
* @return {@link WaitResultBuilder1} the next step of the builder.
*/
public static WaitResultBuilder1<Collection<Path>> removeFileFrom(Path directory) {
requireNonNull(directory, "directory can't be null");
FilePool filePool = new FilePool(directory, ENTRY_DELETE);
return of(callableWithToString(toPathCollection(filePool), () -> "Removed file from " + directory),
filePool::close);
}
private static Callable<Collection<Path>> toPathCollection(Callable<Collection<WatchEvent<Path>>> callable) {
return () -> callable.call().stream().map(WatchEvent::context)
.collect(collectingAndThen(toList(), Collections::unmodifiableList));
}
private static Callable<Path> toPathByName(Callable<Collection<Path>> callable, String name) {
return () -> callable.call().stream()
.filter(p -> Objects.equals(p.getName(p.getNameCount() - 1).toString(), name)).findFirst().orElse(null);
}
}