blob: 8399820dd519dc92f42e44a52d3af8e8822f8916 [file] [log] [blame]
/*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* 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 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.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.IOException;
import java.net.URI;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessMode;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemAlreadyExistsException;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Supplier;
/**
* A {@code FileSystem} that helps testing by trigger exception throwing based on filenames.
*/
class FaultyFileSystem extends FileSystem {
final Path root;
final boolean removeRootAfterClose;
final FileSystem delegate;
boolean isOpen;
FaultyFileSystem(Path root) throws IOException {
if (root == null) {
root = Files.createTempDirectory("faultyFS");
removeRootAfterClose = true;
} else {
if (! Files.isDirectory(root)) {
throw new IllegalArgumentException("must be a directory.");
}
removeRootAfterClose = false;
}
this.root = root;
delegate = root.getFileSystem();
isOpen = true;
}
private static Path unwrap(Path p) {
return PassThroughFileSystem.unwrap(p);
}
Path getRoot() {
return new PassThroughFileSystem.PassThroughPath(this, root);
}
@Override
public void close() throws IOException {
if (isOpen) {
if (removeRootAfterClose) {
TestUtil.removeAll(root);
}
isOpen = false;
}
}
@Override
public FileSystemProvider provider() {
return FaultyFSProvider.getInstance();
}
@Override
public boolean isOpen() {
return isOpen;
}
@Override
public boolean isReadOnly() {
return delegate.isReadOnly();
}
@Override
public String getSeparator() {
return delegate.getSeparator();
}
private <T> Iterable<T> SoleIterable(final T element) {
return new Iterable<T>() {
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private T soleElement = element;
@Override
public boolean hasNext() {
return soleElement != null;
}
@Override
public T next() {
try {
return soleElement;
} finally {
soleElement = null;
}
}
};
}
};
}
@Override
public Iterable<Path> getRootDirectories() {
return SoleIterable(getRoot());
}
@Override
public Iterable<FileStore> getFileStores() {
FileStore store;
try {
store = Files.getFileStore(root);
} catch (IOException ioe) {
store = null;
}
return SoleIterable(store);
}
@Override
public Set<String> supportedFileAttributeViews() {
// assume that unwrapped objects aren't exposed
return delegate.supportedFileAttributeViews();
}
@Override
public Path getPath(String first, String... more) {
return new PassThroughFileSystem.PassThroughPath(this, delegate.getPath(first, more));
}
@Override
public PathMatcher getPathMatcher(String syntaxAndPattern) {
final PathMatcher matcher = delegate.getPathMatcher(syntaxAndPattern);
return new PathMatcher() {
@Override
public boolean matches(Path path) {
return matcher.matches(unwrap(path));
}
};
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() {
// assume that unwrapped objects aren't exposed
return delegate.getUserPrincipalLookupService();
}
@Override
public WatchService newWatchService() throws IOException {
// to keep it simple
throw new UnsupportedOperationException();
}
static class FaultyException extends IOException {
FaultyException() {
super("fault triggered.");
}
}
static class FaultyFSProvider extends FileSystemProvider {
private static final String SCHEME = "faulty";
private static volatile FaultyFileSystem delegate;
private static FaultyFSProvider INSTANCE = new FaultyFSProvider();
private boolean enabled;
private FaultyFSProvider() {}
public static FaultyFSProvider getInstance() {
return INSTANCE;
}
public void setFaultyMode(boolean enable) {
enabled = enable;
}
private void triggerEx(String filename, String... names) throws IOException {
if (! enabled) {
return;
}
if (filename.equals("SecurityException")) {
throw new SecurityException("FaultyFS", new FaultyException());
}
if (filename.equals("IOException")) {
throw new FaultyException();
}
for (String name: names) {
if (name.equals(filename)) {
throw new FaultyException();
}
}
}
private void triggerEx(Path path, String... names) throws IOException {
triggerEx(path.getFileName().toString(), names);
}
@Override
public String getScheme() {
return SCHEME;
}
private void checkScheme(URI uri) {
if (!uri.getScheme().equalsIgnoreCase(SCHEME))
throw new IllegalArgumentException();
}
private void checkUri(URI uri) {
checkScheme(uri);
if (!uri.getSchemeSpecificPart().equals("///"))
throw new IllegalArgumentException();
}
@Override
public FileSystem newFileSystem(Path fakeRoot, Map<String,?> env)
throws IOException
{
if (env != null && env.keySet().contains("IOException")) {
triggerEx("IOException");
}
synchronized (FaultyFSProvider.class) {
if (delegate != null && delegate.isOpen())
throw new FileSystemAlreadyExistsException();
FaultyFileSystem result = new FaultyFileSystem(fakeRoot);
delegate = result;
return result;
}
}
@Override
public FileSystem newFileSystem(URI uri, Map<String,?> env)
throws IOException
{
if (env != null && env.keySet().contains("IOException")) {
triggerEx("IOException");
}
checkUri(uri);
synchronized (FaultyFSProvider.class) {
if (delegate != null && delegate.isOpen())
throw new FileSystemAlreadyExistsException();
FaultyFileSystem result = new FaultyFileSystem(null);
delegate = result;
return result;
}
}
@Override
public FileSystem getFileSystem(URI uri) {
checkUri(uri);
FileSystem result = delegate;
if (result == null)
throw new FileSystemNotFoundException();
return result;
}
@Override
public Path getPath(URI uri) {
checkScheme(uri);
if (delegate == null)
throw new FileSystemNotFoundException();
// only allow absolute path
String path = uri.getSchemeSpecificPart();
if (! path.startsWith("///")) {
throw new IllegalArgumentException();
}
return new PassThroughFileSystem.PassThroughPath(delegate, delegate.root.resolve(path.substring(3)));
}
@Override
public void setAttribute(Path file, String attribute, Object value, LinkOption... options)
throws IOException
{
triggerEx(file, "setAttribute");
Files.setAttribute(unwrap(file), attribute, value, options);
}
@Override
public Map<String,Object> readAttributes(Path file, String attributes, LinkOption... options)
throws IOException
{
triggerEx(file, "readAttributes");
return Files.readAttributes(unwrap(file), attributes, options);
}
@Override
public <V extends FileAttributeView> V getFileAttributeView(Path file,
Class<V> type,
LinkOption... options)
{
return Files.getFileAttributeView(unwrap(file), type, options);
}
@Override
public <A extends BasicFileAttributes> A readAttributes(Path file,
Class<A> type,
LinkOption... options)
throws IOException
{
triggerEx(file, "readAttributes");
return Files.readAttributes(unwrap(file), type, options);
}
@Override
public void delete(Path file) throws IOException {
triggerEx(file, "delete");
Files.delete(unwrap(file));
}
@Override
public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs)
throws IOException
{
triggerEx(target, "createSymbolicLink");
Files.createSymbolicLink(unwrap(link), unwrap(target), attrs);
}
@Override
public void createLink(Path link, Path existing) throws IOException {
triggerEx(existing, "createLink");
Files.createLink(unwrap(link), unwrap(existing));
}
@Override
public Path readSymbolicLink(Path link) throws IOException {
Path target = Files.readSymbolicLink(unwrap(link));
triggerEx(target, "readSymbolicLink");
return new PassThroughFileSystem.PassThroughPath(delegate, target);
}
@Override
public void copy(Path source, Path target, CopyOption... options) throws IOException {
triggerEx(source, "copy");
Files.copy(unwrap(source), unwrap(target), options);
}
@Override
public void move(Path source, Path target, CopyOption... options) throws IOException {
triggerEx(source, "move");
Files.move(unwrap(source), unwrap(target), options);
}
private DirectoryStream<Path> wrap(final DirectoryStream<Path> stream) {
return new DirectoryStream<Path>() {
@Override
public Iterator<Path> iterator() {
final Iterator<Path> itr = stream.iterator();
return new Iterator<Path>() {
private Path next = null;
@Override
public boolean hasNext() {
if (next == null) {
if (itr.hasNext()) {
next = itr.next();
} else {
return false;
}
}
if (next != null) {
try {
triggerEx(next, "DirectoryIteratorException");
} catch (IOException ioe) {
throw new DirectoryIteratorException(ioe);
} catch (SecurityException se) {
// ??? Does DS throw SecurityException during iteration?
next = null;
return hasNext();
}
}
return (next != null);
}
@Override
public Path next() {
try {
if (next != null || hasNext()) {
return new PassThroughFileSystem.PassThroughPath(delegate, next);
} else {
throw new NoSuchElementException();
}
} finally {
next = null;
}
}
@Override
public void remove() {
itr.remove();
}
};
}
@Override
public void close() throws IOException {
stream.close();
}
};
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
throws IOException
{
triggerEx(dir, "newDirectoryStream");
return wrap(Files.newDirectoryStream(unwrap(dir), filter));
}
@Override
public void createDirectory(Path dir, FileAttribute<?>... attrs)
throws IOException
{
triggerEx(dir, "createDirectory");
Files.createDirectory(unwrap(dir), attrs);
}
@Override
public SeekableByteChannel newByteChannel(Path file,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
triggerEx(file, "newByteChannel");
return Files.newByteChannel(unwrap(file), options, attrs);
}
@Override
public boolean isHidden(Path file) throws IOException {
triggerEx(file, "isHidden");
return Files.isHidden(unwrap(file));
}
@Override
public FileStore getFileStore(Path file) throws IOException {
triggerEx(file, "getFileStore");
return Files.getFileStore(unwrap(file));
}
@Override
public boolean isSameFile(Path file, Path other) throws IOException {
triggerEx(file, "isSameFile");
return Files.isSameFile(unwrap(file), unwrap(other));
}
@Override
public void checkAccess(Path file, AccessMode... modes)
throws IOException
{
triggerEx(file, "checkAccess");
// hack
if (modes.length == 0) {
if (Files.exists(unwrap(file)))
return;
else
throw new NoSuchFileException(file.toString());
}
throw new RuntimeException("not implemented yet");
}
}
}