Understanding Models

Models managed by the platform, whether UML models or Domain Specific models, are all model instances of some Ecore models. As an example, every UML model in RMP is an instance of the UML.ecore metamodel available in UML2's org.eclipse.uml2.uml plug-in, which is an implementation of the official UML 2.0 specification. UML models are read and modified using the Java interfaces generated from UML.ecore using EMF's code generation capabilities. The objects found in such EMF model instances are called EMF objects.

As a concrete example, the UML 2.0 specification defines the concept of a Package. In UML2, this concept was modeled in UML.ecore as a class named Package. UML2 also leveraged EMF to generate the interface named Package. When a user creates a UML package using RMP's UML Modeler, internally the tool instantiates an object that implements the Package interface.

The interfaces implemented by an EMF object generally include EObject. This interface gives generic access to EMF objects' content, provides a reflection API and helps determine whether objects are loaded in memory. Because EObject extends Notifier, every EMF object can notify listeners upon state change. In EMF, listeners are called Adapters.

The following diagram presents the interface hierarchy of the UML2 Package metaclass.

UML2 Package Hierarchy

The root of all UML2 interface is Element. This interface specializes EMF's EModelElement, an interface which adds the ability to associate annotations to an EMF object.

Editing Domain

EMF introduced the concept of an Editing Domain to control the lifecycle of models through commands. Among the most important features of Editing Domain are its command stack and its resource set. The DevOps Modeling Platform uses a specialization of EMF's Editing Domain found in the EMF Transactions component. This specialization enables access to models in a multithreaded environment. The editing domain used by RMP's UML Modeler is accessed through the UMLModeler.getEditingDomain method.

Command Stack

The CommandStack contains commands executed to modify the models controlled by the editing domain. A command is executed by calling CommandStack.execute(). A Command is responsible for the modification of a model using the latter's APIs. If it is undoable, the command is also responsible for undoing and redoing the modification. Commands can be chained to form a composite command.

Resources

Models managed by an Editing Domain are contained in Resources. Resources are associated a URI that determines where to load and save the resource's contents, which is simply a list of EObjects. For a resource containing a UML2 model, the list may very well only contain a single EObject, Model. This object is the container for the remaining elements of the model. The resources managed by a given Editing Domain are accessible through the latter's ResourceSet, which is accessible through the getResourceSet() method.

Proxies

An EMF Object owned by a resource can reference other EMF Objects owned by different resources. Such cross-resource references are not resolved until the reference is actually accessed. Until the reference is resolved, it references a Proxy EObject, which is a lightweight object containing a URI to the referenced object. EObject.eIsProxy() can be used to test whether an EObject is a proxy. Usually, the API generated by EMF to access a reference from an EMF object will attempt to resolve it. As an example, the UML2 Generalization.getGeneral() method will attempt to resolve the Classifier it returns.

When resolving a proxy, EMF will first see if the resource containing the referenced element is already loaded in the ResourceSet that contains the resource of the referencing object. If found, it just finds the object in that resource and returns it. If not found, it will attempt to locate the resource using the proxy's URI and attempt to load the resource in the referencer's ResourceSet. If successful, it again finds the object and returns it. If for any reason it cannot locate and/or load the resource, EMF will return back the same proxy. The lesson here is that even though EMF's generated API attempt to resolve proxies, clients should always validate whether the EObject they get back from an API is a proxy.

URIs

URIs are used in EMF to locate resources as well as objects within a resource. A URI consists of a subset or all of the following components:

<scheme>://<authority><path>?<query>#<fragment>

EMF will resolve any URI supported by Java such as URIs using the file scheme.

EMF supports Eclipse's platform scheme to access resources within a workspace. For instance, a URI such as platform:/resource/project1/folder1/model.emx will refer to a file named model.emx in a folder named folder1 in a project named project1.

Also supported are URIs making use of the pathmap scheme. Pathmap variables are defined in the preferences page accessible from Window > Preferences... > Modeling > Pathmap. For instance, pathmap://UML_LIBRARIES/Ecore.library.uml2 references the file named Ecore.library.uml2 located at the location specified for the symbol UML_LIBRARIES in the preference page.

Reflection API

Background

Before delving into EMF's Reflection API, we will define a few concepts.

A metaclass is a class describing a set of classes. A class is an instance of its metaclass

A metamodel is a model describing a set of models at a different abstraction level. A model is an instance of its metamodel. A metamodel contains a set of metaclasses and associations between them called metaassociations. A metamodel can be an instance of a metametamodel or an instance of itself if it is self-sufficient.

The following diagrams provides an example of a model instantiated from a metamodel.

Example of a model instantiated by a metamodel

EMF's Reflection

Not unlike Java's own reflection API, EMF objects offer an API to inquire about an object's metaclass. EMF's provides richer information and performs faster. The metainformation actually reflects the information in the Ecore model used to instantiate the EMF object in the first place.

An EMF Object, thanks to its implementation of the EObject interface, supports a method called eClass that will return its metaclass. The metaclasses of an Ecore model are objects of type EClass that provide the metaclass name, the list of the metaclasses it extends, the list of its attributes and operations, and more. Attributes are described in EMF by EStructuralFeature. Two EStructuralFeature specializations are worth mentioning: EReference which provides information about attributes referencing other EObjects, and EAtttribute which provides information about attributes not referencing other EObject.

For a given Ecore model, the implementation EMF generates usually includes a Package interface that provides access to all metaclasses of the model. As an example, the Package generated for UML2 is UMLPackage. This class can be used to retrieve UML2 metaclasses such as Actor (through its getActor() method). It can also be used to access metaattributes such as a Generalization's "General" (through its getGeneralization_General() method)

The Ecore metamodel

The following diagram shows the main classes of EMF's own Ecore model, named Ecore, which is the metamodel for all Ecore models:

Ecore Metamodel Hierarchy

We often refer to this model as the Ecore metamodel, as opposed to an Ecore model, which is an instance of the former.

Noteworthy is the fact that the Ecore metamodel was bootstrapped using EMF itself; the Ecore metamodel is its own metamodel. The model describing Ecore is an Ecore model defined in a resource named Ecore.ecore available in EMF's org.eclipse.emf.ecore plug-in.


Legal notices