aboutsummaryrefslogtreecommitdiff
path: root/jimfs/src/main/java/com/google/common/jimfs/SystemJimfsFileSystemProvider.java
diff options
context:
space:
mode:
Diffstat (limited to 'jimfs/src/main/java/com/google/common/jimfs/SystemJimfsFileSystemProvider.java')
-rw-r--r--jimfs/src/main/java/com/google/common/jimfs/SystemJimfsFileSystemProvider.java277
1 files changed, 277 insertions, 0 deletions
diff --git a/jimfs/src/main/java/com/google/common/jimfs/SystemJimfsFileSystemProvider.java b/jimfs/src/main/java/com/google/common/jimfs/SystemJimfsFileSystemProvider.java
new file mode 100644
index 0000000..dcf3d02
--- /dev/null
+++ b/jimfs/src/main/java/com/google/common/jimfs/SystemJimfsFileSystemProvider.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed 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 com.google.common.jimfs;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static com.google.common.jimfs.Jimfs.URI_SCHEME;
+
+import com.google.auto.service.AutoService;
+import com.google.common.collect.MapMaker;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.AccessMode;
+import java.nio.file.CopyOption;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemAlreadyExistsException;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.FileSystems;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+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.Set;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * {@link FileSystemProvider} implementation for Jimfs that is loaded by the system as a service.
+ * This implementation only serves as a cache for file system instances and does not implement
+ * actual file system operations.
+ *
+ * <p>While this class is public, it should not be used directly. To create a new file system
+ * instance, see {@link Jimfs}. For other operations, use the public APIs in {@code java.nio.file}.
+ *
+ * @author Colin Decker
+ * @since 1.1
+ */
+@AutoService(FileSystemProvider.class)
+public final class SystemJimfsFileSystemProvider extends FileSystemProvider {
+
+ /**
+ * Env map key that maps to the already-created {@code FileSystem} instance in {@code
+ * newFileSystem}.
+ */
+ static final String FILE_SYSTEM_KEY = "fileSystem";
+
+ /**
+ * Cache of file systems that have been created but not closed.
+ *
+ * <p>This cache is static to ensure that even when this provider isn't loaded by the system class
+ * loader, meaning that a new instance of it must be created each time one of the methods on
+ * {@link FileSystems} or {@link Paths#get(URI)} is called, cached file system instances are still
+ * available.
+ *
+ * <p>The cache uses weak values so that it doesn't prevent file systems that are created but not
+ * closed from being garbage collected if no references to them are held elsewhere. This is a
+ * compromise between ensuring that any file URI continues to work as long as the file system
+ * hasn't been closed (which is technically the correct thing to do but unlikely to be something
+ * that most users care about) and ensuring that users don't get unexpected leaks of large amounts
+ * of memory because they're creating many file systems in tests but forgetting to close them
+ * (which seems likely to happen sometimes). Users that want to ensure that a file system won't be
+ * garbage collected just need to ensure they hold a reference to it somewhere for as long as they
+ * need it to stick around.
+ */
+ private static final ConcurrentMap<URI, FileSystem> fileSystems =
+ new MapMaker().weakValues().makeMap();
+
+ /** @deprecated Not intended to be called directly; this class is only for use by Java itself. */
+ @Deprecated
+ public SystemJimfsFileSystemProvider() {} // a public, no-arg constructor is required
+
+ @Override
+ public String getScheme() {
+ return URI_SCHEME;
+ }
+
+ @Override
+ public FileSystem newFileSystem(URI uri, Map<String, ?> env) throws IOException {
+ checkArgument(
+ uri.getScheme().equalsIgnoreCase(URI_SCHEME),
+ "uri (%s) scheme must be '%s'",
+ uri,
+ URI_SCHEME);
+ checkArgument(
+ isValidFileSystemUri(uri), "uri (%s) may not have a path, query or fragment", uri);
+ checkArgument(
+ env.get(FILE_SYSTEM_KEY) instanceof FileSystem,
+ "env map (%s) must contain key '%s' mapped to an instance of %s",
+ env,
+ FILE_SYSTEM_KEY,
+ FileSystem.class);
+
+ FileSystem fileSystem = (FileSystem) env.get(FILE_SYSTEM_KEY);
+ if (fileSystems.putIfAbsent(uri, fileSystem) != null) {
+ throw new FileSystemAlreadyExistsException(uri.toString());
+ }
+ return fileSystem;
+ }
+
+ @Override
+ public FileSystem getFileSystem(URI uri) {
+ FileSystem fileSystem = fileSystems.get(uri);
+ if (fileSystem == null) {
+ throw new FileSystemNotFoundException(uri.toString());
+ }
+ return fileSystem;
+ }
+
+ @Override
+ public Path getPath(URI uri) {
+ checkArgument(
+ URI_SCHEME.equalsIgnoreCase(uri.getScheme()),
+ "uri scheme does not match this provider: %s",
+ uri);
+
+ String path = uri.getPath();
+ checkArgument(!isNullOrEmpty(path), "uri must have a path: %s", uri);
+
+ return toPath(getFileSystem(toFileSystemUri(uri)), uri);
+ }
+
+ /**
+ * Returns whether or not the given URI is valid as a base file system URI. It must not have a
+ * path, query or fragment.
+ */
+ private static boolean isValidFileSystemUri(URI uri) {
+ // would like to just check null, but fragment appears to be the empty string when not present
+ return isNullOrEmpty(uri.getPath())
+ && isNullOrEmpty(uri.getQuery())
+ && isNullOrEmpty(uri.getFragment());
+ }
+
+ /** Returns the given URI with any path, query or fragment stripped off. */
+ private static URI toFileSystemUri(URI uri) {
+ try {
+ return new URI(
+ uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
+ } catch (URISyntaxException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /** Invokes the {@code toPath(URI)} method on the given {@code FileSystem}. */
+ private static Path toPath(FileSystem fileSystem, URI uri) {
+ // We have to invoke this method by reflection because while the file system should be
+ // an instance of JimfsFileSystem, it may be loaded by a different class loader and as
+ // such appear to be a totally different class.
+ try {
+ Method toPath = fileSystem.getClass().getDeclaredMethod("toPath", URI.class);
+ return (Path) toPath.invoke(fileSystem, uri);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("invalid file system: " + fileSystem);
+ } catch (InvocationTargetException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public FileSystem newFileSystem(Path path, Map<String, ?> env) throws IOException {
+ FileSystemProvider realProvider = path.getFileSystem().provider();
+ return realProvider.newFileSystem(path, env);
+ }
+
+ /**
+ * Returns a runnable that, when run, removes the file system with the given URI from this
+ * provider.
+ */
+ @SuppressWarnings("unused") // called via reflection
+ public static Runnable removeFileSystemRunnable(final URI uri) {
+ return new Runnable() {
+ @Override
+ public void run() {
+ fileSystems.remove(uri);
+ }
+ };
+ }
+
+ @Override
+ public SeekableByteChannel newByteChannel(
+ Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public DirectoryStream<Path> newDirectoryStream(
+ Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void delete(Path path) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void copy(Path source, Path target, CopyOption... options) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void move(Path source, Path target, CopyOption... options) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isSameFile(Path path, Path path2) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isHidden(Path path) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public FileStore getFileStore(Path path) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void checkAccess(Path path, AccessMode... modes) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <V extends FileAttributeView> V getFileAttributeView(
+ Path path, Class<V> type, LinkOption... options) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <A extends BasicFileAttributes> A readAttributes(
+ Path path, Class<A> type, LinkOption... options) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<String, Object> readAttributes(Path path, String attributes, LinkOption... options)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setAttribute(Path path, String attribute, Object value, LinkOption... options)
+ throws IOException {
+ throw new UnsupportedOperationException();
+ }
+}