Working with Sessions |
![]() |
This chapter covers all aspects of session management in CDO client applications, including configuration, lifecycle, facilities, and events.
Sessions are the foundation of any CDO client application. They represent the connection to a CDO repository and provide access to views, transactions, and various session facilities. Understanding how to create, configure, and manage sessions is essential for building robust and scalable CDO applications.
Sessions are created (opened) when needed, kept open for the duration of their use, and closed when no longer required. A client application may have multiple sessions open simultaneously, each connected to different repositories or using different configurations.
Session API Structure
The session API in CDO is organized into two main components: the basic CDO session and the specific Net4j session.
CDOSession
interface provides the core functionality for interacting with a CDO repository.
It offers access to views, transactions, branches, packages, and other repository
entities, independent of the underlying transport protocol.CDONet4jSession
interface extends the basic session with features specific to the Net4j transport
layer, such as advanced connector management, protocol selection, and network
configuration. Net4j sessions are created and configured using
CDONet4jSessionConfiguration and
CDONet4jUtil.Other transport-specific session types are possible in general, allowing for future extensibility of the session API to support additional transport protocols. However, as of now, only the Net4j implementation is available and supported in CDO. This means that while the API is designed to be flexible, all practical usage currently relies on the Net4j session type for networked repository access.
Thread Safety
Sessions in CDO are inherently thread-safe. This means that if multiple threads access the same session instance concurrently, the session ensures that its internal state remains consistent and that operations are executed in a thread-safe manner. This is particularly important in multi-threaded applications where different threads may need to perform operations on the same session simultaneously.
Table of Contents
Learn how to instantiate and configure CDOSession objects, including specifying repository details, authentication, and connection parameters. Proper session configuration ensures secure and efficient communication with the CDO server.
In order to create a session, you first need to create a session configuration. This configuration specifies the connector to use and the repository name to connect to, as well as various other options.
Here is an example of how to create and open a session using a given connector and repository name:
Note that the connector must be created and configured separately. For more information about connectors, refer to the Net4j Connectors section or the Net4j documentation.
Also note that the repository must already exist on the server. For more information about repositories, refer to the Server Programming section.
Once a session is opened, you can still change various options on it, such as enabling or disabling passive updates. For more information about session options, refer to the Session Options section.
Net4j connectors are the transport layer for CDO communication. Learn how to configure TCP, JVM, SSL, WS or WSS connectors to establish reliable and secure connections between client and server.
Typically a Net4j connector is created and configured using a Net4j container. Within an Eclipse environment,
the container is usually managed by the Eclipse extension registry. Such a container can be accessed using
IPluginContainer.INSTANCE. In a non-Eclipse environment, you can create and manage the container
programmatically. Refer to IManagedContainer for more details.
The following example demonstrates how to set up a JVM connector within an Eclipse environment:
The following example demonstrates how to set up a TCP connector within a non-Eclipse environment:
More details on the architecture and usage of Net4j connectors can be found in the Net4j documentation.
The repository may require clients to authenticate before allowing access. In this this case, the repository administrator configures one or more authentication providers on the server side. When a client opens a session, an open-session request is sent to the server. If the server requires authentication, it initiates an authentication challenge-response sequence using one of its configured authentication providers. The client must respond with appropriate credentials. If the credentials are valid, the server allows the session to be opened; otherwise, the session opening fails.
Here is an example of how to configure a session with fixed credentials:
Here is an example of how to configure a session with an interactive credentials provider that opens a
CredentialsDialog whenever challenged from the server:
The actual password is transmitted securely using a cryptographically strong protocol called Diffie-Hellman Key Exchange. It is not sent over the network in clear text, but is restored to clear text on the server side for verification.
Discover strategies for handling session disconnects and automatic reconnection. Learn how to maintain session continuity and recover gracefully from network interruptions.
Normally, when a session is disconnected, it is automatically closed and cannot be used anymore. However, you can create a reconnecting session that automatically tries to reconnect when it gets disconnected. This is done by using CDONet4jUtil.createReconnectingSessionConfiguration() instead of CDONet4jUtil.createNet4jSessionConfiguration().
Here is an example of how to create and open a reconnecting session:
A session can also be disconnected in case of server problems, as opposed to network problems. One way to protect against such situations is to use a fail-over session.
Here is an example of how to create and open a reconnecting session:
Fail-over sessions use a special network service, called fail-over monitor, to ask for the address of a live server.
The configuration and operation of the fail-over monitor is beyond the scope of this documentation.
For more information, refer to FailoverMonitor.
Passive updates allow sessions to receive notifications about changes in the repository. Understand how to enable passive updates and refresh views to keep your client data synchronized.
By default, sessions receive passive updates from the server. This means that when other clients make changes to the repository, the server notifies all connected sessions about these changes. The sessions then invalidate the affected objects in their views, marking them as stale. When your application accesses a stale object, it is automatically refreshed from the server, ensuring that your application always works with the latest data.
Passive updates can be disabled if your application requires more control over when objects are refreshed. In this case, you can call the refresh() method on a view to manually refresh all stale objects.
Here is an example of how to enable/disable passive updates and manually refresh views:
Note that passive updates are a session-wide setting that affects all views within the session. When passive updates are re-enabled, a refresh is automatically performed to synchronize all views with the server. For more information about views, refer to the Waiting For Updates section.
When passive updates are enabled, the amount of information sent from the server to the client can be controlled using the passive update mode setting. There are three modes available:
Here is an example of how to set the passive update mode:
Note that the passive update mode can also be changed at any time on a session via the session options.
For more information about passive update modes, refer to PassiveUpdateMode.
In some scenarios, your application may need to wait for a specific update to be processed before proceeding. As updates are generally triggered by the server asynchronously, for example when another client commits changes, there is no guarantee that an update will be received immediately after it occurs.
To handle such situations, you can use the waitForUpdate(long updateTime) method on a session.
This method blocks the calling thread until all updates that occurred before the specified update time have been processed.
If you want to limit the time spent waiting, you can also specify a timeout value in milliseconds. If the timeout
expires before the updates are processed, the method returns false; otherwise, it returns true.
Here is an example of how to wait for updates:
Note that more commonly, applications use listeners to be notified of updates asynchronously. For more information about session events and listeners, refer to the Session Events section.
Note also that applications usually wait for updates on views rather than sessions. For more information about views, refer to the View Management section.
Views are the primary mechanism for accessing and working with models that are stored in a CDO repository. They provide a consistent and isolated perspective on the repository state, allowing clients to read and, in the case of transactions, modify data. Understanding how to create, manage, and interact with views is essential for building robust CDO client applications.
Views are created from sessions and are associated with a specific branch point in the repository. This branch point determines the exact state of the repository that the view exposes. Views can be read-only or read-write (transactions), and they can also be configured to access historical states of the repository through audit views.
This section covers the key aspects of view management, including the purpose of views and transactions, branch points and view targets, types of views, finding views by ID, listening for view-related events, and closing views. For more detailed information on working with views, refer to the Working with Views section.
Note that a CDOSession inherits most of the view management functionality through the following super interfaces:
CDOViewContainer and IContainer<CDOView>: Provide methods for managing a collection of
views within the session, including the ability to listen for view addition and removal events.CDOViewOpener and CDOTransactionOpener: Provide various methods for opening views and transactions.A CDO repository can store many different versions of a model. Each time a change is committed to the repository, a new version of the model is created. These versions are organized into branches, allowing for parallel lines of development. Each version of the model is identified by a unique combination of a branch and a timestamp, known as a branch point. This multiplicity of versions and branches is the reason why a CDOSession alone is not sufficient to access a consistent model state of the repository. Instead, clients need views and transactions to specify which version of the model they want to work with.
A view represents a read-only window into the repository. Read-only views allow clients to navigate and query models without the risk of modifying data. For example, a client application might use a single read-only view to display a model in a different places of its user interface, allowing users to browse and inspect the data.
Transactions, on the other hand, are special types of views that enable clients to make changes to the repository. Changes made in a transaction are isolated from other views until the transaction is committed, ensuring data consistency and supporting concurrent development.
For more detailed information on working with views, refer to the Working with Views section.
Every view or transaction in CDO is associated with a specific branch point, which determines the exact state of the
repository that the view exposes. The branch point consists of a branch and a
timestamp, together called the
view target. This mechanism allows clients to access not only the latest state of the main branch, but also
historical states (by specifying an earlier timestamp), or parallel lines of development (by specifying a different
branch).
Normally, when you open a view or transaction, you specify the branch and/or timestamp that you want to target. Read-only views can target any branch point, including historical states, while transactions always target the head of a branch, allowing clients to make changes to the latest state of that branch.
When you open a view or transaction without specifying a branch point, it defaults to the head of the main branch, showing the most recent state of the repository. To access a different branch or a historical state, you can explicitly specify a branch and/or timestamp when opening the view. This is useful for auditing, time-travel queries, or working with feature branches.
For both views and transactions, the branch point is initially set when the view is created, but it can be changed
later using the setBranchPoint() method. Changing the branch point
causes the view to refresh its contents to reflect the state of the repository at the new target branch point.
All model objects in the view are updated to match the new branch point, ensuring that the view always presents a
consistent snapshot of the repository. This process is automatic and absolutely transparent to the client application.
It enables powerful scenarios such as switching between different branches or historical states on-the-fly,
without needing to close and reopen views. The restriction is that transactions can only be switched to other branch heads,
not to historical states.
The head of a branch is represented by a special timestamp value called UNSPECIFIED_DATE.
It ultimately represents a timestamp that is always greater than any other timestamp in the repository, effectively pointing to the
latest commit on that branch. When a transaction is committed, it creates a new version of the model at the head of the branch,
and all views targeting that branch head are automatically updated to reflect the new state. The head of a branch is
always moving forward in time as new commits are made, while historical states remain fixed at their respective timestamps.
As transactions always target the branch head, they are always working with the most recent state of the branch.
For more information about branches and branch points, see the CDOBranchPoint
interface and the CDOBranchManager facility.
Views and transactions are created from a session using the various openView() and
openTransaction() methods. These methods allow you to specify the initial branch and/or timestamp
that you want to target, as well as a ResourceSet to use for loading model objects.
You can also omit the branch, timestamp, and/or ResourceSet, in which case the view or transaction starts with reasonable defaults as follows:
Here is an example of how to open views and transactions with different configurations:
Note that views and transactions are lightweight objects that can be created and disposed of as needed. They should be closed when no longer needed to free up resources.
Here is a summary of the various ways to open views and transactions:
A session maintains a collection of all currently open views and transactions. You can access this collection using the
getViews() method, which returns an unmodifiable list of all open views.
This allows you to iterate over the views, inspect their properties, or perform operations on them as needed.
Example:
Each view in a session is assigned a unique integer ID. You can retrieve a view by its ID using the
getView(int viewID) method. This is useful for
managing multiple views or for integrating with external systems that track view identifiers.
Example:
A session is an IContainer<CDOView> and, hence, emits events when views are opened or closed.
You can listen for these events by registering an
IContainerEvent listener with the session. This allows your
application to react to changes in the set of open views, such as updating UI components or managing resources.
Example:
It is important to close views when they are no longer needed to free up resources and avoid memory leaks.
You can close a view by calling its close() method.
Example:
CDO's main focus is on modeling and model management. From this perspective, models are very much like normal data, and CDO provides a rich set of features for working with model data, including access control and permissions. However, it also includes a basic authorization framework that allows you to perform certain operations only if you have the necessary permissions. An authorizable operation can be associated with various actions within CDO, such as opening a session, creating a transaction, or performing specific repository operations.
The AuthorizableOperation interface represents an operation that can be authorized.
Each operation has a unique name and may include additional parameters that provide context for the authorization decision.
An instance of AuthorizableOperation is typically created by the client application and then passed to the local session
for authorization. The session checks whether the current user has the necessary permissions to perform the operation
and returns a veto string if the operation is not authorized. If the operation is authorized, the veto string is null.
Here is an example of how to create and authorize an operation:
Note that the actual enforcement of authorization is first performed on the client side for efficiency.
For this purpose, the session consults a chain of local authorizers that can be registered with the session.
Each authorizer can approve or veto the operation based on its own criteria. It can also modify the operation by adding or changing parameters.
If none of the local authorizers vetoes the operation, it is sent to the server for final authorization.
The server performs its own authorization checks, which may involve consulting server-side authorizers or access control lists.
If the server vetoes the operation, the client is notified of the veto.
For more information about authorizable operations, refer to the AuthorizableOperation interface.
In an RCP application, you often want to hide or disable UI elements that trigger operations the user is not authorized to perform.
And often, these operations are associated with a selected EObject in a viewer. In this case, you can use the
ContextOperationAuthorization class to create and authorize multiple operations based on the current selection context.
This class simplifies the process of checking authorization for operations related to specific model elements.
Here is an example of how to use ContextOperationAuthorization:
A CDO repository can make arbitrary information available to client sessions in the form of entities.
Entities have a name and are contained in a namespace.
The namespace and name of an entity together form its unique identifier.
The associated information is represented by key-value pairs, where both keys and values are strings.
The information is usually static and does not change over the lifetime of a session.
Entities are read-only and cannot be modified by client sessions. They are typically used to convey configuration settings, metadata, or other information that clients may need to interact with the repository effectively. For example, a repository might provide entities that describe supported features, data formats, or integration points with other systems. A repository can store and manage any number of entities.
Client entities are a special subset of those entities. They are loaded when a session is opened and remain available for the lifetime of the session. The repository operator can specify which entities are client entities and what information they contain.
Client sessions can access client entities using the clientEntities() method.
Calling this method returns a map of all client entities available in the connected repository. You can look up
a specific entity by its unique identifier using the map's get() method. For example:
Non-client entities are not automatically loaded when a session is opened. Instead, they can be accessed
using the requestEntities(String namespace, String... names)
method. This method allows you to specify a namespace and one or more entity names to retrieve.
The method returns a map of the requested entities that exist in the repository. Note that the map keys are the
entity names, not their unique identifiers. If an entity with the specified name does not exist in the given
namespace, it is simply not included in the returned map.
Here is an example of how to request and access specific server entities:
For more information about entities, refer to the Entity interface.
Sessions provide access to several facilities that manage branches, packages, revisions, and more. Each facility offers specialized APIs for advanced repository operations.
Access metadata and status information about the connected repository, including version, capabilities, and health. The repository info is available as soon as the session is opened.
The CDORepositoryInfo interface provides access to repository metadata and status. It extends CDOCommonRepository, so it inherits all of its methods. Key methods include:
| Method | Return Type | Description |
|---|---|---|
getSession() | CDOSession | Returns the session. |
getName() | String | Returns the repository name. |
getUUID() | String | Returns the repository UUID. |
getType() | Type | Returns the repository type (e.g., MASTER, BACKUP, CLONE). |
getState() | State | Returns the current repository state (e.g., ONLINE, OFFLINE, SYNCING). |
getCreationTime() | long | Returns the creation time. |
getStoreType() | String | Returns the store type (e.g., "db"). |
getObjectIDTypes() | Set<CDOID.ObjectType> | Returns supported object ID types. |
getIDGenerationLocation() | IDGenerationLocation | Returns where object IDs are generated. |
getLobDigestAlgorithm() | String | Returns the LOB digest algorithm. |
getCommitInfoStorage() | CommitInfoStorage | Returns the commit info storage type. |
getRootResourceID() | CDOID | Returns the root resource ID. |
isAuthenticating() | boolean | Returns whether authentication is required. |
isSupportingLoginPeeks() | boolean | Returns whether login peeking is supported. |
isSupportingAudits() | boolean | Returns whether audit views are supported. |
isSupportingBranches() | boolean | Returns whether branching is supported. |
isSupportingUnits() | boolean | Returns whether units are supported. |
isSerializingCommits() | boolean | Returns whether commits are serialized. |
isEnsuringReferentialIntegrity() | boolean | Returns whether referential integrity is enforced. |
isAuthorizingOperations() | boolean | Returns whether operations are authorized. |
waitWhileInitial(IProgressMonitor) | boolean | Waits while the repository is initializing. |
getTimeStamp() | long | Returns the current repository time. |
getTimeStamp(boolean) | long | Returns the current repository time, optionally refreshing it from the server. |
IAdaptable.getAdapter(Class<T>) | <T> T | Returns an associated adapter object. |
The CDORepositoryInfo (via CDOCommonRepository) can fire two important events:
TypeChangedEvent – Fired when the repository type changes, for example in a fail-over cluster.StateChangedEvent – Fired when the repository state changes, for example in a replicating deployment.session of the repository info object.
This allows your application to react to changes in the repository's type or state, such as updating the UI or
triggering fail-over logic:
The package registry manages EMF packages known to the session. CDO's package registry is a sub-type of EMF's
package registry. It adds support for dynamic package loading from the repository and ensures that packages are
available when needed. To achieve this, the package registry can fetch packages from the repository on demand.
This is particularly useful when working with models that reference packages not yet registered in the client.
The CDO package registry is automatically available in every session and can be accessed using the
getPackageRegistry() method. You can use the package registry to
register new packages, look up existing packages, and query cached sub-type information about all registered
EClasses.
Please note that EMF packages may contain nested packages and that it is not possible to transfer a package
between clients and servers without also transferring all its nested packages. Therefore, CDO introduces the
concept of package units. A package unit represents a tree of related packages that can be transferred as a
single unit. For each package in the tree, the package unit contains a CDOPackageInfo object that
provides metadata about the package, such as its name and namespace URI. The package info is also able to resolve
the actual package when needed. Once a package is resolved, it is registered in the package registry and
the package info is added to the eAdapters() of the package for easy lookup.
For more information about package units, refer to CDOPackageRegistry, CDOPackageUnit, and CDOPackageInfo.
The revision manager loads, caches, and provides object revisions.
It is automatically available in every session and can be accessed using the
getRevisionManager() method. The revision manager
supports efficient retrieval of revisions by their ID and either their
branch point or version. It also supports bulk loading of revisions
and pre-fetching of related revisions to optimize performance.
Note that the explicit use of revision management is usually not necessary when working with views and transactions. Views and transactions automatically manage revisions for the objects they contain. But the revision manager is inevitable when you need to access revisions directly, for example when implementing version control tools, custom synchronization, or replication logic.
The combination of ID, branch, and version uniquely identifies a revision within the repository. Hence the
getRevisionByVersion() method
can be used to retrieve a specific revision:
In addition, the revision manager provides methods to retrieve revisions by their branch point. That is, given an ID and a branch point (branch and time), you can retrieve the revision that was current in the specified branch at the specified time. This is useful for accessing historical states of model objects without needing to know their exact version numbers and without opening an audit view. Here is an example:
The revision manager also supports bulk loading of revisions using the
getRevisions() method. Here is an example:
The above methods assume that you know the IDs of the revisions you want to retrieve.
But sometimes you may want to find revisions based on their type, branch, and time.
The revision manager provides the
handleRevisions()
method for this purpose. Here is an example:
Sometimes you may want to notified when revisions are loaded into the local cache. The following example shows how to register a listener for this purpose:
For more information about revision management, refer to the CDORevisionManager interface.
A revision is an immutable object that represents a specific state of a model object at a given period in time.
A new revision is always created and stored when a new model object is created or an existing model object is modified.
Revisions are never modified after they are created. Instead, new revisions are created to represent
new states of model objects. This immutability ensures that historical states of model objects are preserved
and can be accessed at any time (if the repository mode is at least AUDITING).
Revisions are identified by:
ID, which uniquely refers to the model object they represent,
branch, which specifies the branch in which the revision was created, and
version, which indicates the sequence of the revision within its branch.
The CDORevision interface provides methods to access technical details about a revision, such as its EClass,
ID, branch, version, and timestamps. The actual model data of a revision, including attribute values and references,
is accessed through the CDORevisionData interface, which can be obtained by calling the CDORevision.data()
method. Here is an example of how to access revision data:
There are other methods available on the CDORevisionData interface, especially for working with
multi-valued features, such as size(),
isEmpty, contains(),
indexOf(), and so on.
In addition to accessing revision data, you can also compare two revisions to determine the differences between them.
The compare() method computes the delta between two revisions
and returns a CDORevisionDelta object that describes the changes. Here is an example of how to compare two revisions:
For more information about revisions, refer to the CDORevision and CDORevisionData interfaces.
In addition to loading revisions on demand, the revision manager supports pre-fetching of related revisions. When a revision is loaded, the revision manager can automatically load additional revisions that are related to the requested revision. This is particularly useful when you know that you will need related revisions soon after loading a specific revision. The pre-fetched revisions are loaded from the repository in the same batch as the requested revision, which can significantly reduce the number of round-trips to the server and improve performance. They remain in the local cache and can be accessed later without additional network calls.
There are different mechanisms available for pre-fetching related revisions:
prefetchDepth parameter of the revision manager's loading methods.
This parameter specifies how many levels of contained objects should be pre-fetched.
For example, a prefetch depth of 1 means that all directly contained objects will be pre-fetched,
and a prefetch depth of 2 means that all directly contained objects and their directly contained objects will be pre-fetched, and so on.
The prefetch depth can be set to DEPTH_NONE (no pre-fetching),
DEPTH_INFINITE (pre-fetch all contained objects recursively),
or any positive integer value.
CDOView pre-fetching policy that defines which related revisions should be pre-fetched
when a revision is loaded. The pre-fetching policy can be implemented using the
CDORevisionPrefetchingPolicy interface and set on the view using the
setRevisionPrefetchingPolicy() method.
The pre-fetching policy can define complex rules for pre-fetching related revisions based on
the type of the requested revision, its features, and other criteria. The result of the pre-fetching policy
is a list of revision IDs that should be pre-fetched along with the requested revision.
CDOFetchRuleManager
to define multiple fetch rules for pre-fetching target revisions of one or more references
of the requested revisions. The fetch rules are sent to the server whenever revisions are loaded and evaluated there.
The target revisions of the references that match a fetch rule are then pre-fetched and sent back to the client
along with the requested revisions. For more information about fetch rules, refer to the CDOFetchRule interface.
CDOUnits. Units of your model are disjunct (non-overlapping) subtrees within the model,
identified by their root objects. They can be created, opened, and closed using the unit manager
of a CDOView. Units are very useful when your model, for example, contains multiple independent projects or documents,
and you want to load and work with a complete project or document (including all its contained objects) at once.
For more information about units, refer to Units.
CDOPrefetcherManager that can be used to manage the pre-fetching for an entire
ResourceSet. It automatically creates and manages pre-fetchers for all views
that are associated with the resource set. Each pre-fetcher asynchronously pre-fetches the revisions of all objects of its view.
The pre-fetching is performed in the background and does not block the main thread of the application. A pre-fetcher
listens to changes of the view's target branch point and automatically adjusts its pre-fetching accordingly.
A pre-fetcher can also be configured to pre-fetch lock states
of objects in addition to their revisions.
Manage branches and tags within the repository. Branches enable parallel
development and versioning. Tags provide meaningful names for specific points in a branch's history.
The branch manager is automatically available in every session and can be accessed using the
getBranchManager() method. The branches and tags managed by the branch manager
are specific to the connected repository and are not shared between different repositories or even between different sessions within
the same repository. Not all branches and tags may be immediately available in the branch manager.
Branches and tags are loaded on demand when they are accessed for the first time.
Note that only the main branch is guaranteed to be available for all repository modes.
Sub-branches are only supported in branching. Tags are only supported in
auditing.
For more information about branch management, refer to the CDOBranchManager interface.
Branches are organized in a tree structure, starting from the main branch. In addition to
their immutable technical IDs, branches have user-friendly names, which can change over time. Due to the tree structure
of branches, a branch name is only unique within its parent branch. To uniquely identify a branch, you need to
specify its full path, which includes the names of all its ancestor branches.
Here is an example of how to access branches using the branch manager:
Here is an example of how to retrieve all sub-branches of a given branch:
Here is an example of how to create new branches:
Here is an example of how to listen for branch changes:
For more information about branches, refer to the CDOBranch interface.
Tags are used to mark specific points in a branch's history with meaningful names. Unlike branches, tags do not
have a hierarchical structure. A tag name is unique within the entire repository. Tags
refer to a specific branch point, which consists of a branch and a timestamp.
Tags are often used to mark releases, milestones, or other significant events in the development process.
Both the name and the target branch point of a tag can change over time, also referred to as "renaming tags" and "moving tags".
Here is an example of how to access tags using the branch manager:
Here is an example of how to retrieve all tags associated with a specific branch:
Here is an example of how to create new tags:
Whether a tag created, renamed, moved, or deleted in your session or in another session, the tag list of the branch manager can notify you about these changes. Here is an example of how to listen for tag changes:
For more information about tags, refer to the CDOBranchTag and CDOTagList interfaces.
Control how data is fetched from the repository. Learn to define fetch rules for efficient data loading and minimize network overhead.
You can use a session-wide CDOFetchRuleManager
to define multiple fetch rules for pre-fetching target revisions of one or more references
of the requested revisions. The fetch rules are sent to the server whenever revisions are loaded and evaluated there.
The target revisions of the references that match a fetch rule are then pre-fetched and sent back to the client
along with the requested revisions.
A fetch rule consists of:
class that specifies the type of the revisions for which the fetch rule applies,
references of the target class whose target revisions should be pre-fetched, and
Here is an example of how to create a custom fetch rule manager that pre-fetches the contents of a specific resource whenever that resource is loaded:
As fetch rules specify what features to pre-fetch, it often makes sense to monitor what features are actually
used in your application. This can be achieved by using a CDOFeatureAnalyzer that is attached to the
view options of your views and transactions.
CDO provides two standard implementations of a combined fetch rule manager and feature analyzer:
UIFeatureAnalyzer – Designed for interactive applications with a user interface.
It tracks feature usage and creates fetch rules based on recent access patterns to optimize data
loading for typical user interactions.
ModelBasedFeatureAnalyzer – Designed for applications that process models in a non-interactive way.
It analyzes the model structure and creates fetch rules based on the containment hierarchy
to ensure that related objects are loaded together. This is particularly useful for batch processing
and model transformations.
Here is an example of how to use the UI feature analyzer in a transaction:
See also Pre-fetching Revisions and Units.
For more information about fetch rules, refer to the CDOFetchRule interface.
Access commit history and metadata. This facility allows you to query, filter, and analyze commit information for auditing and tracking changes.
A CDOCommitInfo object represents metadata about a single commit operation in the repository.
It includes information such as the commit's unique identifier (the timestamp of the commit), the user who made the commit,
the branch in which the commit was made, an optional commit message, and an optional CDOCommitData object, which
provides detailed information about the changes made in the commit.
Whether a repository stores commit information depends on its mode. Commit
information is only available in auditing mode or higher. In
addition, the repository must be configured to store commit information, i.e., its
IRepository.Props.COMMIT_INFO_STORAGE property must not be set to CommitInfoStorage.YES or
CommitInfoStorage.WITH_MERGE_SOURCE.
Whether a repository provides commit information with complete change set data to a client
depends on the passive update mode of the client's session, see Passive Updates and Refreshing.
The commit info manager is automatically available in every session and can be accessed using the
getCommitInfoManager() method. The commit info manager
supports efficient retrieval of commit information by timestamp, branch, and other criteria.
Here is an example of how to retrieve commit information using the commit info manager:
Here is an example of how to retrieve the last commit info of a specific branch:
There are many more methods available on the commit info manager for querying commit history, such as retrieving commits within a specific time range, filtering commits by user or message, and accessing the detailed change data of commits.
For more information about commit info management, refer to the CDOCommitInfoManager interface.
If a repository is configured to authenticate users, the authenticated session provides access to just the
user ID of the authenticated user. The user info manager allows you to retrieve
additional information about users, such as their full name, email address, and other custom attributes.
This information can be useful for displaying user-friendly names in the UI, sending notifications,
and performing user-specific operations. Technically, the user infos are made available as Entitys that the repository
creates and manages according to its user management configuration. See also Client and Server Entities.
User infos that are loaded from the repository are cached in the user info manager to optimize performance and reduce network overhead.
The user info manager is automatically available in every session and can be accessed using the
getUserInfoManager() method.
Here is an example of how to retrieve user information using the user info manager:
For more information about user info management, refer to the CDOUserInfoManager interface.
Monitor and interact with other sessions connected to the repository. Useful for collaboration and administrative tasks.
In collaborative environments, it is often necessary to monitor other sessions connected to the same repository, exchange messages, and coordinate activities. The Remote Session Manager provides APIs to observe, interact with, and manage remote sessions, enabling features such as notifications, messaging, and presence.
The CDORemoteSession interface represents a session connected to the repository, other than the local session.
It provides methods to query session properties, send messages, and subscribe to events.
Events such as session connection, disconnection, and message reception are emitted to allow applications to react
to changes in remote session state.
The CDORemoteSessionManager manages all remote sessions for a repository. It allows you to enumerate active sessions,
subscribe to session events, and send messages to one or more sessions. Events include session addition, removal, and message delivery.
To access the remote session manager, use the getRemoteSessionManager() method.
Here is an example of how to use the remote session manager to enumerate remote sessions:
To opt-in for remote session management, the client's remote session manager must be enabled by subscribing to remote session information.
This subscription can be done automatically when you add a listener via addListener() to the remote session manager.
Automatic subscription occurs when the first listener is added and is canceled when the last listener is removed.
Subscription can also be forced, by calling setForceSubscription(true).
Without subscription, the remote session manager will not receive updates about remote sessions.
Here is an example of how to subscribe to remote session events using the remote session manager:
Exchanging messages between sessions enables real-time collaboration, notifications, and coordination among users. This is useful for chat, alerts, or workflow triggers.
The CDORemoteSessionMessage interface represents a message sent between sessions.
It contains a type indicating the message's purpose,
a priority, and payload data
with the actual content in form of a byte[]. To interpret the payload data, you need to agree
on a serialization format with the message sender and receiver. The type of the message can be used to
differentiate between different kinds of messages and to determine how to process the payload data.
CDO does not impose any restrictions on the message content. You can use any serialization format you like,
such as JSON, XML, Protocol Buffers, or custom binary formats.
You can send messages to a specific remote session by using CDORemoteSession.sendMessage(CDORemoteSessionMessage).
A message sent to a remote session is delivered only if the target session is subscribed to receive messages.
You can also broadcast messages to all subscribed remote sessions using
CDORemoteSessionManager.sendMessage(CDORemoteSessionMessage, CDORemoteSession...),
or CDORemoteTopic.sendMessage(CDORemoteSessionMessage) (see Collaborating on Shared Topics).
These methods deliver the message to the target session(s) or topic subscribers.
Here is an example of how to send messages to other sessions using the remote session manager:
To receive messages, register a listener for CDORemoteSessionEvent.MessageReceived events.
The listener will be notified whenever a message is received from another session or topic.
Here is an example of how to receive messages from other sessions using the remote session manager:
Shared topics allow users to collaborate by grouping sessions around common subjects, such as projects, documents, or activities. Topics provide a way to broadcast messages, track presence, and coordinate work.
The CDORemoteTopic interface represents a collaboration topic. It emits events for topic subscription,
message delivery, and presence changes. You can subscribe to a topic using CDORemoteSessionManager.subscribeTopic(String).
This adds your session to the topic and enables you to receive messages and presence updates.
If the topic does not exist, it is created automatically.
The presence of remote sessions in a topic can be queried using CDORemoteTopic.getRemoteSessions().
As the CDORemoteTopic is an IContainer<CDORemoteSession>, you can also listen for presence changes.
Changes in presence are notified via IContainer events.
To collaborate, send messages to a topic using CDORemoteTopic.sendMessage(CDORemoteSessionMessage). These
messages are broadcast to all sessions that are subscribed to the topic.
Subscribers receive messages via CDORemoteTopicEvent.MessageReceived events.
To leave a topic, call CDORemoteTopic.unsubscribe(). This removes the session from the topic and
stops further event notifications. Other sessions in the topic are notified of your departure.
Here is an example of how to collaborate on a shared topic using the remote session manager:
Integrating topics into your UI allows users to see available topics, track presence,
and view messages in real time. This enhances collaboration and awareness.
You can create a custom UI component to display topics, their members, and messages using the APIs provided by
the CDORemoteSessionManager and CDORemoteTopic interfaces. This component can list all subscribed topics,
show the presence of members in each topic, and display messages as they are received. Such a custom component
can be tailored to fit the specific needs and design of your application.
Alternatively, you can use the built-in CDORemoteTopicsView provided by CDO. The CDORemoteTopicsView provides a user interface for displaying topics, session presence, and messages. It displays all topics that the local session is subscribed to, along with the members of each topic. It can be extended to show additional topic information, such as images, labels, descriptions, or custom metadata. This extension can be achieved by implementing a custom CDOTopicProvider, which defines how topics are represented in the view. The CDOTopicProvider interface allows you to specify the topic ID, label, description, and image for each topic, as well as how to create and manage topic listeners. The view uses the topic provider to display topics and their members.
To integrate an editor with CDORemoteTopicsView, implement a CDOTopicProvider and its
Topic and Listener interfaces. For example, the built-in CDOEditor treats
CDOResources as topics, subscribing to the relevant topic on activation and unsubscribing on deactivation.
This ensures the editor's presence and activity are reflected in the topics view on all clients.
Here is an example of how to integrate topics with your user interface using a custom topic provider:
Sessions emit events for lifecycle changes, errors, and repository updates. Learn how to listen for and handle these events to build responsive applications.
The following events are fired from CDOSession:
CDOSessionInvalidationEvent: Fired when the session receives invalidations due to commits from
other sessions. Contains details about the commit, branch, user, and changed objects.
CDOSessionPermissionsChangedEvent: Fired when the session's permissions change, such as when
the user's role is updated. Contains details about the new permissions.
CDOSessionLocksChangedEvent: Fired when the session's lock states change, such as when
locks are acquired or released. Contains details about the affected objects and their new lock states.
CDOSessionRecoveryEvent: Fired when a recovering session
attempts to recover from a connection loss. Indicates the progress and outcome of the recovery attempt.
Contains details about the recovery state and any errors encountered.
RepositoryTypeChangedEvent: Fired when the session detects a change in the repository type,
such as during a fail-over scenario. Contains details about the old and new repository types.
RepositoryStateChangedEvent: Fired when the session detects a change in the repository state,
such as when the repository goes offline or comes back online. Contains details about the old and new states.
LifecycleEvent: Fired when the session is opened or closed. Contains details about the lifecycle state.
Applications can listen for these events by registering listeners with the session. This enables responsive handling of repository changes, errors, and lifecycle events.
Example of listening for session events:
Configure session options to customize behavior such as passive updates, caching, and performance settings.
The options of a session are accessible via the options() method.
An option has getter and setter methods to retrieve and modify its value. When options are changed,
the IOptionsContainer emits events to notify listeners of the changes. This allows applications to react
dynamically to configuration changes.
Each option is documented in its own sub-chapter below.
Controls whether the session creates dynamic EMF packages for missing generated packages. This is useful when working with models that have not been generated yet. Can be enabled or disabled. By default, it is disabled.
API: CDOSession.Options.isGeneratedPackageEmulationEnabled(), CDOSession.Options.setGeneratedPackageEmulationEnabled(boolean)
Controls whether the session receives passive updates from the server. Can be enabled or disabled. By default, it is enabled.
API: CDOSession.Options.isPassiveUpdateEnabled(), CDOSession.Options.setPassiveUpdateEnabled(boolean)
Specifies the mode for passive updates (INVALIDATIONS, CHANGES, ADDITIONS).
The mode determines the type of updates the session receives from the server. By default, it is set to
INVALIDATIONS. In this mode, the session receives invalidation notifications
for changes made by other sessions. This means that when another session commits changes to the repository,
the session is notified that certain objects have changed, but it does not receive the actual changes.
Other modes include CHANGES, where the session receives detailed change information,
and ADDITIONS, where the session receives notifications about changes and new objects being added.
The choice of mode depends on the application's requirements for data freshness and performance.
API: CDOSession.Options.getPassiveUpdateMode(), CDOSession.Options.setPassiveUpdateMode(PassiveUpdateMode)
Controls whether the session receives lock notifications. Can be enabled or disabled. By default, it is disabled.
API: CDOSession.Options.isLockNotificationEnabled(), CDOSession.Options.setLockNotificationEnabled(boolean)
Controls the mode for lock notifications OFF,
IF_REQUIRED_BY_VIEWS, or ALWAYS.
The mode determines whether and when the session receives lock notifications from the server.
By default, it is set to IF_REQUIRED_BY_VIEWS, which means that
the session receives lock notifications only if it has views that require lock information. Other modes include
OFF, where the session does not receive any lock notifications, and
ALWAYS, where the session receives all lock notifications regardless of its views.
The choice of mode depends on the application's requirements for lock awareness and performance.
API: CDOSession.Options.getLockNotificationMode(), CDOSession.Options.setLockNotificationMode(LockNotificationMode)
Defines how large collections are loaded (e.g., all at once or in chunks). Collection loading can impact performance and memory usage. A collection loading policy helps balance performance and memory consumption when dealing with large collections.
You can create custom policies by calling createCollectionLoadingPolicy(int, int)
with your desired initial chunk size and resolve chunk size. The initial chunk size defines how many elements are loaded
when the collection is first accessed. The resolve chunk size defines how many additional elements are loaded
when more elements in the collection are accessed. A smaller chunk size reduces memory consumption but may require
more network round-trips. A larger chunk size improves performance but increases memory usage. The optimal chunk sizes
depend on your application's access patterns and resource constraints.
API: CDOSession.Options.getCollectionLoadingPolicy(), CDOSession.Options.setCollectionLoadingPolicy(CDOCollectionLoadingPolicy)
Manages caching of large objects to optimize memory usage and performance. You
can either implement your own cache by implementing the CDOLobStore interface and setting it via
Options.setLobCache(CDOLobStore), or you can use the built-in
CDOLobStoreImpl, which provides a simple file-based cache. The built-in cache can be configured to
store large objects in a specific directory on the file system. By default, the large object cache is
set to CDOLobStoreImpl.INSTANCE, which stores large objects in a temporary directory.
API: CDOSession.Options.getLobCache(), CDOSession.Options.setLobCache(CDOLobStore)
Controls how to update permissions of revisions when
passive updates are received or the InternalCDOSession.updatePermissions() method is called.
By default, the permission updater is set to CDOPermissionUpdater3.SERVER, which updates permissions
from the server.
API: CDOSession.Options.getPermissionUpdater(), CDOSession.Options.setPermissionUpdater(CDOPermissionUpdater3)
Specifies whether the session supports delegable view locks. All view methods
are protected from concurrent access by the view's view lock. By default, the view lock is the constant object
returned by CDOView.getViewLock(). This lock is reentrant, but it is not delegable. This means that if a thread
holds the view lock and calls a method that tries to acquire the same view lock again, it will succeed. However,
if the thread that holds the view lock schedules a task to be executed asynchronously (e.g., using
Display.syncExec(Runnable)) and waits for its completion, the task will not be able
to acquire the view lock because it is executed in a different thread. This can lead to deadlocks.
To avoid such deadlocks, you can enable delegable view locks. They are based on fact that the thread that holds the view lock does block and no longer needs the lock while waiting for the asynchronous task to complete. Hence, it can delegate the lock to another thread.
When delegable view locks are enabled, the view locks become instances of DelegableReentrantLock,
which uses DelegateDetectors that are contributed to IPluginContainer.INSTANCE in order
to decide whether a thread is allowed to delegate its lock to another thread.
The org.eclipse.net4j.util.ui plug-in contributes a
org.eclipse.net4j.util.internal.ui.DisplayDelegateDetector that allows
delegation to SWT's UI thread. This is particularly useful when a background thread that holds the view lock
(such as a model change listener or EMF Adapter) needs to update the UI synchronously and
the update code (which is executed in the UI thread) needs to access the view.
An alternative to globally enabling delegable view locks for all views in a session is to use
CDOUtil.setNextViewLock(Lock) to set a specific delegable view lock
for the next view that is created in the current thread.
API: CDOSession.Options.isDelegableViewLockEnabled(), CDOSession.Options.setDelegableViewLockEnabled(boolean)
Revision prefetching can improve performance by reducing
the number of network round-trips required to load multiple revisions. On the other hand, prefetching
can lead to some revisions being loaded unnecessarily, because the session already has them in its cache.
To address this, the session tells the server which revisions it already has in its cache by sending their
revision keys in the prefetch request. However, sending too many revision keys can increase
network overhead and impact performance negatively.
To balance these concerns, the prefetch send max revision keys option allows you to limit
the number of revision keys that are sent in a single prefetch request. The default value is 100.
You can also change this default value by setting the system property PREFETCH_SEND_MAX_REVISION_KEYS.
API: CDOSession.Options.getPrefetchSendMaxRevisionKeys(), CDOSession.Options.setPrefetchSendMaxRevisionKeys(int)
Access and modify session properties to store custom metadata and configuration values.
The CDOSession.properties() map allows you to store and retrieve custom
properties associated with the session.
This can be useful for storing session-specific metadata, configuration values, or other information
that you want to associate with the session. The properties map is a simple key-value store
where the keys are strings and the values are arbitrary objects. The map is thread-safe,
so you can safely access and modify it from multiple threads. It also supports change listeners,
so you can be notified when properties are added, removed, or changed.
The properties are not persisted and are lost when the session is closed.
The CDOSessionRegistry keeps track of all active sessions in the application.
It allows you to enumerate, find, and manage sessions. You can also register listeners
to be notified of session lifecycle events, such as when sessions are opened or closed.
The session registry is a singleton and can be accessed via CDOSessionRegistry.INSTANCE.
It provides methods to get all active sessions and find sessions by their global ID.
Global IDs are assigned to each session when it is opened. They are unique within the application
and can be used to identify and reference sessions. The session registry maintains
a mapping between global IDs and sessions, allowing you to look up sessions by their global ID.
The global ID of a session, which is assigned by the session registry,
must not be confused with the session's repository-specific ID,
which is assigned by the repository and may not be unique across different repositories.
Here is an example of how to use the session registry to close all active sessions: