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/13 06:32:08 UTC

svn commit: r368602 - in /directory/trunks: apacheds/src/main/java/org/apache/ldap/server/configuration/ apacheds/src/main/java/org/apache/ldap/server/jndi/ apacheds/src/main/java/org/apache/ldap/server/partition/ apacheds/src/main/java/org/apache/ldap...

Author: akarasulu
Date: Thu Jan 12 21:31:56 2006
New Revision: 368602

URL: http://svn.apache.org/viewcvs?rev=368602&view=rev
Log:
partial commit of referral stuff ... in the works

Added:
    directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/
    directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/ReferralLut.java   (with props)
    directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/ReferralService.java   (with props)
    directory/trunks/apacheds/src/test/java/org/apache/ldap/server/referral/
    directory/trunks/apacheds/src/test/java/org/apache/ldap/server/referral/ReferralLutTest.java   (with props)
    directory/trunks/ldap-common/src/main/java/org/apache/ldap/common/exception/LdapReferralException.java   (with props)
Modified:
    directory/trunks/apacheds/src/main/java/org/apache/ldap/server/configuration/StartupConfiguration.java
    directory/trunks/apacheds/src/main/java/org/apache/ldap/server/jndi/ServerContext.java
    directory/trunks/apacheds/src/main/java/org/apache/ldap/server/jndi/ServerDirContext.java
    directory/trunks/apacheds/src/main/java/org/apache/ldap/server/partition/DirectoryPartitionNexusProxy.java
    directory/trunks/apacheds/src/test/java/org/apache/ldap/server/jndi/RootDSETest.java

Modified: directory/trunks/apacheds/src/main/java/org/apache/ldap/server/configuration/StartupConfiguration.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/src/main/java/org/apache/ldap/server/configuration/StartupConfiguration.java?rev=368602&r1=368601&r2=368602&view=diff
==============================================================================
--- directory/trunks/apacheds/src/main/java/org/apache/ldap/server/configuration/StartupConfiguration.java (original)
+++ directory/trunks/apacheds/src/main/java/org/apache/ldap/server/configuration/StartupConfiguration.java Thu Jan 12 21:31:56 2006
@@ -36,6 +36,7 @@
 import org.apache.ldap.server.exception.ExceptionService;
 import org.apache.ldap.server.normalization.NormalizationService;
 import org.apache.ldap.server.operational.OperationalAttributeService;
+import org.apache.ldap.server.referral.ReferralService;
 import org.apache.ldap.server.schema.SchemaService;
 import org.apache.ldap.server.schema.bootstrap.*;
 import org.apache.ldap.server.subtree.SubentryService;
@@ -159,6 +160,11 @@
         interceptorCfg = new MutableInterceptorConfiguration();
         interceptorCfg.setName( "schemaService" );
         interceptorCfg.setInterceptor( new SchemaService() );
+        list.add( interceptorCfg );
+
+        interceptorCfg = new MutableInterceptorConfiguration();
+        interceptorCfg.setName( "referralService" );
+        interceptorCfg.setInterceptor( new ReferralService() );
         list.add( interceptorCfg );
         
         interceptorCfg = new MutableInterceptorConfiguration();

Modified: directory/trunks/apacheds/src/main/java/org/apache/ldap/server/jndi/ServerContext.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/src/main/java/org/apache/ldap/server/jndi/ServerContext.java?rev=368602&r1=368601&r2=368602&view=diff
==============================================================================
--- directory/trunks/apacheds/src/main/java/org/apache/ldap/server/jndi/ServerContext.java (original)
+++ directory/trunks/apacheds/src/main/java/org/apache/ldap/server/jndi/ServerContext.java Thu Jan 12 21:31:56 2006
@@ -298,19 +298,9 @@
         attributes.put( rdnAttribute, rdnValue );
         attributes.put( JavaLdapSupport.OBJECTCLASS_ATTR, JavaLdapSupport.JCONTAINER_ATTR );
         attributes.put( JavaLdapSupport.OBJECTCLASS_ATTR, JavaLdapSupport.TOP_ATTR );
-        
-        /*
-         * Add the new context to the server which as a side effect adds 
-         * operational attributes to the attributes refering instance which
-         * can them be used to initialize a new ServerLdapContext.  Remember
-         * we need to copy over the controls as well to propagate the complete 
-         * environment besides whats in the hashtable for env.
-         */
+
         nexusProxy.add( target.toString(), target, attributes );
-        ServerLdapContext ctx = new ServerLdapContext( principal, nexusProxy, env, target );
-        Control [] controls = ( Control [] ) ( ( ServerLdapContext ) this ).getRequestControls().clone();
-        ctx.setRequestControls( controls );
-        return ctx;
+        return new ServerLdapContext( principal, nexusProxy, env, target );
     }
 
 

Modified: directory/trunks/apacheds/src/main/java/org/apache/ldap/server/jndi/ServerDirContext.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/src/main/java/org/apache/ldap/server/jndi/ServerDirContext.java?rev=368602&r1=368601&r2=368602&view=diff
==============================================================================
--- directory/trunks/apacheds/src/main/java/org/apache/ldap/server/jndi/ServerDirContext.java (original)
+++ directory/trunks/apacheds/src/main/java/org/apache/ldap/server/jndi/ServerDirContext.java Thu Jan 12 21:31:56 2006
@@ -35,7 +35,6 @@
 import javax.naming.directory.SearchControls;
 import javax.naming.event.EventDirContext;
 import javax.naming.event.NamingListener;
-import javax.naming.ldap.Control;
 import javax.naming.spi.DirStateFactory;
 import javax.naming.spi.DirectoryManager;
 
@@ -350,24 +349,9 @@
             attributes.put( rdnAttribute, rdnValue );
         }
 
-        // Add the new context to the server which as a side effect adds
+        // Add the new entry to the server and return the new context
         getNexusProxy().add( target.toString(), target, attributes );
-
-        // Initialize the new context
-        ServerLdapContext ctx = new ServerLdapContext( getPrincipal(), getNexusProxy(), getEnvironment(), target );
-        Control [] controls = ( ( ServerLdapContext ) this ).getRequestControls();
-
-        if ( controls != null )
-        {
-        	controls = ( Control[] ) controls.clone();
-        }
-        else
-        {
-        	controls = new Control[0];
-        }
-
-        ctx.setRequestControls( controls );
-        return ctx;
+        return new ServerLdapContext( getPrincipal(), getNexusProxy(), getEnvironment(), target );
     }
 
 

Modified: directory/trunks/apacheds/src/main/java/org/apache/ldap/server/partition/DirectoryPartitionNexusProxy.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/src/main/java/org/apache/ldap/server/partition/DirectoryPartitionNexusProxy.java?rev=368602&r1=368601&r2=368602&view=diff
==============================================================================
--- directory/trunks/apacheds/src/main/java/org/apache/ldap/server/partition/DirectoryPartitionNexusProxy.java (original)
+++ directory/trunks/apacheds/src/main/java/org/apache/ldap/server/partition/DirectoryPartitionNexusProxy.java Thu Jan 12 21:31:56 2006
@@ -91,6 +91,7 @@
         c.add( "schemaService" );
         c.add( "subentryService" );
         c.add( "operationalAttributeService" );
+        c.add( "referralService" );
         c.add( "eventService" );
         LOOKUP_BYPASS = Collections.unmodifiableCollection( c );
 
@@ -101,6 +102,7 @@
         c.add( "schemaService" );
         c.add( "subentryService" );
         c.add( "operationalAttributeService" );
+        c.add( "referralService" );
         c.add( "eventService" );
         GETMATCHEDDN_BYPASS = Collections.unmodifiableCollection( c );
     }

Added: directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/ReferralLut.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/ReferralLut.java?rev=368602&view=auto
==============================================================================
--- directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/ReferralLut.java (added)
+++ directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/ReferralLut.java Thu Jan 12 21:31:56 2006
@@ -0,0 +1,296 @@
+/*
+ *   Copyright 2004 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.referral;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.naming.InvalidNameException;
+import javax.naming.Name;
+
+import org.apache.ldap.common.name.LdapName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * A simple lookup table of normalized referral distinguished names.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ReferralLut
+{
+    /** the logger for this class */
+    private static final Logger log = LoggerFactory.getLogger( ReferralLut.class );
+    /** the set of names in the LUT */
+    private Set names = new HashSet();
+
+    
+    // -----------------------------------------------------------------------
+    // Methods to access the LUT: all names are expected to be normalized
+    // -----------------------------------------------------------------------
+    
+    
+    /**
+     * Checks if a the entry at a name is a referral.
+     * 
+     * @param dn the normalized name of the referral
+     */
+    public boolean isReferral( Name dn )
+    {
+        if ( dn == null ) throw new IllegalArgumentException( "dn cannot be null" );
+        return names.contains( dn.toString() );
+    }
+    
+    
+    /**
+     * Checks if a the entry at a name is a referral.
+     * 
+     * @param dn the normalized name of the referral
+     */
+    public boolean isReferral( String dn )
+    {
+        if ( dn == null ) throw new IllegalArgumentException( "dn cannot be null" );
+        return names.contains( dn );
+    }
+    
+    
+    /**
+     * Gets the normalized name of the farthest ancestor that is a referral. If the argument 
+     * is a referral it will not be returned.  Only ancestor's (includes parent) are considered.
+     * 
+     * @param dn the name to get the farthest ancestor referral name for
+     * @return the farthest referral ancestor
+     */
+    public Name getFarthestReferralAncestor( Name dn ) 
+    {
+        if ( dn == null ) throw new IllegalArgumentException( "dn cannot be null" );
+        Name farthest = new LdapName();
+        for ( int ii = 0; ii < dn.size(); ii++ )
+        {
+            try
+            {
+                farthest.add( dn.get( ii ) );
+            }
+            catch ( InvalidNameException e )
+            {
+                log.error( "Should never get this when moving names from a proper normalized name!", e );
+            }
+            // do not return dn if it is the farthest referral
+            if ( isReferral( farthest ) && farthest.size() != dn.size() )
+            {
+                return farthest;
+            }
+        }
+        return null;
+    }
+    
+    
+    /**
+     * Gets the normalized name of the nearest ancestor that is a referral.  If the argument
+     * is a referral it will not be returned.  Only ancestor's (includes parent) are considered.
+     * 
+     * @param dn the name to get the nearest ancestor referral name for
+     * @return the nearest referral ancestor or null if one does not exist
+     */
+    public Name getNearestReferralAncestor( Name dn ) 
+    {
+        if ( dn == null ) throw new IllegalArgumentException( "dn cannot be null" );
+        Name cloned = ( Name ) dn.clone();
+        
+        // do not return the argument dn if it is a referral (skip it)
+        if ( cloned.size() > 0 ) 
+        {
+            try
+            {
+                cloned.remove( cloned.size() - 1 );
+            }
+            catch ( InvalidNameException e )
+            {
+                log.error( "Should never get this when removing from a cloned normalized name!", e );
+            }
+        }
+        else
+        {
+            return null;
+        }
+        
+        while ( ! isReferral( cloned ) && cloned.size() > 0 )
+        {
+            try
+            {
+                cloned.remove( cloned.size() - 1 );
+            }
+            catch ( InvalidNameException e )
+            {
+                log.error( "Should never get this when removing from a cloned normalized name!", e );
+            }
+        }
+        return cloned.isEmpty() ? null : cloned;
+    }
+
+    
+    // -----------------------------------------------------------------------
+    // Methods that notify this lookup table of changes to referrals
+    // -----------------------------------------------------------------------
+    
+    
+    /**
+     * Called to add an entry to the LUT when a referral is added.
+     * 
+     * @param dn the normalized name of the added referral
+     */
+    public void referralAdded( Name dn )
+    {
+        if ( dn == null ) throw new IllegalArgumentException( "dn cannot be null" );
+        if ( ! names.add( dn.toString() ) && log.isWarnEnabled() )
+        {
+            log.warn( "found " + dn + " in refname lut while adding it" );
+        }
+    }
+    
+    
+    /**
+     * Called to add an entry to the LUT when a referral is added.
+     * 
+     * @param dn the normalized name of the added referral
+     */
+    public void referralAdded( String dn )
+    {
+        if ( dn == null ) throw new IllegalArgumentException( "dn cannot be null" );
+        if ( ! names.add( dn ) && log.isWarnEnabled() )
+        {
+            log.warn( "found " + dn + " in refname lut while adding it" );
+        }
+    }
+    
+    
+    /**
+     * Called delete an entry from the LUT when a referral is deleted.
+     * 
+     * @param dn the normalized name of the deleted referral
+     */
+    public void referralDeleted( Name dn )
+    {
+        if ( dn == null ) throw new IllegalArgumentException( "dn cannot be null" );
+        if ( ! names.remove( dn.toString() ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find " + dn + " in refname lut while deleting it" );
+        }
+    }
+    
+    
+    /**
+     * Called delete an entry from the LUT when a referral is deleted.
+     * 
+     * @param dn the normalized name of the deleted referral
+     */
+    public void referralDeleted( String dn )
+    {
+        if ( dn == null ) throw new IllegalArgumentException( "dn cannot be null" );
+        if ( ! names.remove( dn ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find " + dn + " in refname lut while deleting it" );
+        }
+    }
+    
+    
+    /**
+     * Called to update the LUT when the name of the referral changes due to 
+     * a rename or move in the DIT.
+     * 
+     * @param oldDn the normalized old name for the referral
+     * @param newDn the normalized new name for the referral
+     */
+    public void referralChanged( Name oldDn, Name newDn )
+    {
+        if ( oldDn == null || newDn == null ) throw new IllegalArgumentException( "old or new dn cannot be null" );
+        if ( ! names.remove( oldDn.toString() ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find old name (" + oldDn + ") in refname lut while moving or renaming it" );
+        }
+        if ( ! names.add( newDn.toString() ) && log.isWarnEnabled() )
+        {
+            log.warn( "found new name (" + newDn + ") in refname lut while moving or renaming " + oldDn );
+        }
+    }
+    
+    
+    /**
+     * Called to update the LUT when the name of the referral changes due to 
+     * a rename or move in the DIT.
+     * 
+     * @param oldDn the normalized old name for the referral
+     * @param newDn the normalized new name for the referral
+     */
+    public void referralChanged( String oldDn, String newDn )
+    {
+        if ( oldDn == null || newDn == null ) throw new IllegalArgumentException( "old or new dn cannot be null" );
+        if ( ! names.remove( oldDn ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find old name (" + oldDn + ") in refname lut while moving or renaming it" );
+        }
+        if ( ! names.add( newDn ) && log.isWarnEnabled() )
+        {
+            log.warn( "found new name (" + newDn + ") in refname lut while moving or renaming " + oldDn );
+        }
+    }
+    
+    
+    /**
+     * Called to update the LUT when the name of the referral changes due to 
+     * a rename or move in the DIT.
+     * 
+     * @param oldDn the normalized old name for the referral
+     * @param newDn the normalized new name for the referral
+     */
+    public void referralChanged( Name oldDn, String newDn )
+    {
+        if ( oldDn == null || newDn == null ) throw new IllegalArgumentException( "old or new dn cannot be null" );
+        if ( ! names.remove( oldDn.toString() ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find old name (" + oldDn + ") in refname lut while moving or renaming it" );
+        }
+        if ( ! names.add( newDn ) && log.isWarnEnabled() )
+        {
+            log.warn( "found new name (" + newDn + ") in refname lut while moving or renaming " + oldDn );
+        }
+    }
+    
+    
+    /**
+     * Called to update the LUT when the name of the referral changes due to 
+     * a rename or move in the DIT.
+     * 
+     * @param oldDn the normalized old name for the referral
+     * @param newDn the normalized new name for the referral
+     */
+    public void referralChanged( String oldDn, Name newDn )
+    {
+        if ( oldDn == null || newDn == null ) throw new IllegalArgumentException( "old or new dn cannot be null" );
+        if ( ! names.remove( oldDn ) && log.isWarnEnabled() )
+        {
+            log.warn( "cound not find old name (" + oldDn + ") in refname lut while moving or renaming it" );
+        }
+        if ( ! names.add( newDn ) && log.isWarnEnabled() )
+        {
+            log.warn( "found new name (" + newDn + ") in refname lut while moving or renaming " + oldDn );
+        }
+    }
+}

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

Added: directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/ReferralService.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/ReferralService.java?rev=368602&view=auto
==============================================================================
--- directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/ReferralService.java (added)
+++ directory/trunks/apacheds/src/main/java/org/apache/ldap/server/referral/ReferralService.java Thu Jan 12 21:31:56 2006
@@ -0,0 +1,559 @@
+/*
+ *   Copyright 2004 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.referral;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.apache.commons.lang.NotImplementedException;
+
+import org.apache.ldap.common.codec.util.LdapURL;
+import org.apache.ldap.common.codec.util.LdapURLEncodingException;
+import org.apache.ldap.common.exception.LdapNamingException;
+import org.apache.ldap.common.exception.LdapReferralException;
+import org.apache.ldap.common.filter.ExprNode;
+import org.apache.ldap.common.filter.LeafNode;
+import org.apache.ldap.common.filter.SimpleNode;
+import org.apache.ldap.common.message.ResultCodeEnum;
+import org.apache.ldap.common.name.DnParser;
+import org.apache.ldap.common.name.LdapName;
+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.interceptor.BaseInterceptor;
+import org.apache.ldap.server.interceptor.NextInterceptor;
+import org.apache.ldap.server.invocation.Invocation;
+import org.apache.ldap.server.invocation.InvocationStack;
+import org.apache.ldap.server.jndi.ServerLdapContext;
+import org.apache.ldap.server.partition.DirectoryPartition;
+import org.apache.ldap.server.partition.DirectoryPartitionNexus;
+import org.apache.ldap.server.partition.DirectoryPartitionNexusProxy;
+import org.apache.ldap.server.schema.AttributeTypeRegistry;
+import org.apache.ldap.server.schema.ConcreteNameComponentNormalizer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * An service which is responsible referral handling behavoirs.  It manages 
+ * referral handling behavoir when the {@link Context.REFERRAL} is implicitly
+ * or explicitly set to "ignore", when set to "throw" and when set to "follow". 
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ReferralService extends BaseInterceptor
+{
+    private static final Logger log = LoggerFactory.getLogger( ReferralService.class );
+    private static final String IGNORE = "ignore";
+    private static final String THROW = "throw";
+    private static final String FOLLOW = "follow";
+    private static final String REFERRAL_OC = "referral";
+    private static final String OBJCLASS_ATTR = "objectClass";
+    private static final Collection SEARCH_BYPASS;
+    private static final String REF_ATTR = "ref";
+
+    private ReferralLut lut = new ReferralLut();
+    private DnParser parser;
+    private Hashtable env;
+
+    static 
+    {
+        /*
+         * These are the services that we will bypass while searching for referrals in
+         * partitions of the system during startup and during add/remove partition ops
+         */
+        Collection c = new HashSet();
+        c.add( "normalizationService" );
+        c.add( "authenticationService" );
+        c.add( "authorizationService" );
+        c.add( "oldAuthorizationService" );
+        c.add( "schemaService" );
+        c.add( "subentryService" );
+        c.add( "operationalAttributeService" );
+        c.add( "referralService" );
+        c.add( "eventService" );
+        SEARCH_BYPASS = Collections.unmodifiableCollection( c );
+    }
+    
+    
+    static boolean hasValue( Attribute attr, String value ) throws NamingException
+    {
+        if ( attr == null )
+        {
+            return false;
+        }
+        for ( int ii = 0; ii < attr.size(); ii++ )
+        {
+            if ( ! ( attr.get( ii ) instanceof String ) )
+            {
+                continue;
+            }
+            if ( value.equalsIgnoreCase( ( String ) attr.get( ii ) ) )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    static boolean isReferral( Attributes entry ) throws NamingException
+    {
+        Attribute oc = entry.get( OBJCLASS_ATTR );
+        if ( oc == null )
+        {
+            log.warn( "could not find objectClass attribute in entry: " + entry );
+            return false;
+        }
+        for ( int ii = 0; ii < oc.size(); ii++ )
+        {
+            if ( REFERRAL_OC.equalsIgnoreCase( ( String ) oc.get( ii ) ) )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    
+    public void init( DirectoryServiceConfiguration dsConfig, InterceptorConfiguration cfg ) throws NamingException
+    {
+        DirectoryPartitionNexus nexus = dsConfig.getPartitionNexus();
+        AttributeTypeRegistry atr = dsConfig.getGlobalRegistries().getAttributeTypeRegistry();
+        parser = new DnParser( new ConcreteNameComponentNormalizer( atr ) );
+        env = dsConfig.getEnvironment();
+        
+        Iterator suffixes = nexus.listSuffixes( true );
+        while ( suffixes.hasNext() )
+        {
+            Name suffix = new LdapName( ( String ) suffixes.next() );
+            addReferrals( nexus.search( suffix, env, getReferralFilter(), getControls() ), suffix );
+        }
+    }
+
+
+    public void add( NextInterceptor next, String upName, Name normName, Attributes entry ) throws NamingException
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+
+        // handle updating the lut
+        if ( isReferral( entry ) ) 
+        {
+            lut.referralAdded( normName );
+        }
+        
+        // handle a normal add without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            next.add( upName, normName, entry );
+            return;
+        }
+
+        if ( refval.equals( THROW ) )
+        {
+            Name farthest = lut.getFarthestReferralAncestor( normName );
+            if ( farthest == null ) 
+            {
+                next.add( upName, normName, entry );
+                return;
+            }
+            
+            // handle referral here
+            Attributes referral = invocation.getProxy().lookup( farthest, DirectoryPartitionNexusProxy.LOOKUP_BYPASS );
+            Attribute refs = referral.get( REF_ATTR );
+            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() );
+                if ( urlDn.equals( farthest ) )
+                {
+                    // according to the protocol there is no need for the dn since it is the same as this request
+                    list.add( ldapUrl.getScheme() + "://" + ldapUrl.getHost() );
+                    continue;
+                }
+                
+                /*
+                 * If we get here then the DN of the referral was not the same as the 
+                 * DN of the ref LDAP URL.  We must calculate the remaining (difference)
+                 * name past the farthest referral DN which the target name extends.
+                 */
+                Name upNameAsName = new LdapName( upName );
+                int diff = normName.size() - farthest.size();
+                Name extra = new LdapName(); 
+                for ( int jj = 0; jj < diff; jj++ )
+                {
+                    extra.add( upNameAsName.get( farthest.size() + jj ) );
+                }
+
+                urlDn.addAll( extra );
+                list.add( ldapUrl.getScheme() + "://" + ldapUrl.getHost() + "/" + urlDn );
+            }
+            LdapReferralException lre = new LdapReferralException( getRefs( referral ) );
+            throw lre;
+        }
+        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 );
+        }
+    }
+
+    
+    static List getRefs( Attributes referral ) throws NamingException
+    {
+        Attribute ref = referral.get( REF_ATTR );
+        List list = new ArrayList( ref.size() );
+        for ( int ii = 0; ii < ref.size(); ii++ )
+        {
+            list.add( ref.get( ii ) );
+        }
+        return list;
+    }
+    
+    
+    public void delete( NextInterceptor next, Name normName ) throws NamingException 
+    {
+        Invocation invocation = InvocationStack.getInstance().peek();
+        ServerLdapContext caller = ( ServerLdapContext ) invocation.getCaller();
+        String refval = ( String ) caller.getEnvironment().get( Context.REFERRAL );
+
+        // handle updating the lut
+        if ( lut.isReferral( normName ) ) 
+        {
+            lut.referralDeleted( normName );
+        }
+        
+        // handle a normal delete without following referrals
+        if ( refval == null || refval.equals( IGNORE ) )
+        {
+            next.delete( normName );
+            return;
+        }
+        
+        if ( refval.equals( THROW ) )
+        {
+            Name farthest = lut.getFarthestReferralAncestor( normName );
+            if ( farthest == null ) 
+            {
+                next.delete( normName );
+                return;
+            }
+            
+            // handle referral here
+        }
+        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 );
+        }
+    }
+    
+    
+    public void move( NextInterceptor next, Name oldName, Name newParent ) throws NamingException
+    {
+        next.move( oldName, newParent );
+
+        // update the lut of a referral is being moved
+        if ( lut.isReferral( oldName ) )
+        {
+            Name newName = ( Name ) newParent.clone();
+            newName.add( oldName.get( oldName.size() - 1 ) );
+            lut.referralChanged( oldName, newName );
+        }
+    }
+    
+    
+    public void move( NextInterceptor next, Name oldName, Name newParent, String newRdn, boolean deleteOldRdn ) 
+        throws NamingException
+    {
+        next.move( oldName, newParent, newRdn, deleteOldRdn );
+
+        // update the lut of a referral is being moved
+        if ( lut.isReferral( oldName ) )
+        {
+            Name newName = ( Name ) newParent.clone();
+            newName.add( newRdn );
+            lut.referralChanged( oldName, newName );
+        }
+    }
+
+
+    public void modifyRdn( NextInterceptor next, Name oldName, Name newParent, String newRdn, boolean deleteOldRdn ) 
+        throws NamingException
+    {
+        next.modifyRn( oldName, newRdn, deleteOldRdn );
+
+        // update the lut of a referral is being renamed 
+        if ( lut.isReferral( oldName ) )
+        {
+            Name newName = ( Name ) newParent.clone();
+            newName.add( newRdn );
+            lut.referralChanged( oldName, newName );
+        }
+    }
+    
+
+    public void modify( NextInterceptor next, Name name, int modOp, Attributes mods ) throws NamingException
+    {
+        next.modify( name, modOp, mods );
+        
+        // -------------------------------------------------------------------
+        // Check and update lut if we change the objectClass 
+        // -------------------------------------------------------------------
+
+        boolean isTargetReferral = lut.isReferral( name );
+        boolean isOcChange = mods.get( OBJCLASS_ATTR ) != null;
+        boolean modsOcHasReferral = hasValue( mods.get( OBJCLASS_ATTR ), REFERRAL_OC );
+        if ( isOcChange )
+        {
+            switch ( modOp )
+            {
+                /* 
+                 * if ADD op where refferal is added to objectClass of a
+                 * non-referral entry then we add a new referral to lut
+                 */
+                case( DirContext.ADD_ATTRIBUTE ):
+                    if ( modsOcHasReferral && !isTargetReferral )
+                    {
+                        lut.referralAdded( name );
+                    }
+                    break;
+                /* 
+                 * if REMOVE op where refferal is removed from objectClass of a
+                 * referral entry then we remove the referral from lut
+                 */
+                case( DirContext.REMOVE_ATTRIBUTE ):
+                    if ( modsOcHasReferral && isTargetReferral )
+                    {
+                        lut.referralDeleted( name );
+                    }
+                    break;
+                /* 
+                 * if REPLACE op on referral has new set of OC values which does 
+                 * not contain a referral value then we remove the referral from 
+                 * the lut
+                 * 
+                 * if REPLACE op on non-referral has new set of OC values with 
+                 * referral value then we add the new referral to the lut
+                 */
+                case( DirContext.REPLACE_ATTRIBUTE ):
+                    if ( isTargetReferral && ! modsOcHasReferral )
+                    {
+                        lut.referralDeleted( name );
+                    }
+                    else if ( ! isTargetReferral && modsOcHasReferral )
+                    {
+                        lut.referralAdded( name );
+                    }
+                    break;
+                default:
+                    throw new IllegalStateException( "undefined modification operation" );
+            }
+        }
+    }
+    
+    
+    public void modify( NextInterceptor next, Name name, ModificationItem[] mods ) throws NamingException
+    {
+        boolean isTargetReferral = lut.isReferral( name );
+        next.modify( name, mods );
+
+        // -------------------------------------------------------------------
+        // Check and update lut if we change the objectClass 
+        // -------------------------------------------------------------------
+
+        for ( int ii = 0; ii < mods.length; ii++ )
+        {
+            if ( mods[ii].getAttribute().getID().equalsIgnoreCase( OBJCLASS_ATTR ) )
+            {
+                boolean modsOcHasReferral = hasValue( mods[ii].getAttribute(), REFERRAL_OC );
+
+                switch ( mods[ii].getModificationOp() )
+                {
+                    /* 
+                     * if ADD op where refferal is added to objectClass of a
+                     * non-referral entry then we add a new referral to lut
+                     */
+                    case( DirContext.ADD_ATTRIBUTE ):
+                        if ( modsOcHasReferral && !isTargetReferral )
+                        {
+                            lut.referralAdded( name );
+                        }
+                        break;
+                    /* 
+                     * if REMOVE op where refferal is removed from objectClass of a
+                     * referral entry then we remove the referral from lut
+                     */
+                    case( DirContext.REMOVE_ATTRIBUTE ):
+                        if ( modsOcHasReferral && isTargetReferral )
+                        {
+                            lut.referralDeleted( name );
+                        }
+                        break;
+                    /* 
+                     * if REPLACE op on referral has new set of OC values which does 
+                     * not contain a referral value then we remove the referral from 
+                     * the lut
+                     * 
+                     * if REPLACE op on non-referral has new set of OC values with 
+                     * referral value then we add the new referral to the lut
+                     */
+                    case( DirContext.REPLACE_ATTRIBUTE ):
+                        if ( isTargetReferral && ! modsOcHasReferral )
+                        {
+                            lut.referralDeleted( name );
+                        }
+                        else if ( ! isTargetReferral && modsOcHasReferral )
+                        {
+                            lut.referralAdded( name );
+                        }
+                        break;
+                    default:
+                        throw new IllegalStateException( "undefined modification operation" );
+                }
+
+                break;
+            }
+        }
+    }
+    
+    
+    static ExprNode getReferralFilter() 
+    {
+        return new SimpleNode( OBJCLASS_ATTR, REFERRAL_OC, LeafNode.EQUALITY );
+    }
+    
+    
+    static SearchControls getControls()
+    {
+        SearchControls controls = new SearchControls();
+        controls.setReturningObjFlag( false );
+        controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
+        return controls;
+    }
+    
+    
+    public void addContextPartition( NextInterceptor next, DirectoryPartitionConfiguration cfg ) throws NamingException
+    {
+        next.addContextPartition( cfg );
+        
+        // add referrals immediately after adding the new partition
+        DirectoryPartition partition = cfg.getContextPartition();
+        Name suffix = partition.getSuffix( true );
+        Invocation invocation = InvocationStack.getInstance().peek();
+        NamingEnumeration list = invocation.getProxy().search( suffix, env, getReferralFilter(), getControls(), SEARCH_BYPASS );
+        addReferrals( list, suffix );
+    }
+    
+    
+    public void removeContextPartition( NextInterceptor next, Name suffix ) throws NamingException
+    {
+        // remove referrals immediately before removing the partition
+        Invocation invocation = InvocationStack.getInstance().peek();
+        NamingEnumeration list = invocation.getProxy().search( suffix, env, getReferralFilter(), getControls(), SEARCH_BYPASS );
+        deleteReferrals( list, suffix );
+        
+        next.removeContextPartition( suffix );
+    }
+    
+    
+    private void addReferrals( NamingEnumeration referrals, Name base ) throws NamingException
+    {
+        while ( referrals.hasMore() )
+        {
+            SearchResult r = ( SearchResult ) referrals.next();
+            Name referral = null;
+            if ( r.isRelative() )
+            {
+                referral = ( Name ) base.clone();
+                referral.addAll( parser.parse( r.getName() ) );
+            }
+            else
+            {
+                referral = parser.parse( r.getName() );
+            }
+        }
+    }
+    
+    
+    private void deleteReferrals( NamingEnumeration referrals, Name base ) throws NamingException
+    {
+        while ( referrals.hasMore() )
+        {
+            SearchResult r = ( SearchResult ) referrals.next();
+            Name referral = null;
+            if ( r.isRelative() )
+            {
+                referral = ( Name ) base.clone();
+                referral.addAll( parser.parse( r.getName() ) );
+            }
+            else
+            {
+                referral = parser.parse( r.getName() );
+            }
+        }
+    }
+}

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

Modified: directory/trunks/apacheds/src/test/java/org/apache/ldap/server/jndi/RootDSETest.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/src/test/java/org/apache/ldap/server/jndi/RootDSETest.java?rev=368602&r1=368601&r2=368602&view=diff
==============================================================================
--- directory/trunks/apacheds/src/test/java/org/apache/ldap/server/jndi/RootDSETest.java (original)
+++ directory/trunks/apacheds/src/test/java/org/apache/ldap/server/jndi/RootDSETest.java Thu Jan 12 21:31:56 2006
@@ -154,7 +154,6 @@
         Attributes attributes = ctx.getAttributes( "" );
 
         // Added some objectClass attributes to the rootDSE
-
         assertEquals( 2, attributes.size() );
     }
 

Added: directory/trunks/apacheds/src/test/java/org/apache/ldap/server/referral/ReferralLutTest.java
URL: http://svn.apache.org/viewcvs/directory/trunks/apacheds/src/test/java/org/apache/ldap/server/referral/ReferralLutTest.java?rev=368602&view=auto
==============================================================================
--- directory/trunks/apacheds/src/test/java/org/apache/ldap/server/referral/ReferralLutTest.java (added)
+++ directory/trunks/apacheds/src/test/java/org/apache/ldap/server/referral/ReferralLutTest.java Thu Jan 12 21:31:56 2006
@@ -0,0 +1,161 @@
+/*
+ *   Copyright 2004 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.referral;
+
+
+import javax.naming.Name;
+import javax.naming.NamingException;
+
+import org.apache.ldap.common.name.LdapName;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Unit tests for ReferralLut.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class ReferralLutTest extends TestCase
+{
+    public void testNullLimits() 
+    {
+        ReferralLut lut = new ReferralLut();
+        try { lut.isReferral( ( String ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.isReferral( ( Name ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.getFarthestReferralAncestor( ( Name ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.getNearestReferralAncestor( ( Name ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.referralAdded( ( Name ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.referralAdded( ( String ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.referralDeleted( ( Name ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.referralDeleted( ( String ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.referralChanged( ( Name ) null, ( Name ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.referralChanged( ( String ) null, ( String ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.referralChanged( ( Name ) null, ( String ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+        try { lut.referralChanged( ( String ) null, ( Name ) null ); fail( "can't get here" ); } catch( IllegalArgumentException e ) {}
+    }
+    
+    
+    public void testUpdateOperations() throws NamingException
+    {
+        String dn = "ou=users,ou=system";
+        Name name = new LdapName( dn );
+        ReferralLut lut = new ReferralLut();
+
+        // some add delete tests
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralDeleted( dn );
+        assertFalse( lut.isReferral( dn ) );
+
+        assertFalse( lut.isReferral( name ) );
+        lut.referralAdded( name );
+        assertTrue( lut.isReferral( name ) );
+        lut.referralDeleted( name );
+        assertFalse( lut.isReferral( name ) );
+
+        assertFalse( lut.isReferral( name ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( name ) );
+        lut.referralDeleted( name );
+        assertFalse( lut.isReferral( name ) );
+
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( name );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralDeleted( dn );
+        assertFalse( lut.isReferral( dn ) );
+
+        assertFalse( lut.isReferral( name ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( name ) );
+        lut.referralDeleted( dn );
+        assertFalse( lut.isReferral( name ) );
+
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( name );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralDeleted( name );
+        assertFalse( lut.isReferral( dn ) );
+        
+        // change (rename and move) tests
+        String newDn = "ou=people,ou=system";
+        Name newName = new LdapName( newDn );
+
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralChanged( dn, newDn );
+        assertFalse( lut.isReferral( dn ) );
+        assertTrue( lut.isReferral( newDn ) );
+        lut.referralDeleted( dn );
+
+        assertFalse( lut.isReferral( name ) );
+        lut.referralAdded( name );
+        assertTrue( lut.isReferral( name ) );
+        lut.referralChanged( name, newName );
+        assertFalse( lut.isReferral( name ) );
+        assertTrue( lut.isReferral( newName ) );
+        lut.referralDeleted( name );
+
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralChanged( dn, newName );
+        assertFalse( lut.isReferral( dn ) );
+        assertTrue( lut.isReferral( newDn ) );
+        lut.referralDeleted( dn );
+
+        assertFalse( lut.isReferral( dn ) );
+        lut.referralAdded( dn );
+        assertTrue( lut.isReferral( dn ) );
+        lut.referralChanged( name, newDn );
+        assertFalse( lut.isReferral( dn ) );
+        assertTrue( lut.isReferral( newDn ) );
+        lut.referralDeleted( dn );
+    }
+    
+    
+    public void testReferralAncestors() throws NamingException
+    {
+        Name ancestor = new LdapName( "ou=users,ou=system" );
+        Name farthest = new LdapName( "ou=system" );
+        Name nearest = new LdapName( "ou=apache,ou=users,ou=system" );
+        Name testDn = new LdapName( "cn=Alex Karasulu,ou=apache,ou=users,ou=system" );
+        ReferralLut lut = new ReferralLut();
+        assertNull( lut.getNearestReferralAncestor( testDn ) );
+        assertNull( lut.getFarthestReferralAncestor( testDn ) );
+        lut.referralAdded( testDn );
+        assertNull( lut.getNearestReferralAncestor( testDn ) );
+        assertNull( lut.getFarthestReferralAncestor( testDn ) );
+        lut.referralDeleted( testDn );
+        lut.referralAdded( ancestor );
+        assertEquals( ancestor, lut.getNearestReferralAncestor( testDn ) );
+        assertEquals( ancestor, lut.getFarthestReferralAncestor( testDn ) );
+        lut.referralAdded( testDn );
+        assertEquals( ancestor, lut.getNearestReferralAncestor( testDn ) );
+        assertEquals( ancestor, lut.getFarthestReferralAncestor( testDn ) );
+        lut.referralAdded( nearest );
+        assertEquals( nearest, lut.getNearestReferralAncestor( testDn ) );
+        assertEquals( ancestor, lut.getFarthestReferralAncestor( testDn ) );
+        lut.referralAdded( farthest );
+        assertEquals( nearest, lut.getNearestReferralAncestor( testDn ) );
+        assertEquals( farthest, lut.getFarthestReferralAncestor( testDn ) );
+    }
+}

Propchange: directory/trunks/apacheds/src/test/java/org/apache/ldap/server/referral/ReferralLutTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: directory/trunks/ldap-common/src/main/java/org/apache/ldap/common/exception/LdapReferralException.java
URL: http://svn.apache.org/viewcvs/directory/trunks/ldap-common/src/main/java/org/apache/ldap/common/exception/LdapReferralException.java?rev=368602&view=auto
==============================================================================
--- directory/trunks/ldap-common/src/main/java/org/apache/ldap/common/exception/LdapReferralException.java (added)
+++ directory/trunks/ldap-common/src/main/java/org/apache/ldap/common/exception/LdapReferralException.java Thu Jan 12 21:31:56 2006
@@ -0,0 +1,110 @@
+/*
+ *   Copyright 2004 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.common.exception;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ReferralException;
+
+import org.apache.ldap.common.NotImplementedException;
+import org.apache.ldap.common.message.ResultCodeEnum;
+
+
+/**
+ * A ReferralException which associates a resultCode namely the
+ * {@link ResultCodeEnum#REFERRAL} resultCode with the exception.
+ *
+ * @see LdapException
+ * @see ReferralException
+ * @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/jndi/jndi-ldap-gl.html#EXCEPT">
+ * LDAP ResultCode to JNDI Exception Mappings</a>
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class LdapReferralException extends ReferralException implements LdapException
+{
+    static final long serialVersionUID = -8611970137960601723L;
+    private final List refs;
+    private int index = 0;
+
+    
+    /**
+     * @see ReferralException#ReferralException()
+     */
+    public LdapReferralException( Collection refs )
+    {
+        this.refs = new ArrayList( refs );
+    }
+
+
+    /**
+     * @see ReferralException#ReferralException(java.lang.String)
+     */
+    public LdapReferralException( Collection refs, String explanation )
+    {
+        super( explanation );
+        this.refs = new ArrayList( refs );
+    }
+
+
+    /**
+     * Always returns {@link ResultCodeEnum#REFERRAL}
+     *
+     * @see LdapException#getResultCode()
+     */
+    public ResultCodeEnum getResultCode()
+    {
+        return ResultCodeEnum.REFERRAL;
+    }
+
+
+    public Object getReferralInfo()
+    {
+        return refs.get( index );
+    }
+
+
+    public Context getReferralContext() throws NamingException
+    {
+        throw new NotImplementedException();
+    }
+
+
+    public Context getReferralContext( Hashtable arg ) throws NamingException
+    {
+        throw new NotImplementedException();
+    }
+
+
+    public boolean skipReferral()
+    {
+        index++;
+        return index < refs.size();
+    }
+
+
+    public void retryReferral()
+    {
+        throw new NotImplementedException();
+    }
+}

Propchange: directory/trunks/ldap-common/src/main/java/org/apache/ldap/common/exception/LdapReferralException.java
------------------------------------------------------------------------------
    svn:eol-style = native