1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.core.api.schema;
21
22
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import org.apache.directory.api.ldap.model.constants.SchemaConstants;
27 import org.apache.directory.api.ldap.model.entry.DefaultAttribute;
28 import org.apache.directory.api.ldap.model.entry.DefaultModification;
29 import org.apache.directory.api.ldap.model.entry.Entry;
30 import org.apache.directory.api.ldap.model.entry.Modification;
31 import org.apache.directory.api.ldap.model.entry.ModificationOperation;
32 import org.apache.directory.api.ldap.model.exception.LdapContextNotEmptyException;
33 import org.apache.directory.api.ldap.model.exception.LdapException;
34 import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
35 import org.apache.directory.api.ldap.model.filter.ExprNode;
36 import org.apache.directory.api.ldap.model.filter.PresenceNode;
37 import org.apache.directory.api.ldap.model.message.SearchRequest;
38 import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
39 import org.apache.directory.api.ldap.model.message.SearchScope;
40 import org.apache.directory.api.ldap.model.message.controls.Cascade;
41 import org.apache.directory.api.ldap.model.name.Dn;
42 import org.apache.directory.api.ldap.model.schema.AttributeType;
43 import org.apache.directory.api.ldap.model.schema.SchemaManager;
44 import org.apache.directory.api.ldap.model.schema.SchemaUtils;
45 import org.apache.directory.api.util.DateUtils;
46 import org.apache.directory.server.constants.ApacheSchemaConstants;
47 import org.apache.directory.server.core.api.CoreSession;
48 import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
49 import org.apache.directory.server.core.api.interceptor.context.AddOperationContext;
50 import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext;
51 import org.apache.directory.server.core.api.interceptor.context.HasEntryOperationContext;
52 import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext;
53 import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext;
54 import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext;
55 import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext;
56 import org.apache.directory.server.core.api.interceptor.context.OperationContext;
57 import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext;
58 import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
59 import org.apache.directory.server.core.api.interceptor.context.UnbindOperationContext;
60 import org.apache.directory.server.core.api.partition.AbstractPartition;
61 import org.apache.directory.server.core.api.partition.Partition;
62 import org.apache.directory.server.core.api.partition.PartitionReadTxn;
63 import org.apache.directory.server.core.api.partition.PartitionTxn;
64 import org.apache.directory.server.core.api.partition.PartitionWriteTxn;
65 import org.apache.directory.server.core.api.partition.Subordinates;
66 import org.apache.directory.server.core.api.schema.registries.synchronizers.RegistrySynchronizerAdaptor;
67 import org.apache.directory.server.i18n.I18n;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 public final class SchemaPartition extends AbstractPartition
113 {
114
115 private static final Logger LOG = LoggerFactory.getLogger( SchemaPartition.class );
116
117
118 private static final String SCHEMA_ID = "schema";
119
120
121 private Partition wrapped;
122
123
124 private RegistrySynchronizerAdaptor synchronizer;
125
126
127 private Dn schemaModificationDN;
128
129
130 private Dn schemaDN;
131
132
133 private AttributeType objectClassAT;
134
135
136 public SchemaPartition( SchemaManager schemaManager )
137 {
138 try
139 {
140 schemaDN = new Dn( schemaManager, SchemaConstants.OU_SCHEMA );
141 }
142 catch ( LdapInvalidDnException lide )
143 {
144
145 }
146
147 id = SCHEMA_ID;
148 suffixDn = schemaDN;
149 this.schemaManager = schemaManager;
150 objectClassAT = schemaManager.getAttributeType( SchemaConstants.OBJECT_CLASS_AT_OID );
151 }
152
153
154
155
156
157
158
159
160 public void setWrappedPartition( Partition wrapped )
161 {
162 if ( this.isInitialized() )
163 {
164 throw new IllegalStateException( I18n.err( I18n.ERR_429 ) );
165 }
166
167 this.wrapped = wrapped;
168 }
169
170
171
172
173
174
175
176 public Partition getWrappedPartition()
177 {
178 return wrapped;
179 }
180
181
182
183
184
185
186 @Override
187 public void setId( String id )
188 {
189 LOG.warn( "This partition's ID is fixed: {}", SCHEMA_ID );
190 }
191
192
193
194
195
196
197
198
199
200 @Override
201 public void sync() throws LdapException
202 {
203 wrapped.sync();
204 }
205
206
207
208
209
210 @Override
211 protected void doRepair() throws LdapException
212 {
213
214 }
215
216
217
218
219
220 @Override
221 protected void doInit() throws LdapException
222 {
223 if ( !initialized )
224 {
225
226
227
228
229 wrapped.setId( SCHEMA_ID );
230 wrapped.setSuffixDn( schemaDN );
231 wrapped.setSchemaManager( schemaManager );
232
233 try
234 {
235
236
237 schemaManager.setRelaxed();
238
239
240 wrapped.initialize();
241
242
243 schemaManager.setStrict();
244
245 synchronizer = new RegistrySynchronizerAdaptor( schemaManager );
246 }
247 catch ( Exception e )
248 {
249 LOG.error( I18n.err( I18n.ERR_90 ), e );
250 throw new RuntimeException( e );
251 }
252
253 schemaModificationDN = new Dn( schemaManager, SchemaConstants.SCHEMA_MODIFICATIONS_DN );
254 }
255 }
256
257
258
259
260
261 @Override
262 protected void doDestroy( PartitionTxn partitionTxn )
263 {
264 try
265 {
266 wrapped.destroy( partitionTxn );
267 }
268 catch ( Exception e )
269 {
270 LOG.error( I18n.err( I18n.ERR_91 ), e );
271 throw new RuntimeException( e );
272 }
273
274 initialized = false;
275 }
276
277
278
279
280
281
282
283
284
285 public void add( AddOperationContext addContext ) throws LdapException
286 {
287
288
289
290 synchronizer.add( addContext );
291
292
293 try
294 {
295 wrapped.add( addContext );
296 }
297 catch ( LdapException e )
298 {
299
300
301
302 throw e;
303 }
304
305 updateSchemaModificationAttributes( addContext );
306 }
307
308
309
310
311
312 public int getChildCount( DeleteOperationContext deleteContext ) throws LdapException
313 {
314 try
315 {
316 Dn dn = deleteContext.getDn();
317 SearchRequest searchRequest = new SearchRequestImpl();
318 searchRequest.setBase( dn );
319 ExprNode node = new PresenceNode( objectClassAT );
320 searchRequest.setFilter( node );
321 searchRequest.setTypesOnly( true );
322 searchRequest.setScope( SearchScope.ONELEVEL );
323
324 SearchOperationContextceptor/context/SearchOperationContext.html#SearchOperationContext">SearchOperationContext searchContext = new SearchOperationContext( deleteContext.getSession(),
325 searchRequest );
326 searchContext.setPartition( this );
327 searchContext.setTransaction( deleteContext.getTransaction() );
328
329 EntryFilteringCursor cursor = wrapped.search( searchContext );
330
331 cursor.beforeFirst();
332 int nbEntry = 0;
333
334 while ( cursor.next() && ( nbEntry < 2 ) )
335 {
336 nbEntry++;
337 }
338
339 cursor.close();
340
341 return nbEntry;
342 }
343 catch ( Exception e )
344 {
345 LOG.warn( "Failed to get the children of entry {}", deleteContext.getDn() );
346 return 0;
347 }
348 }
349
350
351
352
353
354 public Entry delete( DeleteOperationContext deleteContext ) throws LdapException
355 {
356 boolean cascade = deleteContext.hasRequestControl( Cascade.OID );
357
358
359 int nbChild = getChildCount( deleteContext );
360
361 if ( nbChild > 1 )
362 {
363 throw new LdapContextNotEmptyException( "There are children under the entry " + deleteContext.getDn() );
364 }
365
366
367 synchronizer.delete( deleteContext, cascade );
368 Entry deletedEntry = null;
369
370 try
371 {
372 deletedEntry = wrapped.delete( deleteContext );
373 }
374 catch ( LdapException e )
375 {
376
377 throw e;
378 }
379
380 updateSchemaModificationAttributes( deleteContext );
381
382 return deletedEntry;
383 }
384
385
386
387
388
389 public boolean hasEntry( HasEntryOperationContext hasEntryContext ) throws LdapException
390 {
391 return wrapped.hasEntry( hasEntryContext );
392 }
393
394
395
396
397
398 public void modify( ModifyOperationContext modifyContext ) throws LdapException
399 {
400 Entry entry = modifyContext.getEntry();
401
402 if ( entry == null )
403 {
404 LookupOperationContextnterceptor/context/LookupOperationContext.html#LookupOperationContext">LookupOperationContext lookupCtx = new LookupOperationContext( modifyContext.getSession(),
405 modifyContext.getDn() );
406 lookupCtx.setPartition( this );
407 lookupCtx.setTransaction( modifyContext.getTransaction() );
408 entry = wrapped.lookup( lookupCtx );
409 modifyContext.setEntry( entry );
410 }
411
412 Entry targetEntry = SchemaUtils.getTargetEntry( modifyContext.getModItems(), entry );
413
414 boolean cascade = modifyContext.hasRequestControl( Cascade.OID );
415
416 boolean hasModification = synchronizer.modify( modifyContext, targetEntry, cascade );
417
418 if ( hasModification )
419 {
420 wrapped.modify( modifyContext );
421 }
422
423 if ( !modifyContext.getDn().equals( schemaModificationDN ) )
424 {
425 updateSchemaModificationAttributes( modifyContext );
426 }
427 }
428
429
430
431
432
433 public void move( MoveOperationContext moveContext ) throws LdapException
434 {
435 boolean cascade = moveContext.hasRequestControl( Cascade.OID );
436
437 CoreSession session = moveContext.getSession();
438 LookupOperationContextceptor/context/LookupOperationContext.html#LookupOperationContext">LookupOperationContext lookupContext = new LookupOperationContext( session, moveContext.getDn(),
439 SchemaConstants.ALL_ATTRIBUTES_ARRAY );
440 lookupContext.setPartition( this );
441 lookupContext.setTransaction( moveContext.getTransaction() );
442
443 Entry entry = session.getDirectoryService().getPartitionNexus().lookup( lookupContext );
444 synchronizer.move( moveContext, entry, cascade );
445 wrapped.move( moveContext );
446 updateSchemaModificationAttributes( moveContext );
447 }
448
449
450
451
452
453 public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws LdapException
454 {
455 boolean cascade = moveAndRenameContext.hasRequestControl( Cascade.OID );
456 CoreSession session = moveAndRenameContext.getSession();
457 LookupOperationContextceptor/context/LookupOperationContext.html#LookupOperationContext">LookupOperationContext lookupContext = new LookupOperationContext( session, moveAndRenameContext.getDn(),
458 SchemaConstants.ALL_ATTRIBUTES_ARRAY );
459 lookupContext.setPartition( this );
460 lookupContext.setTransaction( moveAndRenameContext.getTransaction() );
461
462 Entry entry = session.getDirectoryService().getPartitionNexus().lookup( lookupContext );
463 synchronizer.moveAndRename( moveAndRenameContext, entry, cascade );
464 wrapped.moveAndRename( moveAndRenameContext );
465 updateSchemaModificationAttributes( moveAndRenameContext );
466 }
467
468
469
470
471
472 public void rename( RenameOperationContext renameContext ) throws LdapException
473 {
474 boolean cascade = renameContext.hasRequestControl( Cascade.OID );
475
476
477 synchronizer.rename( renameContext, cascade );
478
479
480 wrapped.rename( renameContext );
481
482
483 updateSchemaModificationAttributes( renameContext );
484 }
485
486
487
488
489
490 public EntryFilteringCursor search( SearchOperationContext searchContext ) throws LdapException
491 {
492 return wrapped.search( searchContext );
493 }
494
495
496
497
498
499 public void unbind( UnbindOperationContext unbindContext ) throws LdapException
500 {
501 wrapped.unbind( unbindContext );
502 }
503
504
505
506
507
508 public Entry lookup( LookupOperationContext lookupContext ) throws LdapException
509 {
510 return wrapped.lookup( lookupContext );
511 }
512
513
514
515
516
517
518
519
520
521
522
523
524
525 private void updateSchemaModificationAttributes( OperationContext opContext ) throws LdapException
526 {
527 String modifiersName = opContext.getSession().getEffectivePrincipal().getName();
528 String modifyTimestamp = DateUtils.getGeneralizedTime( opContext.getSession().getDirectoryService().getTimeProvider() );
529
530 List<Modification> mods = new ArrayList<>( 2 );
531
532 mods.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, new DefaultAttribute(
533 ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT, schemaManager
534 .lookupAttributeTypeRegistry( ApacheSchemaConstants.SCHEMA_MODIFY_TIMESTAMP_AT ), modifyTimestamp ) ) );
535
536 mods.add( new DefaultModification( ModificationOperation.REPLACE_ATTRIBUTE, new DefaultAttribute(
537 ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT, schemaManager
538 .lookupAttributeTypeRegistry( ApacheSchemaConstants.SCHEMA_MODIFIERS_NAME_AT ), modifiersName ) ) );
539
540
541
542
543 CoreSession session = opContext.getSession();
544 ModifyOperationContextceptor/context/ModifyOperationContext.html#ModifyOperationContext">ModifyOperationContext modifyContext = new ModifyOperationContext( session, schemaModificationDN, mods );
545 modifyContext.setPartition( this );
546 modifyContext.setTransaction( opContext.getTransaction() );
547
548 session.getDirectoryService().getPartitionNexus().modify( modifyContext );
549 }
550
551
552 @Override
553 public String getContextCsn( PartitionTxn partitionTxn )
554 {
555 return wrapped.getContextCsn( partitionTxn );
556 }
557
558
559 @Override
560 public void saveContextCsn( PartitionTxn partitionTxn ) throws LdapException
561 {
562 wrapped.saveContextCsn( partitionTxn );
563 }
564
565
566
567
568
569 public String toString()
570 {
571 return "Partition : " + SCHEMA_ID;
572 }
573
574
575
576
577
578
579
580
581
582
583 public Subordinates getSubordinates( PartitionTxn partitionTxn, Entry entry ) throws LdapException
584 {
585 return new Subordinates();
586 }
587
588
589 @Override
590 public PartitionReadTxn beginReadTransaction()
591 {
592 return new PartitionReadTxn();
593 }
594
595
596 @Override
597 public PartitionWriteTxn beginWriteTransaction()
598 {
599 return new PartitionWriteTxn();
600 }
601 }