You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2010/05/08 16:50:10 UTC

svn commit: r942396 - in /directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers: ReferralAwareRequestHandler.java SearchHandler.java

Author: elecharny
Date: Sat May  8 14:50:09 2010
New Revision: 942396

URL: http://svn.apache.org/viewvc?rev=942396&view=rev
Log:
Merged the abstract ReferralAwareRequestHandler with the SearchHandler, as we dn't ave any other extending class. It makes it easier to understand how the search request is handled.

Removed:
    directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/ReferralAwareRequestHandler.java
Modified:
    directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java

Modified: directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java?rev=942396&r1=942395&r2=942396&view=diff
==============================================================================
--- directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java (original)
+++ directory/apacheds/trunk/protocol-ldap/src/main/java/org/apache/directory/server/ldap/handlers/SearchHandler.java Sat May  8 14:50:09 2010
@@ -36,7 +36,6 @@ import org.apache.directory.server.core.
 import org.apache.directory.server.i18n.I18n;
 import org.apache.directory.server.ldap.LdapSession;
 import org.apache.directory.server.ldap.handlers.controls.PagedSearchContext;
-import org.apache.directory.shared.ldap.codec.LdapConstants;
 import org.apache.directory.shared.ldap.codec.controls.ManageDsaITControl;
 import org.apache.directory.shared.ldap.codec.search.controls.pagedSearch.PagedResultsControl;
 import org.apache.directory.shared.ldap.codec.search.controls.persistentSearch.PersistentSearchControl;
@@ -47,6 +46,8 @@ import org.apache.directory.shared.ldap.
 import org.apache.directory.shared.ldap.entry.EntryAttribute;
 import org.apache.directory.shared.ldap.entry.Value;
 import org.apache.directory.shared.ldap.exception.LdapException;
+import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
+import org.apache.directory.shared.ldap.exception.LdapOperationException;
 import org.apache.directory.shared.ldap.exception.OperationAbandonedException;
 import org.apache.directory.shared.ldap.filter.EqualityNode;
 import org.apache.directory.shared.ldap.filter.OrNode;
@@ -59,12 +60,14 @@ import org.apache.directory.shared.ldap.
 import org.apache.directory.shared.ldap.message.internal.InternalLdapResult;
 import org.apache.directory.shared.ldap.message.internal.InternalReferral;
 import org.apache.directory.shared.ldap.message.internal.InternalResponse;
+import org.apache.directory.shared.ldap.message.internal.InternalResultResponseRequest;
 import org.apache.directory.shared.ldap.message.internal.InternalSearchRequest;
 import org.apache.directory.shared.ldap.message.internal.InternalSearchResponseDone;
 import org.apache.directory.shared.ldap.message.internal.InternalSearchResponseEntry;
 import org.apache.directory.shared.ldap.message.internal.InternalSearchResponseReference;
 import org.apache.directory.shared.ldap.name.DN;
 import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.ExceptionUtils;
 import org.apache.directory.shared.ldap.util.LdapURL;
 import org.apache.directory.shared.ldap.util.StringTools;
 import org.slf4j.Logger;
@@ -77,7 +80,7 @@ import org.slf4j.LoggerFactory;
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  * @version $Rev: 664302 $
  */
-public class SearchHandler extends ReferralAwareRequestHandler<InternalSearchRequest>
+public class SearchHandler extends LdapRequestHandler<InternalSearchRequest>
 {
     private static final Logger LOG = LoggerFactory.getLogger( SearchHandler.class );
 
@@ -164,6 +167,45 @@ public class SearchHandler extends Refer
     
     
     /**
+     * {@inheritDoc}
+     */
+    public final void handle( LdapSession session, InternalSearchRequest req ) throws Exception
+    {
+        LOG.debug( "Handling single reply request: {}", req );
+        
+        // First, if we have the ManageDSAIt control, go directly
+        // to the handling without pre-processing the request
+        if ( req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
+        {
+            // If the ManageDsaIT control is present, we will
+            // consider that the user wants to get entry which
+            // are referrals as plain entry. We have to return
+            // SearchResponseEntry elements instead of 
+            // SearchResponseReference elements.
+            LOG.debug( "ManageDsaITControl detected." );
+            handleIgnoringReferrals( session, req );
+        }
+        else
+        {
+            // No ManageDsaIT control. If the found entries is a referral,
+            // we will return SearchResponseReference elements.
+            LOG.debug( "ManageDsaITControl NOT detected." );
+    
+            switch ( req.getType() )
+            {
+                case SEARCH_REQUEST:
+                    handleWithReferrals( session, req );
+                    break;
+
+                default:
+                    throw new IllegalStateException( I18n.err( I18n.ERR_685, req ) );
+            }
+            
+        }
+    }
+
+    
+    /**
      * Handles search requests on the RootDSE. 
      * 
      * @param session the LdapSession for which this search is conducted 
@@ -924,7 +966,7 @@ public class SearchHandler extends Refer
      * @param session the associated session
      * @param req the received SearchRequest
      */
-    public void handleIgnoringReferrals( LdapSession session, InternalSearchRequest req )
+    private void handleIgnoringReferrals( LdapSession session, InternalSearchRequest req )
     {
         if ( IS_DEBUG )
         {
@@ -1028,16 +1070,17 @@ public class SearchHandler extends Refer
     /**
      * Handles processing with referrals without ManageDsaIT control.
      */
-    public void handleWithReferrals( LdapSession session, DN reqTargetDn, InternalSearchRequest req ) throws LdapException
+    private void handleWithReferrals( LdapSession session, InternalSearchRequest req ) throws LdapException
     {
         InternalLdapResult result = req.getResultResponse().getLdapResult();
         Entry entry = null;
         boolean isReferral = false;
         boolean isparentReferral = false;
-        ReferralManager referralManager = session.getCoreSession().getDirectoryService().getReferralManager();
+        DirectoryService directoryService = session.getCoreSession().getDirectoryService();
+        ReferralManager referralManager = directoryService.getReferralManager();
+        DN reqTargetDn = req.getBase();
         
-        reqTargetDn.normalize( session.getCoreSession().getDirectoryService().
-            getSchemaManager().getNormalizerMapping() );
+        reqTargetDn.normalize( directoryService.getSchemaManager().getNormalizerMapping() );
         
         // Check if the entry itself is a referral
         referralManager.lockRead();
@@ -1278,4 +1321,308 @@ public class SearchHandler extends Refer
         
         return subschemaSubentryDnNorm.equals( baseNormForm );
     }
+
+
+    /**
+     * Handles processing with referrals without ManageDsaIT control and with 
+     * an ancestor that is a referral.  The original entry was not found and 
+     * the walk of the ancestry returned a referral.
+     * 
+     * @param referralAncestor the farthest referral ancestor of the missing 
+     * entry  
+     */
+    public InternalReferral getReferralOnAncestorForSearch( LdapSession session, InternalSearchRequest req, 
+        ClonedServerEntry referralAncestor ) throws Exception
+    {
+        LOG.debug( "Inside getReferralOnAncestor()" );
+     
+        EntryAttribute refAttr = referralAncestor.getOriginalEntry()
+            .get( SchemaConstants.REF_AT );
+        InternalReferral referral = new ReferralImpl();
+
+        for ( Value<?> value : refAttr )
+        {
+            String ref = value.getString();
+
+            LOG.debug( "Calculating LdapURL for referrence value {}", ref );
+
+            // need to add non-ldap URLs as-is
+            if ( ! ref.startsWith( "ldap" ) )
+            {
+                referral.addLdapUrl( ref );
+                continue;
+            }
+            
+            // Parse the ref value   
+            LdapURL ldapUrl = new LdapURL();
+            try
+            {
+                ldapUrl.parse( ref.toCharArray() );
+            }
+            catch ( LdapURLEncodingException e )
+            {
+                LOG.error( I18n.err( I18n.ERR_165, ref, referralAncestor ) );
+            }
+            
+            // Normalize the DN to check for same dn
+            DN urlDn = new DN( ldapUrl.getDn().getName() );
+            urlDn.normalize( session.getCoreSession().getDirectoryService().getSchemaManager()
+                .getNormalizerMapping() ); 
+            
+            if ( urlDn.getNormName().equals( req.getBase().getNormName() ) )
+            {
+                ldapUrl.setForceScopeRendering( true );
+                ldapUrl.setAttributes( req.getAttributes() );
+                ldapUrl.setScope( req.getScope().getScope() );
+                referral.addLdapUrl( ldapUrl.toString() );
+                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.
+             */
+            int diff = req.getBase().size() - referralAncestor.getDn().size();
+            DN extra = new DN();
+
+            // TODO - fix this by access unormalized RDN values
+            // seems we have to do this because get returns normalized rdns
+            DN reqUnnormalizedDn = new DN( req.getBase().getName() );
+            for ( int jj = 0; jj < diff; jj++ )
+            {
+                extra.add( reqUnnormalizedDn.get( referralAncestor.getDn().size() + jj ) );
+            }
+
+            ldapUrl.getDn().addAll( extra );
+            ldapUrl.setForceScopeRendering( true );
+            ldapUrl.setAttributes( req.getAttributes() );
+            ldapUrl.setScope( req.getScope().getScope() );
+            referral.addLdapUrl( ldapUrl.toString() );
+        }
+        
+        return referral;
+    }
+
+    
+    /**
+     * Handles processing with referrals without ManageDsaIT control and with 
+     * an ancestor that is a referral.  The original entry was not found and 
+     * the walk of the ancestry returned a referral.
+     * 
+     * @param referralAncestor the farthest referral ancestor of the missing 
+     * entry  
+     */
+    public InternalReferral getReferralOnAncestor( LdapSession session, DN reqTargetDn, InternalSearchRequest req, 
+        ClonedServerEntry referralAncestor ) throws Exception
+    {
+        LOG.debug( "Inside getReferralOnAncestor()" );
+        
+        EntryAttribute refAttr =referralAncestor.getOriginalEntry()
+            .get( SchemaConstants.REF_AT );
+        InternalReferral referral = new ReferralImpl();
+
+        for ( Value<?> value : refAttr )
+        {
+            String ref = value.getString();
+
+            LOG.debug( "Calculating LdapURL for referrence value {}", ref );
+
+            // need to add non-ldap URLs as-is
+            if ( ! ref.startsWith( "ldap" ) )
+            {
+                referral.addLdapUrl( ref );
+                continue;
+            }
+            
+            // parse the ref value and normalize the DN  
+            LdapURL ldapUrl = new LdapURL();
+            try
+            {
+                ldapUrl.parse( ref.toCharArray() );
+            }
+            catch ( LdapURLEncodingException e )
+            {
+                LOG.error( I18n.err( I18n.ERR_165, ref, referralAncestor ) );
+            }
+            
+            DN urlDn = new DN( ldapUrl.getDn().getName() );
+            urlDn.normalize( session.getCoreSession().getDirectoryService().getSchemaManager()
+                .getNormalizerMapping() ); 
+            
+            if ( urlDn.getNormName().equals( referralAncestor.getDn().getNormName() ) )
+            {
+                // according to the protocol there is no need for the dn since it is the same as this request
+                StringBuilder buf = new StringBuilder();
+                buf.append( ldapUrl.getScheme() );
+                buf.append( ldapUrl.getHost() );
+
+                if ( ldapUrl.getPort() > 0 )
+                {
+                    buf.append( ":" );
+                    buf.append( ldapUrl.getPort() );
+                }
+
+                referral.addLdapUrl( buf.toString() );
+                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.
+             */
+            int diff = reqTargetDn.size() - referralAncestor.getDn().size();
+            DN extra = new DN();
+
+            // TODO - fix this by access unormalized RDN values
+            // seems we have to do this because get returns normalized rdns
+            DN reqUnnormalizedDn = new DN( reqTargetDn.getName() );
+            for ( int jj = 0; jj < diff; jj++ )
+            {
+                extra.add( reqUnnormalizedDn.get( referralAncestor.getDn().size() + jj ) );
+            }
+
+            urlDn.addAll( extra );
+
+            StringBuilder buf = new StringBuilder();
+            buf.append( ldapUrl.getScheme() );
+            buf.append( ldapUrl.getHost() );
+
+            if ( ldapUrl.getPort() > 0 )
+            {
+                buf.append( ":" );
+                buf.append( ldapUrl.getPort() );
+            }
+
+            buf.append( "/" );
+            buf.append( LdapURL.urlEncode( urlDn.getName(), false ) );
+            referral.addLdapUrl( buf.toString() );
+        }
+        
+        return referral;
+    }
+
+    
+    /**
+     * Handles processing with referrals without ManageDsaIT control.
+     */
+    public void handleException( LdapSession session, InternalResultResponseRequest req, Exception e )
+    {
+        InternalLdapResult result = req.getResultResponse().getLdapResult();
+
+        /*
+         * Set the result code or guess the best option.
+         */
+        ResultCodeEnum code;
+        
+        if ( e instanceof LdapOperationException )
+        {
+            code = ( ( LdapOperationException ) e ).getResultCode();
+        }
+        else
+        {
+            code = ResultCodeEnum.getBestEstimate( e, req.getType() );
+        }
+        
+        result.setResultCode( code );
+
+        /*
+         * Setup the error message to put into the request and put entire
+         * exception into the message if we are in debug mode.  Note we 
+         * embed the result code name into the message.
+         */
+        String msg = code.toString() + ": failed for " + req + ": " + e.getLocalizedMessage();
+        LOG.debug( msg, e );
+        
+        if ( IS_DEBUG )
+        {
+            msg += ":\n" + ExceptionUtils.getStackTrace( e );
+        }
+        
+        result.setErrorMessage( msg );
+
+        if ( e instanceof LdapOperationException )
+        {
+            LdapOperationException ne = ( LdapOperationException ) e;
+
+            // Add the matchedDN if necessary
+            boolean setMatchedDn = 
+                code == ResultCodeEnum.NO_SUCH_OBJECT             || 
+                code == ResultCodeEnum.ALIAS_PROBLEM              ||
+                code == ResultCodeEnum.INVALID_DN_SYNTAX          || 
+                code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM;
+            
+            if ( ( ne.getResolvedDn() != null ) && setMatchedDn )
+            {
+                result.setMatchedDn( ( DN ) ne.getResolvedDn() );
+            }
+        }
+
+        session.getIoSession().write( req.getResultResponse() );
+    }
+    
+    
+    /**
+     * Searches up the ancestry of a DN searching for the farthest referral 
+     * ancestor.  This is required to properly handle referrals.  Note that 
+     * this function is quite costly since it attempts to lookup all the 
+     * ancestors up the hierarchy just to see if they represent referrals. 
+     * Techniques can be employed later to improve this performance hit by
+     * having an intelligent referral cache.
+     *
+     * @return the farthest referral ancestor or null
+     * @throws Exception if there are problems during this search
+     */
+    public static final Entry getFarthestReferralAncestor( LdapSession session, DN target ) 
+        throws Exception
+    {
+        Entry entry;
+        Entry farthestReferralAncestor = null;
+        DN dn = ( DN ) target.clone();
+        
+        try
+        {
+            dn.remove( dn.size() - 1 );
+        }
+        catch ( LdapInvalidDnException e2 )
+        {
+            // never thrown
+        }
+        
+        while ( ! dn.isEmpty() )
+        {
+            LOG.debug( "Walking ancestors of {} to find referrals.", dn );
+            
+            try
+            {
+                entry = session.getCoreSession().lookup( dn );
+
+                boolean isReferral = ((ClonedServerEntry)entry).getOriginalEntry().contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.REFERRAL_OC );
+                
+                if ( isReferral )
+                {
+                    farthestReferralAncestor = entry;
+                }
+
+                dn.remove( dn.size() - 1 );
+            }
+            catch ( LdapException e )
+            {
+                LOG.debug( "Entry for {} not found.", dn );
+
+                // update the DN as we strip last component 
+                try
+                {
+                    dn.remove( dn.size() - 1 );
+                }
+                catch ( LdapInvalidDnException e1 )
+                {
+                    // never happens
+                }
+            }
+        }
+        
+        return farthestReferralAncestor;
+    }
 }
\ No newline at end of file