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 */
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 import com.unboundid.util.NotExtensible;
038 import com.unboundid.util.ThreadSafety;
039 import com.unboundid.util.ThreadSafetyLevel;
040
041 import 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)
121 public 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 }