package com.github.czyzby.websocket; import com.badlogic.gdx.utils.ObjectMap; import com.github.czyzby.websocket.data.WebSocketException; /** This is the proposed implementation of a {@link WebSocketListener} when using communicating with the server using * serialized objects rather than raw strings or bytes. Instead of forcing the user to determine packet type manually * (with {@code instanceof} or class comparing), this listener allows to register {@link Handler handlers} to each * packet type - when the selected type of packet is received, registered handler will be invoked. This allows to build * applications using event-driven approach. Errors during packet handling are delegated to * {@link #onError(WebSocket, Throwable)} method rather than rethrown. * * @author MJ * @see #registerHandler(Class, Handler) * @see Handler */ public class WebSocketHandler extends AbstractWebSocketListener { /** Maps class of expected packets to their handlers. */ private final ObjectMap<Class<?>, Handler<Object>> handlers = new ObjectMap<Class<?>, Handler<Object>>(); /** Used as default value when invoking {@link ObjectMap#get(Object, Object)} on {@link #handlers} to prevent * NPE. */ private final Handler<Object> unknown = new Handler<Object>() { @Override public boolean handle(final WebSocket webSocket, final Object packet) { if (failIfNoHandler) { onError(webSocket, new WebSocketException("Unknown packet type: " + packet.getClass())); } return NOT_HANDLED; } }; private boolean failIfNoHandler = true; /** @param packetClass class of the packet that should be passed to the selected handler. * @param handler will be notified when the chosen type of packet is received. Should be prepared to handle the * specific packet class, otherwise {@link ClassCastException} might be thrown. */ @SuppressWarnings("unchecked") public void registerHandler(final Class<?> packetClass, final Handler<?> handler) { handlers.put(packetClass, (Handler<Object>) handler); } /** @param failIfNoHandler if true and a web socket receives packet that has no handler registered to its class, an * exception will passed to {@link #onError(WebSocket, Throwable)} method. Defaults to true. */ public void setFailIfNoHandler(final boolean failIfNoHandler) { this.failIfNoHandler = failIfNoHandler; } @Override protected boolean onMessage(final WebSocket webSocket, final Object packet) throws WebSocketException { try { return handlers.get(packet.getClass(), unknown).handle(webSocket, packet); } catch (final Exception exception) { return onError(webSocket, new WebSocketException("Unable to handle the received packet: " + packet, exception)); } } /** Common interface for handlers that consume a specific type of packets. * * @author MJ * * @param <Packet> type of handled packets. * @see EmptyHandler */ public static interface Handler<Packet> { /** Should perform the logic using the received packet. * * @param webSocket this socket received the packet. * @param packet the deserialized packet instance. * @return true if message was fully handled and other web socket listeners should not be notified. * @see WebSocketListener#FULLY_HANDLED * @see WebSocketListener#NOT_HANDLED */ boolean handle(WebSocket webSocket, Packet packet); } /** A simple {@link Handler} implementation that does nothing. * * @author MJ */ public static class EmptyHandler implements Handler<Object> { @Override public boolean handle(final WebSocket webSocket, final Object packet) { return NOT_HANDLED; } } }