| /* |
| * 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. |
| */ |
| package org.apache.lucene.mockfile; |
| |
| import java.io.Closeable; |
| 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.OpenOption; |
| import java.nio.file.Path; |
| import java.nio.file.DirectoryStream.Filter; |
| import java.nio.file.attribute.FileAttribute; |
| import java.util.Set; |
| import java.util.concurrent.ExecutorService; |
| |
| import org.apache.lucene.util.IOUtils; |
| |
| /** |
| * 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; |
| |
| /** |
| * Helper method, to deal with onOpen() throwing exception |
| */ |
| final void callOpenHook(Path path, Closeable stream) throws IOException { |
| boolean success = false; |
| try { |
| onOpen(path, stream); |
| success = true; |
| } finally { |
| if (!success) { |
| IOUtils.closeWhileHandlingException(stream); |
| } |
| } |
| } |
| |
| @Override |
| public InputStream newInputStream(Path path, OpenOption... options) throws IOException { |
| InputStream stream = new FilterInputStream2(super.newInputStream(path, options)) { |
| |
| boolean closed; |
| |
| @Override |
| public void close() throws IOException { |
| try { |
| if (!closed) { |
| closed = true; |
| onClose(path, this); |
| } |
| } finally { |
| 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; |
| } |
| }; |
| callOpenHook(path, stream); |
| return stream; |
| } |
| |
| @Override |
| public OutputStream newOutputStream(final Path path, OpenOption... options) throws IOException { |
| OutputStream stream = new FilterOutputStream2(delegate.newOutputStream(toDelegate(path), options)) { |
| |
| boolean closed; |
| |
| @Override |
| public void close() throws IOException { |
| try { |
| if (!closed) { |
| closed = true; |
| onClose(path, this); |
| } |
| } finally { |
| 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; |
| } |
| }; |
| callOpenHook(path, stream); |
| return stream; |
| } |
| |
| @Override |
| public FileChannel newFileChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException { |
| FileChannel channel = new FilterFileChannel(delegate.newFileChannel(toDelegate(path), options, attrs)) { |
| |
| boolean closed; |
| |
| @Override |
| protected void implCloseChannel() throws IOException { |
| if (!closed) { |
| closed = true; |
| try { |
| onClose(path, this); |
| } finally { |
| 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; |
| } |
| }; |
| callOpenHook(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)) { |
| |
| boolean closed; |
| |
| @Override |
| public void close() throws IOException { |
| try { |
| if (!closed) { |
| closed = true; |
| onClose(path, this); |
| } |
| } finally { |
| 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; |
| } |
| }; |
| callOpenHook(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)) { |
| |
| boolean closed; |
| |
| @Override |
| public void close() throws IOException { |
| try { |
| if (!closed) { |
| closed = true; |
| onClose(path, this); |
| } |
| } finally { |
| 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; |
| } |
| }; |
| callOpenHook(path, channel); |
| return channel; |
| } |
| |
| @Override |
| public DirectoryStream<Path> newDirectoryStream(Path dir, Filter<? super Path> filter) throws IOException { |
| Filter<Path> wrappedFilter = new Filter<Path>() { |
| @Override |
| public boolean accept(Path entry) throws IOException { |
| return filter.accept(new FilterPath(entry, fileSystem)); |
| } |
| }; |
| DirectoryStream<Path> stream = delegate.newDirectoryStream(toDelegate(dir), wrappedFilter); |
| stream = new FilterDirectoryStream(stream, fileSystem) { |
| |
| boolean closed; |
| |
| @Override |
| public void close() throws IOException { |
| try { |
| if (!closed) { |
| closed = true; |
| onClose(dir, this); |
| } |
| } finally { |
| 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; |
| } |
| }; |
| callOpenHook(dir, stream); |
| return stream; |
| } |
| } |