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 */ 021package com.unboundid.ldap.sdk.examples; 022 023 024 025import java.io.OutputStream; 026import java.io.Serializable; 027import java.text.ParseException; 028import java.util.LinkedHashMap; 029import java.util.List; 030 031import com.unboundid.ldap.sdk.CompareRequest; 032import com.unboundid.ldap.sdk.CompareResult; 033import com.unboundid.ldap.sdk.Control; 034import com.unboundid.ldap.sdk.LDAPConnection; 035import com.unboundid.ldap.sdk.LDAPException; 036import com.unboundid.ldap.sdk.ResultCode; 037import com.unboundid.ldap.sdk.Version; 038import com.unboundid.util.Base64; 039import com.unboundid.util.LDAPCommandLineTool; 040import com.unboundid.util.StaticUtils; 041import com.unboundid.util.ThreadSafety; 042import com.unboundid.util.ThreadSafetyLevel; 043import com.unboundid.util.args.ArgumentException; 044import com.unboundid.util.args.ArgumentParser; 045import 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) 073public 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}