Notifications and Event Handling |
![]() |
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.