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    
021    package org.apache.directory.server.core.partition.ldif;
022    
023    
024    import java.io.File;
025    import java.io.FileFilter;
026    import java.io.FileWriter;
027    import java.util.Iterator;
028    import java.util.List;
029    import java.util.Set;
030    
031    import org.apache.directory.server.core.partition.avl.AvlStore;
032    import org.apache.directory.server.i18n.I18n;
033    import org.apache.directory.server.xdbm.Index;
034    import org.apache.directory.server.xdbm.IndexCursor;
035    import org.apache.directory.server.xdbm.IndexNotFoundException;
036    import org.apache.directory.server.xdbm.Store;
037    import org.apache.directory.shared.ldap.entry.DefaultServerEntry;
038    import org.apache.directory.shared.ldap.entry.Modification;
039    import org.apache.directory.shared.ldap.entry.ModificationOperation;
040    import org.apache.directory.shared.ldap.entry.ServerEntry;
041    import org.apache.directory.shared.ldap.ldif.LdifEntry;
042    import org.apache.directory.shared.ldap.ldif.LdifReader;
043    import org.apache.directory.shared.ldap.ldif.LdifUtils;
044    import org.apache.directory.shared.ldap.name.DN;
045    import org.apache.directory.shared.ldap.name.RDN;
046    import org.apache.directory.shared.ldap.schema.SchemaManager;
047    import org.slf4j.Logger;
048    import org.slf4j.LoggerFactory;
049    
050    
051    /**
052     * TODO LdifStore.
053     *
054     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
055     * @version $Rev$, $Date$
056     */
057    public class LdifStore<E> implements Store<E, Long>
058    {
059    
060        /** the working directory to use for files */
061        private File workingDirectory;
062    
063        /** true if we sync disks on every write operation */
064        private boolean isSyncOnWrite = true;
065    
066        /** in memory store used for serving the config data present in LDIF files  */
067        private AvlStore<E> wrappedStore = new AvlStore<E>();
068    
069        private SchemaManager schemaManager;
070    
071        private LdifReader ldifReader;
072    
073        private FileFilter dirFilter = new FileFilter()
074        {
075            public boolean accept( File dir )
076            {
077                return dir.isDirectory();
078            }
079        };
080    
081        private static final String CONF_FILE_EXTN = ".ldif";
082    
083        private static Logger LOG = LoggerFactory.getLogger( LdifStore.class );
084    
085    
086        public void init( SchemaManager schemaManager ) throws Exception
087        {
088            this.schemaManager = schemaManager;
089            wrappedStore.init( schemaManager );
090    
091            // load the config 
092            loadConfig();
093        }
094    
095    
096        /**
097         * loads the configuration into the DIT from the file system
098         * @throws Exception
099         */
100        public void loadConfig() throws Exception
101        {
102            String upsuffixDir = wrappedStore.getUpSuffix().getName().toLowerCase();
103            File dir = new File( workingDirectory, upsuffixDir );
104    
105            if ( !dir.exists() )
106            {
107                throw new Exception( I18n.err( I18n.ERR_631, upsuffixDir, workingDirectory.getAbsolutePath() ) );
108            }
109    
110            loadEntry( dir );
111        }
112    
113    
114        /*
115         * recursively load the configuration entries
116         */
117        private void loadEntry( File entryDir ) throws Exception
118        {
119            LOG.debug( "processing dir {}", entryDir.getName() );
120    
121            File ldifFile = new File( entryDir, entryDir.getName() + CONF_FILE_EXTN );
122    
123            try
124            {
125    
126                ldifReader = new LdifReader();
127    
128                if ( ldifFile.exists() )
129                {
130                    LOG.debug( "parsing ldif file {}", ldifFile.getName() );
131                    List<LdifEntry> entries = ldifReader.parseLdifFile( ldifFile.getAbsolutePath() );
132    
133                    if ( entries != null && !entries.isEmpty() )
134                    {
135                        // this ldif will have only one entry
136                        LdifEntry ldifEntry = entries.get( 0 );
137                        LOG.debug( "adding entry {}", ldifEntry );
138    
139                        ServerEntry serverEntry = new DefaultServerEntry( schemaManager, ldifEntry.getEntry() );
140    
141                        // call add on the wrapped store not on the self  
142                        wrappedStore.add( serverEntry );
143                    }
144                }
145                else
146                {
147                    // TODO do we need to bomb out if the expected LDIF file doesn't exist
148                    // I think so
149                    LOG.warn( "ldif file doesn't exist {}", ldifFile.getAbsolutePath() );
150                }
151            }
152            finally
153            {
154                ldifReader.close();
155            }
156    
157            File[] dirs = entryDir.listFiles( dirFilter );
158    
159            if ( dirs != null )
160            {
161                for ( File f : dirs )
162                {
163                    loadEntry( f );
164                }
165            }
166        }
167    
168    
169        private File getFile( DN entryDn )
170        {
171            int size = entryDn.size();
172    
173            StringBuilder filePath = new StringBuilder();
174            filePath.append( workingDirectory.getAbsolutePath() ).append( File.separator );
175    
176            for ( int i = 0; i < size; i++ )
177            {
178                filePath.append( entryDn.getRdn( i ).getName().toLowerCase() ).append( File.separator );
179            }
180    
181            File dir = new File( filePath.toString() );
182            dir.mkdirs();
183    
184            return new File( dir, entryDn.getRdn().getName().toLowerCase() + CONF_FILE_EXTN );
185        }
186    
187    
188        public void add( ServerEntry entry ) throws Exception
189        {
190            wrappedStore.add( entry );
191    
192            FileWriter fw = new FileWriter( getFile( entry.getDn() ) );
193            fw.write( LdifUtils.convertEntryToLdif( entry ) );
194            fw.close();
195        }
196    
197    
198        public void delete( Long id ) throws Exception
199        {
200            ServerEntry entry = lookup( id );
201            LOG.warn( "deleting the entry with id {} and dn {}", id, entry.getDn() );
202    
203            LOG.warn( "having the parent id {}", getParentId( entry.getDn().getName() ) );
204            wrappedStore.delete( id );
205    
206            if ( entry != null )
207            {
208                File file = getFile( entry.getDn() ).getParentFile();
209                boolean deleted = deleteFile( file );
210                LOG.warn( "deleted file {} {}", file.getAbsoluteFile(), deleted );
211            }
212        }
213    
214    
215        private boolean deleteFile( File file )
216        {
217            if ( file.isDirectory() )
218            {
219                File[] files = file.listFiles();
220                for ( File f : files )
221                {
222                    deleteFile( f );
223                }
224            }
225    
226            return file.delete();
227        }
228    
229    
230        public void destroy() throws Exception
231        {
232            wrappedStore.destroy();
233        }
234    
235    
236        public void modify( DN dn, List<Modification> mods ) throws Exception
237        {
238            wrappedStore.modify( dn, mods );
239        }
240    
241    
242        public void modify( DN dn, ModificationOperation modOp, ServerEntry mods ) throws Exception
243        {
244            wrappedStore.modify( dn, modOp, mods );
245        }
246    
247    
248        public void move( DN oldChildDn, DN newParentDn, RDN newRdn, boolean deleteOldRdn ) throws Exception
249        {
250            wrappedStore.move( oldChildDn, newParentDn, newRdn, deleteOldRdn );
251        }
252    
253    
254        public void move( DN oldChildDn, DN newParentDn ) throws Exception
255        {
256            wrappedStore.move( oldChildDn, newParentDn );
257        }
258    
259    
260        public void rename( DN dn, RDN newRdn, boolean deleteOldRdn ) throws Exception
261        {
262            wrappedStore.rename( dn, newRdn, deleteOldRdn );
263        }
264    
265    
266        public void sync() throws Exception
267        {
268            //TODO implement the File I/O here to push the update to entries to the corresponding LDIF file
269        }
270    
271    
272        public void setWorkingDirectory( File workingDirectory )
273        {
274            this.workingDirectory = workingDirectory;
275        }
276    
277    
278        public File getWorkingDirectory()
279        {
280            return workingDirectory;
281        }
282    
283    
284        public void setSyncOnWrite( boolean isSyncOnWrite )
285        {
286            this.isSyncOnWrite = isSyncOnWrite;
287        }
288    
289    
290        public boolean isSyncOnWrite()
291        {
292            return isSyncOnWrite;
293        }
294    
295    
296        public void addIndex( Index<?, E, Long> index ) throws Exception
297        {
298            wrappedStore.addIndex( index );
299        }
300    
301    
302        public int count() throws Exception
303        {
304            return wrappedStore.count();
305        }
306    
307    
308        public Index<String, E, Long> getAliasIndex()
309        {
310            return wrappedStore.getAliasIndex();
311        }
312    
313    
314        public int getChildCount( Long id ) throws Exception
315        {
316            return wrappedStore.getChildCount( id );
317        }
318    
319    
320        public String getEntryDn( Long id ) throws Exception
321        {
322            return wrappedStore.getEntryDn( id );
323        }
324    
325    
326        public Long getEntryId( String dn ) throws Exception
327        {
328            return wrappedStore.getEntryId( dn );
329        }
330    
331    
332        public String getEntryUpdn( Long arg0 ) throws Exception
333        {
334            return wrappedStore.getEntryUpdn( arg0 );
335        }
336    
337    
338        public String getEntryUpdn( String dn ) throws Exception
339        {
340            return wrappedStore.getEntryUpdn( dn );
341        }
342    
343    
344        public String getName()
345        {
346            return wrappedStore.getName();
347        }
348    
349    
350        public Index<String, E, Long> getNdnIndex()
351        {
352            return wrappedStore.getNdnIndex();
353        }
354    
355    
356        public Index<Long, E, Long> getOneAliasIndex()
357        {
358            return wrappedStore.getOneAliasIndex();
359        }
360    
361    
362        public Index<Long, E, Long> getOneLevelIndex()
363        {
364            return wrappedStore.getOneLevelIndex();
365        }
366    
367    
368        public Long getParentId( Long arg0 ) throws Exception
369        {
370            return wrappedStore.getParentId( arg0 );
371        }
372    
373    
374        public Long getParentId( String dn ) throws Exception
375        {
376            return wrappedStore.getParentId( dn );
377        }
378    
379    
380        public Index<String, E, Long> getPresenceIndex()
381        {
382            return wrappedStore.getPresenceIndex();
383        }
384    
385    
386        public String getProperty( String propertyName ) throws Exception
387        {
388            return wrappedStore.getProperty( propertyName );
389        }
390    
391    
392        public Index<Long, E, Long> getSubAliasIndex()
393        {
394            return wrappedStore.getSubAliasIndex();
395        }
396    
397    
398        public Index<Long, E, Long> getSubLevelIndex()
399        {
400            return wrappedStore.getSubLevelIndex();
401        }
402    
403    
404        public Index<?, E, Long> getIndex( String id ) throws IndexNotFoundException
405        {
406            return wrappedStore.getIndex( id );
407        }
408    
409    
410        public Index<?, E, Long> getSystemIndex( String id ) throws IndexNotFoundException
411        {
412            return wrappedStore.getSystemIndex( id );
413        }
414    
415    
416        public Index<String, E, Long> getUpdnIndex()
417        {
418            return wrappedStore.getUpdnIndex();
419        }
420    
421    
422        public Index<?, E, Long> getUserIndex( String id ) throws IndexNotFoundException
423        {
424            return wrappedStore.getUserIndex( id );
425        }
426    
427    
428        public Set<Index<?, E, Long>> getUserIndices()
429        {
430            return wrappedStore.getUserIndices();
431        }
432    
433    
434        public boolean hasIndexOn( String id ) throws Exception
435        {
436            return wrappedStore.hasIndexOn( id );
437        }
438    
439    
440        public boolean hasSystemIndexOn( String id ) throws Exception
441        {
442            return wrappedStore.hasSystemIndexOn( id );
443        }
444    
445    
446        public boolean hasUserIndexOn( String id ) throws Exception
447        {
448            return wrappedStore.hasUserIndexOn( id );
449        }
450    
451    
452        public boolean isInitialized()
453        {
454            return wrappedStore.isInitialized();
455        }
456    
457    
458        public IndexCursor<Long, E, Long> list( Long id ) throws Exception
459        {
460            return wrappedStore.list( id );
461        }
462    
463    
464        public ServerEntry lookup( Long id ) throws Exception
465        {
466            return wrappedStore.lookup( id );
467        }
468    
469    
470        public void setAliasIndex( Index<String, E, Long> index ) throws Exception
471        {
472            wrappedStore.setAliasIndex( index );
473        }
474    
475    
476        public void setName( String name )
477        {
478            wrappedStore.setName( name );
479        }
480    
481    
482        public void setNdnIndex( Index<String, E, Long> index ) throws Exception
483        {
484            wrappedStore.setNdnIndex( index );
485        }
486    
487    
488        public void setOneAliasIndex( Index<Long, E, Long> index ) throws Exception
489        {
490            wrappedStore.setOneAliasIndex( index );
491        }
492    
493    
494        public void setOneLevelIndex( Index<Long, E, Long> index ) throws Exception
495        {
496            wrappedStore.setOneLevelIndex( index );
497        }
498    
499    
500        public void setPresenceIndex( Index<String, E, Long> index ) throws Exception
501        {
502            wrappedStore.setPresenceIndex( index );
503        }
504    
505    
506        public void setProperty( String propertyName, String propertyValue ) throws Exception
507        {
508            wrappedStore.setProperty( propertyName, propertyValue );
509        }
510    
511    
512        public void setSubAliasIndex( Index<Long, E, Long> index ) throws Exception
513        {
514            wrappedStore.setSubAliasIndex( index );
515        }
516    
517    
518        public void setSubLevelIndex( Index<Long, E, Long> index ) throws Exception
519        {
520            wrappedStore.setSubLevelIndex( index );
521        }
522    
523    
524        public void setUpdnIndex( Index<String, E, Long> index ) throws Exception
525        {
526            wrappedStore.setUpdnIndex( index );
527        }
528    
529    
530        public Iterator<String> systemIndices()
531        {
532            return wrappedStore.systemIndices();
533        }
534    
535    
536        public Iterator<String> userIndices()
537        {
538            return wrappedStore.userIndices();
539        }
540    
541    
542        //TODO manage the cache size??
543        public int getCacheSize()
544        {
545            return wrappedStore.getCacheSize();
546        }
547    
548    
549        public Index<String, E, Long> getEntryCsnIndex()
550        {
551            return wrappedStore.getEntryCsnIndex();
552        }
553    
554    
555        public Index<String, E, Long> getEntryUuidIndex()
556        {
557            return wrappedStore.getEntryUuidIndex();
558        }
559    
560    
561        public Index<String, E, Long> getObjectClassIndex()
562        {
563            return wrappedStore.getObjectClassIndex();
564        }
565    
566    
567        public DN getSuffix()
568        {
569            return wrappedStore.getSuffix();
570        }
571    
572    
573        public String getSuffixDn()
574        {
575            return wrappedStore.getSuffixDn();
576        }
577    
578    
579        public DN getUpSuffix()
580        {
581            return wrappedStore.getUpSuffix();
582        }
583    
584    
585        public void setCacheSize( int size )
586        {
587            wrappedStore.setCacheSize( size );
588        }
589    
590    
591        public void setUserIndices( Set<Index<?, E, Long>> userIndices )
592        {
593            wrappedStore.setUserIndices( userIndices );
594        }
595    
596    
597        public void setSuffixDn( String suffixDn )
598        {
599            wrappedStore.setSuffixDn( suffixDn );
600        }
601    
602    
603        public void setEntryCsnIndex( Index<String, E, Long> index ) throws Exception
604        {
605            wrappedStore.setEntryCsnIndex( index );
606        }
607    
608    
609        public void setEntryUuidIndex( Index<String, E, Long> index ) throws Exception
610        {
611            wrappedStore.setEntryUuidIndex( index );
612        }
613    
614    
615        public void setObjectClassIndex( Index<String, E, Long> index ) throws Exception
616        {
617            wrappedStore.setObjectClassIndex( index );
618        }
619    
620    
621        public Long getDefaultId()
622        {
623            return 1L;
624        }
625    
626    }