Author: markt Date: Sat Dec 15 19:55:08 2012 New Revision: 1422332 URL: http://svn.apache.org/viewvc?rev=1422332&view=rev Log: WebSocket 1.0 implementation part 16 of many Re-write the code that identifies the type that a MessaheHandler is associated with. Doing it correctly is non-trivial. Includes a test case.
Added: tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java (with props) Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java?rev=1422332&r1=1422331&r2=1422332&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java Sat Dec 15 19:55:08 2012 @@ -19,6 +19,7 @@ package org.apache.tomcat.websocket; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; import java.net.URI; import java.nio.ByteBuffer; import java.util.HashSet; @@ -59,24 +60,21 @@ public class WsSession implements Sessio @SuppressWarnings("unchecked") @Override public void addMessageHandler(MessageHandler listener) { - Type[] types = ((ParameterizedType) listener.getClass().getGenericSuperclass()).getActualTypeArguments(); - if (types.length != 1) { - // TODO i18n - throw new IllegalArgumentException(); - } - if (types[0].equals(String.class)) { + Type t = getMessageType(listener); + + if (t.equals(String.class)) { if (textMessageHandler != null) { // TODO i18n throw new IllegalStateException(); } textMessageHandler = listener; - } else if (types[0].equals(ByteBuffer.class)) { + } else if (t.equals(ByteBuffer.class)) { if (binaryMessageHandler != null) { // TODO i18n throw new IllegalStateException(); } binaryMessageHandler = listener; - } else if (types[0].equals(PongMessage.class)) { + } else if (t.equals(PongMessage.class)) { if (pongMessageHandler != null) { // TODO i18n throw new IllegalStateException(); @@ -267,6 +265,79 @@ public class WsSession implements Sessio return pongMessageHandler; } + + // Protected so unit tests can use it + protected static Class<?> getMessageType(MessageHandler listener) { + return (Class<?>) getMessageType(listener.getClass()); + } + + + private static Object getMessageType(Class<? extends MessageHandler> clazz) { + + // Look to see if this class implements the generic MessageHandler<> + // interface + + // Get all the interfaces + Type[] interfaces = clazz.getGenericInterfaces(); + for (Type iface : interfaces) { + // Only need to check interfaces that use generics + if (iface instanceof ParameterizedType) { + ParameterizedType pi = (ParameterizedType) iface; + // Look for the MessageHandler<> interface + if (pi.getRawType().equals(MessageHandler.Basic.class) + || pi.getRawType().equals(MessageHandler.Async.class)) { + // Whichever interface it is, there is only one generic + // type. + return getTypeParameter( + clazz, pi.getActualTypeArguments()[0]); + } + } + } + + // Interface not found on this class. Look at the superclass. + Class<? extends MessageHandler> superClazz = + (Class<? extends MessageHandler>) clazz.getSuperclass(); + + Object result = getMessageType(superClazz); + if (result instanceof Class<?>) { + // Superclass implements interface and defines explicit type for + // MessageHandler<> + return result; + } else if (result instanceof Integer) { + // Superclass implements interface and defines unknown type for + // MessageHandler<> + // Map that unknown type to the generic types defined in this class + ParameterizedType superClassType = + (ParameterizedType) clazz.getGenericSuperclass(); + return getTypeParameter(clazz, + superClassType.getActualTypeArguments()[ + ((Integer) result).intValue()]); + } else { + // TODO: Something went wrong. Log an error. + return null; + } + } + + + /* + * For a generic parameter, return either the Class used or if the type + * is unknown, the index for the type in definition of the class + */ + private static Object getTypeParameter(Class<?> clazz, Type argType) { + if (argType instanceof Class<?>) { + return argType; + } else { + TypeVariable<?>[] tvs = clazz.getTypeParameters(); + for (int i = 0; i < tvs.length; i++) { + if (tvs[i].equals(argType)) { + return Integer.valueOf(i); + } + } + return null; + } + } + + private static class DefaultPingMessageHandler implements MessageHandler.Basic<PongMessage> { Added: tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java?rev=1422332&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java (added) +++ tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java Sat Dec 15 19:55:08 2012 @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.tomcat.websocket; + +import javax.websocket.MessageHandler; + +import org.junit.Assert; +import org.junit.Test; + +public class TestWsSession { + + @Test + public void testGetMessageTypeSimple() { + Assert.assertEquals( + String.class, WsSession.getMessageType(new Simple())); + } + + + @Test + public void testGetMessageTypeSubclass() { + Assert.assertEquals(String.class, + WsSession.getMessageType(new SubSimple())); + } + + + @Test + public void testGetMessageTypeGenericSubclass() { + Assert.assertEquals(String.class, + WsSession.getMessageType(new GenericSub())); + } + + + @Test + public void testGetMessageTypeGenericMultipleSubclass() { + Assert.assertEquals(String.class, + WsSession.getMessageType(new GenericMultipleSubSub())); + } + + + @Test + public void testGetMessageTypeGenericMultipleSubclassSwap() { + Assert.assertEquals(String.class, + WsSession.getMessageType(new GenericMultipleSubSubSwap())); + } + + + private static class Simple implements MessageHandler.Basic<String> { + @Override + public void onMessage(String message) { + // NO-OP + } + } + + + private static class SubSimple extends Simple { + } + + + private abstract static class Generic<T> + implements MessageHandler.Basic<T> { + } + + + private static class GenericSub extends Generic<String>{ + + @Override + public void onMessage(String message) { + // NO-OP + } + } + + + private static interface Foo<T> { + void doSomething(T thing); + } + + + private abstract static class GenericMultiple<A,B> + implements MessageHandler.Basic<A>, Foo<B> { + } + + + private abstract static class GenericMultipleSub<X,Y> + extends GenericMultiple<X,Y> { + } + + + private static class GenericMultipleSubSub + extends GenericMultipleSub<String,Boolean> { + + @Override + public void onMessage(String message) { + // NO-OP + } + + @Override + public void doSomething(Boolean thing) { + // NO-OP + } + } + + + private abstract static class GenericMultipleSubSwap<Y,X> + extends GenericMultiple<X,Y> { + } + + + private static class GenericMultipleSubSubSwap + extends GenericMultipleSubSwap<Boolean,String> { + + @Override + public void onMessage(String message) { + // NO-OP + } + + @Override + public void doSomething(Boolean thing) { + // NO-OP + } + } +} Propchange: tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java ------------------------------------------------------------------------------ svn:eol-style = native --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org