eDirectory has for some time supported LDAP transactions and this is documented in the Administration Guide that can be found here:
The documentation nicely describes what kind of LDAP extensions are involved in the transactions mechanism and how the flow works.
But there is no SDK or sample code that shows how to actually use LDAP transactions. Since the concept of transactions in the context of LDAP is quite interesting and I could think of a couple of use cases I decided to write my own sample code that I'm sharing on Github.
What exactly are LDAP transactions then?
If you modify a single object in eDirectory using LDAP the operation will either succeed or fail, there is no middle ground.
For example if you tried to modify a user in a single operation where you try to change multiple attributes, maybe you are attempting to remove a mandatory attribute such as "sn" and add an optional attribute such as "workforceID", the operation would fail. Since eDirectory would refuse to remove the "sn" attribute it wouldn't either add the "workforceID" attribute.
You could say that each update is atomic in regards to single objects.
But what happens if you need to modify multiple objects and you want all the updates to succeed or none at all.
For example you might have a business requirement where you are required to update attribute "X" on "object B", and at the same time change attribute "Y" och "object A".
This is tricky to do in LDAP since every object is updated without regards to any other object.
If you want to modify multiple objects and you want to be sure that all objects are modified correctly, you must write your own code for keeping track if all the modification succeed and in case they don't then you are responsible for rolling back the changes. This is not an ideal position to be in.
LDAP transactions are a way to solve that problem, using LDAP transactions allows you group together all the changes that should occur in a transaction and execute them as a unit.
It means that either all changes succeed or nothing succeeds. There is no need for the application developer to write a bunch of code that will do a rollback in case one change fails.
Over at Github you will find a number of Java classes that one can use to get started with LDAP transactions.
For the purpose of this sample I am using the old "LDAP Classes for Java" library from Novell for connecting to the LDAP server. The library can found here:
Or if you prefer to use Maven:
My sample code consists of these packages and classes:
The SampleEdirTransaction.java file contains example code that shows how to use the LDAP transactions feature in eDirectory.
Here are some key points:
LDAPConnection lc = new LDAPConnection(); //Creates an LDAPConnection object which is used for all subsequent operations such as connecting and binding to the LDAP server as well as carrying out the actual modifications.
lc.connect(); //Establishes a connection to the LDAP server
lc.bind("cn=admin,o=system", "secret"); //Login as the specified user with the password "secret"
CreateGroupingRequest gr = new CreateGroupingRequest(); //Request object that will let the LDAP server know that we want to use the transactions feature.
LDAPExtendedResponse createGroupingRequest = lc.extendedOperation(gr); //Send the request to the LDAP server as an extended operation. The server should reply with a CreateGroupingResponse.
Now we should check the type of the "createGroupingRequest" variable, if it's a CreateGroupingResponse we can continue.
CreateGroupingResponse resp = (CreateGroupingResponse)createGroupingRequest; //Cast the createGroupingRequest to a CreateGroupingResponse that's easier to work with.
GroupingControl control = resp.getControl(true); //The response contains a control with a cookie, the control containing the cookie needs to be sent with each modification to let the server know it belongs to that transaction.
LDAPConstraints constraints = lc.getConstraints();
constraints.setControls(control); //Modify the current constraints by setting the new control (GroupingControl) with the transaction cookie.
Next we will queue all the operations that should be part of this transaction, in this example the first operation will write the current time to the description attribute and the second operation will attempt to delete the "sn" attribute from another user.
lc.modify(firstObjectToModify, addTimeToDesc, constraints);//Add the current time to the description attribute
lc.modify(secondObjectToModify, delSn, constraints);//Delete the sn attribute
//When we are ready we can tell eDirectory to execute the queued operations as one transaction by calling the EndGroupingRequest extended operation.
LDAPExtendedResponse endGroupingResponse = lc.extendedOperation(new EndGroupingRequest(control));
If the EndGroupingRequest fails for some reason, e.g. because one operation didn't succeed, an exception will be thrown which contains the details.
In my example this is the message I got from eDirectory:
LDAPException: Object Class Violation (65) Object Class Violation
LDAPException: Server Message: Transaction (cookie 676069484) operation 2 (modify entry) NDS error: missing mandatory (-609)
Taking a look in eDirectory I can see that the first operation which normally would have succeeded (changing the description attribute), wasn't performed as a result of the failure to delete the mandatory "sn" attribute from a completely different object.
Looking at the LDAP trace during the operation we can see the following:
23:16:30 FB1D2700 LDAP: New cleartext connection 0x132b7180 from 192.168.0.145:30639, monitor = 0xcab82700, index = 12 //Connection established
23:16:31 ADA10700 LDAP: DoBind on connection 0x132b7180
23:16:31 ADA10700 LDAP: Bind name:cn=admin,o=system, version:3, authentication:simple //Login as the admin user
23:16:31 ADA10700 LDAP: Sending operation result 0:"":"" to connection 0x132b7180
23:16:31 C9ECD700 LDAP: DoExtended on connection 0x132b7180
23:16:31 C9ECD700 LDAP: DoExtended: Extension Request OID: 2.16.840.1.1137184.108.40.206.1 //Create grouping request
23:16:31 C9ECD700 LDAP: Sending operation result 0:"":"" to connection 0x132b7180
23:16:31 F9EBF700 LDAP: DoModify on connection 0x132b7180
23:16:31 F9EBF700 LDAP: modify: dn (cn=labdi999305,ou=POC,dc=test) //First modify, this one would normally go through
23:16:31 F9EBF700 LDAP: modifications:
23:16:31 F9EBF700 LDAP: replace: description
23:16:31 F9EBF700 LDAP: ModifyObject: To call DDCMultiObjectTransaction with DDCMOT_OP_MODIFY_ENTRY
23:16:31 F9EBF700 LDAP: Sending operation result 0:"":"" to connection 0x132b7180
23:16:31 CA47B700 LDAP: DoModify on connection 0x132b7180
23:16:31 CA47B700 LDAP: modify: dn (cn=vabdi997915,ou=POC,dc=test) //Second modify, this will fail
23:16:31 CA47B700 LDAP: modifications:
23:16:31 CA47B700 LDAP: delete: sn
23:16:31 CA47B700 LDAP: ModifyObject: To call DDCMultiObjectTransaction with DDCMOT_OP_MODIFY_ENTRY
23:16:31 CA47B700 LDAP: Sending operation result 0:"":"" to connection 0x132b7180
23:16:31 AD6E7700 LDAP: DoExtended on connection 0x132b7180
23:16:31 AD6E7700 LDAP: DoExtended: Extension Request OID: 2.16.840.1.1137220.127.116.11.2 //End grouping request, i.e. execute the transaction
23:16:31 AD6E7700 LDAP: Transaction (cookie 676069484) operation 2 (modify entry) NDS error: missing mandatory (-609) //Boom! The entire transaction fails.
23:16:31 AD6E7700 LDAP: Sending operation result 65:"":"Transaction (cookie 676069484) operation 2 (modify entry) NDS error: missing mandatory (-609)" to connection 0x132b7180
23:16:31 CAB82700 LDAP: Connection 0x132b7180 read failure, setting err = -5874
23:16:31 CAB82700 LDAP: Monitor 0xcab82700 found connection 0x132b7180 socket closed, err = -5874, 0 of 0 bytes read
23:16:31 CAB82700 LDAP: Monitor 0xcab82700 initiating close for connection 0x132b7180
23:16:31 CA47B700 LDAP: Server closing connection 0x132b7180, socket error = -5874
23:16:31 CA47B700 LDAP: Connection 0x132b7180 closed
All the code and a precompiled JAR can be found on Github.