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