Rational Software Architect (RSA) is a well-known UML modelling environment, that I use extensively in my research. Indeed, I used it to create a UML profile for representing RBAC requirements, and created rbacUML, a plugin (it is open source software, under the EPL licence) that provides several RBAC-related features (there’s another very exciting one coming soon!). In rbacUML, I use OCL constraints and queries extensively, not only to verify the well-formedness of the domain-specific modelling language (DSML) I created, but also to perform various kinds of analysis on UML models annotated with RBAC concerns.
One of the advantages of RSA is that it is built on Eclipse, which makes it relatively easy to extend and interface with other products. In particular, RSA uses Eclipse’s modelling projects, including the EMF-based representation of UML or the OCL project. It also adds a lot of interesting features to the stock Eclipse package, such as the graphical representation of UML models, or various profile edition and tooling creation features. In this post I will focus on the UML profile feature.
UML profiles are the official way of extending the UML metamodel to express concepts that were not included in the official metamodel. To make things short, UML profiles consist of stereotypes that can be applied on any kind of UML element, associations and annotations, and OCL constraints to enforce specific properties. RSA provides an easy way of creating such profiles by defining stereotypes, specifying the elements on which they can be applied, associations, and other options. It also allows developers to attach OCL constraints to stereotypes. Models using the stereotype can then be checked against the set of OCL constraints defined on the profile, and warnings and failures will be displayed in the model explorer, on the diagrams and in the Problems view. This is very handy to ensure the well-formedness of a model, or to perform more advanced types of analysis. Even better, the constraints only have to be written once and they will be applied on all models that use the profile.
However, there is one problem: the closure() iterator. The closure() iterator provides the transitive closure of an association. It is very useful, for example, to deal with role hierarchies. Indeed, given
self as a role, it is possible to form the set of all its ancestor roles using something like self->closure(parent), which would capture self->parent, self->parent.parent, self->parent.parent.parent, etc, until reaching the ancestors that have no parents.
Unfortunately, the closure() iterator was not part of the official OCL standard until version 2.3.
RSA (version 8.0, release before OCL 2.3) insists on developers creating only standard-compliant OCL constraints, and therefore will issue a warning to the developer during the profile development to remind him that he is using a non-standard iterator. It doesn’t stop there, unfortunately. Profile users trying to validate a model using an OCL constraint that contains a closure() iterator will always get… an error, no matter what the result of the OCL constraint evaluation is.
As far as I know, there is no obvious way to relax that strict standard requirement to turn the errors into at least warnings, or better, to silence them completely. The solution I found has been posted on the Eclipse forum and involves directly using the underlying Eclipse UML and Eclipse OCL implementations:
ocl = OCL.newInstance(resourceSet);
Helper helper = ocl.createOCLHelper();
BasicEnvironment benv = OCLUtil.getAdapter(ocl.getEnvironment(), BasicEnvironment.class);
Constraint constraint = element.getNearestPackage().getAppliedProfile("profile").getOwnedStereotype("stereotype").getOwnedRules().get(0);
Stereotype stereotype = ((Classifier)element).getAppliedStereotype("profile::stereotype");
OCLExpression expression = helper.createQuery(constraint.getSpecification().stringValue());
Query query = ocl.createQuery(expression);
Object result = query.evaluate(element.getStereotypeApplication(stereotype));
Several things are worth nothing:
element is the UML element on which we want to evaluate the constraint (self)
- the name of the stereotype we want to access is the fully qualified name, of the following form:
<profile name>::<stereotype name>
evaluate() method returns an
Object, but in the case of a constraint it will of course be a boolean value
- the code snippet assumes that an element, or a list of elements, has been selected, and that the correct resources set is passed to
OCL.newinstance(). The resource set can be obtained from an element through
helper.createQuery() will throw a
This solution will evaluate any constraint, even those containing the closure() iterator, without complaining. A similar code snippet can be used to evaluate other OCL constraints that are not embedded in the profile, and even to evaluate queries, that do not return a boolean value. That will be for another post.
EDIT: Fixed the code, there was a bug