You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ni...@apache.org on 2005/11/29 15:47:13 UTC

svn commit: r349722 - in /directory/network/trunk/src: java/org/apache/mina/handler/DemuxingIoHandler.java test/org/apache/mina/handler/ test/org/apache/mina/handler/DemuxingIoHandlerTest.java

Author: niklas
Date: Tue Nov 29 06:47:04 2005
New Revision: 349722

URL: http://svn.apache.org/viewcvs?rev=349722&view=rev
Log:
Resolved DIRMINA-118

Added:
    directory/network/trunk/src/test/org/apache/mina/handler/
    directory/network/trunk/src/test/org/apache/mina/handler/DemuxingIoHandlerTest.java   (with props)
Modified:
    directory/network/trunk/src/java/org/apache/mina/handler/DemuxingIoHandler.java

Modified: directory/network/trunk/src/java/org/apache/mina/handler/DemuxingIoHandler.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/java/org/apache/mina/handler/DemuxingIoHandler.java?rev=349722&r1=349721&r2=349722&view=diff
==============================================================================
--- directory/network/trunk/src/java/org/apache/mina/handler/DemuxingIoHandler.java (original)
+++ directory/network/trunk/src/java/org/apache/mina/handler/DemuxingIoHandler.java Tue Nov 29 06:47:04 2005
@@ -4,7 +4,7 @@
 package org.apache.mina.handler;
 
 import java.util.Collections;
-import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.Map;
 import java.util.Set;
 
@@ -16,17 +16,54 @@
 /**
  * A {@link IoHandler} that demuxes <code>messageReceived</code> events
  * to the appropriate {@link MessageHandler}.
- * 
+ * <p>
  * You can freely register and deregister {@link MessageHandler}s using
  * {@link #addMessageHandler(Class, MessageHandler)} and
  * {@link #removeMessageHandler(Class)}.
+ * </p>
+ * <p>
+ * When <code>message</code> is received through a call to 
+ * {@link #messageReceived(IoSession, Object)} the class of the 
+ * <code>message</code> object will be used to find a {@link MessageHandler} for 
+ * that particular message type. If no {@link MessageHandler} instance can be 
+ * found for the immediate class (i.e. <code>message.getClass()</code>) the 
+ * interfaces implemented by the immediate class will be searched in depth-first 
+ * order. If no match can be found for any of the interfaces the search will be 
+ * repeated recursively for the superclass of the immediate class 
+ * (i.e. <code>message.getClass().getSuperclass()</code>).
+ * </p>
+ * <p>
+ * Consider the following type hierarchy (<code>Cx</code> are classes while 
+ * <code>Ix</code> are interfaces):
+ * <pre>
+ *     C3 - I7 - I9
+ *      |    |   /\
+ *      |   I8  I3 I4
+ *      |
+ *     C2 - I5 - I6
+ *      |
+ *     C1 - I1 - I2 - I4
+ *      |         |
+ *      |        I3
+ *    Object          
+ * </pre>
+ * When <code>message</code> is of type <code>C3</code> this hierarchy will be 
+ * searched in the following order:
+ * <code>C3, I7, I8, I9, I3, I4, C2, I5, I6, C1, I1, I2, I3, I4, Object</code>.
+ * </p>
+ * <p>
+ * For efficiency searches will be cached. Calls to 
+ * {@link #addMessageHandler(Class, MessageHandler)} and
+ * {@link #removeMessageHandler(Class)} clear this cache.
+ * </p>
  * 
  * @author The Apache Directory Project
  * @version $Rev$, $Date$
  */
 public class DemuxingIoHandler extends IoHandlerAdapter
 {
-    private final Map type2handler = new HashMap();
+    private final Map findHandlerCache = new Hashtable();
+    private final Map type2handler = new Hashtable();
 
     /**
      * Creates a new instance with no registered {@link MessageHandler}s.
@@ -44,10 +81,8 @@
      */
     public MessageHandler addMessageHandler( Class type, MessageHandler handler )
     {
-        synchronized( type2handler )
-        {
-            return ( MessageHandler ) type2handler.put( type, handler );
-        }
+        findHandlerCache.clear();
+        return ( MessageHandler ) type2handler.put( type, handler );
     }
 
     /**
@@ -58,10 +93,8 @@
      */
     public MessageHandler removeMessageHandler( Class type )
     {
-        synchronized( type2handler )
-        {
-            return ( MessageHandler ) type2handler.remove( type );
-        }
+        findHandlerCache.clear();
+        return ( MessageHandler ) type2handler.remove( type );
     }
     
     
@@ -100,44 +133,70 @@
                     "No message handler found for message: " + message );
         }
     }
-
+    
     private MessageHandler findHandler( Class type )
     {
-        MessageHandler handler = ( MessageHandler ) type2handler.get( type );
-        if( handler == null )
-        {
-            handler = findHandler( type, new IdentityHashSet() );
-        }
-
-        return handler;
+        return findHandler( type, null );
     }
 
     private MessageHandler findHandler( Class type, Set triedClasses )
     {
-        MessageHandler handler;
+        MessageHandler handler = null;
 
-        if( triedClasses.contains( type ) )
+        if( triedClasses != null && triedClasses.contains( type ) )
             return null;
-        triedClasses.add( type );
 
+        /*
+         * Try the cache first.
+         */
+        handler = ( MessageHandler ) findHandlerCache.get( type );
+        if( handler != null )
+            return handler;
+
+        /*
+         * Try the registered handlers for an immediate match.
+         */
         handler = ( MessageHandler ) type2handler.get( type );
+        
         if( handler == null )
         {
-            handler = findHandler( type, triedClasses );
-            if( handler != null )
-                return handler;
-
+            /*
+             * No immediate match could be found. Search the type's interfaces.
+             */
+            
+            if( triedClasses == null )
+                triedClasses = new IdentityHashSet();
+            triedClasses.add( type );
+            
             Class[] interfaces = type.getInterfaces();
             for( int i = 0; i < interfaces.length; i ++ )
             {
                 handler = findHandler( interfaces[ i ], triedClasses );
                 if( handler != null )
-                    return handler;
+                    break;
             }
-
-            return null;
         }
-        else
-            return handler;
+        
+        if( handler == null )
+        {
+            /*
+             * No match in type's interfaces could be found. Search the 
+             * superclass.
+             */
+            
+            Class superclass = type.getSuperclass();
+            if( superclass != null )
+                handler = findHandler( superclass );
+        }
+        
+        /*
+         * Make sure the handler is added to the cache. By updating the cache
+         * here all the types (superclasses and interfaces) in the path which 
+         * led to a match will be cached along with the immediate message type.
+         */
+        if( handler != null )
+            findHandlerCache.put( type, handler );
+        
+        return handler;
     }
 }

Added: directory/network/trunk/src/test/org/apache/mina/handler/DemuxingIoHandlerTest.java
URL: http://svn.apache.org/viewcvs/directory/network/trunk/src/test/org/apache/mina/handler/DemuxingIoHandlerTest.java?rev=349722&view=auto
==============================================================================
--- directory/network/trunk/src/test/org/apache/mina/handler/DemuxingIoHandlerTest.java (added)
+++ directory/network/trunk/src/test/org/apache/mina/handler/DemuxingIoHandlerTest.java Tue Nov 29 06:47:04 2005
@@ -0,0 +1,256 @@
+/*
+ *   @(#) $Id$
+ *
+ *   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.mina.handler;
+
+import junit.framework.TestCase;
+
+import org.apache.mina.common.IoSession;
+import org.easymock.MockControl;
+
+/**
+ * Tests {@link org.apache.mina.handler.DemuxingIoHandler}.
+ *
+ * @author The Apache Directory Project (dev@directory.apache.org)
+ * @version $Rev$, $Date$
+ */
+public class DemuxingIoHandlerTest extends TestCase
+{
+    MockControl mockHandler1;
+    MockControl mockHandler2;
+    MockControl mockHandler3;
+    
+    MessageHandler handler1;
+    MessageHandler handler2;
+    MessageHandler handler3;
+    
+    IoSession session;
+    
+    Object[] msg;
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        
+        /*
+         * Create the messages.
+         */
+        msg = new Object[9];
+        msg[0] = new C1();
+        msg[1] = new C2();
+        msg[2] = new C3();
+        msg[3] = new C1();
+        msg[4] = new C2();
+        msg[5] = new C3();
+        msg[6] = new C1();
+        msg[7] = new C2();
+        msg[8] = new C3();
+        
+        /*
+         * Create mocks.
+         */
+        mockHandler1 = MockControl.createControl( MessageHandler.class );
+        mockHandler2 = MockControl.createControl( MessageHandler.class );
+        mockHandler3 = MockControl.createControl( MessageHandler.class );
+        
+        handler1 = ( MessageHandler ) mockHandler1.getMock();
+        handler2 = ( MessageHandler ) mockHandler2.getMock();
+        handler3 = ( MessageHandler ) mockHandler3.getMock();
+        
+        session = ( IoSession ) MockControl.createControl( IoSession.class ).getMock();    
+    }
+
+    public void testFindHandlerByClass() throws Exception
+    {
+        /*
+         * Record expectations.
+         */
+        handler1.messageReceived( session, msg[0] );
+        handler1.messageReceived( session, msg[1] );
+        handler1.messageReceived( session, msg[2] );
+        handler1.messageReceived( session, msg[3] );
+        handler2.messageReceived( session, msg[4] );
+        handler2.messageReceived( session, msg[5] );
+        handler1.messageReceived( session, msg[6] );
+        handler2.messageReceived( session, msg[7] );
+        handler3.messageReceived( session, msg[8] );
+        
+        /*
+         * Replay.
+         */
+        mockHandler1.replay();
+        mockHandler2.replay();
+        mockHandler3.replay();
+        
+        DemuxingIoHandler ioHandler = new DemuxingIoHandler();
+        
+        /*
+         * First round. All messages should be handled by handler1
+         */
+        ioHandler.addMessageHandler( C1.class, ( MessageHandler ) mockHandler1.getMock() );
+        ioHandler.messageReceived( session, msg[0] );
+        ioHandler.messageReceived( session, msg[1] );
+        ioHandler.messageReceived( session, msg[2] );
+        
+        /*
+         * Second round. C1 messages should be handled by handler1. C2 and C3
+         * messages should be handled by handler2.
+         */
+        ioHandler.addMessageHandler( C2.class, ( MessageHandler ) mockHandler2.getMock() );
+        ioHandler.messageReceived( session, msg[3] );
+        ioHandler.messageReceived( session, msg[4] );
+        ioHandler.messageReceived( session, msg[5] );
+        
+        /*
+         * Third round. C1 messages should be handled by handler1, C2 by 
+         * handler2 and C3 by handler3.
+         */
+        ioHandler.addMessageHandler( C3.class, ( MessageHandler ) mockHandler3.getMock() );
+        ioHandler.messageReceived( session, msg[6] );
+        ioHandler.messageReceived( session, msg[7] );
+        ioHandler.messageReceived( session, msg[8] );
+        
+        /*
+         * Verify.
+         */
+        mockHandler1.verify();
+        mockHandler2.verify();        
+        mockHandler3.verify();        
+    }
+    
+    public void testFindHandlerByInterface() throws Exception
+    {
+        /*
+         * Record expectations.
+         */
+        handler1.messageReceived( session, msg[0] );
+        handler1.messageReceived( session, msg[1] );
+        handler1.messageReceived( session, msg[2] );
+        handler1.messageReceived( session, msg[3] );
+        handler2.messageReceived( session, msg[4] );
+        handler1.messageReceived( session, msg[5] );
+        handler3.messageReceived( session, msg[6] );
+        handler2.messageReceived( session, msg[7] );
+        handler3.messageReceived( session, msg[8] );
+        
+        /*
+         * Replay.
+         */
+        mockHandler1.replay();
+        mockHandler2.replay();
+        mockHandler3.replay();
+        
+        DemuxingIoHandler ioHandler = new DemuxingIoHandler();
+        
+        /*
+         * First round. All messages should be handled by handler1
+         */
+        ioHandler.addMessageHandler( I4.class, ( MessageHandler ) mockHandler1.getMock() );
+        ioHandler.messageReceived( session, msg[0] );
+        ioHandler.messageReceived( session, msg[1] );
+        ioHandler.messageReceived( session, msg[2] );
+        
+        /*
+         * Second round. C1 and C3 messages should be handled by handler1. C2
+         * messages should be handled by handler2.
+         */
+        ioHandler.addMessageHandler( I6.class, ( MessageHandler ) mockHandler2.getMock() );
+        ioHandler.messageReceived( session, msg[3] );
+        ioHandler.messageReceived( session, msg[4] );
+        ioHandler.messageReceived( session, msg[5] );
+        
+        /*
+         * Third round. C1 and C3 messages should be handled by handler3. C2
+         * messages should be handled by handler2.
+         */
+        ioHandler.addMessageHandler( I3.class, ( MessageHandler ) mockHandler3.getMock() );
+        ioHandler.messageReceived( session, msg[6] );
+        ioHandler.messageReceived( session, msg[7] );
+        ioHandler.messageReceived( session, msg[8] );
+        
+        /*
+         * Verify.
+         */
+        mockHandler1.verify();
+        mockHandler2.verify();        
+        mockHandler3.verify();        
+    }
+    
+    /*
+     * Define some interfaces and classes used when testing the findHandler
+     * method. This is what the hierarchy looks like:
+     * 
+     * C3 - I7 - I9
+     *  |    |   /\
+     *  |   I8  I3 I4
+     *  |
+     * C2 - I5 - I6
+     *  |
+     * C1 - I1 - I2 - I4
+     *            |
+     *           I3
+     */
+    
+    public interface I1
+    {
+    }
+    
+    public interface I2 extends I3
+    {
+    }
+    
+    public interface I3
+    {
+    }
+    
+    public interface I4
+    {
+    }
+    
+    public static class C1 implements I1, I2, I4
+    {
+    }
+    
+    public interface I5
+    {
+    }
+    
+    public interface I6
+    {
+    }
+    
+    public static class C2 extends C1 implements I5, I6
+    {
+    }    
+    
+    public interface I7 extends I8
+    {
+    }
+    
+    public interface I8
+    {
+    }
+    
+    public interface I9 extends I3, I4
+    {
+    }
+    
+    public static class C3 extends C2 implements I7, I9
+    {
+    }    
+}

Propchange: directory/network/trunk/src/test/org/apache/mina/handler/DemuxingIoHandlerTest.java
------------------------------------------------------------------------------
    svn:keywords = Id