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 }