LUCENE-6072: use mockfilesystem in tests

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1641632 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Robert Muir 2014-11-25 15:21:18 +00:00
parent 327d10e153
commit 7dbc2ac63a
20 changed files with 2647 additions and 3 deletions

View File

@ -0,0 +1,205 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.lucene.util.InfoStream;
import org.apache.lucene.util.LuceneTestCase;
public class TestMockFilesystems extends LuceneTestCase {
public void testLeakInputStream() throws IOException {
Path dir = FilterPath.unwrap(createTempDir());
FileSystem fs = new LeakFS(dir.getFileSystem()).getFileSystem(URI.create("file:///"));
Path wrapped = new FilterPath(dir, fs);
OutputStream file = Files.newOutputStream(wrapped.resolve("stillopen"));
file.write(5);
file.close();
InputStream leak = Files.newInputStream(wrapped.resolve("stillopen"));
try {
fs.close();
fail("should have gotten exception");
} catch (Exception e) {
assertTrue(e.getMessage().contains("file handle leaks"));
}
leak.close();
}
public void testLeakOutputStream() throws IOException {
Path dir = FilterPath.unwrap(createTempDir());
FileSystem fs = new LeakFS(dir.getFileSystem()).getFileSystem(URI.create("file:///"));
Path wrapped = new FilterPath(dir, fs);
OutputStream leak = Files.newOutputStream(wrapped.resolve("leaky"));
try {
fs.close();
fail("should have gotten exception");
} catch (Exception e) {
assertTrue(e.getMessage().contains("file handle leaks"));
}
leak.close();
}
public void testLeakFileChannel() throws IOException {
Path dir = FilterPath.unwrap(createTempDir());
FileSystem fs = new LeakFS(dir.getFileSystem()).getFileSystem(URI.create("file:///"));
Path wrapped = new FilterPath(dir, fs);
OutputStream file = Files.newOutputStream(wrapped.resolve("stillopen"));
file.write(5);
file.close();
FileChannel leak = FileChannel.open(wrapped.resolve("stillopen"));
try {
fs.close();
fail("should have gotten exception");
} catch (Exception e) {
assertTrue(e.getMessage().contains("file handle leaks"));
}
leak.close();
}
public void testLeakAsyncFileChannel() throws IOException {
Path dir = FilterPath.unwrap(createTempDir());
FileSystem fs = new LeakFS(dir.getFileSystem()).getFileSystem(URI.create("file:///"));
Path wrapped = new FilterPath(dir, fs);
OutputStream file = Files.newOutputStream(wrapped.resolve("stillopen"));
file.write(5);
file.close();
AsynchronousFileChannel leak = AsynchronousFileChannel.open(wrapped.resolve("stillopen"));
try {
fs.close();
fail("should have gotten exception");
} catch (Exception e) {
assertTrue(e.getMessage().contains("file handle leaks"));
}
leak.close();
}
public void testLeakByteChannel() throws IOException {
Path dir = FilterPath.unwrap(createTempDir());
FileSystem fs = new LeakFS(dir.getFileSystem()).getFileSystem(URI.create("file:///"));
Path wrapped = new FilterPath(dir, fs);
OutputStream file = Files.newOutputStream(wrapped.resolve("stillopen"));
file.write(5);
file.close();
SeekableByteChannel leak = Files.newByteChannel(wrapped.resolve("stillopen"));
try {
fs.close();
fail("should have gotten exception");
} catch (Exception e) {
assertTrue(e.getMessage().contains("file handle leaks"));
}
leak.close();
}
public void testDeleteOpenFile() throws IOException {
Path dir = FilterPath.unwrap(createTempDir());
FileSystem fs = new WindowsFS(dir.getFileSystem()).getFileSystem(URI.create("file:///"));
Path wrapped = new FilterPath(dir, fs);
OutputStream file = Files.newOutputStream(wrapped.resolve("stillopen"));
file.write(5);
file.close();
InputStream is = Files.newInputStream(wrapped.resolve("stillopen"));
try {
Files.delete(wrapped.resolve("stillopen"));
fail("should have gotten exception");
} catch (IOException e) {
assertTrue(e.getMessage().contains("access denied"));
}
is.close();
}
public void testDeleteIfExistsOpenFile() throws IOException {
Path dir = FilterPath.unwrap(createTempDir());
FileSystem fs = new WindowsFS(dir.getFileSystem()).getFileSystem(URI.create("file:///"));
Path wrapped = new FilterPath(dir, fs);
OutputStream file = Files.newOutputStream(wrapped.resolve("stillopen"));
file.write(5);
file.close();
InputStream is = Files.newInputStream(wrapped.resolve("stillopen"));
try {
Files.deleteIfExists(wrapped.resolve("stillopen"));
fail("should have gotten exception");
} catch (IOException e) {
assertTrue(e.getMessage().contains("access denied"));
}
is.close();
}
public void testRenameOpenFile() throws IOException {
Path dir = FilterPath.unwrap(createTempDir());
FileSystem fs = new WindowsFS(dir.getFileSystem()).getFileSystem(URI.create("file:///"));
Path wrapped = new FilterPath(dir, fs);
OutputStream file = Files.newOutputStream(wrapped.resolve("stillopen"));
file.write(5);
file.close();
InputStream is = Files.newInputStream(wrapped.resolve("stillopen"));
try {
Files.move(wrapped.resolve("stillopen"), wrapped.resolve("target"), StandardCopyOption.ATOMIC_MOVE);
fail("should have gotten exception");
} catch (IOException e) {
assertTrue(e.getMessage().contains("access denied"));
}
is.close();
}
public void testVerboseWrite() throws IOException {
Path dir = FilterPath.unwrap(createTempDir());
final AtomicBoolean seenMessage = new AtomicBoolean(false);
InfoStream testStream = new InfoStream() {
@Override
public void close() throws IOException {}
@Override
public void message(String component, String message) {
if ("FS".equals(component) && message.startsWith("newOutputStream")) {
seenMessage.set(true);
}
}
@Override
public boolean isEnabled(String component) {
return true;
}
};
FileSystem fs = new VerboseFS(dir.getFileSystem(), testStream).getFileSystem(URI.create("file:///"));
Path wrapped = new FilterPath(dir, fs);
OutputStream file = Files.newOutputStream(wrapped.resolve("output"));
assertTrue(seenMessage.get());
file.close();
}
}

View File

@ -22,6 +22,11 @@
<property name="build.dir" location="../build/test-framework"/>
<!-- file is part of the API -->
<property name="forbidden-base-excludes" value="
org/apache/lucene/mockfile/FilterPath.class
"/>
<import file="../common-build.xml"/>
<path id="classpath">

View File

@ -0,0 +1,59 @@
package org.apache.lucene.mockfile;
import java.io.IOException;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.FileChannel;
import java.nio.file.FileSystem;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.attribute.FileAttribute;
import java.util.Set;
import java.util.concurrent.ExecutorService;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Disables actual calls to fsync.
* <p>
* All other filesystem operations are passed thru as normal.
*/
public class DisableFsyncFS extends FilterFileSystemProvider {
/**
* Create a new instance, wrapping {@code delegate}.
*/
public DisableFsyncFS(FileSystem delegate) {
super("disablefsync://", delegate);
}
@Override
public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
return new FilterFileChannel(super.newFileChannel(path, options, attrs)) {
@Override
public void force(boolean metaData) throws IOException {}
};
}
@Override
public AsynchronousFileChannel newAsynchronousFileChannel(Path path, Set<? extends OpenOption> options, ExecutorService executor, FileAttribute<?>... attrs) throws IOException {
return new FilterAsynchronousFileChannel(super.newAsynchronousFileChannel(path, options, executor, attrs)) {
@Override
public void force(boolean metaData) throws IOException {}
};
}
}

View File

@ -0,0 +1,112 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.FileLock;
import java.util.Objects;
import java.util.concurrent.Future;
/**
* A {@code FilterAsynchronousFileChannel} contains another
* {@code AsynchronousFileChannel}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
*/
public class FilterAsynchronousFileChannel extends AsynchronousFileChannel {
/**
* The underlying {@code AsynchronousFileChannel} instance.
*/
protected final AsynchronousFileChannel delegate;
/**
* Construct a {@code FilterAsynchronousFileChannel} based on
* the specified base channel.
* <p>
* Note that base channel is closed if this channel is closed.
* @param delegate specified base channel.
*/
public FilterAsynchronousFileChannel(AsynchronousFileChannel delegate) {
this.delegate = Objects.requireNonNull(delegate);
}
@Override
public void close() throws IOException {
delegate.close();
}
@Override
public boolean isOpen() {
return delegate.isOpen();
}
@Override
public long size() throws IOException {
return delegate.size();
}
@Override
public AsynchronousFileChannel truncate(long size) throws IOException {
delegate.truncate(size);
return this;
}
@Override
public void force(boolean metaData) throws IOException {
delegate.force(metaData);
}
@Override
public <A> void lock(long position, long size, boolean shared, A attachment, CompletionHandler<FileLock,? super A> handler) {
delegate.lock(position, size, shared, attachment, handler);
}
@Override
public Future<FileLock> lock(long position, long size, boolean shared) {
return delegate.lock(position, size, shared);
}
@Override
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
return delegate.tryLock(position, size, shared);
}
@Override
public <A> void read(ByteBuffer dst, long position, A attachment, CompletionHandler<Integer,? super A> handler) {
delegate.read(dst, position, attachment, handler);
}
@Override
public Future<Integer> read(ByteBuffer dst, long position) {
return delegate.read(dst, position);
}
@Override
public <A> void write(ByteBuffer src, long position, A attachment, CompletionHandler<Integer,? super A> handler) {
delegate.write(src, position, attachment, handler);
}
@Override
public Future<Integer> write(ByteBuffer src, long position) {
return delegate.write(src, position);
}
}

View File

@ -0,0 +1,58 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.util.Iterator;
import java.util.Objects;
/**
* A {@code FilterDirectoryStream} contains another
* {@code DirectoryStream}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
*/
public class FilterDirectoryStream<T> implements DirectoryStream<T> {
/**
* The underlying {@code DirectoryStream} instance.
*/
protected final DirectoryStream<T> delegate;
/**
* Construct a {@code FilterDirectoryStream} based on
* the specified base stream.
* <p>
* Note that base stream is closed if this stream is closed.
* @param delegate specified base stream.
*/
public FilterDirectoryStream(DirectoryStream<T> delegate) {
this.delegate = Objects.requireNonNull(delegate);
}
@Override
public void close() throws IOException {
delegate.close();
}
@Override
public Iterator<T> iterator() {
return delegate.iterator();
}
}

View File

@ -0,0 +1,157 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOError;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Objects;
/**
* A {@code FilterFileChannel} contains another
* {@code FileChannel}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
*/
public class FilterFileChannel extends FileChannel {
/**
* The underlying {@code FileChannel} instance.
*/
protected final FileChannel delegate;
/**
* Construct a {@code FilterFileChannel} based on
* the specified base channel.
* <p>
* Note that base channel is closed if this channel is closed.
* @param delegate specified base channel.
*/
public FilterFileChannel(FileChannel delegate) {
this.delegate = Objects.requireNonNull(delegate);
}
@Override
public int read(ByteBuffer dst) throws IOException {
return delegate.read(dst);
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
return delegate.read(dsts, offset, length);
}
@Override
public int write(ByteBuffer src) throws IOException {
return delegate.write(src);
}
@Override
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
return delegate.write(srcs, offset, length);
}
@Override
public long position() throws IOException {
return delegate.position();
}
@Override
public FileChannel position(long newPosition) throws IOException {
delegate.position(newPosition);
return this;
}
@Override
public long size() throws IOException {
return delegate.size();
}
@Override
public FileChannel truncate(long size) throws IOException {
delegate.truncate(size);
return this;
}
@Override
public void force(boolean metaData) throws IOException {
delegate.force(metaData);
}
@Override
public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
return delegate.transferTo(position, count, target);
}
@Override
public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
return delegate.transferFrom(src, position, count);
}
@Override
public int read(ByteBuffer dst, long position) throws IOException {
return delegate.read(dst, position);
}
@Override
public int write(ByteBuffer src, long position) throws IOException {
return delegate.write(src, position);
}
@Override
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
return delegate.map(mode, position, size);
}
@Override
public FileLock lock(long position, long size, boolean shared) throws IOException {
return delegate.lock(position, size, shared);
}
@Override
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
return delegate.tryLock(position, size, shared);
}
@Override
protected void implCloseChannel() throws IOException {
// our only way to call delegate.implCloseChannel()
for (Class<?> clazz = delegate.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
final Method method;
try {
method = clazz.getDeclaredMethod("implCloseChannel");
} catch (NoSuchMethodException e) {
continue;
}
try {
method.setAccessible(true);
method.invoke(delegate);
return;
} catch (ReflectiveOperationException e) {
throw new IOError(e);
}
}
throw new AssertionError();
}
}

View File

@ -0,0 +1,109 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.FileStoreAttributeView;
import java.util.Objects;
/**
* A {@code FilterFileStore} contains another
* {@code FileStore}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
*/
public class FilterFileStore extends FileStore {
/**
* The underlying {@code FileStore} instance.
*/
protected final FileStore delegate;
/**
* URI scheme used for this instance.
*/
protected final String scheme;
/**
* Construct a {@code FilterFileStore} based on
* the specified base store.
* @param delegate specified base store.
* @param scheme URI scheme identifying this instance.
*/
public FilterFileStore(FileStore delegate, String scheme) {
this.delegate = Objects.requireNonNull(delegate);
this.scheme = Objects.requireNonNull(scheme);
}
@Override
public String name() {
return delegate.name();
}
@Override
public String type() {
return scheme + "(" + delegate.type() + ")";
}
@Override
public String toString() {
return scheme + "(" + delegate.toString() + ")";
}
@Override
public boolean isReadOnly() {
return delegate.isReadOnly();
}
@Override
public long getTotalSpace() throws IOException {
return delegate.getTotalSpace();
}
@Override
public long getUsableSpace() throws IOException {
return delegate.getUsableSpace();
}
@Override
public long getUnallocatedSpace() throws IOException {
return delegate.getUnallocatedSpace();
}
@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
return delegate.supportsFileAttributeView(type);
}
@Override
public boolean supportsFileAttributeView(String name) {
return delegate.supportsFileAttributeView(name);
}
@Override
public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
return delegate.getFileStoreAttributeView(type);
}
@Override
public Object getAttribute(String attribute) throws IOException {
return delegate.getAttribute(attribute);
}
}

View File

@ -0,0 +1,165 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.WatchService;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
/**
* A {@code FilterFileSystem} contains another
* {@code FileSystem}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
*/
public class FilterFileSystem extends FileSystem {
/**
* FileSystemProvider that created this FilterFileSystem
*/
protected final FilterFileSystemProvider parent;
/**
* The underlying {@code FileSystem} instance.
*/
protected final FileSystem delegate;
/**
* Construct a {@code FilterFileSystem} based on
* the specified base filesystem.
* <p>
* Note that base filesystem is closed if this filesystem is closed,
* however the default filesystem provider will never be closed, it doesn't
* support that.
* @param delegate specified base channel.
*/
public FilterFileSystem(FilterFileSystemProvider parent, FileSystem delegate) {
this.parent = Objects.requireNonNull(parent);
this.delegate = Objects.requireNonNull(delegate);
}
@Override
public FileSystemProvider provider() {
return parent;
}
@Override
public void close() throws IOException {
if (delegate == FileSystems.getDefault()) {
// you can't close the default provider!
parent.onClose();
} else {
try (FileSystem d = delegate) {
parent.onClose();
}
}
}
@Override
public boolean isOpen() {
return delegate.isOpen();
}
@Override
public boolean isReadOnly() {
return delegate.isReadOnly();
}
@Override
public String getSeparator() {
return delegate.getSeparator();
}
@Override
public Iterable<Path> getRootDirectories() {
final Iterable<Path> roots = delegate.getRootDirectories();
return () -> {
final Iterator<Path> iterator = roots.iterator();
return new Iterator<Path>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Path next() {
return new FilterPath(iterator.next(), FilterFileSystem.this);
}
};
};
}
@Override
public Iterable<FileStore> getFileStores() {
final Iterable<FileStore> fileStores = delegate.getFileStores();
return () -> {
final Iterator<FileStore> iterator = fileStores.iterator();
return new Iterator<FileStore>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public FileStore next() {
return new FilterFileStore(iterator.next(), parent.getScheme());
}
};
};
}
@Override
public Set<String> supportedFileAttributeViews() {
return delegate.supportedFileAttributeViews();
}
@Override
public Path getPath(String first, String... more) {
return new FilterPath(delegate.getPath(first, more), this);
}
@Override
public PathMatcher getPathMatcher(String syntaxAndPattern) {
final PathMatcher matcher = delegate.getPathMatcher(syntaxAndPattern);
return path -> {
if (path instanceof FilterPath) {
return matcher.matches(((FilterPath)path).delegate);
}
return false;
};
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() {
return delegate.getUserPrincipalLookupService();
}
@Override
public WatchService newWatchService() throws IOException {
return delegate.newWatchService();
}
}

View File

@ -0,0 +1,245 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessMode;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.spi.FileSystemProvider;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
/**
* A {@code FilterFileSystemProvider} contains another
* {@code FileSystemProvider}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
*/
public class FilterFileSystemProvider extends FileSystemProvider {
/**
* The underlying {@code FileSystemProvider}.
*/
protected final FileSystemProvider delegate;
/**
* The underlying {@code FileSystem} instance.
*/
protected final FileSystem fileSystem;
/**
* The URI scheme for this provider.
*/
protected final String scheme;
/**
* Construct a {@code FilterFileSystemProvider} indicated by
* the specified {@code scheme} and wrapping functionality of the
* provider of the specified base filesystem.
* @param scheme URI scheme
* @param delegateInstance specified base filesystem.
*/
public FilterFileSystemProvider(String scheme, FileSystem delegateInstance) {
this.scheme = Objects.requireNonNull(scheme);
Objects.requireNonNull(delegateInstance);
this.delegate = delegateInstance.provider();
this.fileSystem = new FilterFileSystem(this, delegateInstance);
}
@Override
public String getScheme() {
return scheme;
}
@Override
public FileSystem newFileSystem(URI uri, Map<String,?> env) throws IOException {
return fileSystem;
}
@Override
public FileSystem newFileSystem(Path path, Map<String,?> env) throws IOException {
return fileSystem;
}
@Override
public FileSystem getFileSystem(URI uri) {
return fileSystem;
}
@Override
public Path getPath(URI uri) {
Path path = delegate.getPath(toDelegate(uri));
return new FilterPath(path, fileSystem);
}
@Override
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
delegate.createDirectory(toDelegate(dir), attrs);
}
@Override
public void delete(Path path) throws IOException {
delegate.delete(toDelegate(path));
}
@Override
public void copy(Path source, Path target, CopyOption... options) throws IOException {
delegate.copy(toDelegate(source), toDelegate(target), options);
}
@Override
public void move(Path source, Path target, CopyOption... options) throws IOException {
delegate.move(toDelegate(source), toDelegate(target), options);
}
@Override
public boolean isSameFile(Path path, Path path2) throws IOException {
return delegate.isSameFile(toDelegate(path), toDelegate(path2));
}
@Override
public boolean isHidden(Path path) throws IOException {
return delegate.isHidden(toDelegate(path));
}
@Override
public FileStore getFileStore(Path path) throws IOException {
return delegate.getFileStore(toDelegate(path));
}
@Override
public void checkAccess(Path path, AccessMode... modes) throws IOException {
delegate.checkAccess(toDelegate(path), modes);
}
@Override
public <V extends FileAttributeView> V getFileAttributeView(Path path, Class<V> type, LinkOption... options) {
return delegate.getFileAttributeView(toDelegate(path), type, options);
}
@Override
public <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException {
return delegate.readAttributes(toDelegate(path), type, options);
}
@Override
public Map<String,Object> readAttributes(Path path, String attributes, LinkOption... options) throws IOException {
return delegate.readAttributes(toDelegate(path), attributes, options);
}
@Override
public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
delegate.setAttribute(toDelegate(path), attribute, value, options);
}
@Override
public InputStream newInputStream(Path path, OpenOption... options) throws IOException {
return delegate.newInputStream(toDelegate(path), options);
}
@Override
public OutputStream newOutputStream(Path path, OpenOption... options) throws IOException {
return delegate.newOutputStream(toDelegate(path), options);
}
@Override
public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
return delegate.newFileChannel(toDelegate(path), options, attrs);
}
@Override
public AsynchronousFileChannel newAsynchronousFileChannel(Path path, Set<? extends OpenOption> options, ExecutorService executor, FileAttribute<?>... attrs) throws IOException {
return delegate.newAsynchronousFileChannel(toDelegate(path), options, executor, attrs);
}
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
return delegate.newByteChannel(toDelegate(path), options, attrs);
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException {
return delegate.newDirectoryStream(toDelegate(dir), filter);
}
@Override
public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
delegate.createSymbolicLink(toDelegate(link), toDelegate(target), attrs);
}
@Override
public void createLink(Path link, Path existing) throws IOException {
delegate.createLink(toDelegate(link), toDelegate(existing));
}
@Override
public boolean deleteIfExists(Path path) throws IOException {
return delegate.deleteIfExists(toDelegate(path));
}
@Override
public Path readSymbolicLink(Path link) throws IOException {
return delegate.readSymbolicLink(toDelegate(link));
}
private Path toDelegate(Path path) {
if (path instanceof FilterPath) {
return ((FilterPath) path).delegate;
}
return path;
}
private URI toDelegate(URI uri) {
try {
return new URI(delegate.getScheme(), uri.getSchemeSpecificPart(), uri.getFragment());
} catch (URISyntaxException e) {
throw new IOError(e);
}
}
/**
* Override to trigger some behavior when the filesystem is closed.
* <p>
* This is always called for each FilterFileSystemProvider in the chain.
*/
protected void onClose() {
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + delegate + ")";
}
}

View File

@ -0,0 +1,100 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
/**
* A {@code FilterInputStream2} contains another
* {@code InputStream}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
* <p>
* Note: unlike {@link FilterInputStream} this class
* delegates every method by default. This means to transform
* {@code read} calls, you need to override multiple methods.
* On the other hand, it is less trappy: a simple implementation
* that just overrides {@code close} will not force bytes to be
* read one-at-a-time.
*/
public class FilterInputStream2 extends InputStream {
/**
* The underlying {@code InputStream} instance.
*/
protected final InputStream delegate;
/**
* Construct a {@code FilterInputStream2} based on
* the specified base stream.
* <p>
* Note that base stream is closed if this stream is closed.
* @param delegate specified base stream.
*/
public FilterInputStream2(InputStream delegate) {
this.delegate = Objects.requireNonNull(delegate);
}
@Override
public int read() throws IOException {
return delegate.read();
}
@Override
public int read(byte[] b) throws IOException {
return delegate.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return delegate.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return delegate.skip(n);
}
@Override
public int available() throws IOException {
return delegate.available();
}
@Override
public void close() throws IOException {
delegate.close();
}
@Override
public synchronized void mark(int readlimit) {
delegate.mark(readlimit);
}
@Override
public synchronized void reset() throws IOException {
delegate.reset();
}
@Override
public boolean markSupported() {
return delegate.markSupported();
}
}

View File

@ -0,0 +1,80 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Objects;
/**
* A {@code FilterOutputStream2} contains another
* {@code OutputStream}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
* <p>
* Note: unlike {@link FilterOutputStream} this class
* delegates every method by default. This means to transform
* {@code write} calls, you need to override multiple methods.
* On the other hand, it is less trappy: a simple implementation
* that just overrides {@code close} will not force bytes to be
* written one-at-a-time.
*/
public class FilterOutputStream2 extends OutputStream {
/**
* The underlying {@code OutputStream} instance.
*/
protected final OutputStream delegate;
/**
* Construct a {@code FilterOutputStream2} based on
* the specified base stream.
* <p>
* Note that base stream is closed if this stream is closed.
* @param delegate specified base stream.
*/
public FilterOutputStream2(OutputStream delegate) {
this.delegate = Objects.requireNonNull(delegate);
}
@Override
public void write(byte[] b) throws IOException {
delegate.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
delegate.write(b, off, len);
}
@Override
public void flush() throws IOException {
delegate.flush();
}
@Override
public void close() throws IOException {
delegate.close();
}
@Override
public void write(int b) throws IOException {
delegate.write(b);
}
}

View File

@ -0,0 +1,265 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchEvent.Modifier;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Iterator;
/**
* A {@code FilterPath} contains another
* {@code Path}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
*/
public class FilterPath implements Path {
/**
* The underlying {@code Path} instance.
*/
protected final Path delegate;
/**
* The parent {@code FileSystem} for this path.
*/
protected final FileSystem fileSystem;
/**
* Construct a {@code FilterPath} with parent
* {@code fileSystem}, based on the specified base path.
* @param delegate specified base path.
* @param fileSystem parent fileSystem.
*/
public FilterPath(Path delegate, FileSystem fileSystem) {
this.delegate = delegate;
this.fileSystem = fileSystem;
}
/**
* Get the underlying wrapped path.
* @return wrapped path.
*/
public Path getDelegate() {
return delegate;
}
@Override
public FileSystem getFileSystem() {
return fileSystem;
}
@Override
public boolean isAbsolute() {
return delegate.isAbsolute();
}
@Override
public Path getRoot() {
Path root = delegate.getRoot();
if (root == null) {
return null;
}
return new FilterPath(root, fileSystem);
}
@Override
public Path getFileName() {
Path fileName = delegate.getFileName();
if (fileName == null) {
return null;
}
return new FilterPath(fileName, fileSystem);
}
@Override
public Path getParent() {
Path parent = delegate.getParent();
if (parent == null) {
return null;
}
return new FilterPath(parent, fileSystem);
}
@Override
public int getNameCount() {
return delegate.getNameCount();
}
@Override
public Path getName(int index) {
return new FilterPath(delegate.getName(index), fileSystem);
}
@Override
public Path subpath(int beginIndex, int endIndex) {
return new FilterPath(delegate.subpath(beginIndex, endIndex), fileSystem);
}
@Override
public boolean startsWith(Path other) {
if (other instanceof FilterPath) {
FilterPath f = (FilterPath) other;
return fileSystem == f.fileSystem && delegate.startsWith(f.delegate);
}
return false;
}
@Override
public boolean startsWith(String other) {
return delegate.startsWith(other);
}
@Override
public boolean endsWith(Path other) {
if (other instanceof FilterPath) {
FilterPath f = (FilterPath) other;
return fileSystem == f.fileSystem && delegate.endsWith(f.delegate);
}
return false;
}
@Override
public boolean endsWith(String other) {
return delegate.startsWith(other);
}
@Override
public Path normalize() {
return new FilterPath(delegate.normalize(), fileSystem);
}
@Override
public Path resolve(Path other) {
if (other instanceof FilterPath) {
other = ((FilterPath)other).delegate;
}
return new FilterPath(delegate.resolve(other), fileSystem);
}
@Override
public Path resolve(String other) {
return new FilterPath(delegate.resolve(other), fileSystem);
}
@Override
public Path resolveSibling(Path other) {
if (other instanceof FilterPath) {
other = ((FilterPath)other).delegate;
}
return new FilterPath(delegate.resolveSibling(other), fileSystem);
}
@Override
public Path resolveSibling(String other) {
return new FilterPath(delegate.resolveSibling(other), fileSystem);
}
@Override
public Path relativize(Path other) {
if (other instanceof FilterPath) {
other = ((FilterPath)other).delegate;
}
return new FilterPath(delegate.relativize(other), fileSystem);
}
// TODO: should these methods not expose delegate result directly?
// it could allow code to "escape" the sandbox...
@Override
public URI toUri() {
return delegate.toUri();
}
@Override
public String toString() {
return delegate.toString();
}
@Override
public Path toAbsolutePath() {
return new FilterPath(delegate.toAbsolutePath(), fileSystem);
}
@Override
public Path toRealPath(LinkOption... options) throws IOException {
return new FilterPath(delegate.toRealPath(options), fileSystem);
}
@Override
public File toFile() {
// TODO: should we throw exception here?
return delegate.toFile();
}
@Override
public WatchKey register(WatchService watcher, Kind<?>[] events, Modifier... modifiers) throws IOException {
return delegate.register(watcher, events, modifiers);
}
@Override
public WatchKey register(WatchService watcher, Kind<?>... events) throws IOException {
return delegate.register(watcher, events);
}
@Override
public Iterator<Path> iterator() {
final Iterator<Path> iterator = delegate.iterator();
return new Iterator<Path>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public Path next() {
return new FilterPath(iterator.next(), fileSystem);
}
};
}
@Override
public int compareTo(Path other) {
if (other instanceof FilterPath) {
other = ((FilterPath)other).delegate;
}
return delegate.compareTo(other);
}
/**
* Unwraps all {@code FilterPath}s, returning
* the innermost {@code Path}.
* <p>
* WARNING: this is exposed for testing only!
* @param path specified path.
* @return innermost Path instance
*/
public static Path unwrap(Path path) {
while (path instanceof FilterPath) {
path = ((FilterPath)path).delegate;
}
return path;
}
}

View File

@ -0,0 +1,98 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.SecureDirectoryStream;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileAttributeView;
import java.util.Iterator;
import java.util.Set;
/**
* A {@code FilterSecureDirectoryStream} contains another
* {@code SecureDirectoryStream}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
*/
public class FilterSecureDirectoryStream<T> implements SecureDirectoryStream<T> {
/**
* The underlying {@code SecureDirectoryStream} instance.
*/
protected final SecureDirectoryStream<T> delegate;
/**
* Construct a {@code FilterSecureDirectoryStream} based on
* the specified base stream.
* <p>
* Note that base stream is closed if this stream is closed.
* @param delegate specified base stream.
*/
public FilterSecureDirectoryStream(SecureDirectoryStream<T> delegate) {
this.delegate = delegate;
}
@Override
public Iterator<T> iterator() {
return delegate.iterator();
}
@Override
public void close() throws IOException {
delegate.close();
}
@Override
public SecureDirectoryStream<T> newDirectoryStream(T path, LinkOption... options) throws IOException {
return delegate.newDirectoryStream(path, options);
}
@Override
public SeekableByteChannel newByteChannel(T path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
return delegate.newByteChannel(path, options, attrs);
}
@Override
public void deleteFile(T path) throws IOException {
delegate.deleteFile(path);
}
@Override
public void deleteDirectory(T path) throws IOException {
delegate.deleteDirectory(path);
}
@Override
public void move(T srcpath, SecureDirectoryStream<T> targetdir, T targetpath) throws IOException {
delegate.move(srcpath, targetdir, targetpath);
}
@Override
public <V extends FileAttributeView> V getFileAttributeView(Class<V> type) {
return delegate.getFileAttributeView(type);
}
@Override
public <V extends FileAttributeView> V getFileAttributeView(T path, Class<V> type, LinkOption... options) {
return delegate.getFileAttributeView(path, type, options);
}
}

View File

@ -0,0 +1,89 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
/**
* A {@code FilterSeekableByteChannel} contains another
* {@code SeekableByteChannel}, which it uses as its basic
* source of data, possibly transforming the data along the
* way or providing additional functionality.
*/
public class FilterSeekableByteChannel implements SeekableByteChannel {
/**
* The underlying {@code SeekableByteChannel} instance.
*/
protected final SeekableByteChannel delegate;
/**
* Construct a {@code FilterSeekableByteChannel} based on
* the specified base channel.
* <p>
* Note that base channel is closed if this channel is closed.
* @param delegate specified base channel.
*/
public FilterSeekableByteChannel(SeekableByteChannel delegate) {
this.delegate = delegate;
}
@Override
public boolean isOpen() {
return delegate.isOpen();
}
@Override
public void close() throws IOException {
delegate.close();
}
@Override
public int read(ByteBuffer dst) throws IOException {
return delegate.read(dst);
}
@Override
public int write(ByteBuffer src) throws IOException {
return delegate.write(src);
}
@Override
public long position() throws IOException {
return delegate.position();
}
@Override
public SeekableByteChannel position(long newPosition) throws IOException {
delegate.position(newPosition);
return this;
}
@Override
public long size() throws IOException {
return delegate.size();
}
@Override
public SeekableByteChannel truncate(long size) throws IOException {
delegate.truncate(size);
return this;
}
}

View File

@ -0,0 +1,314 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.SecureDirectoryStream;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.attribute.FileAttribute;
import java.util.Set;
import java.util.concurrent.ExecutorService;
/**
* Base class for tracking file handles.
* <p>
* This class adds tracking to all streams/channels and
* provides two hooks to handle file management:
* <ul>
* <li>{@link #onOpen(Path, Object)}
* <li>{@link #onClose(Path, Object)}
* </ul>
*/
public abstract class HandleTrackingFS extends FilterFileSystemProvider {
/**
* Create a new instance, identified by {@code scheme} and passing
* through operations to {@code delegate}.
* @param scheme URI scheme for this provider
* @param delegate delegate filesystem to wrap.
*/
public HandleTrackingFS(String scheme, FileSystem delegate) {
super(scheme, delegate);
}
/**
* Called when {@code path} is opened via {@code stream}.
* @param path Path that was opened
* @param stream Stream or Channel opened against the path.
* @throws IOException if an I/O error occurs.
*/
protected abstract void onOpen(Path path, Object stream) throws IOException;
/**
* Called when {@code path} is closed via {@code stream}.
* @param path Path that was closed
* @param stream Stream or Channel closed against the path.
* @throws IOException if an I/O error occurs.
*/
protected abstract void onClose(Path path, Object stream) throws IOException;
@Override
public InputStream newInputStream(Path path, OpenOption... options) throws IOException {
InputStream stream = new FilterInputStream2(super.newInputStream(path, options)) {
@Override
public void close() throws IOException {
onClose(path, this);
super.close();
}
@Override
public String toString() {
return "InputStream(" + path.toString() + ")";
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
};
onOpen(path, stream);
return stream;
}
@Override
public OutputStream newOutputStream(final Path path, OpenOption... options) throws IOException {
OutputStream stream = new FilterOutputStream2(super.newOutputStream(path, options)) {
@Override
public void close() throws IOException {
onClose(path, this);
super.close();
}
@Override
public String toString() {
return "OutputStream(" + path.toString() + ")";
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
};
onOpen(path, stream);
return stream;
}
@Override
public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
FileChannel channel = new FilterFileChannel(super.newFileChannel(path, options, attrs)) {
@Override
protected void implCloseChannel() throws IOException {
onClose(path, this);
super.implCloseChannel();
}
@Override
public String toString() {
return "FileChannel(" + path.toString() + ")";
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
};
onOpen(path, channel);
return channel;
}
@Override
public AsynchronousFileChannel newAsynchronousFileChannel(Path path, Set<? extends OpenOption> options, ExecutorService executor, FileAttribute<?>... attrs) throws IOException {
AsynchronousFileChannel channel = new FilterAsynchronousFileChannel(super.newAsynchronousFileChannel(path, options, executor, attrs)) {
@Override
public void close() throws IOException {
onClose(path, this);
super.close();
}
@Override
public String toString() {
return "AsynchronousFileChannel(" + path.toString() + ")";
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
};
onOpen(path, channel);
return channel;
}
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
SeekableByteChannel channel = new FilterSeekableByteChannel(super.newByteChannel(path, options, attrs)) {
@Override
public void close() throws IOException {
onClose(path, this);
super.close();
}
@Override
public String toString() {
return "SeekableByteChannel(" + path.toString() + ")";
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
};
onOpen(path, channel);
return channel;
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException {
DirectoryStream<Path> stream = super.newDirectoryStream(dir, filter);
if (stream instanceof SecureDirectoryStream) {
stream = new TrackingSecureDirectoryStream((SecureDirectoryStream<Path>)stream, dir);
} else {
stream = new FilterDirectoryStream<Path>(stream) {
@Override
public void close() throws IOException {
onClose(dir, this);
super.close();
}
@Override
public String toString() {
return "DirectoryStream(" + dir + ")";
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
};
}
onOpen(dir, stream);
return stream;
}
/** You can also open various things from SecureDirectoryStream, so we ensure we track those */
class TrackingSecureDirectoryStream extends FilterSecureDirectoryStream<Path> {
final Path dir;
TrackingSecureDirectoryStream(SecureDirectoryStream<Path> delegate, Path dir) {
super(delegate);
this.dir = dir;
}
@Override
public void close() throws IOException {
onClose(dir, this);
super.close();
}
@Override
public String toString() {
return "SecureDirectoryStream(" + dir + ")";
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
@Override
public SecureDirectoryStream<Path> newDirectoryStream(Path path, LinkOption... options) throws IOException {
SecureDirectoryStream<Path> stream = new TrackingSecureDirectoryStream(super.newDirectoryStream(path, options), path);
onOpen(path, stream);
return stream;
}
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
SeekableByteChannel channel = new FilterSeekableByteChannel(super.newByteChannel(path, options, attrs)) {
@Override
public void close() throws IOException {
onClose(path, this);
super.close();
}
@Override
public String toString() {
return "SeekableByteChannel(" + path.toString() + ")";
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object obj) {
return this == obj;
}
};
onOpen(path, channel);
return channel;
}
}
}

View File

@ -0,0 +1,67 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* FileSystem that tracks open handles.
* <p>
* When {@link FileSystem#close()} is called, this class will throw
* an exception if any file handles are still open.
*/
public class LeakFS extends HandleTrackingFS {
// we explicitly use reference hashcode/equality in our keys
private final Map<Object,Exception> openHandles = new ConcurrentHashMap<>();
/**
* Create a new instance, tracking file handle leaks for the
* specified delegate filesystem.
* @param delegate delegate filesystem to wrap.
*/
public LeakFS(FileSystem delegate) {
super("leakfs://", delegate);
}
@Override
protected void onOpen(Path path, Object stream) {
openHandles.put(stream, new Exception());
}
@Override
protected void onClose(Path path, Object stream) {
openHandles.remove(stream);
}
@Override
public synchronized void onClose() {
if (!openHandles.isEmpty()) {
// print the first one as its very verbose otherwise
Exception cause = null;
Iterator<Exception> stacktraces = openHandles.values().iterator();
if (stacktraces.hasNext()) {
cause = stacktraces.next();
}
throw new RuntimeException("file handle leaks: " + openHandles.keySet(), cause);
}
}
}

View File

@ -0,0 +1,315 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.io.OutputStream;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryStream;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.FileSystem;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.SecureDirectoryStream;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import org.apache.lucene.util.IOUtils;
import org.apache.lucene.util.InfoStream;
/**
* FileSystem that records all major destructive filesystem activities.
*/
public class VerboseFS extends FilterFileSystemProvider {
final InfoStream infoStream;
final Path root;
/**
* Create a new instance, recording major filesystem write activities
* (create, delete, etc) to the specified {@code InfoStream}.
* @param delegate delegate filesystem to wrap.
* @param infoStream infoStream to send messages to. The component for
* messages is named "FS".
*/
public VerboseFS(FileSystem delegate, InfoStream infoStream) {
super("verbose://", delegate);
this.infoStream = infoStream;
this.root = this.getFileSystem(null).getPath(".").toAbsolutePath().normalize();
}
/** Records message, and rethrows exception if not null */
private void sop(String text, Throwable exception) throws IOException {
if (exception == null) {
if (infoStream.isEnabled("FS")) {
infoStream.message("FS", text);
}
} else {
if (infoStream.isEnabled("FS")) {
infoStream.message("FS", text + " (FAILED: " + exception + ")");
}
IOUtils.reThrow(exception);
}
}
private String path(Path path) {
path = root.relativize(path.toAbsolutePath().normalize());
return path.toString();
}
@Override
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
Throwable exception = null;
try {
super.createDirectory(dir, attrs);
} catch (Throwable t) {
exception = t;
} finally {
sop("createDirectory: " + path(dir), exception);
}
}
@Override
public void delete(Path path) throws IOException {
Throwable exception = null;
try {
super.delete(path);
} catch (Throwable t) {
exception = t;
} finally {
sop("delete: " + path(path), exception);
}
}
@Override
public void copy(Path source, Path target, CopyOption... options) throws IOException {
Throwable exception = null;
try {
super.copy(source, target, options);
} catch (Throwable t) {
exception = t;
} finally {
sop("copy" + Arrays.toString(options) + ": " + path(source) + " -> " + path(target), exception);
}
}
@Override
public void move(Path source, Path target, CopyOption... options) throws IOException {
Throwable exception = null;
try {
super.move(source, target, options);
} catch (Throwable t) {
exception = t;
} finally {
sop("move" + Arrays.toString(options) + ": " + path(source) + " -> " + path(target), exception);
}
}
@Override
public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException {
Throwable exception = null;
try {
super.setAttribute(path, attribute, value, options);
} catch (Throwable t) {
exception = t;
} finally {
sop("setAttribute[" + attribute + "=" + value + "]: " + path(path), exception);
}
}
@Override
public OutputStream newOutputStream(Path path, OpenOption... options) throws IOException {
Throwable exception = null;
try {
return super.newOutputStream(path, options);
} catch (Throwable t) {
exception = t;
} finally {
sop("newOutputStream" + Arrays.toString(options) + ": " + path(path), exception);
}
throw new AssertionError();
}
private boolean containsDestructive(Set<? extends OpenOption> options) {
return (options.contains(StandardOpenOption.APPEND) ||
options.contains(StandardOpenOption.WRITE) ||
options.contains(StandardOpenOption.DELETE_ON_CLOSE));
}
@Override
public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
Throwable exception = null;
try {
return super.newFileChannel(path, options, attrs);
} catch (Throwable t) {
exception = t;
} finally {
if (containsDestructive(options)) {
sop("newFileChannel" + options + ": " + path(path), exception);
}
}
throw new AssertionError();
}
@Override
public AsynchronousFileChannel newAsynchronousFileChannel(Path path, Set<? extends OpenOption> options, ExecutorService executor, FileAttribute<?>... attrs) throws IOException {
Throwable exception = null;
try {
return super.newAsynchronousFileChannel(path, options, executor, attrs);
} catch (Throwable t) {
exception = t;
} finally {
if (containsDestructive(options)) {
sop("newAsynchronousFileChannel" + options + ": " + path(path), exception);
}
}
throw new AssertionError();
}
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
Throwable exception = null;
try {
return super.newByteChannel(path, options, attrs);
} catch (Throwable t) {
exception = t;
} finally {
if (containsDestructive(options)) {
sop("newByteChannel" + options + ": " + path(path), exception);
}
}
throw new AssertionError();
}
@Override
public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws IOException {
Throwable exception = null;
try {
super.createSymbolicLink(link, target, attrs);
} catch (Throwable t) {
exception = t;
} finally {
sop("createSymbolicLink: " + path(link) + " -> " + path(target), exception);
}
}
@Override
public void createLink(Path link, Path existing) throws IOException {
Throwable exception = null;
try {
super.createLink(link, existing);
} catch (Throwable t) {
exception = t;
} finally {
sop("createLink: " + path(link) + " -> " + path(existing), exception);
}
}
@Override
public boolean deleteIfExists(Path path) throws IOException {
Throwable exception = null;
try {
return super.deleteIfExists(path);
} catch (Throwable t) {
exception = t;
} finally {
sop("deleteIfExists: " + path(path), exception);
}
throw new AssertionError();
}
@Override
public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException {
DirectoryStream<Path> stream = super.newDirectoryStream(dir, filter);
if (stream instanceof SecureDirectoryStream) {
stream = new VerboseSecureDirectoryStream((SecureDirectoryStream<Path>)stream, dir);
}
return stream;
}
class VerboseSecureDirectoryStream extends FilterSecureDirectoryStream<Path> {
final Path dir;
VerboseSecureDirectoryStream(SecureDirectoryStream<Path> delegate, Path dir) {
super(delegate);
this.dir = dir;
}
@Override
public SecureDirectoryStream<Path> newDirectoryStream(Path path, LinkOption... options) throws IOException {
return new VerboseSecureDirectoryStream(super.newDirectoryStream(path, options), path);
}
@Override
public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
Throwable exception = null;
try {
return super.newByteChannel(path, options, attrs);
} catch (Throwable t) {
exception = t;
} finally {
if (containsDestructive(options)) {
sop("newByteChannel[SECURE]" + options + ": " + path(path), exception);
}
}
throw new AssertionError();
}
@Override
public void deleteFile(Path path) throws IOException {
Throwable exception = null;
try {
super.deleteFile(path);
} catch (Throwable t) {
exception = t;
} finally {
sop("deleteFile[SECURE]: " + path(path), exception);
}
}
@Override
public void deleteDirectory(Path path) throws IOException {
Throwable exception = null;
try {
super.deleteDirectory(path);
} catch (Throwable t) {
exception = t;
} finally {
sop("deleteDirectory[SECURE]: " + path(path), exception);
}
}
@Override
public void move(Path srcpath, SecureDirectoryStream<Path> targetdir, Path targetpath) throws IOException {
Throwable exception = null;
try {
super.move(srcpath, targetdir, targetpath);
} catch (Throwable t) {
exception = t;
} finally {
sop("move[SECURE]: " + path(srcpath) + " -> " + path(targetpath), exception);
}
}
}
}

View File

@ -0,0 +1,129 @@
package org.apache.lucene.mockfile;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.nio.file.CopyOption;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
/**
* FileSystem that (imperfectly) acts like windows.
* <p>
* Currently this filesystem only prevents deletion of open files.
*/
public class WindowsFS extends HandleTrackingFS {
private final Map<Object,Integer> openFiles = new HashMap<>();
// TODO: try to make this as realistic as possible... it depends e.g. how you
// open files, if you map them, etc, if you can delete them (Uwe knows the rules)
// TODO: add case-insensitivity
/**
* Create a new instance, wrapping the delegate filesystem to
* act like Windows.
* @param delegate delegate filesystem to wrap.
*/
public WindowsFS(FileSystem delegate) {
super("windows://", delegate);
}
/**
* Returns file "key" (e.g. inode) for the specified path
*/
private Object getKey(Path existing) throws IOException {
BasicFileAttributeView view = Files.getFileAttributeView(existing, BasicFileAttributeView.class);
BasicFileAttributes attributes = view.readAttributes();
return attributes.fileKey();
}
@Override
protected void onOpen(Path path, Object stream) throws IOException {
Object key = getKey(path);
synchronized (openFiles) {
Integer v = openFiles.get(key);
if (v != null) {
v = Integer.valueOf(v.intValue()+1);
openFiles.put(key, v);
} else {
openFiles.put(key, Integer.valueOf(1));
}
}
}
@Override
protected void onClose(Path path, Object stream) throws IOException {
Object key = getKey(path);
synchronized (openFiles) {
Integer v = openFiles.get(key);
if (v != null) {
if (v.intValue() == 1) {
openFiles.remove(key);
} else {
v = Integer.valueOf(v.intValue()-1);
openFiles.put(key, v);
}
}
}
}
/**
* Checks that its ok to delete {@code Path}. If the file
* is still open, it throws IOException("access denied").
*/
private void checkDeleteAccess(Path path) throws IOException {
Object key = null;
try {
key = getKey(path);
} catch (Throwable ignore) {
// we don't care if the file doesn't exist
}
if (key != null) {
synchronized(openFiles) {
if (openFiles.containsKey(key)) {
throw new IOException("access denied: " + path);
}
}
}
}
@Override
public void delete(Path path) throws IOException {
checkDeleteAccess(path);
super.delete(path);
}
@Override
public void move(Path source, Path target, CopyOption... options) throws IOException {
checkDeleteAccess(source);
super.move(source, target, options);
}
@Override
public boolean deleteIfExists(Path path) throws IOException {
checkDeleteAccess(path);
return super.deleteIfExists(path);
}
}

View File

@ -0,0 +1,34 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
Support for testing/debugging with virtual filesystems
<p>
The primary classes are:
<ul>
<li>{@link org.apache.lucene.mockfile.LeakFS}: Fails tests if they leave open filehandles.
<li>{@link org.apache.lucene.mockfile.VerboseFS}: Prints destructive filesystem operations to infostream.
<li>{@link org.apache.lucene.mockfile.WindowsFS}: Acts like windows.
<li>{@link org.apache.lucene.mockfile.DisableFsyncFS}: Makes actual fsync calls a no-op.
</ul>
</p>
</body>
</html>

View File

@ -1,14 +1,21 @@
package org.apache.lucene.util;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import org.apache.lucene.mockfile.DisableFsyncFS;
import org.apache.lucene.mockfile.LeakFS;
import org.apache.lucene.mockfile.VerboseFS;
import org.apache.lucene.mockfile.WindowsFS;
import org.apache.lucene.util.LuceneTestCase.SuppressTempFileChecks;
import com.carrotsearch.randomizedtesting.RandomizedContext;
@ -52,6 +59,11 @@ final class TestRuleTemporaryFilesCleanup extends TestRuleAdapter {
* Per-test class temporary folder.
*/
private Path tempDirBase;
/**
* Per-test filesystem
*/
private FileSystem fileSystem;
/**
* Suite failure marker.
@ -90,11 +102,34 @@ final class TestRuleTemporaryFilesCleanup extends TestRuleAdapter {
super.before();
assert tempDirBase == null;
fileSystem = initializeFileSystem();
javaTempDir = initializeJavaTempDir();
}
private FileSystem initializeFileSystem() {
FileSystem fs = FileSystems.getDefault();
if (LuceneTestCase.VERBOSE) {
fs = new VerboseFS(fs, new TestRuleSetupAndRestoreClassEnv.ThreadNameFixingPrintStreamInfoStream(System.out)).getFileSystem(null);
}
Random random = RandomizedContext.current().getRandom();
// sometimes just use a bare filesystem
if (random.nextInt(10) > 0) {
fs = new DisableFsyncFS(fs).getFileSystem(null);
fs = new LeakFS(fs).getFileSystem(null);
// windows is currently slow
if (random.nextInt(10) == 0) {
fs = new WindowsFS(fs).getFileSystem(null);
}
}
if (LuceneTestCase.VERBOSE) {
System.out.println("filesystem: " + fs.provider());
}
return fs.provider().getFileSystem(URI.create("file:///"));
}
private Path initializeJavaTempDir() throws IOException {
Path javaTempDir = Paths.get(System.getProperty("tempDir", System.getProperty("java.io.tmpdir")));
Path javaTempDir = fileSystem.getPath(System.getProperty("tempDir", System.getProperty("java.io.tmpdir")));
Files.createDirectories(javaTempDir);
assert Files.isDirectory(javaTempDir) &&
@ -134,6 +169,9 @@ final class TestRuleTemporaryFilesCleanup extends TestRuleAdapter {
}
throw e;
}
if (fileSystem != FileSystems.getDefault()) {
fileSystem.close();
}
} else {
if (tempDirBasePath != null) {
System.err.println("NOTE: leaving temporary files on disk at: " + tempDirBasePath);
@ -171,7 +209,7 @@ final class TestRuleTemporaryFilesCleanup extends TestRuleAdapter {
}
return tempDirBase;
}
/**
* @see LuceneTestCase#createTempDir()
*/