JCR observation allows a client to register listeners with a Session, and any time changes are persisted to the repository the listeners will be notified asynchronously. However, listeners are only valid during the lifetime of the session with which they're registered. So how can an application create and register a single long-lived listener? What are the common patterns for registering listeners and then responding to the events?
The simple approach is to create a single long-lived Session that is used only to register the application's listener(s). This session should be created as soon as the application needs to start listening for events, and is kept alive until the application no longer needs to respond to events. This can be done by any thread, but generally the application will hold onto this session in a single application-wide location.
It is important, however, that this "listener session" not be used for any other purposes. As we'll see shortly, the listeners will be notified in separate threads, but the JCR specification states that Sessions do not need to be thread-safe. If each listener were to hold onto the session with which it's registered and then use that session to access the workspace content, then multiple listeners might be notified concurrently in separate threads and would attempt to concurrently use the session.
|ModeShape sessions, nodes, and properties are all thread-safe, so it's perfectly safe to use the same session to concurrently read content in separate threads. Just keep in mind that other implementations will likely be more restrictive.|
Each time a set of changes is persisted to the workspace used by the session, the repository will asynchronously call each listener with the set of changes. The JCR specification does not specify how or in what order the listeners will be notified, other than that a single listener will always see events in the same order that they were generated.
|Each listener should process the event and return as quickly as possible.|
The JCR events usually contain enough information to be directly usable without having to look up other information. So the safest and most efficient listeners can just use the information in the event without needing a session.
This isn't always enough. Some listeners may need to respond to an event by reading workspace content. In these situations, it's best for such listeners to use the information in the events to create and enqueue a job that will be performed by a worker pool rather than doing the work within the listener's methods. If the worker needs a session, then it should create a new one to do the work.
|If the response to a change involves much work (like processing additional content), enqueue the work and use a separate worker pool to perform the work.|
Many applications simply register a single listener and then process the events. If the listener is evaluating each change and doing different work based upon which nodes changed, then consider registering multiple listeners with different filter criteria. Or better yet, try it both ways to see which is the fastest.
|Keep your listeners simple by defining multiple listener implementations and registering them with different filter criteria.|