Rest API send websocket message as well as response entity - spring

I'm using spring boot and I was wondering if I can create a REST API that also sends a message to a websocket channel? so anyone subscribed to it can get it. Since it's a rest api, there would also be a response entity when it's done as well. If so, can i see an example of how that would work? i've been googling everywhere.

Good starting points to build a Spring Boot application with Websocket support are
WebSockets chapter in the official Spring documentation
WebSocket Security chapter in the Spring security documentation
Baeldung post Intro to Security and WebSockets
To determine connected users you can use SimpUserRegistry bean and to send messages to them you can use SimpMessagingTemplate, for example:
#RestController
public class ApiController {
private final SimpMessagingTemplate template;
private final SimpUserRegistry userRegistry;
public ApiController(SimpMessagingTemplate template, SimpUserRegistry userRegistry) {
this.template = template;
this.userRegistry = userRegistry;
}
#PostMapping("/api/users/{username}/send")
public ResponseEntity<?> sendMessage(#RequestBody Message message, #PathVariable String username) {
Set<SimpUser> users = userRegistry.getUsers();
if (users.stream().anyMatch(simpUser -> simpUser.getName().equals(username))) {
template.convertAndSendToUser(username, "/messages", message);
return ResponseEntity.noContent().build();
} else {
return ResponseEntity.notFound().build();
}
}
}
You can check my minimal example of working Websockets demo application.

Related

Spring Boot web socket has issues while sending messages for the first time

I am trying to create a chat application with spring boot web socket. The implementation is completed with spring boot and my Angular 7 app is connecting to this. The issue I face is, when i connect to the socket for the very first time after server reboot or so, the first 5-6 messages to the socket are not sent. From then on it works flawlessly and super fast. What is it that I am missing?
I am implementing WebSocketConfigurer and trying to use registerWebSocketHandlers to connect to /socket// where i pass my user id and conversation id to initiate the chat similar to /socket/1/2.
#Configuration
#EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
Logger logger = LogManager.getLogger(WebSocketConfiguration.class);
#Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new
ServletServerContainerFactoryBean();
container.setMaxBinaryMessageBufferSize(1024000);
return container;
}
#Bean
public SessionHandler sessionHandler() {
return new SessionHandler();
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry
registry){
registry.addHandler(sessionHandler(),
"/socket/*/*").setAllowedOrigins("*");
}
}
Expect the system to transport messages flawlessly from message 1. But it is perfect after the first few messages.

Intercepting Camel #Consume

I have an existing application which is using Apache Camel to send messages to SEDA endpoints for Async processing and would like to intercept calls to these methods for instrumentation.
Example code:
#Component
public class CamelMessageService {
private static final Logger log = LoggerFactory.getLogger(CamelMessageService.class);
public static final String QUEUE = "seda:message";
#Resource
private ProducerTemplate producerTemplate;
public void send() {
producerTemplate.sendBody(QUEUE, "Hello World");
}
#Consume(uri = QUEUE)
public void receive(#Body String payload) {
log.info("Received message {}", payload);
}
}
Is there a way to intercept all methods annotated with #Consume before invoking. I looked at an AOP based approach but this seemed to fall over due to existing Spring/Camel proxying of these classes.
I have also tried using various Camel Intercept routes and adding a custom InterceptStrategy but it seems that the example above does not create a Camel route so is not intercepted.
EDIT: On further investigation in seems that these endpoints can be Intercepted using camel but only if there is at least 1 other route defined in the Camel Context?
#Component
class MyRouteBuilder extends RouteBuilder {
private static final Logger log = LoggerFactory.getLogger(MyRouteBuilder.class);
public void configure() {
interceptSendToEndpoint(CamelMessageService.QUEUE)
.process(exchange -> log.info("intercepted exchange {}", exchange));
from("timer:hello?period={{timer.period}}").routeId("hello").routeGroup("hello-group")
.transform().simple("yo")
.filter(simple("${body} contains 'foo'"))
.to("log:foo")
.end()
.to("stream:out");
}
}
If I run this app with the Route Builder above then my interceptor is triggered if however I comment out the hello route it is not?
Any help would be greatly appreciated.

How to Create Spring WebSocket Application With HTML5 WebSocket API?

Recent Version of Spring WebSocket works with SockJS and StompJS libraries. But i don't like to use theme in my application. So how to create Spring WebSocket application with HTML5 WebSocket API and integrate our application with Spring Security?
I could not find any good example on how to configure spring websocket without sockjs but i found some helpful documentation in spring documentation site and i like to share that. Well, How to Create Spring WebSocket Application With HTML5 WebSocket API?
First: Create a Class that extends TextWebSocketHandler or BinaryWebSocketHandler and Annotate it with #Component annotation and Override its appropriate method.This Class works like handler methods in controllers.
#Component
public class SimpleWebSocketHandler extends TextWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session,
TextMessage message) throws Exception {
// Sends back response to client.
session.sendMessage(new TextMessage("Connection is all right."));
}
}
Second: Create a Configuration Class that implements WebSocketConfigurer and Annotate it with #Configuration and #EnableWebSocket annoations and Override its appropriate method.This Class uses Handler Class that we created already.
#Configuration
#EnableWebSocket
public class WebSocketConfigurations implements WebSocketConfigurer {
#Autowired
private SimpleWebSocketHandler simpleWebSocketHandler;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// Regsiters a handler for related endpoint.
registry.addHandler(simpleWebSocketHandler, "/chat");
}
}
Third: Add all your WebSokcet Endpoints to your Spring Security Configuration.
httpSecurity.authorizeRequests()
.antMatchers("/chat").permitAll();
Fourth: We create a new javascript WebSocket objet with appropriate URL.
// Create WebSocket Object.
var ws = new WebSocket("ws://localhost:8080/chat");
// Runs when connecion is estabilished.
ws.onopen = function () {
// Sends request to server with string value.
ws.send("webSocket");
};
// Runs when response is ready.
// Use event to get response value.
ws.onmessage = function (event) {
};
Note: WebSocket URLs Format: ws://domain:port/endpoint

Spring RestTemplate as a Spring Service

I have developed a REST server with our app specific APIs. we also have deployed a different rest Job server into another location. Currently the way I am doing is .
#RestController
public class SparkJobController {
#Autowired
private IJobSchedulerService jobService;
...
And the Service Implementation is
#Service(value="jobService")
public class JobSchedulerServiceImpl implements IJobSchedulerService {
#Override
public Map triggerJob(String context) {
Map<String, ?> s = new HashMap<String,Object>();
RestTemplate restTemplate = new RestTemplate();
// restTemplate call to other REST API. and returns Map.
...
}
My question is , Is my approach correct ? Or Does Spring framework enables us to use some predefined APIs which can help to use RESTTemplate as a Service
[EDIT] : the deployed REST service is third party application.
I did some research and havent' seen yet a way to implement RestTemplate as a service.
I have seen RestTemplate defined in the bean config and auto wired in - https://www.informit.com/guides/content.aspx?g=java&seqNum=546
To summarize, most of the examples I have seen use Resttemplate, similar to how you have implemented in your code.

Problems injecting a BayeuxService into another class with annotations

I have a web app that is using Bayeux to handle Comet connections. I initialize a BayeuxServer and tie it into Spring annotations and it all works fine, listening on selected channels and responding.
I have a Jersey annotated class and an annotated Bayeux service as shown below. The idea is I wanted to be able to control resources via Rest from an individual web app, and then right after the resource is changed, do a server push via Comet to all other applicable clients to tell them to update their information.
Here is the problem: A Bayeux Service is created when the webapp is deployed, setting up proper channels to listen on and monitoring clients. There should only be one instance of this. When Jersey attempts to use the Bayeux service it creates a whole new service, when it should be using the original one. This new service doesn't have the BayeuxServer properly injected so I can't access client information through it.
It makes since that this should be doable, but I don't seem to understand how to inject these things properly via annotations. Can anyone point me in the right direction?
Jersey Annotated Class:
#Path("JsonTest")
public class JsonTest {
#Context
Request request;
#Context
UriInfo uriInfo;
#Context
ResourceContext resourceContext;
protected final Logger log = Logger.getLogger(getClass());
public JsonTest() {
}
#DELETE
#Path("{id}")
public void deleteJson(#PathParam("id") String id) {
JsonTestDao.instance.getModel().remove(id);
log.info("Deleted Json..." + id);
log.info("New json: " + JsonTestDao.instance.getModel().toString());
JsonTestService jsonTestService = resourceContext.getResource(JsonTestService.class);
jsonTestService.sendUpdate();
}
}
BayeuxService:
#Named
// Singleton here didn't seem to make a difference
#Service
public class JsonTestService {
protected final Logger log = Logger.getLogger(getClass());
#Inject
private BayeuxServer bayeux;
#Session
private ServerSession serverSession;
#PostConstruct
public void init() {
log.info("Initializing JsonTest Bayeux HelloService...");
log.info("Current sessions are: " + bayeux.getSessions().toString());
}
#Listener("/cometd/JsonTest")
public void jsonTestHandler(ServerSession remote, ServerMessage.Mutable message) {
}
public void sendUpdate() {
//bayeux.newMessage(); // Need a method that the Jersey class can call to notify changes
log.info("Bayeux server should be sending an update now...");
}
#PreDestroy
public void destroy() {
log.info("Destroying JsonTest Bayeux HelloService...");
}
}
See Jersey and spring integration - bean Injections are null at runtime.
Another question I asked. Both of these stem from the same problem involving properly setting the Jersey dependency and integrating it with spring.

Resources