001/* 002 * Copyright 2007-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.ldif; 022 023 024 025import java.util.Collections; 026import java.util.List; 027 028import com.unboundid.asn1.ASN1OctetString; 029import com.unboundid.ldap.sdk.ChangeType; 030import com.unboundid.ldap.sdk.Control; 031import com.unboundid.ldap.sdk.DN; 032import com.unboundid.ldap.sdk.Entry; 033import com.unboundid.ldap.sdk.LDAPException; 034import com.unboundid.ldap.sdk.LDAPInterface; 035import com.unboundid.ldap.sdk.LDAPResult; 036import com.unboundid.util.ByteStringBuffer; 037import com.unboundid.util.NotExtensible; 038import com.unboundid.util.ThreadSafety; 039import com.unboundid.util.ThreadSafetyLevel; 040 041import static 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 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 public final String getDN() 175 { 176 return dn; 177 } 178 179 180 181 /** 182 * Retrieves the parsed DN for this LDIF change record. 183 * 184 * @return The DN for this LDIF change record. 185 * 186 * @throws LDAPException If a problem occurs while trying to parse the DN. 187 */ 188 public final DN getParsedDN() 189 throws LDAPException 190 { 191 if (parsedDN == null) 192 { 193 parsedDN = new DN(dn); 194 } 195 196 return parsedDN; 197 } 198 199 200 201 /** 202 * Retrieves the type of operation represented by this LDIF change record. 203 * 204 * @return The type of operation represented by this LDIF change record. 205 */ 206 public abstract ChangeType getChangeType(); 207 208 209 210 /** 211 * Retrieves the set of controls for this LDIF change record. 212 * 213 * @return The set of controls for this LDIF change record, or an empty array 214 * if there are no controls. 215 */ 216 public List<Control> getControls() 217 { 218 return controls; 219 } 220 221 222 223 /** 224 * Apply the change represented by this LDIF change record to a directory 225 * server using the provided connection. Any controls included in the 226 * change record will be included in the request. 227 * 228 * @param connection The connection to use to apply the change. 229 * 230 * @return An object providing information about the result of the operation. 231 * 232 * @throws LDAPException If an error occurs while processing this change 233 * in the associated directory server. 234 */ 235 public final LDAPResult processChange(final LDAPInterface connection) 236 throws LDAPException 237 { 238 return processChange(connection, true); 239 } 240 241 242 243 /** 244 * Apply the change represented by this LDIF change record to a directory 245 * server using the provided connection, optionally including any change 246 * record controls in the request. 247 * 248 * @param connection The connection to use to apply the change. 249 * @param includeControls Indicates whether to include any controls in the 250 * request. 251 * 252 * @return An object providing information about the result of the operation. 253 * 254 * @throws LDAPException If an error occurs while processing this change 255 * in the associated directory server. 256 */ 257 public abstract LDAPResult processChange(final LDAPInterface connection, 258 final boolean includeControls) 259 throws LDAPException; 260 261 262 263 /** 264 * Retrieves an {@code Entry} representation of this change record. This is 265 * intended only for internal use by the LDIF reader when operating 266 * asynchronously in the case that it is not possible to know ahead of time 267 * whether a user will attempt to read an LDIF record by {@code readEntry} or 268 * {@code readChangeRecord}. In the event that the LDIF file has an entry 269 * whose first attribute is "changetype" and the client wants to read it as 270 * an entry rather than a change record, then this may be used to generate an 271 * entry representing the change record. 272 * 273 * @return The entry representation of this change record. 274 * 275 * @throws LDIFException If this change record cannot be represented as a 276 * valid entry. 277 */ 278 final Entry toEntry() 279 throws LDIFException 280 { 281 return new Entry(toLDIF()); 282 } 283 284 285 286 /** 287 * Retrieves a string array whose lines contain an LDIF representation of this 288 * change record. 289 * 290 * @return A string array whose lines contain an LDIF representation of this 291 * change record. 292 */ 293 public final String[] toLDIF() 294 { 295 return toLDIF(0); 296 } 297 298 299 300 /** 301 * Retrieves a string array whose lines contain an LDIF representation of this 302 * change record. 303 * 304 * @param wrapColumn The column at which to wrap long lines. A value that 305 * is less than or equal to two indicates that no 306 * wrapping should be performed. 307 * 308 * @return A string array whose lines contain an LDIF representation of this 309 * change record. 310 */ 311 public abstract String[] toLDIF(final int wrapColumn); 312 313 314 315 /** 316 * Appends an LDIF string representation of this change record to the provided 317 * buffer. 318 * 319 * @param buffer The buffer to which to append an LDIF representation of 320 * this change record. 321 */ 322 public final void toLDIF(final ByteStringBuffer buffer) 323 { 324 toLDIF(buffer, 0); 325 } 326 327 328 329 /** 330 * Appends an LDIF string representation of this change record to the provided 331 * buffer. 332 * 333 * @param buffer The buffer to which to append an LDIF representation of 334 * this change record. 335 * @param wrapColumn The column at which to wrap long lines. A value that 336 * is less than or equal to two indicates that no 337 * wrapping should be performed. 338 */ 339 public abstract void toLDIF(final ByteStringBuffer buffer, 340 final int wrapColumn); 341 342 343 344 /** 345 * Retrieves an LDIF string representation of this change record. 346 * 347 * @return An LDIF string representation of this change record. 348 */ 349 public final String toLDIFString() 350 { 351 final StringBuilder buffer = new StringBuilder(); 352 toLDIFString(buffer, 0); 353 return buffer.toString(); 354 } 355 356 357 358 /** 359 * Retrieves an LDIF string representation of this change record. 360 * 361 * @param wrapColumn The column at which to wrap long lines. A value that 362 * is less than or equal to two indicates that no 363 * wrapping should be performed. 364 * 365 * @return An LDIF string representation of this change record. 366 */ 367 public final String toLDIFString(final int wrapColumn) 368 { 369 final StringBuilder buffer = new StringBuilder(); 370 toLDIFString(buffer, wrapColumn); 371 return buffer.toString(); 372 } 373 374 375 376 /** 377 * Appends an LDIF string representation of this change record to the provided 378 * buffer. 379 * 380 * @param buffer The buffer to which to append an LDIF representation of 381 * this change record. 382 */ 383 public final void toLDIFString(final StringBuilder buffer) 384 { 385 toLDIFString(buffer, 0); 386 } 387 388 389 390 /** 391 * Appends an LDIF string representation of this change record to the provided 392 * buffer. 393 * 394 * @param buffer The buffer to which to append an LDIF representation of 395 * this change record. 396 * @param wrapColumn The column at which to wrap long lines. A value that 397 * is less than or equal to two indicates that no 398 * wrapping should be performed. 399 */ 400 public abstract void toLDIFString(final StringBuilder buffer, 401 final int wrapColumn); 402 403 404 405 /** 406 * Retrieves a hash code for this change record. 407 * 408 * @return A hash code for this change record. 409 */ 410 @Override() 411 public abstract int hashCode(); 412 413 414 415 /** 416 * Indicates whether the provided object is equal to this LDIF change record. 417 * 418 * @param o The object for which to make the determination. 419 * 420 * @return {@code true} if the provided object is equal to this LDIF change 421 * record, or {@code false} if not. 422 */ 423 @Override() 424 public abstract boolean equals(final Object o); 425 426 427 428 /** 429 * Encodes a string representation of the provided control for use in the 430 * LDIF representation of the change record. 431 * 432 * @param c The control to be encoded. 433 * 434 * @return The string representation of the control. 435 */ 436 static ASN1OctetString encodeControlString(final Control c) 437 { 438 final ByteStringBuffer buffer = new ByteStringBuffer(); 439 buffer.append(c.getOID()); 440 441 if (c.isCritical()) 442 { 443 buffer.append(" true"); 444 } 445 else 446 { 447 buffer.append(" false"); 448 } 449 450 final ASN1OctetString value = c.getValue(); 451 if (value != null) 452 { 453 LDIFWriter.encodeValue(value, buffer); 454 } 455 456 return buffer.toByteString().toASN1OctetString(); 457 } 458 459 460 461 /** 462 * Retrieves a single-line string representation of this change record. 463 * 464 * @return A single-line string representation of this change record. 465 */ 466 @Override() 467 public final String toString() 468 { 469 final StringBuilder buffer = new StringBuilder(); 470 toString(buffer); 471 return buffer.toString(); 472 } 473 474 475 476 /** 477 * Appends a single-line string representation of this change record to the 478 * provided buffer. 479 * 480 * @param buffer The buffer to which the information should be written. 481 */ 482 public abstract void toString(final StringBuilder buffer); 483}