mirror of https://github.com/apache/lucene.git
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:
parent
327d10e153
commit
7dbc2ac63a
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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">
|
||||
|
|
|
@ -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 {}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 + ")";
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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()
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue