The RMP resource's implementation provides clients with some additional facilities beyond that of MSLResource. Signatures can be provided and serialized along with the resource contents. This signature information can be used to alert the user about components that were involved in affecting the resource's serialized contents.
RMP resources provide facilities to monitor whether the resource was migrated from an older version and to choose whether to preserve that old format when saving. Mapping documents and resource handlers are consulted to automatically migrate resource contents to/from an older format.
This tutorial assumes that the reader has knowledge of EMF and the eclipse PDE development environment. It is essential that the reader understands the basic reflective mechanisms of EMF as well as its resources.
This tutorial will demonstrate how to register the RMP resource to be used for a particular file extension. Also, thee the additional RMP resource API's will be shown in the appropriate contexts. Finally, signature and backward compatibility registrations will be demonstrated.
In order to make use of the facilities provided by the RMP resource it must be registered against your file extension using EMF's extension point.
<extension point="org.eclipse.emf.ecore.extension_parser"> <parser class="com.ibm.xtools.emf.core.resource.RMPResourceFactory" type="elib"/> </extension>
Note that to start off there must be at least one signature registered for your application. There is more on signatures later on in the tutorial.
When loading/saving an RMP resource, there is some special handling available to support backward compatibility and signatures. If a client is not aware of the RMP resource infrastructure the resource will appear to load and save normally as any other EMF resource. Behind the scenes, the resource could be performing some data migration and signature validation during the load. The resource will save automatically to the newest file format with all of the new signatures contributed from the user's runtime environment.
It is recommended that clients of the RMP resource be more proactive about the resource load and save in order to give the user the information that they need and provide them with appropriate choices.
r.load(null);
if (r instanceof RMPResource) {
if (((IRMPResource)r).isOlderArtifactVersion()) {
MessageDialog.openWarning(getShell(), "Backward Compatibility",
"The loaded resource was migrated from and older file format...");
}
}
// Check for errors during load
for (Iterator i = r.getErrors().iterator(); i.hasNext();) {
Resource.Diagnostic diag = (Resource.Diagnostic)i.next();
// Display this error somehow to the user
// The user should probably close this resource before saving it
// because it will probably not be able to save without causing
// data corruption.
}
for (Iterator i = r.getWarnings().iterator(); i.hasNext();) {
Resource.Diagnostic diag = (Resource.Diagnostic)i.next();
if (diag instanceof UnknownSignatureDiagnostic) {
UnknownSignatureDiagnostic usd = (UnknownSignatureDiagnostic)diag;
for (Iterator j = usd.getFeatureDescriptions().iterator(); j.hasNext();) {
IFeatureDescription fd = (IFeatureDescription)j.next();
// Display to the user this feature description
// and recommend that they install this missing feature
}
} else {
// Show the user the information stored in this warning diagnostic
}
}
The above code ensures that the user is aware when the resource is loaded from a file with an older file format. If there were any missing signatures, the user is provided with some information about the features that should be installed so that they can open the file properly. Finally, if any errors or warnings were produced during the load process (they could be generated from data migration among many other places) the user is informed.
It could be recommended that the above code be moved into the form of some form of listener. The reason for this is that resources can be loaded implicitly through proxy resolution. The above code only acts when a resource is loaded explicitly by a specific resource load gesture. It is left as an exercise to the reader to implement the above code as a listener.
if (r instanceof IRMPResource) {
if (((IRMPResource)r).isOlderArtifactVersion()) {
if (MessageDialog.openQuestion(getEditorSite().getShell(), "Backward Compatibility",
"Should the resource that is about to be saved be saved back into the original file-format?")) {
options.put(IRMPResource.OPTION_PRESERVE_CONTENT_VERSION, Boolean.TRUE);
}
}
}
r.save(options);
// Check for errors during save
for (Iterator i = r.getErrors().iterator(); i.hasNext();) {
Resource.Diagnostic diag = (Resource.Diagnostic)i.next();
// Display this error somehow to the user
// The user should probably close this resource because it is
// corrupt and restore their file from backup
}
// Check for warnings during save
for (Iterator i = r.getWarnings().iterator(); i.hasNext();) {
Resource.Diagnostic diag = (Resource.Diagnostic)i.next();
// Show the user the information stored in this warning diagnostic
}
Before we save we are giving the user the option to preserve the old file format if the resource is in the backward compatibility state. After we save the resource, we always check the errors and warnings list for any problems that may have occurred.
Signatures provide a way for an application component to leave a trace of its presence on EMF files. Signatures store information in a file that can help a user discover what components must be installed in order for the file to be loaded properly. Signatures throughs their versions will also allow certain backward compatibility participants to be registered moving forward into newer versions of the component.
In our case, we are the first component to introduce our own metamodel and content type. Because we are using an RMP resource with our files we will need to register at least one signature for this content type. Our signature will point to our feature so that our feature's information (name, description and URL) will be attched to the file when saved.
<extension point="com.ibm.xtools.emf.core.signatures"> <signature id="EXTLibrary" version="1.0"> <feature-association featureId="com.ibm.xtools.rmp" version="3.2.0"/> </signature> <signature-association contentTypeIds="org.eclipse.emf.examples.extlibrary" signatureId="EXTLibrary"/> </extension>
Other components may extend our editor's UI with their own actions. They may place information inside our resources that will get serialized. As a result, they can install their own signature on our content type. Our editor will warn the user if they don't have this component installed if the file contains the signature of this component. Also, they will get the information that they need to download and install this component.
In order for RMPResource to perform transparent migration, there are a number of participants that could be involved. The primary participants are ecore2xml documents, ResourceHandler objects and IURIHandler objects. Ecore2xml documents provide one-to-one mappings between different versions of a metamodel. ResourceHandler objects provide the necessary logic to fix up the resource's contents after a load and before a save. The IURIHandler objects convert URI's found in the file.
In our previous release, our metamodel had named the "Book" EClass as the french word "Livre". As well, our structural feature for a book's title was named "titre". In the current version of the metamodel we decided to change everything to the english wording. As a result, we must provide some mappings and code to convert the changes on the fly.
The first part of our conversion code is the ecore2xml mapping. The metamodel and editor for these mappings is provided as part of EMF's mapping framework. Using the editor, we created mappings for the two EPackage versions and from the "Livre" EClass to the "Book" EClass. The file was then provided to the backward compatibility extension point. Note that ecore2xml mappings are strictly for one-to-one mappings. All other mappings have to be provided by ResourceHandler's (or XMI type mappings but these are beyond the scope of this tutorial).
<extension point="com.ibm.xtools.emf.core.backwardCompatibility"> <Ecore2XML file="xmlMaps/extLibraryFrench.ecore2xml"> <schema nsURI="http:///org/eclipse/emf/examples/library/extlibrary.ecore/0.9.9"/> <schema nsURI="http:///org/eclipse/emf/examples/library/extlibrary.ecore/1.0.0"/> </Ecore2XML>
Resource handlers are defined by EMF in its XMLResource interface to have four basic methods: preLoad(), postLoad(), preSave() and postSave(). For the purposes of the RMP resource, only the postLoad() and preSave() methods are used for the purposes of fixing the resource after a load and preparing it for a save respectively. In the context of RMP resource, resource handlers are distinguished by the way that they are registered. Resource handlers that are registered against a schema are expected to be moving data from the resource's EObject to extension map that is produced by the resource when loading and finding data that is not recognized as part of the metamodel or not fully mapped by an ecore2xml document.
The other type of resource handler is one that is registered against a particular signature version. These handlers are expected to migrate data that completely conforms to the metamodel schema but must be altered for the sanity of the application or component. This type of resource handler is called after all of the schema handlers have finished migrating all of the data into the newest metamodel schemas.
Our resource handler is of the former type. It will be responsible for handling the conversion of the "titre" data into the new "title" structural feature.
<extension point="com.ibm.xtools.emf.core.backwardCompatibility"> <ResourceHandler class="com.ibm.xtools.emf.core.example.resourcehandler.MetaModelFixupResourceHandler"> <schema nsURI="http:///org/eclipse/emf/examples/library/extlibrary.ecore/0.9.9"/> </ResourceHandler>
Finally, our older files had URI's with a small typo. The URI's were supposed to be of the "platform" scheme but were typed as "pltform". We will install a URI handler that will convert these URI's to the proper scheme.
<extension point="com.ibm.xtools.emf.core.backwardCompatibility"> <URIHandler class="com.ibm.xtools.emf.core.example.urihandler.PlatformURIHandler"> <scheme name="pltform"> </URIHandler>
public class PlatformURIHandler implements IURIHandler {
public URI convert(URI original, IRMPResource resource) {
return URI.createURI("platform:"+original.devicePath()).appendFragment(original.fragment()); //$NON-NLS-1$
}
public URI revert(URI newFormatURI, IRMPResource resource) {
return URI.createURI("pltform:"+newFormatURI.devicePath()).appendFragment(newFormatURI.fragment()); //$NON-NLS-1$
}
}
It is assumed that the "pltform" scheme is not being used legitimately anywhere else so we are declaring our URI handler against that scheme and we will be called to convert URI's whenever it is encountered. Otherwise, we would declare the URI handler against the old signature version and the "pltform" scheme to prevent it from corrupting other resources from other applications.
In this tutorial, we did the following: