spring websocket client does not detect network connection loss - spring

Spring #ClientEndpoint websocket client does not detect network disconnect due to cable unplug. I also have implemented a ping/pong mechanism. Can someone please help me out with what's going on?
However I see following exception after reconnecting the cable, FYI, all setting are into default. Also I am connecting to a 3rd party remote endpoint where do not have any control.
xxxxxException: closed with code : CLOSED_ABNORMALLY reason: CloseReason: code [1006], reason [java.io.IOException: Connection reset by peer]
at xxxxxx.onClose(WSClient.java:xx)
#ClientEndpoint
public class WSClient {
private Session session;
private int i = 0;
#OnOpen
public void open(Session session) {
System.out.println("Connected to the server");
this.session = session;
}
#OnClose
public void close(Session session, CloseReason closeReason) {
System.out.println("connection closed " + closeReason.getReasonPhrase());
}
#OnError
public void error(Session session, Throwable t) {
System.out.println(session.getId());
System.out.println("Error in connection " + t.getMessage());
}
#OnMessage
public void message(Session session, String message) {
System.out.println("message received: " + message + " " + i++);
}
public void send(String message){
try {
if(session.isOpen()) {
this.session.getBasicRemote().sendPing(ByteBuffer.wrap(message.getBytes()));
System.out.println("socket is open " + i++);
} else {
System.out.println("socket closed");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Component
public class ClientApp implements ApplicationListener<ApplicationReadyEvent> {
private void startConnection() throws Exception {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
WSClient client = new WSClient();
container.connectToServer(client, new URI("ws://wshost:8080/ping"));
while (true) {
client.send("ping");
TimeUnit.SECONDS.sleep(3);
}
}
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
try {
startConnection();
} catch (Exception e) {
System.out.println(e);
}
}
}

This issue can be resolved by adding below code to WSClient.
#OnMessage
public void pongMessage(Session session, PongMessage msg) {
LOGGER.debug("Pong message received: " + Instant.now());
//schedule a timeout task, and raise an event or so if timed out.
}
The above snippet will be invoked when remote endpoint sends a pong message as a respond to the ping message sent. Basically there will be two methods annotated with #OnMessage, one to received the user message payload and another pong message payload sent by the framework.

Related

Stop RabbitMQ-Connection in Spring-Boot

I have a spring-boot application that pulls all the messages from a RabbitMQ-queue and then terminates. I use rabbitTemplate from the package spring-boot-starter-amqp (version 2.4.0), namely receiveAndConvert(). Somehow, I cannot get my application to start and stop again. When the rabbitConnectionFactory is created, it will never stop.
According to Google and other stackoverflow-questions, calling stop() or destroy() on the rabbitTemplate should do the job, but that doesn't work.
The rabbitTemplate is injected in the constructor.
Here is some code:
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
Object msg = getMessage();
while (msg != null) {
try {
String name = ((LinkedHashMap) msg).get(propertyName).toString();
//business logic
logger.debug("added_" + name);
} catch (Exception e) {
logger.error("" + e.getMessage());
}
msg = getMessage();
}
rabbitTemplate.stop();
private Object getMessage() {
try {
return rabbitTemplate.receiveAndConvert(queueName);
} catch (Exception e) {
logger.error("" + e.getMessage());
return null;
}
}
So, how do you terminate the connection to RabbitMQ properly?
Thanks for your inquiry.
You can call resetConnection() on the CachingConnectionFactory to close the connection.
Or close() the application context.
If I were to do it , I would use #RabbitListener to receive the messages and RabbitListenerEndpointRegistry to start and stop the listener. Sample Code is given below
#EnableScheduling
#SpringBootApplication
public class Application implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
public static final String queueName = "Hello";
#Bean
public Queue hello() {
return new Queue(queueName);
}
#Autowired
private RabbitTemplate template;
#Scheduled(fixedDelay = 1000, initialDelay = 500)
public void send() {
String message = "Hello World!";
this.template.convertAndSend(queueName, message);
System.out.println(" [x] Sent '" + message + "'");
}
#Autowired
RabbitListenerEndpointRegistry registry;
#Override
public void run(ApplicationArguments args) throws Exception {
registry.getListenerContainer( Application.queueName).start();
Thread.sleep(10000L);
registry.getListenerContainer( Application.queueName).stop();
}
}
#Component
class Receiver {
#RabbitListener(id= Application.queueName,queues = Application.queueName)
public void receive(String in) {
System.out.println(" [x] Received '" + in + "'");
}
}

Is decorated function returned by Retry threadsafe?

I have a class that sends a message to a remote service as shown below.
I'm using resilience4j-retry to retry the network call. As the retry instance is thread safe according to the documentation, I'm creating it in the class level and reusing it.
public class RemoteMessageService {
Retry retry = Retry.of("RemoteMessageService", RetryConfig.custom()
.maxAttempts(5)
.retryExceptions(ProcessingException.class)
.intervalFunction(IntervalFunction.ofExponentialBackoff())
.build());
public void postMessageWithRetry(final String message){
Function<Integer, Void> postMessageFunction = Retry.decorateFunction(retry, this::postMessage);
try {
postMessageFunction.apply(message)
} catch (final ProcessingException e) {
LOG.warn("Got processing exception: {}", e.getMessage());
} catch (final Exception e) {
LOG.error("Got unknown exception: {}", e.getMessage());
}
}
private Void postMessage(final String message){
// Do a network call to send the message to a rest service
// throw ProcessingException in case of timeout
return null;
}
}
My question is if the decorated function returned by Retry.decorateFunction(retry, this::postMessage); is also thread safe?
In that case I could move this to class level instead of repeating it every time the postMessageWithRetry function is called.
After looking into the resilience4j-retry code, I found that the decorated function is in fact thread safe; as long as the function that we decorate in the first place is thread safe.
So I can rewrite the code as below since the postMessage function is thread safe, and therefor the decorated postMessageFunction function is also thread safe.
public class RemoteMessageService {
private final Retry retry = Retry.of("RemoteMessageService", RetryConfig.custom()
.maxAttempts(5)
.retryExceptions(ProcessingException.class)
.intervalFunction(IntervalFunction.ofExponentialBackoff())
.build());
private final Function<Integer, Void> postMessageFunction = Retry.decorateFunction(retry, this::postMessage);
public void postMessageWithRetry(final String message) {
try {
postMessageFunction.apply(message)
} catch (final ProcessingException e) {
LOG.warn("Got processing exception: {}", e.getMessage());
} catch (final Exception e) {
LOG.error("Got unknown exception: {}", e.getMessage());
}
}
private Void postMessage(final String message) {
// Do a network call to send the message to a rest service
// throw ProcessingException in case of timeout
return null;
}
}

ServerEnpoint change port

I would like to set the port number of my serverEnPoint.
Here is my server socket:
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
#ServerEndpoint("/myEndPoint")
public class MyEndPoint {
private static Session session;
#OnOpen
public void onOpen(Session s) throws IOException {
session = s;
}
#OnMessage
public String echo(String message) {
return message + " (from your server)";
}
#OnError
public void onError(Throwable t) {
t.printStackTrace();
}
public static void sendMessage(String message){
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
I ask how to do that because my server needs to open a websocket but still allow REST API.
I am assuming that the websocket port by default is the same as REST and this is why my client does not connect. If you have other solution let me know.
Thanks

How to do unit test websocket with embedded jetty?

I want to write Junit UT case for my websocket serverendpoint code using embedded Jetty.
i tried things explained in below link:
JUnit test with javax.websocket on embedded Jetty throws RejectedExecutionException: NonBlockingThread
I want to test my onMessage callback for websocket.
If i dont use server.join() method then the connection closes as soon as it opens.
If i use server.join() method nothing happens after joining.
Below is My code.
Server startup code::
public class EmbeddedJettyServer {
private final int port;
private Server server;
public EmbeddedJettyServer(int port) {
this.port = port;
}
public void start() throws Exception {
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8080);
server.addConnector(connector);
// Setup the basic application "context" for this application at "/"
// This is also known as the handler tree (in jetty speak)
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
try {
// Initialize javax.websocket layer
ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context);
// Add WebSocket endpoint to javax.websocket layer
wscontainer.addEndpoint(WebSocketServer.class);
System.out.println("Begin start");
server.start();
server.dump(System.err);
server.join();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
public void stop() throws Exception {
server.stop();
LOGGER.info("Jetty server stopped");
}
public URI getWebsocketUri(Class<WebSocketServer> class1) {
return server.getURI();
}
}
Client Code:
#ClientEndpoint()
public class WebSocketClientJetty {
WebSocketContainer container;
public Session connect(URI uri) throws Exception {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
try {
// Attempt Connect
Session session = container.connectToServer(WebSocketClientJetty.class,uri);
// return container.connectToServer(WebSocketClientJetty.class, uri);
session.getBasicRemote().sendText("Hello");
// Close session
// session.close();
return session;
} finally {
}
}
public void stop() throws Exception{
if (container instanceof LifeCycle) {
((LifeCycle) container).stop();
}
}
#OnOpen
public void onWebSocketConnect(Session sess)
{
System.out.println("Socket Connected: " + sess);
}
#OnMessage
public void onWebSocketText(String message)
{
System.out.println("Received TEXT message: " + message);
}
#OnClose
public void onWebSocketClose(CloseReason reason)
{
System.out.println("Socket Closed: " + reason);
}
#OnError
public void onWebSocketError(Throwable cause)
{
cause.printStackTrace(System.err);
}
}
Serverendpoint code:
#ServerEndpoint(value = "/echo",
encoders={JsonEncoder.class})
public class WebSocketServer {
private static final Logger LOGGER =
#OnOpen
public void onOpen(Session session){
System.out.println("onopen");
some code....
}
#OnMessage
public void onMessage(String message, Session session) throws IOException{
System.out.println("onmessage");
....
}
#OnClose
public void onClose(Session session){
System.out.println("onClose");
...
}
}
Ut case:
public class WebSocketJettyTest {
private static EmbeddedJettyServer server;
#ClassRule
public static final ExternalResource integrationServer = new ExternalResource() {
#Override
protected void before() throws Throwable {
System.out.println("Starting...");
server = new EmbeddedJettyServer(8080);
server.start();
System.out.println("Started");
}
};
#Before
public void setUp() throws Exception {
}
#After
public void shutdown() throws Exception {
server.stop();
}
#Test
public void testSocket() throws Exception {
/*URI uri = server.getWebsocketUri(WebSocketServer.class);*/
URI uri = URI.create("ws://localhost:8080/echo");
WebSocketClientJetty client = new WebSocketClientJetty();
Session session = client.connect(uri);
session.getBasicRemote().sendText("hello");
Thread.sleep(6000);
client.stop();
}
}
Drop the call to
server.join();
That just makes the current thread wait until the server thread stops.
Which is making it difficult for you.

Spring websocket timeout settings

I'm using the Spring websocket support. My question is how to set the websocket connection timeout. Now the connection is closed automatically after several minutes. I want the connection never to be closed.
Here is my websocket handler:
public class MyHandler implements WebSocketHandler {
private Logger logger = LoggerFactory.getLogger(this.getClass());
class MyTimerTask extends TimerTask {
private WebSocketSession session;
public MyTimerTask(WebSocketSession session) {
this.session = session;
}
#Override
public void run() {
try {
String msg = ((int)(Math.random()*50)) + "";
this.session.sendMessage(new TextMessage(msg.toString()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Autowired
private UserDao userDao;
#Autowired
private JdbcDaoImpl jdbcDaoImpl;
private Timer timer;
#Override
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
System.out.println("websocket????");
timer = new Timer();
timer.schedule(new MyTimerTask(session), 0, 1000);
logger.info("logger connection");
}
#Override
public void handleMessage(WebSocketSession session,
WebSocketMessage<?> message) throws Exception { }
#Override
public void handleTransportError(WebSocketSession session,
Throwable exception) throws Exception { }
#Override
public void afterConnectionClosed(WebSocketSession session,
CloseStatus closeStatus) throws Exception {
System.out.println("websocket????");
timer.cancel();
}
#Override
public boolean supportsPartialMessages() {
return false;
}
}
my websocket config:
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
</websocket:handlers>
<bean id="myHandler" class="com.sdp.websocket.MyHandler"/>
and javascript client:
var webserver = 'ws://localhost:8080/authtest/myHandler';
var websocket = new WebSocket(webserver);
websocket.onopen = function (evt) { onOpen(evt) };
websocket.onclose = function (evt) { onClose(evt) };
websocket.onmessage = function (evt) { onMessage(evt) };
websocket.onerror = function (evt) { onError(evt) };
function onOpen(evt) {
console.log("Connected to WebSocket server.");
}
function onClose(evt) {
console.log("Disconnected");
}
function onMessage(evt) {
console.log('Retrieved data from server: ' + evt.data);
}
function onError(evt) {
console.log('Error occured: ' + evt.data);
}
debugger;
function sendMsg(){
websocket.send("{msg:'hello'}");
}
The websocket stays opened until either the server or the client decide to close it. However, websockets are affected by two timeouts:
HTTP session timeout;
proxy connection timeouts;
If all you have between your client and your server is a websocket connection, and you don't interact over HTTP with AJAX or requesting other pages, the HTTP session expires and some servers decide to invalidate it along with the websocket (Tomcat7 had a bug that did just that). Some other servers don't do that because they see there is activity on the websocket. See here for an extended discussion: Need some resolution or clarification for how and when HttpSession last access time gets updated.
The other timeout is with proxies. They see the connection and if there is no activity on the wire for a longer period of time, they just cut it because they think it hanged. To address this, while you don't send actual application data, you need to have a heartbeat or a ping/pong of messages from time to time to let the proxy know that the connection is still OK.
Other issues might also intervene, like a buggy browser support for websocket, how your network is configured, firewall rules, etc.
For available timeout options in Spring see the websocket documentation: Configuring the WebSocket Engine.
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
var container = new ServletServerContainerFactoryBean();
container.setMaxSessionIdleTimeout(...);
return container;
}
}

Resources