It’s kinda a mantra that I live a reasonable part of my life to. I think the above image is a not unjustifiable representation of my feelings about documentation:
But there are many good reasons that I should be doing doco. Most of them are supposed to save the customer money in the long run. And occasionally by doing the doco, I even find some small errors in my code that I hadn’t seen before.
InnoJam Sydney 2011
Before InnoJam had any of that fun fluffy design thinking aspect to it, we ran one in Sydney. It was good fun, and people could build whatever the heck they wanted.
In staying true to my aversion for writing doco, I came up with an idea about auto-generation UML diagrams from SAP code.
Here’s a video of the solution we came up with:
here’s a link to the Prezi that I presented in that video:
I wrote a blog post about it:
But it was long time ago and the move to a new version of SCN has kinda buggered it up.
Anyway, in short – Rui never managed to get the terms and conditions of CodeExchange changed to a level where I’m happy to support it and put code in there. I’m pretty sure he tried though. So I didn’t do anything with the code.
Fast forward 3 years
I have a whiteboard in the office covered in post-it notes. They all represent at least one development that I’ve done for the current project I’m working on. At the beginning of the project, I was very good, and did all my doco as I went along. Then the poo hit the fan, and everyone wanted everything done yesterday, and didn’t care about doco.
So I now have a whiteboard full of post-it notes that represent potentially weeks of doco hell for me. So in my “free time” in the evening I decided to see if I could recreate the solution that we’d built in Sydney, and perhaps make it a little nicer.
The first thing I decided, was that I was NOT going to try to build the graphical output myself. Having had lots of fun in Sydney trying to make Gravity do something it really wasn’t designed for I thought I’d research how else I could get my interaction diagrams created.
If in doubt Wikipedia
There were loads there, and I’d pretty much decided on UMLet when I discovered something about interaction diagrams. Basically, interaction diagrams are supposed to show interaction between objects. Bit bloody obvious really. However, the thing is, if I’m documenting my code, I’d really like to show the interaction within my objects too. I.e. if I make a call to a private method of my class, I’d really like that to show up in my diagram. Given that interaction diagrams are only supposed to show external interaction it’s not surprising that most of the tools for creating the diagrams don’t really support this idea of internal object calls.
So a bit of browsing later and I found PlantUML. It has some awesome functionality for creating sequence diagrams, actually, most UML diagrams it seems, but it was the sequence diagrams that I was interested in.
Here’s a simple example:
See how it’s quite possible to show “internal” calls of an instance and also show the life time of those calls. This feature I didn’t find on the other free UML tools that I looked at. There are a bunch of other formatting features that can be used too. If you’re interested check out their website: http://plantuml.sourceforge.net/sequence.html
Intercepting the SAP standard UML generation
So in transaction SAT there is the possibility to generate your own JNet UML sequence diagram (this exists as standard.)
However, it does not allow you to do things like filter out standard SAP routines (as far as I know! If anyone can tell me how to do this (without needing to list every method I call, please let me know!) When I was looking at one of my examples, where I ran a program to generate a performance review document for an employee, there were over 100,000 different routines called. Only about 400 of those calls involved my code, so you can imagine generating a UML diagram for the whole 100,000 calls would be a bit of overkill (not to mention an impossible to read diagram).
In customer systems there is a function module ATRA_UML_DECIDER that has been purposely handicapped. One does have to wonder why this has been done, but nevertheless it has. It allows the user to chose from a list of potential UML extraction routines. All of these routines implement the IF_ATRA_UML_TOOL interface. There are classes for extracting to JNet, Borland Together and Altova. Now, I’m sure that Borland and Altova have good products, it’s just that I don’t really want to spend money on then when there are perfectly good (for my tasks) free and open source products out there.
There is a factory class/method CL_ATRA_UML_FACTORY that creates an instance of a class implementing the interface. I overrode this method to use my particular extractor if it was me running the code. In the future, I might enhance this to check for a user role, or perhaps a user parameter, that’s trivial, the main point will be to allow others to access this logic too.
The guts of the code
Simply my implementation of the interface reads the table of data that is passed to the interface, removes all calls that aren’t to or from custom code and then builds a PlantUML representation of that code.
Here’s a very simple output that generates the diagram above.
participant "Instance 1 of Class\nZCL_HR_EMPLOYEE" as 1
1 -> 1: Call method GET_HELD_QUALIFICATIONS
1 -> 1: Call method ZCL_HR_OBJECT->GET_RELATIONSHIPS
create "Static Methods of Class\nCL_HRBAS_READ_INFOTYPE" as 2
1 -> 2: Call method GET_INSTANCE
2 --> 1
create "Instance 1 of Class\nCL_HRBAS_READ_INFOTYPE" as 3
1 -> 3: Call method IF_HRBAS_READ_INFOTYPE~READ_PLAIN_1001
3 --> 1
1 --> 1
create "Instance 1 of Class\nZCL_HR_QUALIFICATION" as 4
1 -> 4: Create instance of class ZCL_HR_QUALIFICATION
4 -> 4: Call method ZCL_HR_OBJECT->CONSTRUCTOR
create "Function Group\nRHS0" as 5
4 -> 5: Call FM RH_GET_ACTIVE_WF_PLVAR
5 --> 4
4 --> 4
4 --> 1
A slightly less trivial example
The following code does some pretty simple stuff, it finds who is my manager, and finds out what required qualifications my position/job has.
DATA: lo_emp TYPE REF TO zcl_hr_employee,
lt_managers TYPE ztthr_employee_objects,
lt_required_quals TYPE ztthr_qualifications.
lo_emp = zcl_hr_employee=>get_employee_by_user_id( sy-uname ).
lt_managers = lo_emp->get_position( )->get_managers_recursive( ).
lt_required_quals = lo_emp->get_position( )->get_required_qualifications( ).
CATCH zcx_hr_no_managing_pos_found ” No managing position found
zcx_hr_no_holders_found ” no holders for position found.
zcx_hr_no_position_found ” no position found
zcx_hr_user_id_not_found. ” cannot find user id for employee
So I thought I’d trace it:
it works out at around 200,000 different routines being called. 62 of those are my code, the rest standard.
First of all, I need to schedule a trace for myself…
Need to know which session I will be recording. If I left it as “Any” it will start recording this session, not very useful!
Session 2 it is!
actually run it now!
and the schedule status changes to executed.
I now need to delete the scheduled measurement. Despite the intimidating words, this does not delete my measurement, just the scheduling of it.
now swapping to the “Evaluate” tab in SAT I can see my measurement, and I can click to output to UML
Clicking on the button triggers a bit of a pause whilst the system code chugs away and does loads of stuff it doesn’t need to do…
Save the data and PlantUML starts converting it immediately:
and the result:
Would probably have been a little bigger if my employee had a job assigned to their position, but you can see how incredibly easy this now makes documenting the functionality I’ve built.
I’m still considering how to make the code publicly available. I’m sure someone else would be happy to post it to CodeExchange, so perhaps I’ll let them.