/** * Receive Pong response, take the time from the payload and calculate * latency. * * @param message */ @OnMessage public synchronized void onPong(PongMessage message) { try { long timeSent = message.getApplicationData().getLong(); long latency = System.currentTimeMillis() - timeSent; lastMeasuredLatency = latency; timeLatencyMeasured = System.currentTimeMillis(); if (latency > 200) { LOGGER.info(String.format("[FFZ-WS] High Latency (%dms)", System.currentTimeMillis() - timeSent)); } } catch (Exception ex) { LOGGER.warning("[FFZ-WS] Invalid Pong message: "+ex); } }
/** * Received a Pong message. * * @param session the WebSocket session for the connection. * @param msg the received message. */ @OnMessage public void onPongMessage(Session session, PongMessage msg) { log.trace("WebSocket Pong message received for session: {}", session.getId()); missingPing = 0; }
@Override public void onMessage(PongMessage message) { getMessages().add(message); if (getLatch() != null) { getLatch().countDown(); } }
private boolean validateOnPongMethod(Object webSocketEndpoint) throws WebSocketMethodParameterException, WebSocketEndpointMethodReturnTypeException { EndpointDispatcher dispatcher = new EndpointDispatcher(); Method method; if (dispatcher.getOnPongMessageMethod(webSocketEndpoint).isPresent()) { method = dispatcher.getOnPongMessageMethod(webSocketEndpoint).get(); } else { return true; } validateReturnType(method); boolean foundPrimaryPong = false; for (Parameter parameter: method.getParameters()) { Class<?> paraType = parameter.getType(); if (paraType == String.class) { if (parameter.getAnnotation(PathParam.class) == null) { throw new WebSocketMethodParameterException("Invalid parameter found on pong message method: " + "string parameter without " + "@PathParam annotation."); } } else if (paraType == PongMessage.class) { if (foundPrimaryPong) { throw new WebSocketMethodParameterException("Invalid parameter found on pong message method: " + "only one PongMessage should be declared."); } foundPrimaryPong = true; } else if (paraType != Session.class) { throw new WebSocketMethodParameterException("Invalid parameter found on pong message method: " + paraType); } } return foundPrimaryPong; }
private boolean validateReturnType(Method method) throws WebSocketEndpointMethodReturnTypeException { Class<?> returnType = method.getReturnType(); boolean foundCorrectReturnType = returnType == String.class || returnType == ByteBuffer.class || returnType == byte[].class || returnType == PongMessage.class || returnType == void.class; if (!foundCorrectReturnType) { throw new WebSocketEndpointMethodReturnTypeException("Unexpected method return type: " + returnType); } return foundCorrectReturnType; }
/** * Extract OnMessage method for Pong from the endpoint if exists. * * @param webSocketEndpoint Endpoint to extract method. * @return method optional to handle pong messages. */ public Optional<Method> getOnPongMessageMethod(Object webSocketEndpoint) { Method[] methods = webSocketEndpoint.getClass().getMethods(); Method returnMethod = null; for (Method method : methods) { if (method.isAnnotationPresent(OnMessage.class)) { Class<?>[] paraTypes = method.getParameterTypes(); List<Class<?>> paraList = Arrays.asList(paraTypes); if (paraList.contains(PongMessage.class)) { returnMethod = method; } } } return Optional.ofNullable(returnMethod); }
private void handleControlCarbonMessage(WebSocketControlMessage controlCarbonMessage, PatternPathRouter. RoutableDestination<Object> routableEndpoint, Session session) { if (routableEndpoint == null) { throw new RuntimeException("Error while handling the message. Routable endpoint is not registered for the" + " request uri:" + controlCarbonMessage.getTarget()); } Object webSocketEndpoint = routableEndpoint.getDestination(); Map<String, String> paramValues = routableEndpoint.getGroupNameValues(); Optional<Method> methodOptional = new EndpointDispatcher().getOnPongMessageMethod(webSocketEndpoint); try { methodOptional.ifPresent(method -> { List<Object> parameterList = new LinkedList<>(); Arrays.stream(method.getParameters()).forEach(parameter -> { if (parameter.getType() == PongMessage.class) { parameterList.add(new WebSocketPongMessage(controlCarbonMessage.getPayload())); } else if (parameter.getType() == Session.class) { parameterList.add(session); } else if (parameter.getType() == String.class) { PathParam pathParam = parameter.getAnnotation(PathParam.class); if (pathParam != null) { parameterList.add(paramValues.get(pathParam.value())); } } else { parameterList.add(null); } }); executeMethod(method, webSocketEndpoint, parameterList, session); }); } catch (Throwable throwable) { handleError(throwable, routableEndpoint, session); } }
@Override public void onMessage(PongMessage message) { logger.info("message: " + message); sentPong = true; }
@Override public void onMessage(PongMessage message) { programmaticServer.doInvoke(session, WebSocket.Type.ON_PONG,true,message); }
@SuppressWarnings("unchecked") private void doAddMessageHandler(Class<?> target, MessageHandler listener) { checkState(); // Message handlers that require decoders may map to text messages, // binary messages, both or neither. // The frame processing code expects binary message handlers to // accept ByteBuffer // Use the POJO message handler wrappers as they are designed to wrap // arbitrary objects with MessageHandlers and can wrap MessageHandlers // just as easily. Set<MessageHandlerResult> mhResults = Util.getMessageHandlers(target, listener, endpointConfig, this); for (MessageHandlerResult mhResult : mhResults) { switch (mhResult.getType()) { case TEXT: { if (textMessageHandler != null) { throw new IllegalStateException( sm.getString("wsSession.duplicateHandlerText")); } textMessageHandler = mhResult.getHandler(); break; } case BINARY: { if (binaryMessageHandler != null) { throw new IllegalStateException( sm.getString("wsSession.duplicateHandlerBinary")); } binaryMessageHandler = mhResult.getHandler(); break; } case PONG: { if (pongMessageHandler != null) { throw new IllegalStateException( sm.getString("wsSession.duplicateHandlerPong")); } MessageHandler handler = mhResult.getHandler(); if (handler instanceof MessageHandler.Whole<?>) { pongMessageHandler = (MessageHandler.Whole<PongMessage>) handler; } else { throw new IllegalStateException( sm.getString("wsSession.invalidHandlerTypePong")); } break; } default: { throw new IllegalArgumentException(sm.getString( "wsSession.unknownHandlerType", listener, mhResult.getType())); } } } }
protected MessageHandler.Whole<PongMessage> getPongMessageHandler() { return pongMessageHandler; }
private boolean processDataControl() throws IOException { TransformationResult tr = transformation.getMoreData(opCode, fin, rsv, controlBufferBinary); if (TransformationResult.UNDERFLOW.equals(tr)) { return false; } // Control messages have fixed message size so // TransformationResult.OVERFLOW is not possible here controlBufferBinary.flip(); if (opCode == Constants.OPCODE_CLOSE) { open = false; String reason = null; int code = CloseCodes.NORMAL_CLOSURE.getCode(); if (controlBufferBinary.remaining() == 1) { controlBufferBinary.clear(); // Payload must be zero or greater than 2 throw new WsIOException(new CloseReason( CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.oneByteCloseCode"))); } if (controlBufferBinary.remaining() > 1) { code = controlBufferBinary.getShort(); if (controlBufferBinary.remaining() > 0) { CoderResult cr = utf8DecoderControl.decode( controlBufferBinary, controlBufferText, true); if (cr.isError()) { controlBufferBinary.clear(); controlBufferText.clear(); throw new WsIOException(new CloseReason( CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.invalidUtf8Close"))); } // There will be no overflow as the output buffer is big // enough. There will be no underflow as all the data is // passed to the decoder in a single call. controlBufferText.flip(); reason = controlBufferText.toString(); } } wsSession.onClose(new CloseReason(Util.getCloseCode(code), reason)); } else if (opCode == Constants.OPCODE_PING) { if (wsSession.isOpen()) { wsSession.getBasicRemote().sendPong(controlBufferBinary); } } else if (opCode == Constants.OPCODE_PONG) { MessageHandler.Whole<PongMessage> mhPong = wsSession.getPongMessageHandler(); if (mhPong != null) { try { mhPong.onMessage(new WsPongMessage(controlBufferBinary)); } catch (Throwable t) { handleThrowableOnSend(t); } finally { controlBufferBinary.clear(); } } } else { // Should have caught this earlier but just in case... controlBufferBinary.clear(); throw new WsIOException(new CloseReason( CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.invalidOpCode", Integer.valueOf(opCode)))); } controlBufferBinary.clear(); newFrame(); return true; }
@Override protected Object decode(PongMessage message) { // Never decoded return null; }
@SuppressWarnings("unchecked") private void doAddMessageHandler(Class<?> target, MessageHandler listener) { checkState(); // Message handlers that require decoders may map to text messages, // binary messages, both or neither. // The frame processing code expects binary message handlers to // accept ByteBuffer // Use the POJO message handler wrappers as they are designed to wrap // arbitrary objects with MessageHandlers and can wrap MessageHandlers // just as easily. Set<MessageHandlerResult> mhResults = Util.getMessageHandlers(target, listener, endpointConfig, this); for (MessageHandlerResult mhResult : mhResults) { switch (mhResult.getType()) { case TEXT: { if (textMessageHandler != null) { throw new IllegalStateException(sm.getString("wsSession.duplicateHandlerText")); } textMessageHandler = mhResult.getHandler(); break; } case BINARY: { if (binaryMessageHandler != null) { throw new IllegalStateException(sm.getString("wsSession.duplicateHandlerBinary")); } binaryMessageHandler = mhResult.getHandler(); break; } case PONG: { if (pongMessageHandler != null) { throw new IllegalStateException(sm.getString("wsSession.duplicateHandlerPong")); } MessageHandler handler = mhResult.getHandler(); if (handler instanceof MessageHandler.Whole<?>) { pongMessageHandler = (MessageHandler.Whole<PongMessage>) handler; } else { throw new IllegalStateException(sm.getString("wsSession.invalidHandlerTypePong")); } break; } default: { throw new IllegalArgumentException( sm.getString("wsSession.unknownHandlerType", listener, mhResult.getType())); } } } }
private boolean processDataControl() throws IOException { TransformationResult tr = transformation.getMoreData(opCode, fin, rsv, controlBufferBinary); if (TransformationResult.UNDERFLOW.equals(tr)) { return false; } // Control messages have fixed message size so // TransformationResult.OVERFLOW is not possible here controlBufferBinary.flip(); if (opCode == Constants.OPCODE_CLOSE) { open = false; String reason = null; int code = CloseCodes.NORMAL_CLOSURE.getCode(); if (controlBufferBinary.remaining() == 1) { controlBufferBinary.clear(); // Payload must be zero or greater than 2 throw new WsIOException( new CloseReason(CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.oneByteCloseCode"))); } if (controlBufferBinary.remaining() > 1) { code = controlBufferBinary.getShort(); if (controlBufferBinary.remaining() > 0) { CoderResult cr = utf8DecoderControl.decode(controlBufferBinary, controlBufferText, true); if (cr.isError()) { controlBufferBinary.clear(); controlBufferText.clear(); throw new WsIOException( new CloseReason(CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.invalidUtf8Close"))); } // There will be no overflow as the output buffer is big // enough. There will be no underflow as all the data is // passed to the decoder in a single call. controlBufferText.flip(); reason = controlBufferText.toString(); } } wsSession.onClose(new CloseReason(Util.getCloseCode(code), reason)); } else if (opCode == Constants.OPCODE_PING) { if (wsSession.isOpen()) { wsSession.getBasicRemote().sendPong(controlBufferBinary); } } else if (opCode == Constants.OPCODE_PONG) { MessageHandler.Whole<PongMessage> mhPong = wsSession.getPongMessageHandler(); if (mhPong != null) { try { mhPong.onMessage(new WsPongMessage(controlBufferBinary)); } catch (Throwable t) { handleThrowableOnSend(t); } finally { controlBufferBinary.clear(); } } } else { // Should have caught this earlier but just in case... controlBufferBinary.clear(); throw new WsIOException(new CloseReason(CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.invalidOpCode", Integer.valueOf(opCode)))); } controlBufferBinary.clear(); newFrame(); return true; }
@OnMessage public void onPongMessage(PongMessage message, Session session) { // nothing to do here }
@OnMessage public PongMessage onPongMessage(PongMessage pongMessage, Session session) { log.info("Received a pong message."); return pongMessage; }
@OnMessage public PongMessage onPong(@PathParam("param1") String param1, @PathParam("param2") String param2, PongMessage pongMessage, Session session) { return pongMessage; }
@OnMessage public PongMessage onPong(PongMessage pongMessage) { return pongMessage; }
@SuppressWarnings("unchecked") @Override public void addMessageHandler(MessageHandler listener) { checkState(); // Message handlers that require decoders may map to text messages, // binary messages, both or neither. // The frame processing code expects binary message handlers to // accept ByteBuffer // Use the POJO message handler wrappers as they are designed to wrap // arbitrary objects with MessageHandlers and can wrap MessageHandlers // just as easily. Set<MessageHandlerResult> mhResults = Util.getMessageHandlers(listener, endpointConfig, this); for (MessageHandlerResult mhResult : mhResults) { switch (mhResult.getType()) { case TEXT: { if (textMessageHandler != null) { throw new IllegalStateException( sm.getString("wsSession.duplicateHandlerText")); } textMessageHandler = mhResult.getHandler(); break; } case BINARY: { if (binaryMessageHandler != null) { throw new IllegalStateException( sm.getString("wsSession.duplicateHandlerBinary")); } binaryMessageHandler = mhResult.getHandler(); break; } case PONG: { if (pongMessageHandler != null) { throw new IllegalStateException( sm.getString("wsSession.duplicateHandlerPong")); } MessageHandler handler = mhResult.getHandler(); if (handler instanceof MessageHandler.Whole<?>) { pongMessageHandler = (MessageHandler.Whole<PongMessage>) handler; } else { throw new IllegalStateException( sm.getString("wsSession.invalidHandlerTypePong")); } break; } default: { throw new IllegalArgumentException(sm.getString( "wsSession.unknownHandlerType", listener, mhResult.getType())); } } } }
private boolean processDataControl() throws IOException { if (!appendPayloadToMessage(controlBufferBinary)) { return false; } controlBufferBinary.flip(); if (opCode == Constants.OPCODE_CLOSE) { open = false; String reason = null; int code = CloseCodes.NORMAL_CLOSURE.getCode(); if (controlBufferBinary.remaining() == 1) { controlBufferBinary.clear(); // Payload must be zero or greater than 2 throw new WsIOException(new CloseReason( CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.oneByteCloseCode"))); } if (controlBufferBinary.remaining() > 1) { code = controlBufferBinary.getShort(); if (controlBufferBinary.remaining() > 0) { CoderResult cr = utf8DecoderControl.decode( controlBufferBinary, controlBufferText, true); if (cr.isError()) { controlBufferBinary.clear(); controlBufferText.clear(); throw new WsIOException(new CloseReason( CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.invalidUtf8Close"))); } // There will be no overflow as the output buffer is big // enough. There will be no underflow as all the data is // passed to the decoder in a single call. controlBufferText.flip(); reason = controlBufferText.toString(); } } wsSession.onClose(new CloseReason(Util.getCloseCode(code), reason)); } else if (opCode == Constants.OPCODE_PING) { if (wsSession.isOpen()) { wsSession.getBasicRemote().sendPong(controlBufferBinary); } } else if (opCode == Constants.OPCODE_PONG) { MessageHandler.Whole<PongMessage> mhPong = wsSession.getPongMessageHandler(); if (mhPong != null) { try { mhPong.onMessage(new WsPongMessage(controlBufferBinary)); } catch (Throwable t) { handleThrowableOnSend(t); } finally { controlBufferBinary.clear(); } } } else { // Should have caught this earlier but just in case... controlBufferBinary.clear(); throw new WsIOException(new CloseReason( CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.invalidOpCode", Integer.valueOf(opCode)))); } controlBufferBinary.clear(); newFrame(); return true; }