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