KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ldap > server > RootNexus


1 /*
2  * Copyright 2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
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 JavaDoc;
31 import javax.naming.NameNotFoundException JavaDoc;
32 import javax.naming.NamingEnumeration JavaDoc;
33 import javax.naming.NamingException JavaDoc;
34 import javax.naming.directory.*;
35 import javax.naming.ldap.LdapContext JavaDoc;
36 import java.util.Collections JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.Map JavaDoc;
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: 169198 $
48  */

49 public class RootNexus implements PartitionNexus
50 {
51     /** the vendorName string proudly set to: Apache Software Foundation*/
52     private static final String JavaDoc ASF = "Apache Software Foundation";
53
54     /** the vendorName DSE operational attribute */
55     private static final String JavaDoc VENDORNAME_ATTR = "vendorName";
56
57     /** the namingContexts DSE operational attribute */
58     private static final String JavaDoc 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 JavaDoc backends = new HashMap JavaDoc();
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 JavaDoc();
89         }
90         
91         s_singleton = this;
92         this.system = system;
93
94         // setup that root DSE
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         // register will add to the list of namingContexts as well
117
register( this.system );
118
119         Runtime.getRuntime().addShutdownHook( new Thread JavaDoc( new Runnable JavaDoc() {
120             public void run()
121             {
122                 try
123                 {
124                     if ( ! isClosed() )
125                     {
126                         RootNexus.this.close();
127                     }
128                 }
129                 catch ( NamingException JavaDoc e )
130                 {
131                     e.printStackTrace();
132                     // @todo again we need to monitor this failure and report
133
// that it occured on shutdown specifically
134
}
135             }
136         }, "RootNexusShutdownHook" ) );
137     }
138
139
140     // ------------------------------------------------------------------------
141
// BackendNexus Interface Method Implementations
142
// ------------------------------------------------------------------------
143

144
145     /**
146      * @see PartitionNexus#getLdapContext()
147      */

148     public LdapContext JavaDoc getLdapContext()
149     {
150         throw new NotImplementedException();
151     }
152
153
154     /**
155      * @see PartitionNexus#getMatchedDn(javax.naming.Name, boolean)
156      */

157     public Name JavaDoc getMatchedDn( Name JavaDoc dn, boolean normalized ) throws NamingException JavaDoc
158     {
159         dn = ( Name JavaDoc ) 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 JavaDoc getSuffix( Name JavaDoc dn, boolean normalized ) throws NamingException JavaDoc
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 JavaDoc listSuffixes( boolean normalized ) throws NamingException JavaDoc
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
// Backend Interface Method Implementations
236
// ------------------------------------------------------------------------
237

238     
239     /**
240      * @see BackingStore#delete(javax.naming.Name)
241      */

242     public void delete( Name JavaDoc dn ) throws NamingException JavaDoc
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 JavaDoc updn, Name JavaDoc dn, Attributes an_entry ) throws NamingException JavaDoc
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 JavaDoc dn, int modOp, Attributes mods ) throws NamingException JavaDoc
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 JavaDoc dn, ModificationItem[] mods ) throws NamingException JavaDoc
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 JavaDoc list( Name JavaDoc base ) throws NamingException JavaDoc
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 JavaDoc search( Name JavaDoc base, Map JavaDoc env, ExprNode filter, SearchControls searchCtls )
305             throws NamingException JavaDoc
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              * if basedn is "", filter is "(objectclass=*)" and scope is object
316              * then we have a request for the rootDSE
317              */

318             if ( filter instanceof PresenceNode && isObjectScope && isSearchAll )
319             {
320                 Attributes attrs = ( Attributes ) getRootDSE().clone();
321
322                 String JavaDoc[] 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 JavaDoc dn ) throws NamingException JavaDoc
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 JavaDoc dn, String JavaDoc[] attrIds ) throws NamingException JavaDoc
389     {
390         if ( dn.size() == 0 )
391         {
392             LockableAttributes retval = new LockableAttributesImpl();
393
394             NamingEnumeration JavaDoc list = rootDSE.getIDs();
395
396             while ( list.hasMore() )
397             {
398                 String JavaDoc id = ( String JavaDoc ) 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 JavaDoc dn ) throws NamingException JavaDoc
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 JavaDoc dn ) throws NamingException JavaDoc
436     {
437         return backends.containsKey( dn.toString() );
438     }
439
440     
441     /**
442      * @see BackingStore#modifyRn(Name, String, boolean)
443      */

444     public void modifyRn( Name JavaDoc dn, String JavaDoc newRdn, boolean deleteOldRdn ) throws NamingException JavaDoc
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 JavaDoc oriChildName, Name JavaDoc newParentName ) throws NamingException JavaDoc
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 JavaDoc oldChildDn, Name JavaDoc newParentDn, String JavaDoc newRdn,
468         boolean deleteOldRdn ) throws NamingException JavaDoc
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 JavaDoc
480     {
481         MultiException error = null;
482
483         Iterator JavaDoc 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 JavaDoc 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                 // @todo really need to send this info to a monitor
503
error.addThrowable( e );
504             }
505         }
506
507         if ( error != null )
508         {
509             String JavaDoc msg = "Encountered failures while performing a sync() operation on backing stores";
510
511             NamingException JavaDoc total = new NamingException JavaDoc( 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 JavaDoc
531     {
532         if ( closed )
533         {
534             return;
535         }
536
537         MultiException error = null;
538
539         Iterator JavaDoc list = this.backends.values().iterator();
540
541         // make sure this loop is not fail fast so all backing stores can
542
// have an attempt at closing down and synching their cached entries
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 JavaDoc 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                 // @todo really need to send this info to a monitor
563
error.addThrowable( e );
564             }
565         }
566
567         s_singleton = null;
568
569         closed = true;
570
571         if ( error != null )
572         {
573             String JavaDoc msg = "Encountered failures while performing a close() operation on backing stores";
574
575             NamingException JavaDoc total = new NamingException JavaDoc( msg );
576
577             total.setRootCause( error );
578
579             throw total;
580         }
581     }
582
583
584     // ------------------------------------------------------------------------
585
// Private Methods
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 JavaDoc dn ) throws NamingException JavaDoc
597     {
598         Name JavaDoc clonedDn = ( Name JavaDoc ) 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 }
613
Popular Tags