Chapter 8. Utility classes

Table of Contents

Entity utility classes
Recursive iterators
Iterator utilities
Entity filters
Property utilities
Adapters
Input and output stream implementations
Other utility classes

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.


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:


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 classDescription
DirectoryContainsFilterMatches directories that contain at least x entities that match a supplied entity filter, where x is a configurable number.
DirectoryEmptyFilterMatches empty directories.
DirectoryFilterMatches directories.
EFileFilterMatches files.
EFileNameExtensionFilterMatches files with specific file name extensions.
EntityIdentityFilterMatches a specific entity object.
EntityLatestModificationTimeFilterMatches 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.
EntityLocationGlobFilterMatches entities whose locations relative to a base directory match a Glob pattern, for instance */test/*.xml.
EntityLocationRegexpFilterMatches entities whose locations relative to a base directory match a Java regexp , for instance .*/test/.*\.xml.
EntityNameFilterMatches entities with a specific name.
EntityNameGlobFilterMatches entities with names that match a Glob pattern, for instance test_[ab]*.xml.
EntityNamePrefixFilterMatches entities with names that starts with a specific prefix.
EntityNameRegexpFilterMatches entities with names that match a Java regular expression , for instance test_[ab].*.xml.
EntityRecentModificationFilterMatches entities that have been modified recently. It can be negated to match entities that have not been recently modified.
EntityTypeFilterMatches entities of a specific EntityType (files, directories, etc.).
FalseEntityFilterDoes not match any entities at all.
FGrepFilterMatches text files that contain a certain text string.
GrepFilterMatches text files that contain text that match a regular expression Pattern.
LockedEntityFilterMatches entities that are locked by the current thread.
ParentFilterA filter that applies another filter to an entity's parent directory.
ReadLockedEntityFilterMatches entities that are read locked by the current thread.
SuperParentAndFilterThis filter matches if all of an entity's parent directories (up to the file system root directory) matches a filter.
SuperParentOrFilterThis filter matches if any of an entity's parent directories (up to the file system root directory) matches a filter.
SymbolicLinkFilterMatches symbolic link entities.
TrueEntityFilterMatches all entities.
WriteLockedEntityFilterMatches entities that are write locked by the current thread.


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.


EntityFS comes with several useful implementations of Java's InputStream and OutputStream. It also has some utility implementations of the RandomAccess interface.








[4] DepthLastIterator should really have been named BreadthFirstIterator