Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Include Page
spaceKeyGrouper
pageTitleNavigation

3rd Party Hooks as of v1.4.0

Hooks

Panel
borderColor#ccc
bgColor#FcFEFF
titleColorwhite
titleBGColor#00a400

Image Added  This topic is discussed in the Advanced Topics training video.

Hooks are points in the Grouper Hooks: points in the grouper API that will callback custom code if configured to do so.  The custom code has the opportunity to be notified that something happened, change data as it is being operated on, veto operations, kickoff other logic (log something, call another grouper API method, etc).

Hooks are available for the following database persistable objects: Group, Stem, Member, Membership, Composite, Field, GrouperSession, GroupType, GroupTypeTuple

Getting started with hooksProof of concept

Getting started with hooks

Children Display

Each of these objects has low-level hooks associated with it: preInsert, postInsert, postCommitInsert, preUpdate, postUpdate, postCommitUpdate, preDelete, postDelete, postCommitDelete. 

Note, some of these operations don't really make sense but are there anyway Wiki MarkupEach of these objects has low level hooks associated with it: preInsert, postInsert, postCommitInsert, preUpdate, postUpdate, postCommitUpdate, preDelete, postDelete, postCommitDelete.  Note, some of these operations dont really make sense but are there anyway (e.g. there is no way to update a GroupType, and no way to delete Member).  The low level hooks are called with each hibernate call for an object which generally corresponds to a row in a DB table (not for groups/attributes which have one hook but which are a  

The low-level hooks are called with each hibernate call for an object that generally corresponds to a row in a DB table (not for groups/attributes which have one hook but which are a one-to-many).   

The pre is before the sql statement is sent to the DB, and the post is after, though both are before the SQL commit.  The postCommit kicks off right after the commit is called in the database transaction (note: since we have long runing transactions this could be after a long workflow).  You would use the pre to edit the object or to insert SQL before the sql.  The post is used to get the hibernate id of an insert (e.g. to add rows with foreign keys).  Both can be used to veto a call if synchronous [default].  Asynchronous hooks, and postCommit hooks cannot veto.  Post commit hooks do not participate in the same database transaction as the business logic.  Post commit hooks can be used for economical notifications (note, there are more robust strategies also).  Post commit and asynchronous hooks receive copies (clones) of the business objects so the data is consistent and safe.

There are high-level hooks which encompass multiple low-The pre is before the sql statement is sent to the DB, and the post is after, though both are before the SQL commit.  The postCommit kicks off right after the commit is called in the database transaction (note: since we have long runing transactions this could be after a long workflow).  You would use the pre to edit the object or to insert SQL before the sql.  The post is used to get the hibernate id of an insert (e.g. to add rows with foreign keys).  Both can be used to veto a call if synchronous \[default\].  Asynchronous hooks, and postCommit hooks cannot veto.  Post commit hooks do not participate in the same database transaction as the business logic.  Post commit hooks can be used for economical notifications (note, there are more robust strategies also).  Post commit and asynchronous hooks receive copies (clones) of the business objects so the data is consistent and safe.There are high level hooks which encompass multiple low level operations, e.g. addMember, removeMember  (only 2 so far)

...

To configure a hook, subclass one of the hooks base classes, e.g. GroupHooks, MembershipHooks, etc.  Then either register this in the grouper.properties (see grouper.example.properties[broken] for documentation).  Note the class must be threadsafe, as an instance is cached and called repeatedly (i.e. dont store instance vars etc)

...

No Format
package edu.internet2.middleware.grouper.hooks;

import edu.internet2.middleware.grouper.hooks.beans.HooksContext;
import edu.internet2.middleware.grouper.hooks.beans.HooksLifecycleHooksInitBean;
import edu.internet2.middleware.grouper.hooks.logic.GrouperHooksUtils;


/**
 *
 */
public class LifecycleHooksImpl extends LifecycleHooks {

  /**
   * @see edu.internet2.middleware.grouper.hooks.LifecycleHooks#hooksInit(edu.internet2.middleware.grouper.hooks.beans.HooksContext,
   *            edu.internet2.middleware.grouper.hooks.beans.HooksLifecycleHooksInitBean)
   */
  @Override
  public void hooksInit(HooksContext hooksContext,
      HooksLifecycleHooksInitBean hooLifecycleHooksInitBean) {
    GrouperHooksUtils.addHookManual("hooks.group.class", MyGroupHooks.class);
    GrouperHooksUtils.addHookManual("hooks.stem.class", MyStemHooks.class);
    GrouperHooksUtils.addHookManual("hooks.member.class", MyMemberHooks.class);
  }

}

Finally, I picutre picture built-in grouper Grouper suites to just insert the registration based on grouper Grouper config property in GrouperHooksUtils in the initHooks block.  To enable the suite the grouper properties file would just have a boolean Boolean switch.  When someone wants to get this working, let me know the grouper Grouper properties config property and the LifecycleHooks subclass and I can do an example.

...

To find out how the data has changed (in an update or delete), you can use the dbVersion API.  Note, this is probably only available in the "pre" hooks.

To veto a hook, throw a HookVeto which is a runtime exception.  The specific hooks which is vetoing will be assigned to the exception, and will be known wherever it is caught.  This HookVeto takes a system name and a friendly description of a reason why it is being vetoed.  The system name can be used to look up a localized error message.  The friendly version can be used for logging or if a localized message doesnt exist.  You can only veto pre and post synchronous hooks.  You cannot veto asynchronous or postCommit hooks.  You also cannot veto lifecycle hooks (e.g. hibernate init).

...

If you want to make a hook non-reentrant (while in hook, do not let that particular hook fire), you can easily setup a threadlocal to accomodateaccommodate.  e.g.

No Format
private static ThreadLocal<Boolean> inOnPreUpdate = new ThreadLocal<Boolean>();

  /**
   * @see edu.internet2.middleware.grouper.hooks.StemHooks#stemPostUpdate()
   */
  @Override
  public void stemPreUpdate(HooksContext hooksContext, HooksStemBean postUpdateBean) {

    Boolean inOnPreUpdateBoolean = inOnPreUpdate.get();
    try {

      if (inOnPreUpdateBoolean == null || !inOnPreUpdateBoolean) {
          inOnPreUpdate.set(true);          ... do logic ...
        }
      } finally {
        //if we changed it
        if (inOnPreUpdateBoolean== null || !inOnPreUpdateBoolean) {
          //change it back
          inOnPreUpdate.remove();
        }
      }


  }

...

No Format
public class MembershipHooksImplAsync2 extends MembershipHooks implements HookAsynchronousMarker {

...

\-or\- 2. Call the asynchronous callback, anything in the callback is in a different thread \ [asynchronous\]

No Format
 public class MembershipHooksImplAsync extends MembershipHooks {

  /**
   *
   */
  @Override
  public void membershipPreAddMember(HooksContext hooksContext,
      HooksMembershipChangeBean preAddMemberBean) {
        //do logic in same thread/transaction
    //also you can get data from the beans above, set final, and use below (if it wont be available otherwise, this might be rare)
    HookAsynchronous.callbackAsynchronous(hooksContext, preAddMemberBean, new HookAsynchronousHandler() {

      public void callback(HooksContext hooksContext, HooksBean hooksBean) {

        HooksMembershipChangeBean preAddMemberBeanThread = (HooksMembershipChangeBean)hooksBean;
                //do logic in a different thread/transaction

      }

    });

  }

}

...

No Format
2008/07/09 02:18:05.482 GrouperHooksUtils.executeHook(174) - START: Hook GroupTypeTupleHooksImpl.groupTypeTuplePreInsert id: PSPTRJ8J
2008/07/09 02:18:05.497 GrouperHooksUtils.executeHook(181) - END (normal): Hook GroupTypeTupleHooksImpl.groupTypeTuplePreInsert id: PSPTRJ8J (15ms)
2008/07/09 02:18:05.497 GrouperHooksUtils.executeHook(174) - START: Hook GroupTypeTupleHooksImpl.groupTypeTuplePostInsert id: PSPTRJ8K
2008/07/09 02:18:05.497 GrouperHooksUtils.executeHook(186) - END (veto): Hook GroupTypeTupleHooksImpl.groupTypeTuplePostInsert id: PSPTRJ8K (0ms),
   veto key: hook.veto.groupTypeTuple.insert.name.not.test4, veto message: name cannot be test4

Use cases

To do

  • Add more high level hooks
  • Collect and code use cases

Hooks code

 Here is an email to signet describing how grouper hooks work and where the code is

    (info) Contact us if you have additional comments or suggestions.

...

See also

Grouper Rules

Access Management Features Overview