Skip to content
lvca edited this page Dec 22, 2012 · 3 revisions

Hook Java API

Introduction

Hook works like a trigger. Hook lets to the user application to intercept internal events before and after each CRUD operation against records. You can use to write custom validation rules, to enforce security or even to orchestrate external events like the replication against a Relational DBMS.

The ORecordHook interface

A hook is an implementation of the interface ORecordHook:

    public interface ORecordHook {
      public enum TYPE {
        ANY,
        BEFORE_CREATE, BEFORE_READ, BEFORE_UPDATE, BEFORE_DELETE,
        AFTER_CREATE, AFTER_READ, AFTER_UPDATE, AFTER_DELETE
      };
    
      public void onTrigger(TYPE iType, ORecord<?> iRecord);
    }

The ORecordHookAbstract abstract class

OrientDB comes with an abstract implementation of the ORecordHook interface called ORecordHookAbstract.java. It switches the callback event calling seperate methods for each one:

    public abstract class ORecordHookAbstract implements ORecordHook {
      public void onRecordBeforeCreate(ORecord<?> iRecord){}
      public void onRecordAfterCreate(ORecord<?> iRecord){}
      public void onRecordBeforeRead(ORecord<?> iRecord){}
      public void onRecordAfterRead(ORecord<?> iRecord){}
      public void onRecordBeforeUpdate(ORecord<?> iRecord){}
      public void onRecordAfterUpdate(ORecord<?> iRecord){}
      public void onRecordBeforeDelete(ORecord<?> iRecord){}
      public void onRecordAfterDelete(ORecord<?> iRecord){}
      ...

Hook example

In this example the events "before-create" and "after-delete" are called during the save() of the Profile object where:

  • before-create is used to check custom validation rules
  • after-delete is used to maintain the references valid
    public class HookTest extends ORecordHookAbstract {
      public saveProfile(){
        ODatabaseObjectTx database = new ODatabaseObjectTx("remote:localhost/demo");
        database.open("writer", "writer");
    
        // REGISTER MYSELF AS HOOK
        database.registerHook(this);
    
        ...
        p = new Profile("Luca");
        p.setAge(10000);
        database.save(p);
        ...
      }
    
      /**
       * Custom validation rules
       */
      @Override
      public void onRecordBeforeCreate(ORecord<?> iRecord){
        if( iRecord instanceof ODocument ){
          ODocument doc = (ODocument) iRecord;
          Integer age = doc .field( "age" );
          if( age != null && age > 130 )
            throw new OValidationException("Invalid age");
        }
      }
    
      /**
       * On deletion removes the reference back.
       */
      @Override
      public void onRecordAfterDelete(ORecord<?> iRecord){
        if( iRecord instanceof ODocument ){
          ODocument doc = (ODocument) iRecord;
    
          Set<OIdentifiable> friends = doc.field( "friends" );
          if( friends != null ){
            for( OIdentifiable friend : friends ){
              Set<OIdentifiable> otherFriends = ((ODocument)friend.getRecord()).field("friends");
              if( friends != null )
                friends.remove( iRecord );
            }
          }
        }
      }
    }  

For more information take a look to the HookTest.java source code.

Install server-side hooks

To let a hook to be executed in the Server space you've to register it in the server orientdb-server-config.xml configuration file.

Write your hook

Example of a hook to execute custom validation rules:

    public class CustomValidationRules implements ORecordHook{
      /**
       * Apply custom validation rules
       */
      public boolean onTrigger(final TYPE iType, final ORecord<?> iRecord) {
        if( iRecord instanceof ODocument ){
          ODocument doc = (ODocument) iRecord;
    
          switch( iType ){
            case BEFORE_CREATE:
            case BEFORE_UPDATE: {
              if( doc.getClassName().equals("Customer") ){
                Integer age = doc .field( "age" );
                if( age != null && age > 130 )
                  throw new OValidationException("Invalid age");
              }
              break;
            }
    
            case BEFORE_DELETE: {
              if( doc.getClassName().equals("Customer") ){
                final ODatabaseRecord db = ODatabaseRecordThreadLocal.INSTANCE.get();
                if( !db.getUser().getName().equals( "admin" ) )
                  throw new OSecurityException("Only admin can delete customers");
              }
              break;
            }
        }
      }
    }

Deploy the hook

Once implemented create a .jar file containing your class and put it under the $ORIENTDB_HOME/lib directory.

Register it in the server configuration

Change the orientdb-server-config.xml file adding your hook inside the <hooks> tag. The position can be one of following values FIRST, EARLY, REGULAR, LATE, LAST:

    <hook class="org.orientdb.test.MyHook" position="REGULAR"/>

Configurable hooks

If your hook must be configurable with external parameters write the parameters in the orientdb-server-config.xml file:

    <hook class="org.orientdb.test.MyHook" position="REGULAR">
      <parameters>
        <parameter name="userCanDelete" value="admin" />
      </parameters>
    </hook>

And in your Java class implement the config() method to read the parameter:

private String userCanDelete;
...
public void config(OServer oServer, OServerParameterConfiguration[] iParams) {
  for (OServerParameterConfiguration param : iParams) {
    if (param.name.equalsIgnoreCase("userCanDelete")) {
      userCanDelete = param.value;
    }
  }
}
...
Clone this wiki locally