participate


Naming and Directory (JNDI) - JNDI, Active Directory, Paging and Range Retrieval
<<   Back to Forum  |   Give us Feedback Topics: « Previous | Next
This topic has 17 replies on 2 pages.    1 | 2 | Next »
adler_steven
Posts:669
Registered: 12/9/04
JNDI, Active Directory, Paging and Range Retrieval   
Dec 9, 2004 5:03 AM

 
A few other problems that developers come across are queries that return either a large number of results, or query that returns a multi-valued attribute that contains a large number of values.

Active Directory incorporates a number of controls, that are designed to ensure optimim performance of the server and to mitigate denial of service attacks.

First of all paging. By default, Active Directory restricts the total number of results that are returned from a LDAP Search to 1000. While this limit can be changed by modifying the LDAP Query policy, the recomended approach is to use paged results.

Note that this ample, which queries for all users that have a value for the mail attribute. uses a page size of 10, not really an optimal use of either the server or of the network, but merely just to demonstrate paging.

Also, the usual security comments apply, you shouldn't hardcode credentials in an application, authentication should either use Kerberos (JAAS & GSSAPI) or if using simple authentication, secured using SSL or TLS, and and any sensitive information communicated between the client and the server should also take place over SSL or TLS.
.
/**
 * paged.java
 * 5 July 2001
 * Sample JNDI application that performs a paged search.
 * 
 */
 
import java.util.Hashtable;
import java.util.Enumeration;
 
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
import com.sun.jndi.ldap.ctl.*;
 
class paged {
 
public static void main(String[] args) {
 
 
		Hashtable env = new Hashtable();
		String adminName = "CN=Administrator,CN=Users,DC=antipodes,DC=com";
		String adminPassword = "XXXXXXXX";
		String searchBase = "DC=antipodes,DC=com";
		String searchFilter = "(&(objectClass=user)(mail=*))";
		
		env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
 
		//set security credentials, note using simple cleartext authentication
		env.put(Context.SECURITY_AUTHENTICATION,"simple");
		env.put(Context.SECURITY_PRINCIPAL,adminName);
		env.put(Context.SECURITY_CREDENTIALS,adminPassword);
				
		//connect to my domain controller
		env.put(Context.PROVIDER_URL, "ldap://mydc.antipodes.com:389");
		try {
 
			// Create the initial directory context
			LdapContext ctx = new InitialLdapContext(env,null);
	    
			// Create the search controls 		
			SearchControls searchCtls = new SearchControls();
		
			//Specify the attributes to return
			String returnedAtts[]={"sn","givenName","mail"};
			searchCtls.setReturningAttributes(returnedAtts);
		
			//Specify the search scope
			searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
 
			//Set the page size and initialize the cookie that we pass back in subsequent pages
			int pageSize = 10;
			byte[] cookie = null;
 
			//Request the paged results control
			Control[] ctls = new Control[]{new PagedResultsControl(pageSize)};
			ctx.setRequestControls(ctls);
 
			//initialize counter to total the results
			int totalResults = 0;
 
			// Search for objects using the filter
 
			do {
				NamingEnumeration results = ctx.search(searchBase, searchFilter, searchCtls);
 
	            	// loop through the results in each page
 
	            	while (results != null && results.hasMoreElements()) {
				SearchResult sr = (SearchResult)results.next();
 
				//print out the name 
				System.out.println("name: " + sr.getName());
 
				//increment the counter
				totalResults++;	
 
	            	}
	
	
			// examine the response controls
			cookie = parseControls(ctx.getResponseControls());
 
	            	// pass the cookie back to the server for the next page
			ctx.setRequestControls(new Control[]{new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
 
			} while ((cookie != null) && (cookie.length != 0));
 
	
			ctx.close();
 
			System.out.println("Total entries: " + totalResults);
 
 
    		} 
		catch (NamingException e) {
       		System.err.println("Paged Search failed." + e);
        	}	
		catch (java.io.IOException e) {
			System.err.println("Paged Search failed." + e);
	      	} 
	}
 
	static byte[] parseControls(Control[] controls) throws NamingException {
 
		byte[] cookie = null;
 
		if (controls != null) {
 
	    		for (int i = 0; i < controls.length; i++) {
				if (controls[i] instanceof PagedResultsResponseControl) {
					PagedResultsResponseControl prrc = (PagedResultsResponseControl)controls[i];
					cookie = prrc.getCookie();
					System.out.println(">>Next Page \n");
				}
	    		}
		}
 
		return (cookie == null) ? new byte[0] : cookie;
    	}
}
 
 


Note that if we had requested a multi-valued attribute instead of the surname (sn), firstname (givenName) and e-mail (mail) attributes, and if that multi-valued had more than 1000 values, then only the first 1000 values would have been returned, irrespective of whether paged results had ben used.

Range retrieval is more evident when retrieving the list of members from a group. The list of members in a group is contained in the member attribute. If there are more than 1000 values in the member attribute, then you must use range retrieval to return all the members.

Information on range retrieval can be found at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/adsi/adsi/attribute_range_retrieval.asp

The following JNDI sample illustrates the retrieval of members from a group, using range retrieval. In this case querying for the list of members of a group called "All Research" in the Active Directory.


/**
 * range.java
 * December 2004
 * Sample JNDI application to demonstrate range retrieval 
 * 
 */
 
import java.util.Hashtable;
import javax.naming.*;
import javax.naming.ldap.*;
import javax.naming.directory.*;
import com.sun.jndi.ldap.ctl.*;
 
//if we were going to do this properly, should check that the server supports
//the range retrieval control 1.2.840.113556.1.4.802 !
 
public class range	{
	public static void main (String[] args)	{
	
		Hashtable env = new Hashtable();
		String adminName = "CN=Administrator,CN=Users,DC=ANTIPODES,DC=COM";
		String adminPassword = "XXXXXX";
		
		env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
		//set security credentials, note using simple cleartext authentication
		env.put(Context.SECURITY_AUTHENTICATION,"simple");
		env.put(Context.SECURITY_PRINCIPAL,adminName);
		env.put(Context.SECURITY_CREDENTIALS,adminPassword);
				
		//connect to my domain controller
		env.put(Context.PROVIDER_URL,"ldap://mydc.antipodes.com:389");
		
		try {
 
			// Create the initial directory context
			LdapContext ctx = new InitialLdapContext(env,null);
		
			// Create the search controls 		
			SearchControls searchCtls = new SearchControls();
		
			//Specify the search scope
			searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
 
			//specify the LDAP search filter
			String searchFilter = "(&(objectClass=group)(CN=All Research))";
 
			//Specify the Base for the search
			String searchBase = "DC=antipodes,DC=com";
 
			//initialize counter to total the group members and range values
			int totalResults = 0;
			int Start = 0;
			int Step = 10;
			int Finish = 9;
			boolean Finished = false;
			String Range;
 
			//loop through the query until we have all the results
			while (!Finished) {		
			
				//Specify the attributes to return
				Range = Start + "-" + Finish;
				String returnedAtts[]={"member;Range=" + Range};
				searchCtls.setReturningAttributes(returnedAtts);
		
				//Search for objects using the filter
				NamingEnumeration answer = ctx.search(searchBase, searchFilter, searchCtls);
 
				//Loop through the search results
				while (answer.hasMoreElements()) {
					SearchResult sr = (SearchResult)answer.next();
 
					System.out.println(">>>" + sr.getName());
 
					//Print out the members
 
					Attributes attrs = sr.getAttributes();
					if (attrs != null) {
 
						try {
							for (NamingEnumeration ae = attrs.getAll();ae.hasMore();) {
								Attribute attr = (Attribute)ae.next();
 
								//check if we are finished
								if (attr.getID().endsWith("*")) {
									Finished=true;
								}
 
								System.out.println("Attribute: " + attr.getID());
								for (NamingEnumeration e = attr.getAll();e.hasMore();totalResults++) {
 
									System.out.println("   " + totalResults + ". " + e.next());
								}
							}
 
						}	 
						catch (NamingException e)	{
							System.err.println("Problem printing attributes: " + e);
						}
				
						Start = Start + Step;
						Finish = Finish + Step;
 
					}
				}
			}
 
 			System.out.println("Total members: " + totalResults);
			ctx.close();
 
		} 
		catch (NamingException e) {
			System.err.println("Problem searching directory: " + e);
		}
	}
}
 
 
pefe
Posts:7
Registered: 1/27/05
Re: JNDI, Active Directory, Paging and Range Retrieval   
Feb 9, 2005 1:40 PM (reply 1 of 17)  (In reply to original post )

 
Thanks for the code samples. When I use your code (class paged) then I receive the following error:
Paged Search failed.javax.naming.OperationNotSupportedException: [LDAP: error code 12 - critical extension is not recognized]; remaining name 'ou=People,dc=umich,dc=edu'

What can I do if the server dosn't support the operation? Is there an other possibility?

Thanks,
Peter
 
carlosortiz@covad.net
Posts:1
Registered: 3/8/05
Re: JNDI, Active Directory, Paging and Range Retrieval   
Mar 8, 2005 1:12 PM (reply 2 of 17)  (In reply to #1 )

 
In the above code sample, what ldap server is being used. We tried this on Sun One Directory 5.2 and it did not work.
 
adler_steven
Posts:669
Registered: 12/9/04
Re: JNDI, Active Directory, Paging and Range Retrieval   
Mar 8, 2005 11:31 PM (reply 3 of 17)  (In reply to #2 )

 
Wow, It seems like a software company regularly criticised as a marketing company has failed in creating brand recognition.

I would have thought that the title of this post would give a subtle hint as to what directory was being used: Active Directory which is the directory included in Windows 2000 Server, Windows Server 2003.
 
KalicutR
Posts:1
Registered: 3/10/05
Re: JNDI, Active Directory, Paging and Range Retrieval   
Mar 10, 2005 12:59 PM (reply 4 of 17)  (In reply to #3 )

 
I need to get the first 10 sorted users of a group. I can get the first 10 using RANGE option. Is there a way to sort users and then return the first 10?

Thanks
 
adler_steven
Posts:669
Registered: 12/9/04
Re: JNDI, Active Directory, Paging and Range Retrieval   
Mar 11, 2005 10:47 AM (reply 5 of 17)  (In reply to #4 )

 
To the best of my knowledge, unfortunately not.
The member attribute is a multi-valued attribute and I don't belive that Active Directory stores these values in any particular sort order and hence can't guarantee the order in which they are returned to a user application.

A potential work around, which may also have its own pitfalls is to search for
users, who are members of a particular group. Your ldap query would be something like:
(&(objectClass=user)(memberOf=cn=Scientists,ou=Research,dc=antipodes,dc=com))
In this case, you should be able to specify the sort order on the user names.
Note that the syntax of the group name (memberOf attribute) is the full distinguished name.

One of the pitfalls would be if the groups contains members of other domains. The results of an ldap query may appear incomplete. You may want to refer to a previous posting describing the Global Catalog and LDAP referrals.
 
carlosortiz
Posts:2
Registered: 1/5/05
Re: JNDI, Active Directory, Paging and Range Retrieval   
Mar 16, 2005 2:49 PM (reply 6 of 17)  (In reply to #5 )

 
Does anyone know if their is another ldap server besides active directory that fully supports paging range?

We're probably going to use the Active Directory Application Mode. The export/import is problematic, adding to the schema is confusing and it's tightly coupled to the silly operating system. I definitely feels like i'm tying myself adam. Openldap is way easier to use it just doesn't support the sort,paging, and range stuff.

I think it's sad that Sun One directory doesn't support it and you have to pay for that server.
 
adler_steven
Posts:669
Registered: 12/9/04
Re: JNDI, Active Directory, Paging and Range Retrieval   
Mar 17, 2005 8:37 AM (reply 7 of 17)  (In reply to #6 )

 
I would be interested in understanding on why you find it difficult to extend the schema and why it is difficult to import and export.

For import/export you could quite easily use LDAP Data Interchange Format (LDIF) to perform bulk imports and exports to and from ADAM.

Similary to extend the schema, you could use LDIF import files, the Windows Server 2003 tools (ADSIEdit) or programmatically (for example a Java/JNDI app).

You can find further information at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ad/ad/extending_the_schema.asp

Or if I had some time, I could demonstrate how the extend the schema using Java/JNDI. It ain't rocket science.
 
nene_ol
Posts:1
Registered: 4/19/05
Re: JNDI, Active Directory, Paging and Range Retrieval   
Apr 19, 2005 7:15 AM (reply 8 of 17)  (In reply to original post )

 
Hi and thanks to all your precious information about JNDI uses.
I only want to know if I can acceed to Active Directory objects (only 'users' and 'groups') through JNDI without having an administrative account. I only want to read these objects, not to modify them.
Thanks again, your tips have already helped me
 
dpmaffsys
Posts:2
Registered: 12/07/05
Re: JNDI, Active Directory, Paging and Range Retrieval   
Jul 12, 2005 8:41 AM (reply 9 of 17)  (In reply to original post )

 
I have found your example code to be very useful. However, I am puzzled
by some aspects of the member range example, particularly the loop
control.

There are two things that seem odd.

First the setting of "Finished" to true. If the "while (answer.hasmore)"
does not get entered or the "if (attrs != null)" is never true how will the
nested loops get to complete?
Perhaps there needs to be some additional termination conditions?

Second the increment for "Start" and "Finish" seem to be at the wrong level.
As with "Finished", if either of the above conditions are not true "Start" and
"Finish" will not get incremented. Alternatively, if "while (answer.hasmore)"
is entered more than once they will be incremented too frequently.

Perhaps I am missing something here!

Many thanks
 
adler_steven
Posts:669
Registered: 12/9/04
Re: JNDI, Active Directory, Paging and Range Retrieval   
Jul 12, 2005 11:43 AM (reply 10 of 17)  (In reply to #9 )

 
The only thing you're missing is that I've never admitted to being a Java programmer !

The reason for selecting the sub-optimal range (in this case 10) is just to simply show how range retrieval works.

Believe it or not, the nested loops do actually get to complete. However in response to your question and deciding to check my sanity I discovered two serious flaws with my sample:
1. What happens if the group is not found ?
2. What happens if the group does not contain any members ?

I guess you could say it was a design rather than coding flaw, because I wasn't really interested in those two boundary cases when I wrote the sample (That's my excuse and I'm sticking to it !).

To solve those two problems replace
while (answer.hasMoreElements())
with
while (answer.hasMore())

and catch the PartialResultException and handle appropriately.
 
dashscreen
Posts:2
Registered: 10/26/05
Re: JNDI, Active Directory, Paging and Range Retrieval   
Oct 26, 2005 11:21 PM (reply 11 of 17)  (In reply to #1 )

 
Thanks for the code samples. When I use your code (class paged) then I receive the following error:
Paged Search failed.javax.naming.OperationNotSupportedException: [LDAP: error code 12 - critical extension is not recognized]; remaining name 'ou=People,dc=umich,dc=edu'

What can I do if the server dosn't support the operation? Is there an other possibility?

Thanks,
Peter
 
adler_steven
Posts:669
Registered: 12/9/04
Re: JNDI, Active Directory, Paging and Range Retrieval   
Oct 27, 2005 3:30 AM (reply 12 of 17)  (In reply to #11 )

 
Are you using Active Directory ?

Both Win2K & Win2K3 support LDAP page results.
 
qassim
Posts:1
Registered: 2/16/07
Re: JNDI, Active Directory, Paging and Range Retrieval   
Feb 16, 2007 4:18 PM (reply 13 of 17)  (In reply to original post )

 
Hi,
I want to use this code. I am using JDK 1.4.
How can I use PagedResultsControl. This class does not exist in javax.naming.ldap package. How can I access com.sun.jndi.ldap.ctl.* package. Do I need to include any Jar file?

Thank you very much for any help in this regards.
mqassim
 
adler_steven
Posts:669
Registered: 12/9/04
Re: JNDI, Active Directory, Paging and Range Retrieval   
Feb 17, 2007 11:57 AM (reply 14 of 17)  (In reply to #13 )

 
Hmmm...
Maybe I'm getting a bit ahead of myself these days (playing with Windows Server "Longhorn", .NET Framework 3.0, Windows Communications Foundation and trying to get Java 6 working against them).

IIRC, a number of controls such as the Paged Results control were included in Sun's ldap booster pack (ldapbp.jar).

Good luck.
 
This topic has 17 replies on 2 pages.    1 | 2 | Next »
Back to Forum
 
Read the Developer Forums Code of Conduct

Click to email this message Email this Topic

Edit this Topic
  
 
 
Forums Statistics
    Users Online : 28
  • Guests : 133

About Sun forums
  • Oracle Forums is a large collection of user generated discussions. It is here to help you ask questions, find answers, and participate in discussions.

    Check out our guide on Getting started with Oracle Forums for a full walkthrough of how to best leverage the benefits of this community.

Powered by Jive Forums