1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server;
18
19
20 import org.apache.ldap.common.MultiException;
21 import org.apache.ldap.common.NotImplementedException;
22 import org.apache.ldap.common.exception.LdapNameNotFoundException;
23 import org.apache.ldap.common.filter.ExprNode;
24 import org.apache.ldap.common.filter.PresenceNode;
25 import org.apache.ldap.common.message.LockableAttributeImpl;
26 import org.apache.ldap.common.message.LockableAttributes;
27 import org.apache.ldap.common.message.LockableAttributesImpl;
28 import org.apache.ldap.common.util.SingletonEnumeration;
29
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.*;
35 import javax.naming.ldap.LdapContext;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.Map;
40
41
42 /***
43 * A nexus for partitions dedicated for storing entries specific to a naming
44 * context.
45 *
46 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
47 * @version $Rev: 159259 $
48 */
49 public class RootNexus implements PartitionNexus
50 {
51 /*** the vendorName string proudly set to: Apache Software Foundation*/
52 private static final String ASF = "Apache Software Foundation";
53
54 /*** the vendorName DSE operational attribute */
55 private static final String VENDORNAME_ATTR = "vendorName";
56
57 /*** the namingContexts DSE operational attribute */
58 private static final String NAMINGCTXS_ATTR = "namingContexts";
59
60 /*** Handle on the singleton instance of this class within the entire JVM. */
61 private static RootNexus s_singleton = null;
62
63 /*** the closed state of this partition */
64 private boolean closed = false;
65
66 /*** the system backend */
67 private SystemPartition system;
68
69 /*** the backends keyed by normalized suffix strings */
70 private HashMap backends = new HashMap();
71
72 /*** the read only rootDSE attributes */
73 private final Attributes rootDSE;
74
75
76 /***
77 * Creates the root nexus singleton of the entire system. The root DSE has
78 * several attributes that are injected into it besides those that may
79 * already exist. As partitions are added to the system more namingContexts
80 * attributes are added to the rootDSE.
81 *
82 * @see <a href="http://www.faqs.org/rfcs/rfc3045.html">Vendor Information</a>
83 */
84 public RootNexus( SystemPartition system, Attributes rootDSE )
85 {
86 if ( null != s_singleton )
87 {
88 throw new IllegalStateException();
89 }
90
91 s_singleton = this;
92 this.system = system;
93
94
95 this.rootDSE = rootDSE;
96 Attribute attr = new LockableAttributeImpl( "subschemaSubentry" );
97 attr.add( "cn=schema,ou=system" );
98 rootDSE.put( attr );
99
100 attr = new LockableAttributeImpl( "supportedLDAPVersion" );
101 rootDSE.put( attr );
102 attr.add( "3" );
103
104 attr = new LockableAttributeImpl( "objectClass" );
105 rootDSE.put( attr );
106 attr.add( "top" );
107 attr.add( "extensibleObject" );
108
109 attr = new LockableAttributeImpl( NAMINGCTXS_ATTR );
110 rootDSE.put( attr );
111
112 attr = new LockableAttributeImpl( VENDORNAME_ATTR );
113 attr.add( ASF );
114 rootDSE.put( attr );
115
116
117 register( this.system );
118
119 Runtime.getRuntime().addShutdownHook( new Thread( new Runnable() {
120 public void run()
121 {
122 try
123 {
124 if ( ! isClosed() )
125 {
126 RootNexus.this.close();
127 }
128 }
129 catch ( NamingException e )
130 {
131 e.printStackTrace();
132
133
134 }
135 }
136 }, "RootNexusShutdownHook" ) );
137 }
138
139
140
141
142
143
144
145 /***
146 * @see PartitionNexus#getLdapContext()
147 */
148 public LdapContext getLdapContext()
149 {
150 throw new NotImplementedException();
151 }
152
153
154 /***
155 * @see PartitionNexus#getMatchedDn(javax.naming.Name, boolean)
156 */
157 public Name getMatchedDn( Name dn, boolean normalized ) throws NamingException
158 {
159 dn = ( Name ) dn.clone();
160
161 while ( dn.size() > 0 )
162 {
163 if ( hasEntry( dn ) )
164 {
165 return dn;
166 }
167
168 dn = dn.getSuffix( 1 );
169 }
170
171 return dn;
172 }
173
174
175 /***
176 * @see org.apache.ldap.server.PartitionNexus#getSuffix(javax.naming.Name, boolean)
177 */
178 public Name getSuffix( Name dn, boolean normalized ) throws NamingException
179 {
180 ContextPartition backend = getBackend( dn );
181
182 return backend.getSuffix( normalized );
183 }
184
185
186 /***
187 * @see org.apache.ldap.server.PartitionNexus#listSuffixes(boolean)
188 */
189 public Iterator listSuffixes( boolean normalized ) throws NamingException
190 {
191 return Collections.unmodifiableSet( backends.keySet() ).iterator();
192 }
193
194
195 /***
196 * Get's the RootDSE entry for the DSA.
197 *
198 * @return the attributes of the RootDSE
199 */
200 public Attributes getRootDSE()
201 {
202 return rootDSE;
203 }
204
205
206 /***
207 * @see org.apache.ldap.server.PartitionNexus#register(
208 * ContextPartition)
209 */
210 public void register( ContextPartition backend )
211 {
212 Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
213
214 namingContexts.add( backend.getSuffix( false ).toString() );
215
216 backends.put( backend.getSuffix( true ).toString(), backend );
217 }
218
219
220 /***
221 * @see PartitionNexus#unregister(
222 * ContextPartition)
223 */
224 public void unregister( ContextPartition backend )
225 {
226 Attribute namingContexts = rootDSE.get( NAMINGCTXS_ATTR );
227
228 namingContexts.remove( backend.getSuffix( false ).toString() );
229
230 backends.remove( backend.getSuffix( true ).toString() );
231 }
232
233
234
235
236
237
238
239 /***
240 * @see BackingStore#delete(javax.naming.Name)
241 */
242 public void delete( Name dn ) throws NamingException
243 {
244 ContextPartition backend = getBackend( dn );
245
246 backend.delete( dn );
247 }
248
249
250 /***
251 * Looks up the backend corresponding to the entry first, then checks to
252 * see if the entry already exists. If so an exception is thrown. If not
253 * the add operation against the backend proceeds. This check is performed
254 * here so backend implementors do not have to worry about performing these
255 * kinds of checks.
256 *
257 * @see org.apache.ldap.server.BackingStore#add(String, Name, Attributes)
258 */
259 public void add( String updn, Name dn, Attributes an_entry ) throws NamingException
260 {
261 ContextPartition backend = getBackend( dn );
262
263 backend.add( updn, dn, an_entry );
264 }
265
266
267 /***
268 * @see BackingStore#modify(Name, int,Attributes)
269 */
270 public void modify( Name dn, int modOp, Attributes mods ) throws NamingException
271 {
272 ContextPartition backend = getBackend( dn );
273
274 backend.modify( dn, modOp, mods );
275 }
276
277
278 /***
279 * @see BackingStore#modify(javax.naming.Name,
280 * javax.naming.directory.ModificationItem[])
281 */
282 public void modify( Name dn, ModificationItem[] mods ) throws NamingException
283 {
284 ContextPartition backend = getBackend( dn );
285
286 backend.modify( dn, mods );
287 }
288
289
290 /***
291 * @see BackingStore#list(javax.naming.Name)
292 */
293 public NamingEnumeration list( Name base ) throws NamingException
294 {
295 ContextPartition backend = getBackend( base );
296
297 return backend.list( base );
298 }
299
300
301 /***
302 * @see BackingStore#search(Name, Map, ExprNode, SearchControls)
303 */
304 public NamingEnumeration search( Name base, Map env, ExprNode filter, SearchControls searchCtls )
305 throws NamingException
306 {
307
308 if ( base.size() == 0 )
309 {
310 boolean isObjectScope = searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE;
311
312 boolean isSearchAll = ( ( PresenceNode ) filter ).getAttribute().equalsIgnoreCase( "objectclass" );
313
314
315
316
317
318 if ( filter instanceof PresenceNode && isObjectScope && isSearchAll )
319 {
320 Attributes attrs = ( Attributes ) getRootDSE().clone();
321
322 String[] ids = searchCtls.getReturningAttributes();
323
324 if ( ids != null && ids.length > 0 )
325 {
326 boolean doSwap = true;
327
328 Attributes askedFor = new LockableAttributesImpl();
329
330 for ( int ii = 0; ii < ids.length; ii++ )
331 {
332 if ( ids[ii].trim().equals( "*" ) )
333 {
334 doSwap = false;
335
336 break;
337 }
338
339 if ( attrs.get( ids[ii] ) != null )
340 {
341 askedFor.put( attrs.get( ids[ii] ) );
342 }
343 }
344
345 if ( doSwap )
346 {
347 attrs = askedFor;
348 }
349 }
350
351 SearchResult result = new SearchResult( "", null, attrs, false );
352
353 return new SingletonEnumeration( result );
354 }
355
356 throw new LdapNameNotFoundException();
357 }
358
359 ContextPartition backend = getBackend( base );
360
361 return backend.search( base, env, filter, searchCtls );
362 }
363
364
365 /***
366 * @see BackingStore#lookup(javax.naming.Name)
367 */
368 public Attributes lookup( Name dn ) throws NamingException
369 {
370 if ( dn.size() == 0 )
371 {
372 LockableAttributes retval = ( LockableAttributes ) rootDSE.clone();
373
374 retval.setLocked( true );
375
376 return retval;
377 }
378
379 ContextPartition backend = getBackend( dn );
380
381 return backend.lookup( dn );
382 }
383
384
385 /***
386 * @see org.apache.ldap.server.BackingStore#lookup(javax.naming.Name, String[])
387 */
388 public Attributes lookup( Name dn, String[] attrIds ) throws NamingException
389 {
390 if ( dn.size() == 0 )
391 {
392 LockableAttributes retval = new LockableAttributesImpl();
393
394 NamingEnumeration list = rootDSE.getIDs();
395
396 while ( list.hasMore() )
397 {
398 String id = ( String ) list.next();
399
400 Attribute attr = rootDSE.get( id );
401
402 retval.put( ( Attribute ) attr.clone() );
403 }
404
405 retval.setLocked( true );
406
407 return retval;
408 }
409
410 ContextPartition backend = getBackend( dn );
411
412 return backend.lookup( dn, attrIds );
413 }
414
415
416 /***
417 * @see BackingStore#hasEntry(javax.naming.Name)
418 */
419 public boolean hasEntry( Name dn ) throws NamingException
420 {
421 if ( dn.size() == 0 )
422 {
423 return true;
424 }
425
426 ContextPartition backend = getBackend( dn );
427
428 return backend.hasEntry( dn );
429 }
430
431
432 /***
433 * @see BackingStore#isSuffix(javax.naming.Name)
434 */
435 public boolean isSuffix( Name dn ) throws NamingException
436 {
437 return backends.containsKey( dn.toString() );
438 }
439
440
441 /***
442 * @see BackingStore#modifyRn(Name, String, boolean)
443 */
444 public void modifyRn( Name dn, String newRdn, boolean deleteOldRdn ) throws NamingException
445 {
446 ContextPartition backend = getBackend( dn );
447
448 backend.modifyRn( dn, newRdn, deleteOldRdn );
449 }
450
451
452 /***
453 * @see BackingStore#move(Name, Name)
454 */
455 public void move( Name oriChildName, Name newParentName ) throws NamingException
456 {
457 ContextPartition backend = getBackend( oriChildName );
458
459 backend.move( oriChildName, newParentName );
460 }
461
462
463 /***
464 * @see BackingStore#move(javax.naming.Name,
465 * javax.naming.Name, java.lang.String, boolean)
466 */
467 public void move( Name oldChildDn, Name newParentDn, String newRdn,
468 boolean deleteOldRdn ) throws NamingException
469 {
470 ContextPartition backend = getBackend( oldChildDn );
471
472 backend.move( oldChildDn, newParentDn, newRdn, deleteOldRdn );
473 }
474
475
476 /***
477 * @see BackingStore#sync()
478 */
479 public void sync() throws NamingException
480 {
481 MultiException error = null;
482
483 Iterator list = this.backends.values().iterator();
484
485 while ( list.hasNext() )
486 {
487 BackingStore store = ( BackingStore ) list.next();
488
489 try
490 {
491 store.sync();
492 }
493 catch ( NamingException e )
494 {
495 e.printStackTrace();
496
497 if ( error == null )
498 {
499 error = new MultiException( "Grouping many exceptions on root nexus sync()" );
500 }
501
502
503 error.addThrowable( e );
504 }
505 }
506
507 if ( error != null )
508 {
509 String msg = "Encountered failures while performing a sync() operation on backing stores";
510
511 NamingException total = new NamingException( msg );
512
513 total.setRootCause( error );
514 }
515 }
516
517
518 /***
519 * @see ContextPartition#isClosed()
520 */
521 public boolean isClosed()
522 {
523 return closed;
524 }
525
526
527 /***
528 * @see org.apache.ldap.server.BackingStore#close()
529 */
530 public synchronized void close() throws NamingException
531 {
532 if ( closed )
533 {
534 return;
535 }
536
537 MultiException error = null;
538
539 Iterator list = this.backends.values().iterator();
540
541
542
543 while ( list.hasNext() )
544 {
545 BackingStore store = ( BackingStore ) list.next();
546
547 try
548 {
549 store.sync();
550
551 store.close();
552 }
553 catch ( NamingException e )
554 {
555 e.printStackTrace();
556
557 if ( error == null )
558 {
559 error = new MultiException( "Grouping many exceptions on root nexus close()" );
560 }
561
562
563 error.addThrowable( e );
564 }
565 }
566
567 s_singleton = null;
568
569 closed = true;
570
571 if ( error != null )
572 {
573 String msg = "Encountered failures while performing a close() operation on backing stores";
574
575 NamingException total = new NamingException( msg );
576
577 total.setRootCause( error );
578
579 throw total;
580 }
581 }
582
583
584
585
586
587
588
589 /***
590 * Gets the backend partition associated with a normalized dn.
591 *
592 * @param dn the normalized distinguished name to resolve to a backend
593 * @return the backend partition associated with the normalized dn
594 * @throws NamingException if the name cannot be resolved to a backend
595 */
596 private ContextPartition getBackend( Name dn ) throws NamingException
597 {
598 Name clonedDn = ( Name ) dn.clone();
599
600 while ( clonedDn.size() > 0 )
601 {
602 if ( backends.containsKey( clonedDn.toString() ) )
603 {
604 return ( ContextPartition ) backends.get( clonedDn.toString() );
605 }
606
607 clonedDn.remove( clonedDn.size() - 1 );
608 }
609
610 throw new NameNotFoundException();
611 }
612 }