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 2006/01/17 00:23:46 UTC

svn commit: r369600 - in /directory/trunks/apacheds: core-unit/src/test/java/org/apache/ldap/server/jndi/ core/src/main/java/org/apache/ldap/server/enumeration/ core/src/main/java/org/apache/ldap/server/referral/

Author: akarasulu
Date: Mon Jan 16 15:23:37 2006
New Revision: 369600

URL: http://svn.apache.org/viewcvs?rev=369600&view=rev
Log:
finished up with search in general including continuation behavoir for JNDI provider

Added:
    directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/enumeration/ReferralHandlingEnumeration.java   (with props)
Modified:
    directory/trunks/apacheds/core-unit/src/test/java/org/apache/ldap/server/jndi/ReferralTest.java
    directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/referral/ReferralService.java

Modified: directory/trunks/apacheds/core-unit/src/test/java/org/apache/ldap/server/jndi/ReferralTest.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/core-unit/src/test/java/org/apache/ldap/server/jndi/ReferralTest.java?rev=369600&r1=369599&r2=369600&view=diff
==============================================================================
--- directory/trunks/apacheds/core-unit/src/test/java/org/apache/ldap/server/jndi/ReferralTest.java (original)
+++ directory/trunks/apacheds/core-unit/src/test/java/org/apache/ldap/server/jndi/ReferralTest.java Mon Jan 16 15:23:37 2006
@@ -17,13 +17,16 @@
 package org.apache.ldap.server.jndi;
 
 
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 
 import javax.naming.Context;
 import javax.naming.Name;
 import javax.naming.NameAlreadyBoundException;
 import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.ReferralException;
 import javax.naming.directory.Attributes;
@@ -31,9 +34,13 @@
 import javax.naming.directory.BasicAttributes;
 import javax.naming.directory.DirContext;
 import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
 import javax.naming.ldap.InitialLdapContext;
 import javax.naming.ldap.LdapContext;
 
+import org.apache.ldap.common.exception.LdapNamingException;
+import org.apache.ldap.common.message.ResultCodeEnum;
 import org.apache.ldap.common.name.LdapName;
 import org.apache.ldap.server.unit.AbstractAdminTestCase;
 
@@ -426,8 +433,6 @@
             checkAncestorReferrals( e );
         }
     }
-
-
     
     
     /**
@@ -485,5 +490,474 @@
         {
             checkAncestorReferrals( e );
         }
+    }
+
+    
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify rdn interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testModifyRdnWithReferralParent() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try 
+        {
+            td.refCtx.rename( "cn=alex karasulu", "cn=aok" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+    
+    
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a modify rdn interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testModifyRdnWithReferralAncestor() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try 
+        {
+            td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=aok,ou=apache" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+    
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralParent() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try 
+        {
+            td.refCtx.rename( "cn=alex karasulu", "cn=alex karasulu,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+    
+    
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralAncestor() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try 
+        {
+            td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=alex karasulu,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+    
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralParent2() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try 
+        {
+            td.refCtx.rename( "cn=alex karasulu", "cn=aok,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch( ReferralException e )
+        {
+            checkParentReferrals( e );
+        }
+    }
+    
+    
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralAncestor2() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try 
+        {
+            td.refCtx.rename( "cn=alex karasulu,ou=apache", "cn=aok,ou=groups" );
+            fail( "Should fail here throwing a ReferralException" );
+        }
+        catch( ReferralException e )
+        {
+            checkAncestorReferrals( e );
+        }
+    }
+
+    
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralParentDest() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try 
+        {
+            td.rootCtx.rename( "cn=akarasulu", "cn=akarasulu,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        catch( LdapNamingException e )
+        {
+            assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTSMULTIPLEDSAS );
+        }
+    }
+    
+    
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralAncestorDest() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createDeepLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try 
+        {
+            td.rootCtx.rename( "cn=akarasulu,ou=deep", "cn=akarasulu,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        catch( LdapNamingException e )
+        {
+            assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTSMULTIPLEDSAS );
+        }
+    }
+
+    
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with the parent context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralParent2Dest() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an parent which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try 
+        {
+            td.rootCtx.rename( "cn=akarasulu", "cn=aok,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        catch( LdapNamingException e )
+        {
+            assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTSMULTIPLEDSAS );
+        }
+    }
+    
+    
+    /**
+     * Checks for correct core behavoir when Context.REFERRAL is set to <b>throw</b>
+     * for a move interceptor operation (corresponds to a subset of the modify 
+     * dn operation) with an ancestor context being a referral.
+     * 
+     * @throws Exception if something goes wrong.
+     */
+    public void testMoveWithReferralAncestor2Dest() throws Exception
+    {
+        // -------------------------------------------------------------------
+        // Attempt to modify the last component of the entry's name which 
+        // resides below an ancestor which is a referral. We should encounter 
+        // referral errors when referral setting is set to throw.
+        // -------------------------------------------------------------------
+
+        createDeepLocalUser();
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try 
+        {
+            td.rootCtx.rename( "cn=akarasulu,ou=deep", "cn=aok,ou=users" );
+            fail( "Should fail here throwing a LdapNamingException with ResultCodeEnum = AFFECTSMULTIPLEDSAS" );
+        }
+        catch( LdapNamingException e )
+        {
+            assertTrue( e.getResultCode() == ResultCodeEnum.AFFECTSMULTIPLEDSAS );
+        }
+    }
+    
+    
+    public void createLocalUser() throws Exception
+    {
+        LdapContext userCtx = null;
+        Attributes referral = new BasicAttributes( "objectClass", "top", true );
+        referral.get( "objectClass" ).add( "person" );
+        referral.put( "cn", "akarasulu" );
+        referral.put( "sn", "karasulu" );
+
+        try { td.rootCtx.destroySubcontext( "uid=akarasulu" ); } catch( NameNotFoundException e ) {}
+        try
+        {
+            userCtx = ( LdapContext ) td.rootCtx.createSubcontext( "cn=akarasulu", referral );
+        }
+        catch( NameAlreadyBoundException e )
+        {
+            td.refCtx = ( LdapContext ) td.rootCtx.lookup( "cn=akarasulu" );
+        }
+        referral = userCtx.getAttributes( "" );
+        assertTrue( referral.get( "cn" ).contains( "akarasulu" ) );
+        assertTrue( referral.get( "sn" ).contains( "karasulu" ) );
+    }
+    
+    
+    public void createDeepLocalUser() throws Exception
+    {
+        LdapContext userCtx = null;
+        Attributes referral = new BasicAttributes( "objectClass", "top", true );
+        referral.get( "objectClass" ).add( "person" );
+        referral.put( "cn", "akarasulu" );
+        referral.put( "sn", "karasulu" );
+
+        try { td.rootCtx.destroySubcontext( "uid=akarasulu,ou=deep" ); } catch( NameNotFoundException e ) {}
+        try { td.rootCtx.destroySubcontext( "ou=deep" ); } catch( NameNotFoundException e ) {}
+        try
+        {
+            td.rootCtx.createSubcontext( "ou=deep" );
+            userCtx = ( LdapContext ) td.rootCtx.createSubcontext( "cn=akarasulu,ou=deep", referral );
+        }
+        catch( NameAlreadyBoundException e )
+        {
+            td.refCtx = ( LdapContext ) td.rootCtx.lookup( "cn=akarasulu,ou=deep" );
+        }
+        referral = userCtx.getAttributes( "" );
+        assertTrue( referral.get( "cn" ).contains( "akarasulu" ) );
+        assertTrue( referral.get( "sn" ).contains( "karasulu" ) );
+    }
+    
+    
+    public void testSearchBaseIsReferral() throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.rootCtx.search( "ou=users", "(objectClass=*)", controls );
+            fail( "should never get here" );
+        }
+        catch( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+    }
+
+    
+    public void testSearchBaseParentIsReferral() throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.search( "cn=alex karasulu", "(objectClass=*)", controls );
+        }
+        catch( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/cn=alex karasulu,ou=users,ou=system??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/cn=alex karasulu,ou=users,dc=example,dc=com??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/cn=alex karasulu,ou=users,ou=system??base", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+    }
+
+    
+    public void testSearchBaseAncestorIsReferral() throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.OBJECT_SCOPE );
+        td.refCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        try
+        {
+            td.refCtx.search( "cn=alex karasulu,ou=apache", "(objectClass=*)", controls );
+        }
+        catch( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/cn=alex karasulu,ou=apache,ou=users,ou=system??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/cn=alex karasulu,ou=apache,ou=users,dc=example,dc=com??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/cn=alex karasulu,ou=apache,ou=users,ou=system??base", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+    }
+
+    
+    public void testSearchContinuations() throws Exception
+    {
+        SearchControls controls = new SearchControls();
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        NamingEnumeration list = td.rootCtx.search( "", "(objectClass=*)", controls );
+        Map results = new HashMap();
+        while ( list.hasMore() )
+        {
+            SearchResult result = ( SearchResult) list.next();
+            System.out.println( "name = " + result.getName() + " results .. " + result );
+            results.put ( result.getName(), result );
+        }
+        
+        assertNotNull( results.get( "ou=users,ou=system" ) );
+        
+        // -------------------------------------------------------------------
+        // Now we will throw exceptions when searching for referrals 
+        // -------------------------------------------------------------------
+        
+        td.rootCtx.addToEnvironment( Context.REFERRAL, "throw" );
+        list = td.rootCtx.search( "", "(objectClass=*)", controls );
+        results = new HashMap();
+        
+        try
+        {
+            while ( list.hasMore() )
+            {
+                SearchResult result = ( SearchResult ) list.next();
+                System.out.println( "name = " + result.getName() + " results .. " + result );
+                results.put ( result.getName(), result );
+            }
+        }
+        catch( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??sub", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/ou=users,ou=system??sub", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+        
+        assertNull( results.get( "ou=users" ) );
+
+        // try again but this time with single level scope
+        
+        controls.setSearchScope( SearchControls.ONELEVEL_SCOPE );
+        list = td.rootCtx.search( "", "(objectClass=*)", controls );
+        results = new HashMap();
+        
+        try
+        {
+            while ( list.hasMore() )
+            {
+                SearchResult result = ( SearchResult ) list.next();
+                System.out.println( "name = " + result.getName() + " results .. " + result );
+                results.put ( result.getName(), result );
+            }
+        }
+        catch( ReferralException e )
+        {
+            assertEquals( "ldap://fermi:10389/ou=users,ou=system??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://hertz:10389/ou=users,dc=example,dc=com??base", e.getReferralInfo() );
+            assertTrue( e.skipReferral() );
+            assertEquals( "ldap://maxwell:10389/ou=users,ou=system??base", e.getReferralInfo() );
+            assertFalse( e.skipReferral() );
+        }
+        
+        assertNull( results.get( "ou=users" ) );
     }
 }

Added: directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/enumeration/ReferralHandlingEnumeration.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/enumeration/ReferralHandlingEnumeration.java?rev=369600&view=auto
==============================================================================
--- directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/enumeration/ReferralHandlingEnumeration.java (added)
+++ directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/enumeration/ReferralHandlingEnumeration.java Mon Jan 16 15:23:37 2006
@@ -0,0 +1,234 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.ldap.server.enumeration;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.ldap.common.codec.util.LdapURL;
+import org.apache.ldap.common.codec.util.LdapURLEncodingException;
+import org.apache.ldap.common.exception.LdapReferralException;
+import org.apache.ldap.common.name.DnParser;
+import org.apache.ldap.server.partition.DirectoryPartitionNexus;
+import org.apache.ldap.server.referral.ReferralLut;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+ 
+
+/**
+ * A wrapper enumeration which saves referral entries to be returned last.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ReferralHandlingEnumeration implements NamingEnumeration
+{
+    private static final String REF_ATTR = "ref";
+    private final Logger log = LoggerFactory.getLogger( ReferralHandlingEnumeration.class );
+    private final List referrals = new ArrayList();
+    private final NamingEnumeration underlying;
+    private final ReferralLut lut;
+    private final DnParser parser;
+    private final DirectoryPartitionNexus nexus;
+    private final boolean doThrow;
+    private final int scope;
+    private SearchResult prefetched;
+    private int refIndex = -1;
+    
+    
+    public ReferralHandlingEnumeration( NamingEnumeration underlying, ReferralLut lut, DnParser parser, 
+        DirectoryPartitionNexus nexus, int scope, boolean doThrow ) throws NamingException
+    {
+        this.underlying = underlying;
+        this.parser = parser;
+        this.doThrow = doThrow;
+        this.lut = lut;
+        this.scope = scope;
+        this.nexus = nexus;
+        prefetch();
+    }
+    
+    
+    public void prefetch() throws NamingException
+    {
+        while ( underlying.hasMore() )
+        {
+            SearchResult result = ( SearchResult ) underlying.next();
+            Name dn = parser.parse( result.getName() );
+            if ( lut.isReferral( dn ) )
+            {
+                referrals.add( result );
+                continue;
+            }
+            prefetched = result;
+            return;
+        }
+        
+        refIndex++;
+        prefetched = ( SearchResult ) referrals.get( refIndex );
+        if ( doThrow )
+        {
+            doReferralExceptionOnSearchBase();
+        }
+    }
+    
+    
+    public Object next() throws NamingException
+    {
+        SearchResult retval = prefetched;
+        prefetch();
+        return retval;
+    }
+
+    
+    public boolean hasMore() throws NamingException
+    {
+        return underlying.hasMore() || refIndex < referrals.size();
+    }
+
+
+    public void close() throws NamingException
+    {
+        underlying.close();
+        referrals.clear();
+        prefetched = null;
+        refIndex = Integer.MAX_VALUE;
+    }
+
+    
+    public boolean hasMoreElements()
+    {
+        try
+        {
+            return hasMore();
+        }
+        catch ( NamingException e )
+        {
+            log.error( "Naming enumeration failure.  Closing enumeration early!", e );
+            try
+            {
+                close();
+            }
+            catch ( NamingException e1 )
+            {
+                log.error( "Naming enumeration failure.  Failed to properly close enumeration!", e1 );
+            }
+        }
+        
+        return false;
+    }
+
+
+    public Object nextElement()
+    {
+        try
+        {
+            return next();
+        }
+        catch ( NamingException e )
+        {
+            log.error( "NamingEnumeration closed prematurely without returning elements.", e );
+        }
+        
+        throw new NoSuchElementException( "NamingEnumeration closed prematurely without returning elements." );
+    }
+
+
+    public void doReferralExceptionOnSearchBase() throws NamingException
+    {
+        // the refs attribute may be filtered out so we might need to lookup the entry
+        Attribute refs = prefetched.getAttributes().get( REF_ATTR );
+        if ( refs == null )
+        {
+            refs = nexus.lookup( parser.parse( prefetched.getName() ) ).get( REF_ATTR );
+        }
+        
+        if ( refs == null )
+        {
+            throw new IllegalStateException( prefetched.getName() 
+                + " does not seem like a referral but we're trying to handle it as one." );
+        }
+        
+        List list = new ArrayList( refs.size() );
+        for ( int ii = 0; ii < refs.size(); ii++ )
+        {
+            String val = ( String ) refs.get( ii );
+            
+            // need to add non-ldap URLs as-is
+            if ( ! val.startsWith( "ldap" ) )
+            {
+                list.add( val );
+                continue;
+            }
+            
+            // parse the ref value and normalize the DN according to schema 
+            LdapURL ldapUrl = new LdapURL();
+            try
+            {
+                ldapUrl.parse( val.toCharArray() );
+            }
+            catch ( LdapURLEncodingException e )
+            {
+                log.error( "Bad URL ("+ val +") for ref in " + prefetched.getName() + ".  Reference will be ignored." ); 
+            }
+            
+            StringBuffer buf = new StringBuffer();
+            buf.append( ldapUrl.getScheme() );
+            buf.append( ldapUrl.getHost() );
+            if ( ldapUrl.getPort() > 0 )
+            {
+                buf.append( ":" );
+                buf.append( ldapUrl.getPort() );
+            }
+            buf.append( "/" );
+            buf.append( ldapUrl.getDn() );
+            buf.append( "??" );
+            
+            switch ( scope )
+            {
+                case( SearchControls.SUBTREE_SCOPE ):
+                    buf.append( "sub" );
+                    break;
+                    
+                // if we search for one level and encounter a referral then search
+                // must be continued at that node using base level search scope
+                case( SearchControls.ONELEVEL_SCOPE ):
+                    buf.append( "base" );
+                    break;
+                case( SearchControls.OBJECT_SCOPE ):
+                    buf.append( "base" );
+                    break;
+                default:
+                    throw new IllegalStateException( "Unknown recognized search scope: " + scope );
+            }
+            
+            list.add( buf.toString() );
+        }
+        LdapReferralException lre = new LdapReferralException( list );
+        throw lre;
+    }
+}

Propchange: directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/enumeration/ReferralHandlingEnumeration.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/referral/ReferralService.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/referral/ReferralService.java?rev=369600&r1=369599&r2=369600&view=diff
==============================================================================
--- directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/referral/ReferralService.java (original)
+++ directory/trunks/apacheds/core/src/main/java/org/apache/ldap/server/referral/ReferralService.java Mon Jan 16 15:23:37 2006
@@ -24,6 +24,7 @@
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import javax.naming.Context;
 import javax.naming.Name;
@@ -51,6 +52,9 @@
 import org.apache.ldap.server.DirectoryServiceConfiguration;
 import org.apache.ldap.server.configuration.DirectoryPartitionConfiguration;
 import org.apache.ldap.server.configuration.InterceptorConfiguration;
+import org.apache.ldap.server.enumeration.ReferralHandlingEnumeration;
+import org.apache.ldap.server.enumeration.SearchResultFilter;
+import org.apache.ldap.server.enumeration.SearchResultFilteringEnumeration;
 import org.apache.ldap.server.interceptor.BaseInterceptor;
 import org.apache.ldap.server.interceptor.NextInterceptor;
 import org.apache.ldap.server.invocation.Invocation;
@@ -86,6 +90,7 @@
     private static final String REF_ATTR = "ref";
 
     private ReferralLut lut = new ReferralLut();
+    private DirectoryPartitionNexus nexus;
     private DnParser parser;
     private Hashtable env;
 
@@ -151,7 +156,7 @@
     
     public void init( DirectoryServiceConfiguration dsConfig, InterceptorConfiguration cfg ) throws NamingException
     {
-        DirectoryPartitionNexus nexus = dsConfig.getPartitionNexus();
+        nexus = dsConfig.getPartitionNexus();
         AttributeTypeRegistry atr = dsConfig.getGlobalRegistries().getAttributeTypeRegistry();
         parser = new DnParser( new ConcreteNameComponentNormalizer( atr ) );
         env = dsConfig.getEnvironment();
@@ -369,16 +374,83 @@
     }
     
     
+    /* -----------------------------------------------------------------------
+     * Special handling instructions for ModifyDn operations:
+     * ======================================================
+     * 
+     * From RFC 3296 here => http://www.ietf.org/rfc/rfc3296.txt
+     * 
+     * 5.6.2 Modify DN
+     *
+     * If the newSuperior is a referral object or is subordinate to a
+     * referral object, the server SHOULD return affectsMultipleDSAs.  If
+     * the newRDN already exists but is a referral object, the server SHOULD
+     * return affectsMultipleDSAs instead of entryAlreadyExists.
+     * -----------------------------------------------------------------------
+     */
+
+    
     public void move( NextInterceptor next, Name oldName, Name newParent ) throws NamingException
     {
-        next.move( oldName, newParent );
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+        Name newName = ( Name ) newParent.clone();
+        newName.add( oldName.get( oldName.size() - 1 ) );
+
+        // handle a normal modify without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            next.move( oldName, newParent );
+            if ( lut.isReferral( oldName ) )
+            {
+                lut.referralChanged( oldName, newName );
+            }
+            return;
+        }
 
-        // update the lut of a referral is being moved
-        if ( lut.isReferral( oldName ) )
+        if ( refval.equals( THROW ) )
+        {
+            Name farthestSrc = lut.getFarthestReferralAncestor( oldName );
+            Name farthestDst = lut.getFarthestReferralAncestor( newName ); // note will not return newName so safe
+            if ( farthestSrc == null && farthestDst == null && ! lut.isReferral(  newName ) ) 
+            {
+                next.move( oldName, newParent );
+                if ( lut.isReferral( oldName ) )
+                {
+                    lut.referralChanged( oldName, newName );
+                }
+                return;
+            }
+            else if ( farthestSrc != null )
+            {
+                Attributes referral = invocation.getProxy().lookup( farthestSrc, DirectoryPartitionNexusProxy.LOOKUP_BYPASS );
+                Attribute refs = referral.get( REF_ATTR );
+                doReferralException( farthestSrc, oldName, refs );
+            }
+            else if ( farthestDst != null )
+            {
+                throw new LdapNamingException( farthestDst + " ancestor is a referral for modifyDn on " + newName 
+                    + " so it affects multiple DSAs", ResultCodeEnum.AFFECTSMULTIPLEDSAS );
+            }
+            else if ( lut.isReferral( newName ) )
+            {
+                throw new LdapNamingException( newName 
+                    + " exists and is a referral for modifyDn destination so it affects multiple DSAs", 
+                    ResultCodeEnum.AFFECTSMULTIPLEDSAS );
+            }
+            
+            throw new IllegalStateException( "If you get this exception the server's logic was flawed in handling a " +
+                    "modifyDn operation while processing referrals.  Report this as a bug!" );
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
         {
-            Name newName = ( Name ) newParent.clone();
-            newName.add( oldName.get( oldName.size() - 1 ) );
-            lut.referralChanged( oldName, newName );
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL  + " key: " 
+                + refval, ResultCodeEnum.OTHER );
         }
     }
     
@@ -386,29 +458,132 @@
     public void move( NextInterceptor next, Name oldName, Name newParent, String newRdn, boolean deleteOldRdn ) 
         throws NamingException
     {
-        next.move( oldName, newParent, newRdn, deleteOldRdn );
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+        Name newName = ( Name ) newParent.clone();
+        newName.add( newRdn );
+
+        // handle a normal modify without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            next.move( oldName, newParent, newRdn, deleteOldRdn );
+            if ( lut.isReferral( oldName ) )
+            {
+                lut.referralChanged( oldName, newName );
+            }
+            return;
+        }
 
-        // update the lut of a referral is being moved
-        if ( lut.isReferral( oldName ) )
+        if ( refval.equals( THROW ) )
+        {
+            Name farthestSrc = lut.getFarthestReferralAncestor( oldName );
+            Name farthestDst = lut.getFarthestReferralAncestor( newName ); // safe to use - does not return newName
+            if ( farthestSrc == null && farthestDst == null && ! lut.isReferral( newName ) ) 
+            {
+                next.move( oldName, newParent, newRdn, deleteOldRdn );
+                if ( lut.isReferral( oldName ) )
+                {
+                    lut.referralChanged( oldName, newName );
+                }
+                return;
+            }
+            else if ( farthestSrc != null )
+            {
+                Attributes referral = invocation.getProxy().lookup( farthestSrc, DirectoryPartitionNexusProxy.LOOKUP_BYPASS );
+                Attribute refs = referral.get( REF_ATTR );
+                doReferralException( farthestSrc, oldName, refs );
+            }
+            else if ( farthestDst != null )
+            {
+                throw new LdapNamingException( farthestDst + " ancestor is a referral for modifyDn on " + newName 
+                    + " so it affects multiple DSAs", ResultCodeEnum.AFFECTSMULTIPLEDSAS );
+            }
+            else if ( lut.isReferral( newName ) )
+            {
+                throw new LdapNamingException( newName 
+                    + " exists and is a referral for modifyDn destination so it affects multiple DSAs", 
+                    ResultCodeEnum.AFFECTSMULTIPLEDSAS );
+            }
+            
+            throw new IllegalStateException( "If you get this exception the server's logic was flawed in handling a " +
+                    "modifyDn operation while processing referrals.  Report this as a bug!" );
+        }
+        else if ( refval.equals( FOLLOW ) )
         {
-            Name newName = ( Name ) newParent.clone();
-            newName.add( newRdn );
-            lut.referralChanged( oldName, newName );
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
+        {
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL  + " key: " 
+                + refval, ResultCodeEnum.OTHER );
         }
     }
 
 
-    public void modifyRdn( NextInterceptor next, Name oldName, Name newParent, String newRdn, boolean deleteOldRdn ) 
+    public void modifyRn( NextInterceptor next, Name oldName, String newRdn, boolean deleteOldRdn ) 
         throws NamingException
     {
-        next.modifyRn( oldName, newRdn, deleteOldRdn );
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+        Name newName = ( Name ) oldName.clone();
+        newName.remove( oldName.size() - 1 );
+        newName.add( parser.parse( newRdn ).toString() );
 
-        // update the lut of a referral is being renamed 
-        if ( lut.isReferral( oldName ) )
+        // handle a normal modify without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            next.modifyRn( oldName, newRdn, deleteOldRdn );
+            if ( lut.isReferral( oldName ) )
+            {
+                lut.referralChanged( oldName, newName );
+            }
+            return;
+        }
+
+        if ( refval.equals( THROW ) )
+        {
+            Name farthestSrc = lut.getFarthestReferralAncestor( oldName );
+            Name farthestDst = lut.getFarthestReferralAncestor( newName );
+            if ( farthestSrc == null && farthestDst == null && ! lut.isReferral( newName ) ) 
+            {
+                next.modifyRn( oldName, newRdn, deleteOldRdn );
+                if ( lut.isReferral( oldName ) )
+                {
+                    lut.referralChanged( oldName, newName );
+                }
+                return;
+            }
+            if ( farthestSrc != null )
+            {
+                Attributes referral = invocation.getProxy().lookup( farthestSrc, DirectoryPartitionNexusProxy.LOOKUP_BYPASS );
+                Attribute refs = referral.get( REF_ATTR );
+                doReferralException( farthestSrc, oldName, refs );
+            }
+            else if ( farthestDst != null )
+            {
+                throw new LdapNamingException( farthestDst + " ancestor is a referral for modifyDn on " + newName 
+                    + " so it affects multiple DSAs", ResultCodeEnum.AFFECTSMULTIPLEDSAS );
+            }
+            else if ( lut.isReferral( newName ) )
+            {
+                throw new LdapNamingException( newName 
+                    + " exists and is a referral for modifyDn destination so it affects multiple DSAs", 
+                    ResultCodeEnum.AFFECTSMULTIPLEDSAS );
+            }
+            
+            throw new IllegalStateException( "If you get this exception the server's logic was flawed in handling a " +
+                    "modifyDn operation while processing referrals.  Report this as a bug!" );
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
         {
-            Name newName = ( Name ) newParent.clone();
-            newName.add( newRdn );
-            lut.referralChanged( oldName, newName );
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL  + " key: " 
+                + refval, ResultCodeEnum.OTHER );
         }
     }
     
@@ -689,5 +864,189 @@
                 referral = parser.parse( r.getName() );
             }
         }
+    }
+    
+    
+    public NamingEnumeration search( NextInterceptor next, Name base, Map env, ExprNode filter, 
+        SearchControls controls ) throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+
+        // handle a normal modify without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            return next.search( base, env, filter, controls );
+        }
+
+        if ( refval.equals( THROW ) )
+        {
+            if ( lut.isReferral( base ) )
+            {
+                Attributes referral = invocation.getProxy().lookup( base, DirectoryPartitionNexusProxy.LOOKUP_BYPASS );
+                Attribute refs = referral.get( REF_ATTR );
+                doReferralExceptionOnSearchBase( base, refs, controls.getSearchScope() );
+            }
+            
+            Name farthest = lut.getFarthestReferralAncestor( base );
+            if ( farthest == null ) 
+            {
+                SearchResultFilteringEnumeration srfe = ( SearchResultFilteringEnumeration ) 
+                    next.search( base, env, filter, controls );
+                return new ReferralHandlingEnumeration( srfe, lut, parser, nexus, controls.getSearchScope(), true );
+            }
+            
+            Attributes referral = invocation.getProxy().lookup( farthest, DirectoryPartitionNexusProxy.LOOKUP_BYPASS );
+            Attribute refs = referral.get( REF_ATTR );
+            doReferralExceptionOnSearchBase( farthest, base, refs, controls.getSearchScope() );
+            throw new IllegalStateException( "Should never get here: shutting up compiler" );
+        }
+        else if ( refval.equals( FOLLOW ) )
+        {
+            throw new NotImplementedException( FOLLOW + " referral handling mode not implemented" );
+        }
+        else
+        {
+            throw new LdapNamingException( "Undefined value for " + Context.REFERRAL  + " key: " 
+                + refval, ResultCodeEnum.OTHER );
+        }
+    }
+
+    
+    class ReferralFilter implements SearchResultFilter//, SearchResultEnumerationAppender 
+    {
+        public boolean accept( Invocation invocation, SearchResult result, SearchControls controls ) throws NamingException
+        {
+            return false;
+        }
+    }
+    
+
+    public void doReferralExceptionOnSearchBase( Name base, Attribute refs, int scope ) throws NamingException
+    {
+        // handle referral here
+        List list = new ArrayList( refs.size() );
+        for ( int ii = 0; ii < refs.size(); ii++ )
+        {
+            String val = ( String ) refs.get( ii );
+            
+            // need to add non-ldap URLs as-is
+            if ( ! val.startsWith( "ldap" ) )
+            {
+                list.add( val );
+                continue;
+            }
+            
+            // parse the ref value and normalize the DN according to schema 
+            LdapURL ldapUrl = new LdapURL();
+            try
+            {
+                ldapUrl.parse( val.toCharArray() );
+            }
+            catch ( LdapURLEncodingException e )
+            {
+                log.error( "Bad URL ("+ val +") for ref in " + base + ".  Reference will be ignored." ); 
+            }
+            
+            StringBuffer buf = new StringBuffer();
+            buf.append( ldapUrl.getScheme() );
+            buf.append( ldapUrl.getHost() );
+            if ( ldapUrl.getPort() > 0 )
+            {
+                buf.append( ":" );
+                buf.append( ldapUrl.getPort() );
+            }
+            buf.append( "/" );
+            buf.append( ldapUrl.getDn() );
+            buf.append( "??" );
+            
+            switch ( scope )
+            {
+                case( SearchControls.SUBTREE_SCOPE ):
+                    buf.append( "sub" );
+                    break;
+                case( SearchControls.ONELEVEL_SCOPE ):
+                    buf.append( "one" );
+                    break;
+                case( SearchControls.OBJECT_SCOPE ):
+                    buf.append( "base" );
+                    break;
+                default:
+                    throw new IllegalStateException( "Unknown recognized search scope: " + scope );
+            }
+            
+            list.add( buf.toString() );
+        }
+        LdapReferralException lre = new LdapReferralException( list );
+        throw lre;
+    }
+
+
+    public void doReferralExceptionOnSearchBase( Name farthest, Name targetUpdn, Attribute refs, int scope ) throws NamingException
+    {
+        // handle referral here
+        List list = new ArrayList( refs.size() );
+        for ( int ii = 0; ii < refs.size(); ii++ )
+        {
+            String val = ( String ) refs.get( ii );
+            
+            // need to add non-ldap URLs as-is
+            if ( ! val.startsWith( "ldap" ) )
+            {
+                list.add( val );
+                continue;
+            }
+            
+            // parse the ref value and normalize the DN according to schema 
+            LdapURL ldapUrl = new LdapURL();
+            try
+            {
+                ldapUrl.parse( val.toCharArray() );
+            }
+            catch ( LdapURLEncodingException e )
+            {
+                log.error( "Bad URL ("+ val +") for ref in " + farthest + ".  Reference will be ignored." ); 
+            }
+            
+            Name urlDn = parser.parse( ldapUrl.getDn().toString() );
+            int diff = targetUpdn.size() - farthest.size();
+            Name extra = new LdapName(); 
+            for ( int jj = 0; jj < diff; jj++ )
+            {
+                extra.add( targetUpdn.get( farthest.size() + jj ) );
+            }
+
+            urlDn.addAll( extra );
+            StringBuffer buf = new StringBuffer();
+            buf.append( ldapUrl.getScheme() );
+            buf.append( ldapUrl.getHost() );
+            if ( ldapUrl.getPort() > 0 )
+            {
+                buf.append( ":" );
+                buf.append( ldapUrl.getPort() );
+            }
+            buf.append( "/" );
+            buf.append( urlDn );
+            buf.append( "??" );
+            
+            switch ( scope )
+            {
+                case( SearchControls.SUBTREE_SCOPE ):
+                    buf.append( "sub" );
+                    break;
+                case( SearchControls.ONELEVEL_SCOPE ):
+                    buf.append( "one" );
+                    break;
+                case( SearchControls.OBJECT_SCOPE ):
+                    buf.append( "base" );
+                    break;
+                default:
+                    throw new IllegalStateException( "Unknown recognized search scope: " + scope );
+            }
+            list.add( buf.toString() );
+        }
+        LdapReferralException lre = new LdapReferralException( list );
+        throw lre;
     }
 }