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