Comprehensive Java LDAP SDK providing full LDAPv3 protocol support, connection pooling, schema handling, and persistence framework for LDAP directory operations.
—
Advanced search capabilities with filter construction, result processing, search controls, and result handling for LDAP directory queries.
Comprehensive search request construction with filters, controls, and result processing options.
/**
* Request for LDAP search operations with full configuration
*/
public class SearchRequest extends UpdatableLDAPRequest {
// Constructors
public SearchRequest(String baseDN, SearchScope scope, String filter);
public SearchRequest(String baseDN, SearchScope scope, String filter, String... attributes);
public SearchRequest(String baseDN, SearchScope scope, Filter filter);
public SearchRequest(String baseDN, SearchScope scope, Filter filter, String... attributes);
public SearchRequest(SearchResultListener searchResultListener, String baseDN, SearchScope scope, String filter, String... attributes);
// Configuration
public String getBaseDN();
public void setBaseDN(String baseDN);
public SearchScope getScope();
public void setScope(SearchScope scope);
public Filter getFilter();
public void setFilter(Filter filter);
public void setFilter(String filter) throws LDAPException;
public String[] getAttributes();
public void setAttributes(String... attributes);
// Search limits
public int getSizeLimit();
public void setSizeLimit(int sizeLimit);
public int getTimeLimitSeconds();
public void setTimeLimit(int timeLimitSeconds);
public boolean typesOnly();
public void setTypesOnly(boolean typesOnly);
// Alias handling
public DereferencePolicy getDerefPolicy();
public void setDerefPolicy(DereferencePolicy derefPolicy);
// Result handling
public SearchResultListener getSearchResultListener();
public void setSearchResultListener(SearchResultListener searchResultListener);
}
/**
* Search scope enumeration
*/
public enum SearchScope {
BASE(0, "base"),
ONE(1, "one"),
SUB(2, "sub"),
SUBORDINATE_SUBTREE(3, "subordinateSubtree");
public int intValue();
public String getName();
public static SearchScope valueOf(int intValue);
public static SearchScope definedValueOf(String name);
}
/**
* Alias dereferencing policy
*/
public enum DereferencePolicy {
NEVER(0, "never"),
IN_SEARCHING(1, "inSearching"),
FINDING_BASE_OBJECT(2, "findingBaseObj"),
ALWAYS(3, "always");
public int intValue();
public String getName();
}Complete search result with entries, references, and metadata.
/**
* Result of a search operation containing entries and metadata
*/
public class SearchResult extends LDAPResult {
public List<SearchResultEntry> getSearchEntries();
public List<SearchResultReference> getSearchReferences();
public int getEntryCount();
public int getReferenceCount();
// Result limits
public boolean sizeLimitExceeded();
public boolean timeLimitExceeded();
// Search-specific result handling
public SearchResultEntry getSearchEntry(String dn);
public boolean entryReturned(String dn);
}
/**
* Individual entry from search results
*/
public class SearchResultEntry extends Entry {
public SearchResultEntry(String dn, Attribute... attributes);
public SearchResultEntry(String dn, Collection<Attribute> attributes, Control... controls);
public Control[] getControls();
public boolean hasControl(String oid);
public <T extends Control> T getControl(Class<T> controlClass);
}
/**
* Search result reference (referral)
*/
public class SearchResultReference {
public SearchResultReference(String[] referralURLs, Control... controls);
public String[] getReferralURLs();
public Control[] getControls();
public boolean hasControl(String oid);
}Interface for processing search results as they arrive.
/**
* Interface for processing search results as they arrive
*/
public interface SearchResultListener extends Serializable {
/**
* Called when a search result entry is returned
* @param searchEntry The search result entry
*/
void searchEntryReturned(SearchResultEntry searchEntry);
/**
* Called when a search result reference is returned
* @param searchReference The search result reference
*/
void searchReferenceReturned(SearchResultReference searchReference);
}
/**
* Convenience implementation that collects results in lists
*/
public class LDAPEntrySource implements EntrySource, Closeable {
public LDAPEntrySource(LDAPInterface connection, String baseDN, SearchScope scope, String filter, String... attributes);
public LDAPEntrySource(LDAPInterface connection, SearchRequest searchRequest);
public Entry nextEntry() throws EntrySourceException;
public void close();
}/**
* Asynchronous search operations
* @param searchRequest The search request
* @param resultListener Listener for handling results
* @return Request ID for tracking
* @throws LDAPException if the operation fails
*/
public AsyncRequestID asyncSearch(SearchRequest searchRequest, AsyncSearchResultListener resultListener) throws LDAPException;
/**
* Cancel an asynchronous operation
* @param asyncRequestID The request ID to cancel
* @throws LDAPException if cancellation fails
*/
public void abandon(AsyncRequestID asyncRequestID) throws LDAPException;
/**
* Listener interface for asynchronous search results
*/
public interface AsyncSearchResultListener extends AsyncResultListener {
void searchEntryReturned(SearchResultEntry entry);
void searchReferenceReturned(SearchResultReference reference);
void searchResultReceived(AsyncRequestID requestID, SearchResult result);
}/**
* Advanced filter construction and manipulation
*/
public class Filter implements Serializable {
// Comprehensive filter creation methods
public static Filter createEqualityFilter(String attributeName, String assertionValue);
public static Filter createEqualityFilter(String attributeName, byte[] assertionValue);
// Substring filters
public static Filter createSubstringFilter(String attributeName, String subInitial, String[] subAny, String subFinal);
public static Filter createSubInitialFilter(String attributeName, String subInitial);
public static Filter createSubAnyFilter(String attributeName, String... subAnyValues);
public static Filter createSubFinalFilter(String attributeName, String subFinal);
// Comparison filters
public static Filter createGreaterOrEqualFilter(String attributeName, String assertionValue);
public static Filter createLessOrEqualFilter(String attributeName, String assertionValue);
public static Filter createApproximateMatchFilter(String attributeName, String assertionValue);
// Presence and extensible match
public static Filter createPresenceFilter(String attributeName);
public static Filter createExtensibleMatchFilter(String attributeName, String matchingRuleID, boolean dnAttributes, String assertionValue);
// Logical combinations
public static Filter createANDFilter(Filter... filters);
public static Filter createANDFilter(Collection<Filter> filters);
public static Filter createORFilter(Filter... filters);
public static Filter createORFilter(Collection<Filter> filters);
public static Filter createNOTFilter(Filter filter);
// Filter analysis
public FilterType getFilterType();
public String getAttributeName();
public String getAssertionValue();
public byte[] getAssertionValueBytes();
public String getSubInitial();
public String[] getSubAny();
public String getSubFinal();
public Filter[] getComponents();
public Filter getNOTComponent();
public String getMatchingRuleID();
public boolean getDNAttributes();
// Filter transformation
public Filter toNormalizedFilter();
public String toNormalizedString();
public boolean matchesEntry(Entry entry) throws LDAPException;
public boolean matchesEntry(Entry entry, Schema schema) throws LDAPException;
}/**
* Control for retrieving search results in pages
*/
public class PagedResultsRequestControl extends Control {
public PagedResultsRequestControl(int pageSize);
public PagedResultsRequestControl(int pageSize, ASN1OctetString cookie);
public PagedResultsRequestControl(int pageSize, ASN1OctetString cookie, boolean isCritical);
public int getSize();
public ASN1OctetString getCookie();
}
/**
* Response control for paged results
*/
public class SimplePagedResultsControl extends Control {
public int getSize();
public ASN1OctetString getCookie();
public boolean moreResultsToReturn();
}/**
* Control for server-side sorting of search results
*/
public class ServerSideSortRequestControl extends Control {
public ServerSideSortRequestControl(SortKey... sortKeys);
public ServerSideSortRequestControl(List<SortKey> sortKeys);
public ServerSideSortRequestControl(boolean isCritical, SortKey... sortKeys);
public List<SortKey> getSortKeys();
}
/**
* Sort key specification for server-side sorting
*/
public class SortKey implements Serializable {
public SortKey(String attributeName);
public SortKey(String attributeName, boolean reverseOrder);
public SortKey(String attributeName, String matchingRuleID);
public SortKey(String attributeName, String matchingRuleID, boolean reverseOrder);
public String getAttributeName();
public String getMatchingRuleID();
public boolean reverseOrder();
}
/**
* Response control for server-side sort status
*/
public class ServerSideSortResponseControl extends Control {
public ResultCode getResultCode();
public String getAttributeName();
}/**
* Control for virtual list view (VLV) searches
*/
public class VirtualListViewRequestControl extends Control {
// Constructor for offset-based VLV
public VirtualListViewRequestControl(int targetOffset, int beforeCount, int afterCount, int contentCount);
public VirtualListViewRequestControl(int targetOffset, int beforeCount, int afterCount, int contentCount, ASN1OctetString contextID);
// Constructor for value-based VLV
public VirtualListViewRequestControl(String targetValue, int beforeCount, int afterCount);
public VirtualListViewRequestControl(byte[] targetValue, int beforeCount, int afterCount);
public int getTargetOffset();
public byte[] getTargetValue();
public int getBeforeCount();
public int getAfterCount();
public int getContentCount();
public ASN1OctetString getContextID();
}
/**
* Response control for VLV result information
*/
public class VirtualListViewResponseControl extends Control {
public int getTargetPosition();
public int getContentCount();
public ResultCode getResult();
public ASN1OctetString getContextID();
}/**
* Control for persistent search operations (change notifications)
*/
public class PersistentSearchRequestControl extends Control {
public PersistentSearchRequestControl();
public PersistentSearchRequestControl(Set<PersistentSearchChangeType> changeTypes, boolean changesOnly, boolean returnECs);
public PersistentSearchRequestControl(Set<PersistentSearchChangeType> changeTypes, boolean changesOnly, boolean returnECs, boolean isCritical);
public Set<PersistentSearchChangeType> getChangeTypes();
public boolean changesOnly();
public boolean returnECs();
}
/**
* Change types for persistent search
*/
public enum PersistentSearchChangeType {
ADD(1),
DELETE(2),
MODIFY(4),
MODIFY_DN(8);
public int intValue();
}
/**
* Entry change notification control (response)
*/
public class EntryChangeNotificationControl extends Control {
public PersistentSearchChangeType getChangeType();
public long getChangeNumber();
public String getPreviousDN();
}/**
* Interface for iterating through entries
*/
public interface EntrySource extends Closeable {
Entry nextEntry() throws EntrySourceException;
void close();
}
/**
* LDAP-backed entry source for search results
*/
public class LDAPEntrySource implements EntrySource {
public LDAPEntrySource(LDAPInterface connection, String baseDN, SearchScope scope, String filter, String... attributes);
public LDAPEntrySource(LDAPInterface connection, SearchRequest searchRequest);
public Entry nextEntry() throws EntrySourceException;
public long getEntriesRead();
public void close();
}
/**
* Exception for entry source operations
*/
public class EntrySourceException extends Exception {
public EntrySourceException(String message);
public EntrySourceException(String message, Throwable cause);
public boolean mayContinueReading();
}import com.unboundid.ldap.sdk.*;
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
try {
connection.bind("cn=admin,dc=example,dc=com", "password");
// Simple search
SearchResult result = connection.search(
"ou=people,dc=example,dc=com", // base DN
SearchScope.ONE, // scope
"(objectClass=inetOrgPerson)", // filter
"cn", "mail", "telephoneNumber" // attributes to return
);
System.out.println("Found " + result.getEntryCount() + " entries");
// Process results
for (SearchResultEntry entry : result.getSearchEntries()) {
System.out.println("DN: " + entry.getDN());
System.out.println("CN: " + entry.getAttributeValue("cn"));
System.out.println("Mail: " + entry.getAttributeValue("mail"));
// Handle multi-valued attributes
String[] phones = entry.getAttributeValues("telephoneNumber");
if (phones != null) {
System.out.println("Phones: " + Arrays.toString(phones));
}
System.out.println("---");
}
} finally {
connection.close();
}import com.unboundid.ldap.sdk.*;
import com.unboundid.ldap.sdk.controls.*;
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
try {
connection.bind("cn=admin,dc=example,dc=com", "password");
// Create complex filter
Filter complexFilter = Filter.createANDFilter(
Filter.createEqualityFilter("objectClass", "inetOrgPerson"),
Filter.createPresenceFilter("mail"),
Filter.createORFilter(
Filter.createSubstringFilter("cn", "John", null, null),
Filter.createSubstringFilter("cn", "Jane", null, null)
)
);
// Create search request with controls
SearchRequest searchRequest = new SearchRequest(
"dc=example,dc=com",
SearchScope.SUB,
complexFilter,
"cn", "mail", "telephoneNumber", "department"
);
// Add server-side sorting
SortKey[] sortKeys = {
new SortKey("department"),
new SortKey("cn")
};
searchRequest.addControl(new ServerSideSortRequestControl(sortKeys));
// Set limits
searchRequest.setSizeLimit(100);
searchRequest.setTimeLimit(30);
// Execute search
SearchResult result = connection.search(searchRequest);
// Check if results were truncated
if (result.getResultCode() == ResultCode.SIZE_LIMIT_EXCEEDED) {
System.out.println("Results truncated due to size limit");
}
// Process sorted results
for (SearchResultEntry entry : result.getSearchEntries()) {
System.out.println("Department: " + entry.getAttributeValue("department"));
System.out.println("Name: " + entry.getAttributeValue("cn"));
System.out.println("Mail: " + entry.getAttributeValue("mail"));
System.out.println("---");
}
} finally {
connection.close();
}import com.unboundid.ldap.sdk.*;
import com.unboundid.ldap.sdk.controls.*;
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
try {
connection.bind("cn=admin,dc=example,dc=com", "password");
// Initial search request
SearchRequest searchRequest = new SearchRequest(
"dc=example,dc=com",
SearchScope.SUB,
"(objectClass=inetOrgPerson)",
"cn", "mail"
);
int pageSize = 10;
ASN1OctetString cookie = null;
int totalEntries = 0;
do {
// Add paged results control
searchRequest.setControls(new PagedResultsRequestControl(pageSize, cookie));
// Execute search
SearchResult result = connection.search(searchRequest);
totalEntries += result.getEntryCount();
// Process current page
System.out.println("Processing page with " + result.getEntryCount() + " entries");
for (SearchResultEntry entry : result.getSearchEntries()) {
System.out.println(" " + entry.getAttributeValue("cn"));
}
// Get cookie for next page
SimplePagedResultsControl responseControl =
SimplePagedResultsControl.get(result);
if (responseControl != null) {
cookie = responseControl.getCookie();
}
} while ((cookie != null) && (cookie.getValueLength() > 0));
System.out.println("Total entries processed: " + totalEntries);
} finally {
connection.close();
}import com.unboundid.ldap.sdk.*;
import java.util.concurrent.CountDownLatch;
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
try {
connection.bind("cn=admin,dc=example,dc=com", "password");
final CountDownLatch latch = new CountDownLatch(1);
final List<SearchResultEntry> entries = new ArrayList<>();
SearchRequest searchRequest = new SearchRequest(
"dc=example,dc=com",
SearchScope.SUB,
"(objectClass=inetOrgPerson)",
"cn", "mail"
);
// Perform asynchronous search
AsyncRequestID requestID = connection.asyncSearch(
searchRequest,
new AsyncSearchResultListener() {
public void searchEntryReturned(SearchResultEntry entry) {
entries.add(entry);
System.out.println("Received entry: " + entry.getAttributeValue("cn"));
}
public void searchReferenceReturned(SearchResultReference reference) {
System.out.println("Received reference: " +
Arrays.toString(reference.getReferralURLs()));
}
public void searchResultReceived(AsyncRequestID requestID, SearchResult result) {
System.out.println("Search completed with result code: " +
result.getResultCode());
latch.countDown();
}
}
);
// Wait for completion
latch.await();
System.out.println("Total entries received: " + entries.size());
} finally {
connection.close();
}import com.unboundid.ldap.sdk.*;
import com.unboundid.ldap.sdk.controls.*;
import java.util.EnumSet;
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
try {
connection.bind("cn=admin,dc=example,dc=com", "password");
// Configure persistent search
Set<PersistentSearchChangeType> changeTypes = EnumSet.of(
PersistentSearchChangeType.ADD,
PersistentSearchChangeType.MODIFY,
PersistentSearchChangeType.DELETE,
PersistentSearchChangeType.MODIFY_DN
);
SearchRequest persistentRequest = new SearchRequest(
"ou=people,dc=example,dc=com",
SearchScope.SUB,
"(objectClass=inetOrgPerson)",
"cn", "mail", "telephoneNumber"
);
// Add persistent search control
persistentRequest.addControl(new PersistentSearchRequestControl(
changeTypes,
true, // changes only (don't return existing entries)
true // return entry change notification control
));
// Start persistent search
AsyncRequestID requestID = connection.asyncSearch(
persistentRequest,
new AsyncSearchResultListener() {
public void searchEntryReturned(SearchResultEntry entry) {
// Check for change notification control
EntryChangeNotificationControl changeControl =
entry.getControl(EntryChangeNotificationControl.class);
if (changeControl != null) {
System.out.println("Change detected:");
System.out.println(" Type: " + changeControl.getChangeType());
System.out.println(" DN: " + entry.getDN());
System.out.println(" Change Number: " + changeControl.getChangeNumber());
if (changeControl.getPreviousDN() != null) {
System.out.println(" Previous DN: " + changeControl.getPreviousDN());
}
}
}
public void searchReferenceReturned(SearchResultReference reference) {
// Handle referrals if needed
}
public void searchResultReceived(AsyncRequestID requestID, SearchResult result) {
System.out.println("Persistent search ended: " + result.getResultCode());
}
}
);
System.out.println("Persistent search started. Monitoring for changes...");
// Keep running until interrupted
Thread.sleep(60000); // Monitor for 1 minute
// Cancel persistent search
connection.abandon(requestID);
} finally {
connection.close();
}import com.unboundid.ldap.sdk.*;
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
try {
connection.bind("cn=admin,dc=example,dc=com", "password");
// Create entry source for memory-efficient processing
LDAPEntrySource entrySource = new LDAPEntrySource(
connection,
"dc=example,dc=com",
SearchScope.SUB,
"(objectClass=inetOrgPerson)",
"cn", "mail", "department"
);
try {
Entry entry;
int count = 0;
// Process entries one at a time
while ((entry = entrySource.nextEntry()) != null) {
count++;
System.out.println("Processing entry " + count + ": " +
entry.getAttributeValue("cn"));
// Process entry without loading all results into memory
String department = entry.getAttributeValue("department");
if ("Engineering".equals(department)) {
// Do something with engineering entries
System.out.println(" Engineering employee: " +
entry.getAttributeValue("mail"));
}
// Prevent memory issues with very large result sets
if (count % 1000 == 0) {
System.out.println("Processed " + count + " entries so far...");
}
}
System.out.println("Total entries processed: " + count);
} finally {
entrySource.close();
}
} finally {
connection.close();
}import com.unboundid.ldap.sdk.*;
// Build complex filters programmatically
Filter complexFilter = Filter.createANDFilter(
// Must be a person
Filter.createEqualityFilter("objectClass", "inetOrgPerson"),
// Must have an email address
Filter.createPresenceFilter("mail"),
// Either in Engineering or Marketing department
Filter.createORFilter(
Filter.createEqualityFilter("department", "Engineering"),
Filter.createEqualityFilter("department", "Marketing")
),
// Name starts with A-M (first half of alphabet)
Filter.createORFilter(
Filter.createSubstringFilter("cn", "A", null, null),
Filter.createSubstringFilter("cn", "B", null, null),
Filter.createSubstringFilter("cn", "C", null, null),
Filter.createSubstringFilter("cn", "D", null, null),
Filter.createSubstringFilter("cn", "E", null, null),
Filter.createSubstringFilter("cn", "F", null, null),
Filter.createSubstringFilter("cn", "G", null, null),
Filter.createSubstringFilter("cn", "H", null, null),
Filter.createSubstringFilter("cn", "I", null, null),
Filter.createSubstringFilter("cn", "J", null, null),
Filter.createSubstringFilter("cn", "K", null, null),
Filter.createSubstringFilter("cn", "L", null, null),
Filter.createSubstringFilter("cn", "M", null, null)
),
// Exclude disabled accounts
Filter.createNOTFilter(
Filter.createEqualityFilter("accountStatus", "disabled")
)
);
System.out.println("Complex filter: " + complexFilter.toString());
// Use the filter in a search
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
try {
connection.bind("cn=admin,dc=example,dc=com", "password");
SearchResult result = connection.search(
"dc=example,dc=com",
SearchScope.SUB,
complexFilter,
"cn", "mail", "department"
);
System.out.println("Found " + result.getEntryCount() + " matching entries");
} finally {
connection.close();
}Install with Tessl CLI
npx tessl i tessl/maven-com-unboundid--unboundid-ldapsdk