Notifications and Event Handling

 

Author: Eike Stepper

CDO applications observe change through two related but different mechanisms. EMF Notifications are emitted by model objects to their adapters. Net4j IEvents are emitted by sessions, views, transactions, and other notifiers to registered listeners. A repository commit received from another client is first a session/view invalidation; it becomes object-level EMF notification only when the view is configured to deliver the corresponding adapter notification.

This chapter explains how the mechanisms fit together, where to register listeners, and how to avoid confusing a remote invalidation with a local edit or a transaction commit. Detailed configuration remains in Working with Sessions, Working with Views, Working with Transactions, and Locking.

Table of Contents

1 The Notification and Event Model
2 EMF Model Notifications
3 CDO Events and Listeners
4 Session Events
5 View Events
6 Transaction Events
7 Remote Changes, Invalidations, and Subscriptions
8 Lock Notifications
9 Event Threading and Delivery
10 Listener Lifetime, Errors, and Ordering
11 Choosing the Right Mechanism

1  The Notification and Event Model

There are four useful levels of observation. A local model edit produces ordinary EMF notifications on the affected objects. A transaction can additionally report its transition from clean to dirty and back to clean. A remote commit is received as a session invalidation and distributed as a view invalidation. Finally, lifecycle, option, permission, lock, and remote-session changes are CDO-specific events on their owning notifiers.

An invalidation says that the revision known by a view is no longer current; it is not itself an EMF feature delta. Use CDOViewInvalidationEvent.getRevisionDeltas() when a view has a delta, and use adapters when the application needs object-level notifications. These channels are complementary, not interchangeable.

2  EMF Model Notifications

CDO objects are EMF objects. Applications can attach an EMF adapter to an object's adapter list and receive normal EMF notifications for local changes. A local transaction edit is the right place to use an adapter when a component needs the feature, old value, new value, or list-position information carried by an EMF notification.

Remote changes need separate care. Passive updates must be enabled for session and view invalidation events. By default, a remote commit invalidates relevant cached revisions; it does not promise a detailed EMF delta to every adapter. CDOView.Options.setInvalidationNotificationEnabled(boolean) can enable the synthetic CDOInvalidationNotification, whose delta-related methods are unsupported. For detailed remote adapter delivery, register matching adapters and enable a change-subscription policy as described in CDOView.Options.addChangeSubscriptionPolicy(CDOAdapterPolicy).

An adapter only observes an object that is present and has that adapter, and a view may fetch or retain objects lazily. Do not use the absence of an object notification as proof that the repository did not change; use the view invalidation or refresh the object instead. Detachment and load notifications are separately configurable through CDOView.Options.

3  CDO Events and Listeners

The Net4j event pattern is small: an notifier exposes add/remove-listener operations and invokes IListener.notifyEvent(IEvent) with an event whose source is the notifier. Sessions, views, transactions, option objects, and managers use this same pattern.

Keep the listener instance if it must later be removed. Filtering is normally done with instanceof, which preserves subtype relationships. Each registration has its own removal obligation.

4  Session Events

Register session listeners for events whose scope is the connection or repository view shared by all of the session's views. The application-level categories are CDOSessionInvalidationEvent for received passive updates, permission changes, and lock changes. Repository/session state and type changes, lifecycle events such as LifecycleEvent, and remote-session manager events are also exposed by the corresponding public notifier. Session option objects emit option events when their configuration changes.

A session invalidation is emitted after passive-update processing has received a commit notification. Its CDOSessionInvalidationEvent.isRemote() flag distinguishes a remote commit from a local transaction, and CDOSessionInvalidationEvent.getLocalTransaction() identifies the local transaction when applicable. See Working with Sessions for passive-update and session configuration details.

5  View Events

View listeners are the most useful CDO-level observation point for model visibility. Important public event families include CDOViewInvalidationEvent, CDOViewAdaptersNotifiedEvent, CDOViewTargetChangedEvent, and CDOViewLocksChangedEvent. A view can also report permissions, durability, provider, and lifecycle changes. The invalidation event provides dirty and detached objects and may provide revision deltas; the adapters-notified event marks completion of adapter delivery for the same update time.

A view's branch point and time-machine position determine which revision is visible. See Working with Views for branch/time and passive-update configuration.

6  Transaction Events

A CDOTransactionStartedEvent is fired when a transaction first becomes dirty. A CDOTransactionFinishedEvent is fired when it becomes clean after a commit, rollback, or undo; use its current CDOTransactionFinishedEvent.getCause() API rather than the deprecated type accessor. Conflict events report objects entering or leaving the transaction's conflict state.

Current nested transactions also have CDOTransactionScopeOpenedEvent and CDOTransactionScopeClosedEvent; a scope shares the root transaction's view and dirty state, and closing a scope is not a repository commit. Transaction and view option objects report option changes through their own event types. Commit details are returned by CDOTransaction.commit(), not by an invented generic "commit event". See Working with Transactions for commit, rollback, conflict, and scope semantics.

7  Remote Changes, Invalidations, and Subscriptions

When another transaction commits, a session configured for passive updates receives a commit notification. The session emits a CDOSessionInvalidationEvent, and each eligible non-historical view incorporates the update and emits a CDOViewInvalidationEvent. The view invalidation identifies modified and detached objects and may contain revision deltas. It does not mean that every object has been eagerly reloaded; the current state is obtained according to the view's update and loading policies. A view can also use this event family for a local rollback; CDOViewInvalidationEvent.LOCAL_ROLLBACK identifies that case rather than a repository commit timestamp.

A transaction's local dirty objects are protected from being silently overwritten. A remote change can instead produce a transaction conflict, which is observable through the transaction conflict APIs and events. Passive update modes, invalidation policies, and refresh behavior are configured at the session/view level; link to Passive Updates and Refreshing and Working with Views for those choices.

Change subscriptions control which object/adapter pairs are registered for detailed server-side change delivery. Add an adapter to an object, add a matching CDOAdapterPolicy to the view options, and remove the policy or adapter when the observing component is disposed. Subscriptions can reduce unnecessary notification traffic, but they do not replace passive updates, view invalidations, or normal cache loading. Temporary objects are subscribed automatically after they become persistent.

8  Lock Notifications

Lock events are part of the same CDO event model but describe repository lock state, not model feature changes. Views can emit CDOViewLocksChangedEvent; sessions can emit CDOSessionLocksChangedEvent. Delivery depends on the session lock-notification mode and, where applicable, the view lock-notification option. Use the current lock state before acting and see Locking for configuration, ownership, durable locking, and lock lifecycle semantics.

9  Event Threading and Delivery

Net4j notifier delivery is synchronous by default: a notifier invokes its listeners on the thread that fires the event. CDO invalidation and remote-session processing can therefore invoke listeners on a CDO/network or invalidation worker thread, while a local edit can invoke EMF adapters on the editing thread. Some notifiers may configure a notification executor, so an application must not assume one universal callback thread.

Callbacks should be short and non-blocking. Do not wait for UI work, perform long-running I/O, or assume that a callback owns a view critical section. If several view accesses must form one consistent observation, use the view's critical section; then hand UI or expensive work to the appropriate application executor. The session Javadoc specifically warns about deadlocks when adapter callbacks synchronously cross to a UI thread.

10  Listener Lifetime, Errors, and Ordering

Listener registration is strong unless a particular notifier documents otherwise. Remove listeners from the same notifier when the component, view, transaction, or session is disposed. Remove object adapters as well, and remove change-subscription policies when they are no longer needed. Closing a notifier stops its useful lifecycle, but it is not a replacement for releasing application-held listener references or unregistering from longer-lived managers. CDO does not require applications to use a weak-listener convention.

The common Net4j notifier catches ordinary listener exceptions, logs them, and continues with the other listeners; applications should nevertheless catch expected failures and should never use listener exceptions as control flow. A cancellation is a special framework mechanism and should not be introduced in ordinary application listeners.

Rely only on documented family-level ordering: a view invalidation is associated with the update it describes, and CDOViewAdaptersNotifiedEvent can be used to observe completion of adapter notification for that update. There is no general global ordering across all session, view, object, option, and transaction listeners.

11  Choosing the Right Mechanism

Use an EMF adapter when one model object needs feature-level local or subscribed remote notifications. Use a view listener for invalidation, adapter-delivery completion, branch/time, permission, or lock changes. Use a session listener for repository-wide passive updates, permissions, locks, lifecycle, and remote-session information. Use a transaction listener for dirty/clean transitions, conflicts, and nested-scope lifecycle. Use a change subscription when selected objects need detailed remote adapter delivery and the associated server traffic is justified. Use lock-specific events only for lock state; they do not describe model changes.

In all cases, treat the event as a signal to inspect the current public state. An event is not a permission to skip view synchronization, conflict handling, transaction checks, or listener cleanup.