cannot establish connection to my websocket server by browser client.
configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic/", "/queue/");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/greeting");
}
}
And controller:
#MessageMapping("/message")
#SendToUser("/queue/reply")
public String processMessageFromClient(#Payload String message, Principal principal) throws Exception {
String name = new Gson().fromJson(message, Map.class).get("name").toString();
System.out.println(name);
//messagingTemplate.convertAndSendToUser(principal.getName(), "/queue/reply", name);
return name;
}
I start server, and then open index.html in browser then make connect to ws://localhost:8080/greeting
and after that sending message to /app/message
but actually happens nothing. Browser inspector shows 404. What's wronng i do?
Here is the way that use to implement WebSocket in Spring. First, you should configure the Web socket message broker and register the stomp endpoint as below. Here I use setAllowedOrigins("*").withSockJS() to access this endpoint to any host.
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/greeting")
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app")
.enableSimpleBroker("/topic", "/queue/");
}
}
Then I create the controller as below.
#Controller
public class WebSocketController {
private final SimpMessagingTemplate template;
#Autowired
WebSocketController(SimpMessagingTemplate template){
this.template = template;
}
#MessageMapping("/queue/reply")
public void sendMessage(String message){
System.out.println(message);
this.template.convertAndSend("/topic", message);
}
}
Use #MessageMapping("/queue/reply") instead of #SendToUser("/queue/reply") as above.
From that Simple Messaging Template, I used convertAndSend() method to
asynchronous data communication with that message broker. If there is
any data comes to that message broker it will automatically send that
data using the above configured endpoint called /socket with SockJS
and Stomp.
You can refer this article to learn more about the Spring web socket.
Related
I have working Stomp WebSocket endpoint:
#Configuration
#EnableWebSocketMessageBroker
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class WebSocketServerConfig implements WebSocketMessageBrokerConfigurer {
...
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("myStompEndpoint");
}
...
and want to add WebSocket endpoint without Stomp too:
#Configuration
#EnableWebSocket
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class WsServerConfig implements WebSocketConfigurer {
private final MyTextWsHandler myTextWsHandler;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(myTextWsHandler, "abcdef")
.addInterceptors(myHandshakeInterceptor());
}
}
Now the url to Stomp WebSocket is: ws://localhost:8080/myStompEndpoint. What is the url to WebSocket without Stomp?
I'm trying to allow cors from the same server, with port 3000.
As I don't know where my server is going to be deployed, I tried to access the server name as string than concat it with my desired port.
So here is my approach :
#SpringBootApplication
public class TestApplication {
#Autowired
private HttpServletRequest request;
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
System.out.println(request.getServerName());
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins(request.getServerName()+":3000");
}
};
}
}
I think these are the highlights of my error message :
Error creating bean with name 'corsConfigurer'
org.springframework.beans.BeanInstantiationException: Failed to instantiate
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread?
A good practice is to specify the allowed origins in the application.properties file and read it in your WebConfig.class. That allows you to easy add your production address later.
application.yml
web:
allowedOrigins:
- "http://localhost:4200"
- "some other"
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private final String[] allowedOrigins;
public WebMvcConfig(#Value("${web.allowedOrigins:}") String[] allowedOrigins) {
this.allowedOrigins = allowedOrigins;
}
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //
.allowedMethods("*") //
.allowCredentials(true) //
.allowedOrigins(allowedOrigins);
}
}
just use localhost, no need to access server name
I want my Spring Boot application to start regardless if it can connect to JMS or not. I have this minimal example:
#SpringBootApplication
#EnableJms
public class JmsActivemqFailoverApplication {
public static void main(String[] args) {
SpringApplication.run(JmsActivemqFailoverApplication.class, args);
}
#Component
public static class JmsReceiver {
#JmsListener(destination = "inbox")
public void receive(Message message) {
System.out.println("Received <" + message + ">");
}
}
#RestController
public static class HelloWorldController {
#GetMapping("/")
public String helloWorld() {
return "Hello world";
}
}
}
when application.properties contains:
spring.activemq.broker-url=tcp://non-existing-broker:61616
I can get response from helloWorld endpoint. When I change property to:
spring.activemq.broker-url=failover:(tcp://non-existing-broker:61616)
Application keeps trying to connect to broker and I can not get response from my REST endpoint.
Please advice, how can I have application running without waiting for ActiveMQ Failover transport to succeed.
Example code available at https://github.com/madoxas/jms-activemq-failover
One way to achieve this is:
Disable automatic JMS container startup with property spring.jms.listener.auto-startup=false
Start JMS container after application has started:
#Component
public class JmsStarter implements ApplicationRunner {
private final JmsListenerEndpointRegistry jmsRegistry;
public JmsStarter(JmsListenerEndpointRegistry jmsRegistry) {
this.jmsRegistry = jmsRegistry;
}
#Override
public void run(ApplicationArguments args) {
for (MessageListenerContainer listenerContainer : jmsRegistry.getListenerContainers()) {
listenerContainer.start();
}
}
}
I have to add support for a custom WebSocket subprotocol (so not STOMP) in a Spring Boot application, but I'm having a very hard time understanding what I need to provide and what Spring already has.
This is how far I got:
#Configuration
#EnableWebSocket
public class WebSocketAutoConfiguration implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(this.webSocketHandler(), new String[]{endpointUrl});
}
#Bean
public WebSocketHandler webSocketHandler() {
ExecutorSubscribableChannel clientInboundChannel = new ExecutorSubscribableChannel();
ExecutorSubscribableChannel clientOutboundChannel = new ExecutorSubscribableChannel();
SubProtocolWebSocketHandler subProtocolWebSocketHandler = new SubProtocolWebSocketHandler(clientInboundChannel, clientOutboundChannel);
subProtocolWebSocketHandler.addProtocolHandler(new SubProtocolHandler() {
public List<String> getSupportedProtocols() {
return Collections.singletonList("custom-protocol");
}
public void handleMessageFromClient(WebSocketSession session, WebSocketMessage<?> message, MessageChannel outputChannel) throws Exception {
session.sendMessage(new TextMessage("some message"));
}
public void handleMessageToClient(WebSocketSession session, Message<?> message) throws Exception {
}
public String resolveSessionId(Message<?> message) {
return UUID.randomUUID().toString();
}
public void afterSessionStarted(WebSocketSession session, MessageChannel outputChannel) throws Exception {
System.out.println("SESSION STARTED");
}
public void afterSessionEnded(WebSocketSession session, CloseStatus closeStatus, MessageChannel outputChannel) throws Exception {
session.close();
System.out.println("SESSION ENDED");
}
});
return subProtocolWebSocketHandler;
}
}
This works, in the sense that handleMessageFromClient does get triggered on a web socket message, but I fail to understand the purpose of MessageChannel outputChannel and handleMessageToClient.
Is it possible to get the PerConnectionWebSocketHandler semantics with SubProtocolWebSocketHandler?
The documentation around this is basically non-existent e.g. the docs for handleMessageToClient say:
Handle the given {#link Message} to the client associated with the given WebSocket session.
Well, fantastic. And the STOMP implementations are mind-boggling, so they're not very usable as a guideline.
Any example, broad steps or anything, really, would be much appreciated.
Turns out it is exceptionally easy. No need to mess with SubProtocolWebSocketHandler at all. The only requirement is that the provided WebSocketHandler implements SubProtocolCapable.
public class CustomHandler implements WebSocketHandler, SubProtocolCapable {
...
}
That's all. To make a PerConnectionWebSocketHandler, it's enough to simply extend it and implement SubProtocolCapable:
public class CustomHandler extends PerConnectionWebSocketHandler implements SubProtocolCapable {
...
}
After some time I got my WebSocket running with this config:
#Configuration
//#EnableWebSocket
public class WebSocketServerConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/var").setAllowedOrigins("*");
}
#Bean
public WebSocketHandler myHandler() {
return new WebsocketServer();
}
}
But I'm unable to find a way to launch this WebSocket from my code.
Is there a way to launch this WebSocket later in my program?
I found this document but it does not provide a way to implement a startWebSocket() function or something similar.
The document you linked shows an example, where it returns an instance of EchoWebSocketHandler.
You could accept connections and implement your own logic for afterConnectionEstablished or handleMessage using some internal status to determine whether you should accept requests, i.e.
#Configuration
#EnableWebSocket
public class MyConfiguration implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoWebSocketHandler(), "/echo").withSockJS();
}
#Bean
public WebSocketHandler echoWebSocketHandler() {
//FooWebSocketHandler implements your specific logic
return new FooWebSocketHandler();
}
}
public class FooWebSocketHandler extends AbstractWebSocketHandler {
private boolean enabled;
...
public void handleMessage(WebSocketSession session,
WebSocketMessage<?> message)
throws Exception {
if (enabled) {//work
} else {
//disabled, i.e. throw exception or send data according to your api
}
}
}