001    /*
002     *   Licensed to the Apache Software Foundation (ASF) under one
003     *   or more contributor license agreements.  See the NOTICE file
004     *   distributed with this work for additional information
005     *   regarding copyright ownership.  The ASF licenses this file
006     *   to you under the Apache License, Version 2.0 (the
007     *   "License"); you may not use this file except in compliance
008     *   with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *   Unless required by applicable law or agreed to in writing,
013     *   software distributed under the License is distributed on an
014     *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *   KIND, either express or implied.  See the License for the
016     *   specific language governing permissions and limitations
017     *   under the License.
018     *
019     */
020    package org.apache.directory.server.core.partition.ldif;
021    
022    
023    import java.io.File;
024    import java.io.FileFilter;
025    import java.io.FileWriter;
026    import java.io.IOException;
027    import java.util.Iterator;
028    import java.util.List;
029    import java.util.UUID;
030    
031    import javax.naming.InvalidNameException;
032    import javax.naming.NamingException;
033    
034    import org.apache.directory.server.core.entry.ClonedServerEntry;
035    import org.apache.directory.server.core.entry.DefaultServerEntry;
036    import org.apache.directory.server.core.entry.ServerEntry;
037    import org.apache.directory.server.core.interceptor.context.AddOperationContext;
038    import org.apache.directory.server.core.interceptor.context.BindOperationContext;
039    import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
040    import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
041    import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
042    import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
043    import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
044    import org.apache.directory.server.core.partition.Partition;
045    import org.apache.directory.server.core.partition.avl.AvlPartition;
046    import org.apache.directory.server.core.partition.impl.btree.BTreePartition;
047    import org.apache.directory.server.i18n.I18n;
048    import org.apache.directory.server.xdbm.Index;
049    import org.apache.directory.server.xdbm.IndexCursor;
050    import org.apache.directory.server.xdbm.IndexEntry;
051    import org.apache.directory.shared.ldap.constants.SchemaConstants;
052    import org.apache.directory.shared.ldap.csn.CsnFactory;
053    import org.apache.directory.shared.ldap.entry.Entry;
054    import org.apache.directory.shared.ldap.ldif.LdifEntry;
055    import org.apache.directory.shared.ldap.ldif.LdifReader;
056    import org.apache.directory.shared.ldap.ldif.LdifUtils;
057    import org.apache.directory.shared.ldap.name.DN;
058    import org.apache.directory.shared.ldap.name.RDN;
059    import org.apache.directory.shared.ldap.schema.AttributeType;
060    import org.apache.directory.shared.ldap.schema.SchemaManager;
061    import org.apache.directory.shared.ldap.util.StringTools;
062    import org.apache.directory.shared.ldap.util.SystemUtils;
063    import org.slf4j.Logger;
064    import org.slf4j.LoggerFactory;
065    
066    
067    /**
068     * A LDIF based partition. Data are stored on disk as LDIF, following this organisation :
069     * <li> each entry is associated with a file, postfixed with LDIF
070     * <li> each entry having at least one child will have a directory created using its name.
071     * The root is the partition's suffix.
072     * <br>
073     * So for instance, we may have on disk :
074     * <pre>
075     * /ou=example,ou=system.ldif
076     * /ou=example,ou=system/
077     *   |
078     *   +--> cn=test.ldif
079     *        cn=test/
080     *           |
081     *           +--> cn=another test.ldif
082     *                ...
083     * </pre>
084     * <br><br>            
085     * In this exemple, the partition's suffix is <b>ou=example,ou=system</b>. 
086     * <br>   
087     *  
088     * @org.apache.xbean.XBean
089     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
090     * @version $Rev$, $Date$
091     */
092    public class LdifPartition extends BTreePartition<Long>
093    {
094        /** A logger for this class */
095        private static Logger LOG = LoggerFactory.getLogger( LdifPartition.class );
096    
097        /** The LDIF file parser */
098        //private LdifReader ldifReader;
099    
100        /** The directory into which the partition is stored */
101        private String workingDirectory;
102    
103        /** The directory into which the entries are stored */
104        private File suffixDirectory;
105    
106        /** The context entry */
107        private ServerEntry contextEntry;
108    
109        /** Flags used for the getFile() method */
110        private static final boolean CREATE = Boolean.TRUE;
111        private static final boolean DELETE = Boolean.FALSE;
112    
113        private int ldifScanInterval;
114    
115        /** A filter used to pick all the directories */
116        private FileFilter dirFilter = new FileFilter()
117        {
118            public boolean accept( File dir )
119            {
120                return dir.isDirectory();
121            }
122        };
123    
124        /** A filter used to pick all the ldif entries */
125        private FileFilter entryFilter = new FileFilter()
126        {
127            public boolean accept( File dir )
128            {
129                if ( dir.getName().endsWith( CONF_FILE_EXTN ) )
130                {
131                    return dir.isFile();
132                }
133                else
134                {
135                    return false;
136                }
137            }
138        };
139    
140        /** The extension used for LDIF entry files */
141        private static final String CONF_FILE_EXTN = ".ldif";
142    
143        /** We use a partition to manage searches on this partition */
144        private AvlPartition wrappedPartition;
145    
146        /** A default CSN factory */
147        private static CsnFactory defaultCSNFactory;
148    
149    
150        /**
151         * Creates a new instance of LdifPartition.
152         */
153        public LdifPartition()
154        {
155            wrappedPartition = new AvlPartition();
156        }
157    
158    
159        /**
160         * {@inheritDoc}
161         */
162        public void initialize() throws Exception
163        {
164            // Initialize the AvlPartition
165            wrappedPartition.setId( id );
166            wrappedPartition.setSuffix( suffix.getName() );
167            wrappedPartition.setSchemaManager( schemaManager );
168            wrappedPartition.initialize();
169    
170            // Create the CsnFactory with a invalid ReplicaId
171            // @TODO : inject a correct ReplicaId
172            defaultCSNFactory = new CsnFactory( 0 );
173    
174            this.searchEngine = wrappedPartition.getSearchEngine();
175    
176            LOG.debug( "id is : {}", wrappedPartition.getId() );
177    
178            // Initialize the suffixDirectory : it's a composition
179            // of the workingDirectory followed by the suffix
180            if ( ( suffix == null ) || ( suffix.isEmpty() ) )
181            {
182                String msg = I18n.err( I18n.ERR_150 );
183                LOG.error( msg );
184                throw new InvalidNameException( msg );
185            }
186    
187            if ( !suffix.isNormalized() )
188            {
189                suffix.normalize( schemaManager.getNormalizerMapping() );
190            }
191    
192            String suffixDirName = getFileName( suffix );
193            suffixDirectory = new File( workingDirectory, suffixDirName );
194    
195            // Create the context entry now, if it does not exists, or load the
196            // existing entries
197            if ( suffixDirectory.exists() )
198            {
199                loadEntries( new File( workingDirectory ) );
200            }
201            else
202            {
203                // The partition directory does not exist, we have to create it
204                try
205                {
206                    suffixDirectory.mkdir();
207                }
208                catch ( SecurityException se )
209                {
210                    String msg = I18n.err( I18n.ERR_151, suffixDirectory.getAbsolutePath(), se.getLocalizedMessage() );
211                    LOG.error( msg );
212                    throw se;
213                }
214    
215                // And create the context entry too
216                File contextEntryFile = new File( suffixDirectory + CONF_FILE_EXTN );
217    
218                LOG.info( "ldif file doesn't exist {}, creating it.", contextEntryFile.getAbsolutePath() );
219    
220                if ( contextEntry == null )
221                {
222                    if ( contextEntryFile.exists() )
223                    {
224                        LdifReader reader = new LdifReader( contextEntryFile );
225                        contextEntry = new DefaultServerEntry( schemaManager, reader.next().getEntry() );
226                        reader.close();
227                    }
228                    else
229                    {
230                        throw new NamingException( I18n.err( I18n.ERR_632 ) );
231                    }
232                }
233    
234                if ( contextEntry.get( SchemaConstants.ENTRY_CSN_AT ) == null )
235                {
236                    contextEntry.add( SchemaConstants.ENTRY_CSN_AT, defaultCSNFactory.newInstance().toString() );
237                }
238    
239                if ( contextEntry.get( SchemaConstants.ENTRY_UUID_AT ) == null )
240                {
241                    String uuid = UUID.randomUUID().toString();
242                    contextEntry.add( SchemaConstants.ENTRY_UUID_AT, uuid );
243                }
244    
245                FileWriter fw = new FileWriter( contextEntryFile );
246                fw.write( LdifUtils.convertEntryToLdif( contextEntry ) );
247                fw.close();
248    
249                // And add this entry to the underlying partition
250                wrappedPartition.getStore().add( contextEntry );
251            }
252        }
253    
254    
255        //-------------------------------------------------------------------------
256        // Operations
257        //-------------------------------------------------------------------------
258        /**
259         * {@inheritDoc}
260         */
261        @Override
262        public void add( AddOperationContext addContext ) throws Exception
263        {
264            wrappedPartition.add( addContext );
265            add( addContext.getEntry() );
266        }
267    
268    
269        /**
270         * {@inheritDoc}
271         */
272        public void bind( BindOperationContext bindContext ) throws Exception
273        {
274            wrappedPartition.bind( bindContext );
275        }
276    
277    
278        /**
279         * {@inheritDoc}
280         */
281        @Override
282        public void delete( Long id ) throws Exception
283        {
284            ServerEntry entry = lookup( id );
285    
286            wrappedPartition.delete( id );
287    
288            if ( entry != null )
289            {
290                File ldifFile = getFile( entry.getDn(), DELETE );
291    
292                boolean deleted = deleteFile( ldifFile );
293    
294                LOG.debug( "deleted file {} {}", ldifFile.getAbsoluteFile(), deleted );
295    
296                // Delete the parent if there is no more children
297                File parentFile = ldifFile.getParentFile();
298    
299                if ( parentFile.listFiles().length == 0 )
300                {
301                    deleteFile( parentFile );
302    
303                    LOG.debug( "deleted file {} {}", parentFile.getAbsoluteFile(), deleted );
304                }
305    
306            }
307    
308        }
309    
310    
311        /**
312         * {@inheritDoc}
313         */
314        @Override
315        public void modify( ModifyOperationContext modifyContext ) throws Exception
316        {
317            Long id = getEntryId( modifyContext.getDn().getNormName() );
318    
319            wrappedPartition.modify( id, modifyContext.getModItems() );
320    
321            // Get the modified entry and store it in the context for post usage
322            ClonedServerEntry modifiedEntry = lookup( id );
323            modifyContext.setAlteredEntry( modifiedEntry );
324    
325            // just overwrite the existing file
326            DN dn = modifyContext.getDn();
327    
328            // And write it back on disk
329            FileWriter fw = new FileWriter( getFile( dn, DELETE ) );
330            fw.write( LdifUtils.convertEntryToLdif( modifiedEntry ) );
331            fw.close();
332        }
333    
334    
335        /**
336         * {@inheritDoc}
337         */
338        @Override
339        public void move( MoveOperationContext moveContext ) throws Exception
340        {
341            DN oldDn = moveContext.getDn();
342            Long id = getEntryId( oldDn.getNormName() );
343    
344            wrappedPartition.move( moveContext );
345    
346            // Get the modified entry
347            ClonedServerEntry modifiedEntry = lookup( id );
348    
349            entryMoved( oldDn, modifiedEntry, id, true );
350        }
351    
352    
353        /**
354         * {@inheritDoc}
355         */
356        @Override
357        public void moveAndRename( MoveAndRenameOperationContext moveAndRenameContext ) throws Exception
358        {
359            DN oldDn = moveAndRenameContext.getDn();
360            Long id = getEntryId( oldDn.getNormName() );
361    
362            wrappedPartition.moveAndRename( moveAndRenameContext );
363    
364            // Get the modified entry and store it in the context for post usage
365            ClonedServerEntry modifiedEntry = lookup( id );
366            moveAndRenameContext.setAlteredEntry( modifiedEntry );
367    
368            entryMoved( oldDn, modifiedEntry, id, moveAndRenameContext.getDelOldDn() );
369        }
370    
371    
372        /**
373         * {@inheritDoc}
374         */
375        @Override
376        public void rename( RenameOperationContext renameContext ) throws Exception
377        {
378            DN oldDn = renameContext.getDn();
379            Long id = getEntryId( oldDn.getNormName() );
380    
381            // Create the new entry 
382            wrappedPartition.rename( renameContext );
383    
384            // Get the modified entry and store it in the context for post usage
385            ClonedServerEntry modifiedEntry = lookup( id );
386            renameContext.setAlteredEntry( modifiedEntry );
387    
388            // Now move the potential children for the old entry
389            // and remove the old entry
390            entryMoved( oldDn, modifiedEntry, id, renameContext.getDelOldDn() );
391        }
392    
393    
394        /**
395         * rewrites the moved entry and its associated children
396         * Note that instead of moving and updating the existing files on disk
397         * this method gets the moved entry and its children and writes the LDIF files
398         *
399         * @param oldEntryDn the moved entry's old DN
400         * @param entryId the moved entry's master table ID
401         * @param deleteOldEntry a flag to tell whether to delete the old entry files
402         * @throws Exception
403         */
404        private void entryMoved( DN oldEntryDn, Entry modifiedEntry, Long entryIdOld, boolean deleteOldEntry )
405            throws Exception
406        {
407            // First, add the new entry
408            add( modifiedEntry );
409    
410            // Then, if there are some children, move then to the new place
411            IndexCursor<Long, ServerEntry, Long> cursor = getSubLevelIndex().forwardCursor( entryIdOld );
412    
413            while ( cursor.next() )
414            {
415                IndexEntry<Long, ServerEntry, Long> entry = cursor.get();
416    
417                // except the parent entry add the rest of entries
418                if ( entry.getId() != entryIdOld )
419                {
420                    add( wrappedPartition.lookup( entry.getId() ) );
421                }
422            }
423    
424            cursor.close();
425    
426            if ( deleteOldEntry )
427            {
428                // And delete the old entry's LDIF file
429                File file = getFile( oldEntryDn, DELETE );
430                boolean deleted = deleteFile( file );
431                LOG.warn( "move operation: deleted file {} {}", file.getAbsoluteFile(), deleted );
432    
433                // and the associated directory ( the file's name's minus ".ldif")
434                String dirName = file.getAbsolutePath();
435                dirName = dirName.substring( 0, dirName.indexOf( CONF_FILE_EXTN ) );
436                deleted = deleteFile( new File( dirName ) );
437                LOG.warn( "move operation: deleted dir {} {}", dirName, deleted );
438            }
439        }
440    
441    
442        /**
443         * loads the configuration into the DIT from the file system
444         * Note that it assumes the presence of a directory with the partition suffix's upname
445         * under the partition's base dir
446         * 
447         * for ex. if 'config' is the partition's id and 'ou=config' is its suffix it looks for the dir with the path
448         * 
449         * <directory-service-working-dir>/config/ou=config
450         * e.x example.com/config/ou=config
451         * 
452         * NOTE: this dir setup is just to ease the testing of this partition, this needs to be 
453         * replaced with some kind of bootstrapping the default config from a jar file and
454         * write to the FS in LDIF format
455         * 
456         * @throws Exception
457         */
458        private void loadEntries( File entryDir ) throws Exception
459        {
460            LOG.debug( "Processing dir {}", entryDir.getName() );
461    
462            // First, load the entries
463            File[] entries = entryDir.listFiles( entryFilter );
464    
465            if ( ( entries != null ) && ( entries.length != 0 ) )
466            {
467                LdifReader ldifReader = new LdifReader();
468    
469                for ( File entry : entries )
470                {
471                    LOG.debug( "parsing ldif file {}", entry.getName() );
472                    List<LdifEntry> ldifEntries = ldifReader.parseLdifFile( entry.getAbsolutePath() );
473                    ldifReader.close();
474    
475                    if ( ( ldifEntries != null ) && !ldifEntries.isEmpty() )
476                    {
477                        // this ldif will have only one entry
478                        LdifEntry ldifEntry = ldifEntries.get( 0 );
479                        LOG.debug( "Adding entry {}", ldifEntry );
480    
481                        ServerEntry serverEntry = new DefaultServerEntry( schemaManager, ldifEntry.getEntry() );
482    
483                        if ( !serverEntry.containsAttribute( SchemaConstants.ENTRY_CSN_AT ) )
484                        {
485                            serverEntry.put( SchemaConstants.ENTRY_CSN_AT, defaultCSNFactory.newInstance().toString() );
486                        }
487    
488                        if ( !serverEntry.containsAttribute( SchemaConstants.ENTRY_UUID_AT ) )
489                        {
490                            serverEntry.put( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
491                        }
492    
493                        // call add on the wrapped partition not on the self
494                        wrappedPartition.getStore().add( serverEntry );
495                    }
496                }
497    
498            }
499            else
500            {
501                // If we don't have ldif files, we won't have sub-directories
502                return;
503            }
504    
505            // Second, recurse on the sub directories
506            File[] dirs = entryDir.listFiles( dirFilter );
507    
508            if ( ( dirs != null ) && ( dirs.length != 0 ) )
509            {
510                for ( File f : dirs )
511                {
512                    loadEntries( f );
513                }
514            }
515        }
516    
517    
518        /**
519         * Create the file name from the entry DN.
520         */
521        private File getFile( DN entryDn, boolean create ) throws NamingException
522        {
523            StringBuilder filePath = new StringBuilder();
524            filePath.append( suffixDirectory ).append( File.separator );
525    
526            DN baseDn = ( DN ) entryDn.getSuffix( suffix.size() );
527    
528            for ( int i = 0; i < baseDn.size() - 1; i++ )
529            {
530                String rdnFileName = getFileName( baseDn.getRdn( i ) );
531    
532                filePath.append( rdnFileName ).append( File.separator );
533            }
534    
535            String rdnFileName = getFileName( entryDn.getRdn() ) + CONF_FILE_EXTN;
536            String parentDir = filePath.toString();
537    
538            File dir = new File( parentDir );
539    
540            if ( !dir.exists() && create )
541            {
542                // We have to create the entry if it does not have a parent
543                dir.mkdir();
544            }
545    
546            File ldifFile = new File( parentDir + rdnFileName );
547    
548            if ( ldifFile.exists() && create )
549            {
550                // The entry already exists
551                throw new NamingException( I18n.err( I18n.ERR_633 ) );
552            }
553    
554            return ldifFile;
555        }
556    
557    
558        /**
559         * Compute the real name based on the RDN, assuming that depending on the underlying 
560         * OS, some characters are not allowed.
561         * 
562         * We don't allow filename which length is > 255 chars.
563         */
564        private String getFileName( RDN rdn ) throws NamingException
565        {
566            // First, get the AT name, or OID
567            String normAT = rdn.getAtav().getNormType();
568            AttributeType at = schemaManager.lookupAttributeTypeRegistry( normAT );
569    
570            String atName = at.getName();
571    
572            // Now, get the normalized value
573            String normValue = rdn.getAtav().getNormValue().getString();
574    
575            String fileName = atName + "=" + normValue;
576    
577            return getOSFileName( fileName );
578        }
579    
580    
581        /**
582         * Compute the real name based on the DN, assuming that depending on the underlying 
583         * OS, some characters are not allowed.
584         * 
585         * We don't allow filename which length is > 255 chars.
586         */
587        private String getFileName( DN dn ) throws NamingException
588        {
589            StringBuilder sb = new StringBuilder();
590            boolean isFirst = true;
591    
592            for ( RDN rdn : dn.getRdns() )
593            {
594                // First, get the AT name, or OID
595                String normAT = rdn.getAtav().getNormType();
596                AttributeType at = schemaManager.lookupAttributeTypeRegistry( normAT );
597    
598                String atName = at.getName();
599    
600                // Now, get the normalized value
601                String normValue = rdn.getAtav().getNormValue().getString();
602    
603                if ( isFirst )
604                {
605                    isFirst = false;
606                }
607                else
608                {
609                    sb.append( "," );
610                }
611    
612                sb.append( atName ).append( "=" ).append( normValue );
613            }
614    
615            return getOSFileName( sb.toString() );
616        }
617    
618    
619        /**
620         * Get a OS compatible file name
621         */
622        private String getOSFileName( String fileName )
623        {
624            if ( SystemUtils.IS_OS_WINDOWS )
625            {
626                // On Windows, we escape '/', '<', '>', '\', '|', '"', ':', '+', ' ', '[', ']', 
627                // '*', [0x00-0x1F], '?'
628                StringBuilder sb = new StringBuilder();
629    
630                for ( char c : fileName.toCharArray() )
631                {
632                    switch ( c )
633                    {
634                        case 0x00:
635                        case 0x01:
636                        case 0x02:
637                        case 0x03:
638                        case 0x04:
639                        case 0x05:
640                        case 0x06:
641                        case 0x07:
642                        case 0x08:
643                        case 0x09:
644                        case 0x0A:
645                        case 0x0B:
646                        case 0x0C:
647                        case 0x0D:
648                        case 0x0E:
649                        case 0x0F:
650                        case 0x10:
651                        case 0x11:
652                        case 0x12:
653                        case 0x13:
654                        case 0x14:
655                        case 0x15:
656                        case 0x16:
657                        case 0x17:
658                        case 0x18:
659                        case 0x19:
660                        case 0x1A:
661                        case 0x1B:
662                        case 0x1C:
663                        case 0x1D:
664                        case 0x1E:
665                        case 0x1F:
666                            sb.append( "\\" ).append( StringTools.dumpHex( ( byte ) ( c >> 4 ) ) ).append(
667                                StringTools.dumpHex( ( byte ) ( c & 0x04 ) ) );
668                            break;
669    
670                        case '/':
671                        case '\\':
672                        case '<':
673                        case '>':
674                        case '|':
675                        case '"':
676                        case ':':
677                        case '+':
678                        case ' ':
679                        case '[':
680                        case ']':
681                        case '*':
682                        case '?':
683                            sb.append( '\\' ).append( c );
684                            break;
685    
686                        default:
687                            sb.append( c );
688                            break;
689                    }
690                }
691    
692                return sb.toString().toLowerCase();
693            }
694            else
695            {
696                // On linux, just escape '/' and null
697                StringBuilder sb = new StringBuilder();
698    
699                for ( char c : fileName.toCharArray() )
700                {
701                    switch ( c )
702                    {
703                        case '/':
704                            sb.append( "\\/" );
705                            break;
706    
707                        case '\0':
708                            sb.append( "\\00" );
709                            break;
710    
711                        default:
712                            sb.append( c );
713                            break;
714                    }
715                }
716    
717                return sb.toString().toLowerCase();
718            }
719        }
720    
721    
722        /**
723         * Write the new entry on disk. It does not exist, as this ha sbeen checked
724         * by the ExceptionInterceptor.
725         */
726        private void add( Entry entry ) throws Exception
727        {
728            FileWriter fw = new FileWriter( getFile( entry.getDn(), CREATE ) );
729            fw.write( LdifUtils.convertEntryToLdif( entry ) );
730            fw.close();
731        }
732    
733    
734        /** 
735         * Recursively delete an entry and all of its children. If the entry is a directory, 
736         * then get into it, call the same method on each of the contained files,
737         * and delete the directory.
738         */
739        private boolean deleteFile( File file )
740        {
741            if ( file.isDirectory() )
742            {
743                File[] files = file.listFiles();
744    
745                // Process the contained files
746                for ( File f : files )
747                {
748                    deleteFile( f );
749                }
750    
751                // then delete the directory itself
752                return file.delete();
753            }
754            else
755            {
756                return file.delete();
757            }
758        }
759    
760    
761        @Override
762        public void addIndexOn( Index<? extends Object, ServerEntry, Long> index ) throws Exception
763        {
764            wrappedPartition.addIndexOn( index );
765        }
766    
767    
768        @Override
769        public int count() throws Exception
770        {
771            return wrappedPartition.count();
772        }
773    
774    
775        @Override
776        public void destroy() throws Exception
777        {
778            wrappedPartition.destroy();
779        }
780    
781    
782        @Override
783        public Index<String, ServerEntry, Long> getAliasIndex()
784        {
785            return wrappedPartition.getAliasIndex();
786        }
787    
788    
789        @Override
790        public int getChildCount( Long id ) throws Exception
791        {
792            return wrappedPartition.getChildCount( id );
793        }
794    
795    
796        @Override
797        public String getEntryDn( Long id ) throws Exception
798        {
799            return wrappedPartition.getEntryDn( id );
800        }
801    
802    
803        @Override
804        public Long getEntryId( String dn ) throws Exception
805        {
806            return wrappedPartition.getEntryId( dn );
807        }
808    
809    
810        @Override
811        public String getEntryUpdn( Long id ) throws Exception
812        {
813            return wrappedPartition.getEntryUpdn( id );
814        }
815    
816    
817        @Override
818        public String getEntryUpdn( String dn ) throws Exception
819        {
820            return wrappedPartition.getEntryUpdn( dn );
821        }
822    
823    
824        @Override
825        public Index<String, ServerEntry, Long> getNdnIndex()
826        {
827            return wrappedPartition.getNdnIndex();
828        }
829    
830    
831        @Override
832        public Index<Long, ServerEntry, Long> getOneAliasIndex()
833        {
834            return wrappedPartition.getOneAliasIndex();
835        }
836    
837    
838        @Override
839        public Index<Long, ServerEntry, Long> getOneLevelIndex()
840        {
841            return wrappedPartition.getOneLevelIndex();
842        }
843    
844    
845        @Override
846        public Long getParentId( Long childId ) throws Exception
847        {
848            return wrappedPartition.getParentId( childId );
849        }
850    
851    
852        @Override
853        public Long getParentId( String dn ) throws Exception
854        {
855            return wrappedPartition.getParentId( dn );
856        }
857    
858    
859        @Override
860        public Index<String, ServerEntry, Long> getPresenceIndex()
861        {
862            return wrappedPartition.getPresenceIndex();
863        }
864    
865    
866        @Override
867        public String getProperty( String propertyName ) throws Exception
868        {
869            return wrappedPartition.getProperty( propertyName );
870        }
871    
872    
873        @Override
874        public Index<Long, ServerEntry, Long> getSubAliasIndex()
875        {
876            return wrappedPartition.getSubAliasIndex();
877        }
878    
879    
880        @Override
881        public Index<Long, ServerEntry, Long> getSubLevelIndex()
882        {
883            return wrappedPartition.getSubLevelIndex();
884        }
885    
886    
887        @Override
888        public Index<?, ServerEntry, Long> getSystemIndex( String id ) throws Exception
889        {
890            return wrappedPartition.getSystemIndex( id );
891        }
892    
893    
894        @Override
895        public Iterator<String> getSystemIndices()
896        {
897            return wrappedPartition.getSystemIndices();
898        }
899    
900    
901        @Override
902        public Index<String, ServerEntry, Long> getUpdnIndex()
903        {
904            return wrappedPartition.getUpdnIndex();
905        }
906    
907    
908        @Override
909        public Index<? extends Object, ServerEntry, Long> getUserIndex( String id ) throws Exception
910        {
911            return wrappedPartition.getUserIndex( id );
912        }
913    
914    
915        @Override
916        public Iterator<String> getUserIndices()
917        {
918            return wrappedPartition.getUserIndices();
919        }
920    
921    
922        @Override
923        public boolean hasSystemIndexOn( String id ) throws Exception
924        {
925            return wrappedPartition.hasSystemIndexOn( id );
926        }
927    
928    
929        @Override
930        public boolean hasUserIndexOn( String id ) throws Exception
931        {
932            return wrappedPartition.hasUserIndexOn( id );
933        }
934    
935    
936        @Override
937        public boolean isInitialized()
938        {
939            return wrappedPartition != null && wrappedPartition.isInitialized();
940        }
941    
942    
943        @Override
944        public IndexCursor<Long, ServerEntry, Long> list( Long id ) throws Exception
945        {
946            return wrappedPartition.list( id );
947        }
948    
949    
950        @Override
951        public ClonedServerEntry lookup( Long id ) throws Exception
952        {
953            return wrappedPartition.lookup( id );
954        }
955    
956    
957        @Override
958        public void setAliasIndexOn( Index<String, ServerEntry, Long> index ) throws Exception
959        {
960            wrappedPartition.setAliasIndexOn( index );
961        }
962    
963    
964        @Override
965        public void setNdnIndexOn( Index<String, ServerEntry, Long> index ) throws Exception
966        {
967            wrappedPartition.setNdnIndexOn( index );
968        }
969    
970    
971        @Override
972        public void setOneAliasIndexOn( Index<Long, ServerEntry, Long> index ) throws Exception
973        {
974            wrappedPartition.setOneAliasIndexOn( index );
975        }
976    
977    
978        @Override
979        public void setOneLevelIndexOn( Index<Long, ServerEntry, Long> index ) throws Exception
980        {
981            wrappedPartition.setOneLevelIndexOn( index );
982        }
983    
984    
985        @Override
986        public void setPresenceIndexOn( Index<String, ServerEntry, Long> index ) throws Exception
987        {
988            wrappedPartition.setPresenceIndexOn( index );
989        }
990    
991    
992        @Override
993        public void setProperty( String propertyName, String propertyValue ) throws Exception
994        {
995            wrappedPartition.setProperty( propertyName, propertyValue );
996        }
997    
998    
999        @Override
1000        public void setSchemaManager( SchemaManager schemaManager )
1001        {
1002            super.setSchemaManager( schemaManager );
1003        }
1004    
1005    
1006        @Override
1007        public void setSubAliasIndexOn( Index<Long, ServerEntry, Long> index ) throws Exception
1008        {
1009            wrappedPartition.setSubAliasIndexOn( index );
1010        }
1011    
1012    
1013        @Override
1014        public void setUpdnIndexOn( Index<String, ServerEntry, Long> index ) throws Exception
1015        {
1016            wrappedPartition.setUpdnIndexOn( index );
1017        }
1018    
1019    
1020        @Override
1021        public void sync() throws Exception
1022        {
1023            wrappedPartition.sync();
1024            //TODO implement the File I/O here to push the update to entries to the corresponding LDIF file
1025        }
1026    
1027    
1028        public void unbind( UnbindOperationContext unbindContext ) throws Exception
1029        {
1030            wrappedPartition.unbind( unbindContext );
1031        }
1032    
1033    
1034        @Override
1035        public String getId()
1036        {
1037            // TODO Auto-generated method stub
1038            return super.getId();
1039        }
1040    
1041    
1042        @Override
1043        public void setId( String id )
1044        {
1045            super.setId( id );
1046            wrappedPartition.setId( id );
1047        }
1048    
1049    
1050        @Override
1051        public void setSuffix( String suffix ) throws InvalidNameException
1052        {
1053            super.setSuffix( suffix );
1054            wrappedPartition.setSuffix( suffix );
1055        }
1056    
1057    
1058        /**
1059         * the interval at which the config directory containing LDIF files
1060         * should be scanned, default value is 10 min
1061         * 
1062         * @param ldifScanInterval the scan interval time in minutes
1063         */
1064        public void setLdifScanInterval( int ldifScanInterval )
1065        {
1066            this.ldifScanInterval = ldifScanInterval;
1067        }
1068    
1069    
1070        /**
1071         * @return the workingDirectory
1072         */
1073        public String getWorkingDirectory()
1074        {
1075            return workingDirectory;
1076        }
1077    
1078    
1079        /**
1080         * @param workingDirectory the workingDirectory to set
1081         */
1082        public void setWorkingDirectory( String workingDirectory )
1083        {
1084            this.workingDirectory = workingDirectory;
1085        }
1086    
1087    
1088        /**
1089         * @return the contextEntry
1090         */
1091        public Entry getContextEntry()
1092        {
1093            return contextEntry;
1094        }
1095    
1096    
1097        /**
1098         * @param contextEntry the contextEntry to set
1099         */
1100        public void setContextEntry( String contextEntry ) throws NamingException
1101        {
1102            LdifReader ldifReader = new LdifReader();
1103            List<LdifEntry> entries = ldifReader.parseLdif( contextEntry );
1104    
1105            try
1106            {
1107                ldifReader.close();
1108            }
1109            catch ( IOException ioe )
1110            {
1111                // What can we do here ???
1112            }
1113    
1114            this.contextEntry = new DefaultServerEntry( schemaManager, entries.get( 0 ).getEntry() );
1115        }
1116    
1117    
1118        /**
1119         * @return the wrappedPartition
1120         */
1121        public Partition getWrappedPartition()
1122        {
1123            return wrappedPartition;
1124        }
1125    
1126    
1127        /**
1128         * @param wrappedPartition the wrappedPartition to set
1129         */
1130        public void setWrappedPartition( AvlPartition wrappedPartition )
1131        {
1132            this.wrappedPartition = wrappedPartition;
1133        }
1134    }