aboutsummaryrefslogtreecommitdiff
path: root/jimfs/src/main/java/com/google/common/jimfs/Jimfs.java
diff options
context:
space:
mode:
Diffstat (limited to 'jimfs/src/main/java/com/google/common/jimfs/Jimfs.java')
-rw-r--r--jimfs/src/main/java/com/google/common/jimfs/Jimfs.java245
1 files changed, 245 insertions, 0 deletions
diff --git a/jimfs/src/main/java/com/google/common/jimfs/Jimfs.java b/jimfs/src/main/java/com/google/common/jimfs/Jimfs.java
new file mode 100644
index 0000000..a04ce46
--- /dev/null
+++ b/jimfs/src/main/java/com/google/common/jimfs/Jimfs.java
@@ -0,0 +1,245 @@
+/*
+ * 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.jimfs.SystemJimfsFileSystemProvider.FILE_SYSTEM_KEY;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.ProviderNotFoundException;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.UUID;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.checkerframework.checker.nullness.compatqual.NullableDecl;
+
+/**
+ * Static factory methods for creating new Jimfs file systems. File systems may either be created
+ * with a basic configuration matching the current operating system or by providing a specific
+ * {@link Configuration}. Basic {@linkplain Configuration#unix() UNIX}, {@linkplain
+ * Configuration#osX() Mac OS X} and {@linkplain Configuration#windows() Windows} configurations are
+ * provided.
+ *
+ * <p>Examples:
+ *
+ * <pre>
+ * // A file system with a configuration similar to the current OS
+ * FileSystem fileSystem = Jimfs.newFileSystem();
+ *
+ * // A file system with paths and behavior generally matching that of Windows
+ * FileSystem windows = Jimfs.newFileSystem(Configuration.windows()); </pre>
+ *
+ * <p>Additionally, various behavior of the file system can be customized by creating a custom
+ * {@link Configuration}. A modified version of one of the existing default configurations can be
+ * created using {@link Configuration#toBuilder()} or a new configuration can be created from
+ * scratch with {@link Configuration#builder(PathType)}. See {@link Configuration.Builder} for what
+ * can be configured.
+ *
+ * <p>Examples:
+ *
+ * <pre>
+ * // Modify the default UNIX configuration
+ * FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix()
+ * .toBuilder()
+ * .setAttributeViews("basic", "owner", "posix", "unix")
+ * .setWorkingDirectory("/home/user")
+ * .setBlockSize(4096)
+ * .build());
+ *
+ * // Create a custom configuration
+ * Configuration config = Configuration.builder(PathType.windows())
+ * .setRoots("C:\\", "D:\\", "E:\\")
+ * // ...
+ * .build(); </pre>
+ *
+ * @author Colin Decker
+ */
+public final class Jimfs {
+
+ /** The URI scheme for the Jimfs file system ("jimfs"). */
+ public static final String URI_SCHEME = "jimfs";
+
+ private static final Logger LOGGER = Logger.getLogger(Jimfs.class.getName());
+
+ private Jimfs() {}
+
+ /**
+ * Creates a new in-memory file system with a {@linkplain Configuration#forCurrentPlatform()
+ * default configuration} appropriate to the current operating system.
+ *
+ * <p>More specifically, if the operating system is Windows, {@link Configuration#windows()} is
+ * used; if the operating system is Mac OS X, {@link Configuration#osX()} is used; otherwise,
+ * {@link Configuration#unix()} is used.
+ */
+ public static FileSystem newFileSystem() {
+ return newFileSystem(newRandomFileSystemName());
+ }
+
+ /**
+ * Creates a new in-memory file system with a {@linkplain Configuration#forCurrentPlatform()
+ * default configuration} appropriate to the current operating system.
+ *
+ * <p>More specifically, if the operating system is Windows, {@link Configuration#windows()} is
+ * used; if the operating system is Mac OS X, {@link Configuration#osX()} is used; otherwise,
+ * {@link Configuration#unix()} is used.
+ *
+ * <p>The returned file system uses the given name as the host part of its URI and the URIs of
+ * paths in the file system. For example, given the name {@code my-file-system}, the file system's
+ * URI will be {@code jimfs://my-file-system} and the URI of the path {@code /foo/bar} will be
+ * {@code jimfs://my-file-system/foo/bar}.
+ */
+ public static FileSystem newFileSystem(String name) {
+ return newFileSystem(name, Configuration.forCurrentPlatform());
+ }
+
+ /** Creates a new in-memory file system with the given configuration. */
+ public static FileSystem newFileSystem(Configuration configuration) {
+ return newFileSystem(newRandomFileSystemName(), configuration);
+ }
+
+ /**
+ * Creates a new in-memory file system with the given configuration.
+ *
+ * <p>The returned file system uses the given name as the host part of its URI and the URIs of
+ * paths in the file system. For example, given the name {@code my-file-system}, the file system's
+ * URI will be {@code jimfs://my-file-system} and the URI of the path {@code /foo/bar} will be
+ * {@code jimfs://my-file-system/foo/bar}.
+ */
+ public static FileSystem newFileSystem(String name, Configuration configuration) {
+ try {
+ URI uri = new URI(URI_SCHEME, name, null, null);
+ return newFileSystem(uri, configuration);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @VisibleForTesting
+ static FileSystem newFileSystem(URI uri, Configuration config) {
+ checkArgument(
+ URI_SCHEME.equals(uri.getScheme()), "uri (%s) must have scheme %s", uri, URI_SCHEME);
+
+ try {
+ // Create the FileSystem. It uses JimfsFileSystemProvider as its provider, as that is
+ // the provider that actually implements the operations needed for Files methods to work.
+ JimfsFileSystem fileSystem =
+ JimfsFileSystems.newFileSystem(JimfsFileSystemProvider.instance(), uri, config);
+
+ /*
+ * Now, call FileSystems.newFileSystem, passing it the FileSystem we just created. This
+ * allows the system-loaded SystemJimfsFileSystemProvider instance to cache the FileSystem
+ * so that methods like Paths.get(URI) work.
+ * We do it in this awkward way to avoid issues when the classes in the API (this class
+ * and Configuration, for example) are loaded by a different classloader than the one that
+ * loads SystemJimfsFileSystemProvider using ServiceLoader. See
+ * https://github.com/google/jimfs/issues/18 for gory details.
+ */
+ try {
+ ImmutableMap<String, ?> env = ImmutableMap.of(FILE_SYSTEM_KEY, fileSystem);
+ FileSystems.newFileSystem(uri, env, SystemJimfsFileSystemProvider.class.getClassLoader());
+ } catch (ProviderNotFoundException | ServiceConfigurationError ignore) {
+ // See the similar catch block below for why we ignore this.
+ // We log there rather than here so that there's only typically one such message per VM.
+ }
+
+ return fileSystem;
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * The system-loaded instance of {@code SystemJimfsFileSystemProvider}, or {@code null} if it
+ * could not be found or loaded.
+ */
+ @NullableDecl static final FileSystemProvider systemProvider = getSystemJimfsProvider();
+
+ /**
+ * Returns the system-loaded instance of {@code SystemJimfsFileSystemProvider} or {@code null} if
+ * it could not be found or loaded.
+ *
+ * <p>Like {@link FileSystems#newFileSystem(URI, Map, ClassLoader)}, this method first looks in
+ * the list of {@linkplain FileSystemProvider#installedProviders() installed providers} and if not
+ * found there, attempts to load it from the {@code ClassLoader} with {@link ServiceLoader}.
+ *
+ * <p>The idea is that this method should return an instance of the same class (i.e. loaded by the
+ * same class loader) as the class whose static cache a {@code JimfsFileSystem} instance will be
+ * placed in when {@code FileSystems.newFileSystem} is called in {@code Jimfs.newFileSystem}.
+ */
+ @NullableDecl
+ private static FileSystemProvider getSystemJimfsProvider() {
+ try {
+ for (FileSystemProvider provider : FileSystemProvider.installedProviders()) {
+ if (provider.getScheme().equals(URI_SCHEME)) {
+ return provider;
+ }
+ }
+
+ /*
+ * Jimfs.newFileSystem passes SystemJimfsFileSystemProvider.class.getClassLoader() to
+ * FileSystems.newFileSystem so that it will fall back to loading from that classloader if
+ * the provider isn't found in the installed providers. So do the same fallback here to ensure
+ * that we can remove file systems from the static cache on SystemJimfsFileSystemProvider if
+ * it gets loaded that way.
+ */
+ ServiceLoader<FileSystemProvider> loader =
+ ServiceLoader.load(
+ FileSystemProvider.class, SystemJimfsFileSystemProvider.class.getClassLoader());
+ for (FileSystemProvider provider : loader) {
+ if (provider.getScheme().equals(URI_SCHEME)) {
+ return provider;
+ }
+ }
+ } catch (ProviderNotFoundException | ServiceConfigurationError e) {
+ /*
+ * This can apparently (https://github.com/google/jimfs/issues/31) occur in an environment
+ * where services are not loaded from META-INF/services, such as JBoss/Wildfly. In this
+ * case, FileSystems.newFileSystem will most likely fail in the same way when called from
+ * Jimfs.newFileSystem above, and there will be no way to make URI-based methods like
+ * Paths.get(URI) work. Rather than making the user completly unable to use Jimfs, just
+ * log this exception and continue.
+ *
+ * Note: Catching both ProviderNotFoundException, which would occur if no provider matching
+ * the "jimfs" URI scheme is found, and ServiceConfigurationError, which can occur if the
+ * ServiceLoader finds the META-INF/services entry for Jimfs (or some other
+ * FileSystemProvider!) but is then unable to load that class.
+ */
+ LOGGER.log(
+ Level.INFO,
+ "An exception occurred when attempting to find the system-loaded FileSystemProvider "
+ + "for Jimfs. This likely means that your environment does not support loading "
+ + "services via ServiceLoader or is not configured correctly. This does not prevent "
+ + "using Jimfs, but it will mean that methods that look up via URI such as "
+ + "Paths.get(URI) cannot work.",
+ e);
+ }
+
+ return null;
+ }
+
+ private static String newRandomFileSystemName() {
+ return UUID.randomUUID().toString();
+ }
+}