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 */ 020package org.apache.directory.server.core.jndi; 021 022 023import java.io.Serializable; 024import java.util.HashMap; 025import java.util.Hashtable; 026import java.util.List; 027import java.util.Map; 028 029import javax.naming.Context; 030import javax.naming.InvalidNameException; 031import javax.naming.Name; 032import javax.naming.NameNotFoundException; 033import javax.naming.NameParser; 034import javax.naming.NamingEnumeration; 035import javax.naming.NamingException; 036import javax.naming.NoPermissionException; 037import javax.naming.Reference; 038import javax.naming.Referenceable; 039import javax.naming.directory.DirContext; 040import javax.naming.directory.SchemaViolationException; 041import javax.naming.directory.SearchControls; 042import javax.naming.event.EventContext; 043import javax.naming.event.NamingListener; 044import javax.naming.ldap.Control; 045import javax.naming.ldap.LdapName; 046import javax.naming.spi.DirStateFactory; 047import javax.naming.spi.DirectoryManager; 048 049import org.apache.directory.api.asn1.DecoderException; 050import org.apache.directory.api.ldap.codec.api.ControlFactory; 051import org.apache.directory.api.ldap.extras.controls.ppolicy.PasswordPolicyRequest; 052import org.apache.directory.api.ldap.extras.controls.syncrepl.syncDone.SyncDoneValue; 053import org.apache.directory.api.ldap.extras.controls.syncrepl.syncRequest.SyncRequestValue; 054import org.apache.directory.api.ldap.extras.controls.syncrepl.syncState.SyncStateValue; 055import org.apache.directory.api.ldap.extras.intermediate.syncrepl.SyncInfoValue; 056import org.apache.directory.api.ldap.model.constants.JndiPropertyConstants; 057import org.apache.directory.api.ldap.model.constants.SchemaConstants; 058import org.apache.directory.api.ldap.model.cursor.EmptyCursor; 059import org.apache.directory.api.ldap.model.cursor.SingletonCursor; 060import org.apache.directory.api.ldap.model.entry.AttributeUtils; 061import org.apache.directory.api.ldap.model.entry.DefaultEntry; 062import org.apache.directory.api.ldap.model.entry.Entry; 063import org.apache.directory.api.ldap.model.entry.Modification; 064import org.apache.directory.api.ldap.model.exception.LdapException; 065import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeTypeException; 066import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; 067import org.apache.directory.api.ldap.model.filter.EqualityNode; 068import org.apache.directory.api.ldap.model.filter.ExprNode; 069import org.apache.directory.api.ldap.model.filter.PresenceNode; 070import org.apache.directory.api.ldap.model.message.AliasDerefMode; 071import org.apache.directory.api.ldap.model.message.SearchScope; 072import org.apache.directory.api.ldap.model.message.controls.Cascade; 073import org.apache.directory.api.ldap.model.message.controls.EntryChange; 074import org.apache.directory.api.ldap.model.message.controls.ManageDsaIT; 075import org.apache.directory.api.ldap.model.message.controls.PagedResults; 076import org.apache.directory.api.ldap.model.message.controls.PersistentSearch; 077import org.apache.directory.api.ldap.model.message.controls.Subentries; 078import org.apache.directory.api.ldap.model.name.Ava; 079import org.apache.directory.api.ldap.model.name.Dn; 080import org.apache.directory.api.ldap.model.name.Rdn; 081import org.apache.directory.api.ldap.model.schema.AttributeType; 082import org.apache.directory.api.ldap.model.schema.SchemaManager; 083import org.apache.directory.api.ldap.util.JndiUtils; 084import org.apache.directory.api.util.Strings; 085import org.apache.directory.server.core.api.CoreSession; 086import org.apache.directory.server.core.api.DirectoryService; 087import org.apache.directory.server.core.api.LdapPrincipal; 088import org.apache.directory.server.core.api.OperationEnum; 089import org.apache.directory.server.core.api.OperationManager; 090import org.apache.directory.server.core.api.entry.ServerEntryUtils; 091import org.apache.directory.server.core.api.event.DirectoryListener; 092import org.apache.directory.server.core.api.event.NotificationCriteria; 093import org.apache.directory.server.core.api.filtering.EntryFilteringCursorImpl; 094import org.apache.directory.server.core.api.filtering.EntryFilteringCursor; 095import org.apache.directory.server.core.api.interceptor.context.AddOperationContext; 096import org.apache.directory.server.core.api.interceptor.context.BindOperationContext; 097import org.apache.directory.server.core.api.interceptor.context.CompareOperationContext; 098import org.apache.directory.server.core.api.interceptor.context.DeleteOperationContext; 099import org.apache.directory.server.core.api.interceptor.context.GetRootDseOperationContext; 100import org.apache.directory.server.core.api.interceptor.context.HasEntryOperationContext; 101import org.apache.directory.server.core.api.interceptor.context.LookupOperationContext; 102import org.apache.directory.server.core.api.interceptor.context.ModifyOperationContext; 103import org.apache.directory.server.core.api.interceptor.context.MoveAndRenameOperationContext; 104import org.apache.directory.server.core.api.interceptor.context.MoveOperationContext; 105import org.apache.directory.server.core.api.interceptor.context.OperationContext; 106import org.apache.directory.server.core.api.interceptor.context.RenameOperationContext; 107import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext; 108import org.apache.directory.server.core.shared.DefaultCoreSession; 109import org.apache.directory.server.i18n.I18n; 110 111 112/** 113 * A non-federated abstract Context implementation. 114 * 115 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 116 */ 117public abstract class ServerContext implements EventContext 118{ 119 /** property key used for deleting the old Rdn on a rename */ 120 public static final String DELETE_OLD_RDN_PROP = JndiPropertyConstants.JNDI_LDAP_DELETE_RDN; 121 122 /** Empty array of controls for use in dealing with them */ 123 protected static final Control[] EMPTY_CONTROLS = new Control[0]; 124 125 /** The directory service which owns this context **/ 126 private final DirectoryService service; 127 128 /** The SchemManager instance */ 129 protected SchemaManager schemaManager; 130 131 /** A reference to the ObjectClass AT */ 132 protected AttributeType objectClassAT; 133 134 /** The cloned environment used by this Context */ 135 private final Hashtable<String, Object> env; 136 137 /** The distinguished name of this Context */ 138 private final Dn dn; 139 140 /** The set of registered NamingListeners */ 141 private final Map<NamingListener, DirectoryListener> listeners = 142 new HashMap<>(); 143 144 /** The request controls to set on operations before performing them */ 145 protected Control[] requestControls = EMPTY_CONTROLS; 146 147 /** The response controls to set after performing operations */ 148 protected Control[] responseControls = EMPTY_CONTROLS; 149 150 /** Connection level controls associated with the session */ 151 protected Control[] connectControls = EMPTY_CONTROLS; 152 153 /** The session */ 154 private final CoreSession session; 155 156 private static final Map<String, ControlEnum> ADS_CONTROLS = new HashMap<>(); 157 158 static 159 { 160 ADS_CONTROLS.put( Cascade.OID, ControlEnum.CASCADE_CONTROL ); 161 ADS_CONTROLS.put( EntryChange.OID, ControlEnum.ENTRY_CHANGE_CONTROL ); 162 ADS_CONTROLS.put( ManageDsaIT.OID, ControlEnum.MANAGE_DSA_IT_CONTROL ); 163 ADS_CONTROLS.put( PagedResults.OID, ControlEnum.PAGED_RESULTS_CONTROL ); 164 ADS_CONTROLS.put( PasswordPolicyRequest.OID, ControlEnum.PASSWORD_POLICY_REQUEST_CONTROL ); 165 ADS_CONTROLS.put( PersistentSearch.OID, ControlEnum.PERSISTENT_SEARCH_CONTROL ); 166 ADS_CONTROLS.put( Subentries.OID, ControlEnum.SUBENTRIES_CONTROL ); 167 ADS_CONTROLS.put( SyncDoneValue.OID, ControlEnum.SYNC_DONE_VALUE_CONTROL ); 168 ADS_CONTROLS.put( SyncInfoValue.OID, ControlEnum.SYNC_INFO_VALUE_CONTROL ); 169 ADS_CONTROLS.put( SyncRequestValue.OID, ControlEnum.SYNC_REQUEST_VALUE_CONTROL ); 170 ADS_CONTROLS.put( SyncStateValue.OID, ControlEnum.SYNC_STATE_VALUE_CONTROL ); 171 } 172 173 174 // ------------------------------------------------------------------------ 175 // Constructors 176 // ------------------------------------------------------------------------ 177 /** 178 * Must be called by all subclasses to initialize the nexus proxy and the 179 * environment settings to be used by this Context implementation. This 180 * specific constructor relies on the presence of the {@link 181 * Context#PROVIDER_URL} key and value to determine the distinguished name 182 * of the newly created context. It also checks to make sure the 183 * referenced name actually exists within the system. This constructor 184 * is used for all InitialContext requests. 185 * 186 * @param service the parent service that manages this context 187 * @param env the environment properties used by this context. 188 * @throws NamingException if the environment parameters are not set 189 * correctly. 190 */ 191 protected ServerContext( DirectoryService service, Hashtable<String, Object> env ) throws Exception 192 { 193 this.service = service; 194 this.env = env; 195 196 LdapJndiProperties props = LdapJndiProperties.getLdapJndiProperties( this.env ); 197 dn = props.getProviderDn(); 198 199 /* 200 * Need do bind operation here, and bindContext returned contains the 201 * newly created session. 202 */ 203 BindOperationContext bindContext = doBindOperation( props.getBindDn(), props.getCredentials(), 204 props.getSaslMechanism(), props.getSaslAuthId() ); 205 206 session = bindContext.getSession(); 207 OperationManager operationManager = service.getOperationManager(); 208 209 HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( session, dn ); 210 211 if ( !operationManager.hasEntry( hasEntryContext ) ) 212 { 213 throw new NameNotFoundException( I18n.err( I18n.ERR_490, dn ) ); 214 } 215 216 schemaManager = service.getSchemaManager(); 217 218 // setup attribute type value 219 objectClassAT = schemaManager.getAttributeType( SchemaConstants.OBJECT_CLASS_AT ); 220 } 221 222 223 /** 224 * Must be called by all subclasses to initialize the nexus proxy and the 225 * environment settings to be used by this Context implementation. This 226 * constructor is used to propagate new contexts from existing contexts. 227 * 228 * @param service the directory service core 229 * @param principal the directory user principal that is propagated 230 * @param name the distinguished name of this context 231 * @throws NamingException if there is a problem creating the new context 232 */ 233 public ServerContext( DirectoryService service, LdapPrincipal principal, Name name ) throws Exception 234 { 235 this.service = service; 236 this.dn = JndiUtils.fromName( name ); 237 238 this.env = new Hashtable<>(); 239 this.env.put( PROVIDER_URL, dn.toString() ); 240 this.env.put( DirectoryService.JNDI_KEY, service ); 241 session = new DefaultCoreSession( principal, service ); 242 OperationManager operationManager = service.getOperationManager(); 243 244 HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( session, dn ); 245 246 if ( !operationManager.hasEntry( hasEntryContext ) ) 247 { 248 throw new NameNotFoundException( I18n.err( I18n.ERR_490, dn ) ); 249 } 250 251 schemaManager = service.getSchemaManager(); 252 253 // setup attribute type value 254 objectClassAT = schemaManager.getAttributeType( SchemaConstants.OBJECT_CLASS_AT ); 255 } 256 257 258 public ServerContext( DirectoryService service, CoreSession session, Name name ) throws Exception 259 { 260 this.service = service; 261 this.dn = JndiUtils.fromName( name ); 262 this.env = new Hashtable<>(); 263 this.env.put( PROVIDER_URL, dn.toString() ); 264 this.env.put( DirectoryService.JNDI_KEY, service ); 265 this.session = session; 266 OperationManager operationManager = service.getOperationManager(); 267 268 HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( session, dn ); 269 270 if ( !operationManager.hasEntry( hasEntryContext ) ) 271 { 272 throw new NameNotFoundException( I18n.err( I18n.ERR_490, dn ) ); 273 } 274 275 schemaManager = service.getSchemaManager(); 276 277 // setup attribute type value 278 objectClassAT = schemaManager.getAttributeType( SchemaConstants.OBJECT_CLASS_AT ); 279 } 280 281 282 /** 283 * Set the referral handling flag into the operation context using 284 * the JNDI value stored into the environment. 285 * 286 * @param opCtx The operation context 287 */ 288 protected void injectReferralControl( OperationContext opCtx ) 289 { 290 if ( "ignore".equalsIgnoreCase( ( String ) env.get( Context.REFERRAL ) ) ) 291 { 292 opCtx.ignoreReferral(); 293 } 294 else if ( "throw".equalsIgnoreCase( ( String ) env.get( Context.REFERRAL ) ) ) 295 { 296 opCtx.throwReferral(); 297 } 298 else 299 { 300 // TODO : handle the 'follow' referral option 301 opCtx.throwReferral(); 302 } 303 } 304 305 306 // ------------------------------------------------------------------------ 307 // Protected Methods for Operations 308 // ------------------------------------------------------------------------ 309 // Use these methods instead of manually calling the nexusProxy so we can 310 // add request controls to operation contexts before the call and extract 311 // response controls from the contexts after the call. NOTE that the 312 // convertControls( requestControls ) must be cleared after each operation. This makes a 313 // context not thread safe. 314 // ------------------------------------------------------------------------ 315 316 /** 317 * Used to encapsulate [de]marshalling of controls before and after add operations. 318 * 319 * @param target The entry's Dn to add 320 * @param entry The entry to add 321 * @throws Exception If the add failed 322 */ 323 protected void doAddOperation( Dn target, Entry entry ) throws Exception 324 { 325 // setup the op context and populate with request controls 326 AddOperationContext opCtx = new AddOperationContext( session, entry ); 327 328 opCtx.addRequestControls( convertControls( true, requestControls ) ); 329 330 // Inject the referral handling into the operation context 331 injectReferralControl( opCtx ); 332 333 // execute add operation 334 OperationManager operationManager = service.getOperationManager(); 335 operationManager.add( opCtx ); 336 337 // clear the request controls and set the response controls 338 requestControls = EMPTY_CONTROLS; 339 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 340 opCtx.getResponseControls() ); 341 } 342 343 344 /** 345 * Used to encapsulate [de]marshalling of controls before and after delete operations. 346 * 347 * @param target The entry's Dn we want to delete 348 * @throws Exception If we can't delete the entry 349 */ 350 protected void doDeleteOperation( Dn target ) throws Exception 351 { 352 // setup the op context and populate with request controls 353 DeleteOperationContext deleteContext = new DeleteOperationContext( session, target ); 354 355 deleteContext.addRequestControls( convertControls( true, requestControls ) ); 356 357 // Inject the referral handling into the operation context 358 injectReferralControl( deleteContext ); 359 360 // execute delete operation 361 OperationManager operationManager = service.getOperationManager(); 362 operationManager.delete( deleteContext ); 363 364 // clear the request controls and set the response controls 365 requestControls = EMPTY_CONTROLS; 366 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 367 deleteContext.getResponseControls() ); 368 } 369 370 371 private org.apache.directory.api.ldap.model.message.Control convertControl( boolean isRequest, 372 Control jndiControl ) throws DecoderException 373 { 374 String controlIdStr = jndiControl.getID(); 375 376 ControlFactory<?> controlFactory; 377 378 if ( isRequest ) 379 { 380 controlFactory = service.getLdapCodecService().getRequestControlFactories().get( controlIdStr ); 381 } 382 else 383 { 384 controlFactory = service.getLdapCodecService().getResponseControlFactories().get( controlIdStr ); 385 } 386 387 if ( controlFactory == null ) 388 { 389 throw new IllegalArgumentException( "Unsupported control " + controlIdStr ); 390 } 391 392 org.apache.directory.api.ldap.model.message.Control apiControl = controlFactory.newControl(); 393 394 apiControl.setCritical( jndiControl.isCritical() ); 395 controlFactory.decodeValue( apiControl, jndiControl.getEncodedValue() ); 396 397 return apiControl; 398 } 399 400 401 /** 402 * Convert the JNDI controls to ADS controls 403 * TODO convertControls. 404 */ 405 private org.apache.directory.api.ldap.model.message.Control[] convertControls( boolean isRequest, 406 Control[] jndiControls ) throws DecoderException 407 { 408 if ( jndiControls != null ) 409 { 410 org.apache.directory.api.ldap.model.message.Control[] controls = 411 new org.apache.directory.api.ldap.model.message.Control[jndiControls.length]; 412 int i = 0; 413 414 for ( javax.naming.ldap.Control jndiControl : jndiControls ) 415 { 416 controls[i++] = convertControl( isRequest, jndiControl ); 417 } 418 419 return controls; 420 } 421 else 422 { 423 return null; 424 } 425 426 } 427 428 429 /** 430 * Used to encapsulate [de]marshalling of controls before and after list operations. 431 * 432 * @param dn The base search Dn 433 * @param aliasDerefMode The AliasDeref mode 434 * @param filter The search filter 435 * @param searchControls The controls 436 * @return A cursor on the found entries 437 * @throws Exception If the search failed 438 */ 439 protected EntryFilteringCursor doSearchOperation( Dn dn, AliasDerefMode aliasDerefMode, 440 ExprNode filter, SearchControls searchControls ) throws Exception 441 { 442 OperationManager operationManager = service.getOperationManager(); 443 EntryFilteringCursor results = null; 444 445 Object typesOnlyObj = getEnvironment().get( "java.naming.ldap.typesOnly" ); 446 boolean typesOnly = false; 447 448 if ( typesOnlyObj != null ) 449 { 450 typesOnly = Boolean.parseBoolean( typesOnlyObj.toString() ); 451 } 452 453 SearchOperationContext searchContext = null; 454 455 // We have to check if it's a compare operation or a search. 456 // A compare operation has a OBJECT scope search, the filter must 457 // be of the form (object=value) (no wildcards), and no attributes 458 // should be asked to be returned. 459 if ( ( searchControls.getSearchScope() == SearchControls.OBJECT_SCOPE ) 460 && ( ( searchControls.getReturningAttributes() != null ) 461 && ( searchControls.getReturningAttributes().length == 0 ) ) 462 && ( filter instanceof EqualityNode ) ) 463 { 464 CompareOperationContext compareContext = new CompareOperationContext( session, dn, 465 ( ( EqualityNode<?> ) filter ).getAttribute(), ( ( EqualityNode ) filter ).getValue() ); 466 467 // Inject the referral handling into the operation context 468 injectReferralControl( compareContext ); 469 470 // Call the operation 471 boolean result = operationManager.compare( compareContext ); 472 473 // setup the op context and populate with request controls 474 searchContext = new SearchOperationContext( session, dn, filter, searchControls ); 475 searchContext.setAliasDerefMode( aliasDerefMode ); 476 searchContext.addRequestControls( convertControls( true, requestControls ) ); 477 478 searchContext.setTypesOnly( typesOnly ); 479 480 if ( result ) 481 { 482 Entry emptyEntry = new DefaultEntry( service.getSchemaManager(), Dn.EMPTY_DN ); 483 return new EntryFilteringCursorImpl( new SingletonCursor<Entry>( emptyEntry ), 484 searchContext, schemaManager ); 485 } 486 else 487 { 488 return new EntryFilteringCursorImpl( new EmptyCursor<Entry>(), searchContext, schemaManager ); 489 } 490 } 491 else 492 { 493 // It's a Search 494 495 // setup the op context and populate with request controls 496 searchContext = new SearchOperationContext( session, dn, filter, searchControls ); 497 searchContext.setAliasDerefMode( aliasDerefMode ); 498 searchContext.addRequestControls( convertControls( true, requestControls ) ); 499 searchContext.setTypesOnly( typesOnly ); 500 501 // Inject the referral handling into the operation context 502 injectReferralControl( searchContext ); 503 504 // execute search operation 505 results = operationManager.search( searchContext ); 506 } 507 508 // clear the request controls and set the response controls 509 requestControls = EMPTY_CONTROLS; 510 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 511 searchContext.getResponseControls() ); 512 513 return results; 514 } 515 516 517 /** 518 * Used to encapsulate [de]marshalling of controls before and after list operations. 519 * 520 * @param target The search Dn 521 * @return A cursor on the found entries 522 * @throws Exception If we can't process the search 523 */ 524 protected EntryFilteringCursor doListOperation( Dn target ) throws Exception 525 { 526 // setup the op context and populate with request controls 527 PresenceNode filter = new PresenceNode( objectClassAT ); 528 SearchOperationContext searchContext = new SearchOperationContext( session, target, SearchScope.ONELEVEL, filter, SchemaConstants.ALL_USER_ATTRIBUTES_ARRAY ); 529 searchContext.addRequestControls( convertControls( true, requestControls ) ); 530 531 // execute search operation 532 OperationManager operationManager = service.getOperationManager(); 533 EntryFilteringCursor results = operationManager.search( searchContext ); 534 535 // clear the request controls and set the response controls 536 requestControls = EMPTY_CONTROLS; 537 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 538 searchContext.getResponseControls() ); 539 540 return results; 541 } 542 543 544 /** 545 * Fetch the rootDSE entry 546 * 547 * @param target Should be empty 548 * @return The RootDSE entry 549 * @throws Exception If we can't fetch the RootDSE entry 550 */ 551 protected Entry doGetRootDseOperation( Dn target ) throws Exception 552 { 553 GetRootDseOperationContext getRootDseContext = new GetRootDseOperationContext( session, target ); 554 getRootDseContext.addRequestControls( convertControls( true, requestControls ) ); 555 556 // do not reset request controls since this is not an external 557 // operation and not do bother setting the response controls either 558 OperationManager operationManager = service.getOperationManager(); 559 560 return operationManager.getRootDse( getRootDseContext ); 561 } 562 563 564 /** 565 * Used to encapsulate [de]marshalling of controls before and after lookup operations. 566 * 567 * @param target The Dn we are looking for 568 * @return The found entry 569 * @throws Exception If the lookup failed 570 */ 571 protected Entry doLookupOperation( Dn target ) throws Exception 572 { 573 // setup the op context and populate with request controls 574 // execute lookup/getRootDSE operation 575 LookupOperationContext lookupContext = new LookupOperationContext( session, target ); 576 lookupContext.addRequestControls( convertControls( true, requestControls ) ); 577 OperationManager operationManager = service.getOperationManager(); 578 579 Entry serverEntry = operationManager.lookup( lookupContext ); 580 581 // clear the request controls and set the response controls 582 requestControls = EMPTY_CONTROLS; 583 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 584 lookupContext.getResponseControls() ); 585 586 return serverEntry; 587 } 588 589 590 /** 591 * Used to encapsulate [de]marshalling of controls before and after lookup operations. 592 * 593 * @param target The Dn we are looking for 594 * @param attrIds The attributes to return 595 * @return The found entry 596 * @throws Exception If the lookup failed 597 */ 598 protected Entry doLookupOperation( Dn target, String[] attrIds ) throws Exception 599 { 600 // setup the op context and populate with request controls 601 // execute lookup/getRootDSE operation 602 LookupOperationContext lookupContext = new LookupOperationContext( session, target, attrIds ); 603 lookupContext.addRequestControls( convertControls( true, requestControls ) ); 604 OperationManager operationManager = service.getOperationManager(); 605 Entry serverEntry = operationManager.lookup( lookupContext ); 606 607 // clear the request controls and set the response controls 608 requestControls = EMPTY_CONTROLS; 609 responseControls = JndiUtils.toJndiControls( 610 getDirectoryService().getLdapCodecService(), 611 lookupContext.getResponseControls() ); 612 613 // Now remove the ObjectClass attribute if it has not been requested 614 if ( ( lookupContext.getReturningAttributes() != null ) 615 && !lookupContext.getReturningAttributes().isEmpty() 616 && ( serverEntry.get( SchemaConstants.OBJECT_CLASS_AT ) != null ) 617 && ( serverEntry.get( SchemaConstants.OBJECT_CLASS_AT ).size() == 0 ) ) 618 { 619 serverEntry.removeAttributes( SchemaConstants.OBJECT_CLASS_AT ); 620 } 621 622 return serverEntry; 623 } 624 625 626 /** 627 * Used to encapsulate [de]marshalling of controls before and after bind operations. 628 * 629 * @param bindDn The user's Dn 630 * @param credentials The credentials 631 * @param saslMechanism The SASL mechanism to use 632 * @param saslAuthId The SASL authorization ID 633 * @return A BindOperationContext instance 634 * @throws Exception If the Bind failed 635 */ 636 protected BindOperationContext doBindOperation( Dn bindDn, byte[] credentials, String saslMechanism, 637 String saslAuthId ) throws Exception 638 { 639 // setup the op context and populate with request controls 640 BindOperationContext bindContext = new BindOperationContext( null ); 641 bindContext.setDn( bindDn ); 642 bindContext.setCredentials( credentials ); 643 bindContext.setSaslMechanism( saslMechanism ); 644 bindContext.setSaslAuthId( saslAuthId ); 645 bindContext.addRequestControls( convertControls( true, requestControls ) ); 646 bindContext.setInterceptors( getDirectoryService().getInterceptors( OperationEnum.BIND ) ); 647 648 // execute bind operation 649 OperationManager operationManager = service.getOperationManager(); 650 operationManager.bind( bindContext ); 651 652 // clear the request controls and set the response controls 653 requestControls = EMPTY_CONTROLS; 654 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 655 bindContext.getResponseControls() ); 656 return bindContext; 657 } 658 659 660 /** 661 * Used to encapsulate [de]marshalling of controls before and after moveAndRename operations. 662 * 663 * @param oldDn The old Dn 664 * @param parent The new parent 665 * @param newRdn The new Rdn 666 * @param delOldRdn If we shoud delete the old Rdn 667 * @throws Exception If the move and rename failed 668 */ 669 protected void doMoveAndRenameOperation( Dn oldDn, Dn parent, Rdn newRdn, boolean delOldRdn ) 670 throws Exception 671 { 672 // setup the op context and populate with request controls 673 MoveAndRenameOperationContext moveAndRenameContext = new MoveAndRenameOperationContext( session, oldDn, parent, 674 newRdn, delOldRdn ); 675 moveAndRenameContext.addRequestControls( convertControls( true, requestControls ) ); 676 677 // Inject the referral handling into the operation context 678 injectReferralControl( moveAndRenameContext ); 679 680 // execute moveAndRename operation 681 OperationManager operationManager = service.getOperationManager(); 682 operationManager.moveAndRename( moveAndRenameContext ); 683 684 // clear the request controls and set the response controls 685 requestControls = EMPTY_CONTROLS; 686 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 687 moveAndRenameContext.getResponseControls() ); 688 } 689 690 691 /** 692 * Used to encapsulate [de]marshalling of controls before and after modify operations. 693 * 694 * @param dn The modified entry's dn 695 * @param modifications The list of modifications to apply 696 * @throws Exception If the modify failed 697 */ 698 protected void doModifyOperation( Dn dn, List<Modification> modifications ) throws Exception 699 { 700 // setup the op context and populate with request controls 701 ModifyOperationContext modifyContext = new ModifyOperationContext( session, dn, modifications ); 702 modifyContext.addRequestControls( convertControls( true, requestControls ) ); 703 704 // Inject the referral handling into the operation context 705 injectReferralControl( modifyContext ); 706 707 // execute modify operation 708 OperationManager operationManager = service.getOperationManager(); 709 operationManager.modify( modifyContext ); 710 711 // clear the request controls and set the response controls 712 requestControls = EMPTY_CONTROLS; 713 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 714 modifyContext.getResponseControls() ); 715 } 716 717 718 /** 719 * Used to encapsulate [de]marshalling of controls before and after moveAndRename operations. 720 * 721 * @param oldDn The old Dn 722 * @param target The target 723 * @throws Exception If the move failed 724 */ 725 protected void doMove( Dn oldDn, Dn target ) throws Exception 726 { 727 // setup the op context and populate with request controls 728 MoveOperationContext moveContext = new MoveOperationContext( session, oldDn, target ); 729 moveContext.addRequestControls( convertControls( true, requestControls ) ); 730 731 // Inject the referral handling into the operation context 732 injectReferralControl( moveContext ); 733 734 // execute move operation 735 OperationManager operationManager = service.getOperationManager(); 736 operationManager.move( moveContext ); 737 738 // clear the request controls and set the response controls 739 requestControls = EMPTY_CONTROLS; 740 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 741 moveContext.getResponseControls() ); 742 } 743 744 745 /** 746 * Used to encapsulate [de]marshalling of controls before and after rename operations. 747 * 748 * @param oldDn The old Dn 749 * @param newRdn The new Rdn 750 * @param delOldRdn If we should delete the old Rdn 751 * @throws Exception If the rename failed 752 */ 753 protected void doRename( Dn oldDn, Rdn newRdn, boolean delOldRdn ) throws Exception 754 { 755 // setup the op context and populate with request controls 756 RenameOperationContext renameContext = new RenameOperationContext( session, oldDn, newRdn, delOldRdn ); 757 renameContext.addRequestControls( convertControls( true, requestControls ) ); 758 759 // Inject the referral handling into the operation context 760 injectReferralControl( renameContext ); 761 762 // execute rename operation 763 OperationManager operationManager = service.getOperationManager(); 764 operationManager.rename( renameContext ); 765 766 // clear the request controls and set the response controls 767 requestControls = EMPTY_CONTROLS; 768 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 769 renameContext.getResponseControls() ); 770 } 771 772 773 public CoreSession getSession() 774 { 775 return session; 776 } 777 778 779 public DirectoryService getDirectoryService() 780 { 781 return service; 782 } 783 784 785 // ------------------------------------------------------------------------ 786 // New Impl Specific Public Methods 787 // ------------------------------------------------------------------------ 788 789 /** 790 * Gets a handle on the root context of the DIT. The RootDSE as the present user. 791 * 792 * @return the rootDSE context 793 * @throws NamingException if this fails 794 */ 795 public abstract ServerContext getRootContext() throws NamingException; 796 797 798 /** 799 * Gets the {@link DirectoryService} associated with this context. 800 * 801 * @return the directory service associated with this context 802 */ 803 public DirectoryService getService() 804 { 805 return service; 806 } 807 808 809 // ------------------------------------------------------------------------ 810 // Protected Accessor Methods 811 // ------------------------------------------------------------------------ 812 813 /** 814 * Gets the distinguished name of the entry associated with this Context. 815 * 816 * @return the distinguished name of this Context's entry. 817 */ 818 protected Dn getDn() 819 { 820 return dn; 821 } 822 823 824 // ------------------------------------------------------------------------ 825 // JNDI Context Interface Methods 826 // ------------------------------------------------------------------------ 827 828 /** 829 * @see javax.naming.Context#close() 830 */ 831 public void close() throws NamingException 832 { 833 for ( DirectoryListener listener : listeners.values() ) 834 { 835 try 836 { 837 service.getEventService().removeListener( listener ); 838 } 839 catch ( Exception e ) 840 { 841 JndiUtils.wrap( e ); 842 } 843 } 844 845 listeners.clear(); 846 } 847 848 849 /** 850 * @see javax.naming.Context#getNameInNamespace() 851 */ 852 public String getNameInNamespace() throws NamingException 853 { 854 return dn.getName(); 855 } 856 857 858 /** 859 * @see javax.naming.Context#getEnvironment() 860 */ 861 public Hashtable<String, Object> getEnvironment() 862 { 863 return env; 864 } 865 866 867 /** 868 * @see javax.naming.Context#addToEnvironment(java.lang.String, 869 * java.lang.Object) 870 */ 871 public Object addToEnvironment( String propName, Object propVal ) throws NamingException 872 { 873 return env.put( propName, propVal ); 874 } 875 876 877 /** 878 * @see javax.naming.Context#removeFromEnvironment(java.lang.String) 879 */ 880 public Object removeFromEnvironment( String propName ) throws NamingException 881 { 882 return env.remove( propName ); 883 } 884 885 886 /** 887 * @see javax.naming.Context#createSubcontext(java.lang.String) 888 */ 889 public Context createSubcontext( String name ) throws NamingException 890 { 891 return createSubcontext( new LdapName( name ) ); 892 } 893 894 895 /** 896 * @see javax.naming.Context#createSubcontext(javax.naming.Name) 897 */ 898 public Context createSubcontext( Name name ) throws NamingException 899 { 900 Dn target = buildTarget( JndiUtils.fromName( name ) ); 901 Entry serverEntry = null; 902 903 try 904 { 905 serverEntry = service.newEntry( target ); 906 } 907 catch ( LdapException le ) 908 { 909 throw new NamingException( le.getMessage() ); 910 } 911 912 try 913 { 914 serverEntry.add( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, JavaLdapSupport.JCONTAINER_ATTR ); 915 } 916 catch ( LdapException le ) 917 { 918 throw new SchemaViolationException( I18n.err( I18n.ERR_491, name ) ); 919 } 920 921 // Now add the CN attribute, which is mandatory 922 Rdn rdn = target.getRdn(); 923 924 if ( rdn != null ) 925 { 926 if ( SchemaConstants.CN_AT_OID.equals( rdn.getNormType() ) ) 927 { 928 serverEntry.put( rdn.getType(), rdn.getValue() ); 929 } 930 else 931 { 932 // No CN in the rdn, this is an error 933 throw new SchemaViolationException( I18n.err( I18n.ERR_491, name ) ); 934 } 935 } 936 else 937 { 938 // No CN in the rdn, this is an error 939 throw new SchemaViolationException( I18n.err( I18n.ERR_491, name ) ); 940 } 941 942 /* 943 * Add the new context to the server which as a side effect adds 944 * operational attributes to the serverEntry refering instance which 945 * can them be used to initialize a new ServerLdapContext. Remember 946 * we need to copy over the controls as well to propagate the complete 947 * environment besides what's in the hashtable for env. 948 */ 949 try 950 { 951 doAddOperation( target, serverEntry ); 952 } 953 catch ( Exception e ) 954 { 955 JndiUtils.wrap( e ); 956 } 957 958 ServerLdapContext ctx = null; 959 960 try 961 { 962 ctx = new ServerLdapContext( service, session.getEffectivePrincipal(), JndiUtils.toName( target ) ); 963 } 964 catch ( Exception e ) 965 { 966 JndiUtils.wrap( e ); 967 } 968 969 return ctx; 970 } 971 972 973 /** 974 * @see javax.naming.Context#destroySubcontext(java.lang.String) 975 */ 976 public void destroySubcontext( String name ) throws NamingException 977 { 978 destroySubcontext( new LdapName( name ) ); 979 } 980 981 982 /** 983 * @see javax.naming.Context#destroySubcontext(javax.naming.Name) 984 */ 985 public void destroySubcontext( Name name ) throws NamingException 986 { 987 Dn target = buildTarget( JndiUtils.fromName( name ) ); 988 989 if ( target.size() == 0 ) 990 { 991 throw new NoPermissionException( I18n.err( I18n.ERR_492 ) ); 992 } 993 994 try 995 { 996 doDeleteOperation( target ); 997 } 998 catch ( Exception e ) 999 { 1000 JndiUtils.wrap( e ); 1001 } 1002 } 1003 1004 1005 /** 1006 * @see javax.naming.Context#bind(java.lang.String, java.lang.Object) 1007 */ 1008 public void bind( String name, Object obj ) throws NamingException 1009 { 1010 bind( new LdapName( name ), obj ); 1011 } 1012 1013 1014 private void injectRdnAttributeValues( Dn target, Entry serverEntry ) 1015 { 1016 // Add all the Rdn attributes and their values to this entry 1017 Rdn rdn = target.getRdn(); 1018 1019 if ( rdn.size() == 1 ) 1020 { 1021 serverEntry.put( rdn.getType(), rdn.getValue() ); 1022 } 1023 else 1024 { 1025 for ( Ava atav : rdn ) 1026 { 1027 serverEntry.put( atav.getType(), atav.getValue() ); 1028 } 1029 } 1030 } 1031 1032 1033 /** 1034 * @see javax.naming.Context#bind(javax.naming.Name, java.lang.Object) 1035 */ 1036 public void bind( Name name, Object obj ) throws NamingException 1037 { 1038 // First, use state factories to do a transformation 1039 DirStateFactory.Result res = DirectoryManager.getStateToBind( obj, name, this, env, null ); 1040 1041 Dn target = buildTarget( JndiUtils.fromName( name ) ); 1042 1043 // let's be sure that the Attributes is case insensitive 1044 Entry outServerEntry = null; 1045 1046 try 1047 { 1048 outServerEntry = ServerEntryUtils.toServerEntry( AttributeUtils.toCaseInsensitive( res 1049 .getAttributes() ), target, service.getSchemaManager() ); 1050 } 1051 catch ( LdapInvalidAttributeTypeException liate ) 1052 { 1053 throw new NamingException( I18n.err( I18n.ERR_495, obj ) ); 1054 } 1055 1056 if ( outServerEntry != null ) 1057 { 1058 try 1059 { 1060 // Remember : a JNDI Bind is a LDAP Add ! Silly, insn't it ? 1061 doAddOperation( target, outServerEntry ); 1062 } 1063 catch ( Exception e ) 1064 { 1065 JndiUtils.wrap( e ); 1066 } 1067 1068 return; 1069 } 1070 1071 if ( obj instanceof Entry ) 1072 { 1073 try 1074 { 1075 doAddOperation( target, ( Entry ) obj ); 1076 } 1077 catch ( Exception e ) 1078 { 1079 JndiUtils.wrap( e ); 1080 } 1081 } 1082 // Check for Referenceable 1083 else if ( obj instanceof Referenceable ) 1084 { 1085 throw new NamingException( I18n.err( I18n.ERR_493 ) ); 1086 } 1087 // Store different formats 1088 else if ( obj instanceof Reference ) 1089 { 1090 // Store as ref and add outAttrs 1091 throw new NamingException( I18n.err( I18n.ERR_494 ) ); 1092 } 1093 else if ( obj instanceof Serializable ) 1094 { 1095 // Serialize and add outAttrs 1096 Entry serverEntry = null; 1097 1098 try 1099 { 1100 serverEntry = service.newEntry( target ); 1101 } 1102 catch ( LdapException le ) 1103 { 1104 throw new NamingException( I18n.err( I18n.ERR_495, obj ) ); 1105 } 1106 1107 // Get target and inject all rdn attributes into entry 1108 injectRdnAttributeValues( target, serverEntry ); 1109 1110 // Serialize object into entry attributes and add it. 1111 try 1112 { 1113 JavaLdapSupport.serialize( serverEntry, obj, service.getSchemaManager() ); 1114 } 1115 catch ( LdapException le ) 1116 { 1117 throw new NamingException( I18n.err( I18n.ERR_495, obj ) ); 1118 } 1119 1120 try 1121 { 1122 doAddOperation( target, serverEntry ); 1123 } 1124 catch ( Exception e ) 1125 { 1126 JndiUtils.wrap( e ); 1127 } 1128 } 1129 else if ( obj instanceof DirContext ) 1130 { 1131 // Grab attributes and merge with outAttrs 1132 Entry serverEntry = null; 1133 1134 try 1135 { 1136 serverEntry = ServerEntryUtils.toServerEntry( ( ( DirContext ) obj ).getAttributes( "" ), 1137 target, service.getSchemaManager() ); 1138 } 1139 catch ( LdapInvalidAttributeTypeException liate ) 1140 { 1141 throw new NamingException( I18n.err( I18n.ERR_495, obj ) ); 1142 } 1143 1144 injectRdnAttributeValues( target, serverEntry ); 1145 1146 try 1147 { 1148 doAddOperation( target, serverEntry ); 1149 } 1150 catch ( Exception e ) 1151 { 1152 JndiUtils.wrap( e ); 1153 } 1154 } 1155 else 1156 { 1157 throw new NamingException( I18n.err( I18n.ERR_495, obj ) ); 1158 } 1159 } 1160 1161 1162 /** 1163 * @see javax.naming.Context#rename(java.lang.String, java.lang.String) 1164 */ 1165 public void rename( String oldName, String newName ) throws NamingException 1166 { 1167 rename( new LdapName( oldName ), new LdapName( newName ) ); 1168 } 1169 1170 1171 /** 1172 * @see javax.naming.Context#rename(javax.naming.Name, javax.naming.Name) 1173 */ 1174 public void rename( Name oldName, Name newName ) throws NamingException 1175 { 1176 Dn oldDn = buildTarget( JndiUtils.fromName( oldName ) ); 1177 Dn newDn = buildTarget( JndiUtils.fromName( newName ) ); 1178 1179 if ( oldDn.size() == 0 ) 1180 { 1181 throw new NoPermissionException( I18n.err( I18n.ERR_312 ) ); 1182 } 1183 1184 // calculate parents 1185 Dn oldParent = oldDn; 1186 1187 oldParent = oldParent.getParent(); 1188 1189 Dn newParent = newDn; 1190 1191 newParent = newParent.getParent(); 1192 1193 Rdn oldRdn = oldDn.getRdn(); 1194 Rdn newRdn = newDn.getRdn(); 1195 boolean delOldRdn = true; 1196 1197 /* 1198 * Attempt to use the java.naming.ldap.deleteRDN environment property 1199 * to get an override for the deleteOldRdn option to modifyRdn. 1200 */ 1201 if ( null != env.get( DELETE_OLD_RDN_PROP ) ) 1202 { 1203 String delOldRdnStr = ( String ) env.get( DELETE_OLD_RDN_PROP ); 1204 delOldRdn = !delOldRdnStr.equalsIgnoreCase( "false" ) && !delOldRdnStr.equalsIgnoreCase( "no" ) 1205 && !delOldRdnStr.equals( "0" ); 1206 } 1207 1208 /* 1209 * We need to determine if this rename operation corresponds to a simple 1210 * Rdn name change or a move operation. If the two names are the same 1211 * except for the Rdn then it is a simple modifyRdn operation. If the 1212 * names differ in size or have a different baseDN then the operation is 1213 * a move operation. Furthermore if the Rdn in the move operation 1214 * changes it is both an Rdn change and a move operation. 1215 */ 1216 if ( oldParent.getNormName().equals( newParent.getNormName() ) ) 1217 { 1218 try 1219 { 1220 doRename( oldDn, newRdn, delOldRdn ); 1221 } 1222 catch ( Exception e ) 1223 { 1224 JndiUtils.wrap( e ); 1225 } 1226 } 1227 else 1228 { 1229 if ( newRdn.getNormName().equals( oldRdn.getNormName() ) ) 1230 { 1231 try 1232 { 1233 doMove( oldDn, newParent ); 1234 } 1235 catch ( Exception e ) 1236 { 1237 JndiUtils.wrap( e ); 1238 } 1239 } 1240 else 1241 { 1242 try 1243 { 1244 doMoveAndRenameOperation( oldDn, newParent, newRdn, delOldRdn ); 1245 } 1246 catch ( Exception e ) 1247 { 1248 JndiUtils.wrap( e ); 1249 } 1250 } 1251 } 1252 } 1253 1254 1255 /** 1256 * @see javax.naming.Context#rebind(java.lang.String, java.lang.Object) 1257 */ 1258 public void rebind( String name, Object obj ) throws NamingException 1259 { 1260 rebind( new LdapName( name ), obj ); 1261 } 1262 1263 1264 /** 1265 * @see javax.naming.Context#rebind(javax.naming.Name, java.lang.Object) 1266 */ 1267 public void rebind( Name name, Object obj ) throws NamingException 1268 { 1269 Dn target = buildTarget( JndiUtils.fromName( name ) ); 1270 OperationManager operationManager = service.getOperationManager(); 1271 1272 try 1273 { 1274 HasEntryOperationContext hasEntryContext = new HasEntryOperationContext( session, target ); 1275 1276 if ( operationManager.hasEntry( hasEntryContext ) ) 1277 { 1278 doDeleteOperation( target ); 1279 } 1280 } 1281 catch ( Exception e ) 1282 { 1283 JndiUtils.wrap( e ); 1284 } 1285 1286 bind( name, obj ); 1287 } 1288 1289 1290 /** 1291 * @see javax.naming.Context#unbind(java.lang.String) 1292 */ 1293 public void unbind( String name ) throws NamingException 1294 { 1295 unbind( new LdapName( name ) ); 1296 } 1297 1298 1299 /** 1300 * @see javax.naming.Context#unbind(javax.naming.Name) 1301 */ 1302 public void unbind( Name name ) throws NamingException 1303 { 1304 try 1305 { 1306 doDeleteOperation( buildTarget( JndiUtils.fromName( name ) ) ); 1307 } 1308 catch ( Exception e ) 1309 { 1310 JndiUtils.wrap( e ); 1311 } 1312 } 1313 1314 1315 /** 1316 * @see javax.naming.Context#lookup(java.lang.String) 1317 */ 1318 public Object lookup( String name ) throws NamingException 1319 { 1320 if ( Strings.isEmpty( name ) ) 1321 { 1322 return lookup( new LdapName( "" ) ); 1323 } 1324 else 1325 { 1326 return lookup( new LdapName( name ) ); 1327 } 1328 } 1329 1330 1331 /** 1332 * @see javax.naming.Context#lookup(javax.naming.Name) 1333 */ 1334 public Object lookup( Name name ) throws NamingException 1335 { 1336 Object obj; 1337 Dn target = buildTarget( JndiUtils.fromName( name ) ); 1338 1339 Entry serverEntry = null; 1340 1341 try 1342 { 1343 if ( name.size() == 0 ) 1344 { 1345 serverEntry = doGetRootDseOperation( target ); 1346 } 1347 else 1348 { 1349 serverEntry = doLookupOperation( target ); 1350 } 1351 } 1352 catch ( Exception e ) 1353 { 1354 JndiUtils.wrap( e ); 1355 } 1356 1357 try 1358 { 1359 obj = DirectoryManager.getObjectInstance( null, name, this, env, 1360 ServerEntryUtils.toBasicAttributes( serverEntry ) ); 1361 } 1362 catch ( Exception e ) 1363 { 1364 String msg = I18n.err( I18n.ERR_497, target ); 1365 NamingException ne = new NamingException( msg ); 1366 ne.setRootCause( e ); 1367 throw ne; 1368 } 1369 1370 if ( obj != null ) 1371 { 1372 return obj; 1373 } 1374 1375 // First lets test and see if the entry is a serialized java object 1376 if ( serverEntry.get( JavaLdapSupport.JCLASSNAME_ATTR ) != null ) 1377 { 1378 // Give back serialized object and not a context 1379 return JavaLdapSupport.deserialize( serverEntry ); 1380 } 1381 1382 // Initialize and return a context since the entry is not a java object 1383 ServerLdapContext ctx = null; 1384 1385 try 1386 { 1387 ctx = new ServerLdapContext( service, session.getEffectivePrincipal(), JndiUtils.toName( target ) ); 1388 } 1389 catch ( Exception e ) 1390 { 1391 JndiUtils.wrap( e ); 1392 } 1393 1394 return ctx; 1395 } 1396 1397 1398 /** 1399 * @see javax.naming.Context#lookupLink(java.lang.String) 1400 */ 1401 public Object lookupLink( String name ) throws NamingException 1402 { 1403 throw new UnsupportedOperationException(); 1404 } 1405 1406 1407 /** 1408 * @see javax.naming.Context#lookupLink(javax.naming.Name) 1409 */ 1410 public Object lookupLink( Name name ) throws NamingException 1411 { 1412 throw new UnsupportedOperationException(); 1413 } 1414 1415 1416 /** 1417 * Non-federated implementation presuming the name argument is not a 1418 * composite name spanning multiple namespaces but a compound name in 1419 * the same LDAP namespace. Hence the parser returned is always the 1420 * same as calling this method with the empty String. 1421 * 1422 * @see javax.naming.Context#getNameParser(java.lang.String) 1423 */ 1424 public NameParser getNameParser( String name ) throws NamingException 1425 { 1426 return new NameParser() 1427 { 1428 public Name parse( String name ) throws NamingException 1429 { 1430 try 1431 { 1432 return JndiUtils.toName( new Dn( name ) ); 1433 } 1434 catch ( LdapInvalidDnException lide ) 1435 { 1436 throw new InvalidNameException( lide.getMessage() ); 1437 } 1438 } 1439 }; 1440 } 1441 1442 1443 /** 1444 * Non-federated implementation presuming the name argument is not a 1445 * composite name spanning multiple namespaces but a compound name in 1446 * the same LDAP namespace. Hence the parser returned is always the 1447 * same as calling this method with the empty String Name. 1448 * 1449 * @see javax.naming.Context#getNameParser(javax.naming.Name) 1450 */ 1451 public NameParser getNameParser( final Name name ) throws NamingException 1452 { 1453 return new NameParser() 1454 { 1455 public Name parse( String n ) throws NamingException 1456 { 1457 try 1458 { 1459 return JndiUtils.toName( new Dn( name.toString() ) ); 1460 } 1461 catch ( LdapInvalidDnException lide ) 1462 { 1463 throw new InvalidNameException( lide.getMessage() ); 1464 } 1465 } 1466 }; 1467 } 1468 1469 1470 /** 1471 * @see javax.naming.Context#list(java.lang.String) 1472 */ 1473 @SuppressWarnings(value = 1474 { "unchecked" }) 1475 public NamingEnumeration list( String name ) throws NamingException 1476 { 1477 return list( new LdapName( name ) ); 1478 } 1479 1480 1481 /** 1482 * @see javax.naming.Context#list(javax.naming.Name) 1483 */ 1484 @SuppressWarnings(value = 1485 { "unchecked" }) 1486 public NamingEnumeration list( Name name ) throws NamingException 1487 { 1488 try 1489 { 1490 return new NamingEnumerationAdapter( doListOperation( buildTarget( JndiUtils.fromName( name ) ) ) ); 1491 } 1492 catch ( Exception e ) 1493 { 1494 JndiUtils.wrap( e ); 1495 return null; // shut up compiler 1496 } 1497 } 1498 1499 1500 /** 1501 * @see javax.naming.Context#listBindings(java.lang.String) 1502 */ 1503 @SuppressWarnings(value = 1504 { "unchecked" }) 1505 public NamingEnumeration listBindings( String name ) throws NamingException 1506 { 1507 return listBindings( new LdapName( name ) ); 1508 } 1509 1510 1511 /** 1512 * @see javax.naming.Context#listBindings(javax.naming.Name) 1513 */ 1514 @SuppressWarnings(value = 1515 { "unchecked" }) 1516 public NamingEnumeration listBindings( Name name ) throws NamingException 1517 { 1518 // Conduct a special one level search at base for all objects 1519 Dn base = buildTarget( JndiUtils.fromName( name ) ); 1520 PresenceNode filter = new PresenceNode( objectClassAT ); 1521 SearchControls ctls = new SearchControls(); 1522 ctls.setSearchScope( SearchControls.ONELEVEL_SCOPE ); 1523 AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() ); 1524 try 1525 { 1526 return new NamingEnumerationAdapter( doSearchOperation( base, aliasDerefMode, filter, ctls ) ); 1527 } 1528 catch ( Exception e ) 1529 { 1530 JndiUtils.wrap( e ); 1531 return null; // shutup compiler 1532 } 1533 } 1534 1535 1536 /** 1537 * @see javax.naming.Context#composeName(java.lang.String, java.lang.String) 1538 */ 1539 public String composeName( String name, String prefix ) throws NamingException 1540 { 1541 return composeName( new LdapName( name ), new LdapName( prefix ) ).toString(); 1542 } 1543 1544 1545 /** 1546 * @see javax.naming.Context#composeName(javax.naming.Name, 1547 * javax.naming.Name) 1548 */ 1549 public Name composeName( Name name, Name prefix ) throws NamingException 1550 { 1551 // No prefix reduces to name, or the name relative to this context 1552 if ( ( prefix == null ) || ( prefix.size() == 0 ) ) 1553 { 1554 return name; 1555 } 1556 1557 /* 1558 * Example: This context is ou=people and say name is the relative 1559 * name of uid=jwalker and the prefix is dc=domain. Then we must 1560 * compose the name relative to prefix which would be: 1561 * 1562 * uid=jwalker,ou=people,dc=domain. 1563 * 1564 * The following general algorithm generates the right name: 1565 * 1). Find the Dn for name and walk it from the head to tail 1566 * trying to match for the head of prefix. 1567 * 2). Remove name components from the Dn until a match for the 1568 * head of the prefix is found. 1569 * 3). Return the remainder of the fqn or Dn after chewing off some 1570 */ 1571 1572 // 1). Find the Dn for name and walk it from the head to tail 1573 Dn fqn = buildTarget( JndiUtils.fromName( name ) ); 1574 1575 try 1576 { 1577 return JndiUtils.toName( JndiUtils.fromName( prefix ).add( fqn ) ); 1578 } 1579 catch ( LdapInvalidDnException lide ) 1580 { 1581 throw new InvalidNameException( lide.getMessage() ); 1582 } 1583 } 1584 1585 1586 // ------------------------------------------------------------------------ 1587 // EventContext implementations 1588 // ------------------------------------------------------------------------ 1589 1590 public void addNamingListener( Name name, int scope, NamingListener namingListener ) throws NamingException 1591 { 1592 ExprNode filter = new PresenceNode( objectClassAT ); 1593 1594 try 1595 { 1596 DirectoryListener listener = new EventListenerAdapter( ( ServerLdapContext ) this, namingListener ); 1597 NotificationCriteria criteria = new NotificationCriteria( schemaManager ); 1598 criteria.setFilter( filter ); 1599 criteria.setScope( SearchScope.getSearchScope( scope ) ); 1600 criteria.setAliasDerefMode( AliasDerefMode.getEnum( env ) ); 1601 criteria.setBase( buildTarget( JndiUtils.fromName( name ) ) ); 1602 1603 service.getEventService().addListener( listener, criteria ); 1604 listeners.put( namingListener, listener ); 1605 } 1606 catch ( Exception e ) 1607 { 1608 JndiUtils.wrap( e ); 1609 } 1610 } 1611 1612 1613 public void addNamingListener( String name, int scope, NamingListener namingListener ) throws NamingException 1614 { 1615 addNamingListener( new LdapName( name ), scope, namingListener ); 1616 } 1617 1618 1619 public void removeNamingListener( NamingListener namingListener ) throws NamingException 1620 { 1621 try 1622 { 1623 DirectoryListener listener = listeners.remove( namingListener ); 1624 1625 if ( listener != null ) 1626 { 1627 service.getEventService().removeListener( listener ); 1628 } 1629 } 1630 catch ( Exception e ) 1631 { 1632 JndiUtils.wrap( e ); 1633 } 1634 } 1635 1636 1637 public boolean targetMustExist() throws NamingException 1638 { 1639 return false; 1640 } 1641 1642 1643 /** 1644 * Allows subclasses to register and unregister listeners. 1645 * 1646 * @return the set of listeners used for tracking registered name listeners. 1647 */ 1648 protected Map<NamingListener, DirectoryListener> getListeners() 1649 { 1650 return listeners; 1651 } 1652 1653 1654 // ------------------------------------------------------------------------ 1655 // Utility Methods to Reduce Code 1656 // ------------------------------------------------------------------------ 1657 1658 /** 1659 * Clones this context's Dn and adds the components of the name relative to 1660 * this context to the left hand side of this context's cloned Dn. 1661 * 1662 * @param relativeName a name relative to this context. 1663 * @return the name of the target 1664 * @throws InvalidNameException if relativeName is not a valid name in 1665 * the LDAP namespace. 1666 */ 1667 Dn buildTarget( Dn relativeName ) throws NamingException 1668 { 1669 Dn target = dn; 1670 1671 // Add to left hand side of cloned Dn the relative name arg 1672 try 1673 { 1674 target = target.add( relativeName ); 1675 1676 if ( !target.isSchemaAware() ) 1677 { 1678 target = new Dn( schemaManager, target ); 1679 } 1680 } 1681 catch ( LdapInvalidDnException lide ) 1682 { 1683 throw new InvalidNameException( lide.getMessage() ); 1684 } 1685 1686 return target; 1687 } 1688}