Hooks Proof of Concept
This document is about a proof of concept on hooks for a group change and a membership change.
The current progress is a working unit test for each, and a Grouper UI example for a group change (member change is in progress).
Grouper UI group example
I added a hook to the Grouper UI which is a veto hook which will not allow a group to be created which is not in the "penn" folder (which is a subfolder of root).
To implement this, it requires a group hook class:
And it requires a configuration in the grouper.properties:
The result when trying to add a group not in that folder, is:
Grouper UI membership example
I added a hook to the Grouper UI (unfinished... was getting too fancy) for a membership veto that if the group type is grouperLoader, do not allow group memberships. However, if the user is a wheel group user, then allow it but put a warning on screen
Here is the starting point for the Java:
Here is the grouper.properties config:
No screen shot yet
Everything went pretty smoothly, but...
- To implement a hook, it will require some understanding about Grouper. We should post a bunch of examples. The problem is we have business objects, data transfer objects, and data access objects. Sometimes logic goes through any of these paths. So I added hooks to the data access layer (layer on top of hibernate) so that all operations can be hooked. Sometimes there will be a reference to the business object (e.g. Group), but it doesnt really make sense in some cases (like on an insert, there is no group uuid yet, so the Group object is not created yet, and even if it were, most methods would be invalid).
- On memberships, the DAO hook will probably not be the useful one. Information like group name or subject id is not even available, it would have to be queried in a lot of cases. In some cases it is known, but it is weird to have it there sometimes and not others
- I added a high level membership hook (addMember) which will give the information about what the group name is, subjectId, etc. This is most likely the one that can be used for veto operations. The low level one would most likely be used for auditing. The weird thing about the addMember hook is that some of the objects are business objects, and some are DTOs (see the BaseMemberOf object which encapsulates one member addition). I think people will figure it out... but for the record, I'm not completely bought in to the necessity of having so many data layers.
Implementation of grouper code details
This is implemented in a 1.4 hooks branch in cvs.
- Here is the start of a groups hook class (you override this to add a hook)
- Here is the start of a membership hook class (notice high and low level hook methods)
- All DB calls have been refactored to go through grouper's HibernateSession API. The transaction implementation in 1.3 brought us more than halfway there, and this seals the deal. The HibernateSession API will allow events to be registered on each hibernate action in a safe way so that the transaction can still be used (differentiates from the built in hibernate interceptors and I think events though events are not documented well)
e.g. the delete and load methods are just wrappers around the ones in hibernate of the same name. ByObject just separates up the namespace a bit (there are also ByHql, and ByCriteria)
* Each hook will have its own bean (these arent finished by any means, but look at add member for decent example). This is to encapsulate the params passed to the hook (and to facilitate an easy way to get data back from a hook if needbe). This way if any params change, existing implementors will be less likely to have to change their code
- Notice the reference in the hook beans to the hook context bean. This holds information about the current user, and gives utility methods (e.g. is the current user in a certain group). This bean also holds attributes which can be set by the current application. e.g. the UI and WS might give a reference to the HttpServletRequest. These attributes can be set as threadsafe or not, which means when the asynchronous callback is called, all the data will be handled correctly (e.g. in a new thread there is no HttpServletRequest object since that is a weak reference and the request will be over before thread is)
- For vetos, there is one exception for various types, and it indicates which type of veto it is (set automatically if not manually). This is going to require a little bit of work on the implementors (e.g. WS, UI, etc) to handle the vetos gracefully. In the UI it was a matter of adding this code (the three lines starting with catch HookVeto). Note this will have to be done in all places where the API is used if it cant be done centrally (hopefully it can be added to filter or something...).
* There was an issue of setters affecting the API, and I think we can fix that. e.g. for group I added a save() method which needs to now be called after setting the description, name, etc. However, there are methods which arent affected (e.g. Grouper.addMember() does not require a save). Only javabean setters.
- Note that in the UI you can specify a message in the nav.properties for each hook veto, or just use the standard one as a default to reduce the number of steps required to get working...
- Its pretty easy/lightweight to add a hook invocation to the grouper code (I will be adding these)... e.g. here is the one for the addMember:
* For the low level hooks, the implementation is similar but even easier (since it is central)... each DAO implements this interface, so those methods just need to be implemented. e.g. in Hib3GroupDAO
This will kick in wherever a group is saved
- Group / member hooks are unit tested, and all existing unit tests still pass