Searching is the most important operation in LDAP. It has to be fast, very fast. On the other hand, as the server does the processing while looking for entries, the client must provide information to get accurate results.
The idea is to define a search API which is easy to use in the simplest cases, but provides all the capability to send complex search requests.
Let‘s first look at a simple search. To process a search we need a starting point in the tree, a filter, and a scope. Here’s an example:
:::java @Test public void testSearch() throws Exception { EntryCursor cursor = connection.search( "ou=system", "(objectclass=*)", SearchScope.ONELEVEL ); for ( Entry entry : cursor ) { assertNotNull( entry ); System.out.println( entry ); } cursor.close(); }
In this example, the connection has been previously created. We search for all entries starting at ou=system along with its children, which have an ObjectClass attribute (all the entries have such an attribute, so we should get back all the entries). The scope (ONELEVEL) searches one level under the starting base.
A cursor of entries is returned, which can be iterated over. Every call to the getEntry() method returns the next entry in the LDAP result set.
That's pretty much it!
But this is not really enough, there are many possible options.
Note Don't forget to close the cursor, otherwise the associated data remains in memory forever (causing a memory leak)! Best practice is to use try-with-resources statement.
In the previous sample, we used a String to define the starting point of the search. Sometimes it's convenient to start a search with a DN (i.e. because you got the DN from another operation). In this case, no need to transform the DN into a String before doing your search, simply use the DN!
:::java @Test public void testSearchWithDn() throws Exception { Dn systemDn = new Dn( "ou=system" ); EntryCursor cursor = connection.search( systemDn, "(objectclass=*)", SearchScope.ONELEVEL ); for ( Entry entry : cursor ) { assertNotNull( entry ); System.out.println( entry ); } cursor.close(); }
This is it!
There are three different different scopes you can use to search for data:
LdapConnection.lookup
if the filter is irrelevent.The filter is used to define the elements that are targeted. There are various possibilities to construct a filter, using one or more connectors, and one or more expression nodes.
Connectors use a prefix notation, followed by as many expression nodes as necessary, like in (& (node1) (node2) ... (nodeN))
Expression nodes are always contained within parenthesis, with a left part - the attributeType, and a right part - the value -.
Here is the list of possible connectors:
And here is the list of possible expression nodes, assuming that an expression node:
=
Equality expression node : the selected entry matches the right part=*
Presence expression node : the entry has the Attribute on the left side>=
Superior expression node : the entry should be superior to the right part<=
Inferior expression node : the entry should be superior to the right part=[start][*][middle][*][final]
Substring expression node : the entry should match a substringNote: As of Apache DS 2.0, we don't support approx matches nor extensible matches.
Sometimes, you want to have more control over the search operation, either with the parameters in use, or the results that are returned.
A search can return other things than entries. In fact, you can get four different kinds of responses:
You may also add a Control to the search request, or request some specific AttributeType to be returned. The parameters that may be injected into a SearchRequest are as follows:
In both cases, you should fill the SearchRequest message and send it to the server:
:::Java @Test public void testSearchWithSearchRequest() throws Exception { // Create the SearchRequest object SearchRequest req = new SearchRequestImpl(); req.setScope( SearchScope.SUBTREE ); req.addAttributes( "*" ); req.setTimeLimit( 0 ); req.setBase( new Dn( "ou=system" ) ); req.setFilter( "(cn=user1)" ); // Process the request SearchCursor searchCursor = connection.search( req ); while ( searchCursor.next() ) { Response response = searchCursor.get(); // process the SearchResultEntry if ( response instanceof SearchResultEntry ) { Entry resultEntry = ( ( SearchResultEntry ) response ).getEntry(); System.out.println( resultEntry ); } } }
As we can see, the response is different, we are returned a SearchCursor instance, and we must verify the response type before being able to process it.