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: 264732 $
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 vendorVersion DSE operational attribute */
77 private static final String VENDORVERSION_ATTR = "vendorVersion";
78
79 /*** the namingContexts DSE operational attribute */
80 private static final String NAMINGCTXS_ATTR = "namingContexts";
81
82 /*** the closed state of this partition */
83 private boolean initialized;
84
85 private ContextFactoryConfiguration factoryCfg;
86
87 /*** the system backend */
88 private ContextPartition system;
89
90 /*** the backends keyed by normalized suffix strings */
91 private HashMap partitions = new HashMap();
92
93 /*** the read only rootDSE attributes */
94 private final Attributes rootDSE;
95
96
97 /***
98 * Creates the root nexus singleton of the entire system. The root DSE has
99 * several attributes that are injected into it besides those that may
100 * already exist. As partitions are added to the system more namingContexts
101 * attributes are added to the rootDSE.
102 *
103 * @see <a href="http://www.faqs.org/rfcs/rfc3045.html">Vendor Information</a>
104 */
105 public DefaultContextPartitionNexus( Attributes rootDSE )
106 {
107
108 this.rootDSE = rootDSE;
109 Attribute attr = new LockableAttributeImpl( "subschemaSubentry" );
110 attr.add( "cn=schema,ou=system" );
111 rootDSE.put( attr );
112
113 attr = new LockableAttributeImpl( "supportedLDAPVersion" );
114 rootDSE.put( attr );
115 attr.add( "3" );
116
117 attr = new LockableAttributeImpl( "supportedFeatures" );
118 rootDSE.put( attr );
119 attr.add( "1.3.6.1.4.1.4203.1.5.1" );
120
121 attr = new LockableAttributeImpl( "objectClass" );
122 rootDSE.put( attr );
123 attr.add( "top" );
124 attr.add( "extensibleObject" );
125
126 attr = new LockableAttributeImpl( NAMINGCTXS_ATTR );
127 rootDSE.put( attr );
128
129 attr = new LockableAttributeImpl( VENDORNAME_ATTR );
130 attr.add( ASF );
131 rootDSE.put( attr );
132
133 attr = new LockableAttributeImpl( VENDORVERSION_ATTR );
134 attr.add( "$Rev: 264732 $" );
135 rootDSE.put( attr );
136 }
137
138
139 public void init( ContextFactoryConfiguration factoryCfg, ContextPartitionConfiguration cfg ) throws NamingException
140 {
141
142 if( initialized )
143 {
144 return;
145 }
146
147 this.factoryCfg = factoryCfg;
148
149 List initializedPartitions = new ArrayList();
150 initializeSystemPartition();
151 initializedPartitions.add( system );
152
153 Iterator i = factoryCfg.getStartupConfiguration().getContextPartitionConfigurations().iterator();
154 try
155 {
156 while( i.hasNext() )
157 {
158 ContextPartitionConfiguration c = ( ContextPartitionConfiguration ) i.next();
159 addContextPartition( c );
160 initializedPartitions.add( 0, c.getContextPartition() );
161 }
162 initialized = true;
163 }
164 finally
165 {
166 if( !initialized )
167 {
168 i = initializedPartitions.iterator();
169 while( i.hasNext() )
170 {
171 ContextPartition partition = ( ContextPartition ) i.next();
172 i.remove();
173 try
174 {
175 partition.destroy();
176 }
177 catch( Exception e )
178 {
179 e.printStackTrace();
180 }
181 finally
182 {
183 unregister( partition );
184 }
185 }
186 }
187 }
188 }
189
190
191 private void initializeSystemPartition() throws NamingException
192 {
193
194 MutableContextPartitionConfiguration systemCfg = new MutableContextPartitionConfiguration();
195 system = new JdbmContextPartition();
196 systemCfg.setName( "system" );
197 systemCfg.setSuffix( ContextPartitionNexus.SYSTEM_PARTITION_SUFFIX );
198 systemCfg.setContextPartition( system );
199
200
201 Set indexedSystemAttrs = new HashSet();
202 indexedSystemAttrs.add( Oid.ALIAS );
203 indexedSystemAttrs.add( Oid.EXISTANCE );
204 indexedSystemAttrs.add( Oid.HIERARCHY );
205 indexedSystemAttrs.add( Oid.NDN );
206 indexedSystemAttrs.add( Oid.ONEALIAS );
207 indexedSystemAttrs.add( Oid.SUBALIAS );
208 indexedSystemAttrs.add( Oid.UPDN );
209 systemCfg.setIndexedAttributes( indexedSystemAttrs );
210
211
212 Attributes systemEntry = new BasicAttributes( true );
213 Attribute objectClassAttr = new BasicAttribute( "objectClass" );
214 objectClassAttr.add( "top" );
215 objectClassAttr.add( "organizationalUnit" );
216 systemEntry.put( objectClassAttr );
217 systemEntry.put( "creatorsName", ContextPartitionNexus.ADMIN_PRINCIPAL ) ;
218 systemEntry.put( "createTimestamp", DateUtils.getGeneralizedTime() ) ;
219 systemEntry.put(
220 NamespaceTools.getRdnAttribute( ContextPartitionNexus.SYSTEM_PARTITION_SUFFIX ),
221 NamespaceTools.getRdnValue( ContextPartitionNexus.SYSTEM_PARTITION_SUFFIX ) ) ;
222 systemCfg.setContextEntry( systemEntry );
223
224 system.init( factoryCfg, systemCfg );
225 String key = system.getSuffix( true ).toString();
226 if( partitions.containsKey( key ) )
227 {
228 throw new ConfigurationException( "Duplicate partition suffix: " + key );
229 }
230 partitions.put( key, system );
231
232 Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
233 namingContexts.add( system.getSuffix( false ).toString() );
234 }
235
236
237 public boolean isInitialized()
238 {
239 return initialized;
240 }
241
242
243 public synchronized void destroy()
244 {
245 if ( !initialized )
246 {
247 return;
248 }
249
250 MultiException error = null;
251
252 Iterator suffixes = new HashSet( this.partitions.keySet() ).iterator();
253
254
255
256 while ( suffixes.hasNext() )
257 {
258 String suffix = (String) suffixes.next();
259 try
260 {
261 removeContextPartition( new LdapName( suffix ) );
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
339 public synchronized void addContextPartition( ContextPartitionConfiguration config ) throws NamingException
340 {
341 ContextPartition partition = config.getContextPartition();
342
343
344 MutableContextPartitionConfiguration mcfg =
345 new MutableContextPartitionConfiguration();
346 mcfg.setName( config.getName() );
347 mcfg.setSuffix( config.getSuffix() );
348 mcfg.setContextEntry( config.getContextEntry() );
349 mcfg.setContextPartition( partition );
350
351 Set indexedAttrs = config.getIndexedAttributes();
352 indexedAttrs.add( Oid.ALIAS );
353 indexedAttrs.add( Oid.EXISTANCE );
354 indexedAttrs.add( Oid.HIERARCHY );
355 indexedAttrs.add( Oid.NDN );
356 indexedAttrs.add( Oid.ONEALIAS );
357 indexedAttrs.add( Oid.SUBALIAS );
358 indexedAttrs.add( Oid.UPDN );
359 mcfg.setIndexedAttributes( indexedAttrs );
360
361 String key = config.getSuffix();
362 if( partitions.containsKey( key ) )
363 {
364 throw new ConfigurationException( "Duplicate partition suffix: " + key );
365 }
366
367 partition.init( factoryCfg, mcfg );
368 partitions.put( partition.getSuffix( true ).toString(), partition );
369
370 Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
371 namingContexts.add( partition.getSuffix( false ).toString() );
372 }
373
374
375 public synchronized void removeContextPartition( Name suffix ) throws NamingException
376 {
377 String key = suffix.toString();
378 ContextPartition partition = (ContextPartition) partitions.get( key );
379 if( partition == null )
380 {
381 throw new NameNotFoundException( "No partition with suffix: " + key );
382 }
383
384 Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
385 namingContexts.remove( partition.getSuffix( false ).toString() );
386 partitions.remove( key );
387
388 partition.sync();
389 partition.destroy();
390 }
391
392 public ContextPartition getSystemPartition()
393 {
394 return system;
395 }
396
397 /***
398 * @see ContextPartitionNexus#getLdapContext()
399 */
400 public LdapContext getLdapContext()
401 {
402 throw new NotImplementedException();
403 }
404
405
406 /***
407 * @see ContextPartitionNexus#getMatchedName(javax.naming.Name, boolean)
408 */
409 public Name getMatchedName( Name dn, boolean normalized ) throws NamingException
410 {
411 dn = ( Name ) dn.clone();
412
413 while ( dn.size() > 0 )
414 {
415 if ( hasEntry( dn ) )
416 {
417 return dn;
418 }
419
420 dn = dn.getSuffix( 1 );
421 }
422
423 return dn;
424 }
425
426
427 public Name getSuffix( boolean normalized )
428 {
429 return new LdapName();
430 }
431
432
433 /***
434 * @see org.apache.ldap.server.partition.ContextPartitionNexus#getSuffix(javax.naming.Name, boolean)
435 */
436 public Name getSuffix( Name dn, boolean normalized ) throws NamingException
437 {
438 ContextPartition backend = getBackend( dn );
439
440 return backend.getSuffix( normalized );
441 }
442
443
444 /***
445 * @see org.apache.ldap.server.partition.ContextPartitionNexus#listSuffixes(boolean)
446 */
447 public Iterator listSuffixes( boolean normalized ) throws NamingException
448 {
449 return Collections.unmodifiableSet( partitions.keySet() ).iterator();
450 }
451
452
453 public Attributes getRootDSE()
454 {
455 return rootDSE;
456 }
457
458
459 /***
460 * Unregisters an ContextPartition with this BackendManager. Called for each
461 * registered Backend right befor it is to be stopped. This prevents
462 * protocol server requests from reaching the Backend and effectively puts
463 * the ContextPartition's naming context offline.
464 *
465 * Operations against the naming context should result in an LDAP BUSY
466 * result code in the returnValue if the naming context is not online.
467 *
468 * @param partition ContextPartition component to unregister with this
469 * BackendNexus.
470 */
471 private void unregister( ContextPartition partition ) throws NamingException
472 {
473 Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
474 namingContexts.remove( partition.getSuffix( false ).toString() );
475 partitions.remove( partition.getSuffix( true ).toString() );
476 }
477
478
479
480
481
482
483
484 /***
485 * @see ContextPartition#delete(javax.naming.Name)
486 */
487 public void delete( Name dn ) throws NamingException
488 {
489 ContextPartition backend = getBackend( dn );
490
491 backend.delete( dn );
492 }
493
494
495 /***
496 * Looks up the backend corresponding to the entry first, then checks to
497 * see if the entry already exists. If so an exception is thrown. If not
498 * the add operation against the backend proceeds. This check is performed
499 * here so backend implementors do not have to worry about performing these
500 * kinds of checks.
501 *
502 * @see org.apache.ldap.server.partition.ContextPartition#add(String, Name, Attributes)
503 */
504 public void add( String updn, Name dn, Attributes an_entry ) throws NamingException
505 {
506 ContextPartition backend = getBackend( dn );
507
508 backend.add( updn, dn, an_entry );
509 }
510
511
512 /***
513 * @see ContextPartition#modify(Name, int,Attributes)
514 */
515 public void modify( Name dn, int modOp, Attributes mods ) throws NamingException
516 {
517 ContextPartition backend = getBackend( dn );
518
519 backend.modify( dn, modOp, mods );
520 }
521
522
523 /***
524 * @see ContextPartition#modify(javax.naming.Name,
525 * javax.naming.directory.ModificationItem[])
526 */
527 public void modify( Name dn, ModificationItem[] mods ) throws NamingException
528 {
529 ContextPartition backend = getBackend( dn );
530
531 backend.modify( dn, mods );
532 }
533
534
535 /***
536 * @see ContextPartition#list(javax.naming.Name)
537 */
538 public NamingEnumeration list( Name base ) throws NamingException
539 {
540 ContextPartition backend = getBackend( base );
541
542 return backend.list( base );
543 }
544
545
546 /***
547 * @see ContextPartition#search(Name, Map, ExprNode, SearchControls)
548 */
549 public NamingEnumeration search( Name base, Map env, ExprNode filter, SearchControls searchCtls )
550 throws NamingException
551 {
552
553 if ( base.size() == 0 )
554 {
555 boolean isObjectScope = searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE;
556
557 boolean isSearchAll = ( ( PresenceNode ) filter ).getAttribute().equalsIgnoreCase( "objectclass" );
558
559
560
561
562
563 if ( filter instanceof PresenceNode && isObjectScope && isSearchAll )
564 {
565 Attributes attrs = ( Attributes ) getRootDSE().clone();
566
567 String[] ids = searchCtls.getReturningAttributes();
568
569 if ( ids != null && ids.length > 0 )
570 {
571 boolean doSwap = true;
572
573 Attributes askedFor = new LockableAttributesImpl();
574
575 for ( int ii = 0; ii < ids.length; ii++ )
576 {
577 if ( ids[ii].trim().equals( "*" ) )
578 {
579 doSwap = false;
580
581 break;
582 }
583
584 if ( attrs.get( ids[ii] ) != null )
585 {
586 askedFor.put( attrs.get( ids[ii] ) );
587 }
588 }
589
590 if ( doSwap )
591 {
592 attrs = askedFor;
593 }
594 }
595
596 SearchResult result = new SearchResult( "", null, attrs, false );
597
598 return new SingletonEnumeration( result );
599 }
600
601 throw new LdapNameNotFoundException();
602 }
603
604 ContextPartition backend = getBackend( base );
605
606 return backend.search( base, env, filter, searchCtls );
607 }
608
609
610 /***
611 * @see ContextPartition#lookup(javax.naming.Name)
612 */
613 public Attributes lookup( Name dn ) throws NamingException
614 {
615 if ( dn.size() == 0 )
616 {
617 LockableAttributes retval = ( LockableAttributes ) rootDSE.clone();
618
619 retval.setLocked( true );
620
621 return retval;
622 }
623
624 ContextPartition backend = getBackend( dn );
625
626 return backend.lookup( dn );
627 }
628
629
630 /***
631 * @see org.apache.ldap.server.partition.ContextPartition#lookup(javax.naming.Name, String[])
632 */
633 public Attributes lookup( Name dn, String[] attrIds ) throws NamingException
634 {
635 if ( dn.size() == 0 )
636 {
637 LockableAttributes retval = new LockableAttributesImpl();
638
639 NamingEnumeration list = rootDSE.getIDs();
640
641 while ( list.hasMore() )
642 {
643 String id = ( String ) list.next();
644
645 Attribute attr = rootDSE.get( id );
646
647 retval.put( ( Attribute ) attr.clone() );
648 }
649
650 retval.setLocked( true );
651
652 return retval;
653 }
654
655 ContextPartition backend = getBackend( dn );
656
657 return backend.lookup( dn, attrIds );
658 }
659
660
661 /***
662 * @see ContextPartition#hasEntry(javax.naming.Name)
663 */
664 public boolean hasEntry( Name dn ) throws NamingException
665 {
666 if ( dn.size() == 0 )
667 {
668 return true;
669 }
670
671 ContextPartition backend = getBackend( dn );
672
673 return backend.hasEntry( dn );
674 }
675
676
677 /***
678 * @see ContextPartition#isSuffix(javax.naming.Name)
679 */
680 public boolean isSuffix( Name dn )
681 {
682 return partitions.containsKey( dn.toString() );
683 }
684
685
686 /***
687 * @see ContextPartition#modifyRn(Name, String, boolean)
688 */
689 public void modifyRn( Name dn, String newRdn, boolean deleteOldRdn ) throws NamingException
690 {
691 ContextPartition backend = getBackend( dn );
692
693 backend.modifyRn( dn, newRdn, deleteOldRdn );
694 }
695
696
697 /***
698 * @see ContextPartition#move(Name, Name)
699 */
700 public void move( Name oriChildName, Name newParentName ) throws NamingException
701 {
702 ContextPartition backend = getBackend( oriChildName );
703
704 backend.move( oriChildName, newParentName );
705 }
706
707
708 /***
709 * @see ContextPartition#move(javax.naming.Name,
710 * javax.naming.Name, java.lang.String, boolean)
711 */
712 public void move( Name oldChildDn, Name newParentDn, String newRdn,
713 boolean deleteOldRdn ) throws NamingException
714 {
715 ContextPartition backend = getBackend( oldChildDn );
716
717 backend.move( oldChildDn, newParentDn, newRdn, deleteOldRdn );
718 }
719
720
721
722
723
724
725
726 /***
727 * Gets the backend partition associated with a normalized dn.
728 *
729 * @param dn the normalized distinguished name to resolve to a backend
730 * @return the backend partition associated with the normalized dn
731 * @throws NamingException if the name cannot be resolved to a backend
732 */
733 private ContextPartition getBackend( Name dn ) throws NamingException
734 {
735 Name clonedDn = ( Name ) dn.clone();
736
737 while ( clonedDn.size() > 0 )
738 {
739 if ( partitions.containsKey( clonedDn.toString() ) )
740 {
741 return ( ContextPartition ) partitions.get( clonedDn.toString() );
742 }
743
744 clonedDn.remove( clonedDn.size() - 1 );
745 }
746
747 throw new NameNotFoundException( dn.toString() );
748 }
749 }