280 likes | 471 Views
UML2 Package Merge. Usage scenarios and their effect on XMI and Java API interoperability Bran Selic, Jim Amsden, Kenn Hussey Oct, 2003. Package merge specifies how one package extends another by merging its contents. Package import makes imported elements visible
E N D
UML2 Package Merge Usage scenarios and their effect on XMI and Java API interoperability Bran Selic, Jim Amsden, Kenn Hussey Oct, 2003
Package merge specifies how one package extends another by merging its contents • Package import makes imported elements visible • Package merge copies non-matching elements and merges matching elements through specialization and redefinitions • Package merge is a shorthand for defining the same specializations and redefinitions, it does not introduce any new semantics • A Model can be exchanged using XMI by retaining the package merges, or by transforming them away
For example, package constructs extends basic by merging its contents with new features Target Merged Package Source Merging Package (this is the package that is updated by the merge)
MOF2 and UML2 Superstructure uses Package Merge to define a number of different modeling languages and compliance levels • Package merge was introduced to support metamodel reuse • UML2 was partitioned to support Basic, Constructs, Kernel, and Levels L1, L2, and L3 • Package merge is used to construct the metamodel language at each level by reusing and merging packages • Levels are specified in UML2 Superstructure, Section 2 • Each level defines a new metamodeling language that has the capabilities of the lower level with additional capabilities
For example, consider merging package basic into package constructs to produce a new language: “constructs” UML2 InfrastructureLibrary does not use package merge directly, but was rather produced by applying the package merge rules “by hand” to produce the Constructs model. This example is not intended to be from InfrastructureLibrary, but rather just an illustration of package merge semantics using simple concepts from InfrastructureLibrary. Target Merged Package Source Merging Package (this is the package that is updated by the merge)
Package basic is the original metamodel we want to extend basic is a complete model that may have existing instances (XMI documents) and client applications
Package constructs starts out having only the deltas or extensions we want to add • constructs is an incomplete model specifying only the new features • constructs could be complete, but the merge result would be the same • For readability, UML2 specifies constructs as a complete model
The merge dependency is converted to an import • Package basic is unchanged • Package constructs specializes and redefines elements in basic • and contains the new capabilities • The result must have classes from both packages
The package merge produces a new complete package constructs that: • specializes and redefines matching elements in basic • includes new elements
Consider the “compatibility” between basic and constructs • There may be existing instances of basic XMI documents • XMI is used to interchange data between basic and constructs based tools • It must be possible to read the existing XMI documents for basic into the new constructs model • It must be possible to read new XMI documents for constructs into basic • There may be existing client applications or tools using the basic API • Existing basic tools should be able to use the constructs API without change
ResourceSetresourceSet=newResourceSetImpl(); Resourceresource=resourceSet.createResource(URI.createFileURI( outputFile)); basic.ClassintType=factory.createClass(); intType.setName("Integer"); resource.getContents().add(intType); basic.ClassstringType=factory.createClass(); stringType.setName("String"); resource.getContents().add(stringType); basic.ClassclassY=factory.createClass(); classY.setName("Y"); resource.getContents().add(classY); basic.ClassclassZ=factory.createClass(); classZ.setName("Z"); resource.getContents().add(classZ); PropertyattribY=factory.createProperty(); attribY.setName("distinguishedZ"); attribY.setType(classZ); classY.getOwnedAttribute().add(attribY); TypeattribYType=attribY.getType(); buildAssociation(classY,"containedZ",classZ,"y"); protectedstaticvoidbuildAssociation(basic.ClassclassA,StringnameA,basic.ClassclassB,StringnameB) { PropertyattribA=factory.createProperty(); attribA.setName(nameA); attribA.setType(classB); classA.getOwnedAttribute().add(attribA); PropertyattribB=factory.createProperty(); attribB.setName(nameB); attribB.setType(classA); classB.getOwnedAttribute().add(attribB); attribA.setOpposite(attribB); attribB.setOpposite(attribA); } Here’s an example basic client application that creates an instance of a model
And the corresponding XMI file for the model <?xml version="1.0" encoding="ASCII"?> <xmi:XMI xmi:version="2.0" xmlns:xmi=http://www.omg.org/XMI xmlns:basic="http:///org/omg/uml2/infrastructure/basic"> <basic:Class name="Integer"/> <basic:Class name="String"/> <basic:Class name="Y"> <ownedAttribute name="distinguishedZ" type="/3"/> <ownedAttribute name="containedZ" type="/3" opposite="/3/@ownedAttribute.0"/> </basic:Class> <basic:Class name="Z"> <ownedAttribute name="y" type="/2" opposite="/2/@ownedAttribute.1"/> </basic:Class> </xmi:XMI>
The basic XMI file is partially compatible with the constructs XMI because of specialization • A constructs application can read the basic XMI document • But the XMI resource would create instances of the basic superclasses, not the constructs subclasses • A “downcast” model-to-model mapping from basic instances to constructs instances is required in order to use the existing basic model instances in the extended API • The constructs XSD doesn’t contain some of its redefined constraints because the property is implemented in the basic superclass
The XMI document for the same model in constructs has a different namespace <?xml version="1.0" encoding="ASCII"?> <xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:constructs="http:///org/omg/uml2/infrastructure/constructs"> <constructs:Class name="Integer"/> <constructs:Class name="String"/> <constructs:Class name="Y"> <ownedAttribute xsi:type="constructs:Property" name="distinguishedZ" type="/3"/> <ownedAttribute xsi:type="constructs:Property" name="containedZ" type="/3" opposite="/3/@ownedAttribute.0"/> </constructs:Class> <constructs:Class name="Z"> <ownedAttribute xsi:type="constructs:Property" name="y" type="/2" opposite="/2/@ownedAttribute.1"/> </constructs:Class> </xmi:XMI> As a result, a basic client cannot import a constructs model with compatible information without doing another model-to-model mapping
The basic client application is also partially compatible with the constructs API • The basic application can be compiled with the new constructs API because it specializes basic • However, in order for the application to use new constructs API, it must: • Change all the package imports from basic to constructs • Use a different factory for creating model element instances • This is even if the application doesn’t use any of the new features
constructs client applications have to do a lot of downcasting because Java can’t implement UML2 redefinition semantics • Java cannot hide an inherited member in one subclass, but not another • So we’re forced to remove the redefining property and use the inherited redefined property • Note that we set the type to a constructs Type, but have to downcast because of the package merge. • The result of getType is a basic Type, not a constructs type since the property is inherited • The constructs API can’t override the accessor because Java does not allow method override based on return type constructs.ClassclassZ=factory.createClass(); classZ.setName("Z"); resource.getContents().add(classZ); // Create Association distinguished: Z <-- :Y PropertyattribY=factory.createProperty(); attribY.setName("distinguishedZ"); attribY.setType(classZ); classY.getOwnedAttribute().add(attribY); // get the type of attribY. TypeattribYType=(Type)attribY.getType(); After the many levels of merges in UML2, client developers will never be able to remember what has to be cast to what and when, leading to unexpected results
In summary, UML2 package merge results in some problems for XSD and Java API generation: • The API is more complex due to the extra supertypes and redefinitions • After merging, the XMI file for all of UML2 is 10231943 bytes and takes about 15 minutes to load into EMF • L3::Classifier has 35 direct superclasses • Redefinitions cannot be directly implemented in XML Schema or Java • Loading an old basic XMI file will produce basic instances, not the new constructs instances • A model-to-model mapping is required to "downcast" the basic instances to the new constructs modeling language • Caused by the namespace change in the XMI document • Any existing basic clients have to be edited to use the new constructs package, even if they don't use any of the new features
Most problems result from the many specializations and redefinitions introduced by package merge: • Redefinitions are introduced that cannot be handled by current OO programming languages like Java • Cannot reduce visibility in a subclass • Cannot hide inherited merged property in subclass • Subclass member variable cannot have the same name as a superclass member variable (unless the superclass member is private) • Java cannot override getter methods based on return type • Java and EMF do not support multiple inheritance of the same property • Accessors that return elements from constructs have to be downcast because the methods are inherited from the corresponding basic class • Merged constraints are lost for matching properties because the superclass property is chosen when eliminating the property redefinitions • Many instances of name collisions arise from introduced multiple inheritance
And some inconveniences: • The result of the merge is a single flattened namespace for all classes in the model • Using any of the inherited superclasses requires the client application to use a fully qualified class name • All the downcasting required for redefined properties will be impossible to deal with
MOF2 defines two kinds of package merge: extend and define • Define merge was introduced in order to specify EMOF • Extend merge, denoted as a <<merge>> dependency converts the merge into an <<import>> and specializes and redefines the merged elements in the merging package • Define merge, denoted as a <<combine>> dependency removes the dependency and does not specialize or redefine merged elements • Extend merge maintains compatibility with the merged elements through generalization and redefinitions • Define merge maintains compatibility by creating different, backward compatible versions of existing elements • Define merge produces a simpler model and API – no specializations and redefinitions
Package merge can be view from two perspectives, depending on the direction of the merge • The merging package defines a new metamodel language in a family of languages • The merging package specifies the new features and merges in the old • The merge results in a new package representing the new modeling language • A new version of the merging package is created by merging in new features • The merging package specifies the old features and merges in the new features • The merge results in a new version of the original, merging package
Consider switching the direction of the merge, and changing the type to PackageMergeKind::define We change the example so package basic is extended by additional features in constructs to produce a new version of “basic” Source Merging Package Target Merged Package
The package merge is removed The API can be generated for just basic. There is no need to generate constructs because it is just a fragment model defining new features that were added to basic. Package basic will also get a new schema - BasicXMI.XSD which has the new features. This would be a new version of the previous BasicXMI.XSD and could be named appropriately to distinguish them, or use some life-cycle management or versioning facility, and perhaps a fragment in the namespace URI. primitiveTypes <<import>> basic The “constructs” version of basic
This produces a new version of the basic package that includes the new features defined in constructs
This solution has the same semantics as the extend merge, but has many advantages: • The API and XSD Schema are much smaller and simpler • There are no redefinitions or property conflicts introduced by multiple inheritance • The old and new XMI files are identical (for the compatible subset) • Loading an old basic XMI file will produce new basic instances, not instances from the constructs package • No model-to-model mapping is required to "downcast" the basic instances to the new constructs modeling language • The new extended basic will have a new XML schema containing the new features added from constructs • Existing basic clients don’t have to be edited to use the new basic classes – there’s no namespace change • Accessors that return elements from constructs have the correct type, there’s no need to downcast • The API and schema capture the additional merged constraints
There are a number ways this result could be produced: • Change the UML2 model • Extend XMI2 • Define a new CMOF to EMOF translation algorithm that flattens the merged class hierarchy • Not completely semantics preserving • Fix during API implementation • Flatten the merged class hierarchy as part of the implementation of an API for UML2 • Flattening algorithm doesn’t correspond to any UML2 semantics • Would introduce non-standard mapping resulting in interoperability problems
Changing the UML2 model • Pick one root package that will be the leaf merging package • Say org.omg.uml2 • Initial contents are Basic and anything required from Abstractions • EMOF capabilities can be <<combine>> merged into this too • Or initial contents are just EMOF to get MOF/UML alignment • Specify new capabilities, extensions, or features as fragment models describing changes to org.omg.uml2 • Change package dependencies to <<combine>> merge extensions into org.omg.uml2 • This may produce additional subpackages which get new features from subsequent merges • Define each compliance “Level” in a separate model that specifies a set of capabilities/extensions that are to be merged into a UML2 version • Re-specify InfrastructureLibrary to use the new merge semantics
Additional notes • Can’t just reverse the direction and change <<merge>> to <<combine>> in UML2 because clean model rules require the merged supertype to be explicitly included in the merging packages • Package merge isn’t the problem, specialization and redefinitions are. Package merge just creates a lot of them • API generation approach is to do the flattening algorithm in the generation of the Java API, not as a model-to-model transformation corresponding to any UML2 semantics • This produces the same result that redoing the UML2 models using <<combine>> instead of <<merge>>