Details
-
Feature Request
-
Resolution: Unresolved
-
Major
-
None
-
2.1.3.Final
-
None
Description
Currently it seems impossible to configure a number of decoders that would pass the resulting object to a generic super type message handler.
interface Message {} class SubscribeResponse implements Message {} class KLineResponse implements Message {} ClientEndpointConfig config = ClientEndpointConfig.Builder.create() .decoders(Arrays.asList(SubscribeResponseDecoder.class, KLineResponseDecoder.class)) .build(); class MyMessageHandler implements MessageHandler.Whole<Message> { void onMessage(Message msg) { .... } } session.addMessageHandler(new MyMessageHandler());
When I do this, the implementation chokes with ...
java.lang.IllegalStateException: UT003006: Unable to detect FrameType for clazz interface io.gridley.test.stream.KLineStreamTest$Message at io.undertow.websockets.jsr.FrameHandler.createHandlerWrappers(FrameHandler.java:432) at io.undertow.websockets.jsr.FrameHandler.addHandlerInternal(FrameHandler.java:382) at io.undertow.websockets.jsr.FrameHandler.addHandler(FrameHandler.java:375) at io.undertow.websockets.jsr.UndertowSession.addMessageHandler(UndertowSession.java:117)
To solve this, I currently do some convoluted reflection on my decoders and message handlers like this ...
public void onOpen(Session session, EndpointConfig config) { LOG.info("Open socket: {}", endpointUri); // Map the configured decoders to Map<Decoder.Text<?>, MessageHandler.Whole<?>> handlerMapping = new HashMap<>(); for (Class<? extends Decoder> clazz : config.getDecoders()) { try { Decoder.Text<?> decoder = (Text<?>) clazz.newInstance(); Method method = clazz.getMethod("decode", String.class); Class<?> decodedType = method.getReturnType(); decoder.init(config); for (MessageHandler.Whole<?> handler : messageHandlers) { Class<?> handledType = Arrays.asList(handler.getClass().getMethods()).stream() .filter(m -> m.getName().equals("onMessage")) .map(m -> m.getParameterTypes()[0]) .findAny().get(); if (handledType.isAssignableFrom(decodedType)) { handlerMapping.put(decoder, handler); } } } catch (ReflectiveOperationException ex) { throw new CheckedExceptionWrapper(ex); } } session.addMessageHandler(String.class, msg -> { Decoder.Text<?> decoder = handlerMapping.keySet().stream() .filter(d -> d.willDecode(msg)) .findFirst().orElse(null); if (decoder != null) { try { Object decoded = decoder.decode(msg); MessageHandler.Whole handler = handlerMapping.get(decoder); handler.onMessage(decoded); } catch (DecodeException ex) { throw new CheckedExceptionWrapper(ex); } } else { LOG.info("Raw Message: {}", msg); } }); }
Would it perhaps be possible to this internally in Undertow? The MessageHandler could perhaps be annotated to advice the implementation which FrameType it is handling. All that mapping logic from decoder to message handler with an explicit calls to the decoder and respective message handler should ideally be avoidable in user code.