Ok, I have a better understanding on what's going on.
On the server, when we process a searchRequest, we loop on the created cursor until we have no more elements to return. This is done by the SearchRequestHandler.writeResults() method :
while ( ( count < sizeLimit ) && cursor.next() )
If, for any reason, the abandonRequest is received and processed at the very same time, what will happen is that the cursor will be closed. We do check this condition :
public boolean next() throws LdapException, CursorException, IOException
checkNotClosed( "next()" );
public final void checkNotClosed( String operation ) throws CursorClosedException
public void checkNotClosed() throws CursorClosedException
// lack of synchronization may cause pass but eventually it will work
if ( closed )
throw new CursorClosedException( cause.getMessage() );
So basically, we get an exception instead of something easier to handle.
It's not happening very often, but still, with 1000 loop, it's almost guaranteed to happen.
As a side effect, we generate an exception which has no cause, which leads to a NPE later in the SearchRequestHandler.handleException() method :
public void handleException( LdapSession session, ResultResponseRequest req, Exception e )
LdapResult result = req.getResultResponse().getLdapResult();
- Set the result code or guess the best option.
if ( e instanceof CursorClosedException )
e = ( Exception ) ( ( CursorClosedException ) e ).getCause(); <<<-------------- Bad ! We should not overwrite the exception parameter here...
Here, e becomes null and it shows in the trace because of :
String msg = code.toString() + ": failed for " + req + ": " + e.getLocalizedMessage(); <<<--- NPE
In any case, we should not throw an exception when the cursor is simply closed because of an abandon request, we should instead return false to a call to cursor.next().
There is a bit of work to fix that, but it's not really complicated.