Working with Views |
![]() |
This chapter covers view management, resource handling, querying, transactions, and related options in CDO client applications. Views are central to accessing and interacting with model data in a CDO repository. Understanding how to use views effectively is key to building responsive and scalable applications.
Table of Contents
A view binds one client-side CDOView and its objects to a session, a resource set,
and a branch point. Use a read-only view for navigation, queries, or a stable historical target; use a
CDOTransaction when the operation must stage local edits and commit them. A normal head view can receive
remote invalidations as repository commits arrive, so it is not a frozen snapshot. Audit or historical views read
an explicit branch/time point and are read-only. Objects are view-scoped: reopen by persistent ID in another view
instead of carrying model objects across view lifetimes.
shows a short-lived read; the transaction example
shows an editable view and its commit boundary.
Open views from a session with CDOSession.openView() for the current branch head, or choose an explicit
branch point through the session's view-opener overloads. Open a transaction when the operation must modify data;
a historical view is read-only. The session tracks its open views, their resource sets, caches, listeners, and
remote view state, so close each view in a finally block or try-with-resources pattern when the work ends.
Closing a view releases those resources and ends the validity of its model-object context. It does not close the
session, which may create later views. Avoid sharing a single long-lived view across unrelated operations just to
avoid reopening it; view lifetime should match the consistency and object-identity scope the application needs.
and
shows the transaction cleanup pattern.
Views in CDO are inherently thread-safe, but this guarantee applies only to individual method calls. When performing
multiple operations that need to be atomic or consistent, developers must use a CriticalSection to
synchronize access to the view. A CDO view provides its critical section via the CDOView.sync().
Each individual access to a view or one of its objects is synchronized with the view's internal work. This does
not make a sequence of separate calls atomic: an invalidation may occur between calls. Use CDOView.sync()
around the smallest multi-call operation that must observe one coherent state. Keep that critical section short;
do not wait for network work, block on another thread, or call arbitrary application callbacks while holding it.
To ensure thread-safe access to a CDO view when performing multiple operations, use the view's critical section
object. It is returned by the CDOView.sync() method. The critical section provides methods to execute code blocks
safely, such as CriticalSection.run(Runnable) and CriticalSection.call(Callable).
Here is an example of using a critical section with a callable to access multiple objects in a view atomically:
The CriticalSection interface provides the following methods:
run(Runnable) - Executes a Runnable within the critical section.
call(Callable) - Executes a Callable within the critical section and returns its result.
call(Class, Callable) - Executes a Callable within the critical section, specifying the exception type it may throw.
supply(Supplier) - Executes a Supplier within the critical section and returns its result.
supply(BooleanSupplier) - Executes a BooleanSupplier within the critical section and returns its boolean result.
supply(IntSupplier) - Executes an IntSupplier within the critical section and returns its int result.
supply(LongSupplier) - Executes a LongSupplier within the critical section and returns its long result.
supply(DoubleSupplier) - Executes a DoubleSupplier within the critical section and returns its double result.
newCondition() - Creates a new Condition associated with the critical section.
A view's critical section uses a real reentrant lock; the current default is a non-fair reentrant lock. A lock supplied with
CDOUtil.setNextViewLock(Lock) or the session's delegable-lock option is used when configured. Synchronizing
directly on the view object is unsupported. By default, CDO detects this when a view lock is next entered and throws
UnsupportedOperationException, directing the caller to CDOView.sync(). Set
-Dorg.eclipse.emf.cdo.view.DISABLE_INTRINSIC_MONITOR_CHECK=true to disable that safety check.
Deprecated monitor and lock methods fail fast by default. Set
-Dorg.eclipse.emf.cdo.view.ENABLE_LEGACY_LOCKING_API=true to re-enable their best-effort behavior.
The compatibility monitor returned by getViewMonitor() coordinates only callers that synchronize on
that returned object; it does not coordinate by itself with CDO's internal view lock. The deprecated lock methods
use the real view lock when enabled.
Here's an example of setting a custom lock for the next view to be opened:
A DelegableReentrantLock is useful when a framework synchronously hands work to another thread that must
access the same view, such as an SWT UI callback. It solves that specific lock-ownership handoff; it is not a
reason to hold the critical section across arbitrary blocking work. Register the relevant delegate detector and
enable the session option or install the lock before opening the view.
As an alternative to the default locking strategy of a view's critical section, you can use
a DelegableReentrantLock, which allows to delegate the lock ownership to a
different thread. This is useful in scenarios where you need to hold the lock while waiting for an
asynchronous operation to complete in a different thread.
A typical example is the Display.syncExec() method in SWT/JFace UI applications. With the default locking strategy this can lead to deadlocks:
Display.syncExec() to execute some code in the UI
thread.
Here is an example that illustrates this scenario:
Note that, in this scenario, Thread A is holding the view lock while waiting for the Runnable to complete. This is kind of an anti-pattern, because it blocks other threads from accessing the view for an indeterminate amount of time. In addition, it is not necessary to hold the view lock while waiting for the Runnable to complete, because Thread A can not access the view in that time.
A DelegableReentrantLock can be used to avoid the deadlock. It uses so called lock delegation to
temporarily transfer the ownership of the lock to a different thread. In the scenario described above,
Thread A can delegate the lock ownership to the UI thread while waiting for the Runnable to complete.
When the Runnable completes, the lock ownership is transferred back to Thread A. This way, the UI thread can
access the view while executing the Runnable and no deadlock occurs.
DelegableReentrantLock
uses DelegateDetectors to determine whether the current thread is allowed to delegate the lock ownership
to a different thread. A DelegateDetector can be registered with the lock by calling
DelegableReentrantLock.addDelegateDetector(DelegateDetector). The org.eclipse.net4j.util.ui plugin provides
a DisplayDelegateDetector for the SWT/JFace UI thread that detects calls to
Display.syncExec().
There are two ways to use a DelegableReentrantLock with a CDO view:
CDOUtil.setNextViewLock(Lock) before opening the view.
This way, the view will use the lock for its critical section. Here's an example:
delegableViewLockEnabled to true on the session.
This way, all views opened from the session will use a DelegableReentrantLock for their critical section.
The lock is created automatically and configured with all DelegateDetectors that are registered.
Example:
The CDO repository exposes a virtual file system for organizing model resources. This section describes the structure and usage of the file system, including root resources, folders, and different resource types.
The root resource is the entry point to the CDO file system. It contains all top-level folders and resources, providing a hierarchical view of the repository's contents.
Each CDO view or transaction can provide the root resource. Here is an example of how to access and list the contents of the root resource:
When you don't have a view or transaction available, you can ask a session for the root resource's ID as follows:
The root resource of a repository is created automatically when the repository is initialized for the first time. It can not be deleted, but its contents can be modified like any other resource.
The CDO resource hierarchy is a repository-side virtual file system. A CDOResourceFolder is a persistent
model object that contains resource nodes; those nodes may be folders, model resources, binary resources, or text
resources. Use folders when resource paths need meaningful grouping and use the node APIs to enumerate children
without loading every model's contents. Folder creation is a transaction change and becomes visible to other
views only after commit.
For creating resource folders you need a CDOTransaction. Here is an example that illustrates how to create folders and subfolders:
Listing subnodes (folders and resources) within a folder does not require a transaction and can be done as follows:
Note that resource folders are model objects and therefore support EMF features such as adapters, notifications, and so on.
A CDOResource is an EMF resource backed by a CDO view. Its root objects and resource metadata participate
in the view's model and notification behavior; creating a resource or changing its contents requires a
CDOTransaction. Loading it in a read-only view gives access to repository state through that view's
ResourceSet. Adding a resource to an ordinary EMF ResourceSet does not persist it in CDO; use the
CDO transaction's resource-creation APIs and commit the transaction to publish the change.
For creating model resources you need a CDOTransaction. Here is an example that illustrates how to create model resources:
Listing root objects and contained objects within a model resource does not require a transaction and can be done as follows:
Note that model resources are model objects and therefore support EMF features such as adapters, notifications, and so on.
Binary resources allow storage of non-model data, such as images or files, within the CDO repository.
They are based on CDO's special data type CDOBlob, which supports efficient handling of large binary objects.
Large object support is described in detail in the chapter Large Objects.
For creating binary resources you need a CDOTransaction. Here is an example that illustrates how to create binary resources:
Getting the binary contents of a binary resource does not require a transaction and can be done as follows:
Note that binary resources are model objects and therefore support EMF features such as adapters, notifications, and so on.
Text resources store textual data, such as configuration files or documentation, in the repository.
They are based on CDO's special data type CDOClob, which supports efficient handling of large text objects.
Large object support is described in detail in the chapter Large Objects.
For creating text resources you need a CDOTransaction. Here is an example that illustrates how to create text resources:
Getting the text contents of a text resource does not require a transaction and can be done as follows:
Note that text resources are model objects and therefore support EMF features such as adapters, notifications, and so on.
A view is associated with a ResourceSet; resources loaded through it contain objects owned by that view. Keep the resource set with the view that created it and close the view when the application is done. Do not move CDO resources or their objects to another resource set and assume their view context changes with them. A ResourceSet can also contain ordinary EMF resources, but their persistence and lifecycle remain the responsibility of their own resource implementation. See Integrating with EMF and Other Frameworks for URI-based loading and resource factories.
Use ordinary EMF navigation for a known, bounded part of the graph. References may be proxies, so reading one can trigger additional loading. A broad walk can therefore cause many round trips and retain many objects. When the desired result is defined by a type or predicate over repository contents, prefer a repository query and load only the returned objects. Choose deliberately between traversal and query; neither is always cheaper.
Passive updates advance or invalidate a live head view according to its session update mode. An application that needs to wait for such updates can use the view/session update-waiting API; a fixed-time historical view does not move forward. Waiting should happen outside a view critical section, because update processing may need that same synchronization boundary. Use view or session events for notification rather than polling model values.
Queries are created from the view and executed by the repository. Use a query when selecting candidate resources is cheaper than loading and traversing a broad resource tree; result limits and asynchronous consumption are covered in Queries and Large-Scale Model Access.
A query runs against the view's repository coordinate and returns objects associated with that view. Select a language supported by both the client and server, bind parameters instead of building expressions from untrusted strings, and close asynchronous result iterators. For a transaction, query options determine whether local dirty state participates; this does not turn a repository query into arbitrary in-memory graph evaluation. OCL is an optional integration, not a universally available language.
queryXRefs asks the repository for objects whose selected source references point at one or more target objects. This is useful when reverse navigation is not already loaded locally. Restrict the source reference set where possible, and use the asynchronous iterator for large results so the whole result list need not be retained at once. The iterator is a resource and must be closed.
A custom query language is only usable when the server has a matching server query handler
registered and the client supplies the language identifier and parameters that handler expects. It is not enough
to install a parser on the client. Handler registration and result delivery are covered in
Query Handlers.
A unit is a repository-defined subtree that the view can treat as a bounded working set. Units can help an application load and manage a coherent part of a large model, but they do not make unrelated object access free or replace transaction boundaries. Unit membership and lifecycle must follow the repository's unit-manager support; close units and views according to the APIs that opened them.
View events report lifecycle and target/update changes for that view. Register listeners only for the period in which the application needs them and remove them when the owning component is disposed. Event callbacks may run on a CDO-managed thread; capture the data needed by the UI and dispatch UI work to its thread instead of blocking the callback. See Notifications and Event Handling for event categories and threading guidance.
View options control view-local behavior such as invalidation, object-cache references, adapters, and locking or notification details. Configure options before relying on the resulting policy, and distinguish them from session options that apply to all views opened by that session. A view option cannot enable a repository feature that the server/store does not provide.
View properties are an application-owned key/value area associated with a view. They are useful for associating client-side metadata with that view; they are not persisted model features, are not shared with the repository, and should not be used to smuggle objects between views. Remove values that retain large application objects when they are no longer needed.