Extended Operations
Extended operations are extension mechanisms defined by the LDAP standards (refer to RFC 2251). They were defined to implement new operations, beyond the standard ones (ABANDON, SEARCH, MODIFY, MODRDN, DELETE, BIND, UNBIND).
To write your extended operations, use the Sun Java System Directory Server Plug-In API. Developers who are familiar with the Plug-In API typically find it easy to learn how to implement extended operations, because they are just special plug-ins. All the code, makefiles, and utilities you built to simplify your work will probably be reusable.
The only relevant difference with these plug-ins compared to pre- and post- plug-ins is that extended operations are explicitly and directly invoked by clients, while pre- and post- plug-ins act in the middle of LDAP operations. Consequently, the client must have a mechanism to invoke extended operations. The client C SDK or Java SDK, now part of the Sun Java System Directory Server Resource Kit, includes the library calls that allow you to invoke extended operations. Also, there are basic examples that show you the minimum amount of code to write to make a call and get a response.
Clients can discover which extended operations are available on a LDAP Server, by querying the Directory Specific Entry (DSE). Here is the search that retrieves the list of extended operations available on Sun Java System Directory Server, on which we installed the extended operation example:
CODE EXAMPLE 1 Search for List of Extended Operations
<nico ldap-triggers>~$ ldapsearch -p 10389 -D "cd=Directory Manager" -w manager0 -s base -b "" "objectclass=*" supportedExtension supportedExtension: 2.16.840.1.113730.3.5.7 supportedExtension: 2.16.840.1.113730.3.5.8 [...cut...] supportedExtension: 1.3.6.1.4.1.42.2.27.9.6.22 supportedExtension: 4.3.2.1 supportedExtension: 1.3.6.1.4.1.4203.1.11.3
The extension "4.3.2.1" is our implementation. You might have noticed how its number has a different pattern when compared with the others listed in the output. In fact it is a number invented for the purpose of our example; instead, it should be unique and officially registered, to avoid conflict with other registered services.
On the client side, the job is fairly easy, because you only need to invoke a library call with all its requested parameters, then either process the resulting data or manage an exception, in case of unexpected error. The difficult job is on the server side, in the extended operation routine itself, which you have to write. To implement an extended operation, you need to do the following:
Write a C function, using the Plug-In API
Register the function in the server (through a standard mechanism)
Handle the call when it is invoked by clients
To register your extended operation at runtime, write an initialization routine that gets called by the server at startup, giving you the opportunity to register and set up the data structure for your operations.
The following code example illustrates the initialization routine in triggers_extop.c, one of the source files in our example.
CODE EXAMPLE 2 Initialization Routine in triggers_extop.c
#include <stdio.h> /* Standard C headers */ #include <stdlib.h> #include <string.h> #include <errno.h> #include <slapi-plugin.h> /* SunONE DS Plugin-API 5.2 */ #include "slapi_utils.h" /* Our logging utilities, log_info_conn, log_error ... */ #include "triggers_impl.h" /* Triggers data structures */ #include "triggers_extop.h" char *trgbase; ... int triggers_extop_init(Slapi_PBlock * pb) { ... trgbase = slapi_ch_strdup(argv[0]); /* Extended operation plug-ins may handle a range of OIDs. */ oid_list = (char **)slapi_ch_malloc((argc) * sizeof(char *)); for (i = 1; i < argc; ++i) { oid_list[i] = slapi_ch_strdup(argv[i]); log_info_conn(pb, "triggers_extop_init", "Argv[%i]= %s.", oid_list[i] ); } oid_list[argc - 1] = NULL; rc |= slapi_pblock_set( /* Extended op. handler */ pb, SLAPI_PLUGIN_EXT_OP_FN, (void *) trg_service_fn ); rc |= slapi_pblock_set( /* List of OIDs handled */ pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, oid_list ); ... }
NOTE
To save space in this article, we omit irrelevant code such as the code for log and error management.
The routines log_info, log_error, log_warning, and so on in slapi_utils.c source are shorter forms of the widely used slapi_log_info_ex, slapi_log_warning_ex, and slapi_log_error_ex functions of the Plug-In API. If you use the official log routines, they have a much longer parameter list and you need an extra call to retrieve some of them. With slapi_utils.c functions, you write less lines and the code is a bit more readable.
The initialization code is divided into two parts:
Get information: plug-in identity and arguments
Set information: version, extended operation routine address, and list of Object IDentifiers (OIDs) handled by the routine
The code is similar to the code example in testextendedop.c, located in the plug-in directory of any Sun Java System Directory Server installation. The one notable difference is that this plug-in requests a plug-in ID, which is necessary if you need to invoke internal server operations (internal operations are LDAP operations not initiated by user requests). This plug-in code uses server internal operations, therefore a plug-in ID is mandatory.
The function trg_service_fn is our extended operation routine. The signature is simple because the server passes all the information through a SLAPI parameter block (which is the only routine argument), and you use slapi_pblock_get to obtain from it the data you need and expect. The plug-in requires an argument list where the first element is the base DN for its operations (for example, ou=triggers,o=sunblueprints), and the rest are OIDs of extended operations implemented in plug-ins. We implement only one extended operation, even though the code supposes many.
To register your extended operation plug-in, you need an LDAP Data Interchange Format (LDIF) file, which defines an add operation under cn=plugins, cn=config. If you prefer, perform the add directly from the command line, without storing instructions in a LDIF file. The content of the LDIF file is as follows:
CODE EXAMPLE 3 LDIF File Contents
dn: cn=Triggers ExtendedOp,cn=plugins,cn=config changetype: add cn: TriggersExtendedOp objectclass: top objectclass: nsSlapdPlugin objectclass: extensibleObject nsslapd-pluginpath: <server_root>/lib/triggers_extop.so.1.0 nsslapd-pluginInitfunc: triggers_extop_init nsslapd-pluginType: extendedop nsslapd-plugin-depends-on-type: database nsslapd-pluginEnabled: on nsslapd-pluginId: triggers_extop nsslapd-pluginVersion: 1.0 nsslapd-pluginVendor: Sun Microsystems Italy nsslapd-pluginDescription: Sun Blueprints Triggers Extended Operation Plugin nsslapd-pluginArg0: ou=triggers,o=SunBlueprints nsslapd-pluginArg1: 4.3.2.1
Among other things, we define plug-in name, plug-in ID, where the library containing the functions is on the file system, what is the initialization function, and what is the plug-in type. The last two attributes make the plug-in argument list. As mentioned previously, OID 4.3.2.1 is a unique unregistered OID for our extended operation. If you want to change the argument list later, after the plug-in deployment, you can use the Administrator's Console using the "Configuration" tab. Refer to the Administrator's Console documentation.
Before illustrating what our extended operation routine does, we want to focus on the purpose and design of our code example.