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.configuration;
021    
022    
023    import org.apache.commons.lang.StringUtils;
024    import org.apache.directory.server.constants.ApacheSchemaConstants;
025    import org.apache.directory.server.constants.ServerDNConstants;
026    import org.apache.directory.server.core.DefaultDirectoryService;
027    import org.apache.directory.server.core.DirectoryService;
028    import org.apache.directory.server.core.entry.ClonedServerEntry;
029    import org.apache.directory.server.core.entry.ServerEntry;
030    import org.apache.directory.server.ldap.LdapServer;
031    import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
032    import org.apache.directory.server.protocol.shared.store.LdifLoadFilter;
033    import org.apache.directory.server.schema.registries.AttributeTypeRegistry;
034    import org.apache.directory.shared.ldap.constants.SchemaConstants;
035    import org.apache.directory.shared.ldap.name.LdapDN;
036    import org.apache.directory.shared.ldap.util.StringTools;
037    import org.slf4j.Logger;
038    import org.slf4j.LoggerFactory;
039    
040    
041    import java.io.File;
042    import java.io.FileFilter;
043    import java.io.IOException;
044    import java.util.ArrayList;
045    import java.util.List;
046    
047    
048    /**
049     * Apache Directory Server top level.
050     *
051     * @org.apache.xbean.XBean
052     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
053     * @version $Rev$
054     */
055    public class ApacheDS
056    {
057        private static final Logger LOG = LoggerFactory.getLogger( ApacheDS.class.getName() );
058        
059        /** Default delay between two flushes to the backend */
060        private static final long DEFAULT_SYNC_PERIOD_MILLIS = 20000;
061    
062        /** Wainting period between two flushes to the backend */
063        private long synchPeriodMillis = DEFAULT_SYNC_PERIOD_MILLIS;
064    
065        /** Directory where are stored the LDIF files to be loaded at startup */
066        private File ldifDirectory;
067        
068        private final List<LdifLoadFilter> ldifFilters = new ArrayList<LdifLoadFilter>();
069    
070        /** The LDAP server protocol handler */
071        private final LdapServer ldapServer;
072        
073        /** The directory service */
074        private DirectoryService directoryService;
075    
076    
077        /**
078         * Creates a new instance of the ApacheDS server
079         *  
080         * @param directoryService 
081         * @param ldapServer
082         */
083        public ApacheDS( LdapServer ldapServer )
084        {
085            LOG.info( "Starting the Apache Directory Server" );
086    
087            this.ldapServer = ldapServer;
088            
089            directoryService = ldapServer.getDirectoryService();
090            
091            if ( directoryService == null )
092            {
093                directoryService = new DefaultDirectoryService();
094            }
095        }
096    
097    
098        /**
099         * Start the server :
100         *  <li>initialize the DirectoryService</li>
101         *  <li>start the LDAP server</li>
102         *  <li>start the LDAPS server</li>
103         *  
104         * @throws NamingException If the server cannot be started
105         * @throws IOException If an IO error occured while reading some file
106         */
107        public void startup() throws Exception
108        {
109            LOG.debug( "Starting the server" );
110            
111            // Start the directory service if not started yet
112            if ( ! directoryService.isStarted() )
113            {
114                LOG.debug( "1. Starting the DirectoryService" );
115                directoryService.startup();
116            }
117    
118            // Load the LDIF files - if any - into the server
119            loadLdifs();
120    
121            // Start the LDAP server
122            if ( ldapServer != null && ! ldapServer.isStarted() )
123            {
124                LOG.debug( "3. Starting the LDAP server" );
125                ldapServer.start();
126            }
127    
128            LOG.debug( "Server successfully started" );
129        }
130    
131    
132        public boolean isStarted()
133        {
134            if ( ldapServer != null )
135            {
136                 return ( ldapServer.isStarted() );
137            }
138            
139            return directoryService.isStarted();
140        }
141        
142    
143        public void shutdown() throws Exception
144        {
145            if ( ldapServer != null && ldapServer.isStarted() )
146            {
147                ldapServer.stop();
148            }
149    
150            directoryService.shutdown();
151        }
152    
153    
154        public LdapServer getLdapServer()
155        {
156            return ldapServer;
157        }
158    
159    
160        public DirectoryService getDirectoryService()
161        {
162            return directoryService;
163        }
164    
165    
166        public long getSynchPeriodMillis()
167        {
168            return synchPeriodMillis;
169        }
170    
171    
172        public void setSynchPeriodMillis( long synchPeriodMillis )
173        {
174            LOG.info( "Set the synchPeriodMillis to {}", synchPeriodMillis );
175            this.synchPeriodMillis = synchPeriodMillis;
176        }
177    
178        
179        /**
180         * Get the directory where 
181         * @return
182         */
183        public File getLdifDirectory()
184        {
185            return ldifDirectory;
186        }
187    
188    
189        public void setLdifDirectory( File ldifDirectory )
190        {
191            LOG.info( "The LDIF directory file is {}", ldifDirectory.getAbsolutePath() );
192            this.ldifDirectory = ldifDirectory;
193        }
194        
195        
196        // ----------------------------------------------------------------------
197        // From CoreContextFactory: presently in intermediate step but these
198        // methods will be moved to the appropriate protocol service eventually.
199        // This is here simply to start to remove the JNDI dependency then further
200        // refactoring will be needed to place these where they belong.
201        // ----------------------------------------------------------------------
202    
203    
204        /**
205         * Check that the entry where are stored the loaded Ldif files is created.
206         * 
207         * If not, create it.
208         * 
209         * The files are stored in ou=loadedLdifFiles,ou=configuration,ou=system
210         */
211        private void ensureLdifFileBase() throws Exception
212        {
213            LdapDN dn = new LdapDN( ServerDNConstants.LDIF_FILES_DN );
214            ServerEntry entry = null;
215            
216            try
217            {
218                entry = directoryService.getAdminSession().lookup( dn );
219            }
220            catch( Exception e )
221            {
222                LOG.info( "Failure while looking up {}. The entry will be created now.", ServerDNConstants.LDIF_FILES_DN, e );
223            }
224    
225            if ( entry == null )
226            {
227                entry = directoryService.newEntry( new LdapDN( ServerDNConstants.LDIF_FILES_DN ) );
228                entry.add( SchemaConstants.OU_AT, "loadedLdifFiles" );
229                entry.add( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
230        
231                directoryService.getAdminSession().add( entry );
232            }
233        }
234    
235    
236        /**
237         * Create a string containing a hex dump of the loaded ldif file name.
238         * 
239         * It is associated with the attributeType wrt to the underlying system.
240         */
241        private LdapDN buildProtectedFileEntryDn( File ldif ) throws Exception
242        {
243            String fileSep = File.separatorChar == '\\' ? 
244                    ApacheSchemaConstants.WINDOWS_FILE_AT : 
245                    ApacheSchemaConstants.UNIX_FILE_AT;
246    
247            return  new LdapDN( fileSep + 
248                    "=" + 
249                    StringTools.dumpHexPairs( StringTools.getBytesUtf8( getCanonical( ldif ) ) ) + 
250                    "," + 
251                    ServerDNConstants.LDIF_FILES_DN ); 
252        }
253    
254        
255        private void addFileEntry( File ldif ) throws Exception
256        {
257            String rdnAttr = File.separatorChar == '\\' ? 
258                ApacheSchemaConstants.WINDOWS_FILE_AT : 
259                ApacheSchemaConstants.UNIX_FILE_AT;
260            String oc = File.separatorChar == '\\' ? ApacheSchemaConstants.WINDOWS_FILE_OC : ApacheSchemaConstants.UNIX_FILE_OC;
261    
262            ServerEntry entry = directoryService.newEntry( buildProtectedFileEntryDn( ldif ) );
263            entry.add( rdnAttr, getCanonical( ldif ) );
264            entry.add( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, oc );
265            directoryService.getAdminSession().add( entry );
266        }
267    
268    
269        private String getCanonical( File file )
270        {
271            String canonical;
272    
273            try
274            {
275                canonical = file.getCanonicalPath();
276            }
277            catch ( IOException e )
278            {
279                LOG.error( "could not get canonical path", e );
280                return null;
281            }
282    
283            return StringUtils.replace( canonical, "\\", "\\\\" );
284        }
285    
286    
287        /**
288         * Load a ldif into the directory.
289         *  
290         * @param root The context in which we will inject the entries
291         * @param ldifFile The ldif file to read
292         * @throws NamingException If something went wrong while loading the entries
293         */
294        private void loadLdif( File ldifFile ) throws Exception
295        {
296            ClonedServerEntry fileEntry = null;
297            try
298            {
299                fileEntry = directoryService.getAdminSession().lookup( buildProtectedFileEntryDn( ldifFile ) );
300            }
301            catch( Exception e )
302            {
303                // if does not exist
304            }
305            
306            if ( fileEntry != null )
307            {
308                String time = ((ClonedServerEntry)fileEntry).getOriginalEntry().get( SchemaConstants.CREATE_TIMESTAMP_AT ).getString();
309                LOG.info( "Load of LDIF file '" + getCanonical( ldifFile )
310                        + "' skipped.  It has already been loaded on " + time + "." );
311            }
312            else
313            {
314                LdifFileLoader loader = new LdifFileLoader( directoryService.getAdminSession(), ldifFile, ldifFilters );
315                int count = loader.execute();
316                LOG.info( "Loaded " + count + " entries from LDIF file '" + getCanonical( ldifFile ) + "'" );
317                addFileEntry( ldifFile );
318            }
319        }
320        
321        
322        /**
323         * Load the ldif files if there are some
324         */
325        public void loadLdifs() throws Exception
326        {
327            // LOG and bail if property not set
328            if ( ldifDirectory == null )
329            {
330                LOG.info( "LDIF load directory not specified.  No LDIF files will be loaded." );
331                return;
332            }
333    
334            // LOG and bail if LDIF directory does not exists
335            if ( ! ldifDirectory.exists() )
336            {
337                LOG.warn( "LDIF load directory '{}' does not exist.  No LDIF files will be loaded.",
338                    getCanonical( ldifDirectory ) );
339                return;
340            }
341    
342    
343            LdapDN dn = new LdapDN( ServerDNConstants.ADMIN_SYSTEM_DN );
344            
345            // Must normalize the dn or - IllegalStateException!
346            AttributeTypeRegistry reg = directoryService.getRegistries().getAttributeTypeRegistry();
347            dn.normalize( reg.getNormalizerMapping() );
348            
349            ensureLdifFileBase();
350    
351            // if ldif directory is a file try to load it
352            if ( ldifDirectory.isFile() )
353            {
354                if ( LOG.isInfoEnabled() )
355                {
356                    LOG.info( "LDIF load directory '{}' is a file. Will attempt to load as LDIF.",
357                        getCanonical( ldifDirectory ) );
358                }
359    
360                try
361                {
362                    loadLdif( ldifDirectory );
363                }
364                catch ( Exception ne )
365                {
366                    // If the file can't be read, log the error, and stop
367                    // loading LDIFs.
368                    LOG.error( "Cannot load the ldif file '{}', error : ",
369                        ldifDirectory.getAbsolutePath(), 
370                        ne.getMessage() );
371                    throw ne;
372                }
373            }
374            else
375            {
376                // get all the ldif files within the directory (should be sorted alphabetically)
377                File[] ldifFiles = ldifDirectory.listFiles( new FileFilter()
378                {
379                    public boolean accept( File pathname )
380                    {
381                        boolean isLdif = pathname.getName().toLowerCase().endsWith( ".ldif" );
382                        return pathname.isFile() && pathname.canRead() && isLdif;
383                    }
384                } );
385        
386                // LOG and bail if we could not find any LDIF files
387                if ( ( ldifFiles == null ) || ( ldifFiles.length == 0 ) )
388                {
389                    LOG.warn( "LDIF load directory '{}' does not contain any LDIF files. No LDIF files will be loaded.", 
390                        getCanonical( ldifDirectory ) );
391                    return;
392                }
393        
394                // load all the ldif files and load each one that is loaded
395                for ( File ldifFile : ldifFiles )
396                {
397                    try
398                    {
399                        LOG.info(  "Loading LDIF file '{}'", ldifFile.getName() );
400                        loadLdif( ldifFile );
401                    }
402                    catch ( Exception ne )
403                    {
404                        // If the file can't be read, log the error, and stop
405                        // loading LDIFs.
406                        LOG.error( "Cannot load the ldif file '{}', error : {}", 
407                            ldifFile.getAbsolutePath(), 
408                            ne.getMessage() );
409                        throw ne;
410                    }
411                }
412            }
413        }
414    }