Table of Contents
Entity utility classes contain static methods for working with entities. The entity interfaces are designed to be as small as possible, so they contain just the bare essentials for implementing an entity. They are complemented by utility classes which implement methods to make it easier for client code to work with the entities. Compare this with the role of Java's Collections class.
Example 8.1. Creating a new file
Compare the Directory entity method for creating a new file:
// d is a directory EFile f; EntityLock wl = d.lockForWriting(); try { f = (EFile) d.newEntity(ETFile.TYPE, "f", null); } finally { wl.unlock(); }
With the Directories utility method:
EFile f = Directories.newFile(d, "f");
In the example above, the utility class takes care of locking all required locks before creating the file. All utility classes use the locking strategy that were described in Chapter 7, Locking and multi-threaded access, which should be fine with most applications.
Since locks are reentrant, that the utility classes do their own locking does not prevent client code from doing its own locking too. For instance, client code might want to hold a write lock on an entity that is modified through several utility method invocations to be sure that no other thread accesses the entity in between calls.
An entity utility class is designed to work with one specific entity interface. There are also capability utility classes with designed for working with capability objects, see Chapter 10, Capabilities. The following utility classes are implemented for entity interfaces:
Example 8.2. Holding a write lock for several utility method calls
// d is a directory EntityLock wl = d.lockForWriting(); try { EFile f1 = Directories.newFile(d, "f1"); // Since we hold a write lock on d here, no other thread can get access to // f1 since that would involve reading d. // writeDataToFile is a method that writes data to the file... writeDataToFile(f1); EFile f2 = Directories.newFile(d, "f2"); } finally { wl.unlock(); }
In addition to the iterators that iterate over a directory's child entities, there are two types of iterators that iterate recursively over the entities in an entire directory hierarchy – the DepthFirstIterator and the DepthLastIterator[4]. Both iterators can be instantiated directly or created by using Directories utility methods.
There are implementations of depth-first and breadth-first iterators, apart from the default implementations DepthFirstIteratorImpl and DepthLastIteratorImpl, that implement a strategy for locking the entities that they return. All implementations are listed in the table below.
Table 8.2. Recursive iterators
Iterator class | Description |
---|---|
DepthFirstIteratorImpl | A depth-first iterator that does not lock the entities that it returns. |
DepthLastIteratorImpl | A depth-last (breadth-first) iterator that does not lock the entities that it returns. |
LocationAwareDepthFirstIteratorImpl | A depth-first iterator that returns both entities and their locations relative to the iteration base directory. |
LocationAwareDepthLastIteratorImpl | A depth-last (breadth-first) iterator that returns both entities and their locations relative to the iteration base directory. |
OptimisticLockingDepthLastIterator | A depth-last (breadth-first) iterator that locks entities as they are returned from the iterator. |
PessimisticLockingDepthFirstIterator | A depth-first iterator that locks all entities that it will return before the first entity is returned. |
PessimisticLockingDepthLastIterator | A depth-last (breadth-first) iterator that locks all entities that it will return before the first entity is returned. |
The example below shows a simple alternative implementation of the IteratorDeleter class – an utility object that deletes entities from a directory tree.
Example 8.3. Simple IteratorDeleter implementation
// d is a directory (or view) Iterator<EntityView> itr = Directories.getDepthFirstIterator(d); while(itr.hasNext()) { EntityView ev = itr.next(); DirectoryView parent; // Get a temporary read lock on ev. We need it to get ev's parent. EntityLock rl = ev.lockForReading(); try { // getParent requires a read lock (a write lock would also do) parent = ev.getParent(); } finally { rl.unlock(); } // parent == null means the root directory. We cannot delete that. if (parent != null) { // Lock the parent before ev. EntityLock pwl = parent.lockForWriting(); try { EntityLock wl = ev.lockForWriting(); try { // Is the parent still the parent of the entity to delete? if (ev.getParent() == parent) { ev.delete(); } else { ev.getFileSystem().getLogAdapter(). logWarning("Cannot delete " + ev + ". It has been moved!"); } } finally { wl.unlock(); } } finally { pwl.unlock(); } } }
EntityFS comes with some utility classes for working with iterators:
Table 8.3. Iterator utilities
Utility class | Description |
---|---|
AggregatingIterator | An iterator that aggregates the results from a list of other iterators. |
FilteringIterator | An entity iterator that only returns the entities that passes its Filter. |
IteratorCopier | Copies entities returned from an iterator to another directory tree. The directory hierarchies are preserved. |
IteratorDeleter | Deletes entities returned from an iterator. This is useful for deleting whole or partial directory hierarchies. |
Iterators | Contains static methods for working with iterators. |
JarCreator | Creates a Jar file containing entities that are returned from an iterator. See Chapter 14, Zip and Jar files. |
ZipCreator | Creates a Zip file containing entities that are returned from an iterator. See Chapter 14, Zip and Jar files. |
EntityFS comes with a generic filter implementation defined by the Filter and ConvenientFilter interfaces. In EntityFS, the filters are used for filtering entities to, for instance, create entity views or select which entities an iterator should return. (Other projects such as Schmant and At4J use the filter library for other purposes.)
There are several implementations of entity filters, as shown in the table below. Different filters can be combined using logical operators by using the methods defined in the ConvenientFilter interface. All entity filters also implement the EntityFilter interface to make them easier to find in the API documentation.
Table 8.4. Entity filters
Entity filter class | Description |
---|---|
DirectoryContainsFilter | Matches directories that contain at least x entities that match a supplied entity filter, where x is a configurable number. |
DirectoryEmptyFilter | Matches empty directories. |
DirectoryFilter | Matches directories. |
EFileFilter | Matches files. |
EFileNameExtensionFilter | Matches files with specific file name extensions. |
EntityIdentityFilter | Matches a specific entity object. |
EntityLatestModificationTimeFilter | Matches entities that were last modified before a specific point in time. It can be negated to match entities that were modified after a specific point in time. |
EntityLocationGlobFilter | Matches entities whose locations relative to a base
directory match a Glob pattern, for instance
*/test/*.xml . |
EntityLocationRegexpFilter | Matches entities whose locations relative to a base
directory match a Java regexp , for instance
.*/test/.*\.xml . |
EntityNameFilter | Matches entities with a specific name. |
EntityNameGlobFilter | Matches entities with names that match a Glob pattern, for instance
test_[ab]*.xml . |
EntityNamePrefixFilter | Matches entities with names that starts with a specific prefix. |
EntityNameRegexpFilter | Matches entities with names that match a Java regular expression
, for instance test_[ab].*.xml .
|
EntityRecentModificationFilter | Matches entities that have been modified recently. It can be negated to match entities that have not been recently modified. |
EntityTypeFilter | Matches entities of a specific EntityType (files, directories, etc.). |
FalseEntityFilter | Does not match any entities at all. |
FGrepFilter | Matches text files that contain a certain text string. |
GrepFilter | Matches text files that contain text that match a regular expression Pattern. |
LockedEntityFilter | Matches entities that are locked by the current thread. |
ParentFilter | A filter that applies another filter to an entity's parent directory. |
ReadLockedEntityFilter | Matches entities that are read locked by the current thread. |
SuperParentAndFilter | This filter matches if all of an entity's parent directories (up to the file system root directory) matches a filter. |
SuperParentOrFilter | This filter matches if any of an entity's parent directories (up to the file system root directory) matches a filter. |
SymbolicLinkFilter | Matches symbolic link entities. |
TrueEntityFilter | Matches all entities. |
WriteLockedEntityFilter | Matches entities that are write locked by the current thread. |
For more information on glob, see The Wikipedia article on glob.
Example 8.4. An iterator for iterating recursively over XML files
// d is a directory (or view) Iterator<EntityView> itr = new FilteringIterator( Directories.getDepthLastIterator(d), new EFileNameExtensionFilter("xml"));
All Filter implementations in EntityFS also implement the
ConvenientFilter interface.
It adds the methods and
,
or
, xor
and
not
that can be used instead of the
AndFilter, OrFilter,
XorFilter and NotFilter to combine different
filters. This gives a more compact and readable syntax.
The EntityFilters class contains static utility methods for working with entity filters.
EntityFS defines its own Properties interface.
It extends Map<String,String>
and
defines methods for accessing properties of different data types. Static property
utilities are implemented in the PropertiesUtil
class.
Example 8.5. Working with properties
// f is a property file Properties p = PropertiesUtil.loadFromFile(f); int i = p.getIntValue("myIntProperty"); // The object value is stored as the object serialized to a // Base64-encoded String. Object o = p.getObjectValue("myObjectProperty"); // If no default value is given, all getter methods throw // a NoSuchPropertyException if a requested property is missing. // Define a default value to use if the property is missing. long l = p.getLongValue("myOptionalLongProperty", 17L);
Adapters are used for adapting the interface of one object to another interface. There are several adapter implementations in EntityFS.
Table 8.5. Adapter implementations
EntityFS comes with several useful implementations of Java's InputStream and OutputStream. It also has some utility implementations of the RandomAccess interface.
Table 8.6. InputStream implementations
Implementation | Description |
---|---|
Base64InputStream | Reads and decodes Base64 encoded data. |
ChecksumInputStream | Calculates a checksum over the data read from a proxied stream. |
CountingInputStream | Counts the number of bytes that are read from a proxied stream. |
LockAwareFileInputStream | A FileInputStream that releases an EntityLock when the stream is closed. |
LockAwareInputStream | An input stream that releases an EntityLock when the stream is closed. |
RangeInputStream | Reads data from a portion of a RandomAccess object. |
Table 8.7. OutputStream implementations
Implementation | Description |
---|---|
Base64OutputStream | Base64 encodes data that is written to it. |
CountingOutputStream | Counts the number of bytes that are written to a proxied stream. |
LockAwareFileOutputStream | A FileOutputStream that releases an EntityLock when the stream is closed. |
LockAwareOutputStream | An output stream that releases an EntityLock when the stream is closed. |
MultiplexingOutputStream | An output stream that writes its output to one or more proxied output streams. |
Table 8.8. RandomAccess implementations
Implementation | Description |
---|---|
ByteArrayRandomAccess | A read only RandomAccess object that reads data from a byte array. |
EmptyRandomAccess | A read only RandomAccess object that does not contain any data. |
LockAwareRandomAccess | A RandomAccess that releases an EntityLock when it is closed. |
RangeRandomAccess | Reads and writes data to a portion of another RandomAccess object. |
TempFileBackedRandomAccess | This is used for opening RandomAccess objects on files that don't support it. A temporary copy is made of the file and the RandomAccess object is opened on that copy. |
Table 8.9. Other utility classes
Utility class | Description |
---|---|
DirectoryDifferentiator | Analyzes differences between a master Directory and a target Directory. Parent class of the DirectorySynchronizer. |
DirectorySynchronizer | One way synchronization of the contents of a target directory with that of a master directory. |
DirectoryTreeDifferentiator | Analyzes differences between a master Directory hierarchy and a target Directory hierarchy. Parent class of the DirectoryTreeSynchronizer. |
DirectoryTreeSynchronizer | One way synchronization of the contents of a target directory hierarchy with that of a master directory hierarchy. See the example below. |
EntityClassLoader | Class loader that loads resources from an EntityFS file system. |
EntityLocations | Methods for working with EntityLocation:s. |
EntityLocks | Methods for working with EntityLock:s. |
StreamUtil | Low-level utility methods for working with I/O streams and NIO channels. |
ZipFiles | Methods for working with Zip files. See Chapter 14, Zip and Jar files |
Example 8.6. Synchronizing directory hierarchies
// Synchronize the directory hierarchy under the directory target with the // hierarchy under the directory master. // // Assume the following hierarchy under master: // master // + f1 // + d1 // + f2 // + f3 new DirectoryTreeSynchronizer(master, target).run(); // target now mirrors the directory hierarchy under master, regardless of what // it contained before synchronizing.