5 Using the Java API Extensions to JNDI

This chapter explains how to use Java extensions to the standard directory APIs to perform many of the operations introduced in Chapter 3. The chapter presents use cases. The Oracle extensions to the standard APIs are documented in full in Oracle Fusion Middleware Java API Reference for Oracle Internet Directory.

The chapter contains the following topics:

5.1 Installing the Java Extensions

The Java extensions are installed along with the standard Java APIs when the LDAP client is installed. The APIs and their extensions are found at $ORACLE_HOME/jlib/ldapjclnt10.jar.

5.2 Using the oracle.ldap.util Package to Model LDAP Objects

In Java, LDAP entities—users, groups, realms, and applications—are modeled as Java objects instead of as handles. This modeling is done in the oracle.java.util package. All other utility functionality is modeled either as individual objects—as, for example, GUID—or as static member functions of a utility class.

For example, to authenticate a user, an application must follow these steps:

  1. Create oracle.ldap.util.User object, given the user DN.

  2. Create a DirContext JNDI object with all of the required properties, or get one from a pool of DirContext objects.

  3. Invoke the User.authenticateUser method, passing in a reference to the DirContext object and the user credentials.

  4. If the DirContext object was retrieved from a pool of existing DirContext objects, return it to that pool.

Unlike their C and PL/SQL counterparts, Java programmers do not have to explicitly free objects. The Java garbage collection mechanism performs this task.

5.3 The Classes PropertySetCollection, PropertySet, and Property

Many of the methods in the user, subscriber, and group classes return a PropertySetCollection object. The object represents a collection of one or more LDAP entries. Each of these entries is represented by a PropertySet object, identified by a DN. A property set can contain attributes, each represented as a property. A property is a collection of one or more values for the particular attribute it represents. An example of the use of these classes follows:

PropertySetCollection psc = Util.getGroupMembership( ctx,
                                                     myuser,
                                                     null,
                                                     true );
    // for loop to go through each PropertySet
    for (int i = 0; i < psc.size(); i++ ) {

    PropertySet ps = psc.getPropertySet(i);

   // Print the DN of each PropertySet
   System.out.println("dn:  " + ps .getDN());

   // Get the values for the "objectclass" Property
   Property objectclass = ps.getProperty( "objectclass" );

  
   // for loop to go through each value of Property "objectclass"
   for (int j = 0; j< objectclass.size(); j++) {

       // Print each "objectclass" value
       System.out.println("objectclass:  " + objectclass.getValue(j));
   }
}

The entity myuser is a user object. The psc object contains all the nested groups that myuser belongs to. The code loops through the resulting entries and prints out all the object class values of each entry.

5.4 Managing Users

All user-related functionality is abstracted in a Java class called oracle.ldap.util.User. The process works like this:

  1. Construct a oracle.ldap.util.User object based on a DN, GUID, or simple name.

  2. Invoke User.authenticateUser(DirContext, int, Object) to authenticate the user if necessary.

  3. Invoke User.getProperties(DirContext) to get the attributes of the user entry.

  4. Invoke User.getExtendedProperties(DirContext, int, String[]) to get the extended properties of the user. int is either shared or application-specific. String[] is the object that represents the type of property desired. If String[] is null, all properties in a given category are retrieved.

  5. Invoke PropertySetCollection.getProperties(int) to get the metadata required to parse the properties returned in step 4.

  6. Parse the extended properties and continue with application-specific logic. This parsing is also performed by application-specific logic.

5.5 Authenticating Users

User authentication is a common LDAP operation that compares the credentials that a user provides at login with the user's credentials in the directory. Oracle Internet Directory supports the following:

  • Arbitrary attributes can be used during authentication

  • Appropriate password policy exceptions are returned by the authentication method. Note, however, that the password policy applies only to the userpassword attribute.

The following code fragment shows how the API is used to authenticate a user:

 // User user1 - is a valid User Object
        try
        {
                user1.authenticateUser(ctx,
                    User.CREDTYPE_PASSWD, "welcome");
 
                // or
                // user1.authenticateUser(ctx, <any
attribute>, <attribute value>);
        }
        catch (UtilException ue)
        {
                // Handle the password policy error
accordingly
                if (ue instanceof PasswordExpiredException)
                        // do something
                else if (ue instanceof GraceLoginException)
                        // do something
        }

5.6 Creating Users

The subscriber class uses the createUser() method to programmatically create users. The object classes required by a user entry are configurable through Oracle Delegated Administration Services. The createUser() method assumes that the client understands the requirement and supplies the values for the mandatory attributes during user creation. If the programmer does not supply the required information the server returns an error.

The following snippet of sample code demonstrates the usage.

// Subscriber sub is a valid Subscriber object
// DirContext ctx is a valid DirContext
 
// Create ModPropertySet object to define all the attributes and their values.
ModPropertySet mps = new ModPropertySet();
mps.addProperty(LDIF.ATTRIBUTE_CHANGE_TYPE_ADD,"cn", "Anika");
mps.addProperty(LDIF.ATTRIBUTE_CHANGE_TYPE_ADD,"sn", "Anika");
mps.addProperty(LDIF.ATTRIBUTE_CHANGE_TYPE_ADD,"mail",
"Anika@example.com");
 
// Create user by specifying the nickname and the ModPropertySet just defined
User newUser = sub.createUser( ctx, mps, true);
 
// Print the newly created user DN
System.out.println( newUser.getDN(ctx) );
 
// Perform other operations with this new user

5.7 Retrieving User Objects

The subscriber class offers the getUser() method to replace the public constructors of the User class. A user object is returned based on the specified information.

The following is a piece of sample code demonstrating the usage:

// DirContext ctx is contains a valid directory connection with
sufficient privilege to perform the operations
 
// Creating RootOracleContext object
RootOracleContext roc = new RootOracleContext(ctx);
 
// Obtain a Subscriber object representing the default
subscriber
Subscriber sub = roc.getSubscriber(ctx,
Util.IDTYPE_DEFAULT, null, null);
 
// Obtain a User object representing the user whose
nickname is "Anika"
User user1 = sub.getUser(ctx, Util.IDTYPE_SIMPLE, "Anika",
null);
// Do work with this user
 
The getUser() method can retrieve users based on DN, GUID
and simple name.  A getUsers() method is also available to
perform a filtered search to return more than one user at a
time.  The returned object is an array of User objects.
For example,
 
// Obtain an array of User object where the user's nickname
starts with "Ani"
User[] userArr = sub.getUsers(ctx, Util.IDTYPE_SIMPLE,
"Ani", null);
// Do work with the User array

5.8 Retrieving Objects from Realms

This section describes how the Java API can be used to retrieve objects in identity management realms.

The RootOracleContext class represents the root Oracle Context. Much of the information needed for identity management realm creation is stored within the root Oracle Context. The RootOracleContext class offers the getSubscriber() method. It replaces the public constructors of the subscriber class and returns an identity management realm object based on the specified information.

The following is a piece of sample code demonstrating the usage:

// DirContext ctx contains a valid directory
// connection with sufficient privilege to perform the
// operations
 
// Creating RootOracleContext object
RootOracleContext roc = new RootOracleContext(ctx);
 
// Obtain a Subscriber object representing the
// Subscriber with simple name "Oracle"
Subscriber sub = roc.getSubscriber(ctx,
Util.IDTYPE_SIMPLE, "Oracle", null);
 
// Do work with the Subscriber object

5.9 Example: Search for Oracle Single Sign-On Login Name

The following example shows how to find a user's login name when you have the simple name, GUID, or DN. The Oracle Single Sign-On login name is also referred to as nickname.

There are two parts to this example:

  1. Determine which attribute is used to store the nickname in this realm.

  2. Retrieve the User object and determine the value of the nickname attribute.

import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
import oracle.ldap.util.jndi.*;
import oracle.ldap.util.*;
import java.io.*; 
 
public class NickNameSearch {
 
   public static void main(String[] args)
      throws Exception
   {
      InitialLdapContext ctx = ConnectionUtil.getDefaultDirCtx( args[0],
                                     args[1], args[2],args[3]);
 
      RootOracleContext roc=new RootOracleContext(ctx);
      Subscriber sub = null;
      sub = roc.getSubscriber(ctx, Util.IDTYPE_DEFAULT, null, null) ;
 
      PropertySetCollection psc = sub.getProperties(ctx, 
                                     Subscriber.USER_NAMING_PROPERTIES, null);
    
      String nickNameAttribute = null;
      try
      {
          nickNameAttribute = (String) psc.getPropertySet(0).getProperty(Subscriber.USER_NAMING_ATTR_SIMPLE).getValue(0);
      }
      catch (Exception e)
      {
         // unable to retrieve the attribute name
         System.exit(0);
      }
      System.out.println("Nickname attribute: " + nickNameAttribute);      
 
      // Retrieve user using simple name, guid or DN
      User user = sub.getUser(ctx, Util.IDTYPE_SIMPLE,"orcladmin", null);
      System.out.println("user DN: " + user.getDN(ctx));
 
      // Retrieve nickname value using User object
      psc = user.getProperties(ctx, new String[]{ nickNameAttribute });
     
      String nickName = null;
      try
      {
          nickName = (String) psc.getPropertySet(0).getProperty(nickNameAttribute).getValue(0);
      }
      catch (Exception e)
      {
         // unable to retrieve the attribute value 
         System.exit(0);
      }
      System.out.println("Nickname : " + nickName);      
   }
}

5.10 Discovering a Directory Server

A new Java class, the public class, has been introduced:

public class oracle.ldap.util.discovery.DiscoveryHelper

This class provides a method for discovering specific information from the specified source.

Table 5-1 Methods for Directory Server Discovery

Method Description
discover

Discovers the specific information from a given source

setProperty

Sets the properties required for discovery

getProperty

Accesses the value of properties


Two new methods are added to the existing Java class oracle.ldap.util.jndi.ConnectionUtil:

  • getDefaultDirCtx: This overloaded function determines the host name and port information of non-SSL ldap servers by making an internal call to oracle.ldap.util.discovery.DiscoveryHelper.discover().

  • getSSLDirCtx: This overloaded function determines the host name and port information of SSL ldap servers by making an internal call to oracle.ldap.util.discovery.DiscoveryHelper.discover().

5.11 Example: Discovering a Directory Server

The following is a sample Java program for directory server discovery:

import java.util.*;
import java.lang.*;
import oracle.ldap.util.discovery.*;
import oracle.ldap.util.jndi.*;
 
public class dsdtest
{
  public static void main(String s[]) throws Exception
  {
    HashMap reshdl = new HashMap();
    String result = new String();
    Object resultObj = new Object();
    DiscoveryHelper disco = new
DiscoveryHelper(DiscoveryHelper.DNS_DISCOVER);
 
// Set the property for the DNS_DN
disco.setProperty(DiscoveryHelper.DNS_DN,"dc=us,dc=fiction,dc=com")
;
 
// Set the property for the DNS_DISCOVER_METHOD
disco.setProperty(DiscoveryHelper.DNS_DISCOVER_METHOD,
   DiscoveryHelper.USE_INPUT_DN_METHOD);
 
// Set the property for the SSLMODE
disco.setProperty(DiscoveryHelper.SSLMODE,"0");
 
// Call the discover method
int res=disco.discover(reshdl);
if (res!=0)
   System.out.println("Error Code returned by the discover method is :"+res) ;
 
// Print the results
printReshdl(reshdl);
}
 
public static void printReshdl(HashMap reshdl)
{
  ArrayList result = (ArrayList)reshdl.get(DiscoveryHelper.DIR_SERVERS);
 
if (result != null)
{
  if (result.size() == 0) return;
  System.out.println("The hostnames are :-");
  for (int i = 0; i< result.size();i++)
  {
      String host = (String)result.get(i);
      System.out.println((i+1)+".
'"+host+"'");
   }
  }
 }
}

5.12 Using DIGEST-MD5 to Perform SASL Authentication

When using JNDI to create a SASL connection, you must set these javax.naming.Context properties:

  • Context.SECURITY_AUTHENTICATION = "DIGEST-MD5"

  • Context.SECURITY_PRINCIPAL

The latter sets the principal name. This name is a server-specific format. It can be either of the following:

  • The DN—that is, dn:—followed by the fully qualified DN of the entity being authenticated

  • The string u: followed by the user identifier.

    The Oracle directory server accepts just a fully qualified DN such as cn=user,ou=my department,o=my company.

Note:

The SASL DN must be normalized before it is passed to the API that calls the SASL bind. To generate SASL verifiers, Oracle Internet Directory supports only normalized DNs.

5.13 Example: Using SASL Digest-MD5 auth-int and auth-conf Modes

The following code provides an example of Java LDAP/JNDI using SASL Digest-MD5.

/* $Header: LdapSasl.java 27-oct-2005.11:26:59 qdinh Exp $ */
 
/* Copyright (c) 2003, 2005, Oracle. All rights reserved.  */
 
/*
  DESCRIPTION
   <short description of component this file declares/defines>
 
  PRIVATE CLASSES
   <list of private classes defined - with one-line descriptions>
 
  NOTES
   <other useful comments, qualifications, and so on.>
 
  MODIFIED    (MM/DD/YY)
   qdinh       04/23/03 - Creation
*/
 
/**
*  @version $Header: LdapSasl.java 27-oct-2005.11:26:59 qdinh Exp $
*  @author  qdinh   *  @since   release specific (what release of product did this appear in)
*/
 
package oracle.ldap.util.jndi;
 
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
import oracle.ldap.util.jndi.*;
import oracle.ldap.util.*;
import java.lang.*;
import java.util.*;  
public class LdapSasl
{
   public static void main( String[] args)
       throws Exception
   {
 
    int numofargs;
 
    numofargs = args.length;
 
    Hashtable hashtable = new Hashtable();
 
    // Look through System Properties for Context Factory if it is available
    // then set the CONTEXT factory only if it has not been set
    // in the environment -
    // set default to com.sun.jndi.ldap.LdapCtxFactory
 
    hashtable.put(Context.INITIAL_CONTEXT_FACTORY,
          "com.sun.jndi.ldap.LdapCtxFactory");
    // possible valid arguments
    // args[0] - hostname
    // args[1] - port number
    // args[2] - Entry DN
    // args[3] - Entry Password
    // args[4] - QoP [ auth | auth-int | auth-conf ]
    // args[5] - SASL Realm
    // args[6] - Cipher Choice
    //    If QoP == "auth-conf" then args[6] cipher choice can be
    //     - des
    //     - 3des
    //     - rc4
    //     - rc4-56
    //     - rc4-40
 
    hashtable.put(Context.PROVIDER_URL, "ldap://"+args[0]+":"+args[1]);
        hashtable.put(Context.SECURITY_AUTHENTICATION, "DIGEST-MD5");
    System.out.println("hash put security dn: " + args[2]);
    hashtable.put(Context.SECURITY_PRINCIPAL, args[2] );
    hashtable.put(Context.SECURITY_CREDENTIALS, args[3] );
 
    // For Quality of Protection modes
    // 1. Authentication and Data Integrity Mode - "auth-int"
    // 2. Authentication and Data Confidentiality Mode  "auth-conf"
 
    //
    // hashtable.put("javax.security.sasl.qop",args[4]);
    hashtable.put("javax.naming.security.sasl.realm", args[5]);
   
    // Setup Quality of Protection
    //
    // System.out.println("hash sasl.qop: " + args[4]);
 
    hashtable.put("javax.security.sasl.qop",args[4]);
 
   
    if (numofargs > 4)
    {
    if (args[4].equalsIgnoreCase("AUTH-CONF"))
    {
 
        //  Setup a cipher choice only if QoP == "auth-conf"
        String strength = "high";
        String cipher = new String(args[6]);
                if (cipher.compareToIgnoreCase("rc4-40") == 0)
        strength = "low";
        else if (cipher.compareToIgnoreCase("rc4-56") == 0 ||
             cipher.compareToIgnoreCase("des")== 0 )
        strength = "medium";
        else if (cipher.compareToIgnoreCase("3des") == 0 ||
             cipher.compareToIgnoreCase("rc4") == 0)
        strength = "high";
 
        // setup cipher choice
        System.out.println("hash sasl.strength:"+strength);
        hashtable.put("javax.security.sasl.strength",strength);
    }
 
    // set maxbuffer length if necessary
    if (numofargs > 7 && !"".equals(args[6]))
        hashtable.put("javax.security.sasl.maxbuf", args[5].toString());
    }
 
 
    // Enable Debug --
    // hashtable.put("com.sun.jndi.ldap.trace.ber", System.err);
 
    LdapContext ctx = new InitialLdapContext(hashtable,null);
 
 
    // At this stage - SASL Digest -MD5 has been successfully
 
    System.out.println("sasl bind successful");
 
    // Ldap Search Scope Options
    //
    // - Search base - OBJECT_SCOPE
    // - One Level   - ONELEVEL_SCOPE
    // - Sub Tree    - SUBTREE_SCOPE
    //
        // Doing an LDAP Search
    PropertySetCollection psc = Util.ldapSearch(ctx,"o=oracle,dc=com","objectclass=*",SearchControls.OBJECT_SCOPE,
                           new String[] {"*"});
      // Print out the serach result
    Util.printResults(psc);
 
    System.exit(0);
 
 }                                                              }

5.14 Using LDAP Transactions

As of 11g Release 1 (11.1.1.6), the Oracle Internet Directory SDK supports transactions, as defined in RFC 5805.

See Also:

RFC 5805 on Lightweight Directory Access Protocol (LDAP) Transactions at: http://www.ietf.org

Transactions are necessary to support a number of applications, including provisioning. When a transaction is committed, either all operations within the transaction update succeed or all fail. Oracle Internet Directory publishes the Object Identifiers of the extended operations and the control as supportedExtension and supportedControl in the root DSE.

Oracle Internet Directory LDAP transactions use the following entities.

  • Start Transaction Request (Object Identifier 1.3.6.1.1.21.1): This is an LDAP extended operation that facilitates grouping of related operations for starting a transaction. It is an LDAPMessage of CHOICE extendedReq where requestName is 1.3.6.1.1.21.1 and the requestValue is absent.

  • Start Transaction Response: This is sent by Oracle Internet Directory server in response to a Start Transaction Request. It is an LDAPMessage of CHOICE extendedRes. Its responseName is absent. Upon success, resultCode (0), responseValue is present and contains the transaction identifier. Upon failure, for example a malformed request, the responseValue is absent.

  • Transaction Specification Control (Object Identifier 1.3.6.1.1.21.2): This is an LDAPControl indicating association of an operation to a transaction by means of the transaction identifier, which is the value of this control. Its criticality is TRUE.

  • End Transaction Request (Object Identifier 1.3.6.1.1.21.3): This is another LDAP extended operation used to indicate the end of a transaction. This indicates the settling of the transaction, resulting in a commit or abort of the transaction. This LDAPMessage has a CHOICE extendedReq where the requestName is 1.3.6.1.1.21.3 and the requestValue is present and contains BER-encoded transaction request.

    A commit value of TRUE indicates a request to commit the transaction identified by the identifier. A commit value of FALSE indicates a request to abort the identified transaction.

  • End Transaction Response: This is an LDAPMessage response of Oracle Internet Directory server to an End Transaction Request. Its response name is absent. The responseValue, when present, contains a BER-encoded transaction response.

  • Aborted Transaction Notice (Object Identifier 1.3.6.1.1.21.4): This is an Unsolicited Notification message where responseName is 1.3.6.1.1.21.4 and responseValue is present and contains a transaction identifier.

The following is the sequence of requests and responses between the Oracle Internet Directory server and its client in an LDAP transaction.

  1. A client issues a Start Transaction Request to Oracle Internet Directory server.

  2. Oracle Internet Directory server responds to this request with a Start Transaction Response providing a transaction identifier and with a resultCode of success (0). Upon failure, for example, in the case of a malformed request, the server responds with a Start Transaction Response with a resultCode other than success indicating the nature of the error.

    This transaction identifier generated and sent to client in a success case is used in subsequent protocol messages to identify this transaction.

  3. If the client receives a success result code from the server, it then attaches a Transaction Specification Control containing the transaction identifier to each of the update operations to indicate that they are to be processed as part of a single transaction. For a valid response, Oracle Internet Directory sends a success. Otherwise it sends to its client an appropriate response to the request with a resultCode along with a detailed nature of the failure.

  4. After the client is done sending all the update operations of the transaction, accompanied by the Transaction Specification Control containing the transaction identifier to Oracle Internet Directory server, it sends an End Transaction Request including the transaction identifier to Oracle Internet Directory server, indicating that it wants to settle the transaction. A commit value of TRUE indicates a request to commit the transaction while a value of FALSE indicates a request to abort the transaction.

  5. Upon receiving a request to abort the transaction, Oracle Internet Directory server aborts the identified transaction by abandoning all the operations that were part of the transaction and indicates that it has done so by returning an End Transaction Response with a resultCode of success (0). Upon receiving a request to commit the transaction, Oracle Internet Directory server applies commit to DB for the updates it performed for all the operations in the transaction. If it succeeds, the server returns an End Transaction Response with a resultCode of success (0). Otherwise, it returns an End Transaction Response with a non-success resultCode, indicating the nature of the failure.

    There is no requirement that the server serialize transactions or updates requested outside of the transaction. That is, the server may process multiple commit requests, from one or more clients, acting upon different sets of entries concurrently.

  6. If, at any time during the above exchange between the client and server, the server is unable to continue the specification of a transaction, the server issues and Aborted Transaction Notice. Upon receiving this notice, the client must discontinue all use of the transaction identifier and the transaction is null and void. All operations that were to be processed as part of the transaction are implicitly abandoned. Any further use of the identifier by the client results in a response containing a failure resultCode from Oracle Internet Directory server.

  7. If, at any time during the above exchange between the client and server, the client closes the connection to Oracle Internet Directory server, the server performs the necessary connection-related clean up.

5.15 Example: Using LDAP Transactions

This section provides a brief outline of the steps required to develop a JNDI-based LDAP transaction Java client module using LDAP extended operations. The steps include implementing four interfaces: Start Transaction Request, Start Transaction Response, End Transaction Request, and End Transaction Response. The classes implementing these four interfaces must be used in code performing LDAP update operations within transaction semantics. The following sample code snippets show the four implemented interfaces and their use in a sample LDAP update operations with transaction semantics.

5.15.1 Start Transaction Request interface implementation (LdapStartTxnReq.java)

import java.lang.*;
import java.util.*;
import java.io.*;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;

public class LdapStartTxnReq implements ExtendedRequest
{

    public static final String TXN_REQ_OID = "1.3.6.1.1.21.1";

    // constructor
    public LdapStartTxnReq()
    {
    }

    // Method for getting the request ID  
    public String getID() {      
      return TXN_REQ_OID; 
   }

    public byte[] getEncodedValue() {  
      return null;  // No value is needed for Start Txn request
    }

    public ExtendedResponse createExtendedResponse(String id, byte[] berValue,
                                                   int offset, int length)
           throws NamingException

    {
        return new LdapStartTxnRes(id, berValue, offset, length);    }
}

5.15.2 Start Transaction Response interface implementation (LdapStartTxnRes.java)

import java.lang.*;
import java.util.*;
import java.io.*;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
 
 
public class LdapStartTxnRes implements ExtendedResponse
{
  public String txnID;
 
  // Called by LdapStartTxnReq.createExtendedResponse()
  protected LdapStartTxnRes(String id, byte[] berValue, int offset, int length)
            throws NamingException
  {
    byte[] buff2 = new byte[length];
 
    System.arraycopy(berValue, offset, buff2, 0, length);
 
    try
    {
       this.txnID = new String(berValue, "UTF8");
    }
    catch (Exception e)
    {
      System.out.println(e);
    }
  }
 
  public String getTxnID()
  {
    return txnID;
  }
 
  // No op for our case
  public byte[] getEncodedValue()
  {
    return null;
  }
 
  public String getID()
  {
    return null; // as have no response ID.
  }
}

5.15.3 End Transaction Request interface implementation (LdapEndTxnReq.java)

import java.lang.*;
import java.util.*;
import java.io.*;
import javax.naming.*;
import javax.naming.ldap.*;
import oracle.ldap.util.ber.*;


public class LdapEndTxnReq implements ExtendedRequest
{
    public  static final String TXN_END_REQ_OID = "1.3.6.1.1.21.3";
    private int sequenceTag = 0x30;
    private boolean commitValue;
    private String  controlValue;

    // constructor
    public LdapEndTxnReq(boolean commitVal, String controlVal)
    {
      this.commitValue = commitVal;
      this.controlValue = controlVal;
    }

    // Method to get End Txn Request ID
    public String getID() {
        return TXN_END_REQ_OID;
    }

    public byte[] getEncodedValue()
    {
      boolean encodeUTF8 = true;
      byte[] bytes = null;

      try
      {
        BEREncoder berElement = new BEREncoder();

        berElement.beginSeq(sequenceTag);

        berElement.encodeBoolean(this.commitValue);

        berElement.encodeString(this.controlValue, encodeUTF8);

        berElement.endSeq();
        bytes = berElement.getTrimmedBuf();

        return bytes;
      }
      catch (EncodeException e)
      {
         return null;
      }
    }

    public ExtendedResponse createExtendedResponse(String id, byte[] berValue,
                                                   int offset, int length)
           throws NamingException
    {
      return null;
    }
}

5.15.4 End Transaction Response interface implementation (LdapEndTxnRes.java)

import java.lang.*;
import java.util.*;
import java.io.*;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;


public class LdapEndTxnRes implements ExtendedResponse
{
  // constructor
  LdapEndTxnRes(String id, byte[] berValue, int offset, int length)
                throws NamingException
  {
    ;
  }

  public String getTxnID()
  {
    return null;
  }

  public byte[] getEncodedValue()
  {
    return null;
  }

  public String getID()
  {
    return null;
  }
}

5.15.5 Java and Pseudocode Sample of Interfaces in LDAP Update Operations Within Transaction Semantics (LdapTxnOperation.java)

import java.lang.*;
import java.util.*;
import java.io.*;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
                .
                .
                .

public class LdapTxnOperation implements  …………….
{
    public static void main( String[] args )
    {
        try
        {             
            LdapContext  ctx;

            Perform ldapbind against OID server on the LDAP context ctx;

            If  bind is successful
            {
                /* Issue LDAP Start Transaction Request to OID server and 
                   receive its response */
                LdapStartTxnRes startResp = (LdapStartTxnRes)ctx.extendedOperation(new                                                                                                                           LdapStartTxnReq());
                /* Extract the transaction ID sent by OID server */
                String txnID = startResp.getTxnID();

Issue LDAP update operations (add, delete, modify) to OID server each containing
 the Transaction Control with ID 1.3.6.1.1.21.2 and value of the transaction ID;
 
/* Vendor's technical doc link is provided in Reference section below */

/* After done sending all update operations, issue LDAP End Transaction Request
 to OID server with your intention to either commit or abort the transaction and
 receive its response. */

If  want to commit the transaction
{ /* commit request */
    LdapEndTxnRes endResp = (LdapEndTxnRes)ctx.extendedOperation(new LdapEndTxnReq(true, txnID));
 }
 Else
 { /* abort request */
    LdapEndTxnRes endResp = (LdapEndTxnRes)ctx.extendedOperation(new  LdapEndTxnReq(false, txnID));
 }
              }
              catch( Exception e)
             {
                e.printStackTrace();
                return;
             }
             return ;
         }
      }