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