I'm trying to create an standalone Java app as a websocket client using for this Spring with Stomp and Sockjs, taking in consideration the spring specifications and the spring-portafolio examples and I'm getting this error:
15:18:01.342 [main] DEBUG com.example.client.WebSocketClientTest - Connecting to : ws://localhost:8080/socket/hello
15:18:01.541 [WebSocketClient-AsyncIO-1] ERROR com.example.client.MyStompSessionHandler - Transport error
org.springframework.messaging.simp.stomp.ConnectionLostException: Connection closed
at org.springframework.messaging.simp.stomp.DefaultStompSession.afterConnectionClosed(DefaultStompSession.java:483) [spring-messaging-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.socket.messaging.WebSocketStompClient$WebSocketTcpConnectionHandlerAdapter.afterConnectionClosed(WebSocketStompClient.java:354) [spring-websocket-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.socket.sockjs.client.AbstractClientSockJsSession.afterTransportClosed(AbstractClientSockJsSession.java:321) [spring-websocket-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.socket.sockjs.client.WebSocketTransport$ClientSockJsWebSocketHandler.afterConnectionClosed(WebSocketTransport.java:172) [spring-websocket-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.onClose(StandardWebSocketHandlerAdapter.java:141) [spring-websocket-4.3.2.RELEASE.jar:4.3.2.RELEASE]...
My code in the java client side is:
StandardWebSocketClient webSocketClient = new StandardWebSocketClient();
List<Transport> transports = new ArrayList<>();
transports.add(new WebSocketTransport(webSocketClient));
SockJsClient sockJsClient = new SockJsClient(transports);
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.afterPropertiesSet();
String stompUrl = "ws://localhost:8080/socket/hello";
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
stompClient.setMessageConverter(new StringMessageConverter());
stompClient.setTaskScheduler(taskScheduler);
stompClient.setDefaultHeartbeat(new long[] {0, 0});
StompSessionHandler sessionHandler = new MyStompSessionHandler();
stompClient.connect(stompUrl, sessionHandler);
stompClient.setTaskScheduler(taskScheduler);
And the server side was made using Spring MVC with Stomp and SockJs, the server works perfectly with a javascript client, this is the config that I used:
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello")
.setHandshakeHandler(new DefaultHandshakeHandler(new TomcatRequestUpgradeStrategy()))
.withSockJS();
}
What am I doing wrong? Can anyone give me an idea of how to fix it or how to create a Java client to connect with an spring websocket server?
Thanks in advance..
I had the same problem. Cause was recieving too big message which default STOMP Spring/Tomcat WS client cannot handle because it is not capable of partial messaging. This StackOverflow answer worked for me (I have set MAX_TEXT_MESSAGE_BUFFER_SIZE=20*1024*1024):
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.setDefaultMaxTextMessageBufferSize(MAX_TEXT_MESSAGE_BUFFER_SIZE);
WebSocketClient wsClient = new StandardWebSocketClient(container);
Setting inboundMessageSizeLimit had no effect:
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
stompClient.setInboundMessageSizeLimit(Integer.MAX_VALUE);
Related
I'm not sure if what I'm trying to do is possible but basically I'm trying to update the current implementation from HornetQ to ActiveMQ making use of Artemis.
My system is a JMS consumer from the HornetQ.
The current implementation works if I use "HornetQJMSConnectionFactory" but when I change to ActiveMQJMSConnectionFactory it is not able to connect.
In order to test the new implementation, I've spun up a local instance of ActiveMQ and works with the new implementation.
So I've tried multiple different things including forcing protocol =HORNETQ and nothing works.
No compilation error, "only":
ERROR o.s.j.l.DefaultMessageListenerContainer.refreshConnectionUntilSuccessful - Could not refresh JMS Connection for destination 'QueueX' - retrying using FixedBackOff{interval=5000, currentAttempts=1, maxAttempts=unlimited}. Cause: Failed to create session factory; nested exception is ActiveMQConnectionTimedOutException[errorType=CONNECTION_TIMEDOUT message=AMQ219013: Timed out waiting to receive cluster topology. Group:null]
Old Implementation
private ConnectionFactory createConnectionFactory(SyncProperties.SmpJmsServer jmsServer) {
final String className = "org.hornetq.core.remoting.impl.netty.NettyConnectorFactory";
Map<String, Object> params = new HashMap<String, Object>();
params.put("host", getJmsHost());
params.put("port", getJmsPort());
TransportConfiguration transportConfiguration = new TransportConfiguration(className, params);
HornetQJMSConnectionFactory hornetQJMSConnectionFactory = new HornetQJMSConnectionFactory(false, transportConfiguration);
hornetQJMSConnectionFactory.setConnectionTTL(300000);
hornetQJMSConnectionFactory.setConsumerWindowSize(0);
UserCredentialsConnectionFactoryAdapter adapter = new UserCredentialsConnectionFactoryAdapter();
adapter.setTargetConnectionFactory(hornetQJMSConnectionFactory);
adapter.setUsername(getJmsUsername());
adapter.setPassword(getJmsPassword());
CachingConnectionFactory smpCachingConnectionFactory = new CachingConnectionFactory(adapter);
return smpCachingConnectionFactory;
}
New Implementation
public ConnectionFactory createActiveMQJMSConnectionFactory() {
ActiveMQJMSConnectionFactory activeMQJMSConnectionFactory = new ActiveMQJMSConnectionFactory(false, amqTransportConfiguration());
activeMQJMSConnectionFactory.setConnectionTTL(300000);
activeMQJMSConnectionFactory.setConsumerWindowSize(0);
UserCredentialsConnectionFactoryAdapter adapter = new UserCredentialsConnectionFactoryAdapter();
adapter.setTargetConnectionFactory(activeMQJMSConnectionFactory);
adapter.setUsername(getJmsUsername());
adapter.setPassword(getJmsPassword());
CachingConnectionFactory smpCachingConnectionFactory = new CachingConnectionFactory(adapter);
return smpCachingConnectionFactory;
}
#Bean("amqTransportConfiguration")
public TransportConfiguration amqTransportConfiguration() {
return new TransportConfiguration("org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory", getParams());
}
static Map<String, Object> getParams() {
Map<String, Object> params = new HashMap<String, Object>();
params.put("host", getJmsHost());
params.put("port", getJmsPort());
return params;
}
Thanks for the help.
Although ActiveMQ Artemis is based on the HornetQ code-base an ActiveMQ Artemis client won't be able to talk with a HornetQ broker. Each client sends a protocol/client identifier when it connects. This ID is different between ActiveMQ Artemis clients and HornetQ clients. A HornetQ broker will not recognize the ID sent by the ActiveMQ Artemis client and therefore will not complete a handshake.
That said, we have worked to ensure that HornetQ clients can still talk to an ActiveMQ Artemis broker. That's what the org.apache.activemq.artemis.core.protocol.hornetq.HornetQProtocolManager provides.
In any case, upgrading your client won't really do much for you anyway. If you want to upgrade anything I recommend you upgrade EAP or even move to a standalone version of ActiveMQ Artemis so you can get the latest fixes & features.
I'm using CXF for a SOAP webservice client and somehow, I'm unable to get a working RetryStrategy as a FailoverFeature. Another LoggingFeature is working fine. This is my Spring configuration:
#Bean
public MyPort myPort() {
final RetryStrategy retryStrategy = new RetryStrategy();
retryStrategy.setMaxNumberOfRetries(5);
retryStrategy.setDelayBetweenRetries(3000);
FailoverFeature failoverFeature = new FailoverFeature();
failoverFeature.setStrategy(retryStrategy);
failoverFeature.setTargetSelector(new FailoverTargetSelector(endpointAddress));
final LoggingFeature logFeature = new LoggingFeature();
MyService service = new MyService(WSDL_LOCATION, logFeature, failoverFeature);
MyPort port = service.getPort();
Client client = ClientProxy.getClient(port);
client.getRequestContext().put(ENDPOINT_ADDRESS, endpointAddress);
return port;
}
CXF seems to happily accept the FailoverFeature at boot time:
INFO org.apache.cxf.clustering.FailoverTargetSelector - corid= Using failover strategy org.apache.cxf.clustering.RetryStrategy#36931450
But a request like the following won't retry because I get a (intended) "502: Connection refused" after about 2sek.
myPort.doSomething()
What am I doing wrong?
My workaround is now to use Spring's retry mechanism:
#Retryable(
value = {HTTPException.class},
backoff = #Backoff(delay = 3000))
public void callWebservice() { ... }
You're creating a feature but not registering it. You need to add something like this:
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
jaxWsProxyFactoryBean.getFeatures().add(failoverFeature);
I'm trying to connect a Spring Boot Stomp Server with multiple sockjs clients offline but I get the warning
Websocket is closed before the connection is established
followed by
GET http://192.168.1.45:8080/socket/327/si5osugt/jsonp?c=jp.a3xdefl net::ERR_ABORTED 404 (Not Found)
I'm using Spring Boot Version 2.1.2 with the spring-boot-starter-websocket package on the backend side and on the frontend side I'm using Angular 6 with sockjs-client version 1.3.0. Frontend and backend are both running on port 8080
I'm getting some errors while turning the internet down. If the internet is turned off the iframe tries to reach to https://cdn.jsdelivr.net/npm/sockjs-client#1/dist/sockjs.js.
I managed by configuring stomp server on the backend to set the client library by adding .setClientLibraryUrl to a absolute path which is offline reachable.
registry.addEndpoint("/socket").setAllowedOrigins("*").withSockJS).setClientLibraryUrl("http://192.168.1.45/dist/sockjs.min.js");
and get a 200 OK on http://192.168.1.45/dist/sockjs.min.js
Spring Boot:
WebSocketConfiguration (extends AbstractWebSocketMessageBrokerConfigurer)
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/socket")
.setAllowedOrigins("*")
.withSockJS().setClientLibraryUrl("http://192.168.1.45/dist/sockjs.min.js");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
MessageBrokerRegistry messageBrokerRegistry = registry.setApplicationDestinationPrefixes("/app");
messageBrokerRegistry.enableSimpleBroker( "/test", "/test2"
);
}
WebSocketController
private final SimpMessagingTemplate template;
#Autowired
WebSocketController(SimpMessagingTemplate template){
this.template=template;
}
#MessageMapping("/send/message")
public void onReceivedMessage( String destination , String message){
this.template.convertAndSend(destination , message);
}
public void convertAndSend(String url, Object o){
this.template.convertAndSend(url, o);
}
Angular 6:
TestComponet
ngAfterViewInit() {
let ws = new SockJS('http://192.168.1.45:8080/socket');
this.stompClient = Stomp.over(ws);
let that = this;
that.stompClient.subscribe("/test", (message) => {
if (message.body) {
console.log(message.body);
}
});
that.stompClient.subscribe("/test2", (message) => {
if (message.body) {
console.log(message.body);
}
});
}
I thought it would work by just adding the sockjs client lib to an offline reachable path but I get the warning
Websocket is closed before the connection is established
followed by
"GET http://192.168.1.45:8080/socket/327/si5osugt/jsonp?c=jp.a3xdefl net::ERR_ABORTED 404 (Not Found)"
The library works with an internet connection perfectly fine, but I need it to work with both situations online and offline.
I had the same issue, and I fixed it by removing SockJs.
So now I'm currently using only Stomp-Websockets.
Changes in SpringBoot-Service(WebsocketConfiguration):
registry.addEndpoint("/justStomp").setAllowedOrigins("*");
I removed the .withSockJS() and .setClientLibraryUrl(../sockjs.min.js)
Changes in my Javascript-Code to connect to the websocket:
const stompClient = Stomp.client(`ws://localhost:8080/justStomp`);
stompClient.heartbeat.outgoing = 0;
stompClient.heartbeat.incoming = 0;
stompClient.connect({ name: 'test' }, frame => this.stompSuccessCallBack(frame, stompClient), err => this.stompFailureCallBack(err));
Instead of using Stomp.over(sockjs) I use the Stomp.client Method to directly connect to the websocket-url.
I have a rabbitMQ in the background with stomp-plugin, and this only works properly with the 2 heartbeat-settings. see here RabbitMQ Web STOMP without SockJS
I have an HTTP2C Embedded Jetty 9.x Server running ... note the server connector shows h2c ...
2016-03-21 09:25:44.082:INFO:oejs.ServerConnector:main: Started ServerConnector#66c7bd3f{HTTP/1.1,[http/1.1, h2c, h2c-17, h2c-16, h2c-15, h2c-14]}{0.0.0.0:8080}
I have an OkHttpClient 3 attempting to talk HTTP2C to this server , however it always gets downgraded to HTTP/1.1, what am I missing? Which Java client API supports HTTP2C? My client code is as below ...
package http2;
import java.util.Collections;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class GetClear {
public static void main(String[] args) throws Exception {
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT).build();
OkHttpClient client = new OkHttpClient.Builder().connectionSpecs(Collections.singletonList(spec)).build();
Request request = new Request.Builder().url("http://localhost:8080/test").build();
Response response = client.newCall(request).execute();
System.out.println (response.body().string());
System.out.println("****");
response.body().close();
}
}
[The server prints the 'request.getProtocol' from a Jetty servlet and that shows HTTP/1.1 instead of HTTP/2].
HTTP/2 server and client on TLS works just fine using HTTP/2(client code and server code are different of course).
Any help will be truly appreciated.
Using a Jetty HTTP2C client, the same server code works. I guess OkHTTPClient does not support HTTP2C.
A complete h2c example using the HelloHandler example from the jetty doc:
public class HelloServer {
public static class HelloHandler extends AbstractHandler {
final String greeting;
final String body;
public HelloHandler() {
this("Hello World");
}
public HelloHandler(String greeting) {
this(greeting, null);
}
public HelloHandler(String greeting, String body) {
this.greeting = greeting;
this.body = body;
}
public void handle(String target,
Request baseRequest,
HttpServletRequest request,
HttpServletResponse response) throws IOException,
ServletException {
response.setContentType("text/html; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
PrintWriter out = response.getWriter();
out.println("<h1>" + greeting + "</h1>");
if (body != null) {
out.println(body);
}
baseRequest.setHandled(true);
}
}
public static void main(String[] args) throws Exception {
Server server = new Server();
server.setHandler(new HelloHandler());
HttpConfiguration httpConfig = new HttpConfiguration();
ConnectionFactory h1 = new HttpConnectionFactory(httpConfig);
ConnectionFactory h2c = new HTTP2CServerConnectionFactory(httpConfig);
ServerConnector serverConnector = new ServerConnector(server, h1, h2c);
serverConnector.setPort(8080);
server.setConnectors(new ServerConnector[] { serverConnector });
server.start();
server.join();
}
}
The Jetty log line shows that you have configured the server connector to have HTTP/1.1 to be the default protocol (that is the upper-case "HTTP/1.1" before the brackets containing the list of protocols supported).
You don't show your server-side code, but you have two choices:
Configure explicitly the default protocol for the server connector:
serverConnector.setDefaultProtocol("h2c");
Pass the ConnectionFactory objects in the right order to the server connector, since the first one will be the default protocol:
HttpConfiguration httpConfig = new HttpConfiguration();
ConnectionFactory h1 = new HttpConnectionFactory(httpConfig);
ConnectionFactory h2c = new HTTP2CServerConnectionFactory(httpConfig);
ServerConnector serverConnector = new ServerConnector(server, h2c, h1);
I have used spring implementation of sockjs websocket server and unable to transmit message over 8KB, following is the error
2014-02-12 19:36:29,990 - org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession - DEBUG - SockJS session id=35n41xel was closed, CloseStatus [code=1009, reason=The decoded text message was too big for the output buffer and the endpoint does not support partial messages]
Any Idea how can I increase the buffer size
I used following factory as spring sockjs leverages tomcat container (App is deployed in tomcat and I also debugged to confirm that it indeed uses tomcat lib)
#Bean
public WebSocketContainerFactoryBean createWebSocketContainer() {
WebSocketContainerFactoryBean container = new WebSocketContainerFactoryBean();
container.setMaxTextMessageBufferSize(16384);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
And then my URL mapping looks
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(coBrowseSockJsCustomerHandler(), "/sockjs/cobrowse/customer/").withSockJS();}
Do I need to set this bean with sockjs somewhere? how does sockjs knows that it has to use this facory?
Solved it by using clue from http://docs.spring.io/spring/docs/4.0.1.RELEASE/javadoc-api/index.html?org/springframework/web/socket/sockjs/SockJsService.html -got hold of ServletServerContainerFactoryBean and set the properties, this worked
#Bean
public ServletServerContainerFactoryBean createServletServerContainerFactoryBean() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(32768);
container.setMaxBinaryMessageBufferSize(32768);
logger.info("Websocket factory returned");
return container;
}
for client side:
#Bean
public static WebSocketStompClient getClient() {
List<Transport> transports = new ArrayList<>();
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.setDefaultMaxBinaryMessageBufferSize(1024 * 1024);
container.setDefaultMaxTextMessageBufferSize(1024 * 1024);
transports.add(new WebSocketTransport(new StandardWebSocketClient(container)));
WebSocketClient webSocketClient = new SockJsClient(transports);
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
stompClient.setInboundMessageSizeLimit(Integer.MAX_VALUE);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
return stompClient;
}
for server side:
#Bean
public ServletServerContainerFactoryBean createServletServerContainerFactoryBean() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(32768);
container.setMaxBinaryMessageBufferSize(32768);
logger.info("Websocket factory returned");
return container;
}
You can configure the websocket engine and increase the buffer size.
Watch out, depending on the actual size you'd like to use, remember that those messages will be buffered entirely in memory!
You may want to consider using partial messages if your client supports it.
Before you start the spring app you can set the property
System.setProperty("org.apache.tomcat.websocket.DEFAULT_BUFFER_SIZE", (1024*1024).toString()).
I've tried multiple bean configurations that didn't work but this property seems to be read in multiple places when I looked at the code. I'm sure there is some bean configuration that would have worked but if you don't want to bother trying many different ones until one works you can just set this parameter.
Setting this in my web.xml file worked for me:
<context-param>
<param-name>org.apache.tomcat.websocket.textBufferSize</param-name>
<param-value>65536</param-value>
</context-param>
<context-param>
<param-name>org.apache.tomcat.websocket.binaryBufferSize</param-name>
<param-value>65536</param-value>
</context-param>