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