001/* 002 * Copyright 2007-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 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.ldif; 022 023 024 025import java.util.Collections; 026import java.util.List; 027import java.util.StringTokenizer; 028 029import com.unboundid.asn1.ASN1OctetString; 030import com.unboundid.ldap.sdk.ChangeType; 031import com.unboundid.ldap.sdk.Control; 032import com.unboundid.ldap.sdk.DN; 033import com.unboundid.ldap.sdk.Entry; 034import com.unboundid.ldap.sdk.LDAPException; 035import com.unboundid.ldap.sdk.LDAPInterface; 036import com.unboundid.ldap.sdk.LDAPResult; 037import com.unboundid.util.ByteStringBuffer; 038import com.unboundid.util.NotExtensible; 039import com.unboundid.util.ThreadSafety; 040import com.unboundid.util.ThreadSafetyLevel; 041import com.unboundid.util.Validator; 042 043 044 045/** 046 * This class provides a base class for LDIF change records, which can be used 047 * to represent add, delete, modify, and modify DN operations in LDIF form. 048 * <BR><BR> 049 * <H2>Example</H2> 050 * The following example iterates through all of the change records contained in 051 * an LDIF file and attempts to apply those changes to a directory server: 052 * <PRE> 053 * LDIFReader ldifReader = new LDIFReader(pathToLDIFFile); 054 * 055 * int changesRead = 0; 056 * int changesProcessed = 0; 057 * int errorsEncountered = 0; 058 * while (true) 059 * { 060 * LDIFChangeRecord changeRecord; 061 * try 062 * { 063 * changeRecord = ldifReader.readChangeRecord(); 064 * if (changeRecord == null) 065 * { 066 * // All changes have been processed. 067 * break; 068 * } 069 * 070 * changesRead++; 071 * } 072 * catch (LDIFException le) 073 * { 074 * errorsEncountered++; 075 * if (le.mayContinueReading()) 076 * { 077 * // A recoverable error occurred while attempting to read a change 078 * // record, at or near line number le.getLineNumber() 079 * // The change record will be skipped, but we'll try to keep reading 080 * // from the LDIF file. 081 * continue; 082 * } 083 * else 084 * { 085 * // An unrecoverable error occurred while attempting to read a change 086 * // record, at or near line number le.getLineNumber() 087 * // No further LDIF processing will be performed. 088 * break; 089 * } 090 * } 091 * catch (IOException ioe) 092 * { 093 * // An I/O error occurred while attempting to read from the LDIF file. 094 * // No further LDIF processing will be performed. 095 * errorsEncountered++; 096 * break; 097 * } 098 * 099 * // Try to process the change in a directory server. 100 * LDAPResult operationResult; 101 * try 102 * { 103 * operationResult = changeRecord.processChange(connection); 104 * // If we got here, then the change should have been processed 105 * // successfully. 106 * changesProcessed++; 107 * } 108 * catch (LDAPException le) 109 * { 110 * // If we got here, then the change attempt failed. 111 * operationResult = le.toLDAPResult(); 112 * errorsEncountered++; 113 * } 114 * } 115 * 116 * ldifReader.close(); 117 * </PRE> 118 */ 119@NotExtensible() 120@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 121public abstract class LDIFChangeRecord 122 implements LDIFRecord 123{ 124 /** 125 * The serial version UID for this serializable class. 126 */ 127 private static final long serialVersionUID = 6917212392170911115L; 128 129 130 131 // The set of controls for the LDIF change record. 132 private final List<Control> controls; 133 134 // The parsed DN for this LDIF change record. 135 private volatile DN parsedDN; 136 137 // The DN for this LDIF change record. 138 private final String dn; 139 140 141 142 /** 143 * Creates a new LDIF change record with the provided DN. 144 * 145 * @param dn The DN of the LDIF change record to create. It must not 146 * be {@code null}. 147 * @param controls The set of controls for the change record to create. It 148 * may be {@code null} or empty if no controls are needed. 149 */ 150 protected LDIFChangeRecord(final String dn, final List<Control> controls) 151 { 152 Validator.ensureNotNull(dn); 153 154 this.dn = dn; 155 parsedDN = null; 156 157 if (controls == null) 158 { 159 this.controls = Collections.emptyList(); 160 } 161 else 162 { 163 this.controls = Collections.unmodifiableList(controls); 164 } 165 } 166 167 168 169 /** 170 * Retrieves the DN for this LDIF change record. 171 * 172 * @return The DN for this LDIF change record. 173 */ 174 @Override() 175 public final String getDN() 176 { 177 return dn; 178 } 179 180 181 182 /** 183 * Retrieves the parsed DN for this LDIF change record. 184 * 185 * @return The DN for this LDIF change record. 186 * 187 * @throws LDAPException If a problem occurs while trying to parse the DN. 188 */ 189 @Override() 190 public final DN getParsedDN() 191 throws LDAPException 192 { 193 if (parsedDN == null) 194 { 195 parsedDN = new DN(dn); 196 } 197 198 return parsedDN; 199 } 200 201 202 203 /** 204 * Retrieves the type of operation represented by this LDIF change record. 205 * 206 * @return The type of operation represented by this LDIF change record. 207 */ 208 public abstract ChangeType getChangeType(); 209 210 211 212 /** 213 * Retrieves the set of controls for this LDIF change record. 214 * 215 * @return The set of controls for this LDIF change record, or an empty array 216 * if there are no controls. 217 */ 218 public List<Control> getControls() 219 { 220 return controls; 221 } 222 223 224 225 /** 226 * Apply the change represented by this LDIF change record to a directory 227 * server using the provided connection. Any controls included in the 228 * change record will be included in the request. 229 * 230 * @param connection The connection to use to apply the change. 231 * 232 * @return An object providing information about the result of the operation. 233 * 234 * @throws LDAPException If an error occurs while processing this change 235 * in the associated directory server. 236 */ 237 public final LDAPResult processChange(final LDAPInterface connection) 238 throws LDAPException 239 { 240 return processChange(connection, true); 241 } 242 243 244 245 /** 246 * Apply the change represented by this LDIF change record to a directory 247 * server using the provided connection, optionally including any change 248 * record controls in the request. 249 * 250 * @param connection The connection to use to apply the change. 251 * @param includeControls Indicates whether to include any controls in the 252 * request. 253 * 254 * @return An object providing information about the result of the operation. 255 * 256 * @throws LDAPException If an error occurs while processing this change 257 * in the associated directory server. 258 */ 259 public abstract LDAPResult processChange(LDAPInterface connection, 260 boolean includeControls) 261 throws LDAPException; 262 263 264 265 /** 266 * Retrieves an {@code Entry} representation of this change record. This is 267 * intended only for internal use by the LDIF reader when operating 268 * asynchronously in the case that it is not possible to know ahead of time 269 * whether a user will attempt to read an LDIF record by {@code readEntry} or 270 * {@code readChangeRecord}. In the event that the LDIF file has an entry 271 * whose first attribute is "changetype" and the client wants to read it as 272 * an entry rather than a change record, then this may be used to generate an 273 * entry representing the change record. 274 * 275 * @return The entry representation of this change record. 276 * 277 * @throws LDIFException If this change record cannot be represented as a 278 * valid entry. 279 */ 280 final Entry toEntry() 281 throws LDIFException 282 { 283 return new Entry(toLDIF()); 284 } 285 286 287 288 /** 289 * Retrieves a string array whose lines contain an LDIF representation of this 290 * change record. 291 * 292 * @return A string array whose lines contain an LDIF representation of this 293 * change record. 294 */ 295 @Override() 296 public final String[] toLDIF() 297 { 298 return toLDIF(0); 299 } 300 301 302 303 /** 304 * Retrieves a string array whose lines contain an LDIF representation of this 305 * change record. 306 * 307 * @param wrapColumn The column at which to wrap long lines. A value that 308 * is less than or equal to two indicates that no 309 * wrapping should be performed. 310 * 311 * @return A string array whose lines contain an LDIF representation of this 312 * change record. 313 */ 314 @Override() 315 public abstract String[] toLDIF(int wrapColumn); 316 317 318 319 /** 320 * Encodes the provided name and value and adds the result to the provided 321 * list of lines. This will handle the case in which the encoded name and 322 * value includes comments about the base64-decoded representation of the 323 * provided value. 324 * 325 * @param name The attribute name to be encoded. 326 * @param value The attribute value to be encoded. 327 * @param lines The list of lines to be updated. 328 */ 329 static void encodeNameAndValue(final String name, final ASN1OctetString value, 330 final List<String> lines) 331 { 332 final String line = LDIFWriter.encodeNameAndValue(name, value); 333 if (LDIFWriter.commentAboutBase64EncodedValues() && 334 line.startsWith(name + "::")) 335 { 336 final StringTokenizer tokenizer = new StringTokenizer(line, "\r\n"); 337 while (tokenizer.hasMoreTokens()) 338 { 339 lines.add(tokenizer.nextToken()); 340 } 341 } 342 else 343 { 344 lines.add(line); 345 } 346 } 347 348 349 350 /** 351 * Appends an LDIF string representation of this change record to the provided 352 * buffer. 353 * 354 * @param buffer The buffer to which to append an LDIF representation of 355 * this change record. 356 */ 357 @Override() 358 public final void toLDIF(final ByteStringBuffer buffer) 359 { 360 toLDIF(buffer, 0); 361 } 362 363 364 365 /** 366 * Appends an LDIF string representation of this change record to the provided 367 * buffer. 368 * 369 * @param buffer The buffer to which to append an LDIF representation of 370 * this change record. 371 * @param wrapColumn The column at which to wrap long lines. A value that 372 * is less than or equal to two indicates that no 373 * wrapping should be performed. 374 */ 375 @Override() 376 public abstract void toLDIF(ByteStringBuffer buffer, int wrapColumn); 377 378 379 380 /** 381 * Retrieves an LDIF string representation of this change record. 382 * 383 * @return An LDIF string representation of this change record. 384 */ 385 @Override() 386 public final String toLDIFString() 387 { 388 final StringBuilder buffer = new StringBuilder(); 389 toLDIFString(buffer, 0); 390 return buffer.toString(); 391 } 392 393 394 395 /** 396 * Retrieves an LDIF string representation of this change record. 397 * 398 * @param wrapColumn The column at which to wrap long lines. A value that 399 * is less than or equal to two indicates that no 400 * wrapping should be performed. 401 * 402 * @return An LDIF string representation of this change record. 403 */ 404 @Override() 405 public final String toLDIFString(final int wrapColumn) 406 { 407 final StringBuilder buffer = new StringBuilder(); 408 toLDIFString(buffer, wrapColumn); 409 return buffer.toString(); 410 } 411 412 413 414 /** 415 * Appends an LDIF string representation of this change record to the provided 416 * buffer. 417 * 418 * @param buffer The buffer to which to append an LDIF representation of 419 * this change record. 420 */ 421 @Override() 422 public final void toLDIFString(final StringBuilder buffer) 423 { 424 toLDIFString(buffer, 0); 425 } 426 427 428 429 /** 430 * Appends an LDIF string representation of this change record to the provided 431 * buffer. 432 * 433 * @param buffer The buffer to which to append an LDIF representation of 434 * this change record. 435 * @param wrapColumn The column at which to wrap long lines. A value that 436 * is less than or equal to two indicates that no 437 * wrapping should be performed. 438 */ 439 @Override() 440 public abstract void toLDIFString(StringBuilder buffer, int wrapColumn); 441 442 443 444 /** 445 * Retrieves a hash code for this change record. 446 * 447 * @return A hash code for this change record. 448 */ 449 @Override() 450 public abstract int hashCode(); 451 452 453 454 /** 455 * Indicates whether the provided object is equal to this LDIF change record. 456 * 457 * @param o The object for which to make the determination. 458 * 459 * @return {@code true} if the provided object is equal to this LDIF change 460 * record, or {@code false} if not. 461 */ 462 @Override() 463 public abstract boolean equals(Object o); 464 465 466 467 /** 468 * Encodes a string representation of the provided control for use in the 469 * LDIF representation of the change record. 470 * 471 * @param c The control to be encoded. 472 * 473 * @return The string representation of the control. 474 */ 475 static ASN1OctetString encodeControlString(final Control c) 476 { 477 final ByteStringBuffer buffer = new ByteStringBuffer(); 478 buffer.append(c.getOID()); 479 480 if (c.isCritical()) 481 { 482 buffer.append(" true"); 483 } 484 else 485 { 486 buffer.append(" false"); 487 } 488 489 final ASN1OctetString value = c.getValue(); 490 if (value != null) 491 { 492 LDIFWriter.encodeValue(value, buffer); 493 } 494 495 return buffer.toByteString().toASN1OctetString(); 496 } 497 498 499 500 /** 501 * Retrieves a single-line string representation of this change record. 502 * 503 * @return A single-line string representation of this change record. 504 */ 505 @Override() 506 public final String toString() 507 { 508 final StringBuilder buffer = new StringBuilder(); 509 toString(buffer); 510 return buffer.toString(); 511 } 512 513 514 515 /** 516 * Appends a single-line string representation of this change record to the 517 * provided buffer. 518 * 519 * @param buffer The buffer to which the information should be written. 520 */ 521 @Override() 522 public abstract void toString(StringBuilder buffer); 523}