001/* 002 * Copyright 2007-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2019 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 * Creates a duplicate of this LDIF change record with the provided set of 227 * controls. 228 * 229 * @param controls The set of controls to include in the duplicate change 230 * record. It may be {@code null} or empty if no controls 231 * should be included. 232 * 233 * @return A duplicate of this LDIF change record with the provided set of 234 * controls. 235 */ 236 public abstract LDIFChangeRecord duplicate(Control... controls); 237 238 239 240 /** 241 * Apply the change represented by this LDIF change record to a directory 242 * server using the provided connection. Any controls included in the 243 * change record will be included in the request. 244 * 245 * @param connection The connection to use to apply the change. 246 * 247 * @return An object providing information about the result of the operation. 248 * 249 * @throws LDAPException If an error occurs while processing this change 250 * in the associated directory server. 251 */ 252 public final LDAPResult processChange(final LDAPInterface connection) 253 throws LDAPException 254 { 255 return processChange(connection, true); 256 } 257 258 259 260 /** 261 * Apply the change represented by this LDIF change record to a directory 262 * server using the provided connection, optionally including any change 263 * record controls in the request. 264 * 265 * @param connection The connection to use to apply the change. 266 * @param includeControls Indicates whether to include any controls in the 267 * request. 268 * 269 * @return An object providing information about the result of the operation. 270 * 271 * @throws LDAPException If an error occurs while processing this change 272 * in the associated directory server. 273 */ 274 public abstract LDAPResult processChange(LDAPInterface connection, 275 boolean includeControls) 276 throws LDAPException; 277 278 279 280 /** 281 * Retrieves an {@code Entry} representation of this change record. This is 282 * intended only for internal use by the LDIF reader when operating 283 * asynchronously in the case that it is not possible to know ahead of time 284 * whether a user will attempt to read an LDIF record by {@code readEntry} or 285 * {@code readChangeRecord}. In the event that the LDIF file has an entry 286 * whose first attribute is "changetype" and the client wants to read it as 287 * an entry rather than a change record, then this may be used to generate an 288 * entry representing the change record. 289 * 290 * @return The entry representation of this change record. 291 * 292 * @throws LDIFException If this change record cannot be represented as a 293 * valid entry. 294 */ 295 final Entry toEntry() 296 throws LDIFException 297 { 298 return new Entry(toLDIF()); 299 } 300 301 302 303 /** 304 * Retrieves a string array whose lines contain an LDIF representation of this 305 * change record. 306 * 307 * @return A string array whose lines contain an LDIF representation of this 308 * change record. 309 */ 310 @Override() 311 public final String[] toLDIF() 312 { 313 return toLDIF(0); 314 } 315 316 317 318 /** 319 * Retrieves a string array whose lines contain an LDIF representation of this 320 * change record. 321 * 322 * @param wrapColumn The column at which to wrap long lines. A value that 323 * is less than or equal to two indicates that no 324 * wrapping should be performed. 325 * 326 * @return A string array whose lines contain an LDIF representation of this 327 * change record. 328 */ 329 @Override() 330 public abstract String[] toLDIF(int wrapColumn); 331 332 333 334 /** 335 * Encodes the provided name and value and adds the result to the provided 336 * list of lines. This will handle the case in which the encoded name and 337 * value includes comments about the base64-decoded representation of the 338 * provided value. 339 * 340 * @param name The attribute name to be encoded. 341 * @param value The attribute value to be encoded. 342 * @param lines The list of lines to be updated. 343 */ 344 static void encodeNameAndValue(final String name, final ASN1OctetString value, 345 final List<String> lines) 346 { 347 final String line = LDIFWriter.encodeNameAndValue(name, value); 348 if (LDIFWriter.commentAboutBase64EncodedValues() && 349 line.startsWith(name + "::")) 350 { 351 final StringTokenizer tokenizer = new StringTokenizer(line, "\r\n"); 352 while (tokenizer.hasMoreTokens()) 353 { 354 lines.add(tokenizer.nextToken()); 355 } 356 } 357 else 358 { 359 lines.add(line); 360 } 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 */ 372 @Override() 373 public final void toLDIF(final ByteStringBuffer buffer) 374 { 375 toLDIF(buffer, 0); 376 } 377 378 379 380 /** 381 * Appends an LDIF string representation of this change record to the provided 382 * buffer. 383 * 384 * @param buffer The buffer to which to append an LDIF representation of 385 * this change record. 386 * @param wrapColumn The column at which to wrap long lines. A value that 387 * is less than or equal to two indicates that no 388 * wrapping should be performed. 389 */ 390 @Override() 391 public abstract void toLDIF(ByteStringBuffer buffer, int wrapColumn); 392 393 394 395 /** 396 * Retrieves an LDIF string representation of this change record. 397 * 398 * @return An LDIF string representation of this change record. 399 */ 400 @Override() 401 public final String toLDIFString() 402 { 403 final StringBuilder buffer = new StringBuilder(); 404 toLDIFString(buffer, 0); 405 return buffer.toString(); 406 } 407 408 409 410 /** 411 * Retrieves an LDIF string representation of this change record. 412 * 413 * @param wrapColumn The column at which to wrap long lines. A value that 414 * is less than or equal to two indicates that no 415 * wrapping should be performed. 416 * 417 * @return An LDIF string representation of this change record. 418 */ 419 @Override() 420 public final String toLDIFString(final int wrapColumn) 421 { 422 final StringBuilder buffer = new StringBuilder(); 423 toLDIFString(buffer, wrapColumn); 424 return buffer.toString(); 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 */ 436 @Override() 437 public final void toLDIFString(final StringBuilder buffer) 438 { 439 toLDIFString(buffer, 0); 440 } 441 442 443 444 /** 445 * Appends an LDIF string representation of this change record to the provided 446 * buffer. 447 * 448 * @param buffer The buffer to which to append an LDIF representation of 449 * this change record. 450 * @param wrapColumn The column at which to wrap long lines. A value that 451 * is less than or equal to two indicates that no 452 * wrapping should be performed. 453 */ 454 @Override() 455 public abstract void toLDIFString(StringBuilder buffer, int wrapColumn); 456 457 458 459 /** 460 * Retrieves a hash code for this change record. 461 * 462 * @return A hash code for this change record. 463 */ 464 @Override() 465 public abstract int hashCode(); 466 467 468 469 /** 470 * Indicates whether the provided object is equal to this LDIF change record. 471 * 472 * @param o The object for which to make the determination. 473 * 474 * @return {@code true} if the provided object is equal to this LDIF change 475 * record, or {@code false} if not. 476 */ 477 @Override() 478 public abstract boolean equals(Object o); 479 480 481 482 /** 483 * Encodes a string representation of the provided control for use in the 484 * LDIF representation of the change record. 485 * 486 * @param c The control to be encoded. 487 * 488 * @return The string representation of the control. 489 */ 490 static ASN1OctetString encodeControlString(final Control c) 491 { 492 final ByteStringBuffer buffer = new ByteStringBuffer(); 493 buffer.append(c.getOID()); 494 495 if (c.isCritical()) 496 { 497 buffer.append(" true"); 498 } 499 else 500 { 501 buffer.append(" false"); 502 } 503 504 final ASN1OctetString value = c.getValue(); 505 if (value != null) 506 { 507 LDIFWriter.encodeValue(value, buffer); 508 } 509 510 return buffer.toByteString().toASN1OctetString(); 511 } 512 513 514 515 /** 516 * Retrieves a single-line string representation of this change record. 517 * 518 * @return A single-line string representation of this change record. 519 */ 520 @Override() 521 public final String toString() 522 { 523 final StringBuilder buffer = new StringBuilder(); 524 toString(buffer); 525 return buffer.toString(); 526 } 527 528 529 530 /** 531 * Appends a single-line string representation of this change record to the 532 * provided buffer. 533 * 534 * @param buffer The buffer to which the information should be written. 535 */ 536 @Override() 537 public abstract void toString(StringBuilder buffer); 538}