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.partition;
021
022
023 import java.io.IOException;
024 import java.util.ArrayList;
025 import java.util.Collections;
026 import java.util.HashMap;
027 import java.util.HashSet;
028 import java.util.Iterator;
029 import java.util.List;
030 import java.util.Map;
031 import java.util.Properties;
032 import java.util.Set;
033 import java.util.UUID;
034
035 import javax.naming.ConfigurationException;
036 import javax.naming.NameNotFoundException;
037 import javax.naming.directory.SearchControls;
038
039 import org.apache.directory.server.constants.ServerDNConstants;
040 import org.apache.directory.server.core.CoreSession;
041 import org.apache.directory.server.core.DefaultCoreSession;
042 import org.apache.directory.server.core.DirectoryService;
043 import org.apache.directory.server.core.LdapPrincipal;
044 import org.apache.directory.server.core.entry.ClonedServerEntry;
045 import org.apache.directory.server.core.filtering.BaseEntryFilteringCursor;
046 import org.apache.directory.server.core.filtering.CursorList;
047 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
048 import org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext;
049 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
050 import org.apache.directory.server.core.interceptor.context.BindOperationContext;
051 import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
052 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
053 import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
054 import org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext;
055 import org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext;
056 import org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext;
057 import org.apache.directory.server.core.interceptor.context.ListOperationContext;
058 import org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext;
059 import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
060 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
061 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
062 import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
063 import org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext;
064 import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
065 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
066 import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
067 import org.apache.directory.server.i18n.I18n;
068 import org.apache.directory.shared.ldap.MultiException;
069 import org.apache.directory.shared.ldap.NotImplementedException;
070 import org.apache.directory.shared.ldap.codec.controls.CascadeControl;
071 import org.apache.directory.shared.ldap.codec.controls.ManageDsaITControl;
072 import org.apache.directory.shared.ldap.codec.controls.replication.syncDoneValue.SyncDoneValueControl;
073 import org.apache.directory.shared.ldap.codec.controls.replication.syncInfoValue.SyncInfoValueControl;
074 import org.apache.directory.shared.ldap.codec.controls.replication.syncRequestValue.SyncRequestValueControl;
075 import org.apache.directory.shared.ldap.codec.controls.replication.syncStateValue.SyncStateValueControl;
076 import org.apache.directory.shared.ldap.codec.search.controls.entryChange.EntryChangeControl;
077 import org.apache.directory.shared.ldap.codec.search.controls.pagedSearch.PagedResultsControl;
078 import org.apache.directory.shared.ldap.codec.search.controls.persistentSearch.PersistentSearchControl;
079 import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl;
080 import org.apache.directory.shared.ldap.constants.AuthenticationLevel;
081 import org.apache.directory.shared.ldap.constants.SchemaConstants;
082 import org.apache.directory.shared.ldap.cursor.EmptyCursor;
083 import org.apache.directory.shared.ldap.cursor.SingletonCursor;
084 import org.apache.directory.shared.ldap.entry.DefaultServerAttribute;
085 import org.apache.directory.shared.ldap.entry.DefaultServerEntry;
086 import org.apache.directory.shared.ldap.entry.EntryAttribute;
087 import org.apache.directory.shared.ldap.entry.ServerEntry;
088 import org.apache.directory.shared.ldap.entry.Value;
089 import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeTypeException;
090 import org.apache.directory.shared.ldap.exception.LdapNoSuchObjectException;
091 import org.apache.directory.shared.ldap.exception.LdapNoSuchAttributeException;
092 import org.apache.directory.shared.ldap.filter.ExprNode;
093 import org.apache.directory.shared.ldap.filter.PresenceNode;
094 import org.apache.directory.shared.ldap.filter.SearchScope;
095 import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
096 import org.apache.directory.shared.ldap.name.DN;
097 import org.apache.directory.shared.ldap.schema.AttributeType;
098 import org.apache.directory.shared.ldap.schema.Normalizer;
099 import org.apache.directory.shared.ldap.schema.SchemaManager;
100 import org.apache.directory.shared.ldap.schema.UsageEnum;
101 import org.apache.directory.shared.ldap.util.DateUtils;
102 import org.apache.directory.shared.ldap.util.NamespaceTools;
103 import org.apache.directory.shared.ldap.util.StringTools;
104 import org.apache.directory.shared.ldap.util.tree.DnBranchNode;
105 import org.slf4j.Logger;
106 import org.slf4j.LoggerFactory;
107
108
109 /**
110 * A root {@link Partition} that contains all other partitions, and
111 * routes all operations to the child partition that matches to its base suffixes.
112 * It also provides some extended operations such as accessing rootDSE and
113 * listing base suffixes.
114 *
115 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
116 * @version $Rev: 927404 $, $Date: 2010-03-25 15:55:18 +0200 (Thu, 25 Mar 2010) $
117 */
118 public class DefaultPartitionNexus extends AbstractPartition implements PartitionNexus
119 {
120 /** A logger for this class */
121 private static final Logger LOG = LoggerFactory.getLogger( DefaultPartitionNexus.class );
122
123 /** Speedup for logs */
124 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
125
126 /** the vendorName string proudly set to: Apache Software Foundation*/
127 private static final String ASF = "Apache Software Foundation";
128
129 /** the read only rootDSE attributes */
130 private final ServerEntry rootDSE;
131
132 /** The DirectoryService instance */
133 private DirectoryService directoryService;
134
135 /** The global schemaManager */
136 private SchemaManager schemaManager;
137
138 /** the partitions keyed by normalized suffix strings */
139 private Map<String, Partition> partitions = new HashMap<String, Partition>();
140
141 /** A structure to hold all the partitions */
142 private DnBranchNode<Partition> partitionLookupTree = new DnBranchNode<Partition>();
143
144 /** the system partition */
145 private Partition system;
146
147 /** the closed state of this partition */
148 private boolean initialized;
149
150
151 /**
152 * Creates the root nexus singleton of the entire system. The root DSE has
153 * several attributes that are injected into it besides those that may
154 * already exist. As partitions are added to the system more namingContexts
155 * attributes are added to the rootDSE.
156 *
157 * @see <a href="http://www.faqs.org/rfcs/rfc3045.html">Vendor Information</a>
158 * @param rootDSE the root entry for the DSA
159 * @throws javax.naming.Exception on failure to initialize
160 */
161 public DefaultPartitionNexus( ServerEntry rootDSE ) throws Exception
162 {
163 // setup that root DSE
164 this.rootDSE = rootDSE;
165
166 // Add the basic informations
167 rootDSE.put( SchemaConstants.SUBSCHEMA_SUBENTRY_AT, ServerDNConstants.CN_SCHEMA_DN );
168 rootDSE.put( SchemaConstants.SUPPORTED_LDAP_VERSION_AT, "3" );
169 rootDSE.put( SchemaConstants.SUPPORTED_FEATURES_AT, SchemaConstants.FEATURE_ALL_OPERATIONAL_ATTRIBUTES );
170 rootDSE.put( SchemaConstants.SUPPORTED_EXTENSION_AT, NoticeOfDisconnect.EXTENSION_OID );
171
172 // Add the supported controls
173 rootDSE.put(
174 SchemaConstants.SUPPORTED_CONTROL_AT,
175 PersistentSearchControl.CONTROL_OID,
176 EntryChangeControl.CONTROL_OID,
177 SubentriesControl.CONTROL_OID,
178 ManageDsaITControl.CONTROL_OID,
179 CascadeControl.CONTROL_OID,
180 PagedResultsControl.CONTROL_OID,
181 // Replication controls
182 SyncDoneValueControl.CONTROL_OID,
183 SyncInfoValueControl.CONTROL_OID,
184 SyncRequestValueControl.CONTROL_OID,
185 SyncStateValueControl.CONTROL_OID
186 );
187
188 // Add the objectClasses
189 rootDSE.put( SchemaConstants.OBJECT_CLASS_AT,
190 SchemaConstants.TOP_OC,
191 SchemaConstants.EXTENSIBLE_OBJECT_OC );
192
193 // Add the 'vendor' name and version infos
194 rootDSE.put( SchemaConstants.VENDOR_NAME_AT, ASF );
195
196 Properties props = new Properties();
197
198 try
199 {
200 props.load( getClass().getResourceAsStream( "version.properties" ) );
201 }
202 catch ( IOException e )
203 {
204 LOG.error( I18n.err( I18n.ERR_33 ) );
205 }
206
207 rootDSE.put( SchemaConstants.VENDOR_VERSION_AT, props.getProperty( "apacheds.version", "UNKNOWN" ) );
208 }
209
210
211 /* (non-Javadoc)
212 * @see org.apache.directory.server.core.partition.PartitionNexus#initialize()
213 */
214 protected void doInit( ) throws Exception
215 {
216 // NOTE: We ignore ContextPartitionConfiguration parameter here.
217 if ( initialized )
218 {
219 return;
220 }
221
222 //this.directoryService = directoryService;
223 schemaManager = directoryService.getSchemaManager();
224
225 // Initialize and normalize the localy used DNs
226 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN );
227 adminDn.normalize( schemaManager.getNormalizerMapping() );
228
229 initializeSystemPartition( directoryService );
230
231 List<Partition> initializedPartitions = new ArrayList<Partition>();
232 initializedPartitions.add( 0, this.system );
233
234
235 try
236 {
237 for ( Partition partition : directoryService.getPartitions() )
238 {
239 partition.setSchemaManager( schemaManager );
240 CoreSession adminSession = new DefaultCoreSession(
241 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService );
242
243 AddContextPartitionOperationContext opCtx =
244 new AddContextPartitionOperationContext( adminSession, partition );
245 addContextPartition( opCtx );
246 initializedPartitions.add( opCtx.getPartition() );
247 }
248
249 initialized = true;
250 }
251 finally
252 {
253 if ( !initialized )
254 {
255 Iterator<Partition> i = initializedPartitions.iterator();
256 while ( i.hasNext() )
257 {
258 Partition partition = i.next();
259 i.remove();
260 try
261 {
262 partition.destroy();
263 }
264 catch ( Exception e )
265 {
266 LOG.warn( "Failed to destroy a partition: " + partition.getSuffixDn(), e );
267 }
268 finally
269 {
270 unregister( partition );
271 }
272 }
273 }
274 }
275 }
276
277
278 private Partition initializeSystemPartition( DirectoryService directoryService ) throws Exception
279 {
280 // initialize system partition first
281 Partition override = directoryService.getSystemPartition();
282
283 if ( override != null )
284 {
285
286 // ---------------------------------------------------------------
287 // check a few things to make sure users configured it properly
288 // ---------------------------------------------------------------
289
290 if ( ! override.getId().equals( "system" ) )
291 {
292 throw new ConfigurationException( I18n.err( I18n.ERR_262, override.getId() ) );
293 }
294
295
296 system = override;
297 }
298 else
299 {
300 }
301
302 system.initialize( );
303
304
305 // Add root context entry for system partition
306 DN systemSuffixDn = new DN( ServerDNConstants.SYSTEM_DN );
307 systemSuffixDn.normalize( schemaManager.getNormalizerMapping() );
308 ServerEntry systemEntry = new DefaultServerEntry( schemaManager, systemSuffixDn );
309
310 // Add the ObjectClasses
311 systemEntry.put( SchemaConstants.OBJECT_CLASS_AT,
312 SchemaConstants.TOP_OC,
313 SchemaConstants.ORGANIZATIONAL_UNIT_OC,
314 SchemaConstants.EXTENSIBLE_OBJECT_OC
315 );
316
317 // Add some operational attributes
318 systemEntry.put( SchemaConstants.CREATORS_NAME_AT, ServerDNConstants.ADMIN_SYSTEM_DN );
319 systemEntry.put( SchemaConstants.CREATE_TIMESTAMP_AT, DateUtils.getGeneralizedTime() );
320 systemEntry.add( SchemaConstants.ENTRY_CSN_AT, directoryService.getCSN().toString() );
321 systemEntry.add( SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString() );
322 systemEntry.put( NamespaceTools.getRdnAttribute( ServerDNConstants.SYSTEM_DN ),
323 NamespaceTools.getRdnValue( ServerDNConstants.SYSTEM_DN ) );
324 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
325 adminDn.normalize( schemaManager.getNormalizerMapping() );
326 CoreSession adminSession = new DefaultCoreSession(
327 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService );
328 AddOperationContext addOperationContext = new AddOperationContext( adminSession, systemEntry );
329
330 if ( !system.hasEntry( new EntryOperationContext( adminSession, systemEntry.getDn() ) ) )
331 {
332 system.add( addOperationContext );
333 }
334
335 String key = system.getSuffixDn().getName();
336
337 if ( partitions.containsKey( key ) )
338 {
339 throw new ConfigurationException( I18n.err( I18n.ERR_263, key ) );
340 }
341
342 synchronized ( partitionLookupTree )
343 {
344 partitions.put( key, system );
345 partitionLookupTree.add( system.getSuffixDn(), system );
346 EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT );
347
348 if ( namingContexts == null )
349 {
350 namingContexts = new DefaultServerAttribute(
351 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.NAMING_CONTEXTS_AT ),
352 system.getSuffixDn().getName() );
353 rootDSE.put( namingContexts );
354 }
355 else
356 {
357 namingContexts.add( system.getSuffixDn().getName() );
358 }
359 }
360
361 return system;
362 }
363
364
365 /* (non-Javadoc)
366 * @see org.apache.directory.server.core.partition.PartitionNexus#destroy()
367 */
368 protected synchronized void doDestroy()
369 {
370 if ( !initialized )
371 {
372 return;
373 }
374
375 // make sure this loop is not fail fast so all backing stores can
376 // have an attempt at closing down and synching their cached entries
377 for ( String suffix : new HashSet<String>( this.partitions.keySet() ) )
378 {
379 try
380 {
381 DN adminDn = new DN( ServerDNConstants.ADMIN_SYSTEM_DN_NORMALIZED );
382 adminDn.normalize( schemaManager.getNormalizerMapping() );
383 CoreSession adminSession = new DefaultCoreSession(
384 new LdapPrincipal( adminDn, AuthenticationLevel.STRONG ), directoryService );
385 removeContextPartition( new RemoveContextPartitionOperationContext(
386 adminSession, new DN( suffix ) ) );
387 }
388 catch ( Exception e )
389 {
390 LOG.warn( "Failed to destroy a partition: " + suffix, e );
391 }
392 }
393
394 initialized = false;
395 }
396
397
398 /* (non-Javadoc)
399 * @see org.apache.directory.server.core.partition.PartitionNexus#getId()
400 */
401 public String getId()
402 {
403 return "NEXUS";
404 }
405
406
407 /* (non-Javadoc)
408 * @see org.apache.directory.server.core.partition.PartitionNexus#setId(java.lang.String)
409 */
410 public void setId( String id )
411 {
412 throw new UnsupportedOperationException( I18n.err( I18n.ERR_264 ) );
413 }
414
415
416 /**
417 * {@inheritDoc}
418 */
419 public SchemaManager getSchemaManager()
420 {
421 return schemaManager;
422 }
423
424
425 /**
426 * {@inheritDoc}
427 */
428 public void setSchemaManager( SchemaManager schemaManager )
429 {
430 this.schemaManager = schemaManager;
431 }
432
433
434 /* (non-Javadoc)
435 * @see org.apache.directory.server.core.partition.PartitionNexus#getSuffixDn()
436 */
437 public DN getSuffixDn()
438 {
439 return DN.EMPTY_DN;
440 }
441
442
443 /* (non-Javadoc)
444 * @see org.apache.directory.server.core.partition.PartitionNexus#getSuffix()
445 */
446 public String getSuffix()
447 {
448 return StringTools.EMPTY;
449 }
450
451
452 /* (non-Javadoc)
453 * @see org.apache.directory.server.core.partition.PartitionNexus#setSuffix(java.lang.String)
454 */
455 public void setSuffix( String suffix )
456 {
457 throw new UnsupportedOperationException();
458 }
459
460
461 /* (non-Javadoc)
462 * @see org.apache.directory.server.core.partition.PartitionNexus#isInitialized()
463 */
464 public boolean isInitialized()
465 {
466 return initialized;
467 }
468
469
470 /* (non-Javadoc)
471 * @see org.apache.directory.server.core.partition.PartitionNexus#sync()
472 */
473 public void sync() throws Exception
474 {
475 MultiException error = null;
476
477 for ( Partition partition : this.partitions.values() )
478 {
479 try
480 {
481 partition.sync();
482 }
483 catch ( Exception e )
484 {
485 LOG.warn( "Failed to flush partition data out.", e );
486 if ( error == null )
487 {
488 //noinspection ThrowableInstanceNeverThrown
489 error = new MultiException( I18n.err( I18n.ERR_265 ) );
490 }
491
492 // @todo really need to send this info to a monitor
493 error.addThrowable( e );
494 }
495 }
496
497 if ( error != null )
498 {
499 throw error;
500 }
501 }
502
503 // ------------------------------------------------------------------------
504 // DirectoryPartition Interface Method Implementations
505 // ------------------------------------------------------------------------
506 /* (non-Javadoc)
507 * @see org.apache.directory.server.core.partition.PartitionNexus#add(org.apache.directory.server.core.interceptor.context.AddOperationContext)
508 */
509 public void add( AddOperationContext addContext ) throws Exception
510 {
511 Partition backend = getPartition( addContext.getDn() );
512 backend.add( addContext );
513 }
514
515
516 /* (non-Javadoc)
517 * @see org.apache.directory.server.core.partition.PartitionNexus#bind(org.apache.directory.server.core.interceptor.context.BindOperationContext)
518 */
519 public void bind( BindOperationContext bindContext ) throws Exception
520 {
521 Partition partition = getPartition( bindContext.getDn() );
522 partition.bind( bindContext );
523 }
524
525
526 /* (non-Javadoc)
527 * @see org.apache.directory.server.core.partition.PartitionNexus#compare(org.apache.directory.server.core.interceptor.context.CompareOperationContext)
528 */
529 public boolean compare( CompareOperationContext compareContext ) throws Exception
530 {
531 Partition partition = getPartition( compareContext.getDn() );
532 //AttributeTypeRegistry registry = schemaManager.getAttributeTypeRegistry();
533
534 // complain if we do not recognize the attribute being compared
535 if ( !schemaManager.getAttributeTypeRegistry().contains( compareContext.getOid() ) )
536 {
537 throw new LdapInvalidAttributeTypeException( I18n.err( I18n.ERR_266, compareContext.getOid() ) );
538 }
539
540 AttributeType attrType = schemaManager.lookupAttributeTypeRegistry( compareContext.getOid() );
541
542 EntryAttribute attr = partition.lookup( compareContext.newLookupContext(
543 compareContext.getDn() ) ).get( attrType.getName() );
544
545 // complain if the attribute being compared does not exist in the entry
546 if ( attr == null )
547 {
548 throw new LdapNoSuchAttributeException();
549 }
550
551 // see first if simple match without normalization succeeds
552 if ( attr.contains( (Value<?>)compareContext.getValue() ) )
553 {
554 return true;
555 }
556
557 // now must apply normalization to all values (attr and in request) to compare
558
559 /*
560 * Get ahold of the normalizer for the attribute and normalize the request
561 * assertion value for comparisons with normalized attribute values. Loop
562 * through all values looking for a match.
563 */
564 Normalizer normalizer = attrType.getEquality().getNormalizer();
565 Value<?> reqVal = normalizer.normalize( compareContext.getValue() );
566
567 for ( Value<?> value:attr )
568 {
569 Value<?> attrValObj = normalizer.normalize( value );
570
571 if ( attrValObj.equals( reqVal ) )
572 {
573 return true;
574 }
575 }
576
577 return false;
578 }
579
580
581 /* (non-Javadoc)
582 * @see org.apache.directory.server.core.partition.PartitionNexus#delete(org.apache.directory.server.core.interceptor.context.DeleteOperationContext)
583 */
584 public void delete( DeleteOperationContext deleteContext ) throws Exception
585 {
586 Partition backend = getPartition( deleteContext.getDn() );
587 backend.delete( deleteContext );
588 }
589
590
591 /* (non-Javadoc)
592 * @see org.apache.directory.server.core.partition.PartitionNexus#hasEntry(org.apache.directory.server.core.interceptor.context.EntryOperationContext)
593 */
594 public boolean hasEntry( EntryOperationContext opContext ) throws Exception
595 {
596 DN dn = opContext.getDn();
597
598 if ( IS_DEBUG )
599 {
600 LOG.debug( "Check if DN '" + dn + "' exists." );
601 }
602
603 if ( dn.size() == 0 )
604 {
605 return true;
606 }
607
608 Partition backend = getPartition( dn );
609 return backend.hasEntry( opContext );
610 }
611
612
613 /* (non-Javadoc)
614 * @see org.apache.directory.server.core.partition.PartitionNexus#list(org.apache.directory.server.core.interceptor.context.ListOperationContext)
615 */
616 public EntryFilteringCursor list( ListOperationContext opContext ) throws Exception
617 {
618 Partition backend = getPartition( opContext.getDn() );
619 return backend.list( opContext );
620 }
621
622
623 /* (non-Javadoc)
624 * @see org.apache.directory.server.core.partition.PartitionNexus#lookup(org.apache.directory.server.core.interceptor.context.LookupOperationContext)
625 */
626 public ClonedServerEntry lookup( LookupOperationContext opContext ) throws Exception
627 {
628 DN dn = opContext.getDn();
629
630 if ( dn.size() == 0 )
631 {
632 ClonedServerEntry retval = new ClonedServerEntry( rootDSE );
633 Set<AttributeType> attributeTypes = rootDSE.getAttributeTypes();
634
635 if ( opContext.getAttrsId() != null && ! opContext.getAttrsId().isEmpty() )
636 {
637 for ( AttributeType attributeType:attributeTypes )
638 {
639 String oid = attributeType.getOid();
640
641 if ( ! opContext.getAttrsId().contains( oid ) )
642 {
643 retval.removeAttributes( attributeType );
644 }
645 }
646 return retval;
647 }
648 else
649 {
650 return new ClonedServerEntry( rootDSE );
651 }
652 }
653
654 Partition backend = getPartition( dn );
655 return backend.lookup( opContext );
656 }
657
658
659 /* (non-Javadoc)
660 * @see org.apache.directory.server.core.partition.PartitionNexus#lookup(java.lang.Long)
661 */
662 public ClonedServerEntry lookup( Long id ) throws Exception
663 {
664 // TODO not implemented until we can use id to figure out the partition using
665 // the partition ID component of the 64 bit Long identifier
666 throw new NotImplementedException();
667 }
668
669
670 /* (non-Javadoc)
671 * @see org.apache.directory.server.core.partition.PartitionNexus#modify(org.apache.directory.server.core.interceptor.context.ModifyOperationContext)
672 */
673 public void modify( ModifyOperationContext modifyContext ) throws Exception
674 {
675 // Special case : if we don't have any modification to apply, just return
676 if ( modifyContext.getModItems().size() == 0 )
677 {
678 return;
679 }
680
681 Partition backend = getPartition( modifyContext.getDn() );
682 backend.modify( modifyContext );
683 }
684
685
686 /* (non-Javadoc)
687 * @see org.apache.directory.server.core.partition.PartitionNexus#move(org.apache.directory.server.core.interceptor.context.MoveOperationContext)
688 */
689 public void move( MoveOperationContext opContext ) throws Exception
690 {
691 Partition backend = getPartition( opContext.getDn() );
692 backend.move( opContext );
693 }
694
695
696 /* (non-Javadoc)
697 * @see org.apache.directory.server.core.partition.PartitionNexus#moveAndRename(org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext)
698 */
699 public void moveAndRename( MoveAndRenameOperationContext opContext ) throws Exception
700 {
701 Partition backend = getPartition( opContext.getDn() );
702 backend.moveAndRename( opContext );
703 }
704
705
706 /* (non-Javadoc)
707 * @see org.apache.directory.server.core.partition.PartitionNexus#rename(org.apache.directory.server.core.interceptor.context.RenameOperationContext)
708 */
709 public void rename( RenameOperationContext opContext ) throws Exception
710 {
711 Partition backend = getPartition( opContext.getDn() );
712 backend.rename( opContext );
713 }
714
715
716 private EntryFilteringCursor searchRootDSE( SearchOperationContext searchOperationContext ) throws Exception
717 {
718 SearchControls searchControls = searchOperationContext.getSearchControls();
719
720 String[] ids = searchControls.getReturningAttributes();
721
722 // -----------------------------------------------------------
723 // If nothing is asked for then we just return the entry asis.
724 // We let other mechanisms filter out operational attributes.
725 // -----------------------------------------------------------
726 if ( ( ids == null ) || ( ids.length == 0 ) )
727 {
728 ServerEntry rootDSE = (ServerEntry)getRootDSE( null ).clone();
729 return new BaseEntryFilteringCursor( new SingletonCursor<ServerEntry>( rootDSE ), searchOperationContext );
730 }
731
732 // -----------------------------------------------------------
733 // Collect all the real attributes besides 1.1, +, and * and
734 // note if we've seen these special attributes as well.
735 // -----------------------------------------------------------
736
737 Set<String> realIds = new HashSet<String>();
738 boolean allUserAttributes = searchOperationContext.isAllUserAttributes();
739 boolean allOperationalAttributes = searchOperationContext.isAllOperationalAttributes();
740 boolean noAttribute = searchOperationContext.isNoAttributes();
741
742 for ( String id:ids )
743 {
744 String idTrimmed = id.trim();
745
746 try
747 {
748 realIds.add( schemaManager.getAttributeTypeRegistry().getOidByName( idTrimmed ) );
749 }
750 catch ( Exception e )
751 {
752 realIds.add( idTrimmed );
753 }
754 }
755
756 // return nothing
757 if ( noAttribute )
758 {
759 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, DN.EMPTY_DN );
760 return new BaseEntryFilteringCursor( new SingletonCursor<ServerEntry>( serverEntry ), searchOperationContext );
761 }
762
763 // return everything
764 if ( allUserAttributes && allOperationalAttributes )
765 {
766 ServerEntry rootDSE = (ServerEntry)getRootDSE( null ).clone();
767 return new BaseEntryFilteringCursor( new SingletonCursor<ServerEntry>( rootDSE ), searchOperationContext );
768 }
769
770 ServerEntry serverEntry = new DefaultServerEntry( schemaManager, DN.EMPTY_DN );
771
772 ServerEntry rootDSE = getRootDSE( new GetRootDSEOperationContext( searchOperationContext.getSession() ) );
773
774 for ( EntryAttribute attribute:rootDSE )
775 {
776 AttributeType type = schemaManager.lookupAttributeTypeRegistry( attribute.getUpId() );
777
778 if ( realIds.contains( type.getOid() ) )
779 {
780 serverEntry.put( attribute );
781 }
782 else if ( allUserAttributes && ( type.getUsage() == UsageEnum.USER_APPLICATIONS ) )
783 {
784 serverEntry.put( attribute );
785 }
786 else if ( allOperationalAttributes && ( type.getUsage() != UsageEnum.USER_APPLICATIONS ) )
787 {
788 serverEntry.put( attribute );
789 }
790 }
791
792 return new BaseEntryFilteringCursor( new SingletonCursor<ServerEntry>( serverEntry ), searchOperationContext );
793 }
794
795
796 /* (non-Javadoc)
797 * @see org.apache.directory.server.core.partition.PartitionNexus#search(org.apache.directory.server.core.interceptor.context.SearchOperationContext)
798 */
799 public EntryFilteringCursor search( SearchOperationContext opContext ) throws Exception
800 {
801 DN base = opContext.getDn();
802 SearchControls searchCtls = opContext.getSearchControls();
803 ExprNode filter = opContext.getFilter();
804
805 // TODO since we're handling the *, and + in the EntryFilteringCursor
806 // we may not need this code: we need see if this is actually the
807 // case and remove this code.
808 if ( base.size() == 0 )
809 {
810 // We are searching from the rootDSE. We have to distinguish three cases :
811 // 1) The scope is OBJECT : we have to return the rootDSE entry, filtered
812 // 2) The scope is ONELEVEL : we have to return all the Namin
813 boolean isObjectScope = searchCtls.getSearchScope() == SearchControls.OBJECT_SCOPE;
814
815 boolean isOnelevelScope = searchCtls.getSearchScope() == SearchControls.ONELEVEL_SCOPE;
816
817 boolean isSublevelScope = searchCtls.getSearchScope() == SearchControls.SUBTREE_SCOPE;
818
819 // test for (objectClass=*)
820 boolean isSearchAll = false;
821
822 // We have to be careful, as we may have a filter which is not a PresenceFilter
823 if ( filter instanceof PresenceNode )
824 {
825 isSearchAll = ( ( PresenceNode ) filter ).getAttribute().equals( SchemaConstants.OBJECT_CLASS_AT_OID );
826 }
827
828 /*
829 * if basedn is "", filter is "(objectclass=*)" and scope is object
830 * then we have a request for the rootDSE
831 */
832 if ( ( filter instanceof PresenceNode) && isObjectScope && isSearchAll )
833 {
834 return searchRootDSE( opContext );
835 }
836 else if ( isObjectScope && ( ! isSearchAll ) )
837 {
838 return new BaseEntryFilteringCursor( new EmptyCursor<ServerEntry>(), opContext );
839 }
840 else if( isOnelevelScope )
841 {
842 List<EntryFilteringCursor> cursors = new ArrayList<EntryFilteringCursor>();
843 for ( Partition p : partitions.values() )
844 {
845 opContext.setDn( p.getSuffixDn() );
846 opContext.setScope( SearchScope.OBJECT );
847 cursors.add( p.search( opContext ) );
848 }
849
850 return new CursorList( cursors, opContext );
851 }
852 else if ( isSublevelScope )
853 {
854 List<EntryFilteringCursor> cursors = new ArrayList<EntryFilteringCursor>();
855 for ( Partition p : partitions.values() )
856 {
857 ClonedServerEntry entry = p.lookup( new LookupOperationContext( directoryService.getAdminSession(), p.getSuffixDn() ) );
858 if( entry != null )
859 {
860 Partition backend = getPartition( entry.getDn() );
861 opContext.setDn( entry.getDn() );
862 cursors.add( backend.search( opContext ) );
863 }
864 }
865
866 // don't feed the above Cursors' list to a BaseEntryFilteringCursor it is skipping the naming context entry of each partition
867 return new CursorList( cursors, opContext );
868 }
869
870 // TODO : handle searches based on the RootDSE
871 throw new LdapNoSuchObjectException();
872 }
873
874 base.normalize( schemaManager.getNormalizerMapping() );
875 Partition backend = getPartition( base );
876 return backend.search( opContext );
877 }
878
879
880 /* (non-Javadoc)
881 * @see org.apache.directory.server.core.partition.PartitionNexus#unbind(org.apache.directory.server.core.interceptor.context.UnbindOperationContext)
882 */
883 public void unbind( UnbindOperationContext unbindContext ) throws Exception
884 {
885 Partition partition = getPartition( unbindContext.getDn() );
886 partition.unbind( unbindContext );
887 }
888
889
890 /* (non-Javadoc)
891 * @see org.apache.directory.server.core.partition.PartitionNexus#getRootDSE(org.apache.directory.server.core.interceptor.context.GetRootDSEOperationContext)
892 */
893 public ClonedServerEntry getRootDSE( GetRootDSEOperationContext getRootDSEContext )
894 {
895 return new ClonedServerEntry( rootDSE );
896 }
897
898
899 /* (non-Javadoc)
900 * @see org.apache.directory.server.core.partition.PartitionNexus#addContextPartition(org.apache.directory.server.core.interceptor.context.AddContextPartitionOperationContext)
901 */
902 public synchronized void addContextPartition( AddContextPartitionOperationContext opContext ) throws Exception
903 {
904 Partition partition = opContext.getPartition();
905
906 // Turn on default indices
907 String key = partition.getSuffixDn().getNormName();
908
909 if ( partitions.containsKey( key ) )
910 {
911 throw new ConfigurationException( I18n.err( I18n.ERR_263, key ) );
912 }
913
914 if ( ! partition.isInitialized() )
915 {
916 partition.initialize( );
917 }
918
919 synchronized ( partitionLookupTree )
920 {
921 DN partitionSuffix = partition.getSuffixDn();
922
923 if ( partitionSuffix == null )
924 {
925 throw new ConfigurationException( I18n.err( I18n.ERR_267, partition.getId() ) );
926 }
927
928 partitions.put( partitionSuffix.getNormName(), partition );
929 partitionLookupTree.add( partition.getSuffixDn(), partition );
930
931 EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT );
932
933 if ( namingContexts == null )
934 {
935 namingContexts = new DefaultServerAttribute(
936 schemaManager.lookupAttributeTypeRegistry( SchemaConstants.NAMING_CONTEXTS_AT ), partitionSuffix.getName() );
937 rootDSE.put( namingContexts );
938 }
939 else
940 {
941 namingContexts.add( partitionSuffix.getName() );
942 }
943 }
944 }
945
946
947 /* (non-Javadoc)
948 * @see org.apache.directory.server.core.partition.PartitionNexus#removeContextPartition(org.apache.directory.server.core.interceptor.context.RemoveContextPartitionOperationContext)
949 */
950 public synchronized void removeContextPartition( RemoveContextPartitionOperationContext removeContextPartition ) throws Exception
951 {
952 // Get the Partition name. It's a DN.
953 String key = removeContextPartition.getDn().getNormName();
954
955 // Retrieve this partition from the aprtition's table
956 Partition partition = partitions.get( key );
957
958 if ( partition == null )
959 {
960 String msg = I18n.err( I18n.ERR_34, key );
961 LOG.error( msg );
962 throw new NameNotFoundException( msg );
963 }
964
965 String partitionSuffix = partition.getSuffixDn().getName();
966
967 // Retrieve the namingContexts from the RootDSE : the partition
968 // suffix must be present in those namingContexts
969 EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT );
970
971 if ( namingContexts != null )
972 {
973 if ( namingContexts.contains( partitionSuffix ) )
974 {
975 namingContexts.remove( partitionSuffix );
976 }
977 else
978 {
979 String msg = I18n.err( I18n.ERR_35, key );
980 LOG.error( msg );
981 throw new NameNotFoundException( msg );
982 }
983 }
984
985 // Update the partition tree
986 partitionLookupTree.remove( partition );
987 partitions.remove( key );
988 partition.destroy();
989 }
990
991
992 /* (non-Javadoc)
993 * @see org.apache.directory.server.core.partition.PartitionNexus#getSystemPartition()
994 */
995 public Partition getSystemPartition()
996 {
997 return system;
998 }
999
1000
1001 /* (non-Javadoc)
1002 * @see org.apache.directory.server.core.partition.PartitionNexus#getPartition(org.apache.directory.shared.ldap.name.DN)
1003 */
1004 public Partition getPartition( DN dn ) throws Exception
1005 {
1006 Partition parent = partitionLookupTree.getParentElement( dn );
1007
1008 if ( parent == null )
1009 {
1010 throw new LdapNoSuchObjectException( I18n.err( I18n.ERR_268, dn ) );
1011 }
1012 else
1013 {
1014 return parent;
1015 }
1016 }
1017
1018
1019 /* (non-Javadoc)
1020 * @see org.apache.directory.server.core.partition.PartitionNexus#getMatchedName(org.apache.directory.server.core.interceptor.context.GetMatchedNameOperationContext)
1021 */
1022 public DN getMatchedName( GetMatchedNameOperationContext matchedNameContext ) throws Exception
1023 {
1024 DN dn = ( DN ) matchedNameContext.getDn().clone();
1025
1026 while ( dn.size() > 0 )
1027 {
1028 if ( hasEntry( new EntryOperationContext( matchedNameContext.getSession(), dn ) ) )
1029 {
1030 return dn;
1031 }
1032
1033 dn.remove( dn.size() - 1 );
1034 }
1035
1036 return dn;
1037 }
1038
1039
1040 /* (non-Javadoc)
1041 * @see org.apache.directory.server.core.partition.PartitionNexus#getSuffix(org.apache.directory.server.core.interceptor.context.GetSuffixOperationContext)
1042 */
1043 public DN getSuffix( GetSuffixOperationContext getSuffixContext ) throws Exception
1044 {
1045 Partition backend = getPartition( getSuffixContext.getDn() );
1046 return backend.getSuffixDn();
1047 }
1048
1049
1050 /* (non-Javadoc)
1051 * @see org.apache.directory.server.core.partition.PartitionNexus#listSuffixes(org.apache.directory.server.core.interceptor.context.ListSuffixOperationContext)
1052 */
1053 public Set<String> listSuffixes( ListSuffixOperationContext emptyContext ) throws Exception
1054 {
1055 return Collections.unmodifiableSet( partitions.keySet() );
1056 }
1057
1058
1059 /* (non-Javadoc)
1060 * @see org.apache.directory.server.core.partition.PartitionNexus#registerSupportedExtensions(java.util.Set)
1061 */
1062 public void registerSupportedExtensions( Set<String> extensionOids ) throws Exception
1063 {
1064 EntryAttribute supportedExtension = rootDSE.get( SchemaConstants.SUPPORTED_EXTENSION_AT );
1065
1066 if ( supportedExtension == null )
1067 {
1068 rootDSE.set( SchemaConstants.SUPPORTED_EXTENSION_AT );
1069 supportedExtension = rootDSE.get( SchemaConstants.SUPPORTED_EXTENSION_AT );
1070 }
1071
1072 for ( String extensionOid : extensionOids )
1073 {
1074 supportedExtension.add( extensionOid );
1075 }
1076 }
1077
1078
1079 /* (non-Javadoc)
1080 * @see org.apache.directory.server.core.partition.PartitionNexus#registerSupportedSaslMechanisms(java.util.Set)
1081 */
1082 public void registerSupportedSaslMechanisms( Set<String> supportedSaslMechanisms ) throws Exception
1083 {
1084 EntryAttribute supportedSaslMechanismsAttribute = rootDSE.get( SchemaConstants.SUPPORTED_SASL_MECHANISMS_AT );
1085
1086 if ( supportedSaslMechanismsAttribute == null )
1087 {
1088 rootDSE.set( SchemaConstants.SUPPORTED_SASL_MECHANISMS_AT );
1089 supportedSaslMechanismsAttribute = rootDSE.get( SchemaConstants.SUPPORTED_SASL_MECHANISMS_AT );
1090 }
1091
1092 for ( String saslMechanism : supportedSaslMechanisms )
1093 {
1094 supportedSaslMechanismsAttribute.add( saslMechanism );
1095 }
1096 }
1097
1098
1099 /**
1100 * Unregisters an ContextPartition with this BackendManager. Called for each
1101 * registered Backend right befor it is to be stopped. This prevents
1102 * protocol server requests from reaching the Backend and effectively puts
1103 * the ContextPartition's naming context offline.
1104 *
1105 * Operations against the naming context should result in an LDAP BUSY
1106 * result code in the returnValue if the naming context is not online.
1107 *
1108 * @param partition ContextPartition component to unregister with this
1109 * BackendNexus.
1110 * @throws Exception if there are problems unregistering the partition
1111 */
1112 private void unregister( Partition partition ) throws Exception
1113 {
1114 EntryAttribute namingContexts = rootDSE.get( SchemaConstants.NAMING_CONTEXTS_AT );
1115
1116 if ( namingContexts != null )
1117 {
1118 namingContexts.remove( partition.getSuffixDn().getName() );
1119 }
1120
1121 partitions.remove( partition.getSuffixDn().getName() );
1122 }
1123
1124
1125 /**
1126 * @return the directoryService
1127 */
1128 public DirectoryService getDirectoryService()
1129 {
1130 return directoryService;
1131 }
1132
1133
1134 /**
1135 * @param directoryService the directoryService to set
1136 */
1137 public void setDirectoryService( DirectoryService directoryService )
1138 {
1139 this.directoryService = directoryService;
1140 }
1141 }