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;
021
022
023 import java.io.BufferedReader;
024 import java.io.File;
025 import java.io.IOException;
026 import java.io.StringReader;
027 import java.util.ArrayList;
028 import java.util.Arrays;
029 import java.util.HashSet;
030 import java.util.List;
031 import java.util.Map;
032 import java.util.Set;
033 import java.util.UUID;
034
035 import javax.naming.directory.Attributes;
036
037 import org.apache.directory.server.constants.ServerDNConstants;
038 import org.apache.directory.server.core.authn.AuthenticationInterceptor;
039 import org.apache.directory.server.core.authz.AciAuthorizationInterceptor;
040 import org.apache.directory.server.core.authz.DefaultAuthorizationInterceptor;
041 import org.apache.directory.server.core.changelog.ChangeLog;
042 import org.apache.directory.server.core.changelog.ChangeLogEvent;
043 import org.apache.directory.server.core.changelog.ChangeLogInterceptor;
044 import org.apache.directory.server.core.changelog.DefaultChangeLog;
045 import org.apache.directory.server.core.changelog.Tag;
046 import org.apache.directory.server.core.changelog.TaggableSearchableChangeLogStore;
047 import org.apache.directory.server.core.collective.CollectiveAttributeInterceptor;
048 import org.apache.directory.server.core.event.EventInterceptor;
049 import org.apache.directory.server.core.event.EventService;
050 import org.apache.directory.server.core.exception.ExceptionInterceptor;
051 import org.apache.directory.server.core.interceptor.Interceptor;
052 import org.apache.directory.server.core.interceptor.InterceptorChain;
053 import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
054 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
055 import org.apache.directory.server.core.interceptor.context.BindOperationContext;
056 import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
057 import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
058 import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
059 import org.apache.directory.server.core.journal.DefaultJournal;
060 import org.apache.directory.server.core.journal.Journal;
061 import org.apache.directory.server.core.journal.JournalInterceptor;
062 import org.apache.directory.server.core.normalization.NormalizationInterceptor;
063 import org.apache.directory.server.core.operational.OperationalAttributeInterceptor;
064 import org.apache.directory.server.core.partition.DefaultPartitionNexus;
065 import org.apache.directory.server.core.partition.Partition;
066 import org.apache.directory.server.core.partition.PartitionNexus;
067 import org.apache.directory.server.core.referral.ReferralInterceptor;
068 import org.apache.directory.server.core.replication.ReplicationConfiguration;
069 import org.apache.directory.server.core.schema.DefaultSchemaService;
070 import org.apache.directory.server.core.schema.SchemaInterceptor;
071 import org.apache.directory.server.core.schema.SchemaService;
072 import org.apache.directory.server.core.security.TlsKeyGenerator;
073 import org.apache.directory.server.core.subtree.SubentryInterceptor;
074 import org.apache.directory.server.core.trigger.TriggerInterceptor;
075 import org.apache.directory.server.i18n.I18n;
076 import org.apache.directory.shared.ldap.NotImplementedException;
077 import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
078 import org.apache.directory.shared.ldap.constants.SchemaConstants;
079 import org.apache.directory.shared.ldap.csn.Csn;
080 import org.apache.directory.shared.ldap.csn.CsnFactory;
081 import org.apache.directory.shared.ldap.cursor.Cursor;
082 import org.apache.directory.shared.ldap.entry.DefaultServerEntry;
083 import org.apache.directory.shared.ldap.entry.Entry;
084 import org.apache.directory.shared.ldap.entry.EntryAttribute;
085 import org.apache.directory.shared.ldap.entry.Modification;
086 import org.apache.directory.shared.ldap.entry.ServerEntry;
087 import org.apache.directory.shared.ldap.entry.client.DefaultClientEntry;
088 import org.apache.directory.shared.ldap.exception.LdapException;
089 import org.apache.directory.shared.ldap.exception.LdapNoPermissionException;
090 import org.apache.directory.shared.ldap.ldif.ChangeType;
091 import org.apache.directory.shared.ldap.ldif.LdifEntry;
092 import org.apache.directory.shared.ldap.ldif.LdifReader;
093 import org.apache.directory.shared.ldap.name.DN;
094 import org.apache.directory.shared.ldap.name.RDN;
095 import org.apache.directory.shared.ldap.schema.SchemaManager;
096 import org.apache.directory.shared.ldap.schema.normalizers.OidNormalizer;
097 import org.apache.directory.shared.ldap.util.DateUtils;
098 import org.apache.directory.shared.ldap.util.StringTools;
099 import org.slf4j.Logger;
100 import org.slf4j.LoggerFactory;
101
102
103 /**
104 * Default implementation of {@link DirectoryService}.
105 *
106 * @org.apache.xbean.XBean
107 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
108 */
109 public class DefaultDirectoryService implements DirectoryService
110 {
111 /** The logger */
112 private static final Logger LOG = LoggerFactory.getLogger( DefaultDirectoryService.class );
113
114 private SchemaService schemaService;
115
116 /** A reference on the SchemaManager */
117 private SchemaManager schemaManager;
118
119 /** the root nexus */
120 private DefaultPartitionNexus partitionNexus;
121
122 /** whether or not server is started for the first time */
123 private boolean firstStart;
124
125 /** The interceptor (or interceptor chain) for this service */
126 private InterceptorChain interceptorChain;
127
128 /** whether or not this instance has been shutdown */
129 private boolean started;
130
131 /** the change log service */
132 private ChangeLog changeLog;
133
134 /** the journal service */
135 private Journal journal;
136
137 /**
138 * the interface used to perform various operations on this
139 * DirectoryService
140 */
141 private OperationManager operationManager = new DefaultOperationManager( this );
142
143 /** the distinguished name of the administrative user */
144 private DN adminDn;
145
146 /** session used as admin for internal operations */
147 private CoreSession adminSession;
148
149 /** The referral manager */
150 private ReferralManager referralManager;
151
152 /** A flag to tell if the userPassword attribute's value must be hidden */
153 private boolean passwordHidden = false;
154
155 /** The service's CSN factory */
156 private CsnFactory csnFactory;
157
158 /** The directory instance replication ID */
159 private int replicaId;
160
161 /** The replication configuration structure */
162 private ReplicationConfiguration replicationConfig;
163
164 /** remove me after implementation is completed */
165 private static final String PARTIAL_IMPL_WARNING =
166 "WARNING: the changelog is only partially operational and will revert\n" +
167 "state without consideration of who made the original change. All reverting " +
168 "changes are made by the admin user.\n Furthermore the used controls are not at " +
169 "all taken into account";
170
171
172 /** The delay to wait between each sync on disk */
173 private long syncPeriodMillis;
174
175 /** The default delay to wait between sync on disk : 15 seconds */
176 private static final long DEFAULT_SYNC_PERIOD = 15000;
177
178 /** */
179 private Thread workerThread;
180
181 /** The sync worker thread */
182 private SynchWorker worker = new SynchWorker();
183
184
185 /** The default timeLimit : 100 entries */
186 public static final int MAX_SIZE_LIMIT_DEFAULT = 100;
187
188 /** The default timeLimit : 10 seconds */
189 public static final int MAX_TIME_LIMIT_DEFAULT = 10000;
190
191 /** The instance Id */
192 private String instanceId;
193
194 /** The server working directory */
195 private File workingDirectory = new File( "server-work" );
196
197 /**
198 * A flag used to shutdown the VM when stopping the server. Useful
199 * when the server is standalone. If the server is embedded, we don't
200 * want to shutdown the VM
201 */
202 private boolean exitVmOnShutdown = true; // allow by default
203
204 /** A flag used to indicate that a shutdown hook has been installed */
205 private boolean shutdownHookEnabled = true; // allow by default
206
207 /** Manage anonymous access to entries other than the RootDSE */
208 private boolean allowAnonymousAccess = true; // allow by default
209
210 /** Manage the basic access control checks */
211 private boolean accessControlEnabled; // off by default
212
213 /** Manage the operational attributes denormalization */
214 private boolean denormalizeOpAttrsEnabled; // off by default
215
216 /** The list of declared interceptors */
217 private List<Interceptor> interceptors;
218
219 /** The System partition */
220 private Partition systemPartition;
221
222 /** The set of all declared partitions */
223 private Set<Partition> partitions = new HashSet<Partition>();
224
225 /** A list of LDIF entries to inject at startup */
226 private List<? extends LdifEntry> testEntries = new ArrayList<LdifEntry>(); // List<Attributes>
227
228 /** The event service */
229 private EventService eventService;
230
231 /** The maximum size for an incoming PDU */
232 private int maxPDUSize = Integer.MAX_VALUE;
233
234
235
236 /**
237 * The synchronizer thread. It flush data on disk periodically.
238 */
239 class SynchWorker implements Runnable
240 {
241 final Object lock = new Object();
242
243 /** A flag to stop the thread */
244 boolean stop;
245
246
247 /**
248 * The main loop
249 */
250 public void run()
251 {
252 while ( !stop )
253 {
254 synchronized ( lock )
255 {
256 try
257 {
258 lock.wait( syncPeriodMillis );
259 }
260 catch ( InterruptedException e )
261 {
262 LOG.warn( "SynchWorker failed to wait on lock.", e );
263 }
264 }
265
266 try
267 {
268 partitionNexus.sync();
269 }
270 catch ( Exception e )
271 {
272 LOG.error( I18n.err( I18n.ERR_74 ), e );
273 }
274 }
275 }
276 }
277
278
279 // ------------------------------------------------------------------------
280 // Constructor
281 // ------------------------------------------------------------------------
282
283
284 /**
285 * Creates a new instance of the directory service.
286 */
287 public DefaultDirectoryService() throws Exception
288 {
289 setDefaultInterceptorConfigurations();
290 changeLog = new DefaultChangeLog();
291 journal = new DefaultJournal();
292 syncPeriodMillis = DEFAULT_SYNC_PERIOD;
293 csnFactory = new CsnFactory( replicaId );
294 schemaService = new DefaultSchemaService();
295 }
296
297
298 // ------------------------------------------------------------------------
299 // C O N F I G U R A T I O N M E T H O D S
300 // ------------------------------------------------------------------------
301
302
303 public void setInstanceId( String instanceId )
304 {
305 this.instanceId = instanceId;
306 }
307
308
309 public String getInstanceId()
310 {
311 return instanceId;
312 }
313
314
315 /**
316 * Gets the {@link Partition}s used by this DirectoryService.
317 *
318 * @return the set of partitions used
319 */
320 public Set<? extends Partition> getPartitions()
321 {
322 Set<Partition> cloned = new HashSet<Partition>();
323 cloned.addAll( partitions );
324 return cloned;
325 }
326
327
328 /**
329 * Sets {@link Partition}s used by this DirectoryService.
330 *
331 * @param partitions the partitions to used
332 */
333 public void setPartitions( Set<? extends Partition> partitions )
334 {
335 Set<Partition> cloned = new HashSet<Partition>();
336 cloned.addAll( partitions );
337 Set<String> names = new HashSet<String>();
338
339 for ( Partition partition : cloned )
340 {
341 String id = partition.getId();
342
343 if ( names.contains( id ) )
344 {
345 LOG.warn( "Encountered duplicate partition {} identifier.", id );
346 }
347
348 names.add( id );
349 }
350
351 this.partitions = cloned;
352 }
353
354
355 /**
356 * Returns <tt>true</tt> if access control checks are enabled.
357 *
358 * @return true if access control checks are enabled, false otherwise
359 */
360 public boolean isAccessControlEnabled()
361 {
362 return accessControlEnabled;
363 }
364
365
366 /**
367 * Sets whether to enable basic access control checks or not.
368 *
369 * @param accessControlEnabled true to enable access control checks, false otherwise
370 */
371 public void setAccessControlEnabled( boolean accessControlEnabled )
372 {
373 this.accessControlEnabled = accessControlEnabled;
374 }
375
376
377 /**
378 * Returns <tt>true</tt> if anonymous access is allowed on entries besides the RootDSE.
379 * If the access control subsystem is enabled then access to some entries may not be
380 * allowed even when full anonymous access is enabled.
381 *
382 * @return true if anonymous access is allowed on entries besides the RootDSE, false
383 * if anonymous access is allowed to all entries.
384 */
385 public boolean isAllowAnonymousAccess()
386 {
387 return allowAnonymousAccess;
388 }
389
390
391 /**
392 * Sets whether to allow anonymous access to entries other than the RootDSE. If the
393 * access control subsystem is enabled then access to some entries may not be allowed
394 * even when full anonymous access is enabled.
395 *
396 * @param enableAnonymousAccess true to enable anonymous access, false to disable it
397 */
398 public void setAllowAnonymousAccess( boolean enableAnonymousAccess )
399 {
400 this.allowAnonymousAccess = enableAnonymousAccess;
401 }
402
403
404 /**
405 * Returns interceptors in the server.
406 *
407 * @return the interceptors in the server.
408 */
409 public List<Interceptor> getInterceptors()
410 {
411 List<Interceptor> cloned = new ArrayList<Interceptor>();
412 cloned.addAll( interceptors );
413 return cloned;
414 }
415
416
417 /**
418 * Sets the interceptors in the server.
419 *
420 * @param interceptors the interceptors to be used in the server.
421 */
422 public void setInterceptors( List<Interceptor> interceptors )
423 {
424 Set<String> names = new HashSet<String>();
425
426 for ( Interceptor interceptor : interceptors )
427 {
428 String name = interceptor.getName();
429
430 if ( names.contains( name ) )
431 {
432 LOG.warn( "Encountered duplicate definitions for {} interceptor", interceptor.getName() );
433 }
434 names.add( name );
435 }
436
437 this.interceptors = interceptors;
438 }
439
440
441 /**
442 * Returns test directory entries({@link LdifEntry}) to be loaded while
443 * bootstrapping.
444 *
445 * @return test entries to load during bootstrapping
446 */
447 public List<LdifEntry> getTestEntries()
448 {
449 List<LdifEntry> cloned = new ArrayList<LdifEntry>();
450 cloned.addAll( testEntries );
451 return cloned;
452 }
453
454
455 /**
456 * Sets test directory entries({@link Attributes}) to be loaded while
457 * bootstrapping.
458 *
459 * @param testEntries the test entries to load while bootstrapping
460 */
461 public void setTestEntries( List<? extends LdifEntry> testEntries )
462 {
463 //noinspection MismatchedQueryAndUpdateOfCollection
464 List<LdifEntry> cloned = new ArrayList<LdifEntry>();
465 cloned.addAll( testEntries );
466 this.testEntries = testEntries;
467 }
468
469
470 /**
471 * Returns working directory (counterpart of <tt>var/lib</tt>) where partitions are
472 * stored by default.
473 *
474 * @return the directory where partition's are stored.
475 */
476 public File getWorkingDirectory()
477 {
478 return workingDirectory;
479 }
480
481
482 /**
483 * Sets working directory (counterpart of <tt>var/lib</tt>) where partitions are stored
484 * by default.
485 *
486 * @param workingDirectory the directory where the server's partitions are stored by default.
487 */
488 public void setWorkingDirectory( File workingDirectory )
489 {
490 this.workingDirectory = workingDirectory;
491 }
492
493
494 public void setShutdownHookEnabled( boolean shutdownHookEnabled )
495 {
496 this.shutdownHookEnabled = shutdownHookEnabled;
497 }
498
499
500 public boolean isShutdownHookEnabled()
501 {
502 return shutdownHookEnabled;
503 }
504
505
506 public void setExitVmOnShutdown( boolean exitVmOnShutdown )
507 {
508 this.exitVmOnShutdown = exitVmOnShutdown;
509 }
510
511
512 public boolean isExitVmOnShutdown()
513 {
514 return exitVmOnShutdown;
515 }
516
517
518 public void setSystemPartition( Partition systemPartition )
519 {
520 this.systemPartition = systemPartition;
521 }
522
523
524 public Partition getSystemPartition()
525 {
526 return systemPartition;
527 }
528
529
530 /**
531 * return true if the operational attributes must be normalized when returned
532 */
533 public boolean isDenormalizeOpAttrsEnabled()
534 {
535 return denormalizeOpAttrsEnabled;
536 }
537
538
539 /**
540 * Sets whether the operational attributes are denormalized when returned
541 * @param denormalizeOpAttrsEnabled The flag value
542 */
543 public void setDenormalizeOpAttrsEnabled( boolean denormalizeOpAttrsEnabled )
544 {
545 this.denormalizeOpAttrsEnabled = denormalizeOpAttrsEnabled;
546 }
547
548
549 /**
550 * {@inheritDoc}
551 */
552 public ChangeLog getChangeLog()
553 {
554 return changeLog;
555 }
556
557
558 /**
559 * {@inheritDoc}
560 */
561 public Journal getJournal()
562 {
563 return journal;
564 }
565
566
567 /**
568 * {@inheritDoc}
569 */
570 public void setChangeLog( ChangeLog changeLog )
571 {
572 this.changeLog = changeLog;
573 }
574
575
576 /**
577 * {@inheritDoc}
578 */
579 public void setJournal( Journal journal )
580 {
581 this.journal = journal;
582 }
583
584
585 public void addPartition( Partition partition ) throws Exception
586 {
587 partition.setSchemaManager( schemaManager );
588 partitions.add( partition );
589
590 if ( ! started )
591 {
592 return;
593 }
594
595 AddContextPartitionOperationContext addPartitionCtx =
596 new AddContextPartitionOperationContext( adminSession, partition );
597 partitionNexus.addContextPartition( addPartitionCtx );
598 }
599
600
601 public void removePartition( Partition partition ) throws Exception
602 {
603 partitions.remove( partition );
604
605 if ( ! started )
606 {
607 return;
608 }
609
610 RemoveContextPartitionOperationContext removePartitionCtx =
611 new RemoveContextPartitionOperationContext( adminSession, partition.getSuffixDn() );
612 partitionNexus.removeContextPartition( removePartitionCtx );
613 }
614
615
616 // ------------------------------------------------------------------------
617 // BackendSubsystem Interface Method Implementations
618 // ------------------------------------------------------------------------
619
620
621 private void setDefaultInterceptorConfigurations()
622 {
623 // Set default interceptor chains
624 List<Interceptor> list = new ArrayList<Interceptor>();
625
626 list.add( new NormalizationInterceptor() );
627 list.add( new AuthenticationInterceptor() );
628 list.add( new ReferralInterceptor() );
629 list.add( new AciAuthorizationInterceptor() );
630 list.add( new DefaultAuthorizationInterceptor() );
631 list.add( new ExceptionInterceptor() );
632 list.add( new ChangeLogInterceptor() );
633 list.add( new OperationalAttributeInterceptor() );
634 list.add( new SchemaInterceptor() );
635 list.add( new SubentryInterceptor() );
636 list.add( new CollectiveAttributeInterceptor() );
637 list.add( new EventInterceptor() );
638 list.add( new TriggerInterceptor() );
639 list.add( new JournalInterceptor() );
640
641 setInterceptors( list );
642 }
643
644
645 public CoreSession getAdminSession()
646 {
647 return adminSession;
648 }
649
650
651 public CoreSession getSession()
652 {
653 return new DefaultCoreSession( new LdapPrincipal(), this );
654 }
655
656
657 public CoreSession getSession( LdapPrincipal principal )
658 {
659 return new DefaultCoreSession( principal, this );
660 }
661
662
663 public CoreSession getSession( DN principalDn, byte[] credentials )
664 throws Exception
665 {
666 if ( ! started )
667 {
668 throw new IllegalStateException( "Service has not started." );
669 }
670
671 BindOperationContext bindContext = new BindOperationContext( null );
672 bindContext.setCredentials( credentials );
673 bindContext.setDn( principalDn );
674 operationManager.bind( bindContext );
675
676 return bindContext.getSession();
677 }
678
679
680 public CoreSession getSession( DN principalDn, byte[] credentials, String saslMechanism, String saslAuthId )
681 throws Exception
682 {
683 if ( ! started )
684 {
685 throw new IllegalStateException( "Service has not started." );
686 }
687
688 BindOperationContext bindContext = new BindOperationContext( null );
689 bindContext.setCredentials( credentials );
690 bindContext.setDn( principalDn );
691 bindContext.setSaslMechanism( saslMechanism );
692 operationManager.bind( bindContext );
693
694 return bindContext.getSession();
695 }
696
697
698 public long revert() throws Exception
699 {
700 if ( changeLog == null || ! changeLog.isEnabled() )
701 {
702 throw new IllegalStateException( I18n.err( I18n.ERR_310 ) );
703 }
704
705 Tag latest = changeLog.getLatest();
706
707 if ( null != latest )
708 {
709 if ( latest.getRevision() < changeLog.getCurrentRevision() )
710 {
711 return revert( latest.getRevision() );
712 }
713 else
714 {
715 LOG.info( "Ignoring request to revert without changes since the latest tag." );
716 return changeLog.getCurrentRevision();
717 }
718 }
719
720 throw new IllegalStateException( I18n.err( I18n.ERR_311 ) );
721 }
722
723
724 /**
725 * We handle the ModDN/ModRDN operation for the revert here.
726 */
727 private void moddn( DN oldDn, DN newDn, boolean delOldRdn ) throws Exception
728 {
729 if ( oldDn.size() == 0 )
730 {
731 throw new LdapNoPermissionException( I18n.err( I18n.ERR_312 ) );
732 }
733
734 // calculate parents
735 DN oldBase = ( DN ) oldDn.clone();
736 oldBase.remove( oldDn.size() - 1 );
737 DN newBase = ( DN ) newDn.clone();
738 newBase.remove( newDn.size() - 1 );
739
740 // Compute the RDN for each of the DN
741 RDN newRdn = newDn.getRdn( newDn.size() - 1 );
742 RDN oldRdn = oldDn.getRdn( oldDn.size() - 1 );
743
744 /*
745 * We need to determine if this rename operation corresponds to a simple
746 * RDN name change or a move operation. If the two names are the same
747 * except for the RDN then it is a simple modifyRdn operation. If the
748 * names differ in size or have a different baseDN then the operation is
749 * a move operation. Furthermore if the RDN in the move operation
750 * changes it is both an RDN change and a move operation.
751 */
752 if ( ( oldDn.size() == newDn.size() ) && oldBase.equals( newBase ) )
753 {
754 adminSession.rename( oldDn, newRdn, delOldRdn );
755 }
756 else
757 {
758 DN target = ( DN ) newDn.clone();
759 target.remove( newDn.size() - 1 );
760
761 if ( newRdn.equals( oldRdn ) )
762 {
763 adminSession.move( oldDn, target );
764 }
765 else
766 {
767 adminSession.moveAndRename( oldDn, target, new RDN( newRdn ), delOldRdn );
768 }
769 }
770 }
771
772
773 public long revert( long revision ) throws Exception
774 {
775 if ( changeLog == null || ! changeLog.isEnabled() )
776 {
777 throw new IllegalStateException( I18n.err( I18n.ERR_310 ) );
778 }
779
780 if ( revision < 0 )
781 {
782 throw new IllegalArgumentException( I18n.err( I18n.ERR_239 ) );
783 }
784
785 if ( revision >= changeLog.getChangeLogStore().getCurrentRevision() )
786 {
787 throw new IllegalArgumentException( I18n.err( I18n.ERR_314 ) );
788 }
789
790 Cursor<ChangeLogEvent> cursor = changeLog.getChangeLogStore().findAfter( revision );
791
792 /*
793 * BAD, BAD, BAD!!!
794 *
795 * No synchronization no nothing. Just getting this to work for now
796 * so we can revert tests. Any production grade use of this feature
797 * needs to synchronize on all changes while the revert is in progress.
798 *
799 * How about making this operation transactional?
800 *
801 * First of all just stop using JNDI and construct the operations to
802 * feed into the interceptor pipeline.
803 *
804 * TODO review this code.
805 */
806
807 try
808 {
809 LOG.warn( PARTIAL_IMPL_WARNING );
810 cursor.afterLast();
811
812 while ( cursor.previous() ) // apply ldifs in reverse order
813 {
814 ChangeLogEvent event = cursor.get();
815 List<LdifEntry> reverses = event.getReverseLdifs();
816
817 for ( LdifEntry reverse:reverses )
818 {
819 switch( reverse.getChangeType().getChangeType() )
820 {
821 case ChangeType.ADD_ORDINAL :
822 adminSession.add(
823 new DefaultServerEntry( schemaManager, reverse.getEntry() ), true );
824 break;
825
826 case ChangeType.DELETE_ORDINAL :
827 adminSession.delete( reverse.getDn(), true );
828 break;
829
830 case ChangeType.MODIFY_ORDINAL :
831 List<Modification> mods = reverse.getModificationItems();
832
833 adminSession.modify( reverse.getDn(), mods, true );
834 break;
835
836 case ChangeType.MODDN_ORDINAL :
837 // NO BREAK - both ModDN and ModRDN handling is the same
838
839 case ChangeType.MODRDN_ORDINAL :
840 DN forwardDn = event.getForwardLdif().getDn();
841 DN reverseDn = reverse.getDn();
842
843 moddn( reverseDn, forwardDn, reverse.isDeleteOldRdn() );
844
845 break;
846
847 default:
848 LOG.error( I18n.err( I18n.ERR_75 ) );
849 throw new NotImplementedException( I18n.err( I18n.ERR_76, reverse.getChangeType() ) );
850 }
851 }
852 }
853 }
854 catch ( IOException e )
855 {
856 String message = I18n.err( I18n.ERR_77, revision );
857 LOG.error( message );
858 throw new LdapException( message );
859 }
860
861 return changeLog.getCurrentRevision();
862 }
863
864
865 public OperationManager getOperationManager()
866 {
867 return operationManager;
868 }
869
870
871 /**
872 * @throws Exception if the LDAP server cannot be started
873 */
874 public synchronized void startup() throws Exception
875 {
876 if ( started )
877 {
878 return;
879 }
880
881 if ( shutdownHookEnabled )
882 {
883 Runtime.getRuntime().addShutdownHook( new Thread( new Runnable()
884 {
885 public void run()
886 {
887 try
888 {
889 shutdown();
890 }
891 catch ( Exception e )
892 {
893 LOG.warn( "Failed to shut down the directory service: "
894 + DefaultDirectoryService.this.instanceId, e );
895 }
896 }
897 }, "ApacheDS Shutdown Hook (" + instanceId + ')' ) );
898
899 LOG.info( "ApacheDS shutdown hook has been registered with the runtime." );
900 }
901 else if ( LOG.isWarnEnabled() )
902 {
903 LOG.warn( "ApacheDS shutdown hook has NOT been registered with the runtime."
904 + " This default setting for standalone operation has been overriden." );
905 }
906
907 initialize();
908 showSecurityWarnings();
909
910 // Start the sync thread if required
911 if ( syncPeriodMillis > 0 )
912 {
913 workerThread = new Thread( worker, "SynchWorkerThread" );
914 workerThread.start();
915 }
916
917
918 started = true;
919
920 if ( !testEntries.isEmpty() )
921 {
922 createTestEntries();
923 }
924 }
925
926
927 public synchronized void sync() throws Exception
928 {
929 if ( !started )
930 {
931 return;
932 }
933
934 this.changeLog.sync();
935 this.partitionNexus.sync();
936 }
937
938
939 public synchronized void shutdown() throws Exception
940 {
941 if ( !started )
942 {
943 return;
944 }
945
946 // --------------------------------------------------------------------
947 // Shutdown the changelog
948 // --------------------------------------------------------------------
949 changeLog.sync();
950 changeLog.destroy();
951
952 // --------------------------------------------------------------------
953 // Shutdown the journal if enabled
954 // --------------------------------------------------------------------
955 if ( journal.isEnabled() )
956 {
957 journal.destroy();
958 }
959
960 // --------------------------------------------------------------------
961 // Shutdown the partition
962 // --------------------------------------------------------------------
963
964 partitionNexus.sync();
965 partitionNexus.destroy();
966
967 // --------------------------------------------------------------------
968 // Shutdown the sync thread
969 // --------------------------------------------------------------------
970 if ( workerThread != null )
971 {
972 worker.stop = true;
973
974 synchronized ( worker.lock )
975 {
976 worker.lock.notify();
977 }
978
979 while ( workerThread.isAlive() )
980 {
981 LOG.info( "Waiting for SynchWorkerThread to die." );
982 workerThread.join( 500 );
983 }
984 }
985
986
987 // --------------------------------------------------------------------
988 // And shutdown the server
989 // --------------------------------------------------------------------
990 interceptorChain.destroy();
991 started = false;
992 setDefaultInterceptorConfigurations();
993 }
994
995
996 /**
997 * @return The referral manager
998 */
999 public ReferralManager getReferralManager()
1000 {
1001 return referralManager;
1002 }
1003
1004 /**
1005 * Set the referralManager
1006 * @param referralManager The initialized referralManager
1007 */
1008 public void setReferralManager( ReferralManager referralManager )
1009 {
1010 this.referralManager = referralManager;
1011 }
1012
1013
1014 /**
1015 * @return the SchemaManager
1016 */
1017 public SchemaManager getSchemaManager()
1018 {
1019 return schemaManager;
1020 }
1021
1022
1023 /**
1024 * Set the SchemaManager instance.
1025 *
1026 * @param schemaManager The schemaManager
1027 */
1028 public void setSchemaManager( SchemaManager schemaManager )
1029 {
1030 this.schemaManager = schemaManager;
1031 }
1032
1033
1034 public SchemaService getSchemaService()
1035 {
1036 return schemaService;
1037 }
1038
1039
1040 public void setSchemaService( SchemaService schemaService )
1041 {
1042 this.schemaService = schemaService;
1043 }
1044
1045
1046 public DefaultPartitionNexus getPartitionNexus()
1047 {
1048 return partitionNexus;
1049 }
1050
1051
1052 public InterceptorChain getInterceptorChain()
1053 {
1054 return interceptorChain;
1055 }
1056
1057
1058 public boolean isFirstStart()
1059 {
1060 return firstStart;
1061 }
1062
1063
1064 public boolean isStarted()
1065 {
1066 return started;
1067 }
1068
1069
1070 public ServerEntry newEntry( DN dn )
1071 {
1072 return new DefaultServerEntry( schemaManager, dn );
1073 }
1074
1075
1076 /**
1077 * Returns true if we had to create the bootstrap entries on the first
1078 * start of the server. Otherwise if all entries exist, meaning none
1079 * had to be created, then we are not starting for the first time.
1080 *
1081 * @return true if the bootstrap entries had to be created, false otherwise
1082 * @throws Exception if entries cannot be created
1083 */
1084 private boolean createBootstrapEntries() throws Exception
1085 {
1086 boolean firstStart = false;
1087
1088 // -------------------------------------------------------------------
1089 // create admin entry
1090 // -------------------------------------------------------------------
1091
1092 /*
1093 * If the admin entry is there, then the database was already created
1094 */
1095 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, adminDn ) ) )
1096 {
1097 firstStart = true;
1098
1099 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, adminDn );
1100
1101 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT,
1102 SchemaConstants.TOP_OC,
1103 SchemaConstants.PERSON_OC,
1104 SchemaConstants.ORGANIZATIONAL_PERSON_OC,
1105 SchemaConstants.INET_ORG_PERSON_OC );
1106
1107 serverEntry.put( SchemaConstants.UID_AT, PartitionNexus.ADMIN_UID );
1108 serverEntry.put( SchemaConstants.USER_PASSWORD_AT, PartitionNexus.ADMIN_PASSWORD_BYTES );
1109 serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" );
1110 serverEntry.put( SchemaConstants.CN_AT, "system administrator" );
1111 serverEntry.put( SchemaConstants.SN_AT, "administrator" );
1112 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1113 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1114 serverEntry.put( SchemaConstants.DISPLAY_NAME_AT, "Directory Superuser" );
1115 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1116 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1117
1118 TlsKeyGenerator.addKeyPair( serverEntry );
1119 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1120 }
1121
1122 // -------------------------------------------------------------------
1123 // create system users area
1124 // -------------------------------------------------------------------
1125
1126 Map<String,OidNormalizer> oidsMap = schemaManager.getNormalizerMapping();
1127 DN userDn = new DN( ServerDNConstants.USERS_SYSTEM_DN );
1128 userDn.normalize( oidsMap );
1129
1130 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, userDn ) ) )
1131 {
1132 firstStart = true;
1133
1134 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, userDn );
1135
1136 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT,
1137 SchemaConstants.TOP_OC,
1138 SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1139
1140 serverEntry.put( SchemaConstants.OU_AT, "users" );
1141 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1142 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1143 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1144 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1145
1146 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1147 }
1148
1149 // -------------------------------------------------------------------
1150 // create system groups area
1151 // -------------------------------------------------------------------
1152
1153 DN groupDn = new DN( ServerDNConstants.GROUPS_SYSTEM_DN );
1154 groupDn.normalize( oidsMap );
1155
1156 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, groupDn ) ) )
1157 {
1158 firstStart = true;
1159
1160 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, groupDn );
1161
1162 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT,
1163 SchemaConstants.TOP_OC,
1164 SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1165
1166 serverEntry.put( SchemaConstants.OU_AT, "groups" );
1167 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1168 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1169 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1170 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1171
1172 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1173 }
1174
1175 // -------------------------------------------------------------------
1176 // create administrator group
1177 // -------------------------------------------------------------------
1178
1179 DN name = new DN( ServerDNConstants.ADMINISTRATORS_GROUP_DN );
1180 name.normalize( oidsMap );
1181
1182 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, name ) ) )
1183 {
1184 firstStart = true;
1185
1186 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, name );
1187
1188 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT,
1189 SchemaConstants.TOP_OC,
1190 SchemaConstants.GROUP_OF_UNIQUE_NAMES_OC );
1191
1192 serverEntry.put( SchemaConstants.CN_AT, "Administrators" );
1193 serverEntry.put( SchemaConstants.UNIQUE_MEMBER_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1194 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1195 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1196 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1197 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1198
1199 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1200
1201 // TODO - confirm if we need this at all since the
1202 // group cache on initialization after this stage will
1203 // search the directory for all the groups anyway
1204
1205 // Interceptor authzInterceptor = interceptorChain.get( AciAuthorizationInterceptor.class.getName() );
1206 //
1207 // if ( authzInterceptor == null )
1208 // {
1209 // LOG.error( "The Authorization service is null : this is not allowed" );
1210 // throw new NamingException( "The Authorization service is null" );
1211 // }
1212 //
1213 // if ( !( authzInterceptor instanceof AciAuthorizationInterceptor ) )
1214 // {
1215 // LOG.error( "The Authorization service is not set correctly : '{}' is an incorect interceptor",
1216 // authzInterceptor.getClass().getName() );
1217 // throw new NamingException( "The Authorization service is incorrectly set" );
1218 //
1219 // }
1220 //
1221 // AciAuthorizationInterceptor authzSrvc = ( AciAuthorizationInterceptor ) authzInterceptor;
1222 // authzSrvc.cacheNewGroup( name, serverEntry );
1223 }
1224
1225 // -------------------------------------------------------------------
1226 // create system configuration area
1227 // -------------------------------------------------------------------
1228
1229 DN configurationDn = new DN( "ou=configuration,ou=system" );
1230 configurationDn.normalize( oidsMap );
1231
1232 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, configurationDn ) ) )
1233 {
1234 firstStart = true;
1235
1236 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, configurationDn );
1237 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1238
1239 serverEntry.put( SchemaConstants.OU_AT, "configuration" );
1240 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1241 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1242 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1243 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1244
1245 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1246 }
1247
1248 // -------------------------------------------------------------------
1249 // create system configuration area for partition information
1250 // -------------------------------------------------------------------
1251
1252 DN partitionsDn = new DN( "ou=partitions,ou=configuration,ou=system" );
1253 partitionsDn.normalize( oidsMap );
1254
1255 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, partitionsDn ) ) )
1256 {
1257 firstStart = true;
1258
1259 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, partitionsDn );
1260 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1261 serverEntry.put( SchemaConstants.OU_AT, "partitions" );
1262 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1263 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1264 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1265 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1266
1267 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1268 }
1269
1270 // -------------------------------------------------------------------
1271 // create system configuration area for services
1272 // -------------------------------------------------------------------
1273
1274 DN servicesDn = new DN( "ou=services,ou=configuration,ou=system" );
1275 servicesDn.normalize( oidsMap );
1276
1277 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, servicesDn ) ) )
1278 {
1279 firstStart = true;
1280
1281 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, servicesDn );
1282 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1283
1284 serverEntry.put( SchemaConstants.OU_AT, "services" );
1285 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1286 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1287 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1288 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1289
1290 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1291 }
1292
1293 // -------------------------------------------------------------------
1294 // create system configuration area for interceptors
1295 // -------------------------------------------------------------------
1296
1297 DN interceptorsDn = new DN( "ou=interceptors,ou=configuration,ou=system" );
1298 interceptorsDn.normalize( oidsMap );
1299
1300 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, interceptorsDn ) ) )
1301 {
1302 firstStart = true;
1303
1304 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, interceptorsDn );
1305 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, SchemaConstants.ORGANIZATIONAL_UNIT_OC );
1306
1307 serverEntry.put( SchemaConstants.OU_AT, "interceptors" );
1308 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1309 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1310 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1311 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1312
1313 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1314 }
1315
1316 // -------------------------------------------------------------------
1317 // create system preferences area
1318 // -------------------------------------------------------------------
1319
1320 DN sysPrefRootDn = new DN( ServerDNConstants.SYSPREFROOT_SYSTEM_DN );
1321 sysPrefRootDn.normalize( oidsMap );
1322
1323 if ( !partitionNexus.hasEntry( new EntryOperationContext( adminSession, sysPrefRootDn ) ) )
1324 {
1325 firstStart = true;
1326
1327 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, sysPrefRootDn );
1328 serverEntry.put( SchemaConstants.OBJECT_CLASS_AT,
1329 SchemaConstants.TOP_OC,
1330 SchemaConstants.ORGANIZATIONAL_UNIT_OC,
1331 SchemaConstants.EXTENSIBLE_OBJECT_OC );
1332
1333 serverEntry.put( "prefNodeName", "sysPrefRoot" );
1334 serverEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
1335 serverEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
1336 serverEntry.add( SchemaConstants.ENTRY_CSN_AT, getCSN().toString() );
1337 serverEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
1338
1339 partitionNexus.add( new AddOperationContext( adminSession, serverEntry ) );
1340 }
1341
1342 return firstStart;
1343 }
1344
1345
1346 /**
1347 * Displays security warning messages if any possible secutiry issue is found.
1348 * @throws Exception if there are failures parsing and accessing internal structures
1349 */
1350 private void showSecurityWarnings() throws Exception
1351 {
1352 // Warn if the default password is not changed.
1353 boolean needToChangeAdminPassword = false;
1354
1355 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN );
1356 adminDn.normalize( schemaManager.getNormalizerMapping() );
1357
1358 ServerEntry adminEntry = partitionNexus.lookup( new LookupOperationContext( adminSession, adminDn ) );
1359 Object userPassword = adminEntry.get( SchemaConstants.USER_PASSWORD_AT ).get();
1360
1361 if ( userPassword instanceof byte[] )
1362 {
1363 needToChangeAdminPassword = Arrays.equals( PartitionNexus.ADMIN_PASSWORD_BYTES, ( byte[] ) userPassword );
1364 }
1365 else if ( userPassword.toString().equals( PartitionNexus.ADMIN_PASSWORD_STRING ) )
1366 {
1367 needToChangeAdminPassword = PartitionNexus.ADMIN_PASSWORD_STRING.equals( userPassword.toString() );
1368 }
1369
1370 if ( needToChangeAdminPassword )
1371 {
1372 LOG.warn( "You didn't change the admin password of directory service " + "instance '" + instanceId + "'. "
1373 + "Please update the admin password as soon as possible " + "to prevent a possible security breach." );
1374 }
1375 }
1376
1377
1378 /**
1379 * Adds test entries into the core.
1380 *
1381 * @todo this may no longer be needed when JNDI is not used for bootstrapping
1382 *
1383 * @throws Exception if the creation of test entries fails.
1384 */
1385 private void createTestEntries() throws Exception
1386 {
1387 for ( LdifEntry testEntry : testEntries )
1388 {
1389 try
1390 {
1391 LdifEntry ldifEntry = testEntry.clone();
1392 Entry entry = ldifEntry.getEntry();
1393 String dn = ldifEntry.getDn().getName();
1394
1395 try
1396 {
1397 getAdminSession().add( new DefaultServerEntry( schemaManager, entry ) );
1398 }
1399 catch ( Exception e )
1400 {
1401 LOG.warn( dn + " test entry already exists.", e );
1402 }
1403 }
1404 catch ( CloneNotSupportedException cnse )
1405 {
1406 LOG.warn( "Cannot clone the entry ", cnse );
1407 }
1408 }
1409 }
1410
1411
1412 /**
1413 * Kicks off the initialization of the entire system.
1414 *
1415 * @throws Exception if there are problems along the way
1416 */
1417 private void initialize() throws Exception
1418 {
1419 if ( LOG.isDebugEnabled() )
1420 {
1421 LOG.debug( "---> Initializing the DefaultDirectoryService " );
1422 }
1423
1424 // triggers partition to load schema fully from schema partition
1425 schemaService.initialize();
1426 schemaService.getSchemaPartition().initialize();
1427 partitions.add( schemaService.getSchemaPartition() );
1428 systemPartition.getSuffixDn().normalize( schemaManager.getNormalizerMapping() );
1429
1430 adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN ).normalize( schemaManager.getNormalizerMapping() );
1431 adminDn.normalize( schemaManager.getNormalizerMapping() );
1432 adminSession = new DefaultCoreSession( new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), this );
1433
1434 // @TODO - NOTE: Need to find a way to instantiate without dependency on DPN
1435 partitionNexus = new DefaultPartitionNexus( new DefaultServerEntry( schemaManager, DN.EMPTY_DN ) );
1436 partitionNexus.setDirectoryService( this );
1437 partitionNexus.initialize( );
1438 //partitionNexus.addContextPartition( new AddContextPartitionOperationContext( adminSession, schemaService.getSchemaPartition() ) );
1439
1440 // --------------------------------------------------------------------
1441 // Create all the bootstrap entries before initializing chain
1442 // --------------------------------------------------------------------
1443
1444 firstStart = createBootstrapEntries();
1445
1446 interceptorChain = new InterceptorChain();
1447 interceptorChain.init( this );
1448
1449 // --------------------------------------------------------------------
1450 // Initialize the changeLog if it's enabled
1451 // --------------------------------------------------------------------
1452
1453 if ( changeLog.isEnabled() )
1454 {
1455 changeLog.init( this );
1456
1457 if( changeLog.isExposed() && changeLog.isTagSearchSupported() )
1458 {
1459 String clSuffix = ( ( TaggableSearchableChangeLogStore ) changeLog.getChangeLogStore() ).getPartition().getSuffixDn().getName();
1460 partitionNexus.getRootDSE( null ).getOriginalEntry().add( SchemaConstants.CHANGELOG_CONTEXT_AT, clSuffix );
1461 }
1462 }
1463
1464 // --------------------------------------------------------------------
1465 // Initialize the journal if it's enabled
1466 // --------------------------------------------------------------------
1467 if ( journal.isEnabled() )
1468 {
1469 journal.init( this );
1470 }
1471
1472 if ( LOG.isDebugEnabled() )
1473 {
1474 LOG.debug( "<--- DefaultDirectoryService initialized" );
1475 }
1476 }
1477
1478
1479 /**
1480 * Read an entry (without DN)
1481 *
1482 * @param text The ldif format file
1483 * @return An entry.
1484 */
1485 private Entry readEntry( String text )
1486 {
1487 StringReader strIn = new StringReader( text );
1488 BufferedReader in = new BufferedReader( strIn );
1489
1490 String line = null;
1491 Entry entry = new DefaultClientEntry();
1492
1493 try
1494 {
1495 while ( ( line = in.readLine() ) != null )
1496 {
1497 if ( line.length() == 0 )
1498 {
1499 continue;
1500 }
1501
1502 String addedLine = line.trim();
1503
1504 if ( StringTools.isEmpty( addedLine ) )
1505 {
1506 continue;
1507 }
1508
1509 EntryAttribute attribute = LdifReader.parseAttributeValue( addedLine );
1510 EntryAttribute oldAttribute = entry.get( attribute.getId() );
1511
1512 if ( oldAttribute != null )
1513 {
1514 try
1515 {
1516 oldAttribute.add( attribute.get() );
1517 entry.put( oldAttribute );
1518 }
1519 catch ( LdapException ne )
1520 {
1521 // Do nothing
1522 }
1523 }
1524 else
1525 {
1526 try
1527 {
1528 entry.put( attribute );
1529 }
1530 catch ( LdapException ne )
1531 {
1532 // TODO do nothing ...
1533 }
1534 }
1535 }
1536 }
1537 catch (IOException ioe)
1538 {
1539 // Do nothing : we can't reach this point !
1540 }
1541
1542 return entry;
1543 }
1544
1545
1546 /**
1547 * Create a new ServerEntry
1548 *
1549 * @param ldif The String representing the attributes, as a LDIF file
1550 * @param dn The DN for this new entry
1551 */
1552 public ServerEntry newEntry( String ldif, String dn )
1553 {
1554 try
1555 {
1556 Entry entry = readEntry( ldif );
1557 DN newDn = new DN( dn );
1558
1559 entry.setDn( newDn );
1560
1561 // TODO Let's get rid of this Attributes crap
1562 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, entry );
1563 return serverEntry;
1564 }
1565 catch ( Exception e )
1566 {
1567 LOG.error( I18n.err( I18n.ERR_78, ldif, dn ) );
1568 // do nothing
1569 return null;
1570 }
1571 }
1572
1573
1574 public EventService getEventService()
1575 {
1576 return eventService;
1577 }
1578
1579
1580 public void setEventService( EventService eventService )
1581 {
1582 this.eventService = eventService;
1583 }
1584
1585
1586 /**
1587 * {@inheritDoc}
1588 */
1589 public boolean isPasswordHidden()
1590 {
1591 return passwordHidden;
1592 }
1593
1594
1595 /**
1596 * {@inheritDoc}
1597 */
1598 public void setPasswordHidden( boolean passwordHidden )
1599 {
1600 this.passwordHidden = passwordHidden;
1601 }
1602
1603
1604 /**
1605 * @return The maximum allowed size for an incoming PDU
1606 */
1607 public int getMaxPDUSize()
1608 {
1609 return maxPDUSize;
1610 }
1611
1612
1613 /**
1614 * Set the maximum allowed size for an incoming PDU
1615 * @param maxPDUSize A positive number of bytes for the PDU. A negative or
1616 * null value will be transformed to {@link Integer#MAX_VALUE}
1617 */
1618 public void setMaxPDUSize( int maxPDUSize )
1619 {
1620 if ( maxPDUSize <= 0 )
1621 {
1622 maxPDUSize = Integer.MAX_VALUE;
1623 }
1624
1625 this.maxPDUSize = maxPDUSize;
1626 }
1627
1628
1629 /**
1630 * {@inheritDoc}
1631 */
1632 public Interceptor getInterceptor( String interceptorName )
1633 {
1634 for ( Interceptor interceptor:interceptors )
1635 {
1636 if ( interceptor.getName().equalsIgnoreCase( interceptorName ) )
1637 {
1638 return interceptor;
1639 }
1640 }
1641
1642 return null;
1643 }
1644
1645
1646 /**
1647 * Get a new CSN
1648 * @return The CSN generated for this directory service
1649 */
1650 public Csn getCSN()
1651 {
1652 return csnFactory.newInstance();
1653 }
1654
1655
1656 /**
1657 * @return the replicaId
1658 */
1659 public int getReplicaId()
1660 {
1661 return replicaId;
1662 }
1663
1664
1665 /**
1666 * @param replicaId the replicaId to set
1667 */
1668 public void setReplicaId( int replicaId )
1669 {
1670 if ( ( replicaId < 0 ) || ( replicaId > 999 ) )
1671 {
1672 LOG.error( I18n.err( I18n.ERR_79 ) );
1673 this.replicaId = 0;
1674 }
1675 else
1676 {
1677 this.replicaId = replicaId;
1678 }
1679 }
1680
1681
1682 public void setReplicationConfiguration( ReplicationConfiguration replicationConfig )
1683 {
1684 this.replicationConfig = replicationConfig;
1685
1686 }
1687
1688
1689 /**
1690 * @return the replication configuration for this DirectoryService
1691 */
1692 public ReplicationConfiguration getReplicationConfiguration()
1693 {
1694 return replicationConfig;
1695 }
1696
1697
1698 /**
1699 * @return the syncPeriodMillis
1700 */
1701 public long getSyncPeriodMillis()
1702 {
1703 return syncPeriodMillis;
1704 }
1705
1706
1707 /**
1708 * @param syncPeriodMillis the syncPeriodMillis to set
1709 */
1710 public void setSyncPeriodMillis( long syncPeriodMillis )
1711 {
1712 this.syncPeriodMillis = syncPeriodMillis;
1713 }
1714 }