1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.directory.server.core.partition.ldif;
22
23
24 import java.io.File;
25 import java.io.FileNotFoundException;
26 import java.io.IOException;
27 import java.io.RandomAccessFile;
28 import java.util.Iterator;
29 import java.util.UUID;
30
31 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
32 import org.apache.directory.api.ldap.model.cursor.Cursor;
33 import org.apache.directory.api.ldap.model.entry.DefaultEntry;
34 import org.apache.directory.api.ldap.model.entry.Entry;
35 import org.apache.directory.api.ldap.model.entry.Modification;
36 import org.apache.directory.api.ldap.model.exception.LdapException;
37 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
38 import org.apache.directory.api.ldap.model.exception.LdapOperationException;
39 import org.apache.directory.api.ldap.model.exception.LdapOtherException;
40 import org.apache.directory.api.ldap.model.ldif.LdifEntry;
41 import org.apache.directory.api.ldap.model.ldif.LdifReader;
42 import org.apache.directory.api.ldap.model.ldif.LdifUtils;
43 import org.apache.directory.api.ldap.model.name.Dn;
44 import org.apache.directory.api.ldap.model.name.Rdn;
45 import org.apache.directory.api.ldap.model.schema.SchemaManager;
46 import org.apache.directory.api.util.Strings;
47 import org.apache.directory.server.core.api.DnFactory;
48 import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
49 import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
50 import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
51 import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
52 import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
53 import org.apache.directory.server.core.api.partition.PartitionTxn;
54 import org.apache.directory.server.i18n.I18n;
55 import org.apache.directory.server.xdbm.IndexEntry;
56 import org.apache.directory.server.xdbm.ParentIdAndRdn;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60
61
62
63
64
65
66 public class SingleFileLdifPartition extends AbstractLdifPartition
67 {
68
69 private RandomAccessFile ldifFile;
70
71
72 private volatile boolean enableRewriting = true;
73
74
75 private boolean dirty = false;
76
77
78 private Object lock = new Object();
79
80 private static final Logger LOG = LoggerFactory.getLogger( SingleFileLdifPartition.class );
81
82
83
84
85
86
87
88
89 public SingleFileLdifPartition( SchemaManager schemaManager, DnFactory dnFactory )
90 {
91 super( schemaManager, dnFactory );
92 }
93
94
95 @Override
96 protected void doInit() throws LdapException
97 {
98 if ( !initialized )
99 {
100 if ( getPartitionPath() == null )
101 {
102 throw new IllegalArgumentException( "Partition path cannot be null" );
103 }
104
105 File partitionFile = new File( getPartitionPath() );
106
107 if ( partitionFile.exists() && !partitionFile.isFile() )
108 {
109 throw new IllegalArgumentException( "Partition path must be a LDIF file" );
110 }
111
112 try
113 {
114 ldifFile = new RandomAccessFile( partitionFile, "rws" );
115 }
116 catch ( FileNotFoundException fnfe )
117 {
118 throw new LdapOtherException( fnfe.getMessage(), fnfe );
119 }
120
121 LOG.debug( "id is : {}", getId() );
122
123
124
125 if ( ( suffixDn == null ) || ( suffixDn.isEmpty() ) )
126 {
127 String msg = I18n.err( I18n.ERR_150 );
128 LOG.error( msg );
129 throw new LdapInvalidDnException( msg );
130 }
131
132 if ( !suffixDn.isSchemaAware() )
133 {
134 suffixDn = new Dn( schemaManager, suffixDn );
135 }
136
137 super.doInit();
138
139 loadEntries();
140 }
141 }
142
143
144
145
146
147
148 private void loadEntries() throws LdapException
149 {
150 try ( RandomAccessLdifReader parser = new RandomAccessLdifReader( schemaManager ) )
151 {
152 Iterator<LdifEntry> itr = parser.iterator();
153
154 if ( !itr.hasNext() )
155 {
156 return;
157 }
158
159 LdifEntry ldifEntry = itr.next();
160
161 contextEntry = new DefaultEntry( schemaManager, ldifEntry.getEntry() );
162
163 if ( suffixDn.equals( contextEntry.getDn() ) )
164 {
165 addMandatoryOpAt( contextEntry );
166
167 AddOperationContext/interceptor/context/AddOperationContext.html#AddOperationContext">AddOperationContext addContext = new AddOperationContext( null, contextEntry );
168 addContext.setPartition( this );
169 addContext.setTransaction( this.beginWriteTransaction() );
170
171 super.add( addContext );
172 }
173 else
174 {
175 throw new LdapException( "The given LDIF file doesn't contain the context entry" );
176 }
177
178 while ( itr.hasNext() )
179 {
180 ldifEntry = itr.next();
181
182 Entry entry = new DefaultEntry( schemaManager, ldifEntry.getEntry() );
183
184 addMandatoryOpAt( entry );
185
186 AddOperationContext/interceptor/context/AddOperationContext.html#AddOperationContext">AddOperationContext addContext = new AddOperationContext( null, entry );
187 addContext.setPartition( this );
188 addContext.setTransaction( this.beginWriteTransaction() );
189
190 super.add( addContext );
191 }
192 }
193 catch ( IOException ioe )
194 {
195 throw new LdapOtherException( ioe.getMessage(), ioe );
196 }
197 }
198
199
200
201
202
203
204
205
206 @Override
207 public void add( AddOperationContext addContext ) throws LdapException
208 {
209 synchronized ( lock )
210 {
211 super.add( addContext );
212
213 if ( contextEntry == null )
214 {
215 Entry entry = addContext.getEntry();
216
217 if ( entry.getDn().equals( suffixDn ) )
218 {
219 contextEntry = entry;
220 }
221 }
222
223 dirty = true;
224 rewritePartitionData( addContext.getTransaction() );
225 }
226 }
227
228
229
230
231
232 @Override
233 public void modify( ModifyOperationContext modifyContext ) throws LdapException
234 {
235 PartitionTxn partitionTxn = modifyContext.getTransaction();
236
237 synchronized ( lock )
238 {
239 try
240 {
241 Entry modifiedEntry = super.modify( partitionTxn, modifyContext.getDn(),
242 modifyContext.getModItems().toArray( new Modification[]
243 {} ) );
244
245
246 modifiedEntry.removeAttributes( entryDnAT );
247
248 modifyContext.setAlteredEntry( modifiedEntry );
249 }
250 catch ( Exception e )
251 {
252 throw new LdapOperationException( e.getMessage(), e );
253 }
254
255 dirty = true;
256 rewritePartitionData( partitionTxn );
257 }
258 }
259
260
261
262
263
264 @Override
265 public void rename( RenameOperationContext renameContext ) throws LdapException
266 {
267 synchronized ( lock )
268 {
269 super.rename( renameContext );
270 dirty = true;
271 rewritePartitionData( renameContext.getTransaction() );
272 }
273 }
274
275
276
277
278
279 @Override
280 public void move( MoveOperationContext moveContext ) throws LdapException
281 {
282 synchronized ( lock )
283 {
284 super.move( moveContext );
285 dirty = true;
286 rewritePartitionData( moveContext.getTransaction() );
287 }
288 }
289
290
291
292
293
294 @Override
295 public void moveAndRename( MoveAndRenameOperationContext opContext ) throws LdapException
296 {
297 synchronized ( lock )
298 {
299 super.moveAndRename( opContext );
300 dirty = true;
301 rewritePartitionData( opContext.getTransaction() );
302 }
303 }
304
305
306 @Override
307 public Entry delete( PartitionTxn partitionTxn, String id ) throws LdapException
308 {
309 synchronized ( lock )
310 {
311 Entry deletedEntry = super.delete( partitionTxn, id );
312 dirty = true;
313 rewritePartitionData( partitionTxn );
314
315 return deletedEntry;
316 }
317 }
318
319
320
321
322
323
324
325
326 private void rewritePartitionData( PartitionTxn partitionTxn ) throws LdapException
327 {
328 synchronized ( lock )
329 {
330 if ( !enableRewriting || !dirty )
331 {
332 return;
333 }
334
335 try
336 {
337 ldifFile.setLength( 0 );
338
339 String suffixId = getEntryId( partitionTxn, suffixDn );
340
341 if ( suffixId == null )
342 {
343 contextEntry = null;
344 return;
345 }
346
347 ParentIdAndRdn suffixEntry = rdnIdx.reverseLookup( partitionTxn, suffixId );
348
349 if ( suffixEntry != null )
350 {
351 Entry entry = master.get( partitionTxn, suffixId );
352
353
354 entry.removeAttributes( entryDnAT );
355
356 entry.setDn( suffixDn );
357
358 appendLdif( entry );
359
360 appendRecursive( partitionTxn, suffixId, suffixEntry.getNbChildren() );
361 }
362
363 dirty = false;
364 }
365 catch ( LdapException e )
366 {
367 throw e;
368 }
369 catch ( Exception e )
370 {
371 throw new LdapException( e );
372 }
373 }
374 }
375
376
377 private void appendRecursive( PartitionTxn partitionTxn, String id, int nbSibbling ) throws Exception
378 {
379
380 Cursor<IndexEntry<ParentIdAndRdn, String>> cursor = rdnIdx.forwardCursor( partitionTxn );
381
382 IndexEntry<ParentIdAndRdn, String> startingPos = new IndexEntry<>();
383 startingPos.setKey( new ParentIdAndRdn( id, ( Rdn[] ) null ) );
384 cursor.before( startingPos );
385 int countChildren = 0;
386
387 while ( cursor.next() && ( countChildren < nbSibbling ) )
388 {
389 IndexEntry<ParentIdAndRdn, String> element = cursor.get();
390 String childId = element.getId();
391 Entry entry = fetch( partitionTxn, childId );
392
393
394 entry.removeAttributes( SchemaConstants.ENTRY_DN_AT );
395
396 appendLdif( entry );
397
398 countChildren++;
399
400
401 int nbChildren = element.getKey().getNbChildren();
402
403 if ( nbChildren > 0 )
404 {
405 appendRecursive( partitionTxn, childId, nbChildren );
406 }
407 }
408
409 cursor.close();
410 }
411
412
413
414
415
416
417
418
419 private void appendLdif( Entry entry ) throws IOException
420 {
421 synchronized ( lock )
422 {
423 String ldif = LdifUtils.convertToLdif( entry );
424 ldifFile.write( Strings.getBytesUtf8( ldif + "\n" ) );
425 }
426 }
427
428
429
430
431 private class RandomAccessLdifReader extends LdifReader
432 {
433 private long len;
434
435
436 RandomAccessLdifReader() throws LdapException
437 {
438 try
439 {
440 len = ldifFile.length();
441 super.init();
442 }
443 catch ( IOException e )
444 {
445 LdapException le = new LdapException( e.getMessage(), e );
446 le.initCause( e );
447
448 throw le;
449 }
450 }
451
452
453 RandomAccessLdifReader( SchemaManager schemaManager ) throws LdapException
454 {
455 try
456 {
457 this.schemaManager = schemaManager;
458 len = ldifFile.length();
459 super.init();
460 }
461 catch ( IOException e )
462 {
463 LdapException le = new LdapException( e.getMessage(), e );
464 le.initCause( e );
465
466 throw le;
467 }
468 }
469
470
471 @Override
472 protected String getLine() throws IOException
473 {
474 if ( len == 0 )
475 {
476 return null;
477 }
478
479 return ldifFile.readLine();
480 }
481 }
482
483
484
485
486
487 private void addMandatoryOpAt( Entry entry ) throws LdapException
488 {
489 if ( entry.get( SchemaConstants.ENTRY_CSN_AT ) == null )
490 {
491 entry.add( SchemaConstants.ENTRY_CSN_AT, defaultCSNFactory.newInstance().toString() );
492 }
493
494 if ( entry.get( SchemaConstants.ENTRY_UUID_AT ) == null )
495 {
496 String uuid = UUID.randomUUID().toString();
497 entry.add( SchemaConstants.ENTRY_UUID_AT, uuid );
498 }
499 }
500
501
502
503
504
505 @Override
506 protected void doDestroy( PartitionTxn partitionTxn ) throws LdapException
507 {
508 super.doDestroy( partitionTxn );
509
510 try
511 {
512 ldifFile.close();
513 }
514 catch ( IOException ioe )
515 {
516 throw new LdapOtherException( ioe.getMessage(), ioe );
517 }
518 }
519
520
521
522
523
524
525
526
527
528
529 public void setEnableRewriting( PartitionTxn partitionTxn, boolean enableRewriting ) throws LdapException
530 {
531 this.enableRewriting = enableRewriting;
532
533
534 rewritePartitionData( partitionTxn );
535 }
536 }