diff options
Diffstat (limited to 'jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystem.java')
-rw-r--r-- | jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystem.java | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystem.java b/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystem.java new file mode 100644 index 0000000..dd72146 --- /dev/null +++ b/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystem.java @@ -0,0 +1,337 @@ +/* + * 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.checkNotNull; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import java.io.Closeable; +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.WatchService; +import java.nio.file.attribute.UserPrincipalLookupService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.checkerframework.checker.nullness.compatqual.NullableDecl; + +/** + * {@link FileSystem} implementation for Jimfs. Most behavior for the file system is implemented by + * its {@linkplain #getDefaultView() default file system view}. + * + * <h3>Overview of file system design</h3> + * + * {@link com.google.common.jimfs.JimfsFileSystem JimfsFileSystem} instances are created by {@link + * com.google.common.jimfs.JimfsFileSystems JimfsFileSystems} using a user-provided {@link + * com.google.common.jimfs.Configuration Configuration}. The configuration is used to create the + * various classes that implement the file system with the correct settings and to create the file + * system root directories and working directory. The file system is then used to create the {@code + * Path} objects that all file system operations use. + * + * <p>Once created, the primary entry points to the file system are {@link + * com.google.common.jimfs.JimfsFileSystemProvider JimfsFileSystemProvider}, which handles calls to + * methods in {@link java.nio.file.Files}, and {@link + * com.google.common.jimfs.JimfsSecureDirectoryStream JimfsSecureDirectoryStream}, which provides + * methods that are similar to those of the file system provider but which treat relative paths as + * relative to the stream's directory rather than the file system's working directory. + * + * <p>The implementation of the methods on both of those classes is handled by the {@link + * com.google.common.jimfs.FileSystemView FileSystemView} class, which acts as a view of the file + * system with a specific working directory. The file system provider uses the file system's default + * view, while each secure directory stream uses a view specific to that stream. + * + * <p>File system views make use of the file system's singleton {@link + * com.google.common.jimfs.JimfsFileStore JimfsFileStore} which handles file creation, storage and + * attributes. The file store delegates to several other classes to handle each of these: + * + * <ul> + * <li>{@link com.google.common.jimfs.FileFactory FileFactory} handles creation of new file + * objects. + * <li>{@link com.google.common.jimfs.HeapDisk HeapDisk} handles allocation of blocks to {@link + * RegularFile RegularFile} instances. + * <li>{@link com.google.common.jimfs.FileTree FileTree} stores the root of the file hierarchy and + * handles file lookup. + * <li>{@link com.google.common.jimfs.AttributeService AttributeService} handles file attributes, + * using a set of {@link com.google.common.jimfs.AttributeProvider AttributeProvider} + * implementations to handle each supported file attribute view. + * </ul> + * + * <h3>Paths</h3> + * + * The implementation of {@link java.nio.file.Path} for the file system is {@link + * com.google.common.jimfs.JimfsPath JimfsPath}. Paths are created by a {@link + * com.google.common.jimfs.PathService PathService} with help from the file system's configured + * {@link com.google.common.jimfs.PathType PathType}. + * + * <p>Paths are made up of {@link com.google.common.jimfs.Name Name} objects, which also serve as + * the file names in directories. A name has two forms: + * + * <ul> + * <li>The <b>display form</b> is used in {@code Path} for {@code toString()}. It is also used for + * determining the equality and sort order of {@code Path} objects for most file systems. + * <li>The <b>canonical form</b> is used for equality of two {@code Name} objects. This affects + * the notion of name equality in the file system itself for file lookup. A file system may be + * configured to use the canonical form of the name for path equality (a Windows-like file + * system configuration does this, as the real Windows file system implementation uses + * case-insensitive equality for its path objects. + * </ul> + * + * <p>The canonical form of a name is created by applying a series of {@linkplain PathNormalization + * normalizations} to the original string. These normalization may be either a Unicode normalization + * (e.g. NFD) or case folding normalization for case-insensitivity. Normalizations may also be + * applied to the display form of a name, but this is currently only done for a Mac OS X type + * configuration. + * + * <h3>Files</h3> + * + * All files in the file system are an instance of {@link com.google.common.jimfs.File File}. A file + * object contains both the file's attributes and content. + * + * <p>There are three types of files: + * + * <ul> + * <li>{@link Directory Directory} - contains a table linking file names to {@linkplain + * com.google.common.jimfs.DirectoryEntry directory entries}. + * <li>{@link RegularFile RegularFile} - an in-memory store for raw bytes. + * <li>{@link com.google.common.jimfs.SymbolicLink SymbolicLink} - contains a path. + * </ul> + * + * <p>{@link com.google.common.jimfs.JimfsFileChannel JimfsFileChannel}, {@link + * com.google.common.jimfs.JimfsInputStream JimfsInputStream} and {@link + * com.google.common.jimfs.JimfsOutputStream JimfsOutputStream} implement the standard + * channel/stream APIs for regular files. + * + * <p>{@link com.google.common.jimfs.JimfsSecureDirectoryStream JimfsSecureDirectoryStream} handles + * reading the entries of a directory. The secure directory stream additionally contains a {@code + * FileSystemView} with its directory as the working directory, allowing for operations relative to + * the actual directory file rather than just the path to the file. This allows the operations to + * continue to work as expected even if the directory is moved. + * + * <p>A directory can be watched for changes using the {@link java.nio.file.WatchService} + * implementation, {@link com.google.common.jimfs.PollingWatchService PollingWatchService}. + * + * <h3>Regular files</h3> + * + * {@link RegularFile RegularFile} makes use of a singleton {@link com.google.common.jimfs.HeapDisk + * HeapDisk}. A disk is a resizable factory and cache for fixed size blocks of memory. These blocks + * are allocated to files as needed and returned to the disk when a file is deleted or truncated. + * When cached free blocks are available, those blocks are allocated to files first. If more blocks + * are needed, they are created. + * + * <h3>Linking</h3> + * + * When a file is mapped to a file name in a directory table, it is <i>linked</i>. Each type of file + * has different rules governing how it is linked. + * + * <ul> + * <li>Directory - A directory has two or more links to it. The first is the link from its parent + * directory to it. This link is the name of the directory. The second is the <i>self</i> link + * (".") which links the directory to itself. The directory may also have any number of + * additional <i>parent</i> links ("..") from child directories back to it. + * <li>Regular file - A regular file has one link from its parent directory by default. However, + * regular files are also allowed to have any number of additional user-created hard links, + * from the same directory with different names and/or from other directories with any names. + * <li>Symbolic link - A symbolic link can only have one link, from its parent directory. + * </ul> + * + * <h3>Thread safety</h3> + * + * All file system operations should be safe in a multithreaded environment. The file hierarchy + * itself is protected by a file system level read-write lock. This ensures safety of all + * modifications to directory tables as well as atomicity of operations like file moves. Regular + * files are each protected by a read-write lock which is obtained for each read or write operation. + * File attributes are protected by synchronization on the file object itself. + * + * @author Colin Decker + */ +final class JimfsFileSystem extends FileSystem { + + private final JimfsFileSystemProvider provider; + private final URI uri; + + private final JimfsFileStore fileStore; + private final PathService pathService; + + private final UserPrincipalLookupService userLookupService = new UserLookupService(true); + + private final FileSystemView defaultView; + + private final WatchServiceConfiguration watchServiceConfig; + + JimfsFileSystem( + JimfsFileSystemProvider provider, + URI uri, + JimfsFileStore fileStore, + PathService pathService, + FileSystemView defaultView, + WatchServiceConfiguration watchServiceConfig) { + this.provider = checkNotNull(provider); + this.uri = checkNotNull(uri); + this.fileStore = checkNotNull(fileStore); + this.pathService = checkNotNull(pathService); + this.defaultView = checkNotNull(defaultView); + this.watchServiceConfig = checkNotNull(watchServiceConfig); + } + + @Override + public JimfsFileSystemProvider provider() { + return provider; + } + + /** Returns the URI for this file system. */ + public URI getUri() { + return uri; + } + + /** Returns the default view for this file system. */ + public FileSystemView getDefaultView() { + return defaultView; + } + + @Override + public String getSeparator() { + return pathService.getSeparator(); + } + + @SuppressWarnings("unchecked") // safe cast of immutable set + @Override + public ImmutableSortedSet<Path> getRootDirectories() { + ImmutableSortedSet.Builder<JimfsPath> builder = ImmutableSortedSet.orderedBy(pathService); + for (Name name : fileStore.getRootDirectoryNames()) { + builder.add(pathService.createRoot(name)); + } + return (ImmutableSortedSet<Path>) (ImmutableSortedSet<?>) builder.build(); + } + + /** Returns the working directory path for this file system. */ + public JimfsPath getWorkingDirectory() { + return defaultView.getWorkingDirectoryPath(); + } + + /** Returns the path service for this file system. */ + @VisibleForTesting + PathService getPathService() { + return pathService; + } + + /** Returns the file store for this file system. */ + public JimfsFileStore getFileStore() { + return fileStore; + } + + @Override + public ImmutableSet<FileStore> getFileStores() { + fileStore.state().checkOpen(); + return ImmutableSet.<FileStore>of(fileStore); + } + + @Override + public ImmutableSet<String> supportedFileAttributeViews() { + return fileStore.supportedFileAttributeViews(); + } + + @Override + public JimfsPath getPath(String first, String... more) { + fileStore.state().checkOpen(); + return pathService.parsePath(first, more); + } + + /** Gets the URI of the given path in this file system. */ + public URI toUri(JimfsPath path) { + fileStore.state().checkOpen(); + return pathService.toUri(uri, path.toAbsolutePath()); + } + + /** Converts the given URI into a path in this file system. */ + public JimfsPath toPath(URI uri) { + fileStore.state().checkOpen(); + return pathService.fromUri(uri); + } + + @Override + public PathMatcher getPathMatcher(String syntaxAndPattern) { + fileStore.state().checkOpen(); + return pathService.createPathMatcher(syntaxAndPattern); + } + + @Override + public UserPrincipalLookupService getUserPrincipalLookupService() { + fileStore.state().checkOpen(); + return userLookupService; + } + + @Override + public WatchService newWatchService() throws IOException { + return watchServiceConfig.newWatchService(defaultView, pathService); + } + + @NullableDecl private ExecutorService defaultThreadPool; + + /** + * Returns a default thread pool to use for asynchronous file channels when users do not provide + * an executor themselves. (This is required by the spec of newAsynchronousFileChannel in + * FileSystemProvider.) + */ + public synchronized ExecutorService getDefaultThreadPool() { + if (defaultThreadPool == null) { + defaultThreadPool = + Executors.newCachedThreadPool( + new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat("JimfsFileSystem-" + uri.getHost() + "-defaultThreadPool-%s") + .build()); + + // ensure thread pool is closed when file system is closed + fileStore + .state() + .register( + new Closeable() { + @Override + public void close() { + defaultThreadPool.shutdown(); + } + }); + } + return defaultThreadPool; + } + + /** + * Returns {@code false}; currently, cannot create a read-only file system. + * + * @return {@code false}, always + */ + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public boolean isOpen() { + return fileStore.state().isOpen(); + } + + @Override + public void close() throws IOException { + fileStore.state().close(); + } +} |