Spring websockets, NettyTcpClient - Failed to connect - java

I'm receiving the following message every 5 seconds:
[WEBAPP] 02 Nov 2014 17:55:43 INFO NettyTcpClient - Failed to connect to /127.0.0.1:61613. Attempting reconnect in 5000ms.
I'm using spring 4 with stomp and activemq
any ideas why that happnes?
BTW: the webapp runs on tomcat #1 while my activemq broker is located # a different machine.

Ok the URL was wrong.
I fixed it with the following code:
config.enableStompBrokerRelay("/topic","/queue/").setRelayHost(THE_RIGHT_URL);

I slved this problem with this configuration:
#Configuration
#EnableConfigurationProperties(ActiveMQProperties.class)
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport {
#Autowired
private ActiveMQProperties activeMQProperties;
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableStompBrokerRelay("/notify").setRelayHost("192.168.99.100")
.setSystemLogin("admin").setSystemPasscode("admin");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/createBookJob").withSockJS();
}
}
In the my use case I'm experiencing whe use activeMq as STOMP provider ina docker image.
setRelayHost("192.168.99.100") was usefull becouse 192.168.99.100 is the defualt ip that I use
.setClientLogin("admin").setClientPasscode("admin")
.setSystemLogin("admin").setSystemPasscode("admin");
becouse in active mq the default users has admin admin as user and password
it works for me
I hope that this can help you

Related

Springboot WebSocket: Cors policy blocking ws request

I'm trying to build a chat application. When hosting on a VPS I'm having this error message from browser
Access to XMLHttpRequest at 'http://localhost:8080/samplews/info?t=1603709911491' from origin 'http://my-server-ip:8081' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This is my WebSocketConfiguration class
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry){
registry.addEndpoint("/samplews").setAllowedOrigins("http://my-server-ip:8081").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry){
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
I also tried to add a WebMvcConfiguration, which doesn't change anything:
#Configuration
#EnableWebMvc
public class WebAppConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://my-server-ip:8081")
.allowedMethods("GET");
}
I'm using springboot version 2.2.1.RELEASE and also tried with the latest release.
The client is an angular application which does nothing but this:
ngOnInit() {
const ws = new SockJS(this.webSocketEndPoint);
this.stompClient = Stomp.over(ws);
this.stompClient.connect({}, (frame) => {
// TODO: Do something when connecting ws
console.log(frame);
});
}
Client and server are on two different docker containers, this is docker ps result:
f3aa46625fc4 chatapp "java -jar /app.jar" 15 minutes ago Up 15 minutes 0.0.0.0:8080->8080/tcp chatapp
7aa6e8a885de chatui "catalina.sh run" 16 minutes ago Up 16 minutes 0.0.0.0:8081->8080/tcp chatui
It all works fine locally (even if I use docker or ng serve the client and start the springboot server app via IntelliJ)
I don't think I'm missing basic information, any ideas?
edit:
I've changed this
public class WebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer
to this:
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer
And now I'm getting this error:
GET http://localhost:8080/samplews/info?t=1603798741148 net::ERR_CONNECTION_REFUSED
edit:
I was using localhost as the URL target for opening the socket, I should use the server ip instead, solved

"No decoder for session id" in logs for Spring web sockets and Stomp

I have a tomcat cluster with 2 instances and apache proxy in one server. Application use Spring framework 4.3.10 with web sockets, apache-activemq-5.15.0 as stomp broker:
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/wshandler" allowed-origins="*">
</websocket:stomp-endpoint>
<websocket:stomp-broker-relay prefix="/topic,/queue"
relay-host="localhost" relay-port="62356"
heartbeat-send-interval="10000" heartbeat-receive-interval="10000"/>
<websocket:client-inbound-channel>
<websocket:interceptors>
<bean class="somepath.TopicSubscriptionInterceptor"/>
</websocket:interceptors>
</websocket:client-inbound-channel>
</websocket:message-broker>
Now about 20 clients connect to web-sockets in the same time. All works well, but periodically I have an error in logs (estimated 8-10 times in an hour). How can I fix it?
2017-10-06 09:54:01,046 ERROR [StompSubProtocolHandler] Failed to parse TextMessage payload=[], byteCount=1, last=true] in session 6f. Sending STOMP ERROR to client.
java.lang.IllegalStateException: No decoder for session id '6f'
at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient(StompSubProtocolHandler.java:249)
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:307)
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:110)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:42)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:81)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:78)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:394)
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:119)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:495)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:294)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:82)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:171)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:151)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:148)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:54)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
It's solved in new spring framework version (https://github.com/spring-projects/spring-framework/commit/f425a993e7be82ffdbdda24370925a34c42925f2)
If you are using old version ( up to 2019 ), you can fix this problem by overriding WebSocketHandlerDecorator :
#Slf4j
public class WebSocketSessionCapturingHandlerDecorator extends WebSocketHandlerDecorator {
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
if(session.isOpen()){
super.handleMessage(session, message);
}else{
log.info("Dropped inbound WebSocket message due to closed session");
}
}
And use it in main WebSocketConfig :
#Slf4j
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
#Override
public WebSocketHandler decorate(WebSocketHandler webSocketHandler) {
return new WebSocketSessionCapturingHandlerDecorator(webSocketHandler);
}
});
}
Update the heartbeat header in the connect frame to 5000,5000 which means both client and server will need to send the heartbeat frame every frame 5 sec. In my case the server was dropping the connection when it was idle for too long. What this essentially does is keep the underlying TCP connection alive so that the server does not close it. Also restart the server after making this change.
Hope this helps!!
If you are using a java client over amqp to connect to rabbitMq you can set
ConnectionFactory cf = new ConnectionFactory();
// set the heartbeat timeout to 60 seconds
cf.setRequestedHeartbeat(60);
References:
https://www.rabbitmq.com/heartbeats.html
https://stomp.github.io/stomp-specification-1.2.html#CONNECT_or_STOMP_Frame

simpMessagingTemplate doesn't send message from client to server

I want to exchange messages by web sockets between 2 java apps.
I have the following server configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/queue", "/topic");
registry.setUserDestinationPrefix("/user");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//todo remove handshake handler when authorization is implemented
registry.addEndpoint("/ws").setAllowedOrigins("*").setHandshakeHandler(new TestHandshakeHandler()).withSockJS();
}
}
and inside class marked with #Controller I have wrote following theme:
#MessageMapping("/consumer/client/add")
public void addClientRequest(String msgReq) {
logger.info(msgReq);
}
and inside clien I do connect and in sime bean I wrote following:
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
...
simpMessagingTemplate.convertAndSend("/app/consumer/client/add", new StubObject("message"));
But after sending from client method addClientRequest doesn't invoke.
Please advice ways to troubleshot this issue.
Actually I don't understand issue. Maybe I send to wrong destination or I have issue with configuration or path is wrong or something else.
P.S.
I know that I can extend StompSessionHandlerAdapter
and obtain session from there but looks like it is the bad style and should be another way to achieve it
P.S.2
Inside class WebSocketTcpConnectionHandlerAdapter(inner class inside WebSocketStompClient) I see private volatile WebSocketSession session;
I want to obtain this object to send messages
I don't think it was designed to be used like this.
I think you must use a specific websocket client. This one for exemple :
http://www.programmingforliving.com/2013/08/jsr-356-java-api-for-websocket-client-api.html
This code :
#MessageMapping("/consumer/client/add")
public void addClientRequest(String msgReq) {
logger.info(msgReq);
}
Will NOT connect to a websocket client and wait to have messages. It expect a client to connect throught it and send messages.

How to limit client connections to my stomp websocket handler?

I need to keep track of users opening a websocket to my stomp broker in Spring 4.x.
The stomp endpoint get configured the usual way:
#Configuration
#EnableWebSocketMessageBroker
public class StompWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/stomp");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app")
.enableSimpleBroker("/queue", "/topic");
}
}
I tried to implement the ApplicationListener interface in order to receive SessionConnectEvent SessionConnectedEvent but I'm unable to get the remote host ip from those events.
How I'm supposed to get the ip of the client connecting to my service?
My goal is to limit the number of connections to my websocket handler from the same ip.
The way to do it is to customize the HandshakeHandler and overriding functions like isvalidOrigin.
To add an HandshakeHandler using XML config a snippet like this one can be used:
<bean id="customHandler" class="my.CustomWebsocketHandshakeHandler"/>
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/Stomp">
<websocket:handshake-handler ref="customHandler"/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic,/queue" />
</websocket:message-broker>
I'm not able to produce the equivalent configuration using Java Config, though.
I'll appreciate very much help on that subject

spring websockets: sending a message to an offline user - messages not enqueued in message broker

I have a webapp with spring and websockets using a message broker (activemq).
here is my config class:
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableStompBrokerRelay("/topic","/queue/");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello").withSockJS();
}
}
I have a scheduled task that constantly pushing messages to the username "StanTheMan" :
#Scheduled(fixedDelay = 1000)
public void sendGreetings() {
HelloMessage hello = new HelloMessage();
hello.setContent("timeStamp:" + System.currentTimeMillis());
String queueMapping = "/queue/greetings";
template.convertAndSendToUser(
"DannyD",
queueMapping,
hello);
}
Currently, if the user is NOT connected via a websocket to the server - all the messages for him are not being en-queued for him, they simply discarded. whenever he connects - fresh messages are being en-queued for him.
Is it possible for me to "convertAndSendToUser" a message to an offline user in any way? i would like to en-queue messages with an expired time for offline users to be later on pushed when they are connecting again and the expired time wasn't over.
how can i achieve that? Obviously using a real message broker (activemq) supposed to help achieving that, but how?
Thanks!
Indeed this feature can only be used to send messages to a (presently) connected user.
We plan to provide better ways to track failed messages (see SPR-10891). In the meantime as a workaround you could inject the UserSessionRegistry into your #Scheduled component and check if the getSessionIds methods returns a non-empty Set. That would indicate the user is connected.
One alternative may be to use your own convention for a queue name that each user can subscribe to (probably based on their user name or something else that's unique enough) in order to receive persistent messages. The ActiveMQ STOMP page has a section on persistent messages and expiration times.
This is a default behavior for message brokers.
And you haven't setup the ActiveMQ broker as your default broker so Spring is setting up a in-memory broker.
To achieve what you want setup/give your details of activeMQ to your spring websocket message broker. As Spring doesn't provide these settings, you have to do all the persistence settings at the ActiveMQ side. .
To setup ActiveMQ for spring websocket message broker you also need to enable stomp and use this:
registry.enableStompBrokerRelay("/topic").setRelayHost("hostName").setRelayPort("00000");
For more information checkout:
http://docs.spring.io/spring/docs/4.0.0.RELEASE/javadoc-api/org/springframework/messaging/simp/config/StompBrokerRelayRegistration.html

Categories