How To Use SFMF4J

For Library Architects

Developing a file monitoring solution using SFMF4J is pretty simple. You simply need to:

  1. Define a dependency on sfmf4j-api.
  2. Implement DirectoryListener.
  3. Register the DirectoryListener implementation with a FileMonitorService.

Define a dependency on sfmf4j-api.

When writing code against SFMF4J, you need only add a compile-time dependency on sfmf4j-api. You can (and should) defer the choice of implementation to the application architecture.

Implement DirectoryListener

SFMF4J uses an event/listener paradigm for handling file system changes at the directory level. SFMF4J supports three types of events:

File Creation
events are fired when a new file or subdirectory is detected
File Change
events are fired when the contents of a file or subdirectory have changed, or are likely to have changed.
File Deletion
events are fired when a monitored directory no longer contains a subdirectory or file.

Sometimes you may only be interested in a subset of these events. For example, you may have a process which monitors a folder on an FTP server where users upload files, so you only really care about new files being added. In such a case, you are encouraged to extend DirectoryListenerAdapter and override its no-op implementations.

Register with a FileMonitorService

The act of monitoring the file system for changes and notifying interested parties is handled by a FileMonitorService. How you obtain a reference to a FileMonitorService is up to you, but for your convenience, you can create a pre-configured instance from a FileMonitorServiceFactory.

You will need to maintain a reference to your FileMonitorService in order to later unregister the listener when you no longer have any interest in file system events.

For Application Architects

Deploying an application with file monitoring is easy.

  1. Choose your implementation
  2. Obtain a FileMonitorServiceFactory and a FileMonitorService
  3. Make your FileMonitorService available to interested parties.

Choose your implementation

Adding an SFMF4J implementation to your application is a simple matter of ensuring the implementation's jar is on the application's classpath (along with its dependencies). For OSGi environments, the implementation bundle needs to be installed in the OSGi container.

You should give some consideration to which implementation you choose, as each has its own advantages and drawbacks.

Obtain a FileMonitorServiceFactory

In Java 6+ environments, implementations can be loaded via Java SPI (a.k.a. java.util.ServiceLoader). Note that when using this method, you must invoke the lifecycle methods initialize and shutdown yourself.

import com.github.sworisbreathing.sfmf4j.api.FileMonitorServiceFactory;
import java.util.Iterator;
import java.util.ServiceLoader;

static FileMonitorServiceFactory loadFileMonitorServiceFactory() {
    ServiceLoader<FileMonitorServiceFactory> serviceLoader = ServiceLoader.load(
        FileMonitorServiceFactory.class);
    Iterator<FileMonitorServiceFactory> implementationIterator = serviceLoader.iterator();
    if(implementationIterator.hasNext()) {
        return implementationIterator.next();
    }
}

static FileMonitorService newFileMonitorServiceInstance() {
    FileMonitorServiceFactory implementationFactory = loadFileMonitorServiceFactory();
    FileMonitorService results = implementationFactory.createFileMonitorService();
    return results;
}

In OSGi environments, implementations are registered as services via Blueprint. Using this approach, you can easily rely on the OSGi container to manage the file monitor service's lifecycle for you, simply by declaring the init-method and destroy-method.

<reference id="implementationFactory" interface="com.github.sworisbreathing.sfmf4j.api.FileMonitorServiceFactory" />
<bean id="fileMonitorService" class="com.github.sworisbreathing.sfmf4j.api.FileMonitorService"
    factory-ref="implementationFactory" factory-method="createFileMonitorService"
    init-method="initialize" destroy-method="shutdown"/>

Supplying a FileMonitorService

Once your application has a FileMonitorService instance, it must be made available to interested parties. If you are using a dependency injection framework, such as Spring, then this should be fairly easy. Otherwise, you may need to register the service via JNDI or make it available via some other mechanism. This largely depends on how the library code expects to obtain the FileMonitorService instance.

Notes On Threading

By default, each instance of FileMonitorService runs in its own dedicated thread, from which it detects file change events and invokes DirectoryListener callbacks. If this is a concern for you, you have a few options, such as:

  • Offloading DirectoryListener callbacks to a separate thread. There are various approaches which can be used here. For example, your DirectoryListener callbacks can submit tasks to an ExecutorService or push the events onto a service bus such as Guava EventBus or an asynchronous Camel route. This can be a good option for library architects.
  • Using multiple FileMonitorService instances. This is better suited to application architects than library architects, since its effectiveness largely depends on how it is applied to the deployment environment. You might choose to split up the FileMonitorService instances based on which file systems or directories are being monitored (i.e. "per-disk" or "per-folder"), or you might split them up based on which system functions they are being used for.
  • Using custom FileMonitorServiceFactory configurations. This is also better suited for application architects, since it requires knowing which implementation is in use, and how it can be configured.