directory-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From akaras...@apache.org
Subject svn commit: r366037 - in /directory/trunk: apacheds-server-main/ apacheds-server-unit/src/test/java/org/apache/ldap/server/ ldap-common/src/main/java/org/apache/ldap/common/ldif/ ldap-common/src/main/java/org/apache/ldap/common/message/ ldap-common/src...
Date Thu, 05 Jan 2006 00:43:32 GMT
Author: akarasulu
Date: Wed Jan  4 16:43:21 2006
New Revision: 366037

URL: http://svn.apache.org/viewcvs?rev=366037&view=rev
Log:
added some psearch functionality

Added:
    directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java
  (with props)
Modified:
    directory/trunk/apacheds-server-main/server.xml
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/ldif/LdifEntry.java
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/message/ControlImpl.java
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/message/PersistentSearchControl.java
    directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/name/LdapName.java
    directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/SessionRegistry.java
    directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java

Modified: directory/trunk/apacheds-server-main/server.xml
URL: http://svn.apache.org/viewcvs/directory/trunk/apacheds-server-main/server.xml?rev=366037&r1=366036&r2=366037&view=diff
==============================================================================
--- directory/trunk/apacheds-server-main/server.xml (original)
+++ directory/trunk/apacheds-server-main/server.xml Wed Jan  4 16:43:21 2006
@@ -7,7 +7,7 @@
   <bean id="environment" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
     <property name="properties">
       <props>
-        <prop key="asn.1.berlib.provider">org.apache.ldap.common.asn1.TwixProvider</prop>
+        <prop key="asn.1.berlib.provider">org.apache.ldap.common.codec.TwixProvider</prop>
         <prop key="java.naming.security.authentication">simple</prop>
         <prop key="java.naming.security.principal">uid=admin,ou=system</prop>
         <prop key="java.naming.security.credentials">secret</prop>

Added: directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java
URL: http://svn.apache.org/viewcvs/directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java?rev=366037&view=auto
==============================================================================
--- directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java
(added)
+++ directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java
Wed Jan  4 16:43:21 2006
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2004 Solarsis Group LLC.
+ *
+ * Licensed under the Open Software License, Version 2.1 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://opensource.org/licenses/osl-2.1.php
+ *
+ * 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.ldap.server;
+
+
+import java.util.Hashtable;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+
+import org.apache.ldap.common.message.PersistentSearchControl;
+
+
+/**
+ * Testcase which tests the correct operation of the persistent search control.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class PersistentSearchTest extends AbstractServerTest
+{
+    private LdapContext ctx = null;
+
+    public static final String RDN = "cn=Tori Amos";
+
+    public static final String PERSON_DESCRIPTION = "an American singer-songwriter";
+
+
+    /**
+     * Creation of required attributes of a person entry.
+     */
+    protected Attributes getPersonAttributes( String sn, String cn )
+    {
+        Attributes attributes = new BasicAttributes();
+        Attribute attribute = new BasicAttribute( "objectClass" );
+        attribute.add( "top" );
+        attribute.add( "person" );
+        attributes.put( attribute );
+        attributes.put( "cn", cn );
+        attributes.put( "sn", sn );
+
+        return attributes;
+    }
+
+
+    /**
+     * Create context and a person entry.
+     */
+    public void setUp() throws Exception
+    {
+        super.setUp();
+
+        Hashtable env = new Hashtable();
+        env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
+        env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" );
+        env.put( "java.naming.security.principal", "uid=admin,ou=system" );
+        env.put( "java.naming.security.credentials", "secret" );
+        env.put( "java.naming.security.authentication", "simple" );
+
+        ctx = new InitialLdapContext( env, null );
+        assertNotNull( ctx );
+
+        // Create a person with description
+        Attributes attributes = this.getPersonAttributes( "Amos", "Tori Amos" );
+        attributes.put( "description", PERSON_DESCRIPTION );
+        ctx.createSubcontext( RDN, attributes );
+    }
+
+
+    /**
+     * Remove person entry and close context.
+     */
+    public void tearDown() throws Exception
+    {
+        ctx.unbind( RDN );
+        ctx.close();
+
+        ctx.close();
+        ctx = null;
+
+        super.tearDown();
+    }
+
+
+    public void testPsearchSend() throws Exception
+    {
+        PSearchListener listener = new PSearchListener();
+        Thread t = new Thread( listener );
+        t.start();
+        
+        Thread.sleep( 3000 );
+
+        System.out.println( "--------------------------------------------------------------"
);
+        ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, 
+            new BasicAttributes( "description", PERSON_DESCRIPTION, true ) );
+        Thread.sleep( 3000 );
+        
+        // Create a person with description
+        System.out.println( "--------------------------------------------------------------"
);
+        Attributes attributes = this.getPersonAttributes( "Black", "Jack Black" );
+        attributes.put( "description", PERSON_DESCRIPTION );
+        ctx.createSubcontext( "cn=Jack Black", attributes );
+        Thread.sleep( 3000 );
+    }
+    
+    
+    class PSearchListener implements Runnable
+    {
+        public void run()
+        {
+            PersistentSearchControl control = new PersistentSearchControl();
+            control.setCritical( true );
+            Control[] ctxCtls = new Control[] { control };
+            
+            try
+            {
+                ctx.setRequestControls( ctxCtls );
+                NamingEnumeration list = ctx.search( "", "objectClass=*", null );
+                while( list.hasMore() )
+                {
+                    SearchResult result = ( SearchResult ) list.next();
+                    System.out.print( "got entry: " + result );
+                }
+            }
+            catch( Exception e ) 
+            {
+                e.printStackTrace();
+            }
+        }
+    }
+}

Propchange: directory/trunk/apacheds-server-unit/src/test/java/org/apache/ldap/server/PersistentSearchTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/ldif/LdifEntry.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/ldif/LdifEntry.java?rev=366037&r1=366036&r2=366037&view=diff
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/ldif/LdifEntry.java (original)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/ldif/LdifEntry.java Wed
Jan  4 16:43:21 2006
@@ -47,9 +47,6 @@
     /** the version of the ldif */
     private int m_version ;
 
-    /** the control attribute list */
-    private LinkedList m_controlList ;
-
     /** the modification type */
     private String m_modType ;
 
@@ -68,7 +65,6 @@
      */
     public LdifEntry()
     {
-        m_controlList = new LinkedList() ;
         m_modType = "add" ; // Default LDIF content
         m_itemList = new LinkedList() ;
         m_dn = null ;
@@ -190,5 +186,11 @@
     public String getDn()
     {
         return m_dn ;
+    }
+
+
+    public int getVersion()
+    {
+        return this.m_version;
     }
 }

Modified: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/message/ControlImpl.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/message/ControlImpl.java?rev=366037&r1=366036&r2=366037&view=diff
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/message/ControlImpl.java
(original)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/message/ControlImpl.java
Wed Jan  4 16:43:21 2006
@@ -35,7 +35,6 @@
     private byte [] value ;
     /** Flag for control criticality */
     private boolean isCritical ;
-    private String id;
 
 
     // ------------------------------------------------------------------------
@@ -152,6 +151,6 @@
       */
     public String getID()
     {
-        return id;
+        return this.oid;
     }
 }

Modified: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/message/PersistentSearchControl.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/message/PersistentSearchControl.java?rev=366037&r1=366036&r2=366037&view=diff
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/message/PersistentSearchControl.java
(original)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/message/PersistentSearchControl.java
Wed Jan  4 16:43:21 2006
@@ -16,6 +16,11 @@
  */
 package org.apache.ldap.common.message;
 
+import org.apache.asn1.codec.EncoderException;
+import org.apache.ldap.common.codec.search.controls.PSearchControl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 
 /**
  * The control for a persistent search operation.
@@ -26,7 +31,9 @@
 public class PersistentSearchControl extends ControlImpl
 {
     private static final long serialVersionUID = -2356861450876343999L;
-
+    private static final Logger log = LoggerFactory.getLogger( PersistentSearchControl.class
);
+    public static final String CONTROL_ID = "2.16.840.1.113730.3.4.3";
+    
     /** 
      * If changesOnly is TRUE, the server MUST NOT return any existing
      * entries that match the search criteria.  Entries are only
@@ -52,13 +59,14 @@
      */
     private int changeTypes;
 
-    
-    public byte[] getEncodedValue()
+
+    public PersistentSearchControl()
     {
-        return null;
+        super();
+        setType( CONTROL_ID );
     }
-
-
+    
+    
     public void setChangesOnly( boolean changesOnly )
     {
         this.changesOnly = changesOnly;
@@ -92,5 +100,28 @@
     public int getChangeTypes()
     {
         return changeTypes;
+    }
+
+
+    public byte[] getEncodedValue()
+    {
+        if ( getValue() == null )
+        {
+            PSearchControl psearchCtl = new PSearchControl();
+            psearchCtl.setChangesOnly( isChangesOnly() );
+            psearchCtl.setChangeTypes( getChangeTypes() );
+            psearchCtl.setReturnECs( isReturnECs() );
+
+            try
+            {
+                setValue( psearchCtl.encode( null ).array() );
+            }
+            catch ( EncoderException e )
+            {
+                log.error( "Failed to encode psearch control", e );
+            }
+        }
+        
+        return getValue();
     }
 }

Modified: directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/name/LdapName.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/name/LdapName.java?rev=366037&r1=366036&r2=366037&view=diff
==============================================================================
--- directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/name/LdapName.java (original)
+++ directory/trunk/ldap-common/src/main/java/org/apache/ldap/common/name/LdapName.java Wed
Jan  4 16:43:21 2006
@@ -928,11 +928,7 @@
         return a_dn.get( a_dn.size() - 1 ) ;
     }
     
-    private static String getRdn( String nameComponent, int start )
-    {
-    	return "";
-    }
-    
+
     public static LdapName toOidName( LdapName dn, Map oids ) throws InvalidNameException
     {
     	if ( ( dn == null ) || ( dn.size() == 0 ) )

Modified: directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/SessionRegistry.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/SessionRegistry.java?rev=366037&r1=366036&r2=366037&view=diff
==============================================================================
--- directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/SessionRegistry.java
(original)
+++ directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/SessionRegistry.java
Wed Jan  4 16:43:21 2006
@@ -17,6 +17,7 @@
 package org.apache.ldap.server.protocol;
 
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Map;
@@ -29,6 +30,7 @@
 import javax.naming.spi.InitialContextFactory;
 
 import org.apache.ldap.common.exception.LdapNoPermissionException;
+import org.apache.ldap.common.message.Request;
 import org.apache.ldap.server.configuration.Configuration;
 import org.apache.ldap.server.configuration.StartupConfiguration;
 import org.apache.ldap.server.jndi.ServerLdapContext;
@@ -48,6 +50,9 @@
 
     /** the set of client contexts */
     private final Map contexts = new HashMap();
+    
+    /** outstanding requests for a session */
+    private final Map requests = new HashMap();
 
     /** the properties associated with this SessionRegistry */
     private Hashtable env;
@@ -91,15 +96,12 @@
         if ( env == null )
         {
             this.env = new Hashtable();
-
             this.env.put( Context.PROVIDER_URL, "" );
-
             this.env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.ldap.server.jndi.ServerContextFactory"
);
         }
         else
         {
             this.env = env;
-
             this.env.put( Context.PROVIDER_URL, "" );
         }
     }
@@ -117,9 +119,81 @@
 
 
     /**
+     * Adds a request to the map of outstanding requests for a session.
+     * 
+     * @param session the session the request was issued on
+     * @param req the request to add
+     */
+    public void addOutstandingRequest( IoSession session, Request req )
+    {
+        // pull out the map of requests by id
+        synchronized ( requests )
+        {
+            Map reqmap = ( Map ) requests.get( session );
+            if ( reqmap == null )
+            {
+                reqmap = new HashMap();
+            }
+            reqmap.put( new Integer( req.getMessageId() ), req );
+        }
+    }
+    
+
+    /**
+     * Removes an outstanding request from the session's outstanding request map.
+     * 
+     * @param session the session the request is removed from
+     * @param id the messageId of the request to remove
+     * @return the Request if it is removed or null if no such request was mapped as outstanding
+     */
+    public Request removeOutstandingRequest( IoSession session, Integer id )
+    {
+        // pull out the map of requests by id
+        synchronized ( requests )
+        {
+            Map reqmap = ( Map ) requests.get( session );
+            if ( reqmap == null ) return null;
+            return ( Request ) reqmap.remove( id );
+        }
+    }
+
+    
+    /**
+     * Returns a shallow copied map of all outstanding requests for an IoSession.
+     * 
+     * @param session the session to get outstanding requests for
+     * @return a map by message id as an Integer to Request objects
+     */
+    public Map getOutstandingRequests( IoSession session )
+    {
+        Map reqmap = ( Map ) requests.get( session );
+        if ( reqmap == null )
+        {
+            return Collections.EMPTY_MAP;
+        }
+        return new HashMap( reqmap );
+    }
+    
+    
+    /**
+     * Gets an outstanding request by messageId for a session.
+     * 
+     * @param session the LDAP session 
+     * @param id the message id of the request
+     * @return the request in session for id or null if request has completed
+     */
+    public Request getOutstandingRequest( IoSession session, Integer id )
+    {
+        Map reqmap = ( Map ) requests.get( session );
+        if ( reqmap == null ) return null;
+        return ( Request ) reqmap.get( id );
+    }
+    
+    
+    /**
      * Gets the InitialContext to the root of the system that was gotten for
      * client.  If the context is not present then there was no bind operation
-     * that set it.  Hence this operation requesting the IC is anonymous.
+     * that set it.  Hence this operation requesting the context is anonymous.
      *
      * @todo this allowAnonymous parameter is a bit confusing - figure out
      * something better to call it.  I think only bind requests a context

Modified: directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java
URL: http://svn.apache.org/viewcvs/directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java?rev=366037&r1=366036&r2=366037&view=diff
==============================================================================
--- directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java
(original)
+++ directory/trunk/ldap-protocol/src/main/java/org/apache/ldap/server/protocol/support/SearchHandler.java
Wed Jan  4 16:43:21 2006
@@ -21,16 +21,26 @@
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
+import javax.naming.Binding;
+import javax.naming.Context;
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
 import javax.naming.directory.SearchControls;
 import javax.naming.directory.SearchResult;
+import javax.naming.event.NamespaceChangeListener;
+import javax.naming.event.NamingEvent;
+import javax.naming.event.NamingExceptionEvent;
+import javax.naming.event.NamingListener;
+import javax.naming.event.ObjectChangeListener;
 import javax.naming.ldap.LdapContext;
 
 import org.apache.ldap.common.exception.LdapException;
 import org.apache.ldap.common.filter.PresenceNode;
+import org.apache.ldap.common.message.Control;
 import org.apache.ldap.common.message.LdapResultImpl;
+import org.apache.ldap.common.message.PersistentSearchControl;
 import org.apache.ldap.common.message.ReferralImpl;
 import org.apache.ldap.common.message.ResultCodeEnum;
 import org.apache.ldap.common.message.ScopeEnum;
@@ -53,6 +63,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.sun.corba.se.spi.ior.iiop.JavaCodebaseComponent;
+
 /**
  * A handler for processing search requests.
  *
@@ -61,13 +73,13 @@
  */
 public class SearchHandler implements MessageHandler
 {
-    private static final Logger LOG = LoggerFactory.getLogger( SearchHandler.class );
+    private static final Logger log = LoggerFactory.getLogger( SearchHandler.class );
     private static final String DEREFALIASES_KEY = "java.naming.ldap.derefAliases";
 
 
     public void messageReceived( IoSession session, Object request )
     {
-        LdapContext ctx;
+        ServerLdapContext ctx;
         SearchRequest req = ( SearchRequest ) request;
         NamingEnumeration list = null;
 
@@ -109,17 +121,28 @@
             // bypass checks to disallow anonymous binds for search on RootDSE with base
obj scope
             if ( isRootDSESearch )
             {
-                ctx = SessionRegistry.getSingleton().getLdapContextOnRootDSEAccess( session,
null );
+                LdapContext unknown = SessionRegistry.getSingleton().getLdapContextOnRootDSEAccess(
session, null );
+                if ( ! ( unknown instanceof ServerLdapContext ) )
+                {
+                    ctx = ( ServerLdapContext ) unknown.lookup( "" );
+                }
+                else
+                {
+                    ctx = ( ServerLdapContext ) unknown;
+                }
             }
             // all those search operations are subject to anonymous bind checks when anonymous
binda are disallowed
             else
             {
-                ctx = ( LdapContext ) SessionRegistry.getSingleton().getLdapContext( session,
null, true ).lookup( "" );
-            }
-
-            if ( ! ( ctx instanceof ServerLdapContext ) )
-            {
-                ctx = ( ServerLdapContext ) ctx.lookup( "" );
+                LdapContext unknown = SessionRegistry.getSingleton().getLdapContext( session,
null, true );
+                if ( ! ( unknown instanceof ServerLdapContext ) )
+                {
+                    ctx = ( ServerLdapContext ) unknown.lookup( "" );
+                }
+                else
+                {
+                    ctx = ( ServerLdapContext ) unknown;
+                }
             }
 
             StartupConfiguration cfg = ( StartupConfiguration ) Configuration.toConfiguration(
ctx.getEnvironment() );
@@ -138,9 +161,43 @@
                 return;
             }
 
+            /*
+             * Persistent Search Implementation
+             * --------------------------------
+             * 
+             * The persistent search implementation uses the event notification scheme build
into
+             * the core and exposes access to it via the JNDI EventContext.  A psearch will
not 
+             * return until an abandon request cancels it.  Hence time and size limits as
weill normal
+             * search operations do not apply.  In this handler we simply setup the structures
to 
+             * trickle back PDUs as we recieve event notifications using this IoSession.
 
+             * 
+             * We need structures in Ldap p-p to track outstanding operations to implement
AbandonOperations.
+             */
+            PersistentSearchControl psearchControl = getPersistentSearchControl( req );
+            if ( psearchControl != null )
+            {
+                if ( psearchControl.isReturnECs() )
+                {
+                    SearchResponseDone resp = new SearchResponseDoneImpl( req.getMessageId()
);
+                    LdapResultImpl result = new LdapResultImpl( resp );
+                    resp.setLdapResult( result );
+                    result.setResultCode( ResultCodeEnum.UNAVAILABLECRITICALEXTENSION );
+                    String msg = "EntryChangeNotification response controls not implemented
yet!";
+                    log.error( msg );
+                    result.setErrorMessage( msg );
+                    session.write( resp );
+                    return;
+                }
+
+                PersistentSearchHandler handler = new PersistentSearchHandler( ctx, session,
req );
+                StringBuffer buf = new StringBuffer();
+                req.getFilter().printToBuffer( buf );
+                ctx.addNamingListener( req.getBase(), buf.toString(), controls, handler );
+                return;
+            }
+            
             ctx.addToEnvironment( DEREFALIASES_KEY, req.getDerefAliases().getName() );
             list = ( ( ServerLdapContext ) ctx ).search( new LdapName( req.getBase() ), req.getFilter(),
controls );
-
             if( list.hasMore() )
             {
                 Iterator it = new SearchResponseIterator( req, list );
@@ -171,7 +228,7 @@
         {
             String msg = "failed on search operation";
 
-            if ( LOG.isDebugEnabled() )
+            if ( log.isDebugEnabled() )
             {
                 msg += ":\n" + req + ":\n" + ExceptionUtils.getStackTrace( e );
             }
@@ -215,13 +272,12 @@
     {
         String msg = "failed on search operation";
 
-        if ( LOG.isDebugEnabled() )
+        if ( log.isDebugEnabled() )
         {
             msg += ":\n" + req + ":\n" + ExceptionUtils.getStackTrace( e );
         }
 
         SearchResponseDone resp = new SearchResponseDoneImpl( req.getMessageId() );
-
         ResultCodeEnum code = null;
 
         if( e instanceof LdapException )
@@ -234,9 +290,7 @@
         }
 
         resp.setLdapResult( new LdapResultImpl( resp ) );
-
         resp.getLdapResult().setResultCode( code );
-
         resp.getLdapResult().setErrorMessage( msg );
 
         if ( ( e.getResolvedName() != null ) &&
@@ -254,13 +308,9 @@
     class SearchResponseIterator implements Iterator
     {
         private final SearchRequest req;
-
         private final NamingEnumeration underlying;
-
         private SearchResponseDone respDone;
-
         private boolean done = false;
-
         private Object prefetched;
 
         /**
@@ -291,21 +341,15 @@
                     if( ref == null || ref.size() > 0 )
                     {
                         SearchResponseEntry respEntry;
-
                         respEntry = new SearchResponseEntryImpl( req.getMessageId() );
-
                         respEntry.setAttributes( result.getAttributes() );
-
                         respEntry.setObjectName( result.getName() );
-
                         prefetched = respEntry;
                     }
                     else
                     {
                         SearchResponseReference respRef;
-
                         respRef = new SearchResponseReferenceImpl( req.getMessageId() );
-
                         respRef.setReferral( new ReferralImpl( respRef ) );
 
                         for( int ii = 0; ii < ref.size(); ii ++ )
@@ -315,7 +359,6 @@
                             try
                             {
                                 url = ( String ) ref.get( ii );
-
                                 respRef.getReferral().addLdapUrl( url );
                             }
                             catch( NamingException e )
@@ -329,7 +372,6 @@
                                 }
 
                                 prefetched = null;
-
                                 respDone = getResponse( req, e );
                             }
                         }
@@ -360,7 +402,6 @@
         public Object next()
         {
             Object next = prefetched;
-
             SearchResult result = null;
 
             // if we're done we got nothing to give back
@@ -373,7 +414,6 @@
             if( respDone != null )
             {
                 done = true;
-
                 return respDone;
             }
 
@@ -402,13 +442,9 @@
                     }
 
                     respDone = new SearchResponseDoneImpl( req.getMessageId() );
-
                     respDone.setLdapResult( new LdapResultImpl( respDone ) );
-
                     respDone.getLdapResult().setResultCode( ResultCodeEnum.SUCCESS );
-
                     prefetched = null;
-
                     return next;
                 }
             }
@@ -423,9 +459,7 @@
                 }
 
                 prefetched = null;
-
                 respDone = getResponse( req, e );
-
                 return next;
             }
 
@@ -438,17 +472,13 @@
             if( ref == null || ref.size() > 0 )
             {
                 SearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId()
);
-
                 respEntry.setAttributes( result.getAttributes() );
-
                 respEntry.setObjectName( result.getName() );
-
                 prefetched = respEntry;
             }
             else
             {
                 SearchResponseReference respRef = new SearchResponseReferenceImpl( req.getMessageId()
);
-
                 respRef.setReferral( new ReferralImpl( respRef ) );
 
                 for( int ii = 0; ii < ref.size(); ii ++ )
@@ -458,7 +488,6 @@
                     try
                     {
                         url = ( String ) ref.get( ii );
-
                         respRef.getReferral().addLdapUrl( url );
                     }
                     catch( NamingException e )
@@ -472,9 +501,7 @@
                         }
 
                         prefetched = null;
-
                         respDone = getResponse( req, e );
-
                         return next;
                     }
                 }
@@ -493,6 +520,160 @@
         public void remove()
         {
             throw new UnsupportedOperationException();
+        }
+    }
+
+
+    private PersistentSearchControl getPersistentSearchControl( SearchRequest req )
+    {
+        Iterator list = req.getControls().iterator();
+        while ( list.hasNext() )
+        {
+            Control control = ( Control ) list.next();
+            if ( control.getID().equals( "2.16.840.1.113730.3.4.3" ) )
+            {
+                return ( PersistentSearchControl ) control;
+            }
+        }
+        
+        return null;
+    }
+    
+    
+    class PersistentSearchHandler implements ObjectChangeListener, NamespaceChangeListener
+    {
+        final ServerLdapContext ctx;
+        final IoSession session;
+        final SearchRequest req;
+        
+        PersistentSearchHandler( ServerLdapContext ctx, IoSession session, SearchRequest
req ) 
+        {
+            this.session = session;
+            this.req = req;
+            this.ctx = ctx;
+            this.req.put( "PersistentSearchHandler", this );
+        }
+        
+        
+        public void abandon() throws NamingException
+        {
+            // must abandon the operation and send response done with success
+            ctx.removeNamingListener( this );
+
+            // remove from outstanding map
+            SessionRegistry.getSingleton().removeOutstandingRequest( session, new Integer(
req.getMessageId() ) );
+            
+            // send successful response back to client
+            SearchResponseDone resp = new SearchResponseDoneImpl( req.getMessageId() );
+            resp.setLdapResult( new LdapResultImpl( resp ) );
+            resp.getLdapResult().setResultCode( ResultCodeEnum.SUCCESS );
+            resp.getLdapResult().setMatchedDn( req.getBase() );
+            session.write( resp );
+        }
+        
+        
+        public void namingExceptionThrown( NamingExceptionEvent evt ) 
+        {
+            // must abandon the operation and send response done with an
+            // error message if this occurs because something is wrong
+
+            try
+            {
+                ctx.removeNamingListener( this );
+            }
+            catch ( NamingException e )
+            {
+                log.error( "Attempt to remove listener from context failed", e );
+            }
+
+            SessionRegistry.getSingleton().removeOutstandingRequest( session, new Integer(
req.getMessageId() ) );
+            String msg = "failed on persistent search operation";
+
+            if ( log.isDebugEnabled() )
+            {
+                msg += ":\n" + req + ":\n" + ExceptionUtils.getStackTrace( evt.getException()
);
+            }
+
+            SearchResponseDone resp = new SearchResponseDoneImpl( req.getMessageId() );
+            ResultCodeEnum code = null;
+
+            if( evt.getException() instanceof LdapException )
+            {
+                code = ( ( LdapException ) evt.getException() ).getResultCode();
+            }
+            else
+            {
+                code = ResultCodeEnum.getBestEstimate( evt.getException(), req.getType()
);
+            }
+
+            resp.setLdapResult( new LdapResultImpl( resp ) );
+            resp.getLdapResult().setResultCode( code );
+            resp.getLdapResult().setErrorMessage( msg );
+
+            if ( ( evt.getException().getResolvedName() != null ) &&
+                    ( ( code == ResultCodeEnum.NOSUCHOBJECT ) ||
+                      ( code == ResultCodeEnum.ALIASPROBLEM ) ||
+                      ( code == ResultCodeEnum.INVALIDDNSYNTAX ) ||
+                      ( code == ResultCodeEnum.ALIASDEREFERENCINGPROBLEM ) ) )
+            {
+                resp.getLdapResult().setMatchedDn( evt.getException().getResolvedName().toString()
);
+            }
+
+            session.write( resp );
+        }
+
+        
+        public void objectChanged( NamingEvent evt )
+        {
+            // send the entry back
+            sendEntry( evt );
+        }
+
+        public void objectAdded( NamingEvent evt )
+        {
+            // send the entry back
+            sendEntry( evt );
+        }
+
+        public void objectRemoved( NamingEvent evt )
+        {
+            // send the entry back
+            sendEntry( evt );
+        }
+
+        public void objectRenamed( NamingEvent evt )
+        {
+            // send the entry back
+            sendEntry( evt );
+        }
+
+        private void sendEntry( NamingEvent evt ) 
+        {
+            SearchResponseEntry respEntry = new SearchResponseEntryImpl( req.getMessageId()
);
+
+            switch ( evt.getType() )
+            {
+                case( NamingEvent.OBJECT_ADDED ):
+                    respEntry.setObjectName( evt.getOldBinding().getName() );
+                    respEntry.setAttributes( ( Attributes ) evt.getChangeInfo() );
+                    break;
+                case( NamingEvent.OBJECT_CHANGED ):
+                    respEntry.setObjectName( evt.getOldBinding().getName() );
+                    respEntry.setAttributes( ( Attributes ) evt.getOldBinding().getObject()
);
+                    break;
+                case( NamingEvent.OBJECT_REMOVED ):
+                    respEntry.setObjectName( evt.getOldBinding().getName() );
+                    respEntry.setAttributes( ( Attributes ) evt.getOldBinding().getObject()
);
+                    break;
+                case( NamingEvent.OBJECT_RENAMED ):
+                    respEntry.setObjectName( evt.getOldBinding().getName() );
+                    respEntry.setAttributes( ( Attributes ) evt.getOldBinding().getObject()
);
+                    break;
+                default:
+                    return;
+            }
+            
+            session.write( respEntry );
         }
     }
 }



Mime
View raw message