View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.core.jndi;
21  
22  
23  import org.apache.directory.server.core.CoreSession;
24  import org.apache.directory.server.core.DirectoryService;
25  import org.apache.directory.server.core.authn.LdapPrincipal;
26  import org.apache.directory.server.core.entry.ServerEntry;
27  import org.apache.directory.server.core.entry.ServerEntryUtils;
28  import org.apache.directory.server.core.event.DirectoryListener;
29  import org.apache.directory.server.core.event.NotificationCriteria;
30  import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
31  import org.apache.directory.shared.ldap.constants.SchemaConstants;
32  import org.apache.directory.shared.ldap.entry.EntryAttribute;
33  import org.apache.directory.shared.ldap.entry.Modification;
34  import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
35  import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
36  import org.apache.directory.shared.ldap.filter.AndNode;
37  import org.apache.directory.shared.ldap.filter.BranchNode;
38  import org.apache.directory.shared.ldap.filter.EqualityNode;
39  import org.apache.directory.shared.ldap.filter.ExprNode;
40  import org.apache.directory.shared.ldap.filter.FilterParser;
41  import org.apache.directory.shared.ldap.filter.PresenceNode;
42  import org.apache.directory.shared.ldap.filter.SearchScope;
43  import org.apache.directory.shared.ldap.filter.SimpleNode;
44  import org.apache.directory.shared.ldap.message.AliasDerefMode;
45  import org.apache.directory.shared.ldap.name.AttributeTypeAndValue;
46  import org.apache.directory.shared.ldap.name.LdapDN;
47  import org.apache.directory.shared.ldap.name.Rdn;
48  import org.apache.directory.shared.ldap.util.AttributeUtils;
49  import org.apache.directory.shared.ldap.util.StringTools;
50  
51  import javax.naming.Name;
52  import javax.naming.NamingEnumeration;
53  import javax.naming.NamingException;
54  import javax.naming.Reference;
55  import javax.naming.Referenceable;
56  import javax.naming.directory.Attribute;
57  import javax.naming.directory.Attributes;
58  import javax.naming.directory.DirContext;
59  import javax.naming.directory.InvalidSearchFilterException;
60  import javax.naming.directory.ModificationItem;
61  import javax.naming.directory.SearchControls;
62  import javax.naming.directory.SearchResult;
63  import javax.naming.event.EventDirContext;
64  import javax.naming.event.NamingListener;
65  import javax.naming.spi.DirStateFactory;
66  import javax.naming.spi.DirectoryManager;
67  import java.io.Serializable;
68  import java.text.ParseException;
69  import java.util.ArrayList;
70  import java.util.Hashtable;
71  import java.util.Iterator;
72  import java.util.List;
73  
74  
75  /**
76   * The DirContext implementation for the Server Side JNDI LDAP provider.
77   *
78   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
79   * @version $Rev: 691179 $
80   */
81  public abstract class ServerDirContext extends ServerContext implements EventDirContext
82  {
83      // ------------------------------------------------------------------------
84      // Constructors
85      // ------------------------------------------------------------------------
86  
87      /**
88       * Creates a new ServerDirContext by reading the PROVIDER_URL to resolve the
89       * distinguished name for this context.
90       *
91       * @param service the parent service that manages this context
92       * @param env the environment used for this context
93       * @throws NamingException if something goes wrong
94       */
95      public ServerDirContext( DirectoryService service, Hashtable<String, Object> env ) throws Exception
96      {
97          super( service, env );
98      }
99  
100 
101     /**
102      * Creates a new ServerDirContext with a distinguished name which is used to
103      * set the PROVIDER_URL to the distinguished name for this context.
104      *
105      * @param principal the principal which is propagated
106      * @param dn the distinguished name of this context
107      */
108     public ServerDirContext( DirectoryService service, LdapPrincipal principal, Name dn ) throws Exception
109     {
110         super( service, principal, dn );
111     }
112 
113 
114     // ------------------------------------------------------------------------
115     // DirContext Implementations
116     // ------------------------------------------------------------------------
117 
118     
119     public ServerDirContext( DirectoryService service, CoreSession session, LdapDN bindDn ) throws Exception
120     {
121         super( service, session, bindDn );
122     }
123 
124 
125     /**
126      * @see javax.naming.directory.DirContext#getAttributes(java.lang.String)
127      */
128     public Attributes getAttributes( String name ) throws NamingException
129     {
130         return getAttributes( new LdapDN( name ) );
131     }
132 
133 
134     /**
135      * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name)
136      */
137     public Attributes getAttributes( Name name ) throws NamingException
138     {
139         Attributes attrs = null;
140         
141         try
142         {
143             attrs = ServerEntryUtils.toBasicAttributes( doLookupOperation( buildTarget( name ) ) );
144         }
145         catch ( Exception e )
146         {
147             JndiUtils.wrap( e );
148         }
149         
150         return attrs;
151     }
152 
153 
154     /**
155      * @see javax.naming.directory.DirContext#getAttributes(java.lang.String,
156      *      java.lang.String[])
157      */
158     public Attributes getAttributes( String name, String[] attrIds ) throws NamingException
159     {
160         return getAttributes( new LdapDN( name ), attrIds );
161     }
162 
163 
164     /**
165      * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name,
166      *      java.lang.String[])
167      */
168     public Attributes getAttributes( Name name, String[] attrIds ) throws NamingException
169     {
170         Attributes attrs = null;
171         try
172         {
173             attrs = ServerEntryUtils.toBasicAttributes( doLookupOperation( buildTarget( name ), attrIds ) );
174         }
175         catch ( Exception e )
176         {
177             JndiUtils.wrap( e );
178         }
179         
180         return attrs;
181     }
182 
183 
184     /**
185      * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
186      *      int, javax.naming.directory.Attributes)
187      */
188     public void modifyAttributes( String name, int modOp, Attributes attrs ) throws NamingException
189     {
190         modifyAttributes( new LdapDN( name ), modOp, AttributeUtils.toCaseInsensitive( attrs ) );
191     }
192 
193 
194     /**
195      * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
196      *      int, javax.naming.directory.Attributes)
197      */
198     public void modifyAttributes( Name name, int modOp, Attributes attrs ) throws NamingException
199     {
200         List<ModificationItem> modItems = null;
201 
202         if ( attrs != null )
203         {
204             modItems = new ArrayList<ModificationItem>( attrs.size() );
205             NamingEnumeration<? extends Attribute> e = ( NamingEnumeration<? extends Attribute> ) attrs.getAll();
206 
207             while ( e.hasMore() )
208             {
209                 modItems.add( new ModificationItem( modOp, e.next() ) );
210             }
211         }
212 
213         List<Modification> newMods = ServerEntryUtils.convertToServerModification( 
214             modItems, 
215             getDirectoryService().getRegistries().getAttributeTypeRegistry() );
216 
217         try
218         {
219             if ( name instanceof LdapDN )
220             {
221                 doModifyOperation( buildTarget( name ), newMods );
222             }
223             else
224             {
225                 doModifyOperation( buildTarget( new LdapDN( name ) ), newMods );
226             }
227         }
228         catch( Exception e )
229         {
230             JndiUtils.wrap( e );
231         }
232     }
233 
234 
235     /**
236      * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
237      *      javax.naming.directory.ModificationItem[])
238      */
239     public void modifyAttributes( String name, ModificationItem[] mods ) throws NamingException
240     {
241         modifyAttributes( new LdapDN( name ), mods );
242     }
243 
244 
245     /**
246      * @see javax.naming.directory.DirContext#modifyAttributes(
247      * javax.naming.Name, javax.naming.directory.ModificationItem[])
248      */
249     public void modifyAttributes( Name name, ModificationItem[] mods ) throws NamingException
250     {
251         List<Modification> newMods = ServerEntryUtils
252             .toServerModification( mods, getDirectoryService().getRegistries().getAttributeTypeRegistry() );
253         try
254         {
255             doModifyOperation( buildTarget( new LdapDN( name ) ), newMods );
256         }
257         catch ( Exception e )
258         {
259             JndiUtils.wrap( e );
260         }
261     }
262 
263 
264     /**
265      * @see javax.naming.directory.DirContext#modifyAttributes(
266      * javax.naming.Name, javax.naming.directory.ModificationItem[])
267      */
268     public void modifyAttributes( Name name, List<ModificationItem> mods ) throws NamingException
269     {
270         List<Modification> newMods = ServerEntryUtils
271             .convertToServerModification( mods, 
272                 getDirectoryService().getRegistries().getAttributeTypeRegistry() );
273         try
274         {
275             doModifyOperation( buildTarget( new LdapDN( name ) ), newMods );
276         }
277         catch ( Exception e )
278         {
279             JndiUtils.wrap( e );
280         }
281     }
282 
283 
284     /**
285      * @see javax.naming.directory.DirContext#bind(java.lang.String,
286      *      java.lang.Object, javax.naming.directory.Attributes)
287      */
288     public void bind( String name, Object obj, Attributes attrs ) throws NamingException
289     {
290         bind( new LdapDN( name ), obj, AttributeUtils.toCaseInsensitive( attrs ) );
291     }
292 
293 
294     /**
295      * @see javax.naming.directory.DirContext#bind(javax.naming.Name,
296      *      java.lang.Object, javax.naming.directory.Attributes)
297      */
298     public void bind( Name name, Object obj, Attributes attrs ) throws NamingException
299     {
300         if ( ( null == obj ) && ( null == attrs ) )
301         {
302             throw new NamingException( "Both obj and attrs args are null. "
303                 + "At least one of these parameters must not be null." );
304         }
305 
306         // A null attrs defaults this to the Context.bind() operation
307         if ( null == attrs )
308         {
309             super.bind( name, obj );
310             return;
311         }
312 
313         LdapDN target = buildTarget( name );
314 
315         ServerEntry serverEntry = ServerEntryUtils.toServerEntry( AttributeUtils.toCaseInsensitive( attrs ), target,
316             getDirectoryService().getRegistries() );
317 
318         // No object binding so we just add the attributes
319         if ( null == obj )
320         {
321             ServerEntry clone = ( ServerEntry ) serverEntry.clone();
322             try
323             {
324                 doAddOperation( target, clone );
325             }
326             catch ( Exception e )
327             {
328                 JndiUtils.wrap( e );
329             }
330             return;
331         }
332 
333         // First, use state factories to do a transformation
334         DirStateFactory.Result res = DirectoryManager.getStateToBind( obj, name, this, getEnvironment(), attrs );
335         ServerEntry outServerEntry = ServerEntryUtils.toServerEntry( 
336             res.getAttributes(), target, getDirectoryService().getRegistries() );
337 
338         if ( outServerEntry != serverEntry )
339         {
340             ServerEntry clone = ( ServerEntry ) serverEntry.clone();
341 
342             if ( ( outServerEntry != null ) && ( outServerEntry.size() > 0 ) )
343             {
344                 for ( EntryAttribute attribute : outServerEntry )
345                 {
346                     clone.put( attribute );
347                 }
348             }
349 
350             try
351             {
352                 // setup the op context
353                 doAddOperation( target, clone );
354             }
355             catch ( Exception e )
356             {
357                 JndiUtils.wrap( e );
358             }
359             return;
360         }
361 
362         // Check for Referenceable
363         if ( obj instanceof Referenceable )
364         {
365             throw new NamingException( "Do not know how to store Referenceables yet!" );
366         }
367 
368         // Store different formats
369         if ( obj instanceof Reference )
370         {
371             // Store as ref and add outAttrs
372             throw new NamingException( "Do not know how to store References yet!" );
373         }
374         else if ( obj instanceof Serializable )
375         {
376             // Serialize and add outAttrs
377             ServerEntry clone = ( ServerEntry ) serverEntry.clone();
378 
379             if ( outServerEntry != null && outServerEntry.size() > 0 )
380             {
381                 for ( EntryAttribute attribute : outServerEntry )
382                 {
383                     clone.put( attribute );
384                 }
385             }
386 
387             // Serialize object into entry attributes and add it.
388             JavaLdapSupport.serialize( serverEntry, obj, getDirectoryService().getRegistries() );
389             try
390             {
391                 // setup the op context
392                 doAddOperation( target, clone );
393             }
394             catch ( Exception e )
395             {
396                 JndiUtils.wrap( e );
397             }
398         }
399         else if ( obj instanceof DirContext )
400         {
401             // Grab attributes and merge with outAttrs
402             ServerEntry entry = ServerEntryUtils.toServerEntry( ( ( DirContext ) obj ).getAttributes( "" ), target,
403                 getDirectoryService().getRegistries() );
404 
405             if ( ( outServerEntry != null ) && ( outServerEntry.size() > 0 ) )
406             {
407                 for ( EntryAttribute attribute : outServerEntry )
408                 {
409                     entry.put( attribute );
410                 }
411             }
412 
413             try
414             {
415                 // setup the op context
416                 doAddOperation( target, entry );
417             }
418             catch ( Exception e )
419             {
420                 JndiUtils.wrap( e );
421             }
422         }
423         else
424         {
425             throw new NamingException( "Can't find a way to bind: " + obj );
426         }
427     }
428 
429 
430     /**
431      * @see javax.naming.directory.DirContext#rebind(java.lang.String,
432      *      java.lang.Object, javax.naming.directory.Attributes)
433      */
434     public void rebind( String name, Object obj, Attributes attrs ) throws NamingException
435     {
436         rebind( new LdapDN( name ), obj, AttributeUtils.toCaseInsensitive( attrs ) );
437     }
438 
439 
440     /**
441      * @see javax.naming.directory.DirContext#rebind(javax.naming.Name,
442      *      java.lang.Object, javax.naming.directory.Attributes)
443      */
444     public void rebind( Name name, Object obj, Attributes attrs ) throws NamingException
445     {
446         LdapDN target = buildTarget( name );
447 
448         try
449         {
450             if ( getDirectoryService().getOperationManager().hasEntry( new EntryOperationContext( getSession(), target ) ) )
451             {
452                 doDeleteOperation( target );
453             }
454         }
455         catch ( Exception e )
456         {
457             JndiUtils.wrap( e );
458         }
459 
460         bind( name, obj, AttributeUtils.toCaseInsensitive( attrs ) );
461     }
462 
463 
464     /**
465      * @see javax.naming.directory.DirContext#createSubcontext(java.lang.String,
466      *      javax.naming.directory.Attributes)
467      */
468     public DirContext createSubcontext( String name, Attributes attrs ) throws NamingException
469     {
470         return createSubcontext( new LdapDN( name ), AttributeUtils.toCaseInsensitive( attrs ) );
471     }
472 
473 
474     /**
475      * @see javax.naming.directory.DirContext#createSubcontext(
476      * javax.naming.Name, javax.naming.directory.Attributes)
477      */
478     public DirContext createSubcontext( Name name, Attributes attrs ) throws NamingException
479     {
480         if ( null == attrs )
481         {
482             return ( DirContext ) super.createSubcontext( name );
483         }
484 
485         LdapDN target = buildTarget( name );
486         Rdn rdn = target.getRdn( target.size() - 1 );
487 
488         attrs = AttributeUtils.toCaseInsensitive( attrs );
489         Attributes attributes = ( Attributes ) attrs.clone();
490 
491         if ( rdn.size() == 1 )
492         {
493             String rdnAttribute = rdn.getUpType();
494             String rdnValue = ( String ) rdn.getValue();
495 
496             // Add the Rdn attribute
497             boolean doRdnPut = attributes.get( rdnAttribute ) == null;
498             doRdnPut = doRdnPut || attributes.get( rdnAttribute ).size() == 0;
499 
500             // TODO Fix DIRSERVER-832
501             doRdnPut = doRdnPut || !attributes.get( rdnAttribute ).contains( rdnValue );
502 
503             if ( doRdnPut )
504             {
505                 attributes.put( rdnAttribute, rdnValue );
506             }
507         }
508         else
509         {
510             for ( Iterator<AttributeTypeAndValue> ii = rdn.iterator(); ii.hasNext(); /**/)
511             {
512                 AttributeTypeAndValue atav = ii.next();
513 
514                 // Add the Rdn attribute
515                 boolean doRdnPut = attributes.get( atav.getNormType() ) == null;
516                 doRdnPut = doRdnPut || attributes.get( atav.getNormType() ).size() == 0;
517 
518                 // TODO Fix DIRSERVER-832
519                 doRdnPut = doRdnPut || !attributes.get( atav.getNormType() ).contains( atav.getNormValue() );
520 
521                 if ( doRdnPut )
522                 {
523                     attributes.put( atav.getNormType(), atav.getNormValue() );
524                 }
525             }
526         }
527 
528         // Add the new context to the server which as a side effect adds
529         try
530         {
531             ServerEntry serverEntry = ServerEntryUtils.toServerEntry( attributes, 
532                 target, getDirectoryService().getRegistries() );
533             doAddOperation( target, serverEntry );
534         }
535         catch ( Exception e )
536         {
537             JndiUtils.wrap( e );
538         }
539 
540         // Initialize the new context
541         ServerLdapContext ctx = null;
542         
543         try
544         {
545             ctx = new ServerLdapContext( getService(), getSession().getEffectivePrincipal(), target );
546         }
547         catch ( Exception e )
548         {
549             JndiUtils.wrap( e );
550         }
551         
552         
553         return ctx;
554     }
555 
556 
557     /**
558      * Presently unsupported operation!
559      */
560     public DirContext getSchema( Name name ) throws NamingException
561     {
562         throw new UnsupportedOperationException();
563     }
564 
565 
566     /**
567      * Presently unsupported operation!
568      */
569     public DirContext getSchema( String name ) throws NamingException
570     {
571         throw new UnsupportedOperationException();
572     }
573 
574 
575     /**
576      * Presently unsupported operation!
577      */
578     public DirContext getSchemaClassDefinition( Name name ) throws NamingException
579     {
580         throw new UnsupportedOperationException();
581     }
582 
583 
584     /**
585      * Presently unsupported operation!
586      */
587     public DirContext getSchemaClassDefinition( String name ) throws NamingException
588     {
589         throw new UnsupportedOperationException();
590     }
591 
592 
593     // ------------------------------------------------------------------------
594     // Search Operation Implementations
595     // ------------------------------------------------------------------------
596 
597     /**
598      * @see javax.naming.directory.DirContext#search(java.lang.String,
599      *      javax.naming.directory.Attributes)
600      */
601     public NamingEnumeration<SearchResult> search( String name, Attributes matchingAttributes ) throws NamingException
602     {
603         return search( new LdapDN( name ), matchingAttributes, null );
604     }
605 
606 
607     /**
608      * @see javax.naming.directory.DirContext#search(javax.naming.Name,
609      *      javax.naming.directory.Attributes)
610      */
611     public NamingEnumeration<SearchResult> search( Name name, Attributes matchingAttributes ) throws NamingException
612     {
613         return search( name, AttributeUtils.toCaseInsensitive( matchingAttributes ), null );
614     }
615 
616 
617     /**
618      * @see javax.naming.directory.DirContext#search(java.lang.String,
619      *      javax.naming.directory.Attributes, java.lang.String[])
620      */
621     public NamingEnumeration<SearchResult> search( String name, Attributes matchingAttributes,
622         String[] attributesToReturn ) throws NamingException
623     {
624         return search( new LdapDN( name ), AttributeUtils.toCaseInsensitive( matchingAttributes ), attributesToReturn );
625     }
626 
627 
628     /**
629      * @see javax.naming.directory.DirContext#search(javax.naming.Name,
630      *      javax.naming.directory.Attributes, java.lang.String[])
631      */
632     public NamingEnumeration<SearchResult> search( Name name, Attributes matchingAttributes, String[] attributesToReturn )
633         throws NamingException
634     {
635         SearchControls ctls = new SearchControls();
636         LdapDN target = buildTarget( name );
637 
638         // If we need to return specific attributes add em to the SearchControls
639         if ( null != attributesToReturn )
640         {
641             ctls.setReturningAttributes( attributesToReturn );
642         }
643 
644         // If matchingAttributes is null/empty use a match for everything filter
645         matchingAttributes = AttributeUtils.toCaseInsensitive( matchingAttributes );
646 
647         if ( ( null == matchingAttributes ) || ( matchingAttributes.size() <= 0 ) )
648         {
649             PresenceNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT );
650             AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
651             try
652             {
653                 return new NamingEnumerationAdapter ( 
654                     doSearchOperation( target, aliasDerefMode, filter, ctls ) );
655             }
656             catch ( Exception e )
657             {
658                 JndiUtils.wrap( e );
659             }
660         }
661 
662         // Handle simple filter expressions without multiple terms
663         if ( matchingAttributes.size() == 1 )
664         {
665             NamingEnumeration<? extends Attribute> list = matchingAttributes.getAll();
666             Attribute attr = list.next();
667             list.close();
668 
669             if ( attr.size() == 1 )
670             {
671                 Object value = attr.get();
672                 SimpleNode<?> node;
673 
674                 if ( value instanceof byte[] )
675                 {
676                     node = new EqualityNode<byte[]>( attr.getID(), new ClientBinaryValue( ( byte[] ) value ) );
677                 }
678                 else
679                 {
680                     node = new EqualityNode<String>( attr.getID(), new ClientStringValue( ( String ) value ) );
681                 }
682 
683                 AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
684                 try
685                 {
686                     return new NamingEnumerationAdapter ( 
687                         doSearchOperation( target, aliasDerefMode, node, ctls ) );
688                 }
689                 catch ( Exception e )
690                 {
691                     JndiUtils.wrap( e );
692                     return null; // shut compiler up
693                 }
694             }
695         }
696 
697         /*
698          * Go through the set of attributes using each attribute value pair as 
699          * an attribute value assertion within one big AND filter expression.
700          */
701         Attribute attr;
702         SimpleNode node;
703         BranchNode filter = new AndNode();
704         NamingEnumeration<? extends Attribute> list = matchingAttributes.getAll();
705 
706         // Loop through each attribute value pair
707         while ( list.hasMore() )
708         {
709             attr = list.next();
710 
711             /*
712              * According to JNDI if an attribute in the matchingAttributes
713              * list does not have any values then we match for just the presence
714              * of the attribute in the entry
715              */
716             if ( attr.size() == 0 )
717             {
718                 filter.addNode( new PresenceNode( attr.getID() ) );
719                 continue;
720             }
721 
722             /*
723              * With 1 or more value we build a set of simple nodes and add them
724              * to the AND node - each attribute value pair is a simple AVA node.
725              */
726             for ( int ii = 0; ii < attr.size(); ii++ )
727             {
728                 Object val = attr.get( ii );
729 
730                 // Add simpel AVA node if its value is a String 
731                 if ( val instanceof String )
732                 {
733                     node = new EqualityNode<String>( attr.getID(), new ClientStringValue( ( String ) val ) );
734                     filter.addNode( node );
735                 }
736             }
737         }
738 
739         AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
740         try
741         {
742             return new NamingEnumerationAdapter( doSearchOperation( target, aliasDerefMode, filter, ctls ) );
743         }
744         catch ( Exception e )
745         {
746             JndiUtils.wrap( e );
747             return null; // shut compiler up
748         }
749     }
750 
751 
752     /**
753      * @see javax.naming.directory.DirContext#search(java.lang.String,
754      *      java.lang.String, javax.naming.directory.SearchControls)
755      */
756     public NamingEnumeration<SearchResult> search( String name, String filter, SearchControls cons )
757         throws NamingException
758     {
759         return search( new LdapDN( name ), filter, cons );
760     }
761 
762 
763     /**
764      * A search overload that is used for optimizing search handling in the
765      * LDAP protocol provider which deals with an ExprNode instance rather than
766      * a String for the filter.
767      *
768      * @param name the relative name of the object serving as the search base
769      * @param filter the search filter as an expression tree
770      * @param cons the search controls to use
771      * @return an enumeration over the SearchResults
772      * @throws NamingException if there are problems performing the search
773      */
774     public NamingEnumeration<SearchResult> search( Name name, ExprNode filter, SearchControls cons )
775         throws NamingException
776     {
777         LdapDN target = buildTarget( name );
778         AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
779         try
780         {
781             return new NamingEnumerationAdapter( doSearchOperation( target, aliasDerefMode, filter, cons ) );
782         }
783         catch ( Exception e )
784         {
785             JndiUtils.wrap( e );
786             return null; // shut compiler up
787         }
788     }
789 
790 
791     /**
792      * @see javax.naming.directory.DirContext#search(javax.naming.Name,
793      *      java.lang.String, javax.naming.directory.SearchControls)
794      */
795     public NamingEnumeration<SearchResult> search( Name name, String filter, SearchControls cons )
796         throws NamingException
797     {
798         ExprNode filterNode;
799         LdapDN target = buildTarget( name );
800 
801         try
802         {
803             filterNode = FilterParser.parse( filter );
804         }
805         catch ( ParseException pe )
806         {
807             InvalidSearchFilterException isfe = new InvalidSearchFilterException(
808                 "Encountered parse exception while parsing the filter: '" + filter + "'" );
809             isfe.setRootCause( pe );
810             throw isfe;
811         }
812 
813         AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
814         try
815         {
816             return new NamingEnumerationAdapter( doSearchOperation( target, aliasDerefMode, filterNode, cons ) );
817         }
818         catch ( Exception e )
819         {
820             JndiUtils.wrap( e );
821             return null; // shut compiler up
822         }
823     }
824 
825 
826     /**
827      * @see javax.naming.directory.DirContext#search(java.lang.String,
828      *      java.lang.String, java.lang.Object[],
829      *      javax.naming.directory.SearchControls)
830      */
831     public NamingEnumeration<SearchResult> search( String name, String filterExpr, Object[] filterArgs,
832         SearchControls cons ) throws NamingException
833     {
834         return search( new LdapDN( name ), filterExpr, filterArgs, cons );
835     }
836 
837 
838     /**
839      * @see javax.naming.directory.DirContext#search(javax.naming.Name,
840      *      java.lang.String, java.lang.Object[],
841      *      javax.naming.directory.SearchControls)
842      */
843     public NamingEnumeration<SearchResult> search( Name name, String filterExpr, Object[] filterArgs,
844         SearchControls cons ) throws NamingException
845     {
846         int start;
847         int index;
848 
849         StringBuffer buf = new StringBuffer( filterExpr );
850 
851         // Scan until we hit the end of the string buffer 
852         for ( int ii = 0; ii < buf.length(); ii++ )
853         {
854             try
855             {
856                 // Advance until we hit the start of a variable
857                 while ( ii < buf.length() && '{' != buf.charAt( ii ) )
858                 {
859                     ii++;
860                 }
861 
862                 // Record start of variable at '{'
863                 start = ii;
864 
865                 // Advance to the end of a variable at '}'
866                 while ( '}' != buf.charAt( ii ) )
867                 {
868                     ii++;
869                 }
870             }
871             catch ( IndexOutOfBoundsException e )
872             {
873                 // End of filter so done.
874                 break;
875             }
876 
877             // Parse index
878             index = Integer.parseInt( buf.substring( start + 1, ii ) );
879 
880             if ( filterArgs[index] instanceof String )
881             {
882                 /*
883                  * Replace the '{ i }' with the string representation of the value
884                  * held in the filterArgs array at index index.
885                  */
886                 buf.replace( start, ii + 1, ( String ) filterArgs[index] );
887             }
888             else if ( filterArgs[index] instanceof byte[] )
889             {
890                 String hexstr = "#" + StringTools.toHexString( ( byte[] ) filterArgs[index] );
891                 buf.replace( start, ii + 1, hexstr );
892             }
893             else
894             {
895                 /*
896                  * Replace the '{ i }' with the string representation of the value
897                  * held in the filterArgs array at index index.
898                  */
899                 buf.replace( start, ii + 1, filterArgs[index].toString() );
900             }
901         }
902 
903         return search( name, buf.toString(), cons );
904     }
905 
906 
907     // ------------------------------------------------------------------------
908     // EventDirContext implementations
909     // ------------------------------------------------------------------------
910 
911     
912     public void addNamingListener( Name name, String filterStr, SearchControls searchControls,
913         NamingListener namingListener ) throws NamingException
914     {
915         ExprNode filter;
916 
917         try
918         {
919             filter = FilterParser.parse( filterStr );
920         }
921         catch ( Exception e )
922         {
923             NamingException e2 = new NamingException( "could not parse filter: " + filterStr );
924             e2.setRootCause( e );
925             throw e2;
926         }
927 
928         try
929         {
930             DirectoryListener listener = new EventListenerAdapter( ( ServerLdapContext ) this, namingListener );
931             NotificationCriteria criteria = new NotificationCriteria();
932             criteria.setFilter( filter );
933             criteria.setScope( SearchScope.getSearchScope( searchControls ) );
934             criteria.setAliasDerefMode( AliasDerefMode.getEnum( getEnvironment() ) );
935             criteria.setBase( buildTarget( name ) );
936             
937             getDirectoryService().getEventService().addListener( listener );
938             getListeners().put( namingListener, listener );
939         }
940         catch ( Exception e )
941         {
942             JndiUtils.wrap( e );
943         }
944     }
945 
946 
947     public void addNamingListener( String name, String filter, SearchControls searchControls,
948         NamingListener namingListener ) throws NamingException
949     {
950         addNamingListener( new LdapDN( name ), filter, searchControls, namingListener );
951     }
952 
953 
954     public void addNamingListener( Name name, String filterExpr, Object[] filterArgs, SearchControls searchControls,
955         NamingListener namingListener ) throws NamingException
956     {
957         int start;
958         StringBuffer buf = new StringBuffer( filterExpr );
959 
960         // Scan until we hit the end of the string buffer
961         for ( int ii = 0; ii < buf.length(); ii++ )
962         {
963             // Advance until we hit the start of a variable
964             while ( '{' != buf.charAt( ii ) )
965             {
966                 ii++;
967             }
968 
969             // Record start of variable at '{'
970             start = ii;
971 
972             // Advance to the end of a variable at '}'
973             while ( '}' != buf.charAt( ii ) )
974             {
975                 ii++;
976             }
977 
978             /*
979              * Replace the '{ i }' with the string representation of the value
980              * held in the filterArgs array at index index.
981              */
982             buf.replace( start, ii + 1, filterArgs[ii].toString() );
983         }
984 
985         addNamingListener( name, buf.toString(), searchControls, namingListener );
986     }
987 
988 
989     public void addNamingListener( String name, String filter, Object[] objects, SearchControls searchControls,
990         NamingListener namingListener ) throws NamingException
991     {
992         addNamingListener( new LdapDN( name ), filter, objects, searchControls, namingListener );
993     }
994 }