All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.examples.LDAPSearch Maven / Gradle / Ivy

/*
 * Copyright 2008-2017 UnboundID Corp.
 * All Rights Reserved.
 */
/*
 * Copyright (C) 2008-2017 UnboundID Corp.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.examples;



import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;

import com.unboundid.ldap.sdk.Control;
import com.unboundid.ldap.sdk.DereferencePolicy;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchResultListener;
import com.unboundid.ldap.sdk.SearchResultReference;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.Version;
import com.unboundid.util.Debug;
import com.unboundid.util.LDAPCommandLineTool;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.WakeableSleeper;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import com.unboundid.util.args.BooleanArgument;
import com.unboundid.util.args.ControlArgument;
import com.unboundid.util.args.DNArgument;
import com.unboundid.util.args.IntegerArgument;
import com.unboundid.util.args.ScopeArgument;



/**
 * This class provides a simple tool that can be used to search an LDAP
 * directory server.  Some of the APIs demonstrated by this example include:
 * 
    *
  • Argument Parsing (from the {@code com.unboundid.util.args} * package)
  • *
  • LDAP Command-Line Tool (from the {@code com.unboundid.util} * package)
  • *
  • LDAP Communication (from the {@code com.unboundid.ldap.sdk} * package)
  • *
*

* All of the necessary information is provided using * command line arguments. Supported arguments include those allowed by the * {@link LDAPCommandLineTool} class, as well as the following additional * arguments: *
    *
  • "-b {baseDN}" or "--baseDN {baseDN}" -- specifies the base DN to use * for the search. This must be provided.
  • *
  • "-s {scope}" or "--scope {scope}" -- specifies the scope to use for the * search. The scope value should be one of "base", "one", "sub", or * "subord". If this isn't specified, then a scope of "sub" will be * used.
  • *
  • "-R" or "--followReferrals" -- indicates that the tool should follow * any referrals encountered while searching.
  • *
  • "-t" or "--terse" -- indicates that the tool should generate minimal * output beyond the search results.
  • *
  • "-i {millis}" or "--repeatIntervalMillis {millis}" -- indicates that * the search should be periodically repeated with the specified delay * (in milliseconds) between requests.
  • *
  • "-n {count}" or "--numSearches {count}" -- specifies the total number * of times that the search should be performed. This may only be used in * conjunction with the "--repeatIntervalMillis" argument. If * "--repeatIntervalMillis" is used without "--numSearches", then the * searches will continue to be repeated until the tool is * interrupted.
  • *
  • "--bindControl {control}" -- specifies a control that should be * included in the bind request sent by this tool before performing any * search operations.
  • *
  • "-J {control}" or "--control {control}" -- specifies a control that * should be included in the search request(s) sent by this tool.
  • *
* In addition, after the above named arguments are provided, a set of one or * more unnamed trailing arguments must be given. The first argument should be * the string representation of the filter to use for the search. If there are * any additional trailing arguments, then they will be interpreted as the * attributes to return in matching entries. If no attribute names are given, * then the server should return all user attributes in matching entries. *

* Note that this class implements the SearchResultListener interface, which * will be notified whenever a search result entry or reference is returned from * the server. Whenever an entry is received, it will simply be printed * displayed in LDIF. */ @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) public final class LDAPSearch extends LDAPCommandLineTool implements SearchResultListener { /** * The date formatter that should be used when writing timestamps. */ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss.SSS"); /** * The serial version UID for this serializable class. */ private static final long serialVersionUID = 7465188734621412477L; // The argument parser used by this program. private ArgumentParser parser; // Indicates whether the search should be repeated. private boolean repeat; // The argument used to indicate whether to follow referrals. private BooleanArgument followReferrals; // The argument used to indicate whether to use terse mode. private BooleanArgument terseMode; // The argument used to specify any bind controls that should be used. private ControlArgument bindControls; // The argument used to specify any search controls that should be used. private ControlArgument searchControls; // The number of times to perform the search. private IntegerArgument numSearches; // The interval in milliseconds between repeated searches. private IntegerArgument repeatIntervalMillis; // The argument used to specify the base DN for the search. private DNArgument baseDN; // The argument used to specify the scope for the search. private ScopeArgument scopeArg; /** * Parse the provided command line arguments and make the appropriate set of * changes. * * @param args The command line arguments provided to this program. */ public static void main(final String[] args) { final ResultCode resultCode = main(args, System.out, System.err); if (resultCode != ResultCode.SUCCESS) { System.exit(resultCode.intValue()); } } /** * Parse the provided command line arguments and make the appropriate set of * changes. * * @param args The command line arguments provided to this program. * @param outStream The output stream to which standard out should be * written. It may be {@code null} if output should be * suppressed. * @param errStream The output stream to which standard error should be * written. It may be {@code null} if error messages * should be suppressed. * * @return A result code indicating whether the processing was successful. */ public static ResultCode main(final String[] args, final OutputStream outStream, final OutputStream errStream) { final LDAPSearch ldapSearch = new LDAPSearch(outStream, errStream); return ldapSearch.runTool(args); } /** * Creates a new instance of this tool. * * @param outStream The output stream to which standard out should be * written. It may be {@code null} if output should be * suppressed. * @param errStream The output stream to which standard error should be * written. It may be {@code null} if error messages * should be suppressed. */ public LDAPSearch(final OutputStream outStream, final OutputStream errStream) { super(outStream, errStream); } /** * Retrieves the name for this tool. * * @return The name for this tool. */ @Override() public String getToolName() { return "ldapsearch"; } /** * Retrieves the description for this tool. * * @return The description for this tool. */ @Override() public String getToolDescription() { return "Search an LDAP directory server."; } /** * Retrieves the version string for this tool. * * @return The version string for this tool. */ @Override() public String getToolVersion() { return Version.NUMERIC_VERSION_STRING; } /** * Retrieves the minimum number of unnamed trailing arguments that are * required. * * @return One, to indicate that at least one trailing argument (representing * the search filter) must be provided. */ @Override() public int getMinTrailingArguments() { return 1; } /** * Retrieves the maximum number of unnamed trailing arguments that are * allowed. * * @return A negative value to indicate that any number of trailing arguments * may be provided. */ @Override() public int getMaxTrailingArguments() { return -1; } /** * Retrieves a placeholder string that may be used to indicate what kinds of * trailing arguments are allowed. * * @return A placeholder string that may be used to indicate what kinds of * trailing arguments are allowed. */ @Override() public String getTrailingArgumentsPlaceholder() { return "{filter} [attr1 [attr2 [...]]]"; } /** * Indicates whether this tool should provide support for an interactive mode, * in which the tool offers a mode in which the arguments can be provided in * a text-driven menu rather than requiring them to be given on the command * line. If interactive mode is supported, it may be invoked using the * "--interactive" argument. Alternately, if interactive mode is supported * and {@link #defaultsToInteractiveMode()} returns {@code true}, then * interactive mode may be invoked by simply launching the tool without any * arguments. * * @return {@code true} if this tool supports interactive mode, or * {@code false} if not. */ @Override() public boolean supportsInteractiveMode() { return true; } /** * Indicates whether this tool defaults to launching in interactive mode if * the tool is invoked without any command-line arguments. This will only be * used if {@link #supportsInteractiveMode()} returns {@code true}. * * @return {@code true} if this tool defaults to using interactive mode if * launched without any command-line arguments, or {@code false} if * not. */ @Override() public boolean defaultsToInteractiveMode() { return true; } /** * Indicates whether this tool should provide arguments for redirecting output * to a file. If this method returns {@code true}, then the tool will offer * an "--outputFile" argument that will specify the path to a file to which * all standard output and standard error content will be written, and it will * also offer a "--teeToStandardOut" argument that can only be used if the * "--outputFile" argument is present and will cause all output to be written * to both the specified output file and to standard output. * * @return {@code true} if this tool should provide arguments for redirecting * output to a file, or {@code false} if not. */ @Override() protected boolean supportsOutputFile() { return true; } /** * Indicates whether this tool supports the use of a properties file for * specifying default values for arguments that aren't specified on the * command line. * * @return {@code true} if this tool supports the use of a properties file * for specifying default values for arguments that aren't specified * on the command line, or {@code false} if not. */ @Override() public boolean supportsPropertiesFile() { return true; } /** * Indicates whether this tool should default to interactively prompting for * the bind password if a password is required but no argument was provided * to indicate how to get the password. * * @return {@code true} if this tool should default to interactively * prompting for the bind password, or {@code false} if not. */ @Override() protected boolean defaultToPromptForBindPassword() { return true; } /** * Indicates whether the LDAP-specific arguments should include alternate * versions of all long identifiers that consist of multiple words so that * they are available in both camelCase and dash-separated versions. * * @return {@code true} if this tool should provide multiple versions of * long identifiers for LDAP-specific arguments, or {@code false} if * not. */ @Override() protected boolean includeAlternateLongIdentifiers() { return true; } /** * Adds the arguments used by this program that aren't already provided by the * generic {@code LDAPCommandLineTool} framework. * * @param parser The argument parser to which the arguments should be added. * * @throws ArgumentException If a problem occurs while adding the arguments. */ @Override() public void addNonLDAPArguments(final ArgumentParser parser) throws ArgumentException { this.parser = parser; String description = "The base DN to use for the search. This must be " + "provided."; baseDN = new DNArgument('b', "baseDN", true, 1, "{dn}", description); baseDN.addLongIdentifier("base-dn"); parser.addArgument(baseDN); description = "The scope to use for the search. It should be 'base', " + "'one', 'sub', or 'subord'. If this is not provided, then " + "a default scope of 'sub' will be used."; scopeArg = new ScopeArgument('s', "scope", false, "{scope}", description, SearchScope.SUB); parser.addArgument(scopeArg); description = "Follow any referrals encountered during processing."; followReferrals = new BooleanArgument('R', "followReferrals", description); followReferrals.addLongIdentifier("follow-referrals"); parser.addArgument(followReferrals); description = "Information about a control to include in the bind request."; bindControls = new ControlArgument(null, "bindControl", false, 0, null, description); bindControls.addLongIdentifier("bind-control"); parser.addArgument(bindControls); description = "Information about a control to include in search requests."; searchControls = new ControlArgument('J', "control", false, 0, null, description); parser.addArgument(searchControls); description = "Generate terse output with minimal additional information."; terseMode = new BooleanArgument('t', "terse", description); parser.addArgument(terseMode); description = "Specifies the length of time in milliseconds to sleep " + "before repeating the same search. If this is not " + "provided, then the search will only be performed once."; repeatIntervalMillis = new IntegerArgument('i', "repeatIntervalMillis", false, 1, "{millis}", description, 0, Integer.MAX_VALUE); repeatIntervalMillis.addLongIdentifier("repeat-interval-millis"); parser.addArgument(repeatIntervalMillis); description = "Specifies the number of times that the search should be " + "performed. If this argument is present, then the " + "--repeatIntervalMillis argument must also be provided to " + "specify the length of time between searches. If " + "--repeatIntervalMillis is used without --numSearches, " + "then the search will be repeated until the tool is " + "interrupted."; numSearches = new IntegerArgument('n', "numSearches", false, 1, "{count}", description, 1, Integer.MAX_VALUE); numSearches.addLongIdentifier("num-searches"); parser.addArgument(numSearches); parser.addDependentArgumentSet(numSearches, repeatIntervalMillis); } /** * {@inheritDoc} */ @Override() public void doExtendedNonLDAPArgumentValidation() throws ArgumentException { // There must have been at least one trailing argument provided, and it must // be parsable as a valid search filter. if (parser.getTrailingArguments().isEmpty()) { throw new ArgumentException("At least one trailing argument must be " + "provided to specify the search filter. Additional trailing " + "arguments are allowed to specify the attributes to return in " + "search result entries."); } try { Filter.create(parser.getTrailingArguments().get(0)); } catch (final Exception e) { Debug.debugException(e); throw new ArgumentException( "The first trailing argument value could not be parsed as a valid " + "LDAP search filter.", e); } } /** * {@inheritDoc} */ @Override() protected List getBindControls() { return bindControls.getValues(); } /** * Performs the actual processing for this tool. In this case, it gets a * connection to the directory server and uses it to perform the requested * search. * * @return The result code for the processing that was performed. */ @Override() public ResultCode doToolProcessing() { // Make sure that at least one trailing argument was provided, which will be // the filter. If there were any other arguments, then they will be the // attributes to return. final List trailingArguments = parser.getTrailingArguments(); if (trailingArguments.isEmpty()) { err("No search filter was provided."); err(); err(parser.getUsageString(StaticUtils.TERMINAL_WIDTH_COLUMNS - 1)); return ResultCode.PARAM_ERROR; } final Filter filter; try { filter = Filter.create(trailingArguments.get(0)); } catch (LDAPException le) { err("Invalid search filter: ", le.getMessage()); return le.getResultCode(); } final String[] attributesToReturn; if (trailingArguments.size() > 1) { attributesToReturn = new String[trailingArguments.size() - 1]; for (int i=1; i < trailingArguments.size(); i++) { attributesToReturn[i-1] = trailingArguments.get(i); } } else { attributesToReturn = StaticUtils.NO_STRINGS; } // Get the connection to the directory server. final LDAPConnection connection; try { connection = getConnection(); if (! terseMode.isPresent()) { out("# Connected to ", connection.getConnectedAddress(), ':', connection.getConnectedPort()); } } catch (LDAPException le) { err("Error connecting to the directory server: ", le.getMessage()); return le.getResultCode(); } // Create a search request with the appropriate information and process it // in the server. Note that in this case, we're creating a search result // listener to handle the results since there could potentially be a lot of // them. final SearchRequest searchRequest = new SearchRequest(this, baseDN.getStringValue(), scopeArg.getValue(), DereferencePolicy.NEVER, 0, 0, false, filter, attributesToReturn); searchRequest.setFollowReferrals(followReferrals.isPresent()); final List controlList = searchControls.getValues(); if (controlList != null) { searchRequest.setControls(controlList); } final boolean infinite; final int numIterations; if (repeatIntervalMillis.isPresent()) { repeat = true; if (numSearches.isPresent()) { infinite = false; numIterations = numSearches.getValue(); } else { infinite = true; numIterations = Integer.MAX_VALUE; } } else { infinite = false; repeat = false; numIterations = 1; } ResultCode resultCode = ResultCode.SUCCESS; long lastSearchTime = System.currentTimeMillis(); final WakeableSleeper sleeper = new WakeableSleeper(); for (int i=0; (infinite || (i < numIterations)); i++) { if (repeat && (i > 0)) { final long sleepTime = (lastSearchTime + repeatIntervalMillis.getValue()) - System.currentTimeMillis(); if (sleepTime > 0) { sleeper.sleep(sleepTime); } lastSearchTime = System.currentTimeMillis(); } try { final SearchResult searchResult = connection.search(searchRequest); if ((! repeat) && (! terseMode.isPresent())) { out("# The search operation was processed successfully."); out("# Entries returned: ", searchResult.getEntryCount()); out("# References returned: ", searchResult.getReferenceCount()); } } catch (LDAPException le) { err("An error occurred while processing the search: ", le.getMessage()); err("Result Code: ", le.getResultCode().intValue(), " (", le.getResultCode().getName(), ')'); if (le.getMatchedDN() != null) { err("Matched DN: ", le.getMatchedDN()); } if (le.getReferralURLs() != null) { for (final String url : le.getReferralURLs()) { err("Referral URL: ", url); } } if (resultCode == ResultCode.SUCCESS) { resultCode = le.getResultCode(); } if (! le.getResultCode().isConnectionUsable()) { break; } } } // Close the connection to the directory server and exit. connection.close(); if (! terseMode.isPresent()) { out(); out("# Disconnected from the server"); } return resultCode; } /** * Indicates that the provided search result entry was returned from the * associated search operation. * * @param entry The entry that was returned from the search. */ public void searchEntryReturned(final SearchResultEntry entry) { if (repeat) { out("# ", DATE_FORMAT.format(new Date())); } out(entry.toLDIFString()); } /** * Indicates that the provided search result reference was returned from the * associated search operation. * * @param reference The reference that was returned from the search. */ public void searchReferenceReturned(final SearchResultReference reference) { if (repeat) { out("# ", DATE_FORMAT.format(new Date())); } out(reference.toString()); } /** * {@inheritDoc} */ @Override() public LinkedHashMap getExampleUsages() { final LinkedHashMap examples = new LinkedHashMap(); final String[] args = { "--hostname", "server.example.com", "--port", "389", "--bindDN", "uid=admin,dc=example,dc=com", "--bindPassword", "password", "--baseDN", "dc=example,dc=com", "--scope", "sub", "(uid=jdoe)", "givenName", "sn", "mail" }; final String description = "Perform a search in the directory server to find all entries " + "matching the filter '(uid=jdoe)' anywhere below " + "'dc=example,dc=com'. Include only the givenName, sn, and mail " + "attributes in the entries that are returned."; examples.put(args, description); return examples; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy