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 }