EMF Compare/Logical Resources
Logical resource support will allow EMF Compare to better handle fragmented models. Among others, this support will allow EMF Compare to prevent comparison/synchronize/commit actions without taking into account the whole set of resources the target depends on. For example, you wouldn't be able to compare a fragment without considering its parent if there are links between the two.
What are logical resources?
Up until now, we've considered that EMF models were tied to physical files, one EMF Resource being one File on the disk. That is true in some cases ... but in many others, a "model" is not really a "file". One file holding an EMF Resource (let's call it "library") can reference multiple other EMF Resources ("books" and "writers"), and it can even be split into multiple physical files itself.
If we call EMF models "logical resources" and their files on disk "physical resources", we thus distinguish two cases for EMF Compare :
- one logical resource mapped to one physical resource (no reference, no fragments) and
- one logical resource mapped to multiple physical resources (an EMF model that references others, and/or is fragmented).
Only the first of these two cases is properly handled for now; we've only scratched the surface of the second and handled the most common cases.
Eclipse provides us with a framework to work on logical resources and resource mappings. We've decided to take advantage of these APIs for the case of collaborative work on EMF models. The idea is to make sure that the user never ends up with a corrupt logical model by preventing him from doing anything on a single part of the physical resources' set (or warning him when he does so).
For example, if an EMF model "library.genmodel" references another EMF model "library.ecore", and there are changes in both (the name of an EClass changed, and so did the corresponding GenClass in order to react to that name change), it should never be allowed to the user to commit only one of those two files : they are part of the same logical model, commiting only one of the two physical files may corrupt the logical model (which is the case in this example). The same applies to comparing either one of the physical files with the repository : as they are part of a set, the whole set should be used when comparing.
In other words, you are allowed to commit the genmodel file alone... which will prevent anyone retrieving that version from opening it from their side as it references a Class that does not exist without the change to "library.ecore". On the contrary, here is the same flow of action with the logical resources support implemented :
Here, even though we tried to commit "library.genmodel", Eclipse forces us to commit "library.ecore" along with it in order not to corrupt the logical model underneath.
Benefits of the logical resources support
The primary aim of the logical resources integration are the different Version Control System's (VCS) actions (commit, update ...). The target VCS are CVS and Git (through the CVS plugin and the eGit plugin respectively). The listing below wil enumerate the distinct operations that are concerned for both of these VCS.
- Create patch
- Apply patch
- Compare with (all)
- Replace with (all)
Git patches, fetch and pull are commit-based. Thus, ensuring that the commit action does not corrupt models should be enough. However we still need to check on pull that the whole logical resource is being retrieved from the remote repository. Indeed, The person that committed that resource may not have used the logical resource integration, and in such a case our goal is to warn the user that pulling that revision may end up corrupting his model.
- Compare with (all)
- Replace with (all)
The benefits of the support for logical resources does not end with ensuring that versionned models are sound though. Indeed, some of the operations a user can make in the Eclipse workspace may end up corrupting his models if he does not pay enough attention.
For example, changing one of the fragments of a logical model without loading the parent, moving that fragment in a new repository ... or deleting it altogether. The different operations that could cause the corruption of a logical models include :
- Deleting a physical file that is part of a logical model
- Moving a physical file that is part of a logical model
But also changing a physical file that is part of a logical model in any case that alters one of its EMF element's URI :
- changing the ID EAttribute
- changing the XMI ID
- moving the element to a new container
- deleting the element
How is the logical model resolved?
A logical model can span any number of physical files; from a single file for the most simple, to hundreds for the most complex. We'll take the example of a commit for the following description of the model resolving process, but this will be true for any "input" physical file.
When a user right-clicks on a given file (corresponding to an EMF Resource) in the workspace, and then hit the "commit" action, the logical resource support will be called in order to determine whether this file is part of a bigger logical resource. By default, it will only load that EMF Resource and use a "EcoreUtil.resolveAll" on that Resource in order to load all of its children (for a fragmented model) and referenced EMF Resources (for example, the "ecore" file referenced by the "genmodel" file).
However, that will not be sufficient in all cases. The most common will be if the "input" physical file "A" is not the root of a fragmented model, but itself one of the fragments. In such a case, we have no way to "go up" to the parent if it is not directly referenced by the fragment, and a simple "resolveAll" will only go down to the fragments of "A" and the EMF Resources it reference. EMF Compare will not handle these cases directly, but we do provide both an extension point for clients to provide their own model resolving algorithm.
For example, if I consider the two files "match.ecore" and its genmodel "match.genmodel". The genmodel references its ecore; so far so good : if I try to commit the genmodel, the corresponding ecore will be found automatically :
However, the ecore file does not directly reference its associated genmodel! If I try and commit it, I'll end up committing **only** the ecore :
By leveraging the model resolver mechanism, a third-party plugin can activate its own logic to resolve the logical model. For example, you could say that all "ecore" files have a "genmodel" beside them; you could say that all "myExtension" files have linked resources in the "resources" folder above them ... just about any logic you may have for the fragmentation of your logical resources.
EMF Compare provides an example implementation of that extension point that will go up to the desired "parent" of the input model (IContainer or IProject), load all resources located in that parent, and check for files that would be part of the same logical model. Here is what would happen with my previous example if I were to use that mechanism on "ecore" files, with the parent IContainer :
This example implementation can be retrieved by downloading the org.eclipse.emf.compare.example.library example from the git repository. Explanations on how to check out the EMF Compare code can be found on the contributor guide.