In the context of issue 4946 (Loading project which references non-default profile doesn't work) I needed to make some design changes to the profile sub-system and to the MDR implementation of the model sub-system of ArgoUML. At first I used what I now see as a brute force approach, banging the existing code to try to make the problem go away. I'm glad it didn't worked and I started thinking and designing my way out of this priority 1 bug.
Basically the problem aroused when we tried to finish the profile sub-system to have a developer release of ArgoUML, the release 0.25.5. Originally the UML profile for C++ was contained in the ArgoUML core. There were two types of profiles:
- ArgoUML core profiles – these are XMI files contained in ArgoUML, being physically part of the argouml.jar.
- User defined profiles – these are XMI files saved by the user, which he intends to use as his own profiles, normally for reusing in several models.
It was agreed that part of the work would be to enable the language modules to contain the corresponding profiles, and as such I tried to put the C++ profile in the ArgoUML C++ module. So, ArgoUML would now have three types of profiles:
- code profiles;
- module profiles;
- user defined profiles.
Now I need to explain the idea of resolving references when loading XMI files...
A XMI file is a XML file. Being so, it is possible to make a reference from one XMI file to another and this is how in the future ArgoUML you can have a model and several profiles referenced from this model. But, the references take the form of URL#ID, being the URL the reference to a different XMI file where there must exist a definition of a model element with the identifier ID.
To enable offline work in the case of core and module profiles and flexible user defined profile directories locations in the case of user defined profiles, you need to add a midle man resolving these beautiful URLs to system paths - enter the
XmiReferenceResolverImpl was based on the AndroMDA code that did the same thing. It has some complex code that is able to resolve a reference from the net, local disk or even in the classpath. But, the resolving of a reference to a profile was hard coded, being the base URL and the base system path constants of
So, when the C++ profile was moved, the reference resolving didn't worked any longer for it since its system reference was now different. It broke the initial solution of having the reference resolving somehow hard coded in the MDR model implementation.
I tried very much to make it work again, doing some more hard coding in the profile loading side and in the MDR implementation side:
- [2008-01-28 to 2008-02-05] Hack, hack, debug, debug, and I made the C++ profile module work :-)
- [2008-02-26 to 2008-03-04] Hack, hack, debug, debug, hack, hack and the user profiles work too :-))
- [2008-03-??] But wait, my automated test for the C++ profile persistency is now failing :-(
- [2008-03-?? to 2008-03-23] Debug, debuug, hack, haaack...
- [2008-03-24] Stop! Think a bit, and think a bit more and... This design for resolving references is broken.
So, I went back to the drawing board and started thinking a bit on what is required to enable the reference resolving that we needed. There were some lessons learned:
XmiReferenceResolverImplmust not have hard coded base URLs and base system paths to resolve the profiles references;
- when the XMI for a profile is read, the
XmiReferenceResolverImplmust replace the given system reference with a public reference which is handed to it;
- the best place to know about what are the system and public references for a given XMI is the place where the call to load the XMI is done.
Picture 7 –
ProfileReference class diagram.
As seen, to enable DRY, I added the
CoreProfileReference and the
UserProfileReference, but, the language modules will in principle use directly the
ProfileReference, or define their own
ProfileModelLoader.loadModel(String path) method is deprecated and the
ProfileModelLoader.loadModel(ProfileReference reference) replaces it.
But, now, how do I get all these neat references into the realm of
XmiReferenceResolverImpl? Well, some more design and I hope the ArgoUML powered sequence diagram of the interesting part of loading a profile XMI explains it.
Picture 7 – profile loading sequence diagram.
As with many sequence diagrams, I only kept the important steps, but, one significant is that there is a map of public to system references involved, which is kept at the
MDRModelImplementation level. And this is important, since the
XmiReferenceResolverImpl and its creator, the
XmiReaderImpl are short lived - they are only kept around while the XMI is being loaded.
So, when I find myself working very hard at the debugger and hacking cycle I will try to stop and think. I recall already having stated this to myself some time ago, but, sometimes I forget this kind of things for which there is nothing like a bump with the head (or several as in this case) for one to remember the basics again ;-)