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.text.ParseException;
025 import java.util.ArrayList;
026 import java.util.Hashtable;
027 import java.util.Iterator;
028 import java.util.List;
029
030 import javax.naming.Name;
031 import javax.naming.NamingEnumeration;
032 import javax.naming.NamingException;
033 import javax.naming.Reference;
034 import javax.naming.Referenceable;
035 import javax.naming.directory.Attribute;
036 import javax.naming.directory.Attributes;
037 import javax.naming.directory.DirContext;
038 import javax.naming.directory.InvalidAttributeIdentifierException;
039 import javax.naming.directory.InvalidAttributeValueException;
040 import javax.naming.directory.InvalidAttributesException;
041 import javax.naming.directory.InvalidSearchFilterException;
042 import javax.naming.directory.ModificationItem;
043 import javax.naming.directory.SearchControls;
044 import javax.naming.directory.SearchResult;
045 import javax.naming.event.EventDirContext;
046 import javax.naming.event.NamingListener;
047 import javax.naming.ldap.LdapName;
048 import javax.naming.spi.DirStateFactory;
049 import javax.naming.spi.DirectoryManager;
050
051 import org.apache.directory.server.core.CoreSession;
052 import org.apache.directory.server.core.DirectoryService;
053 import org.apache.directory.server.core.LdapPrincipal;
054 import org.apache.directory.server.core.entry.ServerEntryUtils;
055 import org.apache.directory.server.core.event.DirectoryListener;
056 import org.apache.directory.server.core.event.NotificationCriteria;
057 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
058 import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
059 import org.apache.directory.server.i18n.I18n;
060 import org.apache.directory.shared.ldap.constants.SchemaConstants;
061 import org.apache.directory.shared.ldap.entry.BinaryValue;
062 import org.apache.directory.shared.ldap.entry.StringValue;
063 import org.apache.directory.shared.ldap.entry.EntryAttribute;
064 import org.apache.directory.shared.ldap.entry.Modification;
065 import org.apache.directory.shared.ldap.entry.ServerEntry;
066 import org.apache.directory.shared.ldap.exception.LdapException;
067 import org.apache.directory.shared.ldap.exception.LdapInvalidAttributeTypeException;
068 import org.apache.directory.shared.ldap.filter.AndNode;
069 import org.apache.directory.shared.ldap.filter.BranchNode;
070 import org.apache.directory.shared.ldap.filter.EqualityNode;
071 import org.apache.directory.shared.ldap.filter.ExprNode;
072 import org.apache.directory.shared.ldap.filter.FilterParser;
073 import org.apache.directory.shared.ldap.filter.PresenceNode;
074 import org.apache.directory.shared.ldap.filter.SearchScope;
075 import org.apache.directory.shared.ldap.filter.SimpleNode;
076 import org.apache.directory.shared.ldap.jndi.JndiUtils;
077 import org.apache.directory.shared.ldap.message.AliasDerefMode;
078 import org.apache.directory.shared.ldap.name.AVA;
079 import org.apache.directory.shared.ldap.name.DN;
080 import org.apache.directory.shared.ldap.name.RDN;
081 import org.apache.directory.shared.ldap.util.AttributeUtils;
082 import org.apache.directory.shared.ldap.util.StringTools;
083
084
085 /**
086 * The DirContext implementation for the Server Side JNDI LDAP provider.
087 *
088 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
089 * @version $Rev: 929091 $
090 */
091 public abstract class ServerDirContext extends ServerContext implements EventDirContext
092 {
093 // ------------------------------------------------------------------------
094 // Constructors
095 // ------------------------------------------------------------------------
096
097 /**
098 * Creates a new ServerDirContext by reading the PROVIDER_URL to resolve the
099 * distinguished name for this context.
100 *
101 * @param service the parent service that manages this context
102 * @param env the environment used for this context
103 * @throws NamingException if something goes wrong
104 */
105 public ServerDirContext( DirectoryService service, Hashtable<String, Object> env ) throws Exception
106 {
107 super( service, env );
108 }
109
110
111 /**
112 * Creates a new ServerDirContext with a distinguished name which is used to
113 * set the PROVIDER_URL to the distinguished name for this context.
114 *
115 * @param principal the principal which is propagated
116 * @param dn the distinguished name of this context
117 */
118 public ServerDirContext( DirectoryService service, LdapPrincipal principal, Name dn ) throws Exception
119 {
120 super( service, principal, dn );
121 }
122
123
124 // ------------------------------------------------------------------------
125 // DirContext Implementations
126 // ------------------------------------------------------------------------
127
128
129 public ServerDirContext( DirectoryService service, CoreSession session, Name bindDn ) throws Exception
130 {
131 super( service, session, bindDn );
132 }
133
134
135 /**
136 * @see javax.naming.directory.DirContext#getAttributes(java.lang.String)
137 */
138 public Attributes getAttributes( String name ) throws NamingException
139 {
140 return getAttributes( new LdapName( name ) );
141 }
142
143
144 /**
145 * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name)
146 */
147 public Attributes getAttributes( Name name ) throws NamingException
148 {
149 Attributes attrs = null;
150
151 try
152 {
153 attrs = ServerEntryUtils.toBasicAttributes( doLookupOperation( buildTarget( DN.fromName( name ) ) ) );
154 }
155 catch ( Exception e )
156 {
157 JndiUtils.wrap( e );
158 }
159
160 return attrs;
161 }
162
163
164 /**
165 * @see javax.naming.directory.DirContext#getAttributes(java.lang.String,
166 * java.lang.String[])
167 */
168 public Attributes getAttributes( String name, String[] attrIds ) throws NamingException
169 {
170 return getAttributes( new LdapName( name ), attrIds );
171 }
172
173
174 /**
175 * @see javax.naming.directory.DirContext#getAttributes(javax.naming.Name,
176 * java.lang.String[])
177 */
178 public Attributes getAttributes( Name name, String[] attrIds ) throws NamingException
179 {
180 Attributes attrs = null;
181 try
182 {
183 attrs = ServerEntryUtils.toBasicAttributes( doLookupOperation( buildTarget( DN.fromName( name ) ), attrIds ) );
184 }
185 catch ( Exception e )
186 {
187 JndiUtils.wrap( e );
188 }
189
190 return 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( String name, int modOp, Attributes attrs ) throws NamingException
199 {
200 modifyAttributes( new LdapName( name ), modOp, AttributeUtils.toCaseInsensitive( attrs ) );
201 }
202
203
204 /**
205 * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
206 * int, javax.naming.directory.Attributes)
207 */
208 public void modifyAttributes( Name name, int modOp, Attributes attrs ) throws NamingException
209 {
210 List<ModificationItem> modItems = null;
211
212 if ( attrs != null )
213 {
214 modItems = new ArrayList<ModificationItem>( attrs.size() );
215 NamingEnumeration<? extends Attribute> e = ( NamingEnumeration<? extends Attribute> ) attrs.getAll();
216
217 while ( e.hasMore() )
218 {
219 modItems.add( new ModificationItem( modOp, e.next() ) );
220 }
221 }
222
223 List<Modification> newMods = null;
224
225 try
226 {
227 newMods = ServerEntryUtils.convertToServerModification(
228 modItems,
229 getDirectoryService().getSchemaManager() );
230 }
231 catch ( LdapException le )
232 {
233 throw new InvalidAttributesException( le.getMessage() );
234 }
235
236 try
237 {
238 doModifyOperation( buildTarget( DN.fromName( name ) ), newMods );
239 }
240 catch( Exception e )
241 {
242 JndiUtils.wrap( e );
243 }
244 }
245
246
247 /**
248 * @see javax.naming.directory.DirContext#modifyAttributes(java.lang.String,
249 * javax.naming.directory.ModificationItem[])
250 */
251 public void modifyAttributes( String name, ModificationItem[] mods ) throws NamingException
252 {
253 modifyAttributes( new LdapName( name ), mods );
254 }
255
256
257 /**
258 * @see javax.naming.directory.DirContext#modifyAttributes(
259 * javax.naming.Name, javax.naming.directory.ModificationItem[])
260 */
261 public void modifyAttributes( Name name, ModificationItem[] mods ) throws NamingException
262 {
263 List<Modification> newMods;
264
265 try
266 {
267 newMods = ServerEntryUtils
268 .toServerModification( mods, getDirectoryService().getSchemaManager() );
269 }
270 catch ( LdapException le )
271 {
272 throw new InvalidAttributesException( le.getMessage() );
273 }
274
275 try
276 {
277 doModifyOperation( buildTarget( DN.fromName( name ) ), newMods );
278 }
279 catch ( Exception e )
280 {
281 JndiUtils.wrap( e );
282 }
283 }
284
285
286 /**
287 * @see javax.naming.directory.DirContext#modifyAttributes(
288 * javax.naming.Name, javax.naming.directory.ModificationItem[])
289 */
290 public void modifyAttributes( Name name, List<ModificationItem> mods ) throws NamingException
291 {
292 List<Modification> newMods;
293 try
294 {
295 newMods = ServerEntryUtils
296 .convertToServerModification( mods,
297 getDirectoryService().getSchemaManager() );
298 }
299 catch ( LdapException le )
300 {
301 throw new InvalidAttributesException( le.getMessage() );
302 }
303
304 try
305 {
306 doModifyOperation( buildTarget( DN.fromName( name ) ), newMods );
307 }
308 catch ( Exception e )
309 {
310 JndiUtils.wrap( e );
311 }
312 }
313
314
315 /**
316 * @see javax.naming.directory.DirContext#bind(java.lang.String,
317 * java.lang.Object, javax.naming.directory.Attributes)
318 */
319 public void bind( String name, Object obj, Attributes attrs ) throws NamingException
320 {
321 bind( new LdapName( name ), obj, AttributeUtils.toCaseInsensitive( attrs ) );
322 }
323
324
325 /**
326 * @see javax.naming.directory.DirContext#bind(javax.naming.Name,
327 * java.lang.Object, javax.naming.directory.Attributes)
328 */
329 public void bind( Name name, Object obj, Attributes attrs ) throws NamingException
330 {
331 if ( ( null == obj ) && ( null == attrs ) )
332 {
333 throw new NamingException( I18n.err( I18n.ERR_499 ) );
334 }
335
336 // A null attrs defaults this to the Context.bind() operation
337 if ( null == attrs )
338 {
339 super.bind( name, obj );
340 return;
341 }
342
343 DN target = buildTarget( DN.fromName( name ) );
344
345 ServerEntry serverEntry = null;
346
347 try
348 {
349 serverEntry = ServerEntryUtils.toServerEntry( AttributeUtils.toCaseInsensitive( attrs ), target,
350 getDirectoryService().getSchemaManager() );
351 }
352 catch ( LdapInvalidAttributeTypeException liate )
353 {
354 throw new InvalidAttributesException( liate.getMessage() );
355 }
356
357 // No object binding so we just add the attributes
358 if ( null == obj )
359 {
360 ServerEntry clone = ( ServerEntry ) serverEntry.clone();
361 try
362 {
363 doAddOperation( target, clone );
364 }
365 catch ( Exception e )
366 {
367 JndiUtils.wrap( e );
368 }
369 return;
370 }
371
372 // First, use state factories to do a transformation
373 DirStateFactory.Result res = DirectoryManager.getStateToBind( obj, name, this, getEnvironment(), attrs );
374 ServerEntry outServerEntry = null;
375
376 try
377 {
378 outServerEntry = ServerEntryUtils.toServerEntry(
379 res.getAttributes(), target, getDirectoryService().getSchemaManager() );
380 }
381 catch ( LdapInvalidAttributeTypeException le )
382 {
383 throw new InvalidAttributesException( le.getMessage() );
384 }
385
386 if ( outServerEntry != serverEntry )
387 {
388 ServerEntry clone = ( ServerEntry ) serverEntry.clone();
389
390 if ( ( outServerEntry != null ) && ( outServerEntry.size() > 0 ) )
391 {
392 for ( EntryAttribute attribute : outServerEntry )
393 {
394 try
395 {
396 clone.put( attribute );
397 }
398 catch ( LdapException e )
399 {
400 // TODO Auto-generated catch block
401 e.printStackTrace();
402 }
403 }
404 }
405
406 try
407 {
408 // setup the op context
409 doAddOperation( target, clone );
410 }
411 catch ( Exception e )
412 {
413 JndiUtils.wrap( e );
414 }
415
416 return;
417 }
418
419 // Check for Referenceable
420 if ( obj instanceof Referenceable )
421 {
422 throw new NamingException( I18n.err( I18n.ERR_493 ) );
423 }
424
425 // Store different formats
426 if ( obj instanceof Reference )
427 {
428 // Store as ref and add outAttrs
429 throw new NamingException( I18n.err( I18n.ERR_494 ) );
430 }
431 else if ( obj instanceof Serializable )
432 {
433 // Serialize and add outAttrs
434 ServerEntry clone = ( ServerEntry ) serverEntry.clone();
435
436 if ( outServerEntry != null && outServerEntry.size() > 0 )
437 {
438 for ( EntryAttribute attribute : outServerEntry )
439 {
440 try
441 {
442 clone.put( attribute );
443 }
444 catch ( LdapException le )
445 {
446 throw new InvalidAttributesException( le.getMessage() );
447 }
448 }
449 }
450
451 try
452 {
453 // Serialize object into entry attributes and add it.
454 JavaLdapSupport.serialize( serverEntry, obj, getDirectoryService().getSchemaManager() );
455
456 // setup the op context
457 doAddOperation( target, clone );
458 }
459 catch ( Exception e )
460 {
461 JndiUtils.wrap( e );
462 }
463 }
464 else if ( obj instanceof DirContext )
465 {
466 // Grab attributes and merge with outAttrs
467 ServerEntry entry = null;
468
469 try
470 {
471 entry = ServerEntryUtils.toServerEntry( ( ( DirContext ) obj ).getAttributes( "" ), target,
472 getDirectoryService().getSchemaManager() );
473 }
474 catch ( LdapInvalidAttributeTypeException liate )
475 {
476 throw new InvalidAttributeIdentifierException( liate.getMessage() );
477 }
478
479 if ( ( outServerEntry != null ) && ( outServerEntry.size() > 0 ) )
480 {
481 for ( EntryAttribute attribute : outServerEntry )
482 {
483 try
484 {
485 entry.put( attribute );
486 }
487 catch ( LdapException le )
488 {
489 throw new InvalidAttributeValueException( le.getMessage() );
490 }
491 }
492 }
493
494 try
495 {
496 // setup the op context
497 doAddOperation( target, entry );
498 }
499 catch ( Exception e )
500 {
501 JndiUtils.wrap( e );
502 }
503 }
504 else
505 {
506 throw new NamingException( I18n.err( I18n.ERR_495, obj ) );
507 }
508 }
509
510
511 /**
512 * @see javax.naming.directory.DirContext#rebind(java.lang.String,
513 * java.lang.Object, javax.naming.directory.Attributes)
514 */
515 public void rebind( String name, Object obj, Attributes attrs ) throws NamingException
516 {
517 rebind( new LdapName( name ), obj, AttributeUtils.toCaseInsensitive( attrs ) );
518 }
519
520
521 /**
522 * @see javax.naming.directory.DirContext#rebind(javax.naming.Name,
523 * java.lang.Object, javax.naming.directory.Attributes)
524 */
525 public void rebind( Name name, Object obj, Attributes attrs ) throws NamingException
526 {
527 DN target = buildTarget( DN.fromName( name ) );
528
529 try
530 {
531 if ( getDirectoryService().getOperationManager().hasEntry( new EntryOperationContext( getSession(), target ) ) )
532 {
533 doDeleteOperation( target );
534 }
535 }
536 catch ( Exception e )
537 {
538 JndiUtils.wrap( e );
539 }
540
541 bind( name, obj, AttributeUtils.toCaseInsensitive( attrs ) );
542 }
543
544
545 /**
546 * @see javax.naming.directory.DirContext#createSubcontext(java.lang.String,
547 * javax.naming.directory.Attributes)
548 */
549 public DirContext createSubcontext( String name, Attributes attrs ) throws NamingException
550 {
551 Attributes attributes = AttributeUtils.toCaseInsensitive( attrs );
552 return createSubcontext( new LdapName( name ), attributes );
553 }
554
555
556 /**
557 * @see javax.naming.directory.DirContext#createSubcontext(
558 * javax.naming.Name, javax.naming.directory.Attributes)
559 */
560 public DirContext createSubcontext( Name name, Attributes attrs ) throws NamingException
561 {
562 if ( null == attrs )
563 {
564 return ( DirContext ) super.createSubcontext( name );
565 }
566
567 DN target = buildTarget( DN.fromName( name ) );
568 RDN rdn = target.getRdn( target.size() - 1 );
569
570 attrs = AttributeUtils.toCaseInsensitive( attrs );
571 Attributes attributes = ( Attributes ) attrs.clone();
572
573 if ( rdn.size() == 1 )
574 {
575 String rdnAttribute = rdn.getUpType();
576 String rdnValue = ( String ) rdn.getNormValue();
577
578 // Add the RDN attribute
579 boolean doRdnPut = attributes.get( rdnAttribute ) == null;
580 doRdnPut = doRdnPut || attributes.get( rdnAttribute ).size() == 0;
581
582 // TODO Fix DIRSERVER-832
583 doRdnPut = doRdnPut || !attributes.get( rdnAttribute ).contains( rdnValue );
584
585 if ( doRdnPut )
586 {
587 attributes.put( rdnAttribute, rdnValue );
588 }
589 }
590 else
591 {
592 for ( Iterator<AVA> ii = rdn.iterator(); ii.hasNext(); /**/)
593 {
594 AVA atav = ii.next();
595
596 // Add the RDN attribute
597 boolean doRdnPut = attributes.get( atav.getNormType() ) == null;
598 doRdnPut = doRdnPut || attributes.get( atav.getNormType() ).size() == 0;
599
600 // TODO Fix DIRSERVER-832
601 doRdnPut = doRdnPut || !attributes.get( atav.getNormType() ).contains( atav.getNormValue() );
602
603 if ( doRdnPut )
604 {
605 attributes.put( atav.getNormType(), atav.getNormValue() );
606 }
607 }
608 }
609
610 // Add the new context to the server which as a side effect adds
611 try
612 {
613 ServerEntry serverEntry = ServerEntryUtils.toServerEntry( attributes,
614 target, getDirectoryService().getSchemaManager() );
615 doAddOperation( target, serverEntry );
616 }
617 catch ( Exception e )
618 {
619 JndiUtils.wrap( e );
620 }
621
622 // Initialize the new context
623 ServerLdapContext ctx = null;
624
625 try
626 {
627 ctx = new ServerLdapContext( getService(), getSession().getEffectivePrincipal(), DN.toName( target ) );
628 }
629 catch ( Exception e )
630 {
631 JndiUtils.wrap( e );
632 }
633
634
635 return ctx;
636 }
637
638
639 /**
640 * Presently unsupported operation!
641 */
642 public DirContext getSchema( Name name ) throws NamingException
643 {
644 throw new UnsupportedOperationException();
645 }
646
647
648 /**
649 * Presently unsupported operation!
650 */
651 public DirContext getSchema( String name ) throws NamingException
652 {
653 throw new UnsupportedOperationException();
654 }
655
656
657 /**
658 * Presently unsupported operation!
659 */
660 public DirContext getSchemaClassDefinition( Name name ) throws NamingException
661 {
662 throw new UnsupportedOperationException();
663 }
664
665
666 /**
667 * Presently unsupported operation!
668 */
669 public DirContext getSchemaClassDefinition( String name ) throws NamingException
670 {
671 throw new UnsupportedOperationException();
672 }
673
674
675 // ------------------------------------------------------------------------
676 // Search Operation Implementations
677 // ------------------------------------------------------------------------
678
679 /**
680 * @see javax.naming.directory.DirContext#search(java.lang.String,
681 * javax.naming.directory.Attributes)
682 */
683 public NamingEnumeration<SearchResult> search( String name, Attributes matchingAttributes ) throws NamingException
684 {
685 return search( new LdapName( name ), matchingAttributes, null );
686 }
687
688
689 /**
690 * @see javax.naming.directory.DirContext#search(javax.naming.Name,
691 * javax.naming.directory.Attributes)
692 */
693 public NamingEnumeration<SearchResult> search( Name name, Attributes matchingAttributes ) throws NamingException
694 {
695 return search( name, AttributeUtils.toCaseInsensitive( matchingAttributes ), null );
696 }
697
698
699 /**
700 * @see javax.naming.directory.DirContext#search(java.lang.String,
701 * javax.naming.directory.Attributes, java.lang.String[])
702 */
703 public NamingEnumeration<SearchResult> search( String name, Attributes matchingAttributes,
704 String[] attributesToReturn ) throws NamingException
705 {
706 return search( new LdapName( name ), AttributeUtils.toCaseInsensitive( matchingAttributes ), attributesToReturn );
707 }
708
709
710 /**
711 * @see javax.naming.directory.DirContext#search(javax.naming.Name,
712 * javax.naming.directory.Attributes, java.lang.String[])
713 */
714 public NamingEnumeration<SearchResult> search( Name name, Attributes matchingAttributes, String[] attributesToReturn )
715 throws NamingException
716 {
717 SearchControls ctls = new SearchControls();
718 DN target = buildTarget( DN.fromName( name ) );
719
720 // If we need to return specific attributes add em to the SearchControls
721 if ( null != attributesToReturn )
722 {
723 ctls.setReturningAttributes( attributesToReturn );
724 }
725
726 // If matchingAttributes is null/empty use a match for everything filter
727 matchingAttributes = AttributeUtils.toCaseInsensitive( matchingAttributes );
728
729 if ( ( null == matchingAttributes ) || ( matchingAttributes.size() <= 0 ) )
730 {
731 PresenceNode filter = new PresenceNode( SchemaConstants.OBJECT_CLASS_AT );
732 AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
733 try
734 {
735 EntryFilteringCursor cursor = doSearchOperation( target, aliasDerefMode, filter, ctls );
736 return new NamingEnumerationAdapter ( cursor );
737 }
738 catch ( Exception e )
739 {
740 JndiUtils.wrap( e );
741 }
742 }
743
744 // Handle simple filter expressions without multiple terms
745 if ( matchingAttributes.size() == 1 )
746 {
747 NamingEnumeration<? extends Attribute> list = matchingAttributes.getAll();
748 Attribute attr = list.next();
749 list.close();
750
751 if ( attr.size() == 1 )
752 {
753 Object value = attr.get();
754 SimpleNode<?> node;
755
756 if ( value instanceof byte[] )
757 {
758 node = new EqualityNode<byte[]>( attr.getID(), new BinaryValue( ( byte[] ) value ) );
759 }
760 else
761 {
762 node = new EqualityNode<String>( attr.getID(), new StringValue( ( String ) value ) );
763 }
764
765 AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
766 try
767 {
768 EntryFilteringCursor cursor = doSearchOperation( target, aliasDerefMode, node, ctls );
769 return new NamingEnumerationAdapter( cursor );
770 }
771 catch ( Exception e )
772 {
773 JndiUtils.wrap( e );
774 return null; // shut compiler up
775 }
776 }
777 }
778
779 /*
780 * Go through the set of attributes using each attribute value pair as
781 * an attribute value assertion within one big AND filter expression.
782 */
783 Attribute attr;
784 SimpleNode node;
785 BranchNode filter = new AndNode();
786 NamingEnumeration<? extends Attribute> list = matchingAttributes.getAll();
787
788 // Loop through each attribute value pair
789 while ( list.hasMore() )
790 {
791 attr = list.next();
792
793 /*
794 * According to JNDI if an attribute in the matchingAttributes
795 * list does not have any values then we match for just the presence
796 * of the attribute in the entry
797 */
798 if ( attr.size() == 0 )
799 {
800 filter.addNode( new PresenceNode( attr.getID() ) );
801 continue;
802 }
803
804 /*
805 * With 1 or more value we build a set of simple nodes and add them
806 * to the AND node - each attribute value pair is a simple AVA node.
807 */
808 for ( int ii = 0; ii < attr.size(); ii++ )
809 {
810 Object val = attr.get( ii );
811
812 // Add simpel AVA node if its value is a String
813 if ( val instanceof String )
814 {
815 node = new EqualityNode<String>( attr.getID(), new StringValue( ( String ) val ) );
816 filter.addNode( node );
817 }
818 }
819 }
820
821 AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
822 try
823 {
824 EntryFilteringCursor cursor = doSearchOperation( target, aliasDerefMode, filter, ctls );
825 return new NamingEnumerationAdapter( cursor );
826 }
827 catch ( Exception e )
828 {
829 JndiUtils.wrap( e );
830 return null; // shut compiler up
831 }
832 }
833
834
835 /**
836 * @see javax.naming.directory.DirContext#search(java.lang.String,
837 * java.lang.String, javax.naming.directory.SearchControls)
838 */
839 public NamingEnumeration<SearchResult> search( String name, String filter, SearchControls cons )
840 throws NamingException
841 {
842 return search( new LdapName( name ), filter, cons );
843 }
844
845
846 /**
847 * A search overload that is used for optimizing search handling in the
848 * LDAP protocol provider which deals with an ExprNode instance rather than
849 * a String for the filter.
850 *
851 * @param name the relative name of the object serving as the search base
852 * @param filter the search filter as an expression tree
853 * @param cons the search controls to use
854 * @return an enumeration over the SearchResults
855 * @throws NamingException if there are problems performing the search
856 */
857 public NamingEnumeration<SearchResult> search( Name name, ExprNode filter, SearchControls cons )
858 throws NamingException
859 {
860 DN target = buildTarget( DN.fromName( name ) );
861 AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
862 try
863 {
864 return new NamingEnumerationAdapter( doSearchOperation( target, aliasDerefMode, filter, cons ) );
865 }
866 catch ( Exception e )
867 {
868 JndiUtils.wrap( e );
869 return null; // shut compiler up
870 }
871 }
872
873
874 /**
875 * @see javax.naming.directory.DirContext#search(javax.naming.Name,
876 * java.lang.String, javax.naming.directory.SearchControls)
877 */
878 public NamingEnumeration<SearchResult> search( Name name, String filter, SearchControls cons )
879 throws NamingException
880 {
881 ExprNode filterNode;
882 DN target = buildTarget( DN.fromName( name ) );
883
884 try
885 {
886 filterNode = FilterParser.parse( filter );
887 }
888 catch ( ParseException pe )
889 {
890 InvalidSearchFilterException isfe = new InvalidSearchFilterException( I18n.err( I18n.ERR_500, filter ) );
891 isfe.setRootCause( pe );
892 throw isfe;
893 }
894
895 AliasDerefMode aliasDerefMode = AliasDerefMode.getEnum( getEnvironment() );
896 try
897 {
898 EntryFilteringCursor cursor = doSearchOperation( target, aliasDerefMode, filterNode, cons );
899 return new NamingEnumerationAdapter( cursor );
900 }
901 catch ( Exception e )
902 {
903 JndiUtils.wrap( e );
904 return null; // shut compiler up
905 }
906 }
907
908
909 /**
910 * @see javax.naming.directory.DirContext#search(java.lang.String,
911 * java.lang.String, java.lang.Object[],
912 * javax.naming.directory.SearchControls)
913 */
914 public NamingEnumeration<SearchResult> search( String name, String filterExpr, Object[] filterArgs,
915 SearchControls cons ) throws NamingException
916 {
917 return search( new LdapName( name ), filterExpr, filterArgs, cons );
918 }
919
920
921 /**
922 * @see javax.naming.directory.DirContext#search(javax.naming.Name,
923 * java.lang.String, java.lang.Object[],
924 * javax.naming.directory.SearchControls)
925 */
926 public NamingEnumeration<SearchResult> search( Name name, String filterExpr, Object[] filterArgs,
927 SearchControls cons ) throws NamingException
928 {
929 int start;
930 int index;
931
932 StringBuffer buf = new StringBuffer( filterExpr );
933
934 // Scan until we hit the end of the string buffer
935 for ( int ii = 0; ii < buf.length(); ii++ )
936 {
937 try
938 {
939 // Advance until we hit the start of a variable
940 while ( ii < buf.length() && '{' != buf.charAt( ii ) )
941 {
942 ii++;
943 }
944
945 // Record start of variable at '{'
946 start = ii;
947
948 // Advance to the end of a variable at '}'
949 while ( '}' != buf.charAt( ii ) )
950 {
951 ii++;
952 }
953 }
954 catch ( IndexOutOfBoundsException e )
955 {
956 // End of filter so done.
957 break;
958 }
959
960 // Parse index
961 index = Integer.parseInt( buf.substring( start + 1, ii ) );
962
963 if ( filterArgs[index] instanceof String )
964 {
965 /*
966 * Replace the '{ i }' with the string representation of the value
967 * held in the filterArgs array at index index.
968 */
969 buf.replace( start, ii + 1, ( String ) filterArgs[index] );
970 }
971 else if ( filterArgs[index] instanceof byte[] )
972 {
973 String hexstr = "#" + StringTools.toHexString( ( byte[] ) filterArgs[index] );
974 buf.replace( start, ii + 1, hexstr );
975 }
976 else
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[index].toString() );
983 }
984 }
985
986 return search( name, buf.toString(), cons );
987 }
988
989
990 // ------------------------------------------------------------------------
991 // EventDirContext implementations
992 // ------------------------------------------------------------------------
993
994
995 public void addNamingListener( Name name, String filterStr, SearchControls searchControls,
996 NamingListener namingListener ) throws NamingException
997 {
998 ExprNode filter;
999
1000 try
1001 {
1002 filter = FilterParser.parse( filterStr );
1003 }
1004 catch ( Exception e )
1005 {
1006 NamingException e2 = new NamingException( I18n.err( I18n.ERR_501, filterStr ) );
1007 e2.setRootCause( e );
1008 throw e2;
1009 }
1010
1011 try
1012 {
1013 DirectoryListener listener = new EventListenerAdapter( ( ServerLdapContext ) this, namingListener );
1014 NotificationCriteria criteria = new NotificationCriteria();
1015 criteria.setFilter( filter );
1016 criteria.setScope( SearchScope.getSearchScope( searchControls.getSearchScope() ) );
1017 criteria.setAliasDerefMode( AliasDerefMode.getEnum( getEnvironment() ) );
1018 criteria.setBase( buildTarget( DN.fromName( name ) ) );
1019
1020 getDirectoryService().getEventService().addListener( listener );
1021 getListeners().put( namingListener, listener );
1022 }
1023 catch ( Exception e )
1024 {
1025 JndiUtils.wrap( e );
1026 }
1027 }
1028
1029
1030 public void addNamingListener( String name, String filter, SearchControls searchControls,
1031 NamingListener namingListener ) throws NamingException
1032 {
1033 addNamingListener( new LdapName( name ), filter, searchControls, namingListener );
1034 }
1035
1036
1037 public void addNamingListener( Name name, String filterExpr, Object[] filterArgs, SearchControls searchControls,
1038 NamingListener namingListener ) throws NamingException
1039 {
1040 int start;
1041 StringBuffer buf = new StringBuffer( filterExpr );
1042
1043 // Scan until we hit the end of the string buffer
1044 for ( int ii = 0; ii < buf.length(); ii++ )
1045 {
1046 // Advance until we hit the start of a variable
1047 while ( '{' != buf.charAt( ii ) )
1048 {
1049 ii++;
1050 }
1051
1052 // Record start of variable at '{'
1053 start = ii;
1054
1055 // Advance to the end of a variable at '}'
1056 while ( '}' != buf.charAt( ii ) )
1057 {
1058 ii++;
1059 }
1060
1061 /*
1062 * Replace the '{ i }' with the string representation of the value
1063 * held in the filterArgs array at index index.
1064 */
1065 buf.replace( start, ii + 1, filterArgs[ii].toString() );
1066 }
1067
1068 addNamingListener( name, buf.toString(), searchControls, namingListener );
1069 }
1070
1071
1072 public void addNamingListener( String name, String filter, Object[] objects, SearchControls searchControls,
1073 NamingListener namingListener ) throws NamingException
1074 {
1075 addNamingListener( new LdapName( name ), filter, objects, searchControls, namingListener );
1076 }
1077 }