001 /*
002 * Copyright 2008-2015 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2015 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk.examples;
022
023
024
025 import java.io.OutputStream;
026 import java.io.Serializable;
027 import java.text.ParseException;
028 import java.util.LinkedHashMap;
029 import java.util.List;
030
031 import com.unboundid.ldap.sdk.CompareRequest;
032 import com.unboundid.ldap.sdk.CompareResult;
033 import com.unboundid.ldap.sdk.Control;
034 import com.unboundid.ldap.sdk.LDAPConnection;
035 import com.unboundid.ldap.sdk.LDAPException;
036 import com.unboundid.ldap.sdk.ResultCode;
037 import com.unboundid.ldap.sdk.Version;
038 import com.unboundid.util.Base64;
039 import com.unboundid.util.LDAPCommandLineTool;
040 import com.unboundid.util.StaticUtils;
041 import com.unboundid.util.ThreadSafety;
042 import com.unboundid.util.ThreadSafetyLevel;
043 import com.unboundid.util.args.ArgumentException;
044 import com.unboundid.util.args.ArgumentParser;
045 import com.unboundid.util.args.ControlArgument;
046
047
048
049 /**
050 * This class provides a simple tool that can be used to perform compare
051 * operations in an LDAP directory server. All of the necessary information is
052 * provided using command line arguments. Supported arguments include those
053 * allowed by the {@link LDAPCommandLineTool} class. In addition, a set of at
054 * least two unnamed trailing arguments must be given. The first argument
055 * should be a string containing the name of the target attribute followed by a
056 * colon and the assertion value to use for that attribute (e.g.,
057 * "cn:john doe"). Alternately, the attribute name may be followed by two
058 * colons and the base64-encoded representation of the assertion value
059 * (e.g., "cn:: am9obiBkb2U="). Any subsequent trailing arguments will be the
060 * DN(s) of entries in which to perform the compare operation(s).
061 * <BR><BR>
062 * Some of the APIs demonstrated by this example include:
063 * <UL>
064 * <LI>Argument Parsing (from the {@code com.unboundid.util.args}
065 * package)</LI>
066 * <LI>LDAP Command-Line Tool (from the {@code com.unboundid.util}
067 * package)</LI>
068 * <LI>LDAP Communication (from the {@code com.unboundid.ldap.sdk}
069 * package)</LI>
070 * </UL>
071 */
072 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
073 public final class LDAPCompare
074 extends LDAPCommandLineTool
075 implements Serializable
076 {
077 /**
078 * The serial version UID for this serializable class.
079 */
080 private static final long serialVersionUID = 719069383330181184L;
081
082
083
084 // The argument parser for this tool.
085 private ArgumentParser parser;
086
087 // The argument used to specify any bind controls that should be used.
088 private ControlArgument bindControls;
089
090 // The argument used to specify any compare controls that should be used.
091 private ControlArgument compareControls;
092
093
094
095 /**
096 * Parse the provided command line arguments and make the appropriate set of
097 * changes.
098 *
099 * @param args The command line arguments provided to this program.
100 */
101 public static void main(final String[] args)
102 {
103 final ResultCode resultCode = main(args, System.out, System.err);
104 if (resultCode != ResultCode.SUCCESS)
105 {
106 System.exit(resultCode.intValue());
107 }
108 }
109
110
111
112 /**
113 * Parse the provided command line arguments and make the appropriate set of
114 * changes.
115 *
116 * @param args The command line arguments provided to this program.
117 * @param outStream The output stream to which standard out should be
118 * written. It may be {@code null} if output should be
119 * suppressed.
120 * @param errStream The output stream to which standard error should be
121 * written. It may be {@code null} if error messages
122 * should be suppressed.
123 *
124 * @return A result code indicating whether the processing was successful.
125 */
126 public static ResultCode main(final String[] args,
127 final OutputStream outStream,
128 final OutputStream errStream)
129 {
130 final LDAPCompare ldapCompare = new LDAPCompare(outStream, errStream);
131 return ldapCompare.runTool(args);
132 }
133
134
135
136 /**
137 * Creates a new instance of this tool.
138 *
139 * @param outStream The output stream to which standard out should be
140 * written. It may be {@code null} if output should be
141 * suppressed.
142 * @param errStream The output stream to which standard error should be
143 * written. It may be {@code null} if error messages
144 * should be suppressed.
145 */
146 public LDAPCompare(final OutputStream outStream, final OutputStream errStream)
147 {
148 super(outStream, errStream);
149 }
150
151
152
153 /**
154 * Retrieves the name for this tool.
155 *
156 * @return The name for this tool.
157 */
158 @Override()
159 public String getToolName()
160 {
161 return "ldapcompare";
162 }
163
164
165
166 /**
167 * Retrieves the description for this tool.
168 *
169 * @return The description for this tool.
170 */
171 @Override()
172 public String getToolDescription()
173 {
174 return "Process compare operations in LDAP directory server.";
175 }
176
177
178
179 /**
180 * Retrieves the version string for this tool.
181 *
182 * @return The version string for this tool.
183 */
184 @Override()
185 public String getToolVersion()
186 {
187 return Version.NUMERIC_VERSION_STRING;
188 }
189
190
191
192 /**
193 * Retrieves the maximum number of unnamed trailing arguments that are
194 * allowed.
195 *
196 * @return A negative value to indicate that any number of trailing arguments
197 * may be provided.
198 */
199 @Override()
200 public int getMaxTrailingArguments()
201 {
202 return -1;
203 }
204
205
206
207 /**
208 * Retrieves a placeholder string that may be used to indicate what kinds of
209 * trailing arguments are allowed.
210 *
211 * @return A placeholder string that may be used to indicate what kinds of
212 * trailing arguments are allowed.
213 */
214 @Override()
215 public String getTrailingArgumentsPlaceholder()
216 {
217 return "attr:value dn1 [dn2 [dn3 [...]]]";
218 }
219
220
221
222 /**
223 * Adds the arguments used by this program that aren't already provided by the
224 * generic {@code LDAPCommandLineTool} framework.
225 *
226 * @param parser The argument parser to which the arguments should be added.
227 *
228 * @throws ArgumentException If a problem occurs while adding the arguments.
229 */
230 @Override()
231 public void addNonLDAPArguments(final ArgumentParser parser)
232 throws ArgumentException
233 {
234 // Save a reference to the argument parser.
235 this.parser = parser;
236
237 String description =
238 "Information about a control to include in the bind request.";
239 bindControls = new ControlArgument(null, "bindControl", false, 0, null,
240 description);
241 parser.addArgument(bindControls);
242
243
244 description = "Information about a control to include in compare requests.";
245 compareControls = new ControlArgument('J', "control", false, 0, null,
246 description);
247 parser.addArgument(compareControls);
248 }
249
250
251
252 /**
253 * {@inheritDoc}
254 */
255 @Override()
256 protected List<Control> getBindControls()
257 {
258 return bindControls.getValues();
259 }
260
261
262
263 /**
264 * Performs the actual processing for this tool. In this case, it gets a
265 * connection to the directory server and uses it to perform the requested
266 * comparisons.
267 *
268 * @return The result code for the processing that was performed.
269 */
270 @Override()
271 public ResultCode doToolProcessing()
272 {
273 // Make sure that at least two trailing arguments were provided, which will
274 // be the attribute value assertion and at least one entry DN.
275 final List<String> trailingArguments = parser.getTrailingArguments();
276 if (trailingArguments.isEmpty())
277 {
278 err("No attribute value assertion was provided.");
279 err();
280 err(parser.getUsageString(79));
281 return ResultCode.PARAM_ERROR;
282 }
283 else if (trailingArguments.size() == 1)
284 {
285 err("No target entry DNs were provided.");
286 err();
287 err(parser.getUsageString(79));
288 return ResultCode.PARAM_ERROR;
289 }
290
291
292 // Parse the attribute value assertion.
293 final String avaString = trailingArguments.get(0);
294 final int colonPos = avaString.indexOf(':');
295 if (colonPos <= 0)
296 {
297 err("Malformed attribute value assertion.");
298 err();
299 err(parser.getUsageString(79));
300 return ResultCode.PARAM_ERROR;
301 }
302
303 final String attributeName = avaString.substring(0, colonPos);
304 final byte[] assertionValueBytes;
305 final int doubleColonPos = avaString.indexOf("::");
306 if (doubleColonPos == colonPos)
307 {
308 // There are two colons, so it's a base64-encoded assertion value.
309 try
310 {
311 assertionValueBytes = Base64.decode(avaString.substring(colonPos+2));
312 }
313 catch (ParseException pe)
314 {
315 err("Unable to base64-decode the assertion value: ",
316 pe.getMessage());
317 err();
318 err(parser.getUsageString(79));
319 return ResultCode.PARAM_ERROR;
320 }
321 }
322 else
323 {
324 // There is only a single colon, so it's a simple UTF-8 string.
325 assertionValueBytes =
326 StaticUtils.getBytes(avaString.substring(colonPos+1));
327 }
328
329
330 // Get the connection to the directory server.
331 final LDAPConnection connection;
332 try
333 {
334 connection = getConnection();
335 out("Connected to ", connection.getConnectedAddress(), ':',
336 connection.getConnectedPort());
337 }
338 catch (LDAPException le)
339 {
340 err("Error connecting to the directory server: ", le.getMessage());
341 return le.getResultCode();
342 }
343
344
345 // For each of the target entry DNs, process the compare.
346 ResultCode resultCode = ResultCode.SUCCESS;
347 CompareRequest compareRequest = null;
348 for (int i=1; i < trailingArguments.size(); i++)
349 {
350 final String targetDN = trailingArguments.get(i);
351 if (compareRequest == null)
352 {
353 compareRequest = new CompareRequest(targetDN, attributeName,
354 assertionValueBytes);
355 compareRequest.setControls(compareControls.getValues());
356 }
357 else
358 {
359 compareRequest.setDN(targetDN);
360 }
361
362 try
363 {
364 out("Processing compare request for entry ", targetDN);
365 final CompareResult result = connection.compare(compareRequest);
366 if (result.compareMatched())
367 {
368 out("The compare operation matched.");
369 }
370 else
371 {
372 out("The compare operation did not match.");
373 }
374 }
375 catch (LDAPException le)
376 {
377 resultCode = le.getResultCode();
378 err("An error occurred while processing the request: ",
379 le.getMessage());
380 err("Result Code: ", le.getResultCode().intValue(), " (",
381 le.getResultCode().getName(), ')');
382 if (le.getMatchedDN() != null)
383 {
384 err("Matched DN: ", le.getMatchedDN());
385 }
386 if (le.getReferralURLs() != null)
387 {
388 for (final String url : le.getReferralURLs())
389 {
390 err("Referral URL: ", url);
391 }
392 }
393 }
394 out();
395 }
396
397
398 // Close the connection to the directory server and exit.
399 connection.close();
400 out();
401 out("Disconnected from the server");
402 return resultCode;
403 }
404
405
406
407 /**
408 * {@inheritDoc}
409 */
410 @Override()
411 public LinkedHashMap<String[],String> getExampleUsages()
412 {
413 final LinkedHashMap<String[],String> examples =
414 new LinkedHashMap<String[],String>();
415
416 final String[] args =
417 {
418 "--hostname", "server.example.com",
419 "--port", "389",
420 "--bindDN", "uid=admin,dc=example,dc=com",
421 "--bindPassword", "password",
422 "givenName:John",
423 "uid=jdoe,ou=People,dc=example,dc=com"
424 };
425 final String description =
426 "Attempt to determine whether the entry for user " +
427 "'uid=jdoe,ou=People,dc=example,dc=com' has a value of 'John' for " +
428 "the givenName attribute.";
429 examples.put(args, description);
430
431 return examples;
432 }
433 }