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