You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ak...@apache.org on 2008/08/21 06:38:51 UTC

svn commit: r687555 - in /directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap: ./ handlers/

Author: akarasulu
Date: Wed Aug 20 21:38:50 2008
New Revision: 687555

URL: http://svn.apache.org/viewvc?rev=687555&view=rev
Log:
implemented search size limits handling using Cursor ClosureMonitor technique and added abandon handling

Added:
    directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java
    directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java
Modified:
    directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java
    directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java
    directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java

Modified: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java?rev=687555&r1=687554&r2=687555&view=diff
==============================================================================
--- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java (original)
+++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java Wed Aug 20 21:38:50 2008
@@ -120,6 +120,12 @@
     /** The default maximum time limit. */
     private static final int MAX_TIME_LIMIT_DEFAULT = 10000;
 
+    /** Value (0) for configuration where size limit is unlimited. */
+    public static final int NO_SIZE_LIMIT = 0;
+
+    /** Value (0) for configuration where time limit is unlimited. */
+    public static final int NO_TIME_LIMIT = 0;
+
     /** The default service pid. */
     private static final String SERVICE_PID_DEFAULT = "org.apache.directory.server.ldap";
 

Modified: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java?rev=687555&r1=687554&r2=687555&view=diff
==============================================================================
--- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java (original)
+++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java Wed Aug 20 21:38:50 2008
@@ -117,6 +117,10 @@
     public final void messageReceived( IoSession session, T message ) throws Exception
     {
         LdapSession ldapSession = ldapServer.getLdapSession( session );
+        
+        // TODO - session you get from LdapServer should have the ldapServer 
+        // member already set no?  Should remove these lines where ever they
+        // may be if that's the case.
         ldapSession.setLdapServer( ldapServer );
         
         // protect against insecure conns when confidentiality is required 

Added: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java?rev=687555&view=auto
==============================================================================
--- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java (added)
+++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java Wed Aug 20 21:38:50 2008
@@ -0,0 +1,102 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import org.apache.directory.server.core.event.DirectoryListener;
+import org.apache.directory.server.core.filtering.EntryFilteringCursor;
+import org.apache.directory.server.ldap.LdapServer;
+import org.apache.directory.shared.ldap.exception.OperationAbandonedException;
+import org.apache.directory.shared.ldap.message.AbandonListener;
+import org.apache.directory.shared.ldap.message.AbandonableRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An AbandonListener implementation which closes an associated cursor or 
+ * removes a DirectoryListener.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SearchAbandonListener implements AbandonListener
+{
+    private static final Logger LOG = LoggerFactory.getLogger( SearchAbandonListener.class );
+    private final LdapServer ldapServer;
+    private EntryFilteringCursor cursor;
+    private DirectoryListener listener;
+    
+    
+    public SearchAbandonListener( LdapServer ldapServer, EntryFilteringCursor cursor, DirectoryListener listener )
+    {
+        if ( ldapServer == null )
+        {
+            throw new NullPointerException( "ldapServer" );
+        }
+        
+        this.ldapServer = ldapServer;
+        this.cursor = cursor;
+        this.listener = listener;
+    }
+    
+    
+    public SearchAbandonListener( LdapServer ldapServer, DirectoryListener listener )
+    {
+        this ( ldapServer, null, listener );
+    }
+    
+    
+    public SearchAbandonListener( LdapServer ldapServer, EntryFilteringCursor cursor )
+    {
+        this ( ldapServer, cursor, null );
+    }
+    
+    
+    public void requestAbandoned( AbandonableRequest req )
+    {
+        if ( listener != null )
+        {
+            ldapServer.getDirectoryService().getEventService().removeListener( listener );
+        }
+
+        try
+        {
+            if ( cursor != null )
+            {
+                /*
+                 * When this method is called due to an abandon request it 
+                 * will close the cursor but other threads processing the 
+                 * search will get an OperationAbandonedException which as
+                 * seen below will make sure the proper handling is 
+                 * performed.
+                 */
+                cursor.close( new OperationAbandonedException() );
+            }
+        }
+        catch ( Exception e )
+        {
+            LOG.error( "Failed to close the search cursor for message {} on abandon request.", 
+                req.getMessageId(), e );
+        }
+    }
+}
+
+

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=687555&r1=687554&r2=687555&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 Wed Aug 20 21:38:50 2008
@@ -20,12 +20,15 @@
 package org.apache.directory.server.ldap.handlers;
 
 
+import java.util.concurrent.TimeUnit;
+
 import org.apache.directory.server.core.entry.ClonedServerEntry;
 import org.apache.directory.server.core.entry.ServerEntryUtils;
 import org.apache.directory.server.core.entry.ServerStringValue;
 import org.apache.directory.server.core.event.EventType;
 import org.apache.directory.server.core.event.NotificationCriteria;
 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
+import org.apache.directory.server.ldap.LdapServer;
 import org.apache.directory.server.ldap.LdapSession;
 import org.apache.directory.shared.ldap.codec.util.LdapURL;
 import org.apache.directory.shared.ldap.codec.util.LdapURLEncodingException;
@@ -36,7 +39,6 @@
 import org.apache.directory.shared.ldap.filter.EqualityNode;
 import org.apache.directory.shared.ldap.filter.OrNode;
 import org.apache.directory.shared.ldap.filter.PresenceNode;
-import org.apache.directory.shared.ldap.message.AbandonListener;
 import org.apache.directory.shared.ldap.message.LdapResult;
 import org.apache.directory.shared.ldap.message.ManageDsaITControl;
 import org.apache.directory.shared.ldap.message.PersistentSearchControl;
@@ -70,10 +72,20 @@
 
     /** Speedup for logs */
     private static final boolean IS_DEBUG = LOG.isDebugEnabled();
+
+    /** cached to save redundant lookups into registries */ 
+    private AttributeType objectClassAttributeType;
     
-    AttributeType objectClassAttributeType;
     
-    private EqualityNode<String> getOcIsReferralAssertion( LdapSession session ) throws Exception
+    /**
+     * Constructs a new filter EqualityNode asserting that a candidate 
+     * objectClass is a referral.
+     *
+     * @param session the {@link LdapSession} to construct the node for
+     * @return the {@link EqualityNode} (objectClass=referral) non-normalized
+     * @throws Exception in the highly unlikely event of schema related failures
+     */
+    private EqualityNode<String> newIsReferralEqualityNode( LdapSession session ) throws Exception
     {
         if ( objectClassAttributeType == null )
         {
@@ -81,14 +93,23 @@
                 .getAttributeTypeRegistry().lookup( SchemaConstants.OBJECT_CLASS_AT );
         }
         
-        EqualityNode<String> ocIsReferral = new EqualityNode<String>( 
-            SchemaConstants.OBJECT_CLASS_AT,
+        EqualityNode<String> ocIsReferral = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT,
             new ServerStringValue( objectClassAttributeType, SchemaConstants.REFERRAL_OC ) );
         
         return ocIsReferral;
     }
     
     
+    /**
+     * Handles search requests containing the persistent search control but 
+     * delegates to doSimpleSearch() if the changesOnly parameter of the 
+     * control is set to false.
+     *
+     * @param session the LdapSession for which this search is conducted 
+     * @param req the search request containing the persistent search control
+     * @param psearchControl the persistent search control extracted
+     * @throws Exception if failures are encountered while searching
+     */
     private void handlePersistentSearch( LdapSession session, SearchRequest req, 
         PersistentSearchControl psearchControl ) throws Exception 
     {
@@ -108,7 +129,12 @@
             }
         }
 
-        // now we process entries for ever as they change
+        if ( req.isAbandoned() )
+        {
+            return;
+        }
+        
+        // now we process entries forever as they change
         PersistentSearchListener handler = new PersistentSearchListener( session, req );
         
         // compose notification criteria and add the listener to the event 
@@ -121,15 +147,17 @@
         criteria.setScope( req.getScope() );
         criteria.setEventMask( EventType.getEventTypes( psearchControl.getChangeTypes() ) );
         getLdapServer().getDirectoryService().getEventService().addListener( handler, criteria );
+        req.addAbandonListener( new SearchAbandonListener( ldapServer, handler ) );
         return;
     }
     
     
     /**
-     * Deal with RootDE search. 
-     * @param session
-     * @param req
-     * @throws Exception
+     * Handles search requests on the RootDSE. 
+     * 
+     * @param session the LdapSession for which this search is conducted 
+     * @param req the search request on the RootDSE
+     * @throws Exception if failures are encountered while searching
      */
     private void handleRootDseSearch( LdapSession session, SearchRequest req ) throws Exception
     {
@@ -147,9 +175,9 @@
             {
             	if ( hasRootDSE )
             	{
-            		// This is an error ! We should never find more
-            		// than one rootDSE !
-            		// TODO : handle this error
+            		// This is an error ! We should never find more than one rootDSE !
+            	    LOG.error( "Got back more than one entry for search on RootDSE which means " +
+            	    		"Cursor is not functioning properly!" );
             	}
             	else
             	{
@@ -181,6 +209,66 @@
     
     
     /**
+     * Based on the server maximum time limits configured for search and the 
+     * requested time limits this method determines if at all to replace the 
+     * default ClosureMonitor of the result set Cursor with one that closes
+     * the Cursor when either server mandated or request mandated time limits 
+     * are reached.
+     *
+     * @param req the {@link SearchRequest} issued
+     * @param session the {@link LdapSession} on which search was requested
+     * @param cursor the {@link EntryFilteringCursor} over the search results
+     */
+    private void setTimeLimitsOnCursor( SearchRequest req, LdapSession session, final EntryFilteringCursor cursor )
+    {
+        // Don't bother setting time limits for administrators
+        if ( session.getCoreSession().isAnAdministrator() && req.getTimeLimit() == 0 )
+        {
+            return;
+        }
+        
+        /*
+         * Non administrator based searches are limited by time if the server 
+         * has been configured with unlimited time and the request specifies 
+         * unlimited search time
+         */
+        if ( ldapServer.getMaxTimeLimit() == LdapServer.NO_TIME_LIMIT && req.getTimeLimit() == 0 )
+        {
+            return;
+        }
+        
+        /*
+         * If the non-administrator user specifies unlimited time but the server 
+         * is configured to limit the search time then we limit by the max time 
+         * allowed by the configuration 
+         */
+        if ( req.getTimeLimit() == 0 )
+        {
+            cursor.setClosureMonitor( new SearchTimeLimitingMonitor( ldapServer.getMaxTimeLimit(), TimeUnit.SECONDS ) );
+            return;
+        }
+        
+        /*
+         * If the non-administrative user specifies a time limit equal to or 
+         * less than the maximum limit configured in the server then we 
+         * constrain search by the amount specified in the request
+         */
+        if ( ldapServer.getMaxSizeLimit() >= req.getTimeLimit() )
+        {
+            cursor.setClosureMonitor( new SearchTimeLimitingMonitor( req.getTimeLimit(), TimeUnit.SECONDS ) );
+            return;
+        }
+
+        /*
+         * Here the non-administrative user's requested time limit is greater 
+         * than what the server's configured maximum limit allows so we limit
+         * the search to the configured limit
+         */
+        cursor.setClosureMonitor( new SearchTimeLimitingMonitor( ldapServer.getMaxTimeLimit(), TimeUnit.SECONDS ) );
+    }
+    
+    
+    /**
      * 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
@@ -191,7 +279,8 @@
      * @return the result done 
      * @throws Exception if there are failures while processing the request
      */
-    private SearchResponseDone doSimpleSearch( LdapSession session, SearchRequest req ) throws Exception
+    private SearchResponseDone doSimpleSearch( LdapSession session, SearchRequest req ) 
+        throws Exception
     {
         /*
          * Iterate through all search results building and sending back responses
@@ -202,13 +291,9 @@
         try
         {
             cursor = session.getCoreSession().search( req );
+            req.addAbandonListener( new SearchAbandonListener( ldapServer, cursor ) );
+            setTimeLimitsOnCursor( req, session, cursor );
             
-            // TODO - fix this (need to make Cursors abandonable)
-            if ( cursor instanceof AbandonListener )
-            {
-                req.addAbandonListener( ( AbandonListener ) cursor );
-            }
-    
             // Position the cursor at the beginning
             cursor.beforeFirst();
             
@@ -361,7 +446,7 @@
         }
 
         // using varags to add two expressions to an OR node 
-        req.setFilter( new OrNode( req.getFilter(), getOcIsReferralAssertion( session ) ) );
+        req.setFilter( new OrNode( req.getFilter(), newIsReferralEqualityNode( session ) ) );
     }
     
     

Added: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java?rev=687555&view=auto
==============================================================================
--- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java (added)
+++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java Wed Aug 20 21:38:50 2008
@@ -0,0 +1,149 @@
+/*
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *   or more contributor license agreements.  See the NOTICE file
+ *   distributed with this work for additional information
+ *   regarding copyright ownership.  The ASF licenses this file
+ *   to you under the Apache License, Version 2.0 (the
+ *   "License"); you may not use this file except in compliance
+ *   with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing,
+ *   software distributed under the License is distributed on an
+ *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *   KIND, either express or implied.  See the License for the
+ *   specific language governing permissions and limitations
+ *   under the License.
+ *
+ */
+package org.apache.directory.server.ldap.handlers;
+
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.directory.server.core.cursor.ClosureMonitor;
+import org.apache.directory.server.core.cursor.CursorClosedException;
+import org.apache.directory.shared.ldap.exception.LdapTimeLimitExceededException;
+
+
+/**
+ * A ClosureMonitor implementation which takes into account a time limit.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$, $Date$
+ */
+public class SearchTimeLimitingMonitor implements ClosureMonitor
+{
+    private final long startTime = System.currentTimeMillis();
+    private final long millisToLive;
+    
+    private boolean closed;
+    private Exception cause;
+    
+    
+    public SearchTimeLimitingMonitor( long timeToLive, TimeUnit unit )
+    {
+        switch ( unit )
+        {
+            case MICROSECONDS:
+                this.millisToLive = timeToLive / 1000;
+                break;
+            case MILLISECONDS:
+                this.millisToLive = timeToLive;
+                break;
+            case SECONDS:
+                this.millisToLive = timeToLive * 1000;
+                break;
+            case MINUTES:
+                this.millisToLive = timeToLive * 60000;
+                break;
+            default:
+                throw new IllegalStateException( "TimeUnit not supported: " + unit );
+        }
+    }
+
+    
+    public void checkNotClosed() throws Exception
+    {
+        if ( System.currentTimeMillis() > startTime + millisToLive )
+        {
+            // state check needed to "try" not to overwrite exception (lack of 
+            // synchronization may still allow overwriting but who cares that 
+            // much
+            if ( ! closed )
+            {
+                // not going to sync because who cares if it takes a little 
+                // longer to stop but we need to set cause before toggling 
+                // closed state or else check for closure can throw null cause 
+                cause = new LdapTimeLimitExceededException();
+                closed = true;
+            }
+        }
+        
+        if ( closed )
+        {
+            throw cause;
+        }
+    }
+
+    
+    public void close()
+    {
+        if ( ! closed )
+        {
+            // not going to sync because who cares if it takes a little longer 
+            // to stop but we need to set cause before toggling closed state 
+            // or else check for closure can throw null cause 
+            cause = new CursorClosedException();
+            closed = true;
+        }
+    }
+
+    
+    public void close( String cause )
+    {
+        if ( ! closed )
+        {
+            // not going to sync because who cares if it takes a little longer 
+            // to stop but we need to set cause before toggling closed state 
+            // or else check for closure can throw null cause 
+            this.cause = new CursorClosedException( cause );
+            closed = true;
+        }
+    }
+
+    
+    public void close( Exception cause )
+    {
+        if ( ! closed )
+        {
+            // not going to sync because who cares if it takes a little longer 
+            // to stop but we need to set cause before toggling closed state 
+            // or else check for closure can throw null cause 
+            this.cause = cause;
+            closed = true;
+        }
+    }
+
+    
+    public Exception getCause()
+    {
+        return cause;
+    }
+
+    
+    public boolean isClosed()
+    {
+        if ( System.currentTimeMillis() > startTime + millisToLive )
+        {
+            // set cause first always
+            cause = new LdapTimeLimitExceededException();
+            closed = true;
+        }
+        
+        return closed;
+    }
+}
+
+



Re: svn commit: r687555 - in /directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap: ./ handlers/

Posted by Emmanuel Lecharny <el...@gmail.com>.
Another problem with the SearchTimeLimitingMonitor class : 
TimeUnit.MINUTE does not exist in Java 5.

You should build the server using a older JVM, even if it's slower :)

-- 
--
cordialement, regards,
Emmanuel Lécharny
www.iktek.com
directory.apache.org



Re: svn commit: r687555 - in /directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap: ./ handlers/

Posted by Alex Karasulu <ak...@apache.org>.
Sure will do that if power stays on long enough.  Power cut out while I was
working on this yesterday.

On Thu, Aug 21, 2008 at 8:44 AM, Emmanuel Lecharny <el...@gmail.com>wrote:

> Hi Alex,
>
> can you add the minimum Javadoc needed for the public methods and for the
> fields of the SearchTimeLimitingMonitor class ?
>
> Thanks !
>
> akarasulu@apache.org wrote:
>
>> Author: akarasulu
>> Date: Wed Aug 20 21:38:50 2008
>> New Revision: 687555
>>
>> URL: http://svn.apache.org/viewvc?rev=687555&view=rev
>> Log:
>> implemented search size limits handling using Cursor ClosureMonitor
>> technique and added abandon handling
>>
>> Added:
>>
>>  directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java
>>
>>  directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java
>> Modified:
>>
>>  directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java
>>
>>  directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java
>>
>>  directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java
>>
>> Modified:
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java
>> URL:
>> http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java?rev=687555&r1=687554&r2=687555&view=diff
>>
>> ==============================================================================
>> ---
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java
>> (original)
>> +++
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java
>> Wed Aug 20 21:38:50 2008
>> @@ -120,6 +120,12 @@
>>     /** The default maximum time limit. */
>>     private static final int MAX_TIME_LIMIT_DEFAULT = 10000;
>>  +    /** Value (0) for configuration where size limit is unlimited. */
>> +    public static final int NO_SIZE_LIMIT = 0;
>> +
>> +    /** Value (0) for configuration where time limit is unlimited. */
>> +    public static final int NO_TIME_LIMIT = 0;
>> +
>>     /** The default service pid. */
>>     private static final String SERVICE_PID_DEFAULT =
>> "org.apache.directory.server.ldap";
>>
>> Modified:
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java
>> URL:
>> http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java?rev=687555&r1=687554&r2=687555&view=diff
>>
>> ==============================================================================
>> ---
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java
>> (original)
>> +++
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java
>> Wed Aug 20 21:38:50 2008
>> @@ -117,6 +117,10 @@
>>     public final void messageReceived( IoSession session, T message )
>> throws Exception
>>     {
>>         LdapSession ldapSession = ldapServer.getLdapSession( session );
>> +        +        // TODO - session you get from LdapServer should have
>> the ldapServer +        // member already set no?  Should remove these lines
>> where ever they
>> +        // may be if that's the case.
>>         ldapSession.setLdapServer( ldapServer );
>>                 // protect against insecure conns when confidentiality is
>> required
>> Added:
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java
>> URL:
>> http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java?rev=687555&view=auto
>>
>> ==============================================================================
>> ---
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java
>> (added)
>> +++
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java
>> Wed Aug 20 21:38:50 2008
>> @@ -0,0 +1,102 @@
>> +/*
>> + *   Licensed to the Apache Software Foundation (ASF) under one
>> + *   or more contributor license agreements.  See the NOTICE file
>> + *   distributed with this work for additional information
>> + *   regarding copyright ownership.  The ASF licenses this file
>> + *   to you under the Apache License, Version 2.0 (the
>> + *   "License"); you may not use this file except in compliance
>> + *   with the License.  You may obtain a copy of the License at
>> + *
>> + *     http://www.apache.org/licenses/LICENSE-2.0
>> + *
>> + *   Unless required by applicable law or agreed to in writing,
>> + *   software distributed under the License is distributed on an
>> + *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>> + *   KIND, either express or implied.  See the License for the
>> + *   specific language governing permissions and limitations
>> + *   under the License.
>> + *
>> + */
>> +package org.apache.directory.server.ldap.handlers;
>> +
>> +
>> +import org.apache.directory.server.core.event.DirectoryListener;
>> +import org.apache.directory.server.core.filtering.EntryFilteringCursor;
>> +import org.apache.directory.server.ldap.LdapServer;
>> +import
>> org.apache.directory.shared.ldap.exception.OperationAbandonedException;
>> +import org.apache.directory.shared.ldap.message.AbandonListener;
>> +import org.apache.directory.shared.ldap.message.AbandonableRequest;
>> +import org.slf4j.Logger;
>> +import org.slf4j.LoggerFactory;
>> +
>> +
>> +/**
>> + * An AbandonListener implementation which closes an associated cursor or
>> + * removes a DirectoryListener.
>> + *
>> + * @author <a href="mailto:dev@directory.apache.org">Apache Directory
>> Project</a>
>> + * @version $Rev$, $Date$
>> + */
>> +public class SearchAbandonListener implements AbandonListener
>> +{
>> +    private static final Logger LOG = LoggerFactory.getLogger(
>> SearchAbandonListener.class );
>> +    private final LdapServer ldapServer;
>> +    private EntryFilteringCursor cursor;
>> +    private DirectoryListener listener;
>> +    +    +    public SearchAbandonListener( LdapServer ldapServer,
>> EntryFilteringCursor cursor, DirectoryListener listener )
>> +    {
>> +        if ( ldapServer == null )
>> +        {
>> +            throw new NullPointerException( "ldapServer" );
>> +        }
>> +        +        this.ldapServer = ldapServer;
>> +        this.cursor = cursor;
>> +        this.listener = listener;
>> +    }
>> +    +    +    public SearchAbandonListener( LdapServer ldapServer,
>> DirectoryListener listener )
>> +    {
>> +        this ( ldapServer, null, listener );
>> +    }
>> +    +    +    public SearchAbandonListener( LdapServer ldapServer,
>> EntryFilteringCursor cursor )
>> +    {
>> +        this ( ldapServer, cursor, null );
>> +    }
>> +    +    +    public void requestAbandoned( AbandonableRequest req )
>> +    {
>> +        if ( listener != null )
>> +        {
>> +
>>  ldapServer.getDirectoryService().getEventService().removeListener( listener
>> );
>> +        }
>> +
>> +        try
>> +        {
>> +            if ( cursor != null )
>> +            {
>> +                /*
>> +                 * When this method is called due to an abandon request
>> it +                 * will close the cursor but other threads processing
>> the +                 * search will get an OperationAbandonedException which
>> as
>> +                 * seen below will make sure the proper handling is +
>>             * performed.
>> +                 */
>> +                cursor.close( new OperationAbandonedException() );
>> +            }
>> +        }
>> +        catch ( Exception e )
>> +        {
>> +            LOG.error( "Failed to close the search cursor for message {}
>> on abandon request.", +                req.getMessageId(), e );
>> +        }
>> +    }
>> +}
>> +
>> +
>>
>> 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=687555&r1=687554&r2=687555&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
>> Wed Aug 20 21:38:50 2008
>> @@ -20,12 +20,15 @@
>>  package org.apache.directory.server.ldap.handlers;
>>   +import java.util.concurrent.TimeUnit;
>> +
>>  import org.apache.directory.server.core.entry.ClonedServerEntry;
>>  import org.apache.directory.server.core.entry.ServerEntryUtils;
>>  import org.apache.directory.server.core.entry.ServerStringValue;
>>  import org.apache.directory.server.core.event.EventType;
>>  import org.apache.directory.server.core.event.NotificationCriteria;
>>  import org.apache.directory.server.core.filtering.EntryFilteringCursor;
>> +import org.apache.directory.server.ldap.LdapServer;
>>  import org.apache.directory.server.ldap.LdapSession;
>>  import org.apache.directory.shared.ldap.codec.util.LdapURL;
>>  import
>> org.apache.directory.shared.ldap.codec.util.LdapURLEncodingException;
>> @@ -36,7 +39,6 @@
>>  import org.apache.directory.shared.ldap.filter.EqualityNode;
>>  import org.apache.directory.shared.ldap.filter.OrNode;
>>  import org.apache.directory.shared.ldap.filter.PresenceNode;
>> -import org.apache.directory.shared.ldap.message.AbandonListener;
>>  import org.apache.directory.shared.ldap.message.LdapResult;
>>  import org.apache.directory.shared.ldap.message.ManageDsaITControl;
>>  import org.apache.directory.shared.ldap.message.PersistentSearchControl;
>> @@ -70,10 +72,20 @@
>>       /** Speedup for logs */
>>     private static final boolean IS_DEBUG = LOG.isDebugEnabled();
>> +
>> +    /** cached to save redundant lookups into registries */ +    private
>> AttributeType objectClassAttributeType;
>>     -    AttributeType objectClassAttributeType;
>>     -    private EqualityNode<String> getOcIsReferralAssertion(
>> LdapSession session ) throws Exception
>> +    /**
>> +     * Constructs a new filter EqualityNode asserting that a candidate +
>>     * objectClass is a referral.
>> +     *
>> +     * @param session the {@link LdapSession} to construct the node for
>> +     * @return the {@link EqualityNode} (objectClass=referral)
>> non-normalized
>> +     * @throws Exception in the highly unlikely event of schema related
>> failures
>> +     */
>> +    private EqualityNode<String> newIsReferralEqualityNode( LdapSession
>> session ) throws Exception
>>     {
>>         if ( objectClassAttributeType == null )
>>         {
>> @@ -81,14 +93,23 @@
>>                 .getAttributeTypeRegistry().lookup(
>> SchemaConstants.OBJECT_CLASS_AT );
>>         }
>>         -        EqualityNode<String> ocIsReferral = new
>> EqualityNode<String>( -            SchemaConstants.OBJECT_CLASS_AT,
>> +        EqualityNode<String> ocIsReferral = new EqualityNode<String>(
>> SchemaConstants.OBJECT_CLASS_AT,
>>             new ServerStringValue( objectClassAttributeType,
>> SchemaConstants.REFERRAL_OC ) );
>>                 return ocIsReferral;
>>     }
>>         +    /**
>> +     * Handles search requests containing the persistent search control
>> but +     * delegates to doSimpleSearch() if the changesOnly parameter of
>> the +     * control is set to false.
>> +     *
>> +     * @param session the LdapSession for which this search is conducted
>> +     * @param req the search request containing the persistent search
>> control
>> +     * @param psearchControl the persistent search control extracted
>> +     * @throws Exception if failures are encountered while searching
>> +     */
>>     private void handlePersistentSearch( LdapSession session,
>> SearchRequest req,         PersistentSearchControl psearchControl ) throws
>> Exception     {
>> @@ -108,7 +129,12 @@
>>             }
>>         }
>>  -        // now we process entries for ever as they change
>> +        if ( req.isAbandoned() )
>> +        {
>> +            return;
>> +        }
>> +        +        // now we process entries forever as they change
>>         PersistentSearchListener handler = new PersistentSearchListener(
>> session, req );
>>                 // compose notification criteria and add the listener to
>> the event @@ -121,15 +147,17 @@
>>         criteria.setScope( req.getScope() );
>>         criteria.setEventMask( EventType.getEventTypes(
>> psearchControl.getChangeTypes() ) );
>>
>> getLdapServer().getDirectoryService().getEventService().addListener(
>> handler, criteria );
>> +        req.addAbandonListener( new SearchAbandonListener( ldapServer,
>> handler ) );
>>         return;
>>     }
>>             /**
>> -     * Deal with RootDE search. -     * @param session
>> -     * @param req
>> -     * @throws Exception
>> +     * Handles search requests on the RootDSE. +     * +     * @param
>> session the LdapSession for which this search is conducted +     * @param
>> req the search request on the RootDSE
>> +     * @throws Exception if failures are encountered while searching
>>      */
>>     private void handleRootDseSearch( LdapSession session, SearchRequest
>> req ) throws Exception
>>     {
>> @@ -147,9 +175,9 @@
>>             {
>>                if ( hasRootDSE )
>>                {
>> -                       // This is an error ! We should never find more
>> -                       // than one rootDSE !
>> -                       // TODO : handle this error
>> +                       // This is an error ! We should never find more
>> than one rootDSE !
>> +                   LOG.error( "Got back more than one entry for search on
>> RootDSE which means " +
>> +                               "Cursor is not functioning properly!" );
>>                }
>>                else
>>                {
>> @@ -181,6 +209,66 @@
>>             /**
>> +     * Based on the server maximum time limits configured for search and
>> the +     * requested time limits this method determines if at all to
>> replace the +     * default ClosureMonitor of the result set Cursor with one
>> that closes
>> +     * the Cursor when either server mandated or request mandated time
>> limits +     * are reached.
>> +     *
>> +     * @param req the {@link SearchRequest} issued
>> +     * @param session the {@link LdapSession} on which search was
>> requested
>> +     * @param cursor the {@link EntryFilteringCursor} over the search
>> results
>> +     */
>> +    private void setTimeLimitsOnCursor( SearchRequest req, LdapSession
>> session, final EntryFilteringCursor cursor )
>> +    {
>> +        // Don't bother setting time limits for administrators
>> +        if ( session.getCoreSession().isAnAdministrator() &&
>> req.getTimeLimit() == 0 )
>> +        {
>> +            return;
>> +        }
>> +        +        /*
>> +         * Non administrator based searches are limited by time if the
>> server +         * has been configured with unlimited time and the request
>> specifies +         * unlimited search time
>> +         */
>> +        if ( ldapServer.getMaxTimeLimit() == LdapServer.NO_TIME_LIMIT &&
>> req.getTimeLimit() == 0 )
>> +        {
>> +            return;
>> +        }
>> +        +        /*
>> +         * If the non-administrator user specifies unlimited time but the
>> server +         * is configured to limit the search time then we limit by
>> the max time +         * allowed by the configuration +         */
>> +        if ( req.getTimeLimit() == 0 )
>> +        {
>> +            cursor.setClosureMonitor( new SearchTimeLimitingMonitor(
>> ldapServer.getMaxTimeLimit(), TimeUnit.SECONDS ) );
>> +            return;
>> +        }
>> +        +        /*
>> +         * If the non-administrative user specifies a time limit equal to
>> or +         * less than the maximum limit configured in the server then we
>> +         * constrain search by the amount specified in the request
>> +         */
>> +        if ( ldapServer.getMaxSizeLimit() >= req.getTimeLimit() )
>> +        {
>> +            cursor.setClosureMonitor( new SearchTimeLimitingMonitor(
>> req.getTimeLimit(), TimeUnit.SECONDS ) );
>> +            return;
>> +        }
>> +
>> +        /*
>> +         * Here the non-administrative user's requested time limit is
>> greater +         * than what the server's configured maximum limit allows
>> so we limit
>> +         * the search to the configured limit
>> +         */
>> +        cursor.setClosureMonitor( new SearchTimeLimitingMonitor(
>> ldapServer.getMaxTimeLimit(), TimeUnit.SECONDS ) );
>> +    }
>> +    +    +    /**
>>      * 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
>> @@ -191,7 +279,8 @@
>>      * @return the result done      * @throws Exception if there are
>> failures while processing the request
>>      */
>> -    private SearchResponseDone doSimpleSearch( LdapSession session,
>> SearchRequest req ) throws Exception
>> +    private SearchResponseDone doSimpleSearch( LdapSession session,
>> SearchRequest req ) +        throws Exception
>>     {
>>         /*
>>          * Iterate through all search results building and sending back
>> responses
>> @@ -202,13 +291,9 @@
>>         try
>>         {
>>             cursor = session.getCoreSession().search( req );
>> +            req.addAbandonListener( new SearchAbandonListener(
>> ldapServer, cursor ) );
>> +            setTimeLimitsOnCursor( req, session, cursor );
>>             -            // TODO - fix this (need to make Cursors
>> abandonable)
>> -            if ( cursor instanceof AbandonListener )
>> -            {
>> -                req.addAbandonListener( ( AbandonListener ) cursor );
>> -            }
>> -                 // Position the cursor at the beginning
>>             cursor.beforeFirst();
>>             @@ -361,7 +446,7 @@
>>         }
>>           // using varags to add two expressions to an OR node -
>>  req.setFilter( new OrNode( req.getFilter(), getOcIsReferralAssertion(
>> session ) ) );
>> +        req.setFilter( new OrNode( req.getFilter(),
>> newIsReferralEqualityNode( session ) ) );
>>     }
>>
>> Added:
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java
>> URL:
>> http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java?rev=687555&view=auto
>>
>> ==============================================================================
>> ---
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java
>> (added)
>> +++
>> directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java
>> Wed Aug 20 21:38:50 2008
>> @@ -0,0 +1,149 @@
>> +/*
>> + *   Licensed to the Apache Software Foundation (ASF) under one
>> + *   or more contributor license agreements.  See the NOTICE file
>> + *   distributed with this work for additional information
>> + *   regarding copyright ownership.  The ASF licenses this file
>> + *   to you under the Apache License, Version 2.0 (the
>> + *   "License"); you may not use this file except in compliance
>> + *   with the License.  You may obtain a copy of the License at
>> + *
>> + *     http://www.apache.org/licenses/LICENSE-2.0
>> + *
>> + *   Unless required by applicable law or agreed to in writing,
>> + *   software distributed under the License is distributed on an
>> + *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>> + *   KIND, either express or implied.  See the License for the
>> + *   specific language governing permissions and limitations
>> + *   under the License.
>> + *
>> + */
>> +package org.apache.directory.server.ldap.handlers;
>> +
>> +
>> +import java.util.concurrent.TimeUnit;
>> +
>> +import org.apache.directory.server.core.cursor.ClosureMonitor;
>> +import org.apache.directory.server.core.cursor.CursorClosedException;
>> +import
>> org.apache.directory.shared.ldap.exception.LdapTimeLimitExceededException;
>> +
>> +
>> +/**
>> + * A ClosureMonitor implementation which takes into account a time limit.
>> + *
>> + * @author <a href="mailto:dev@directory.apache.org">Apache Directory
>> Project</a>
>> + * @version $Rev$, $Date$
>> + */
>> +public class SearchTimeLimitingMonitor implements ClosureMonitor
>> +{
>> +    private final long startTime = System.currentTimeMillis();
>> +    private final long millisToLive;
>> +    +    private boolean closed;
>> +    private Exception cause;
>> +    +    +    public SearchTimeLimitingMonitor( long timeToLive, TimeUnit
>> unit )
>> +    {
>> +        switch ( unit )
>> +        {
>> +            case MICROSECONDS:
>> +                this.millisToLive = timeToLive / 1000;
>> +                break;
>> +            case MILLISECONDS:
>> +                this.millisToLive = timeToLive;
>> +                break;
>> +            case SECONDS:
>> +                this.millisToLive = timeToLive * 1000;
>> +                break;
>> +            case MINUTES:
>> +                this.millisToLive = timeToLive * 60000;
>> +                break;
>> +            default:
>> +                throw new IllegalStateException( "TimeUnit not supported:
>> " + unit );
>> +        }
>> +    }
>> +
>> +    +    public void checkNotClosed() throws Exception
>> +    {
>> +        if ( System.currentTimeMillis() > startTime + millisToLive )
>> +        {
>> +            // state check needed to "try" not to overwrite exception
>> (lack of +            // synchronization may still allow overwriting but who
>> cares that +            // much
>> +            if ( ! closed )
>> +            {
>> +                // not going to sync because who cares if it takes a
>> little +                // longer to stop but we need to set cause before
>> toggling +                // closed state or else check for closure can
>> throw null cause +                cause = new
>> LdapTimeLimitExceededException();
>> +                closed = true;
>> +            }
>> +        }
>> +        +        if ( closed )
>> +        {
>> +            throw cause;
>> +        }
>> +    }
>> +
>> +    +    public void close()
>> +    {
>> +        if ( ! closed )
>> +        {
>> +            // not going to sync because who cares if it takes a little
>> longer +            // to stop but we need to set cause before toggling
>> closed state +            // or else check for closure can throw null cause
>> +            cause = new CursorClosedException();
>> +            closed = true;
>> +        }
>> +    }
>> +
>> +    +    public void close( String cause )
>> +    {
>> +        if ( ! closed )
>> +        {
>> +            // not going to sync because who cares if it takes a little
>> longer +            // to stop but we need to set cause before toggling
>> closed state +            // or else check for closure can throw null cause
>> +            this.cause = new CursorClosedException( cause );
>> +            closed = true;
>> +        }
>> +    }
>> +
>> +    +    public void close( Exception cause )
>> +    {
>> +        if ( ! closed )
>> +        {
>> +            // not going to sync because who cares if it takes a little
>> longer +            // to stop but we need to set cause before toggling
>> closed state +            // or else check for closure can throw null cause
>> +            this.cause = cause;
>> +            closed = true;
>> +        }
>> +    }
>> +
>> +    +    public Exception getCause()
>> +    {
>> +        return cause;
>> +    }
>> +
>> +    +    public boolean isClosed()
>> +    {
>> +        if ( System.currentTimeMillis() > startTime + millisToLive )
>> +        {
>> +            // set cause first always
>> +            cause = new LdapTimeLimitExceededException();
>> +            closed = true;
>> +        }
>> +        +        return closed;
>> +    }
>> +}
>> +
>> +
>>
>>
>>
>>
>>
>
>
> --
> --
> cordialement, regards,
> Emmanuel Lécharny
> www.iktek.com
> directory.apache.org
>
>
>

Re: svn commit: r687555 - in /directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap: ./ handlers/

Posted by Emmanuel Lecharny <el...@gmail.com>.
Hi Alex,

can you add the minimum Javadoc needed for the public methods and for 
the fields of the SearchTimeLimitingMonitor class ?

Thanks !

akarasulu@apache.org wrote:
> Author: akarasulu
> Date: Wed Aug 20 21:38:50 2008
> New Revision: 687555
>
> URL: http://svn.apache.org/viewvc?rev=687555&view=rev
> Log:
> implemented search size limits handling using Cursor ClosureMonitor technique and added abandon handling
>
> Added:
>     directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java
>     directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java
> Modified:
>     directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java
>     directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java
>     directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java
>
> Modified: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java
> URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java?rev=687555&r1=687554&r2=687555&view=diff
> ==============================================================================
> --- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java (original)
> +++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/LdapServer.java Wed Aug 20 21:38:50 2008
> @@ -120,6 +120,12 @@
>      /** The default maximum time limit. */
>      private static final int MAX_TIME_LIMIT_DEFAULT = 10000;
>  
> +    /** Value (0) for configuration where size limit is unlimited. */
> +    public static final int NO_SIZE_LIMIT = 0;
> +
> +    /** Value (0) for configuration where time limit is unlimited. */
> +    public static final int NO_TIME_LIMIT = 0;
> +
>      /** The default service pid. */
>      private static final String SERVICE_PID_DEFAULT = "org.apache.directory.server.ldap";
>  
>
> Modified: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java
> URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java?rev=687555&r1=687554&r2=687555&view=diff
> ==============================================================================
> --- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java (original)
> +++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/LdapRequestHandler.java Wed Aug 20 21:38:50 2008
> @@ -117,6 +117,10 @@
>      public final void messageReceived( IoSession session, T message ) throws Exception
>      {
>          LdapSession ldapSession = ldapServer.getLdapSession( session );
> +        
> +        // TODO - session you get from LdapServer should have the ldapServer 
> +        // member already set no?  Should remove these lines where ever they
> +        // may be if that's the case.
>          ldapSession.setLdapServer( ldapServer );
>          
>          // protect against insecure conns when confidentiality is required 
>
> Added: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java
> URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java?rev=687555&view=auto
> ==============================================================================
> --- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java (added)
> +++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchAbandonListener.java Wed Aug 20 21:38:50 2008
> @@ -0,0 +1,102 @@
> +/*
> + *   Licensed to the Apache Software Foundation (ASF) under one
> + *   or more contributor license agreements.  See the NOTICE file
> + *   distributed with this work for additional information
> + *   regarding copyright ownership.  The ASF licenses this file
> + *   to you under the Apache License, Version 2.0 (the
> + *   "License"); you may not use this file except in compliance
> + *   with the License.  You may obtain a copy of the License at
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + *   Unless required by applicable law or agreed to in writing,
> + *   software distributed under the License is distributed on an
> + *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + *   KIND, either express or implied.  See the License for the
> + *   specific language governing permissions and limitations
> + *   under the License.
> + *
> + */
> +package org.apache.directory.server.ldap.handlers;
> +
> +
> +import org.apache.directory.server.core.event.DirectoryListener;
> +import org.apache.directory.server.core.filtering.EntryFilteringCursor;
> +import org.apache.directory.server.ldap.LdapServer;
> +import org.apache.directory.shared.ldap.exception.OperationAbandonedException;
> +import org.apache.directory.shared.ldap.message.AbandonListener;
> +import org.apache.directory.shared.ldap.message.AbandonableRequest;
> +import org.slf4j.Logger;
> +import org.slf4j.LoggerFactory;
> +
> +
> +/**
> + * An AbandonListener implementation which closes an associated cursor or 
> + * removes a DirectoryListener.
> + *
> + * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
> + * @version $Rev$, $Date$
> + */
> +public class SearchAbandonListener implements AbandonListener
> +{
> +    private static final Logger LOG = LoggerFactory.getLogger( SearchAbandonListener.class );
> +    private final LdapServer ldapServer;
> +    private EntryFilteringCursor cursor;
> +    private DirectoryListener listener;
> +    
> +    
> +    public SearchAbandonListener( LdapServer ldapServer, EntryFilteringCursor cursor, DirectoryListener listener )
> +    {
> +        if ( ldapServer == null )
> +        {
> +            throw new NullPointerException( "ldapServer" );
> +        }
> +        
> +        this.ldapServer = ldapServer;
> +        this.cursor = cursor;
> +        this.listener = listener;
> +    }
> +    
> +    
> +    public SearchAbandonListener( LdapServer ldapServer, DirectoryListener listener )
> +    {
> +        this ( ldapServer, null, listener );
> +    }
> +    
> +    
> +    public SearchAbandonListener( LdapServer ldapServer, EntryFilteringCursor cursor )
> +    {
> +        this ( ldapServer, cursor, null );
> +    }
> +    
> +    
> +    public void requestAbandoned( AbandonableRequest req )
> +    {
> +        if ( listener != null )
> +        {
> +            ldapServer.getDirectoryService().getEventService().removeListener( listener );
> +        }
> +
> +        try
> +        {
> +            if ( cursor != null )
> +            {
> +                /*
> +                 * When this method is called due to an abandon request it 
> +                 * will close the cursor but other threads processing the 
> +                 * search will get an OperationAbandonedException which as
> +                 * seen below will make sure the proper handling is 
> +                 * performed.
> +                 */
> +                cursor.close( new OperationAbandonedException() );
> +            }
> +        }
> +        catch ( Exception e )
> +        {
> +            LOG.error( "Failed to close the search cursor for message {} on abandon request.", 
> +                req.getMessageId(), e );
> +        }
> +    }
> +}
> +
> +
>
> 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=687555&r1=687554&r2=687555&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 Wed Aug 20 21:38:50 2008
> @@ -20,12 +20,15 @@
>  package org.apache.directory.server.ldap.handlers;
>  
>  
> +import java.util.concurrent.TimeUnit;
> +
>  import org.apache.directory.server.core.entry.ClonedServerEntry;
>  import org.apache.directory.server.core.entry.ServerEntryUtils;
>  import org.apache.directory.server.core.entry.ServerStringValue;
>  import org.apache.directory.server.core.event.EventType;
>  import org.apache.directory.server.core.event.NotificationCriteria;
>  import org.apache.directory.server.core.filtering.EntryFilteringCursor;
> +import org.apache.directory.server.ldap.LdapServer;
>  import org.apache.directory.server.ldap.LdapSession;
>  import org.apache.directory.shared.ldap.codec.util.LdapURL;
>  import org.apache.directory.shared.ldap.codec.util.LdapURLEncodingException;
> @@ -36,7 +39,6 @@
>  import org.apache.directory.shared.ldap.filter.EqualityNode;
>  import org.apache.directory.shared.ldap.filter.OrNode;
>  import org.apache.directory.shared.ldap.filter.PresenceNode;
> -import org.apache.directory.shared.ldap.message.AbandonListener;
>  import org.apache.directory.shared.ldap.message.LdapResult;
>  import org.apache.directory.shared.ldap.message.ManageDsaITControl;
>  import org.apache.directory.shared.ldap.message.PersistentSearchControl;
> @@ -70,10 +72,20 @@
>  
>      /** Speedup for logs */
>      private static final boolean IS_DEBUG = LOG.isDebugEnabled();
> +
> +    /** cached to save redundant lookups into registries */ 
> +    private AttributeType objectClassAttributeType;
>      
> -    AttributeType objectClassAttributeType;
>      
> -    private EqualityNode<String> getOcIsReferralAssertion( LdapSession session ) throws Exception
> +    /**
> +     * Constructs a new filter EqualityNode asserting that a candidate 
> +     * objectClass is a referral.
> +     *
> +     * @param session the {@link LdapSession} to construct the node for
> +     * @return the {@link EqualityNode} (objectClass=referral) non-normalized
> +     * @throws Exception in the highly unlikely event of schema related failures
> +     */
> +    private EqualityNode<String> newIsReferralEqualityNode( LdapSession session ) throws Exception
>      {
>          if ( objectClassAttributeType == null )
>          {
> @@ -81,14 +93,23 @@
>                  .getAttributeTypeRegistry().lookup( SchemaConstants.OBJECT_CLASS_AT );
>          }
>          
> -        EqualityNode<String> ocIsReferral = new EqualityNode<String>( 
> -            SchemaConstants.OBJECT_CLASS_AT,
> +        EqualityNode<String> ocIsReferral = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT,
>              new ServerStringValue( objectClassAttributeType, SchemaConstants.REFERRAL_OC ) );
>          
>          return ocIsReferral;
>      }
>      
>      
> +    /**
> +     * Handles search requests containing the persistent search control but 
> +     * delegates to doSimpleSearch() if the changesOnly parameter of the 
> +     * control is set to false.
> +     *
> +     * @param session the LdapSession for which this search is conducted 
> +     * @param req the search request containing the persistent search control
> +     * @param psearchControl the persistent search control extracted
> +     * @throws Exception if failures are encountered while searching
> +     */
>      private void handlePersistentSearch( LdapSession session, SearchRequest req, 
>          PersistentSearchControl psearchControl ) throws Exception 
>      {
> @@ -108,7 +129,12 @@
>              }
>          }
>  
> -        // now we process entries for ever as they change
> +        if ( req.isAbandoned() )
> +        {
> +            return;
> +        }
> +        
> +        // now we process entries forever as they change
>          PersistentSearchListener handler = new PersistentSearchListener( session, req );
>          
>          // compose notification criteria and add the listener to the event 
> @@ -121,15 +147,17 @@
>          criteria.setScope( req.getScope() );
>          criteria.setEventMask( EventType.getEventTypes( psearchControl.getChangeTypes() ) );
>          getLdapServer().getDirectoryService().getEventService().addListener( handler, criteria );
> +        req.addAbandonListener( new SearchAbandonListener( ldapServer, handler ) );
>          return;
>      }
>      
>      
>      /**
> -     * Deal with RootDE search. 
> -     * @param session
> -     * @param req
> -     * @throws Exception
> +     * Handles search requests on the RootDSE. 
> +     * 
> +     * @param session the LdapSession for which this search is conducted 
> +     * @param req the search request on the RootDSE
> +     * @throws Exception if failures are encountered while searching
>       */
>      private void handleRootDseSearch( LdapSession session, SearchRequest req ) throws Exception
>      {
> @@ -147,9 +175,9 @@
>              {
>              	if ( hasRootDSE )
>              	{
> -            		// This is an error ! We should never find more
> -            		// than one rootDSE !
> -            		// TODO : handle this error
> +            		// This is an error ! We should never find more than one rootDSE !
> +            	    LOG.error( "Got back more than one entry for search on RootDSE which means " +
> +            	    		"Cursor is not functioning properly!" );
>              	}
>              	else
>              	{
> @@ -181,6 +209,66 @@
>      
>      
>      /**
> +     * Based on the server maximum time limits configured for search and the 
> +     * requested time limits this method determines if at all to replace the 
> +     * default ClosureMonitor of the result set Cursor with one that closes
> +     * the Cursor when either server mandated or request mandated time limits 
> +     * are reached.
> +     *
> +     * @param req the {@link SearchRequest} issued
> +     * @param session the {@link LdapSession} on which search was requested
> +     * @param cursor the {@link EntryFilteringCursor} over the search results
> +     */
> +    private void setTimeLimitsOnCursor( SearchRequest req, LdapSession session, final EntryFilteringCursor cursor )
> +    {
> +        // Don't bother setting time limits for administrators
> +        if ( session.getCoreSession().isAnAdministrator() && req.getTimeLimit() == 0 )
> +        {
> +            return;
> +        }
> +        
> +        /*
> +         * Non administrator based searches are limited by time if the server 
> +         * has been configured with unlimited time and the request specifies 
> +         * unlimited search time
> +         */
> +        if ( ldapServer.getMaxTimeLimit() == LdapServer.NO_TIME_LIMIT && req.getTimeLimit() == 0 )
> +        {
> +            return;
> +        }
> +        
> +        /*
> +         * If the non-administrator user specifies unlimited time but the server 
> +         * is configured to limit the search time then we limit by the max time 
> +         * allowed by the configuration 
> +         */
> +        if ( req.getTimeLimit() == 0 )
> +        {
> +            cursor.setClosureMonitor( new SearchTimeLimitingMonitor( ldapServer.getMaxTimeLimit(), TimeUnit.SECONDS ) );
> +            return;
> +        }
> +        
> +        /*
> +         * If the non-administrative user specifies a time limit equal to or 
> +         * less than the maximum limit configured in the server then we 
> +         * constrain search by the amount specified in the request
> +         */
> +        if ( ldapServer.getMaxSizeLimit() >= req.getTimeLimit() )
> +        {
> +            cursor.setClosureMonitor( new SearchTimeLimitingMonitor( req.getTimeLimit(), TimeUnit.SECONDS ) );
> +            return;
> +        }
> +
> +        /*
> +         * Here the non-administrative user's requested time limit is greater 
> +         * than what the server's configured maximum limit allows so we limit
> +         * the search to the configured limit
> +         */
> +        cursor.setClosureMonitor( new SearchTimeLimitingMonitor( ldapServer.getMaxTimeLimit(), TimeUnit.SECONDS ) );
> +    }
> +    
> +    
> +    /**
>       * 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
> @@ -191,7 +279,8 @@
>       * @return the result done 
>       * @throws Exception if there are failures while processing the request
>       */
> -    private SearchResponseDone doSimpleSearch( LdapSession session, SearchRequest req ) throws Exception
> +    private SearchResponseDone doSimpleSearch( LdapSession session, SearchRequest req ) 
> +        throws Exception
>      {
>          /*
>           * Iterate through all search results building and sending back responses
> @@ -202,13 +291,9 @@
>          try
>          {
>              cursor = session.getCoreSession().search( req );
> +            req.addAbandonListener( new SearchAbandonListener( ldapServer, cursor ) );
> +            setTimeLimitsOnCursor( req, session, cursor );
>              
> -            // TODO - fix this (need to make Cursors abandonable)
> -            if ( cursor instanceof AbandonListener )
> -            {
> -                req.addAbandonListener( ( AbandonListener ) cursor );
> -            }
> -    
>              // Position the cursor at the beginning
>              cursor.beforeFirst();
>              
> @@ -361,7 +446,7 @@
>          }
>  
>          // using varags to add two expressions to an OR node 
> -        req.setFilter( new OrNode( req.getFilter(), getOcIsReferralAssertion( session ) ) );
> +        req.setFilter( new OrNode( req.getFilter(), newIsReferralEqualityNode( session ) ) );
>      }
>      
>      
>
> Added: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java
> URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java?rev=687555&view=auto
> ==============================================================================
> --- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java (added)
> +++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchTimeLimitingMonitor.java Wed Aug 20 21:38:50 2008
> @@ -0,0 +1,149 @@
> +/*
> + *   Licensed to the Apache Software Foundation (ASF) under one
> + *   or more contributor license agreements.  See the NOTICE file
> + *   distributed with this work for additional information
> + *   regarding copyright ownership.  The ASF licenses this file
> + *   to you under the Apache License, Version 2.0 (the
> + *   "License"); you may not use this file except in compliance
> + *   with the License.  You may obtain a copy of the License at
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + *   Unless required by applicable law or agreed to in writing,
> + *   software distributed under the License is distributed on an
> + *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + *   KIND, either express or implied.  See the License for the
> + *   specific language governing permissions and limitations
> + *   under the License.
> + *
> + */
> +package org.apache.directory.server.ldap.handlers;
> +
> +
> +import java.util.concurrent.TimeUnit;
> +
> +import org.apache.directory.server.core.cursor.ClosureMonitor;
> +import org.apache.directory.server.core.cursor.CursorClosedException;
> +import org.apache.directory.shared.ldap.exception.LdapTimeLimitExceededException;
> +
> +
> +/**
> + * A ClosureMonitor implementation which takes into account a time limit.
> + *
> + * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
> + * @version $Rev$, $Date$
> + */
> +public class SearchTimeLimitingMonitor implements ClosureMonitor
> +{
> +    private final long startTime = System.currentTimeMillis();
> +    private final long millisToLive;
> +    
> +    private boolean closed;
> +    private Exception cause;
> +    
> +    
> +    public SearchTimeLimitingMonitor( long timeToLive, TimeUnit unit )
> +    {
> +        switch ( unit )
> +        {
> +            case MICROSECONDS:
> +                this.millisToLive = timeToLive / 1000;
> +                break;
> +            case MILLISECONDS:
> +                this.millisToLive = timeToLive;
> +                break;
> +            case SECONDS:
> +                this.millisToLive = timeToLive * 1000;
> +                break;
> +            case MINUTES:
> +                this.millisToLive = timeToLive * 60000;
> +                break;
> +            default:
> +                throw new IllegalStateException( "TimeUnit not supported: " + unit );
> +        }
> +    }
> +
> +    
> +    public void checkNotClosed() throws Exception
> +    {
> +        if ( System.currentTimeMillis() > startTime + millisToLive )
> +        {
> +            // state check needed to "try" not to overwrite exception (lack of 
> +            // synchronization may still allow overwriting but who cares that 
> +            // much
> +            if ( ! closed )
> +            {
> +                // not going to sync because who cares if it takes a little 
> +                // longer to stop but we need to set cause before toggling 
> +                // closed state or else check for closure can throw null cause 
> +                cause = new LdapTimeLimitExceededException();
> +                closed = true;
> +            }
> +        }
> +        
> +        if ( closed )
> +        {
> +            throw cause;
> +        }
> +    }
> +
> +    
> +    public void close()
> +    {
> +        if ( ! closed )
> +        {
> +            // not going to sync because who cares if it takes a little longer 
> +            // to stop but we need to set cause before toggling closed state 
> +            // or else check for closure can throw null cause 
> +            cause = new CursorClosedException();
> +            closed = true;
> +        }
> +    }
> +
> +    
> +    public void close( String cause )
> +    {
> +        if ( ! closed )
> +        {
> +            // not going to sync because who cares if it takes a little longer 
> +            // to stop but we need to set cause before toggling closed state 
> +            // or else check for closure can throw null cause 
> +            this.cause = new CursorClosedException( cause );
> +            closed = true;
> +        }
> +    }
> +
> +    
> +    public void close( Exception cause )
> +    {
> +        if ( ! closed )
> +        {
> +            // not going to sync because who cares if it takes a little longer 
> +            // to stop but we need to set cause before toggling closed state 
> +            // or else check for closure can throw null cause 
> +            this.cause = cause;
> +            closed = true;
> +        }
> +    }
> +
> +    
> +    public Exception getCause()
> +    {
> +        return cause;
> +    }
> +
> +    
> +    public boolean isClosed()
> +    {
> +        if ( System.currentTimeMillis() > startTime + millisToLive )
> +        {
> +            // set cause first always
> +            cause = new LdapTimeLimitExceededException();
> +            closed = true;
> +        }
> +        
> +        return closed;
> +    }
> +}
> +
> +
>
>
>
>   


-- 
--
cordialement, regards,
Emmanuel Lécharny
www.iktek.com
directory.apache.org