View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.db;
18  
19  
20  import org.apache.ldap.common.filter.ExprNode;
21  import org.apache.ldap.common.filter.ScopeNode;
22  import org.apache.ldap.common.util.SingletonEnumeration;
23  
24  import javax.naming.NamingEnumeration;
25  import javax.naming.NamingException;
26  import javax.naming.directory.SearchControls;
27  import java.math.BigInteger;
28  
29  
30  /***
31   * Enumerates candidates based on scope.
32   * 
33   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
34   * @version $Rev: 164287 $
35   */
36  public class ScopeEnumerator implements Enumerator
37  {
38      /*** Database used to enumerate based on scope */
39      private Database db = null;
40      /*** Filter scope expression evaluator */
41      private ScopeEvaluator evaluator = null;
42  
43  
44      public ScopeEnumerator( Database db, ScopeEvaluator evaluator )
45      {
46          this.db = db;
47          this.evaluator = evaluator;
48      }
49  
50  
51      /***
52       * Builds an enumeration over all entries that satisfy the constraints of 
53       * the scope assertion node.
54       *
55       * @param node the scope node 
56       * @return the candidates that are within scope
57       * @throws NamingException if any system indices fail
58       * @see org.apache.ldap.server.db.Enumerator#enumerate(ExprNode)
59       */
60      public NamingEnumeration enumerate( ExprNode node ) throws NamingException
61      {
62          final ScopeNode snode = ( ScopeNode ) node;
63          final BigInteger id = db.getEntryId( snode.getBaseDn() );
64  
65          switch( snode.getScope() )
66          {
67          case( SearchControls.OBJECT_SCOPE ):
68              final IndexRecord record = new IndexRecord();
69              record.setEntryId( id );
70              record.setIndexKey( snode.getBaseDn() );
71              return new SingletonEnumeration( record ); 
72          case( SearchControls.ONELEVEL_SCOPE ):
73              return enumerateChildren( snode.getBaseDn(),
74                  snode.getDerefAliases().derefInSearching() );
75          case( SearchControls.SUBTREE_SCOPE ):
76              return enumerateDescendants( snode );
77          default:
78              throw new NamingException( "Unrecognized search scope!" );
79          }
80      }
81  
82  
83      /***
84       * Constructs an enumeration over all entries within one level scope even
85       * when aliases are enabled while searching.
86       * 
87       * @param dn the base dn
88       * @param deref whether or not we dereference while searching
89       * @return the enumeration of all entries in direct or alias extended one 
90       * level scope to the base
91       * @throws NamingException if any failures occur while accessing system
92       * indices.
93       */
94      private NamingEnumeration enumerateChildren( String dn, boolean deref )
95          throws NamingException
96      {
97          Index idx = db.getHierarchyIndex();
98          final BigInteger id = db.getEntryId( dn );
99          final NamingEnumeration children = idx.listIndices( id );
100         
101         /*
102          * If alias dereferencing is not enabled while searching then we just
103          * return the enumeration of the base entry's children.
104          */
105         if ( ! deref )
106         {
107            return children; 
108         }
109 
110         /* ====================================================================
111          * From here on Dereferencing while searching is enabled
112          * ====================================================================
113          *
114          * Dereferencing in search is enabled so we need to wrap the child
115          * listing with an assertion enumeration to weed out aliases that will
116          * not be returned.  Next we need to compose an enumeration which 
117          * combines the list of non-alias child entries with those entries that
118          * are brought into one level scope by aliases.
119          */
120 
121         // List all entries brought into one level scope at base by aliases
122         idx = db.getOneAliasIndex();
123         NamingEnumeration aliasIntroduced = idx.listIndices( id );
124         
125         // Still need to use assertion enum to weed out aliases
126         NamingEnumeration nonAliasChildren = new IndexAssertionEnumeration( 
127             children, new AssertNotAlias() );
128         
129         // Combine both into one enumeration
130         NamingEnumeration [] all = {nonAliasChildren, aliasIntroduced}; 
131         return new DisjunctionEnumeration( all );
132     }
133     
134 
135     /***
136      * Constructs an enumeration over all entries within subtree scope even
137      * when aliases are enabled while searching.
138      * 
139      * @param node the scope node
140      * @return the enumeration of all entries in direct or alias extended 
141      * subtree scope to the base
142      * @throws NamingException if any failures occur while accessing system
143      * indices.
144      */
145     private NamingEnumeration enumerateDescendants( final ScopeNode node )
146         throws NamingException
147     {
148         Index idx = null;
149 
150         /*
151          * If we do not dereference while searching then we simply return any
152          * entry that is not a descendant of the base.
153          */
154         if ( ! node.getDerefAliases().derefInSearching() )
155         {
156             // Gets a NamingEnumeration over all elements
157             idx = db.getNdnIndex();
158             NamingEnumeration underlying = idx.listIndices();
159             return new IndexAssertionEnumeration( underlying, 
160                 new AssertDescendant( node ) );
161         }
162 
163         // Create an assertion to assert or evaluate an expression
164         IndexAssertion assertion = new IndexAssertion()
165         {
166             public boolean assertCandidate( IndexRecord rec )
167                 throws NamingException
168             {
169                 return evaluator.evaluate( node, rec );
170             }
171         };
172 
173         // Gets a NamingEnumeration over all elements
174         idx = db.getNdnIndex();
175         NamingEnumeration underlying = idx.listIndices();
176         return new IndexAssertionEnumeration( underlying, assertion );
177     }
178 
179 
180     /***
181      * Asserts an entry is a descendant.
182      */
183     class AssertDescendant implements IndexAssertion
184     {
185         /*** Scope node with base and alias info */
186         private final ScopeNode scope;
187 
188         
189         /***
190          * Creates a assertion using a ScopeNode to determine the search base.
191          *
192          * @param node the scope node with search base
193          */
194         AssertDescendant( final ScopeNode node )
195         {
196             scope = node;
197         }
198 
199 
200         /***
201          * Returns true if the candidate with id is a descendant of the base, 
202          * false otherwise.
203          * 
204          * @see org.apache.ldap.server.db.IndexAssertion#assertCandidate(IndexRecord)
205          */
206         public boolean assertCandidate( IndexRecord record ) throws NamingException
207         {
208             String dn = db.getEntryDn( record.getEntryId() );
209             return dn.endsWith( scope.getBaseDn() );
210         }
211     }    
212 
213 
214     /***
215      * Asserts an entry is NOT an alias.
216      */
217     class AssertNotAlias implements IndexAssertion
218     {
219         /***
220          * Returns true if the candidate is not an alias, false otherwise.
221          * 
222          * @see IndexAssertion#assertCandidate(IndexRecord)
223          */
224         public boolean assertCandidate( IndexRecord record ) throws NamingException
225         {
226             Index aliasIdx = db.getAliasIndex();
227                
228             if ( null == aliasIdx.reverseLookup( record.getEntryId() ) )
229             {
230                 return true;
231             }
232                
233             return false;
234         } 
235     }
236 }