Custom Modules

Introduction

Modules allow you to extend Penrose's functionality by intercepting Penrose operations. With modules you can either just inspect the operations, perform additional operations, or even modify the behavior of the operations.

Writing Custom Module

All custom modules should inherit from the org.safehaus.penrose.module.Module class. You can overwrite the following methods:

  • init() - This method will be called during initialization.
  • start() and stop() - These methods will be called when Penrose is started or stopped. If you overwrite any of these methods, you should call setStatus() with the appropriate status (STARTING, STARTED, STOPPING, STOPPED).
  • beforeXXX() and afterXXX() - XXX can be Bind, Unbind, Compare, Add, Modify, ModRdn, Delete, or Search. These methods will be called before or after the operations.

These methods are available to use in your custom module:

  • getName() - Get module name as specified in modules.xml.
  • getParameter() - Get module parameter as specified in modules.xml.
  • getPartition() - Get the partition where the module is installed.

Example

The example files can be found in PENROSE_SERVER_HOME/samples/module. See DemoModule.java.

Compile the sample module and put the jar file into PENROSE_SERVER_HOME/lib/ext:

cd PENROSE_SERVER_HOME/samples/module
ant clean dist
cp target/penrose-example-module.jar ../../lib/ext

Create a new partition by copying the files in PENROSE_SERVER_HOME/samples/module/partition into PENROSE_SERVER_HOME/partitions/module. Then add the partition in PENROSE_SERVER_HOME/conf/server.xml:

<server>
  <partition name="module" path="partitions/module"/>
</server>

Then run Penrose.

Intercepting Add Operations

The following code intercepts add operations and converts the sn attribute into upper case:

public void beforeAdd(AddEvent event) throws Exception {
    System.out.println("Adding "+event.getDn()+":");

    Attributes attributes = event.getAttributes();
    for (NamingEnumeration i=attributes.getAll(); i.hasMore(); ) {
        Attribute attribute = (Attribute)i.next();
        String name = attribute.getID();
        for (NamingEnumeration j=attribute.getAll(); j.hasMore(); ) {
            Object value = j.next();
            System.out.println(" - "+name+": "+value);
        }
    }

    // change sn attribute to upper case
    Attribute sn = attributes.get("sn");
    String value = (String)sn.get();
    sn.clear();
    sn.add(value.toUpperCase());
}

public void afterAdd(AddEvent event) throws Exception {
    int rc = event.getReturnCode();
    if (rc == LDAPException.SUCCESS) {
        System.out.println("Add succeded.");
    } else {
        System.out.println("Add failed. RC="+rc);
    }
}

Run the following command:

ant test-add

You should see this in Penrose output:

Adding uid=test,ou=Users,dc=Module,dc=Example,dc=com:
 - userpassword: [B@e3ffdf
 - uid: test
 - objectclass: inetOrgPerson
 - objectclass: organizationalPerson
 - objectclass: person
 - sn: User
 - cn: Test User
Add succeded.

Then run the following command:

ant test-search

You should see that the sn attribute is in upper case:

[exec] # test, Users, Module.Example.com
[exec] dn: uid=test,ou=Users,dc=Module,dc=Example,dc=com
[exec] objectClass: inetOrgPerson
[exec] objectClass: organizationalPerson
[exec] objectClass: person
[exec] uid: test
[exec] userPassword:: e1NIQX1xVXFQNWN5eG02WWNUQWh6MDVIcGg1Z3Z1OU09
[exec] sn: USER
[exec] cn: Test USER

Intercepting Bind Operations

The following code intercepts bind operations and displays the bind information:

public void beforeBind(BindEvent event) throws Exception {
    System.out.println("Binding as "+event.getDn()+" with password "+event.getPassword()+".");
}

public void afterBind(BindEvent event) throws Exception {
    int rc = event.getReturnCode();
    if (rc == LDAPException.SUCCESS) {
        System.out.println("Bound as "+event.getDn()+".");
    } else {
        System.out.println("Failed to bind as "+event.getDn()+". RC="+rc);
    }
}

Run the following command:

ant test-search -Dtest.bindDn=uid=swhite,ou=Users,dc=Module,dc=Example,dc=com -Dtest.password=swh1t3

You should see this in Penrose output:

Binding as uid=swhite,ou=Users,dc=Module,dc=Example,dc=com with password swh1t3.
Bound as uid=swhite,ou=Users,dc=Module,dc=Example,dc=com.

Intercepting Search Operations

The following code intercepts search operations and displays the search results:

public void beforeSearch(SearchEvent event) throws Exception {
    System.out.println("Searching "+event.getBase()+" with filter "+event.getFilter()+".");

    PenroseSearchResults results = event.getSearchResults();

    // register result listener
    results.addListener(new PipelineAdapter() {
        public void objectAdded(PipelineEvent event) {
            SearchResult sr = (SearchResult)event.getObject();
            String dn = sr.getName();
            System.out.println("Returning "+dn+".");
        }
    });
}

public void afterSearch(SearchEvent event) throws Exception {
    int rc = event.getReturnCode();
    if (rc == LDAPException.SUCCESS) {
        System.out.println("Search succeded.");
    } else {
        System.out.println("Search failed. RC="+rc);
    }
}

Run the following:

ant test-search -Dtest.baseDn=ou=Users,dc=Module,dc=Example,dc=com

You should see this in Penrose output:

Searching ou=Users,dc=Module,dc=Example,dc=com with filter (objectclass=*) .
Returning ou=Users,dc=Module,dc=Example,dc=com.
Returning uid=alange,ou=Users,dc=Module,dc=Example,dc=com.
Returning uid=jstockton,ou=Users,dc=Module,dc=Example,dc=com.
Returning uid=lwalker,ou=Users,dc=Module,dc=Example,dc=com.
Returning uid=pfarmer,ou=Users,dc=Module,dc=Example,dc=com.
Returning uid=swhite,ou=Users,dc=Module,dc=Example,dc=com.
Returning uid=test,ou=Users,dc=Module,dc=Example,dc=com.
Search succeded.

References