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