You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2008/12/14 14:53:56 UTC

svn commit: r726446 - in /directory/apacheds/trunk: protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java server-integ/src/test/java/org/apache/directory/server/operations/search/SearchLimitsIT.java

Author: elecharny
Date: Sun Dec 14 05:53:56 2008
New Revision: 726446

URL: http://svn.apache.org/viewvc?rev=726446&view=rev
Log:
Restored back the SizeLimit tests, and the simple search code. The PagedSearch are not yet working, but will soon.

Modified:
    directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java
    directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/search/SearchLimitsIT.java

Modified: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java?rev=726446&r1=726445&r2=726446&view=diff
==============================================================================
--- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java (original)
+++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java Sun Dec 14 05:53:56 2008
@@ -69,6 +69,8 @@
 import javax.naming.NamingException;
 import javax.naming.ldap.PagedResultsControl;
 
+import static java.lang.Math.min;
+
 
 /**
  * A handler for processing search requests.
@@ -278,21 +280,52 @@
     }
     
     
-    private int getSearchSizeLimits( SearchRequest req, LdapSession session )
+    /**
+     * Return the server size limit
+     */
+    private int getServerSizeLimit( LdapSession session, SearchRequest request )
     {
-        LOG.debug( "req size limit = {}, configured size limit = {}", req.getSizeLimit(), 
-            ldapService.getMaxSizeLimit() );
-        
-        // Don't bother setting size limits for administrators that don't ask for it
-        if ( session.getCoreSession().isAnAdministrator() && req.getSizeLimit() == NO_SIZE_LIMIT )
+        if ( session.getCoreSession().isAnAdministrator() )
         {
-            return Integer.MAX_VALUE;
+            if ( request.getSizeLimit() == NO_SIZE_LIMIT )
+            {
+                return Integer.MAX_VALUE;
+            }
+            else
+            {
+                return request.getSizeLimit();
+            }
+        }
+        else
+        {
+            if ( ldapService.getMaxSizeLimit() == NO_SIZE_LIMIT )
+            {
+                return Integer.MAX_VALUE;
+            }
+            else
+            {
+                return ldapService.getMaxSizeLimit();
+            }
         }
+    }
+    
+    
+    private int getSearchSizeLimits( SearchRequest req, LdapSession session )
+    {
+        LOG.debug( "req size limit = {}, server size limit = {}", req.getSizeLimit(), 
+            ldapService.getMaxSizeLimit() );
         
         // Don't bother setting size limits for administrators that don't ask for it
         if ( session.getCoreSession().isAnAdministrator() )
         {
-            return req.getSizeLimit();
+            if ( req.getSizeLimit() == NO_SIZE_LIMIT )
+            {
+                return Integer.MAX_VALUE;
+            }
+            else
+            {
+                return req.getSizeLimit();
+            }
         }
         
         /*
@@ -340,13 +373,14 @@
     
     
     private SearchResponseDone readResults( LdapSession session, SearchRequest req, 
-        LdapResult ldapResult,  EntryFilteringCursor cursor, int sizeLimit, boolean isPaged, 
+        LdapResult ldapResult,  EntryFilteringCursor cursor, int requestLimit, int serverLimit, boolean isPaged, 
         PagedSearchCookie cookieInstance, PagedResultsControl pagedResultsControl ) throws Exception
     {
         req.addAbandonListener( new SearchAbandonListener( ldapService, cursor ) );
         setTimeLimitsOnCursor( req, session, cursor );
-        LOG.debug( "using {} for size limit", sizeLimit );
+        LOG.debug( "using <{},{}> for size limit", requestLimit, serverLimit );
         int cookieValue = 0;
+        int sizeLimit = min( requestLimit, serverLimit );
         
         int count = 0;
         
@@ -409,17 +443,13 @@
             }
             else
             {
-                int serverLimit = session.getCoreSession().getDirectoryService().getMaxSizeLimit();
-                
-                if ( serverLimit == 0 )
-                {
-                    serverLimit = Integer.MAX_VALUE;
-                }
-                
                 // DO NOT WRITE THE RESPONSE - JUST RETURN IT
-                if ( count > serverLimit ) 
+                if ( ( count >= sizeLimit ) && ( cursor.next() ) )
                 {
-                    // Special case if the user has requested more elements than the limit
+                    // Move backward on the cursor to restore the previous position, as we moved forward
+                    // to check if there is one more entry available
+                    cursor.previous();
+                    // Special case if the user has requested more elements than the request size limit
                     ldapResult.setResultCode( ResultCodeEnum.SIZE_LIMIT_EXCEEDED );
                 }
                 
@@ -428,154 +458,228 @@
         }
     }
     
+    
     /**
-     * Conducts a simple search across the result set returning each entry 
-     * back except for the search response done.  This is calculated but not
-     * returned so the persistent search mechanism can leverage this method
-     * along with standard search.
-     *
-     * @param session the LDAP session object for this request
-     * @param req the search request 
-     * @return the result done 
-     * @throws Exception if there are failures while processing the request
+     * Manage the abandoned Paged Search (when paged size = 0). We have to
+     * remove the cookie and its associated cursor from the session.
      */
-    private SearchResponseDone doSimpleSearch( LdapSession session, SearchRequest req ) 
+    private SearchResponseDone abandonPagedSearch( LdapSession session, SearchRequest req ) 
         throws Exception
     {
+        PagedResultsControl pagedResultsControl = null;
+        PagedSearchControl pagedSearchControl = 
+            ( PagedSearchControl )req.getControls().get( PagedSearchControl.CONTROL_OID );
+        byte [] cookie= pagedSearchControl.getCookie();
+        
+        if ( !StringTools.isEmpty( cookie ) )
+        {
+            // If the cookie is not null, we have to destroy the associated
+            // cursor stored into the session (if any)
+            int cookieValue = pagedSearchControl.getCookieValue();
+            PagedSearchCookie psCookie = 
+                (PagedSearchCookie)session.getIoSession().removeAttribute( cookieValue );
+            pagedResultsControl = new PagedResultsControl( 0, psCookie.getCookie(), true );
+            
+            // Close the cursor
+            EntryFilteringCursor cursor = psCookie.getCursor();
+            
+            if ( cursor != null )
+            {
+                cursor.close();
+            }
+        }
+        else
+        {
+            pagedResultsControl = new PagedResultsControl( 0, true );
+        }
+        
+        // and return
+        // DO NOT WRITE THE RESPONSE - JUST RETURN IT
         LdapResult ldapResult = req.getResultResponse().getLdapResult();
+        ldapResult.setResultCode( ResultCodeEnum.SUCCESS );
+        req.getResultResponse().add( pagedResultsControl );
+        return ( SearchResponseDone ) req.getResultResponse();
+    }
+    
+    
+    /**
+     * Handle a Paged Search.
+     */
+    private SearchResponseDone doPagedSearch( LdapSession session, SearchRequest req, PagedSearchControl control )
+    {
+        return null;
+        /*
+        PagedSearchControl pagedSearchControl = ( PagedSearchControl )control;
         PagedResultsControl pagedResultsControl = null;
+        int serverLimit = ldapService.getMaxSizeLimit() == 0 ? 
+            Integer.MAX_VALUE : ldapService.getMaxSizeLimit();
+        int requestLimit = req.getSizeLimit() == 0 ?
+            Integer.MAX_VALUE : req.getSizeLimit();
+        int pagedLimit = pagedSearchControl.getSize();
         EntryFilteringCursor cursor = null;
-        int sizeLimit = getSearchSizeLimits( req, session );
-        boolean isPaged = false;
         PagedSearchCookie cookieInstance = null;
 
-        // Check that the PagedSearchControl is present or not.
-        // Check if we are using the Paged Search Control
-        Object control = req.getControls().get( PagedSearchControl.CONTROL_OID );
-        PagedSearchControl pagedSearchControl = null;
-        
-        if ( control != null )
-        {
-            pagedSearchControl = ( PagedSearchControl )control;
-        }
+        // We have the following cases :
+        // 1) The SIZE is 0 and the cookie is the same than the previous one : this
+        // is a abandon request for this paged search.
+        // 2) The cookie is empty : this is a new request. If the requested
+        // size is above the serverLimit and the request limit, this is a normal
+        // search
+        // 3) The cookie is not empty and the request is the same, we return
+        // the next SIZE elements
+        // 4) The cookie is not empty, but the request is not the same : this is 
+        // a new request (we have to discard the cookie and do a new search from
+        // the beginning)
+        // 5) The SIZE is above the size-limit : the request is treated as if it
+        // was a simple search
+        
+        // Case 1
+        if ( pagedLimit == 0 )
+        {
+            // An abandoned paged search
+            return abandonPagedSearch( session, req );
+        }
+        
+        // Now, depending on the cookie, we will deal with case 2, 3, 4 and 5
+        pagedLimit = pagedSearchControl.getSize();
+        byte [] cookie= pagedSearchControl.getCookie();
         
-        if ( pagedSearchControl != null )
+        if ( StringTools.isEmpty( cookie ) )
         {
-            // We have the following cases :
-            // 1) The SIZE is above the size-limit : the request is treated as if it
-            // was a simple search
-            // 2) The cookie is empty : this is a new request
-            // 3) The cookie is not empty, but the request is not the same : this is 
-            // a new request (we have to discard the cookie and do a new search from
-            // the beginning)
-            // 4) The cookie is not empty and the request is the same, we return
-            // the next SIZE elements
-            // 5) The SIZE is 0 and the cookie is the same than the previous one : this
-            // is a abandon request for this paged search.
-            int size = pagedSearchControl.getSize();
-            byte [] cookie= pagedSearchControl.getCookie();
-            
-            // Case 5
-            if ( size == 0 )
-            {
-                // Remove the cookie from the session, if it's not null
-                if ( !StringTools.isEmpty( cookie ) )
-                {
-                    int cookieValue = pagedSearchControl.getCookieValue();
-                    PagedSearchCookie psCookie = 
-                        (PagedSearchCookie)session.getIoSession().removeAttribute( cookieValue );
-                    pagedResultsControl = new PagedResultsControl( 0, psCookie.getCookie(), true );
-                    
-                    // Close the cursor
-                    cursor = psCookie.getCursor();
-                    
-                    if ( cursor != null )
+            // This is a new search.
+            if ( ( pagedLimit > serverLimit ) && ( pagedLimit > requestLimit ) )
+            {
+                // Normal search : create the cursor, and set pagedControl to false
+                try
+                {
+                    readResults( session, req, ldapResult, cursor, min( serverLimit, requestLimit ), true, cookieInstance, pagedResultsControl );
+                }
+                finally
+                {
+                    try
                     {
                         cursor.close();
                     }
-                }
-                else
-                {
-                    pagedResultsControl = new PagedResultsControl( 0, true );
+                    catch ( NamingException e )
+                    {
+                        LOG.error( "failed on list.close()", e );
+                    }
                 }
                 
-                // and return
-                // DO NOT WRITE THE RESPONSE - JUST RETURN IT
-                ldapResult.setResultCode( ResultCodeEnum.SUCCESS );
-                req.getResultResponse().add( pagedResultsControl );
                 return ( SearchResponseDone ) req.getResultResponse();
             }
+            // Case 2 : create the cookie
+            cookieInstance = new PagedSearchCookie( req );
+            cookie = cookieInstance.getCookie();
+            int cookieValue = cookieInstance.getCookieValue();
+
+            session.getIoSession().setAttribute( cookieValue, cookieInstance );
+            pagedResultsControl = new PagedResultsControl( 0, cookie, true );
+        }
+        else
+        {
+            // Either case 3, 4 or 5
+            int cookieValue = pagedSearchControl.getCookieValue();
+            cookieInstance = 
+                (PagedSearchCookie)session.getIoSession().getAttribute( cookieValue );
             
-            if ( sizeLimit < size )
+            if ( cookieInstance.hasSameRequest( req, session ) )
             {
-                // Case 1
-                cursor = session.getCoreSession().search( req );
+                // Case 3 : continue the search
+                cursor = cookieInstance.getCursor();
+                
+                // get the cookie
+                cookie = cookieInstance.getCookie();
+                sizeLimit = size;
+                pagedResultsControl = new PagedResultsControl( 0, cookie, true );
             }
             else
             {
-                isPaged = true;
-                sizeLimit = size;
+                // case 2 : create a new cursor
+                // We have to close the cursor
+                cursor = cookieInstance.getCursor();
                 
-                // Now, depending on the cookie, we will deal with case 2, 3 and 4
-                if ( StringTools.isEmpty( cookie ) )
+                if ( cursor != null )
                 {
-                    // Case 2 : create the cookie
-                    cookieInstance = new PagedSearchCookie( req );
-                    cookie = cookieInstance.getCookie();
-                    int cookieValue = cookieInstance.getCookieValue();
-
-                    session.getIoSession().setAttribute( cookieValue, cookieInstance );
-                    pagedResultsControl = new PagedResultsControl( 0, cookie, true );
+                    cursor.close();
                 }
-                else
-                {
-                    int cookieValue = pagedSearchControl.getCookieValue();
-                    cookieInstance = 
-                        (PagedSearchCookie)session.getIoSession().getAttribute( cookieValue );
-                    
-                    if ( cookieInstance.hasSameRequest( req, session ) )
-                    {
-                        // Case 4 : continue the search
-                        cursor = cookieInstance.getCursor();
-                        
-                        // get the cookie
-                        cookie = cookieInstance.getCookie();
-                        isPaged = true;
-                        sizeLimit = size;
-                        pagedResultsControl = new PagedResultsControl( 0, cookie, true );
-                    }
-                    else
-                    {
-                        // case 3 : create a new cursor
-                        // We have to close the cursor
-                        cursor = cookieInstance.getCursor();
-                        
-                        if ( cursor != null )
-                        {
-                            cursor.close();
-                        }
-                        
-                        // Now create a new cookie and stores it into the session
-                        cookieInstance = new PagedSearchCookie( req );
-                        cookie = cookieInstance.getCookie();
-                        cookieValue = cookieInstance.getCookieValue();
+                
+                // Now create a new cookie and stores it into the session
+                cookieInstance = new PagedSearchCookie( req );
+                cookie = cookieInstance.getCookie();
+                cookieValue = cookieInstance.getCookieValue();
 
-                        session.getIoSession().setAttribute( cookieValue, cookieInstance );
-                        pagedResultsControl = new PagedResultsControl( 0, cookie, true );
-                    }
-                }
+                session.getIoSession().setAttribute( cookieValue, cookieInstance );
+                pagedResultsControl = new PagedResultsControl( 0, cookie, true );
             }
         }
+        */
+    }
+
+    
+    /**
+     * Conducts a simple search across the result set returning each entry 
+     * back except for the search response done.  This is calculated but not
+     * returned so the persistent search mechanism can leverage this method
+     * along with standard search.<br>
+     * <br>
+     * The loop will stop when we have found all the entries, or if we 
+     * have reached the limit, or if we have reached the paged limit. The 
+     * following table describes the different possibilities (SL = server limit,
+     * RL = request limit, PL = paged limit)
+     * 
+     * 1) SL=0, RL=0, PL=0 => no limit
+     * 2) SL=0, RL=0, PL=x => loop until we have sent X entries, or until we don't have
+     * any more entries to send
+     * 3) SL=0, RL=x, PL=0 => loop until we don't have anymore entries, or break and 
+     * return an error if we reach the RL
+     * 4) SL=x, RL=0, PL=0 => loop until we don't have anymore entries, or break and 
+     * return an error if we reach the SL
+     * 5) SL=0, RL=x, PL=y => if x > y, default to case (1), otherwise default to 
+     * case (3)
+     * 6) SL=x, RL=0, PL=y => if x > y, default to case (1), otherwise default to 
+     * case (4)
+     * 7) SL=x, RL=y, PL=0 => loop until we don't have anymore entries, or break and 
+     * return an error if we reach the min(SL, RL)
+     * 8) SL=x, RL=y, PL=z => if z < min(x, y=, default to case 1, otherwise loop until 
+     * we don't have anymore entries, or break and return an error if we reach the
+     * min(SL, RL)
+     * 
+     *
+     * @param session the LDAP session object for this request
+     * @param req the search request 
+     * @return the result done 
+     * @throws Exception if there are failures while processing the request
+     */
+    private SearchResponseDone doSimpleSearch( LdapSession session, SearchRequest req ) 
+        throws Exception
+    {
+        LdapResult ldapResult = req.getResultResponse().getLdapResult();
+        PagedResultsControl pagedResultsControl = null;
         
-        // Check that we have a cursor or not. 
-        if ( cursor == null )
+        // Get the size limits
+        // Don't bother setting size limits for administrators that don't ask for it
+        int serverLimit = getServerSizeLimit( session, req );
+        
+        int requestLimit = req.getSizeLimit() == 0 ?
+            Integer.MAX_VALUE : req.getSizeLimit();
+
+        // Check if we are using the Paged Search Control
+        Object control = req.getControls().get( PagedSearchControl.CONTROL_OID );
+        
+        if ( control != null )
         {
+            // Let's deal with the pagedControl
+            return doPagedSearch( session, req, (PagedSearchControl)control );
+        }
+        
+        // A normal search
+        // Check that we have a cursor or not. 
             // No cursor : do a search.
-            cursor = session.getCoreSession().search( req );
+        EntryFilteringCursor cursor = session.getCoreSession().search( req );
 
-            // Position the cursor at the beginning
-            cursor.beforeFirst();
-        }
+        // Position the cursor at the beginning
+        cursor.beforeFirst();
         
         /*
          * Iterate through all search results building and sending back responses
@@ -583,11 +687,11 @@
          */
         try
         {
-            readResults( session, req, ldapResult, cursor, sizeLimit, isPaged, cookieInstance, pagedResultsControl );
+            readResults( session, req, ldapResult, cursor, requestLimit, serverLimit, false, null, pagedResultsControl );
         }
         finally
         {
-            if ( ( cursor != null ) && !isPaged )
+            if ( cursor != null )
             {
                 try
                 {

Modified: directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/search/SearchLimitsIT.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/search/SearchLimitsIT.java?rev=726446&r1=726445&r2=726446&view=diff
==============================================================================
--- directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/search/SearchLimitsIT.java (original)
+++ directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/search/SearchLimitsIT.java Sun Dec 14 05:53:56 2008
@@ -34,7 +34,6 @@
 import org.apache.directory.server.integ.SiRunner;
 import static org.apache.directory.server.integ.ServerIntegrationUtils.getWiredContext;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
@@ -277,20 +276,12 @@
      * by request size limit value to cause a size limit exceeded exception on
      * the client.
      */
-    @Test
+    @Test (expected = SizeLimitExceededException.class)
     public void testRequestConstrainedUnlimitByConfigurationSize() throws Exception
     {
         ldapService.setMaxSizeLimit( LdapService.NO_SIZE_LIMIT );
         
-        try
-        {
-            getActorsWithLimit( "(objectClass=*)", LdapService.NO_TIME_LIMIT, 1 );
-            assertTrue( true );
-        }
-        catch ( SizeLimitExceededException slee )
-        {
-            fail();
-        }
+        getActorsWithLimit( "(objectClass=*)", LdapService.NO_TIME_LIMIT, 1 );
     }
     
 
@@ -299,19 +290,12 @@
      * which constrains size by request size limit value to cause a size limit 
      * exceeded exception on the client.
      */
-    @Test
+    @Test ( expected = SizeLimitExceededException.class )
     public void testRequestConstrainedLessThanConfigurationSize() throws Exception
     {
         ldapService.setMaxSizeLimit( 10000 ); 
 
-        try
-        {
-            getActorsWithLimit( "(objectClass=*)", LdapService.NO_TIME_LIMIT, 1 );
-        }
-        catch ( SizeLimitExceededException slee )
-        {
-            fail();
-        }
+        getActorsWithLimit( "(objectClass=*)", LdapService.NO_TIME_LIMIT, 1 );
     }
 
 
@@ -333,7 +317,7 @@
      * Sets up the server with shorter search size limit than the request's 
      * which constrains size by using server max limit value to cause a size 
      * limit exceeded exception on the client.
-     * TODO : reestablish this test
+     */
     @Test (expected = SizeLimitExceededException.class ) 
     public void testNonAdminRequestConstrainedGreaterThanConfigurationSize() throws Exception
     {
@@ -362,7 +346,7 @@
     /**
      * Sets up the server with limited search size with unlimited request
      * size limit.  Should not work for non administrative users.
-     * TODO : reestablish this test
+     */
     @Test ( expected = SizeLimitExceededException.class ) 
     public void testNonAdminRequestUnlimitedConfigurationLimitedSize() throws Exception
     {
@@ -382,7 +366,7 @@
      *  "sizeLimitExceeded (4)
      *   Indicates that the size limit specified by the client was
      *   exceeded before the operation could be completed."
-     * TODO : reestablish this test
+     */
     @Test ( expected = SizeLimitExceededException.class )
     public void testRequestConstraintedLessThanExpectedSize() throws Exception
     {