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.
CDO provides several types of views, including read-only views and transactional views. Read-only views allow safe navigation of repository data without risk of modification, while transactional views enable changes and commits. This section explains the differences, use cases, and lifecycle of each view type.
Learn how to open views to access repository data and close them to release resources. Proper management of view lifecycles helps prevent memory leaks and ensures efficient resource usage.
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().
Thread safety of CDO views is absolutely essential, even in single-threaded applications. The reason is that CDO views are accessed by background threads for tasks such as asynchronous updates, notifications, and event handling. If a view were not thread-safe, these background operations could lead to data corruption, inconsistent states, and unpredictable behavior in the application. By ensuring that views are thread-safe, CDO allows developers to build robust applications that can safely handle concurrent operations without risking data integrity.
This section discusses best practices for managing concurrent access, synchronizing operations, and avoiding race conditions when working with views in multi-threaded environments.
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.
By default the critical section of a view uses the monitor lock of that view to synchronize. If you need a different locking
strategy you can override this by calling CDOUtil.setNextViewLock(Lock) before opening the view.
Here's an example of setting a custom lock for the next view to be opened:
The following chapter describes how to use a special kind of lock, a DelegableReentrantLock, that
allows to delegate the lock ownership to a different thread.
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.
Resource folders organize model resources into logical groups. Learn how to create, navigate, and manage folders to structure your repository effectively.
Model resources store EMF model data in the repository. This section explains how to load, save, and query model resources, and how they relate to EMF ResourceSet.
Binary resources allow storage of non-model data, such as images or files, within the CDO repository. Learn how to manage binary resources and integrate them with your application.
Text resources store textual data, such as configuration files or documentation, in the repository. This section covers reading, writing, and organizing text resources.
Resource sets are collections of resources managed together. Learn how to use EMF ResourceSet with CDO, manage resource lifecycles, and optimize performance for large models.
This section provides techniques for traversing and querying model objects within views, including use of EMF APIs and CDO-specific features for efficient navigation.
Learn how to synchronize your client with repository changes, block for updates, and react to notifications to keep your application state current.
CDO supports querying resources using various criteria. This section explains how to construct and execute queries to locate resources and model objects efficiently.
Learn how to query model objects using CDO's query APIs, including support for OCL, custom queries, and cross-references.
Cross references allow navigation between related model objects. This section covers techniques for querying and resolving cross references in CDO models.
Extend CDO's querying capabilities with custom query implementations. Learn how to define, register, and execute custom queries for advanced use cases.
Units are disjunct subtrees of model objects that can be managed independently. This section explains how to create, open, and close units, and how they can improve performance and consistency in your application.
Views emit events for changes, updates, and errors. This section explains how to listen for and handle view events to build responsive applications.
Configure view options to customize behavior, such as passive updates, notification handling, and more.
Access and modify view properties to store custom metadata and configuration values.