An in memory implementation of a JSR-203 (Java 7) file system for testing purposes.
<dependency>
<groupId>com.github.marschall</groupId>
<artifactId>memoryfilesystem</artifactId>
<version>0.7.3</version>
</dependency>
SeekableByteChannel
FileChannel
AsynchronousFileChannel
InputStream
OutputStream
BasicFileAttributeView
,BasicFileAttributes
DosFileAttributeView
,DosFileAttributes
PosixFileAttributeView
,PosixFileAttributes
UserDefinedFileAttributeView
FileLock
DirectoryStream
PathMatcher
- glob
- regex
StandardCopyOption
- REPLACE_EXISTING
- COPY_ATTRIBUTES
- ATOMIC_MOVE
StandardOpenOption
- READ
- WRITE
- TRUNCATE_EXISTING
- CREATE
- DELETE_ON_CLOSE
- symbolic links
- symbolic link loop detection
- switching the current user
- switching the current group
- DOS access checks
- POSIX access checks
- umask
FileChannel#map
,MappedByteBuffer
has final methods that call native methodsSecureDirectoryStream
WatchService
FileTypeDetector
, has to be accessible by system classloader- faked DOS attribute view under Linux, totally unspecified
UnixFileAttributeView
, sun package, totally unspecifiedAclFileAttributeView
- files larger than 16MB
StandardOpenOption
- SPARSE
- SYNC
- DSYNC
- hard links
URL
interoperability, needs a customURLStreamHandler
which ins't very nice. That means you can't for example create anURLClassLoader
on a memory file system. However if you really want to create aClassLoader
on a memory file system you can use path-classloader which is completely portable across Java 7 file systems.- maximum path length checks
Quite likely.
MIT
Yes, but hasn't been subject much scrutiny so bugs are likely.
Not with the one that ships with the JDK 7 because of bug 8004789. However there's a repackaged version that fixes this bug and is compatible. It should work fine in JDK 8.
No, it's only intended for testing purposes.
No
No
Yes, there is a POJO factory bean. It has been tested with Spring 3.2.4 but since it doesn't have any dependencies on Spring it should work with every ⩾ 2.x version. You can of course also use Java configuration or any other IoC container.
Yes, it's a bundle and there's an activator that prevents class loader leaks. You should use the MemoryFileSystemBuilder
instead of FileSystems#newFileSystem
because ServiceLoader
uses the thread context class loader. MemoryFileSystemBuilder
avoids this by passing in the correct class loader.
No
A logging file system that wraps an other file system is the best way to do this.
Use CurrentUser#useDuring
Use CurrentGroup#useDuring
Yes, google/jimfs and openCage/memoryfs seem similar.
ShrinkWrap NIO.2 seems to be mainly targeted at interacting with a ShrinkWrap archive instead of simulating a file system.
It should work on compact1. Below is the jdeps output. The only Full JRE package we depend on is javax.annotation and that is only for documentation purposes (@PreDestroy). If that annotation is not present everything should still work.
com.github.marschall.memoryfilesystem (classes)
-> java.io compact1
-> java.lang compact1
-> java.lang.reflect compact1
-> java.net compact1
-> java.nio compact1
-> java.nio.channels compact1
-> java.nio.file compact1
-> java.nio.file.attribute compact1
-> java.nio.file.spi compact1
-> java.text compact1
-> java.util compact1
-> java.util.concurrent compact1
-> java.util.concurrent.atomic compact1
-> java.util.concurrent.locks compact1
-> java.util.regex compact1
-> javax.annotation Full JRE
The easiest way to get started is to use the MemoryFileSystemBuilder
try (FileSystem fileSystem = MemoryFileSystemBuilder.newEmpty().build("test")) {
Path p = fileSystem.getPath("p");
System.out.println(Files.exists(p));
}
It's important to know that at any given time there can only be one memory file system with a given name. Any attempt to create a memory file system with the name of an existing one will throw an exception.
There are other new
methods on MemoryFileSystemBuilder
that allow you to create different file systems and other methods that allow you to customize the file system.
You probably want to create a JUnit TestRule
that sets up and tears down a file system for you. A rule can look like this
final class FileSystemRule implements TestRule {
private FileSystem fileSystem;
FileSystem getFileSystem() {
return this.fileSystem;
}
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
fileSystem = MemoryFileSystemBuilder.newEmpty().build("name");
try {
base.evaluate();
} finally {
fileSystem.close();
}
}
};
}
}
and is used like this
public class FileSystemTest {
@Rule
public final FileSystemRule rule = new FileSystemRule();
@Test
public void lockAsyncChannel() throws IOException {
FileSystem fileSystem = this.rule.getFileSystem();
Path path = fileSystem.getPath("sample.txt");
assertFalse(Files.exists(path));
}
}
It's important to note that the field holding the rule must be public. If you're using an IoC container for integration tests check out the section below.
The com.github.marschall.memoryfilesystem.MemoryFileSystemFactoryBean
provides integration with Spring.
<bean id="memoryFileSystemFactory"
class="com.github.marschall.memoryfilesystem.MemoryFileSystemFactoryBean"/>
<bean id="memoryFileSystem" destroy-method="close"
factory-bean="memoryFileSystemFactory" factory-method="getObject"/>
You can of course also write a Java Configuration class and a @Bean
method that uses MemoryFileSystemBuilder
to create a new file system. Or a CDI class with a @Produces
method that uses MemoryFileSystemBuilder
to create a new file system.
By setting the "type" attribute to "windows", "linux" or "macos" you can control the semantics of the created file system.
The following guidelines are designed to help you write code that can easily be tested using this project. In general code using the old File
API has to moved over to the new Java 7 API.
- Inject a
Path
orFileSystem
instance into the object doing the file handling. This allows you to pass in an instance of a memory file system when testing and an instance of the default file system when running in production. You can always the the file system of a path by usingPath#getFileSystem()
. - Don't use
File
,FileInputStream
,FileOutputStream
,RandomAccessFile
andPath#toFile()
. These classes are hard wired to the default file system.- Use
Path
instead ofFile
. - Use
SeekableByteChannel
instead ofRandomAccessFile
. UseFiles#newByteChannel
to create an instance ofSeekableByteChannel
. - Use
Files#newInputStream
andFiles#newOutputStream
to createInputStream
s andOutputStream
s on files. - Use
FileChannel#open
instead ofFileInputStream#getChannel()
,FileOutputStream#getChannel()
, orRandomAccessFile#getChannel()
to create aFileChannel
- Use
- Use
FileSystem#getPath(String, String...)
instead ofPaths#get(String, String...)
to create aPath
instance because the latter creates an instance on the default file system.