Unified or Pivot Programmers Guide

Unified or Pivot Programmers Guide

OCL Environment
The Pivot Evaluator
Pivot Standalone Configuration
Pivot Thread Safety
Parsing Constraints and Queries
Evaluating Constraints and Queries
Parsing OCL Documents
OCL Relationship to Metamodels

The Unified or Pivot Programmers Guide describes the ways in which the Pivot binding Eclipse OCL can be used from Java programs.

The Pivot binding was first available as an examples quality prototype in 3.1.0 (Indigo). The Pivot binding became the preferred binding in 6.0.0 (Mars). The older Ecore and UML bindings are described in a separate Ecore/UML Programmers Guide.

The OCL Parser/Interpreter provides an implementation of the Object Constraint Language 2.4 specification for EMF-based metamodels and models. It offers OCL constraint and query parsing and evaluation, model-based validation, and provides an infrastructure for content assist in textual editors.

The following features are supported in the current version:

The above constructs are supported by the parser for parsing and for evaluation, with the exception of the oclIsNew() operation and message expressions. All of the above are supported for both Ecore and UML models. The following are supported by default for UML (both in parsing and evaluation):

The following features are provided in addition to the OCL specification:

The OCL implementation is defined in plug-ins for convenient deployment in Eclipse, but as is the case for EMF, it can also be used stand-alone. The plug-ins are partitioned thus:

OCL Environment

The Pivot-based Eclips OCL maintains signifcant amount of working state to cache aspects of the user metamodels and models.

The OCL working meta-state used for OCL parsing and analysis comprises at least a CompleteEnvironment, CompleteModel, EnvironmentFactory, MetamodelManager, Orphanage and StandardLibrary.

The additional OCL working state for OCL execution comprises an Executor and a ModelManager.

OCL Implementation Problem

Eclipse OCL exploits the Eclipse Modeling Framework (EMF) and so has to comply with EMF APIs. Unfortunately the most basic EMF API getXXX() to access the feature xxx provides no ability to pass any OCL working state to an OCL implementation of getXXX(). There is also no ability to create or destroy the OCL working state before and after a sequence of activities for which the OCL requirement of model invariance is satisfied.

OCL adapters pre-2021-03

Prior to the 2021-03 (6.14.0) release, the OCL working meta-state was lazily created on demand and eventually garbage collected. The working meta-state was persisted by use of an EnvironmentFactoryAdapter to attach the state to the ResourceSet containing the user model / metamodel. The state could be recovered by a hiearchical search of the user model’s containment hierarchy. The OCL facade supports multiple usage of the state via a share count so that GC only kicks in once all usage is done. A WeakOCLRef provides another mechanism for sharing.

The OCL working state is similarly maintained via ResourceSet adapters and also by an entry in the validation context of the EMF validate() API.

The above approaches work for straightforward usage. But...

When OCL invariants or getters are executed from an installed model, there is no ResourceSet and so the user’s OCL context is not available. Rather a special global OCL working meta-state is used; it ignores any Complete OCL contributions from the user’s application.

Models shared between applications are isolated by careful use of ValidationAdapters to identify which OCL is involved.

Multi-threaded applications may successfully share the working state provided the user has resolved all other threading hazards.

Applications using multiple OCLs such as one reference OCL for an old-way, and another experimental OCL for a new way, may successfully use distinct adapters.

However from a developer’s perspective it’s all very fragile and complicated and metamodel schizophrenia is always waiting for an opportunity to bite.

Avoiding leakage with EMF applications is quite hard since a single refernce from one of the global registries can easily lock everything into memory,

Identifying when the user model analysis in the working state can be re-used and when it must be recomputed is hard since there is no mechanism for a user to delimit the duration of model invariance.

Investigation of a bug in the implicit opposites underpinning a stereotype in a static UMLprofioe revealed that the analyses supporting allInstances and implicitOpposites was very very pessimitic and consequently very inefficient.

The major use cases for OCL applications are model validation and model transformation. For the latter, the transformation tool can easily initialize the OCL state prior to transformation and clean up afterwards. For the former case, a massive performance improvement is possible if the one working state can be re-used throughout the model validation.

OCL per Thread post-2021-03

For the 2021-03 (6.14.0) release a new approach is taken. The OCL working state is referenced by a thread local variable making discovery easy and cleanup inherent in the demise of the thread. Lazy creation is fairly easy too.

There is no need for a special global OCL context with defective capabilities; the GlobalEnvironmentFactory and its INSTANCE is obsolete.

But, by default multi-threaded applications have a distinct OCL working state per-thread, which may be beneficial or inefficient. if the programmer is able to manage thread safety, then the ThreadLocalExecutor.set API may be used to share the same OCL state on more than one theead. NB the current OCL code has some synchronizations that may help with thread safety but overall there is no guarantee of thread safety.

But, multi-OCL applications cannot have more than one active OCL per-thread.

One variation of the multi-OCL use case was prevalent in the OCL JUnit tests where one OCL processed a reference model while another OCL processed an application model. The solution for interleaved usages is to invoke OCL.deactivate or ThreadLocalExecutor.detachEnvironmentFactory to suspend one OCL before OCL.activate or ThreadLocalExecutor.attachEnvironmentFactory resumes another OCL. The multiple OCLs can therefore co=exist till eventually OCL.dispose() cleans up.

Another variation of the multi-OCL use case was prevalent in the QVTd JUnit tests where one main extended OCL processed an application model while a nested OCL validated/serialized intermediate results. The same deactivate/activate solution again ensures that only one OCL is actve.

The use of concurrent OCLs on the same thread is diagnosed and results in a log message such as Concurrent EnvironmentFactory instances inhibit local thread Executor passing forcing th code to fall back on the old adapter mased approach. The solution, as described above, is to use deactivate/activate to eliminate the concuurent activity.

But, whereas the old approach was very pessimistic creating a new OCL working state far too often, the thread-based OCL state must be invalidated by invoking ThreadLocalExecutor.reset() whenever the user models are changed in any way that might undermine the OCL assumption that models do not change. (This change could be automated using EMF’s TrackingModificatinAdapters but the cost is considered too high compared to a manual reset).

But, there is currently no support for sharing the UI thread across many IWorkbenchPart. i.e if two views both use OCL on the UI thread, the state of one view may confuse the other. Fortunately OCL activity such as validation is generally delegated to a worker thread avoiding the confusion and guaranteeing a new OCL per usage.

The adapters associated with the old approach are now obsolete but remain for now as legacy/compatibility clutter. They will be removed in a future release.