View Javadoc

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.jndi;
18  
19  
20  import org.apache.ldap.common.exception.LdapAuthenticationNotSupportedException;
21  import org.apache.ldap.common.exception.LdapConfigurationException;
22  import org.apache.ldap.common.exception.LdapNoPermissionException;
23  import org.apache.ldap.common.message.LockableAttributesImpl;
24  import org.apache.ldap.common.message.ResultCodeEnum;
25  import org.apache.ldap.common.name.LdapName;
26  import org.apache.ldap.common.schema.AttributeType;
27  import org.apache.ldap.common.schema.Normalizer;
28  import org.apache.ldap.common.util.DateUtils;
29  import org.apache.ldap.common.util.StringTools;
30  import org.apache.ldap.server.*;
31  import org.apache.ldap.server.db.*;
32  import org.apache.ldap.server.db.jdbm.JdbmDatabase;
33  import org.apache.ldap.server.interceptor.InterceptorChain;
34  import org.apache.ldap.server.interceptor.InterceptorConfigBuilder;
35  import org.apache.ldap.server.interceptor.InterceptorContext;
36  import org.apache.ldap.server.schema.AttributeTypeRegistry;
37  import org.apache.ldap.server.schema.GlobalRegistries;
38  import org.apache.ldap.server.schema.MatchingRuleRegistry;
39  import org.apache.ldap.server.schema.OidRegistry;
40  import org.apache.ldap.server.schema.bootstrap.BootstrapRegistries;
41  import org.apache.ldap.server.schema.bootstrap.BootstrapSchemaLoader;
42  
43  import javax.naming.Context;
44  import javax.naming.Name;
45  import javax.naming.NamingException;
46  import javax.naming.directory.Attributes;
47  import javax.naming.directory.Attribute;
48  import javax.naming.spi.InitialContextFactory;
49  import java.io.File;
50  import java.lang.reflect.Constructor;
51  import java.util.ArrayList;
52  import java.util.Hashtable;
53  import java.util.List;
54  
55  
56  /***
57   * A server-side provider implementation of a InitialContextFactory.  Can be
58   * utilized via JNDI API in the standard fashion:
59   *
60   * <code>
61   * Hashtable env = new Hashtable();
62   * env.put( Context.PROVIDER_URL, "ou=system" );
63   * env.put(
64   * Context.INITIAL_CONTEXT_FACTORY, "org.apache.ldap.server.jndi.CoreContextFactory" );
65   * InitialContext initialContext = new InitialContext( env );
66   * </code>
67   *
68   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
69   * @version $Rev$
70   * @see javax.naming.spi.InitialContextFactory
71   */
72  public class CoreContextFactory implements InitialContextFactory
73  {
74      /*
75       * @todo this class needs to be better broken down - its in disarray; too much
76       * fuctionality in one place which can be better organized
77       */
78  
79      /*** shorthand reference to the authentication type property */
80      private static final String TYPE = Context.SECURITY_AUTHENTICATION;
81  
82      /*** shorthand reference to the authentication credentials property */
83      private static final String CREDS = Context.SECURITY_CREDENTIALS;
84  
85      /*** shorthand reference to the authentication principal property */
86      protected static final String PRINCIPAL = Context.SECURITY_PRINCIPAL;
87  
88      /*** shorthand reference to the admin principal name */
89      protected static final String ADMIN = SystemPartition.ADMIN_PRINCIPAL;
90  
91      /*** shorthand reference to the admin principal distinguished name */
92      protected static final Name ADMIN_NAME = SystemPartition.getAdminDn();
93  
94      /*** default path to working directory if WKDIR_ENV property is not set */
95      public static final String DEFAULT_WKDIR = "server-work";
96  
97      /*** default schema classes for the SCHEMAS_ENV property if not set */
98      protected static final String[] DEFAULT_SCHEMAS = new String[]
99      {
100         "org.apache.ldap.server.schema.bootstrap.CoreSchema",
101         "org.apache.ldap.server.schema.bootstrap.CosineSchema",
102         "org.apache.ldap.server.schema.bootstrap.ApacheSchema",
103         "org.apache.ldap.server.schema.bootstrap.InetorgpersonSchema",
104         "org.apache.ldap.server.schema.bootstrap.JavaSchema",
105         "org.apache.ldap.server.schema.bootstrap.SystemSchema"
106     };
107 
108     // ------------------------------------------------------------------------
109     // Members
110     // ------------------------------------------------------------------------
111 
112     /*** The singleton JndiProvider instance */
113     protected JndiProvider provider = null;
114 
115     /*** the initial context environment that fired up the backend subsystem */
116     protected Hashtable initialEnv;
117 
118     /*** the system partition used by the context factory */
119     protected SystemPartition system;
120 
121     /*** the registries for system schema objects */
122     protected GlobalRegistries globalRegistries;
123 
124     /*** the root nexus */
125     protected RootNexus nexus;
126 
127     /*** whether or not server is started for the first time */
128     protected boolean createMode;
129 
130 
131     /***
132      * Default constructor that sets the provider of this ServerContextFactory.
133      */
134     public CoreContextFactory()
135     {
136         JndiProvider.setProviderOn( this );
137     }
138 
139 
140     /***
141      * Enables this ServerContextFactory with a handle to the JndiProvider singleton.
142      *
143      * @param provider the system's singleton BackendSubsystem service.
144      */
145     void setProvider( JndiProvider provider )
146     {
147         this.provider = provider;
148     }
149 
150 
151     public Context getInitialContext( Hashtable env ) throws NamingException
152     {
153         env = ( Hashtable ) env.clone();
154 
155         Context ctx = null;
156 
157         if ( env.containsKey( EnvKeys.SHUTDOWN ) )
158         {
159             if ( this.provider == null )
160             {
161                 return new DeadContext();
162             }
163 
164             try
165             {
166                 this.provider.shutdown();
167             }
168             catch ( Throwable t )
169             {
170                 t.printStackTrace();
171             }
172             finally
173             {
174                 ctx = new DeadContext();
175 
176                 provider = null;
177 
178                 initialEnv = null;
179             }
180 
181             return ctx;
182         }
183 
184         if ( env.containsKey( EnvKeys.SYNC ) )
185         {
186             provider.sync();
187 
188             return provider.getLdapContext( env );
189         }
190 
191         checkSecuritySettings( env );
192 
193         if ( isAnonymous( env ) )
194         {
195             env.put( PRINCIPAL, "" );
196         }
197 
198         // fire up the backend subsystem if we need to
199         if ( null == provider )
200         {
201             // we need to check this here instead of in AuthenticationService
202             // because otherwise we are going to start up the system incorrectly
203             if ( isAnonymous( env ) && env.containsKey( EnvKeys.DISABLE_ANONYMOUS ) )
204             {
205                 throw new LdapNoPermissionException( "cannot bind as anonymous "
206                         + "on startup while disabling anonymous binds w/ property: "
207                         + EnvKeys.DISABLE_ANONYMOUS );
208             }
209 
210             this.initialEnv = env;
211 
212             initialize();
213 
214             createMode = createBootstrapEntries();
215 
216             /*
217              * Unfortunately to test non-root user startup of the core and make sure
218              * all the appropriate functionality is there we need to load more user
219              * entries at startup due to a chicken and egg like problem.  The value
220              * of this property is a list of attributes to be added.
221              */
222 
223             if ( createMode && env.containsKey( EnvKeys.TEST_ENTRIES ) )
224             {
225                 ArrayList list = ( ArrayList ) initialEnv.get( EnvKeys.TEST_ENTRIES );
226 
227                 if ( list != null )
228                 {
229                     for ( int ii = 0; ii < list.size(); ii++ )
230                     {
231                         Attributes attributes = ( Attributes ) list.get( ii );
232 
233                         attributes.put( "creatorsName", ADMIN );
234 
235                         attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
236 
237                         Attribute dn = attributes.remove( "dn" );
238 
239                         nexus.add( ( String ) dn.get(), new LdapName( ( String ) dn.get() ), attributes );
240                     }
241                 }
242             }
243         }
244 
245         ctx = ( ServerContext ) provider.getLdapContext( env );
246 
247         return ctx;
248     }
249 
250 
251     /***
252      * Checks to make sure security environment parameters are set correctly.
253      *
254      * @throws javax.naming.NamingException if the security settings are not correctly configured.
255      */
256     protected void checkSecuritySettings( Hashtable env ) throws NamingException
257     {
258         if ( env.containsKey( TYPE ) && env.get( TYPE ) != null )
259         {
260             /*
261              * If bind is simple make sure we have the credentials and the
262              * principal name set within the environment, otherwise complain
263              */
264             if ( env.get( TYPE ).equals( "simple" ) )
265             {
266                 if ( !env.containsKey( CREDS ) )
267                 {
268                     throw new LdapConfigurationException( "missing required "
269                             + CREDS + " property for simple authentication" );
270                 }
271 
272                 if ( !env.containsKey( PRINCIPAL ) )
273                 {
274                     throw new LdapConfigurationException( "missing required "
275                             + PRINCIPAL + " property for simple authentication" );
276                 }
277             }
278             /*
279              * If bind is none make sure credentials and the principal
280              * name are NOT set within the environment, otherwise complain
281              */
282             else if ( env.get( TYPE ).equals( "none" ) )
283             {
284                 if ( env.containsKey( CREDS ) )
285                 {
286                     throw new LdapConfigurationException( "ambiguous bind "
287                             + "settings encountered where bind is anonymous yet "
288                             + CREDS + " property is set" );
289                 }
290                 if ( env.containsKey( PRINCIPAL ) )
291                 {
292                     throw new LdapConfigurationException( "ambiguous bind "
293                             + "settings encountered where bind is anonymous yet "
294                             + PRINCIPAL + " property is set" );
295                 }
296             }
297             /*
298              * If bind is anything other than simple or none we need to
299              * complain because SASL is not a supported auth method yet
300              */
301             else
302             {
303                 throw new LdapAuthenticationNotSupportedException( ResultCodeEnum.AUTHMETHODNOTSUPPORTED );
304             }
305         }
306         else if ( env.containsKey( CREDS ) )
307         {
308             if ( !env.containsKey( PRINCIPAL ) )
309             {
310                 throw new LdapConfigurationException( "credentials provided "
311                         + "without principal name property: " + PRINCIPAL );
312             }
313         }
314     }
315 
316 
317     /***
318      * Checks to see if an anonymous bind is being attempted.
319      *
320      * @return true if bind is anonymous, false otherwise
321      */
322     protected boolean isAnonymous( Hashtable env )
323     {
324 
325         if ( env.containsKey( TYPE ) && env.get( TYPE ) != null )
326         {
327             if ( env.get( TYPE ).equals( "none" ) )
328             {
329                 return true;
330             }
331 
332             return false;
333         }
334 
335         if ( env.containsKey( CREDS ) )
336         {
337             return false;
338         }
339 
340         return true;
341     }
342 
343 
344     /***
345      * Returns true if we had to create the bootstrap entries on the first
346      * start of the server.  Otherwise if all entries exist, meaning none
347      * had to be created, then we are not starting for the first time.
348      *
349      * @throws javax.naming.NamingException
350      */
351     private boolean createBootstrapEntries() throws NamingException
352     {
353         boolean isFirstStart = false;
354 
355         // -------------------------------------------------------------------
356         // create admin entry
357         // -------------------------------------------------------------------
358 
359         /*
360          * If the admin entry is there, then the database was already created
361          */
362         if ( nexus.hasEntry( ADMIN_NAME ) )
363         {
364             isFirstStart = false;
365         }
366         else
367         {
368             isFirstStart = true;
369 
370             Attributes attributes = new LockableAttributesImpl();
371 
372             attributes.put( "objectClass", "top" );
373 
374             attributes.put( "objectClass", "person" );
375 
376             attributes.put( "objectClass", "organizationalPerson" );
377 
378             attributes.put( "objectClass", "inetOrgPerson" );
379 
380             attributes.put( "uid", SystemPartition.ADMIN_UID );
381 
382             attributes.put( "userPassword", SystemPartition.ADMIN_PW );
383 
384             attributes.put( "displayName", "Directory Superuser" );
385 
386             attributes.put( "creatorsName", ADMIN );
387 
388             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
389 
390             attributes.put( "displayName", "Directory Superuser" );
391 
392             nexus.add( ADMIN, ADMIN_NAME, attributes );
393         }
394 
395         // -------------------------------------------------------------------
396         // create system users area
397         // -------------------------------------------------------------------
398 
399         if ( nexus.hasEntry( new LdapName( "ou=users,ou=system" ) ) )
400         {
401             isFirstStart = false;
402         }
403         else
404         {
405             isFirstStart = true;
406 
407             Attributes attributes = new LockableAttributesImpl();
408 
409             attributes.put( "objectClass", "top" );
410 
411             attributes.put( "objectClass", "organizationalUnit" );
412 
413             attributes.put( "ou", "users" );
414 
415             attributes.put( "creatorsName", ADMIN );
416 
417             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
418 
419             nexus.add( "ou=users,ou=system", new LdapName( "ou=users,ou=system" ), attributes );
420         }
421 
422         // -------------------------------------------------------------------
423         // create system groups area
424         // -------------------------------------------------------------------
425 
426         if ( nexus.hasEntry( new LdapName( "ou=groups,ou=system" ) ) )
427         {
428             isFirstStart = false;
429         }
430         else
431         {
432             isFirstStart = true;
433 
434             Attributes attributes = new LockableAttributesImpl();
435 
436             attributes.put( "objectClass", "top" );
437 
438             attributes.put( "objectClass", "organizationalUnit" );
439 
440             attributes.put( "ou", "groups" );
441 
442             attributes.put( "creatorsName", ADMIN );
443 
444             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
445 
446             nexus.add( "ou=groups,ou=system", new LdapName( "ou=groups,ou=system" ), attributes );
447         }
448 
449         // -------------------------------------------------------------------
450         // create system preferences area
451         // -------------------------------------------------------------------
452 
453         if ( nexus.hasEntry( new LdapName( "prefNodeName=sysPrefRoot,ou=system" ) ) )
454         {
455             isFirstStart = false;
456         }
457         else
458         {
459             isFirstStart = true;
460 
461             Attributes attributes = new LockableAttributesImpl();
462 
463             attributes.put( "objectClass", "top" );
464 
465             attributes.put( "objectClass", "prefNode" );
466 
467             attributes.put( "objectClass", "extensibleObject" );
468 
469             attributes.put( "prefNodeName", "sysPrefRoot" );
470 
471             attributes.put( "creatorsName", ADMIN );
472 
473             attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() );
474 
475             LdapName dn = new LdapName( "prefNodeName=sysPrefRoot,ou=system" );
476 
477             nexus.add( "prefNodeName=sysPrefRoot,ou=system", dn, attributes );
478         }
479 
480         return isFirstStart;
481     }
482 
483 
484     /***
485      * Kicks off the initialization of the entire system.
486      *
487      * @throws javax.naming.NamingException if there are problems along the way
488      */
489     protected void initialize() throws NamingException
490     {
491         // --------------------------------------------------------------------
492         // Load the schema here and check that it is ok!
493         // --------------------------------------------------------------------
494 
495         BootstrapRegistries bootstrapRegistries = new BootstrapRegistries();
496 
497         BootstrapSchemaLoader loader = new BootstrapSchemaLoader();
498 
499         String[] schemas = DEFAULT_SCHEMAS;
500 
501         if ( initialEnv.containsKey( EnvKeys.SCHEMAS ) )
502         {
503             String schemaList = ( String ) initialEnv.get( EnvKeys.SCHEMAS );
504 
505             schemaList = StringTools.deepTrim( schemaList );
506 
507             schemas = schemaList.split( " " );
508 
509             for ( int ii = 0; ii < schemas.length; ii++ )
510             {
511                 schemas[ii] = schemas[ii].trim();
512             }
513         }
514 
515         loader.load( schemas, bootstrapRegistries );
516 
517         List errors = bootstrapRegistries.checkRefInteg();
518 
519         if ( !errors.isEmpty() )
520         {
521             NamingException e = new NamingException();
522 
523             e.setRootCause( ( Throwable ) errors.get( 0 ) );
524 
525             throw e;
526         }
527 
528         // --------------------------------------------------------------------
529         // Fire up the system partition
530         // --------------------------------------------------------------------
531 
532         String wkdir = DEFAULT_WKDIR;
533 
534         if ( initialEnv.containsKey( EnvKeys.WKDIR ) )
535         {
536             wkdir = ( ( String ) initialEnv.get( EnvKeys.WKDIR ) ).trim();
537         }
538 
539         File wkdirFile = new File( wkdir );
540 
541         if ( wkdirFile.isAbsolute() )
542         {
543             if ( !wkdirFile.exists() )
544             {
545                 throw new NamingException( "working directory " + wkdir + " does not exist" );
546             }
547         }
548         else
549         {
550             File current = new File( "." );
551 
552             mkdirs( current.getAbsolutePath(), wkdir );
553         }
554 
555         LdapName suffix = new LdapName();
556 
557         suffix.add( SystemPartition.SUFFIX );
558 
559         Database db = new JdbmDatabase( suffix, suffix, wkdir );
560 
561         AttributeTypeRegistry attributeTypeRegistry;
562 
563         attributeTypeRegistry = bootstrapRegistries .getAttributeTypeRegistry();
564 
565         OidRegistry oidRegistry;
566 
567         oidRegistry = bootstrapRegistries.getOidRegistry();
568 
569         ExpressionEvaluator evaluator;
570 
571         evaluator = new ExpressionEvaluator( db, oidRegistry, attributeTypeRegistry );
572 
573         ExpressionEnumerator enumerator;
574 
575         enumerator = new ExpressionEnumerator( db, attributeTypeRegistry, evaluator );
576 
577         SearchEngine eng = new DefaultSearchEngine( db, evaluator, enumerator );
578 
579         AttributeType[] attributes = new AttributeType[]
580         {
581             attributeTypeRegistry.lookup( SystemPartition.ALIAS_OID ),
582 
583             attributeTypeRegistry.lookup( SystemPartition.EXISTANCE_OID ),
584 
585             attributeTypeRegistry.lookup( SystemPartition.HIERARCHY_OID ),
586 
587             attributeTypeRegistry.lookup( SystemPartition.NDN_OID ),
588 
589             attributeTypeRegistry.lookup( SystemPartition.ONEALIAS_OID ),
590 
591             attributeTypeRegistry.lookup( SystemPartition.SUBALIAS_OID ),
592 
593             attributeTypeRegistry.lookup( SystemPartition.UPDN_OID )
594         };
595 
596         system = new SystemPartition( db, eng, attributes );
597 
598         globalRegistries = new GlobalRegistries( system, bootstrapRegistries );
599 
600         nexus = new RootNexus( system, new LockableAttributesImpl() );
601 
602         provider = new JndiProvider( nexus );
603 
604         // --------------------------------------------------------------------
605         // Adding interceptors
606         // --------------------------------------------------------------------
607         InterceptorChain interceptor = ( InterceptorChain ) initialEnv.get( EnvKeys.INTERCEPTORS );
608 
609         if( interceptor == null )
610         {
611             // If custom interceptor is not specified, use defaule one.
612 
613             interceptor = InterceptorChain.newDefaultChain();
614         }
615 
616         interceptor.init( new InterceptorContext( initialEnv, system, globalRegistries, nexus,
617                 InterceptorConfigBuilder.build( initialEnv, EnvKeys.INTERCEPTORS ) ) );
618 
619         provider.setInterceptor( interceptor );
620 
621         // fire up the app partitions now!
622         if ( initialEnv.get( EnvKeys.PARTITIONS ) != null )
623         {
624             startUpAppPartitions( wkdir );
625         }
626     }
627 
628     /***
629      * Starts up all the application partitions that will be attached to naming contexts in the system.  Partition
630      * database files are created within a subdirectory immediately under the Eve working directory base.
631      *
632      * @param eveWkdir the base Eve working directory
633      * @throws javax.naming.NamingException if there are problems creating and starting these new application
634      *                                      partitions
635      */
636     protected void startUpAppPartitions( String eveWkdir ) throws NamingException
637     {
638         OidRegistry oidRegistry = globalRegistries.getOidRegistry();
639 
640         AttributeTypeRegistry attributeTypeRegistry;
641 
642         attributeTypeRegistry = globalRegistries.getAttributeTypeRegistry();
643 
644         MatchingRuleRegistry reg = globalRegistries.getMatchingRuleRegistry();
645 
646         // start getting all the parameters from the initial environment
647         ContextPartitionConfig[] configs = null;
648 
649         configs = PartitionConfigBuilder.getContextPartitionConfigs( initialEnv );
650 
651         for ( int ii = 0; ii < configs.length; ii++ )
652         {
653             // ----------------------------------------------------------------
654             // create working directory under eve directory for app partition
655             // ----------------------------------------------------------------
656 
657             String wkdir = eveWkdir + File.separator + configs[ii].getId();
658 
659             mkdirs( eveWkdir, configs[ii].getId() );
660 
661             // ----------------------------------------------------------------
662             // create the database/store
663             // ----------------------------------------------------------------
664 
665             Name upSuffix = new LdapName( configs[ii].getSuffix() );
666 
667             Normalizer dnNorm = reg.lookup( "distinguishedNameMatch" ) .getNormalizer();
668 
669             Name normSuffix = new LdapName( ( String ) dnNorm.normalize( configs[ii].getSuffix() ) );
670 
671             Database db = new JdbmDatabase( upSuffix, normSuffix, wkdir );
672 
673             // ----------------------------------------------------------------
674             // create the search engine using db, enumerators and evaluators
675             // ----------------------------------------------------------------
676 
677             ExpressionEvaluator evaluator;
678 
679             evaluator = new ExpressionEvaluator( db, oidRegistry, attributeTypeRegistry );
680 
681             ExpressionEnumerator enumerator;
682 
683             enumerator = new ExpressionEnumerator( db, attributeTypeRegistry, evaluator );
684 
685             SearchEngine eng = new DefaultSearchEngine( db, evaluator, enumerator );
686 
687             // ----------------------------------------------------------------
688             // fill up a list with the AttributeTypes for the system indices
689             // ----------------------------------------------------------------
690 
691             ArrayList attributeTypeList = new ArrayList();
692 
693             attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.ALIAS_OID ) );
694 
695             attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.EXISTANCE_OID ) );
696 
697             attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.HIERARCHY_OID ) );
698 
699             attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.NDN_OID ) );
700 
701             attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.ONEALIAS_OID ) );
702 
703             attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.SUBALIAS_OID ) );
704 
705             attributeTypeList.add( attributeTypeRegistry.lookup( SystemPartition.UPDN_OID ) );
706 
707             // ----------------------------------------------------------------
708             // if user indices are specified add those attribute types as well
709             // ----------------------------------------------------------------
710 
711             for ( int jj = 0; jj < configs[ii].getIndices().length; jj++ )
712             {
713                 attributeTypeList.add( attributeTypeRegistry
714                         .lookup( configs[ii].getIndices()[jj] ) );
715             }
716 
717             // ----------------------------------------------------------------
718             // fire up the appPartition & register it with the nexus
719             // ----------------------------------------------------------------
720 
721             AttributeType[] indexTypes = ( AttributeType[] ) attributeTypeList
722                     .toArray( new AttributeType[attributeTypeList.size()] );
723 
724             String partitionClass = configs[ii].getPartitionClass();
725 
726             String properties = configs[ii].getProperties();
727 
728             ContextPartition partition = null;
729 
730             if ( partitionClass == null )
731             {
732                 // If custom partition is not defined, use the ApplicationPartion.
733                 partition = new ApplicationPartition( upSuffix, normSuffix, db, eng, indexTypes );
734 
735             }
736             else
737             {
738                 // If custom partition is defined, instantiate it.
739                 try
740                 {
741                     Class clazz = Class.forName( partitionClass );
742 
743                     Constructor constructor = clazz.getConstructor(
744                             new Class[] { Name.class, Name.class, String.class } );
745 
746                     partition = ( ContextPartition ) constructor.newInstance(
747                             new Object[] { upSuffix, normSuffix, properties } );
748                 }
749                 catch ( Exception e )
750                 {
751                     e.printStackTrace();
752                 }
753             }
754 
755             if ( partition != null ) 
756             { 
757                 nexus.register( partition );
758             }
759 
760             // ----------------------------------------------------------------
761             // add the nexus context entry
762             // ----------------------------------------------------------------
763 
764             partition.add( configs[ii].getSuffix(), normSuffix, configs[ii].getAttributes() );
765         }
766     }
767 
768 
769     /***
770      * Recursively creates a bunch of directories from a base down to a path.
771      *
772      * @param base the base directory to start at
773      * @param path the path to recursively create if we have to
774      * @return true if the target directory has been created or exists, false if we fail along the way somewhere
775      */
776     protected boolean mkdirs( String base, String path )
777     {
778         String[] comps = path.split( "/" );
779 
780         File file = new File( base );
781 
782         if ( !file.exists() )
783         {
784             file.mkdirs();
785         }
786 
787         for ( int ii = 0; ii < comps.length; ii++ )
788         {
789             file = new File( file, comps[ii] );
790 
791             if ( !file.exists() )
792             {
793                 file.mkdirs();
794             }
795         }
796 
797         return file.exists();
798     }
799 }