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 java.io.File; 024 import java.io.FileFilter; 025 import java.io.IOException; 026 import java.util.ArrayList; 027 import java.util.List; 028 import java.util.Set; 029 030 import javax.naming.NamingException; 031 032 import org.apache.commons.lang.StringUtils; 033 import org.apache.directory.server.constants.ApacheSchemaConstants; 034 import org.apache.directory.server.constants.ServerDNConstants; 035 import org.apache.directory.server.core.DefaultDirectoryService; 036 import org.apache.directory.server.core.DirectoryService; 037 import org.apache.directory.server.core.entry.ClonedServerEntry; 038 import org.apache.directory.server.core.partition.Partition; 039 import org.apache.directory.server.core.partition.impl.btree.BTreePartition; 040 import org.apache.directory.server.core.partition.ldif.LdifPartition; 041 import org.apache.directory.server.core.schema.SchemaPartition; 042 import org.apache.directory.server.i18n.I18n; 043 import org.apache.directory.server.ldap.LdapServer; 044 import org.apache.directory.server.protocol.shared.store.LdifFileLoader; 045 import org.apache.directory.server.protocol.shared.store.LdifLoadFilter; 046 import org.apache.directory.shared.ldap.constants.SchemaConstants; 047 import org.apache.directory.shared.ldap.entry.ServerEntry; 048 import org.apache.directory.shared.ldap.name.DN; 049 import org.apache.directory.shared.ldap.schema.SchemaManager; 050 import org.apache.directory.shared.ldap.schema.ldif.extractor.SchemaLdifExtractor; 051 import org.apache.directory.shared.ldap.schema.ldif.extractor.impl.DefaultSchemaLdifExtractor; 052 import org.apache.directory.shared.ldap.schema.loader.ldif.LdifSchemaLoader; 053 import org.apache.directory.shared.ldap.schema.manager.impl.DefaultSchemaManager; 054 import org.apache.directory.shared.ldap.schema.registries.SchemaLoader; 055 import org.apache.directory.shared.ldap.util.ExceptionUtils; 056 import org.apache.directory.shared.ldap.util.StringTools; 057 import org.slf4j.Logger; 058 import org.slf4j.LoggerFactory; 059 060 061 /** 062 * Apache Directory Server top level. 063 * 064 * @org.apache.xbean.XBean 065 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 066 * @version $Rev$ 067 */ 068 public class ApacheDS 069 { 070 private static final Logger LOG = LoggerFactory.getLogger( ApacheDS.class.getName() ); 071 072 /** Default delay between two flushes to the backend */ 073 private static final long DEFAULT_SYNC_PERIOD_MILLIS = 20000; 074 075 /** Wainting period between two flushes to the backend */ 076 private long synchPeriodMillis = DEFAULT_SYNC_PERIOD_MILLIS; 077 078 /** Directory where are stored the LDIF files to be loaded at startup */ 079 private File ldifDirectory; 080 081 private final List<LdifLoadFilter> ldifFilters = new ArrayList<LdifLoadFilter>(); 082 083 /** The LDAP server protocol handler */ 084 private final LdapServer ldapServer; 085 086 /** The directory service */ 087 private DirectoryService directoryService; 088 089 090 /** 091 * Creates a new instance of the ApacheDS server 092 * 093 * @param directoryService 094 * @param ldapServer 095 */ 096 public ApacheDS( LdapServer ldapServer ) throws Exception 097 { 098 LOG.info( "Starting the Apache Directory Server" ); 099 100 this.ldapServer = ldapServer; 101 102 directoryService = ldapServer.getDirectoryService(); 103 104 if ( directoryService == null ) 105 { 106 directoryService = new DefaultDirectoryService(); 107 } 108 } 109 110 111 /** 112 * Start the server : 113 * <li>initialize the DirectoryService</li> 114 * <li>start the LDAP server</li> 115 * <li>start the LDAPS server</li> 116 * 117 * @throws NamingException If the server cannot be started 118 * @throws IOException If an IO error occured while reading some file 119 */ 120 public void startup() throws Exception 121 { 122 LOG.debug( "Starting the server" ); 123 124 initSchema(); 125 126 SchemaManager schemaManager = directoryService.getSchemaManager(); 127 128 if ( ! directoryService.isStarted() ) 129 { 130 // inject the schema manager and set the partition directory 131 // once the CiDIT gets done we need not do this kind of dirty hack 132 Set<? extends Partition> partitions = directoryService.getPartitions(); 133 134 for( Partition p : partitions ) 135 { 136 if( p instanceof BTreePartition ) 137 { 138 ( ( BTreePartition ) p ).setPartitionDir( new File( directoryService.getWorkingDirectory(), p.getId() ) ); 139 } 140 141 if( p.getSchemaManager() == null ) 142 { 143 LOG.info( "setting the schema manager for partition {}", p.getSuffix() ); 144 p.setSchemaManager( schemaManager ); 145 } 146 } 147 148 Partition sysPartition = directoryService.getSystemPartition(); 149 150 if( sysPartition instanceof BTreePartition ) 151 { 152 ( ( BTreePartition ) sysPartition ).setPartitionDir( new File( directoryService.getWorkingDirectory(), sysPartition.getId() ) ); 153 } 154 155 if( sysPartition.getSchemaManager() == null ) 156 { 157 LOG.info( "setting the schema manager for partition {}", sysPartition.getSuffix() ); 158 sysPartition.setSchemaManager( schemaManager ); 159 } 160 161 // Start the directory service if not started yet 162 LOG.debug( "1. Starting the DirectoryService" ); 163 directoryService.startup(); 164 } 165 166 // Load the LDIF files - if any - into the server 167 loadLdifs(); 168 169 // Start the LDAP server 170 if ( ldapServer != null && ! ldapServer.isStarted() ) 171 { 172 LOG.debug( "3. Starting the LDAP server" ); 173 ldapServer.start(); 174 } 175 176 LOG.debug( "Server successfully started" ); 177 } 178 179 180 public boolean isStarted() 181 { 182 if ( ldapServer != null ) 183 { 184 return ( ldapServer.isStarted() ); 185 } 186 187 return directoryService.isStarted(); 188 } 189 190 191 public void shutdown() throws Exception 192 { 193 if ( ldapServer != null && ldapServer.isStarted() ) 194 { 195 ldapServer.stop(); 196 } 197 198 directoryService.shutdown(); 199 } 200 201 202 public LdapServer getLdapServer() 203 { 204 return ldapServer; 205 } 206 207 208 public DirectoryService getDirectoryService() 209 { 210 return directoryService; 211 } 212 213 214 public long getSynchPeriodMillis() 215 { 216 return synchPeriodMillis; 217 } 218 219 220 public void setSynchPeriodMillis( long synchPeriodMillis ) 221 { 222 LOG.info( "Set the synchPeriodMillis to {}", synchPeriodMillis ); 223 this.synchPeriodMillis = synchPeriodMillis; 224 } 225 226 227 /** 228 * Get the directory where 229 * @return 230 */ 231 public File getLdifDirectory() 232 { 233 return ldifDirectory; 234 } 235 236 237 public void setLdifDirectory( File ldifDirectory ) 238 { 239 LOG.info( "The LDIF directory file is {}", ldifDirectory.getAbsolutePath() ); 240 this.ldifDirectory = ldifDirectory; 241 } 242 243 244 // ---------------------------------------------------------------------- 245 // From CoreContextFactory: presently in intermediate step but these 246 // methods will be moved to the appropriate protocol service eventually. 247 // This is here simply to start to remove the JNDI dependency then further 248 // refactoring will be needed to place these where they belong. 249 // ---------------------------------------------------------------------- 250 251 252 /** 253 * Check that the entry where are stored the loaded Ldif files is created. 254 * 255 * If not, create it. 256 * 257 * The files are stored in ou=loadedLdifFiles,ou=configuration,ou=system 258 */ 259 private void ensureLdifFileBase() throws Exception 260 { 261 DN dn = new DN( ServerDNConstants.LDIF_FILES_DN ); 262 ServerEntry entry = null; 263 264 try 265 { 266 entry = directoryService.getAdminSession().lookup( dn ); 267 } 268 catch( Exception e ) 269 { 270 LOG.info( "Failure while looking up {}. The entry will be created now.", ServerDNConstants.LDIF_FILES_DN, e ); 271 } 272 273 if ( entry == null ) 274 { 275 entry = directoryService.newEntry( new DN( ServerDNConstants.LDIF_FILES_DN ) ); 276 entry.add( SchemaConstants.OU_AT, "loadedLdifFiles" ); 277 entry.add( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC ); 278 279 directoryService.getAdminSession().add( entry ); 280 } 281 } 282 283 284 /** 285 * Create a string containing a hex dump of the loaded ldif file name. 286 * 287 * It is associated with the attributeType wrt to the underlying system. 288 */ 289 private DN buildProtectedFileEntryDn( File ldif ) throws Exception 290 { 291 String fileSep = File.separatorChar == '\\' ? 292 ApacheSchemaConstants.WINDOWS_FILE_AT : 293 ApacheSchemaConstants.UNIX_FILE_AT; 294 295 return new DN( fileSep + 296 "=" + 297 StringTools.dumpHexPairs( StringTools.getBytesUtf8( getCanonical( ldif ) ) ) + 298 "," + 299 ServerDNConstants.LDIF_FILES_DN ); 300 } 301 302 303 private void addFileEntry( File ldif ) throws Exception 304 { 305 String rdnAttr = File.separatorChar == '\\' ? 306 ApacheSchemaConstants.WINDOWS_FILE_AT : 307 ApacheSchemaConstants.UNIX_FILE_AT; 308 String oc = File.separatorChar == '\\' ? ApacheSchemaConstants.WINDOWS_FILE_OC : ApacheSchemaConstants.UNIX_FILE_OC; 309 310 ServerEntry entry = directoryService.newEntry( buildProtectedFileEntryDn( ldif ) ); 311 entry.add( rdnAttr, getCanonical( ldif ) ); 312 entry.add( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, oc ); 313 directoryService.getAdminSession().add( entry ); 314 } 315 316 317 private String getCanonical( File file ) 318 { 319 String canonical; 320 321 try 322 { 323 canonical = file.getCanonicalPath(); 324 } 325 catch ( IOException e ) 326 { 327 LOG.error( I18n.err( I18n.ERR_179 ), e ); 328 return null; 329 } 330 331 return StringUtils.replace( canonical, "\\", "\\\\" ); 332 } 333 334 335 /** 336 * Load a ldif into the directory. 337 * 338 * @param root The context in which we will inject the entries 339 * @param ldifFile The ldif file to read 340 * @throws NamingException If something went wrong while loading the entries 341 */ 342 private void loadLdif( File ldifFile ) throws Exception 343 { 344 ClonedServerEntry fileEntry = null; 345 try 346 { 347 fileEntry = directoryService.getAdminSession().lookup( buildProtectedFileEntryDn( ldifFile ) ); 348 } 349 catch( Exception e ) 350 { 351 // if does not exist 352 } 353 354 if ( fileEntry != null ) 355 { 356 String time = ((ClonedServerEntry)fileEntry).getOriginalEntry().get( SchemaConstants.CREATE_TIMESTAMP_AT ).getString(); 357 LOG.info( "Load of LDIF file '" + getCanonical( ldifFile ) 358 + "' skipped. It has already been loaded on " + time + "." ); 359 } 360 else 361 { 362 LdifFileLoader loader = new LdifFileLoader( directoryService.getAdminSession(), ldifFile, ldifFilters ); 363 int count = loader.execute(); 364 LOG.info( "Loaded " + count + " entries from LDIF file '" + getCanonical( ldifFile ) + "'" ); 365 addFileEntry( ldifFile ); 366 } 367 } 368 369 370 /** 371 * Load the ldif files if there are some 372 */ 373 public void loadLdifs() throws Exception 374 { 375 // LOG and bail if property not set 376 if ( ldifDirectory == null ) 377 { 378 LOG.info( "LDIF load directory not specified. No LDIF files will be loaded." ); 379 return; 380 } 381 382 // LOG and bail if LDIF directory does not exists 383 if ( ! ldifDirectory.exists() ) 384 { 385 LOG.warn( "LDIF load directory '{}' does not exist. No LDIF files will be loaded.", 386 getCanonical( ldifDirectory ) ); 387 return; 388 } 389 390 391 DN dn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN ); 392 393 // Must normalize the dn or - IllegalStateException! 394 dn.normalize( directoryService.getSchemaManager().getNormalizerMapping() ); 395 396 ensureLdifFileBase(); 397 398 // if ldif directory is a file try to load it 399 if ( ldifDirectory.isFile() ) 400 { 401 if ( LOG.isInfoEnabled() ) 402 { 403 LOG.info( "LDIF load directory '{}' is a file. Will attempt to load as LDIF.", 404 getCanonical( ldifDirectory ) ); 405 } 406 407 try 408 { 409 loadLdif( ldifDirectory ); 410 } 411 catch ( Exception ne ) 412 { 413 // If the file can't be read, log the error, and stop 414 // loading LDIFs. 415 LOG.error( I18n.err( I18n.ERR_180, ldifDirectory.getAbsolutePath(), ne.getLocalizedMessage() ) ); 416 throw ne; 417 } 418 } 419 else 420 { 421 // get all the ldif files within the directory (should be sorted alphabetically) 422 File[] ldifFiles = ldifDirectory.listFiles( new FileFilter() 423 { 424 public boolean accept( File pathname ) 425 { 426 boolean isLdif = pathname.getName().toLowerCase().endsWith( ".ldif" ); 427 return pathname.isFile() && pathname.canRead() && isLdif; 428 } 429 } ); 430 431 // LOG and bail if we could not find any LDIF files 432 if ( ( ldifFiles == null ) || ( ldifFiles.length == 0 ) ) 433 { 434 LOG.warn( "LDIF load directory '{}' does not contain any LDIF files. No LDIF files will be loaded.", 435 getCanonical( ldifDirectory ) ); 436 return; 437 } 438 439 // load all the ldif files and load each one that is loaded 440 for ( File ldifFile : ldifFiles ) 441 { 442 try 443 { 444 LOG.info( "Loading LDIF file '{}'", ldifFile.getName() ); 445 loadLdif( ldifFile ); 446 } 447 catch ( Exception ne ) 448 { 449 // If the file can't be read, log the error, and stop 450 // loading LDIFs. 451 LOG.error( I18n.err( I18n.ERR_180, ldifFile.getAbsolutePath(), ne.getLocalizedMessage() ) ); 452 throw ne; 453 } 454 } 455 } 456 } 457 458 459 /** 460 * initialize the schema partition by loading the schema LDIF files 461 * 462 * @throws Exception in case of any problems while extracting and writing the schema files 463 */ 464 private void initSchema() throws Exception 465 { 466 SchemaPartition schemaPartition = directoryService.getSchemaService().getSchemaPartition(); 467 468 // Init the LdifPartition 469 LdifPartition ldifPartition = new LdifPartition(); 470 String workingDirectory = directoryService.getWorkingDirectory().getPath(); 471 ldifPartition.setWorkingDirectory( workingDirectory + "/schema" ); 472 473 // Extract the schema on disk (a brand new one) and load the registries 474 File schemaRepository = new File( workingDirectory, "schema" ); 475 476 if( schemaRepository.exists() ) 477 { 478 LOG.info( "schema partition already exists, skipping schema extraction" ); 479 } 480 else 481 { 482 SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor( new File( workingDirectory ) ); 483 extractor.extractOrCopy(); 484 } 485 486 schemaPartition.setWrappedPartition( ldifPartition ); 487 488 SchemaLoader loader = new LdifSchemaLoader( schemaRepository ); 489 SchemaManager schemaManager = new DefaultSchemaManager( loader ); 490 directoryService.setSchemaManager( schemaManager ); 491 492 // We have to load the schema now, otherwise we won't be able 493 // to initialize the Partitions, as we won't be able to parse 494 // and normalize their suffix DN 495 schemaManager.loadAllEnabled(); 496 497 schemaPartition.setSchemaManager( schemaManager ); 498 499 List<Throwable> errors = schemaManager.getErrors(); 500 501 if ( errors.size() != 0 ) 502 { 503 throw new Exception( I18n.err( I18n.ERR_317, ExceptionUtils.printErrors( errors ) ) ); 504 } 505 } 506 507 }