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 }