Appendix B. EntityFS cookbook

Table of Contents

EntityFS overview
Interoperability with java.io classes
File systems
Entities
Directory views
Utility classes
EntityFilter implementations
Best practices

Schmant uses EntityFS extensively for working with files and directories. This appendix gives some basic information about EntityFS programming that might be useful for build script writers. More exhaustive information about EntityFS programming can be found in the EntityFS Programmer's Guide.

EntityFS overview

EntityFS is an object-oriented file system API for Java. The difference in philosophy between EntityFS and Java's built in file and directory support is that Java's File object represents a path in the local file system, while EntityFS entities represent the files and directories themselves. From the application's perspective, an EntityFS EFile entity, for instance, can be said to be the file, while a Java File object is a pointer to the file. If the EntityFS EFile is moved, the object is updated with the new location. If it is deleted, the entity object is invalidated. If a new file is put in the same location in the file system, the original EFile object is still invalid.

The properties of entity objects are further discussed in this article on the EntityFS site.

EntityFS file systems can be built on the operating system's file system, on Zip or Jar files or in memory. See for instance the section called “In-memory file systems”.

An EntityFS FileSystem contains file and directory entities. Entities are mostly used through any of the utility classes.

Interoperability with java.io classes

The EntityFS Programmer's Guide contains an appendix on how to move from EntityFS classes to java.io classes and back again. See also Example 5.3, “From Java files to EntityFS and back again”.

The SchmantFileSystems utility class contains the makeFileBacked and makeRandomlyAccessible methods for making EFile entities File-backed and FCRandomAccess:ible, respectively.

File systems

Entities always live within the context of a FileSystem. It has some global properties, such as strategies for entity locking and for handling of file system events. It can also have any number of FileSystemCapability:s and EntityCapability:s that extend its functionality. For instance, the ECFileResolvable entity capability makes files and directories resolvable to Java's File objects.

A file system always contains a root Directory that can be retrieved by calling its getRootDirectory() method.

File systems are either created manually using a FileSystemBuilder or through any of the utility methods of SchmantFileSystems. SchmantFileSystems adapts the file system-creating methods of EntityFS' FileSystems and also adds a few file system-creation methods of its own.

FileSystem:s may be created to support entity locking. This should be enabled for all read/write file systems accessed by parallel build threads, since it prevents tasks from accidentally writing to the same files or directories simultaneously. Many of the file systems returned by SchmantFileSystems' methods have locking enabled.

Table B.1. FileSystemBuilder implementations

ImplementationDescription
FSROFileSystemBuilderBuilds a read only file system that is backed by the operating system's file system. The file system's root directory can be in any directory in the backing file system.
FSRWFileSystemBuilderBuilds a read/write file system that is backed by the operating system's file system. The file system's root directory can be in any directory in the backing file system.
JarFileSystemBuilderBuilds a read only file system that is backed by a Jar file. The root directory is in the root of the Jar file.
RamFileSystemBuilderBuilds a read/write file system that stores its data in memory.
ZipFileSystemBuilderBuilds a read only file system that is backed by a Zip file. The root directory is in the root of the Zip file.

Example B.1. Creating source and target file systems

import java.io.File import org.entityfs.util.Directories import org.entityfs.util.filter.entity.EntityNameFilter import org.schmant.support.entityfs.SchmantFileSystems import org.schmant.support.io.TempFileUtil // Create a read only file system with its root directory in // /home/me/myproject/src srcd = SchmantFileSystems.getEntityForDirectory( new File("/home/me/myproject/src"), true) // Get a view of the root directory that hides all .svn directories. When this // view is used for getting child directories, all .svn directories will be // hidden in them too, making all .svn directories in the file system invisible // for all EntityFS-aware tasks. // The ~ is used to negate the filter. srcRoot = srcd.newView(~(new EntityNameFilter(".svn"))) // Create a read/write file system in a temporary directory to use for putting // built files in. Keep this directory when the script is done (set keep flag to // true). targetRoot = TempFileUtil.createTempDirectory( "myproject", null, true) // Set a temporary directory on the target file system. Tasks that need to // create temporary files use this directory for them. targetRoot.fileSystem.setTemporaryFilesDirectory( Directories.newDirectory(targetRoot, "tmp"))

Schmant implements a LogAdapter, the SchmantReportLogAdapter, for making EntityFS log into a Schmant Report. This log adapter can manually be set on a file system fs by calling fs.getLogAdapterHolder().setLogAdapter(SchmantReportLogAdapter.INSTANCE). If any of the methods of SchmantFileSystems or TempFileUtil are used for creating entities (and thus also file systems), the returned file systems already have the Schmant log adapter set.

Example B.2. Using the SchmantReportLogAdapter

import org.entityfs.fs.FSRWFileSystemBuilder import org.schmant.report.SchmantReportLogAdapter // Create a new file system with its root directory in the directory d // (a File). def fs = new FSRWFileSystemBuilder(). setRoot(d). create() // Set the Schmant log adapter fs.logAdapterHolder.setLogAdapter( SchmantReportLogAdapter.INSTANCE) // This is written to the current Report fs.logAdapter.logWarning("This is a warning message!")

Entities

Entity:s live within the context of a FileSystem; an entity can never leave it. Every entity has a unique location, an AbsoluteLocation in its file system.

Entity objects are never instantiated directly by clients. Existing entities are retrieved from their parent Directory object or, more commonly, by using the utility methods of Directories. New entities are also created by using the methods of their parent Directory or by using Directories' methods.

All entities use read/write locking to protect them from concurrent modification. Before using an entity method on an entity in a locking file system, the entity has to be either locked for reading or writing, depending on what the method does. All utility class methods acquire the necessary locks before calling entity methods, which make them much easier to use compared with calling entity methods directly.

Directory views

A DirectoryView is a Directory viewed through zero or more EntityFilter:s. If a child directory is fetched through a directory view, it inherits the parent directory's view settings.

A common usage is to use directory views to hide all .svn directories in a file system, see this example.

Utility classes

Often, most of the work against a FileSystem and its entities is done through EntityFS' utility classes. They have high-level methods for common operations, such as listing directory contents with filters or reading contents of files.

Table B.2. EntityFS utility classes (useful subset)

Utility classUtility methods for
DirectoriesWorking with Directory:s.
ECFileResolvableUtilEntities that can be resolvable to Java's File objects.
ECJarEntryUtilEntities in a Jar file.
ECUriResolvableUtilEntities that can be resolvable to Java's URI:s.
DirectoriesWorking with Directory:s.
ECZipEntryUtilEntities in a Zip or Jar file.
EntitiesWorking with entities (files or directories).
EntityFiltersWorking with EntityFilter:s.
FCFileBackedUtilWorking with file entities that can be resolved to Java's File objects.
FilesWorking with EFile:s.
FileSystemsWorking with FileSystem:s.
FSCFileResolvableUtilWorking with file systems whose entities can be resolved to Java's File objects.
FSCUriResolvableUtilWorking with file systems whose entities can be resolved to Java's URI objects.
IteratorsWorking with Iterator:s.
PropertiesUtilLoading EntityFS Properties.
StreamUtilWorking with streams and channels.

EntityFilter implementations

The following table contains the entity Filter implementations that EntityFS comes with.

Table B.3. EntityFilter implementations

ImplementationDescription
DirectoryEmptyFilterMatches empty directories.
DirectoryFilterMatches directories.
EFileFilterMatches files.
EFileNameExtensionFilterMatches files whose names have one of a set of extensions.
EntityIdentityFilterMatches a specific entity instance.
EntityLatestModificationTimeFilterMatches entities that were modified before a specific time.
EntityLocationGlobFilterMatches entities whose location relative to a base directory matches a glob pattern.
EntityLocationRegexpFilterMatches entities whose location relative to a base directory matches a regular expression pattern.
EntityNameFilterMatches entities with a specific name.
EntityNameGlobFilterMatches entities whose names match a glob pattern.
EntityNamePrefixFilterMatches entities whose names start with a specific prefix.
EntityNameRegexpFilterMatches entities whose names match a regular expression pattern.
EntityRecentModificationFilterMatches entities that have been modified within a specified time limit.
EntityTypeFilterMatches entities of a specific type (files or directories). It is better to use EFileFilter or DirectoryFilter instead.
FalseFilterDoes not match any entity.
LockedEntityFilterMatches entities that are locked for reading or writing by the current thread.
ParentFilterMatches entities that have a specific parent directory.
ReadLockedEntityFilterMatches entities that are locked for reading by the current thread.
SuperParentAndFilterMatches an entity if all of its parent directories match another filter.
SuperParentAndFilterMatches an entity if any of its parent directories match another filter.
TrueFilterMatches all entities.
WriteLockedEntityFilterMatches entities that are locked for writing by the current thread.

All entity filters implement ConvenientFilter so they have and, or, xor and not methods to combine them with other filters.

See for instance this example for how filters can be used.

Best practices

The following is a collection of best practices for using EntityFS in Schmant build scripts:

  1. If possible, use SchmantFileSystems methods instead of FileSystemBuilder:s for creating file systems. The file systems created by SchmantFileSystems are configured with settings that work well with build scripts.

  2. Create a read only file system for the build's source files and a read/write file system for the build's target directories. See Example B.1, “Creating source and target file systems”.