go to  ForumEasy.com   
LdapPro  
 
 
   Home  |  MyForum  |  FAQ  |  Archive    You are not logged in. [Login] or [Register]  
Forum Home » LDAP Operations & Controls » LDAP Persistent Search Control -- JNDI Client
Email To Friend  |   Set Alert To This Topic Rewarding Points Availabe: 0 (What's this) New Topic  |   Post Reply
Author Topic: LDAP Persistent Search Control -- JNDI Client
SteveHB
member
offline   
 
posts: 113
joined: 05/31/2006
from: Mountain View, CA
  posted on: 03/13/2007 01:32:06 PM    Edit  |   Quote  |   Report 
LDAP Persistent Search Control -- JNDI Client
The LDAP Persistent Search Control is to alter the standard LDAP search operation so that it does not end after the initial set of entries matching the search criteria are returned. Instead, LDAP servers keep the search operation going. This provides clients and servers participating in Persistent Search with an active channel through which entries that change (and additional information about the changes that occur) can be communicated.


From the client's point of view, the control sent to server may be included in the Controls portion of an LDAPv3 SearchRequest message, as defined in Section 4.1.12 of [LDAPv3]. The structure of this control is as follows:
	PersistentSearchControl ::= SEQUENCE {
        	controlType     2.16.840.1.113730.3.4.3,
	        criticality     BOOLEAN DEFAULT FALSE,
	        controlValue    persistentSearchControlValue
	}


The persistentSearchControlValue is an OCTET STRING wrapping the BER-encoded
version of the following SEQUENCE:

	persistentSearchControlValue::= SEQUENCE {
                   changeTypes INTEGER,
			-- add(1)|delete(2)|modify(4)|modDN(8)
                   changesOnly BOOLEAN,
                   returnECs BOOLEAN
	}


 Profile | Reply Points Earned: 0
SteveHB
member
offline   
 
posts: 113
joined: 05/31/2006
from: Mountain View, CA
  posted on: 03/13/2007 01:33:47 PM    Edit  |   Quote  |   Report 
Code example of Persistent Search Control
/**
 *  A code example of Persistent Search Control JNDI Client 
 *  Note: This example has been tested to work with SunOne Directory Server
 *        It doesn't work with Active Directory.
 */
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.event.*;
import javax.naming.ldap.*;

import java.util.Hashtable;
import java.io.*;

public class PersistentSearchControlJndiClient 
{
	   
  static final String PERSISTENT_SEARCH_OID = "2.16.840.1.113730.3.4.3";
  static final String QUIT_PROMPT = "\nEnter 'q' to quit: ";
	
  public static void main(String[] args)
  {
    LdapContext   rootContext;
    EventDirContext eventContext;

    //create initial context
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://myserevr.mycompany.com:389");
    env.put(Context.SECURITY_AUTHENTICATION, "simple");
    env.put(Context.SECURITY_PRINCIPAL, "cn=directory manager");
    env.put(Context.SECURITY_CREDENTIALS, "mypassword");
    env.put(Context.BATCHSIZE, "1");  //return each change as it occurs
    env.put("java.naming.ldap.derefAliases", "never");
    
    try{
      // creating the initial context performs the LDAP bind
      rootContext = new InitialLdapContext(env, null);

      // verify that persistent search is supported, exit if not supported
      if (!isPersistentSearchSupported(rootContext)){
        System.out.println(
        		"The LDAP Server does not support persistent search");
        System.exit(1);
      }

      // do a look up of the search base to create an EventDirContext
      // to which the search listener can be added.
      eventContext = (EventDirContext)rootContext.lookup("dc=mydomain,dc=com");

      // create a MyEventListener instance to listen for events
      MyEventListener listener = new MyEventListener("mylistener");

      // Set up the search constraints
      SearchControls constraints = new SearchControls();
      constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);

      // Add the naming listener to the searchBase context. The interface
      // methods of the listener will be called in a separate thread when
      // a relevant event occurs
      eventContext.addNamingListener(
        "",         //use the eventContext object as the target
        "(objectClass=*)",  //filter, include all objects
        constraints,    //search the subtree below the eventContext
        listener);      //the listener object

      // loop until the user enters a q to quit.
      BufferedReader in
        = new BufferedReader(new InputStreamReader(System.in));
      try{
        String input;
        while (true){
          System.out.print(QUIT_PROMPT);
          input = in.readLine();
          if ( input.startsWith("q") || input.startsWith("Q") )
            break;
        }
      }
      catch(IOException e)
      {
        System.out.println(e.getMessage());
      }

      // Not strictly necessary since the context is closed below
      eventContext.removeNamingListener(listener);

      // Close context when we're done
      eventContext.close();

    }
    catch(AuthenticationException e)
    {
      System.out.println(e.getMessage());
    }
    catch(NamingException e)
    {
      System.out.println(e.getMessage());
    }
  }

  /**
   * isPersistentSearchSupported
   *
   * Query the rootDSE to find out if the persistent search control
   * is supported.
   */
  static boolean isPersistentSearchSupported(
                  LdapContext rootContext) throws NamingException
  {
    SearchResult     rootDSE;
    NamingEnumeration  searchResults;
    Attributes       attrs;
    NamingEnumeration  attrEnum;
    Attribute      attr;
    NamingEnumeration  values;
    String         value;
    String[]       attrNames = {"supportedControl"};
    SearchControls     searchControls = new SearchControls();

    searchControls.setCountLimit(0);  //0 means no limit
    searchControls.setReturningAttributes(attrNames);
    searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);

    // search for the rootDSE object
    searchResults =
       rootContext.search("", "(objectClass=*)", searchControls);

    while (searchResults.hasMore())
    {
      rootDSE = (SearchResult)searchResults.next();

      attrs = rootDSE.getAttributes();
      attrEnum = attrs.getAll();
      while (attrEnum.hasMore())
      {
        attr = (Attribute)attrEnum.next();
        values = attr.getAll();
        while (values.hasMore())
        {
          value = (String) values.next();
          if (value.equals(PERSISTENT_SEARCH_OID))
            return true;
        }
      }
    }
    return false;
  }


  /**
   * MyEventlistener class
   * An instance of this class is registered with an EventDirContext object.
   * The registered instance's NamespaceChangeListener interface methods are
   * called when a pertinent event occurs.
   */
  static class MyEventListener implements NamespaceChangeListener,
                      ObjectChangeListener
  {
    private String id;

    public MyEventListener(String id)
    {
      this.id = id;
    }

    public void objectAdded(NamingEvent evt)
    {
      System.out.println(
       "\n\n" + id + ">>> object added event. Object Name: " +
       evt.getNewBinding().getName());
      System.out.print(QUIT_PROMPT);
    }

    public void objectRemoved(NamingEvent evt)
    {
      System.out.println(
       "\n\n" + id + ">>> object removed event. Object Name: " +
       evt.getOldBinding().getName());
      System.out.print(QUIT_PROMPT);
    }

    public void objectRenamed(NamingEvent evt)
    {
      System.out.println(
       "\n\n" + id + ">>> object renamed event. New name: " +
       evt.getNewBinding().getName() +
       " Old name: " + evt.getOldBinding().getName());
      System.out.print(QUIT_PROMPT);
    }

    public void objectChanged(NamingEvent evt)
    {
      System.out.println(
       "\n\n" + id + ">>> object changed event. Object name: " +
       evt.getNewBinding().getName());
      System.out.print(QUIT_PROMPT);
    }

    public void namingExceptionThrown(NamingExceptionEvent evt)
    {
      System.out.println(
        "\n\n" + id + ">>> Listener received a naming exception");
      evt.getException().printStackTrace();
      System.out.print(QUIT_PROMPT);
    }
  }
} 

 Profile | Reply Points Earned: 0
SteveHB
member
offline   
 
posts: 113
joined: 05/31/2006
from: Mountain View, CA
  posted on: 03/15/2010 07:46:34 PM    Edit  |   Quote  |   Report 
How do I know what specific attributes had been changed?

By the nature of event notification which is used in PeersistentSearchControl or precisely EntryChangeNotificationControl, there is no spot in the control to tell the detailed information about the changes. You have to poll it to see the what has been changed.


Two ways:

Way #1 -- based on 'changeNumber'

If chamgeNumber is opted to return within EntryChangeNotification, you can poll all detailed information from the server's Changelog by the given change number.

Way #2 -- based on 'dn'
If chamgeNumber is not available, you have to poll the current entry by the given event's 'dn' and then compare the entry with the previous one to tell the differences.

Here is the related code:

public void objectChanged(NamingEvent evt)
{
    System.out.println(
            "\n\n" + id + ">>> object changed event. Object name: " +
            evt.getNewBinding().getName());
    
    // In order to get the detailed information as to what had really changed, 
    // another search based on the returned name is required.  
    try{
    	EventDirContext eDir = (EventDirContext)evt.getEventContext();
    	// The following command will trigger additional search 
    	// request (without PersistentSearchControl)
    	Attributes attrs = eDir.getAttributes(evt.getNewBinding().getName(), null);
    	Enumeration enu = attrs.getAll();
    	while(enu.hasMoreElements()){
    		Attribute attr = (Attribute)enu.nextElement();
            System.out.println(attr.getID() + ": " + attr.get());
    	}
    }catch(Exception e){
    	e.printStackTrace();
    }
    
    System.out.print(QUIT_PROMPT);
}

 Profile | Reply Points Earned: 0

 
Powered by ForumEasy © 2003-2005, All Rights Reserved. | Privacy Policy | Terms of Use
 
Get your own forum today. It's easy and free.