001/* 002 * Copyright 2008-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2014 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.LDAPConnection; 034import com.unboundid.ldap.sdk.LDAPException; 035import com.unboundid.ldap.sdk.ResultCode; 036import com.unboundid.ldap.sdk.Version; 037import com.unboundid.util.Base64; 038import com.unboundid.util.LDAPCommandLineTool; 039import com.unboundid.util.StaticUtils; 040import com.unboundid.util.ThreadSafety; 041import com.unboundid.util.ThreadSafetyLevel; 042import com.unboundid.util.args.ArgumentException; 043import 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) 071public 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}