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