1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.partition;
18
19
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28
29 import javax.naming.ConfigurationException;
30 import javax.naming.Name;
31 import javax.naming.NameNotFoundException;
32 import javax.naming.NamingEnumeration;
33 import javax.naming.NamingException;
34 import javax.naming.directory.Attribute;
35 import javax.naming.directory.Attributes;
36 import javax.naming.directory.BasicAttribute;
37 import javax.naming.directory.BasicAttributes;
38 import javax.naming.directory.ModificationItem;
39 import javax.naming.directory.SearchControls;
40 import javax.naming.directory.SearchResult;
41 import javax.naming.ldap.LdapContext;
42
43 import org.apache.ldap.common.MultiException;
44 import org.apache.ldap.common.NotImplementedException;
45 import org.apache.ldap.common.exception.LdapNameNotFoundException;
46 import org.apache.ldap.common.filter.ExprNode;
47 import org.apache.ldap.common.filter.PresenceNode;
48 import org.apache.ldap.common.message.LockableAttributeImpl;
49 import org.apache.ldap.common.message.LockableAttributes;
50 import org.apache.ldap.common.message.LockableAttributesImpl;
51 import org.apache.ldap.common.name.LdapName;
52 import org.apache.ldap.common.util.DateUtils;
53 import org.apache.ldap.common.util.NamespaceTools;
54 import org.apache.ldap.common.util.SingletonEnumeration;
55 import org.apache.ldap.server.configuration.ContextPartitionConfiguration;
56 import org.apache.ldap.server.configuration.MutableContextPartitionConfiguration;
57 import org.apache.ldap.server.jndi.ContextFactoryConfiguration;
58 import org.apache.ldap.server.partition.impl.btree.jdbm.JdbmContextPartition;
59
60
61 /***
62 * A nexus for partitions dedicated for storing entries specific to a naming
63 * context.
64 *
65 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
66 * @version $Rev: 226451 $
67 */
68 public class DefaultContextPartitionNexus extends ContextPartitionNexus
69 {
70 /*** the vendorName string proudly set to: Apache Software Foundation*/
71 private static final String ASF = "Apache Software Foundation";
72
73 /*** the vendorName DSE operational attribute */
74 private static final String VENDORNAME_ATTR = "vendorName";
75
76 /*** the namingContexts DSE operational attribute */
77 private static final String NAMINGCTXS_ATTR = "namingContexts";
78
79 /*** the closed state of this partition */
80 private boolean initialized;
81
82 /*** the system backend */
83 private ContextPartition system;
84
85 /*** the backends keyed by normalized suffix strings */
86 private HashMap partitions = new HashMap();
87
88 /*** the read only rootDSE attributes */
89 private final Attributes rootDSE;
90
91
92 /***
93 * Creates the root nexus singleton of the entire system. The root DSE has
94 * several attributes that are injected into it besides those that may
95 * already exist. As partitions are added to the system more namingContexts
96 * attributes are added to the rootDSE.
97 *
98 * @see <a href="http://www.faqs.org/rfcs/rfc3045.html">Vendor Information</a>
99 */
100 public DefaultContextPartitionNexus( Attributes rootDSE )
101 {
102
103 this.rootDSE = rootDSE;
104 Attribute attr = new LockableAttributeImpl( "subschemaSubentry" );
105 attr.add( "cn=schema,ou=system" );
106 rootDSE.put( attr );
107
108 attr = new LockableAttributeImpl( "supportedLDAPVersion" );
109 rootDSE.put( attr );
110 attr.add( "3" );
111
112 attr = new LockableAttributeImpl( "objectClass" );
113 rootDSE.put( attr );
114 attr.add( "top" );
115 attr.add( "extensibleObject" );
116
117 attr = new LockableAttributeImpl( NAMINGCTXS_ATTR );
118 rootDSE.put( attr );
119
120 attr = new LockableAttributeImpl( VENDORNAME_ATTR );
121 attr.add( ASF );
122 rootDSE.put( attr );
123 }
124
125
126 public void init( ContextFactoryConfiguration factoryCfg, ContextPartitionConfiguration cfg ) throws NamingException
127 {
128
129 if( initialized )
130 {
131 return;
132 }
133
134 List initializedPartitions = new ArrayList();
135 initializeSystemPartition( factoryCfg );
136 initializedPartitions.add( system );
137
138 Iterator i = factoryCfg.getStartupConfiguration().getContextPartitionConfigurations().iterator();
139 try
140 {
141 while( i.hasNext() )
142 {
143 cfg = ( ContextPartitionConfiguration ) i.next();
144 ContextPartition partition = cfg.getContextPartition();
145
146
147 MutableContextPartitionConfiguration mcfg =
148 new MutableContextPartitionConfiguration();
149 mcfg.setName( cfg.getName() );
150 mcfg.setSuffix( cfg.getSuffix() );
151 mcfg.setContextEntry( cfg.getContextEntry() );
152 mcfg.setContextPartition( partition );
153
154 Set indexedAttrs = cfg.getIndexedAttributes();
155 indexedAttrs.add( Oid.ALIAS );
156 indexedAttrs.add( Oid.EXISTANCE );
157 indexedAttrs.add( Oid.HIERARCHY );
158 indexedAttrs.add( Oid.NDN );
159 indexedAttrs.add( Oid.ONEALIAS );
160 indexedAttrs.add( Oid.SUBALIAS );
161 indexedAttrs.add( Oid.UPDN );
162 mcfg.setIndexedAttributes( indexedAttrs );
163
164 partition.init( factoryCfg, mcfg );
165 initializedPartitions.add( 0, partition );
166 register( partition );
167 }
168 initialized = true;
169 }
170 finally
171 {
172 if( !initialized )
173 {
174 i = initializedPartitions.iterator();
175 while( i.hasNext() )
176 {
177 ContextPartition partition = ( ContextPartition ) i.next();
178 i.remove();
179 try
180 {
181 partition.destroy();
182 }
183 catch( Exception e )
184 {
185 e.printStackTrace();
186 }
187 finally
188 {
189 unregister( partition );
190 }
191 }
192 }
193 }
194 }
195
196
197 private void initializeSystemPartition( ContextFactoryConfiguration factoryCfg ) throws NamingException
198 {
199
200 MutableContextPartitionConfiguration systemCfg = new MutableContextPartitionConfiguration();
201 system = new JdbmContextPartition();
202 systemCfg.setName( "system" );
203 systemCfg.setSuffix( ContextPartitionNexus.SYSTEM_PARTITION_SUFFIX );
204 systemCfg.setContextPartition( system );
205
206
207 Set indexedSystemAttrs = new HashSet();
208 indexedSystemAttrs.add( Oid.ALIAS );
209 indexedSystemAttrs.add( Oid.EXISTANCE );
210 indexedSystemAttrs.add( Oid.HIERARCHY );
211 indexedSystemAttrs.add( Oid.NDN );
212 indexedSystemAttrs.add( Oid.ONEALIAS );
213 indexedSystemAttrs.add( Oid.SUBALIAS );
214 indexedSystemAttrs.add( Oid.UPDN );
215 systemCfg.setIndexedAttributes( indexedSystemAttrs );
216
217
218 Attributes systemEntry = new BasicAttributes();
219 Attribute objectClassAttr = new BasicAttribute( "objectClass" );
220 objectClassAttr.add( "top" );
221 objectClassAttr.add( "organizationalUnit" );
222 systemEntry.put( objectClassAttr );
223 systemEntry.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL ) ;
224 systemEntry.put( "createTimestamp", DateUtils.getGeneralizedTime() ) ;
225 systemEntry.put(
226 NamespaceTools.getRdnAttribute( ContextPartitionNexus.SYSTEM_PARTITION_SUFFIX ),
227 NamespaceTools.getRdnValue( ContextPartitionNexus.SYSTEM_PARTITION_SUFFIX ) ) ;
228 systemCfg.setContextEntry( systemEntry );
229
230 system.init( factoryCfg, systemCfg );
231 register( system );
232 }
233
234
235 public boolean isInitialized()
236 {
237 return initialized;
238 }
239
240
241 public synchronized void destroy()
242 {
243 if ( !initialized )
244 {
245 return;
246 }
247
248 MultiException error = null;
249
250 Iterator list = this.partitions.values().iterator();
251
252
253
254 while ( list.hasNext() )
255 {
256 ContextPartition partition = ( ContextPartition ) list.next();
257
258 try
259 {
260 partition.sync();
261 partition.destroy();
262 }
263 catch ( NamingException e )
264 {
265 e.printStackTrace();
266
267 if ( error == null )
268 {
269 error = new MultiException( "Grouping many exceptions on root nexus close()" );
270 }
271
272
273 error.addThrowable( e );
274 }
275 }
276
277 initialized = false;
278
279 if ( error != null )
280 {
281 String msg = "Encountered failures while performing a close() operation on backing stores";
282
283 NamingException total = new NamingException( msg );
284
285 total.setRootCause( error );
286 total.printStackTrace();
287 }
288 }
289
290
291 /***
292 * @see ContextPartition#sync()
293 */
294 public void sync() throws NamingException
295 {
296 MultiException error = null;
297
298 Iterator list = this.partitions.values().iterator();
299
300 while ( list.hasNext() )
301 {
302 ContextPartition store = ( ContextPartition ) list.next();
303
304 try
305 {
306 store.sync();
307 }
308 catch ( NamingException e )
309 {
310 e.printStackTrace();
311
312 if ( error == null )
313 {
314 error = new MultiException( "Grouping many exceptions on root nexus sync()" );
315 }
316
317
318 error.addThrowable( e );
319 }
320 }
321
322 if ( error != null )
323 {
324 String msg = "Encountered failures while performing a sync() operation on backing stores";
325
326 NamingException total = new NamingException( msg );
327
328 total.setRootCause( error );
329 }
330 }
331
332
333
334
335
336
337
338 public ContextPartition getSystemPartition()
339 {
340 return system;
341 }
342
343 /***
344 * @see ContextPartitionNexus#getLdapContext()
345 */
346 public LdapContext getLdapContext()
347 {
348 throw new NotImplementedException();
349 }
350
351
352 /***
353 * @see ContextPartitionNexus#getMatchedName(javax.naming.Name, boolean)
354 */
355 public Name getMatchedName( Name dn, boolean normalized ) throws NamingException
356 {
357 dn = ( Name ) dn.clone();
358
359 while ( dn.size() > 0 )
360 {
361 if ( hasEntry( dn ) )
362 {
363 return dn;
364 }
365
366 dn = dn.getSuffix( 1 );
367 }
368
369 return dn;
370 }
371
372
373 public Name getSuffix( boolean normalized )
374 {
375 return new LdapName();
376 }
377
378
379 /***
380 * @see org.apache.ldap.server.partition.ContextPartitionNexus#getSuffix(javax.naming.Name, boolean)
381 */
382 public Name getSuffix( Name dn, boolean normalized ) throws NamingException
383 {
384 ContextPartition backend = getBackend( dn );
385
386 return backend.getSuffix( normalized );
387 }
388
389
390 /***
391 * @see org.apache.ldap.server.partition.ContextPartitionNexus#listSuffixes(boolean)
392 */
393 public Iterator listSuffixes( boolean normalized ) throws NamingException
394 {
395 return Collections.unmodifiableSet( partitions.keySet() ).iterator();
396 }
397
398
399 public Attributes getRootDSE()
400 {
401 return rootDSE;
402 }
403
404
405 /***
406 * Registers an ContextPartition with this BackendManager. Called by each
407 * ContextPartition implementation after it has started to register for
408 * backend operation calls. This method effectively puts the
409 * ContextPartition's naming context online.
410 *
411 * Operations against the naming context should result in an LDAP BUSY
412 * result code in the returnValue if the naming context is not online.
413 *
414 * @param partition ContextPartition component to register with this
415 * BackendNexus.
416 * @throws ConfigurationException
417 */
418 private void register( ContextPartition partition ) throws NamingException
419 {
420 String key = partition.getSuffix( true ).toString();
421 if( partitions.containsKey( key ) )
422 {
423 throw new ConfigurationException( "Duplicate partition suffix: " + key );
424 }
425 partitions.put( key, partition );
426
427 Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
428 namingContexts.add( partition.getSuffix( false ).toString() );
429 }
430
431
432 /***
433 * Unregisters an ContextPartition with this BackendManager. Called for each
434 * registered Backend right befor it is to be stopped. This prevents
435 * protocol server requests from reaching the Backend and effectively puts
436 * the ContextPartition's naming context offline.
437 *
438 * Operations against the naming context should result in an LDAP BUSY
439 * result code in the returnValue if the naming context is not online.
440 *
441 * @param partition ContextPartition component to unregister with this
442 * BackendNexus.
443 */
444 private void unregister( ContextPartition partition ) throws NamingException
445 {
446 Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
447 namingContexts.remove( partition.getSuffix( false ).toString() );
448 partitions.remove( partition.getSuffix( true ).toString() );
449 }
450
451
452
453
454
455
456
457 /***
458 * @see ContextPartition#delete(javax.naming.Name)
459 */
460 public void delete( Name dn ) throws NamingException
461 {
462 ContextPartition backend = getBackend( dn );
463
464 backend.delete( dn );
465 }
466
467
468 /***
469 * Looks up the backend corresponding to the entry first, then checks to
470 * see if the entry already exists. If so an exception is thrown. If not
471 * the add operation against the backend proceeds. This check is performed
472 * here so backend implementors do not have to worry about performing these
473 * kinds of checks.
474 *
475 * @see org.apache.ldap.server.partition.ContextPartition#add(String, Name, Attributes)
476 */
477 public void add( String updn, Name dn, Attributes an_entry ) throws NamingException
478 {
479 ContextPartition backend = getBackend( dn );
480
481 backend.add( updn, dn, an_entry );
482 }
483
484
485 /***
486 * @see ContextPartition#modify(Name, int,Attributes)
487 */
488 public void modify( Name dn, int modOp, Attributes mods ) throws NamingException
489 {
490 ContextPartition backend = getBackend( dn );
491
492 backend.modify( dn, modOp, mods );
493 }
494
495
496 /***
497 * @see ContextPartition#modify(javax.naming.Name,
498 * javax.naming.directory.ModificationItem[])
499 */
500 public void modify( Name dn, ModificationItem[] mods ) throws NamingException
501 {
502 ContextPartition backend = getBackend( dn );
503
504 backend.modify( dn, mods );
505 }
506
507
508 /***
509 * @see ContextPartition#list(javax.naming.Name)
510 */
511 public NamingEnumeration list( Name base ) throws NamingException
512 {
513 ContextPartition backend = getBackend( base );
514
515 return backend.list( base );
516 }
517
518
519 /***
520 * @see ContextPartition#search(Name, Map, ExprNode, SearchControls)
521 */
522 public NamingEnumeration search( Name base, Map env, ExprNode filter, SearchControls searchCtls )
523 throws NamingException
524 {
525
526 if ( base.size() == 0 )
527 {
528 boolean isObjectScope = searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE;
529
530 boolean isSearchAll = ( ( PresenceNode ) filter ).getAttribute().equalsIgnoreCase( "objectclass" );
531
532
533
534
535
536 if ( filter instanceof PresenceNode && isObjectScope && isSearchAll )
537 {
538 Attributes attrs = ( Attributes ) getRootDSE().clone();
539
540 String[] ids = searchCtls.getReturningAttributes();
541
542 if ( ids != null && ids.length > 0 )
543 {
544 boolean doSwap = true;
545
546 Attributes askedFor = new LockableAttributesImpl();
547
548 for ( int ii = 0; ii < ids.length; ii++ )
549 {
550 if ( ids[ii].trim().equals( "*" ) )
551 {
552 doSwap = false;
553
554 break;
555 }
556
557 if ( attrs.get( ids[ii] ) != null )
558 {
559 askedFor.put( attrs.get( ids[ii] ) );
560 }
561 }
562
563 if ( doSwap )
564 {
565 attrs = askedFor;
566 }
567 }
568
569 SearchResult result = new SearchResult( "", null, attrs, false );
570
571 return new SingletonEnumeration( result );
572 }
573
574 throw new LdapNameNotFoundException();
575 }
576
577 ContextPartition backend = getBackend( base );
578
579 return backend.search( base, env, filter, searchCtls );
580 }
581
582
583 /***
584 * @see ContextPartition#lookup(javax.naming.Name)
585 */
586 public Attributes lookup( Name dn ) throws NamingException
587 {
588 if ( dn.size() == 0 )
589 {
590 LockableAttributes retval = ( LockableAttributes ) rootDSE.clone();
591
592 retval.setLocked( true );
593
594 return retval;
595 }
596
597 ContextPartition backend = getBackend( dn );
598
599 return backend.lookup( dn );
600 }
601
602
603 /***
604 * @see org.apache.ldap.server.partition.ContextPartition#lookup(javax.naming.Name, String[])
605 */
606 public Attributes lookup( Name dn, String[] attrIds ) throws NamingException
607 {
608 if ( dn.size() == 0 )
609 {
610 LockableAttributes retval = new LockableAttributesImpl();
611
612 NamingEnumeration list = rootDSE.getIDs();
613
614 while ( list.hasMore() )
615 {
616 String id = ( String ) list.next();
617
618 Attribute attr = rootDSE.get( id );
619
620 retval.put( ( Attribute ) attr.clone() );
621 }
622
623 retval.setLocked( true );
624
625 return retval;
626 }
627
628 ContextPartition backend = getBackend( dn );
629
630 return backend.lookup( dn, attrIds );
631 }
632
633
634 /***
635 * @see ContextPartition#hasEntry(javax.naming.Name)
636 */
637 public boolean hasEntry( Name dn ) throws NamingException
638 {
639 if ( dn.size() == 0 )
640 {
641 return true;
642 }
643
644 ContextPartition backend = getBackend( dn );
645
646 return backend.hasEntry( dn );
647 }
648
649
650 /***
651 * @see ContextPartition#isSuffix(javax.naming.Name)
652 */
653 public boolean isSuffix( Name dn )
654 {
655 return partitions.containsKey( dn.toString() );
656 }
657
658
659 /***
660 * @see ContextPartition#modifyRn(Name, String, boolean)
661 */
662 public void modifyRn( Name dn, String newRdn, boolean deleteOldRdn ) throws NamingException
663 {
664 ContextPartition backend = getBackend( dn );
665
666 backend.modifyRn( dn, newRdn, deleteOldRdn );
667 }
668
669
670 /***
671 * @see ContextPartition#move(Name, Name)
672 */
673 public void move( Name oriChildName, Name newParentName ) throws NamingException
674 {
675 ContextPartition backend = getBackend( oriChildName );
676
677 backend.move( oriChildName, newParentName );
678 }
679
680
681 /***
682 * @see ContextPartition#move(javax.naming.Name,
683 * javax.naming.Name, java.lang.String, boolean)
684 */
685 public void move( Name oldChildDn, Name newParentDn, String newRdn,
686 boolean deleteOldRdn ) throws NamingException
687 {
688 ContextPartition backend = getBackend( oldChildDn );
689
690 backend.move( oldChildDn, newParentDn, newRdn, deleteOldRdn );
691 }
692
693
694
695
696
697
698
699 /***
700 * Gets the backend partition associated with a normalized dn.
701 *
702 * @param dn the normalized distinguished name to resolve to a backend
703 * @return the backend partition associated with the normalized dn
704 * @throws NamingException if the name cannot be resolved to a backend
705 */
706 private ContextPartition getBackend( Name dn ) throws NamingException
707 {
708 Name clonedDn = ( Name ) dn.clone();
709
710 while ( clonedDn.size() > 0 )
711 {
712 if ( partitions.containsKey( clonedDn.toString() ) )
713 {
714 return ( ContextPartition ) partitions.get( clonedDn.toString() );
715 }
716
717 clonedDn.remove( clonedDn.size() - 1 );
718 }
719
720 throw new NameNotFoundException( dn.toString() );
721 }
722 }