Spring server send realtime Objects to client - java

I have a question.
I got a Spring server and a Client (Angularjs).
In my client i got a Rickshaw realtime chart.
When my server get some objects from a 3rd party software, i need it to send it to the client. This happens each 28th seconds.
I tried with websocket, and with SSE and i cannot get it to work.
I need someone to point me at a direction, since im lost atm.
Example.
Our server got a class called Animal.
Then a 3rd party software is sending a new object of Animal (E.g dog-cat) each 28th second.
When the server retrieves this object it, insert it in the DB and send it to the client.
The last part is what i can't make work.
Websocket tryout:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config){
config.enableSimpleBroker("/MashData/data");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry){
registry.addEndpoint("/mashData").setAllowedOrigins("*").withSockJS();
}
}
And in my controller
#MessageMapping("/mashData")
#SendTo("/MashData/data")
#Async
public MashData insertData(#PathVariable Long id, #RequestBody MashData INCmashData){
*Data created here*
return insertData
}

Related

Is it possible to have different controllers for each stomp endpoint in Spring Boot?

Is it possible to assign different Controller or at least a different MessageMapping for each of the stomp endpoints?
My goal is to have client1 connecting to /endpoint1 and client2 connecting to /endpoint2 without client1 being able to access any topics/queues of /endpoint2 and vice-versa (they are completely different applications).
So they would be completely encapsulated implementations based on the endpoint to which they connect.
Bonus points for being able to use different Jackson ObjectMapper for each endpoint as well.
So far I have created a websocket configuration with 2 endpoints (/endpoint1 and /endpoint2):
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/endpoint1", "/endpoint2")
.setAllowedOriginPatterns("*")
.withSockJS();
}
// etc...
}
I also have a Controller which can process requests and send them to appropriate user response queue, but it's accessible from both endpoints:
#Controller
public class WebSocketController {
#MessageMapping("/request")
#SendToUser("/queue/response")
public MyResponse handleMessage(MyRequest request) {
// implementation
}
}
Current behaviour:
It doesn't matter which endpoint is my client connecting to in my current implementation, both can access the same topics, which is unwanted behavior.
You should alter your application design so that clients would be only able to send messages to their respective STOMP destinations. You can name your STOMP destinations in a client-specific prefixed way such as:
/endpoint1/request
/endpoint2/request
You should then be able to define different #MessageMapping-annotated message handlers after the above naming pattern:
#Controller
public class WebSocketController {
#MessageMapping("/endpoint1/request")
#SendToUser("/endpoint1/queue/response")
public MyResponse handleClient1Message(MyRequest request) {
// process STOMP message from client 1
}
#MessageMapping("/endpoint2/request")
#SendToUser("/endpoint2/queue/response")
public MyResponse handleClient2Message(MyRequest request) {
// process STOMP message from client 2
}
}

How can I get the client IP address of requests in Spring Boot?

I'm having trouble with my security configuration and I need to create a list of the Ip's that are accessing my application.
My problem is to get the Ip in a dynamic way for all my application requests.
But I don't want to add a HttpServletRequest for all the requests of my application, what I want is a method that is called before every request, even before SecurityConfig events.
What I've tried to do is to use AuthenticationProvider to get the request HttpServletRequest and then get the Ip of my client. The problem is that I can't find a way to create a single class and it connects to all my requests. This is my code:
public abstract class IPAddressBasedAuthenticationProvider implements AuthenticationProvider {
/**
* Context http request
*/
#Autowired
private HttpServletRequest request;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String ipAddress = request.getRemoteAddr();
System.out.println(ipAddress);
return authentication;
}
}
What I was thinking was that my requests where going to pass in this code and then show my client Ip.
I tried autowiring it in my controller classes but my application doesn't execute then.
What can I do to make this work?
I've used a different way to get the addresses of my client requests.
I've created a class called WebConfig that implements WebMvcConfigurer. This class configures some usages in spring. and here's the code for this class:
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggerInterceptor());
}
}
This method is registering an interceptor, this interceptor will intercept the requests and using methods you will create it will execute when some request is send. For exemple my code:
public class LoggerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
System.out.println(request.getRemoteAddr());
return true;
}
}
In my case I've used "preHandle" method to get the Ip of my client request, this method is called every time someone calls my application via request and before spring handles the request my method is called, it can be used as a security configuration as well, because you can return false, in that case the request won't execute. But for that usage there's others ways with spring boot.
If I'm doing something wrong please correct me.

Broadcast messages with websockets and SpringBoot

I'm trying to understand how to publish/broadcast messages using websockets with Spring Boot to a Javascript application. All examples I can find are making use of a StompJs client - I however am unable to use StompJs in my client code, and I'm not sure my backend is correct which doesn't help.
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/subscribe")
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
}
Just using a simple #Scheduled to produce the time every 5 seconds, and send it to the time topic (Well, I believe that's what it's doing...)
#Component
#Slf4j
public class TimeSender {
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm:ss");
private SimpMessagingTemplate broker;
#Autowired
public TimeSender(final SimpMessagingTemplate broker) {
this.broker = broker;
}
#Scheduled(fixedRate = 5000)
public void run() {
String time = LocalTime.now().format(TIME_FORMAT);
log.info("Time broadcast: {}", time);
broker.convertAndSend("/topic/time", "Current time is " + time);
}
}
There are a few points I'm a little confused about when trying to test this. Using the Simple websocket client plugin for Chrome, I have to add websocket to the end of my request in order to connect. A connection would like ws://localhost:8080/subscribe/websocket Without the websocket I can't connect, but I can't find this mentioned in any examples or Spring documentation?
The second question is how do I subscribe to the time topic? All StompJs clients call something like client.subscribe("time") etc.
I've tried ws://localhost:8080/subscribe/topic/time/websocket but no luck in receiving any timestamps.
I'm not sure if my backend code is just wrong, my URL is wrong, or I'm just missing something else.
Note: My #Controller is missing from above as I'm just focused on pushing messages from Spring to clients at this stage, not receiving messages and It's my understanding controllers just deal with incoming?
Well, I suppose if one searches obsessively enough the answer eventually turns up. Almost immediately after finding your post I found the answer I needed at http://www.marcelustrojahn.com/2016/08/spring-boot-websocket-example/. There is a really good example that essentially does what you are describing. The difference is they are using a Spring SimpMessagingTemplate to send messages to the queue. Once I followed his pattern, it all worked like a charm. Here is the relevant code snippet:
#Autowired
SimpMessagingTemplate template
#Scheduled(fixedDelay = 20000L)
#SendTo("/topic/pingpong")
public void sendPong() {
template.convertAndSend("/topic/pingpong", "pong (periodic)")
}
The method is void so the convertAndSend() method handles publishing to the topic, not the return statement as just about every other tutorial I've seen on the web indicates. This helped solve my problem.

MessageMapping Handler - No matching methods

I have a Spring webservice #Controller class with a #MessageMapping annotated method as follows:
#MessageMapping("/trade")
public void executeTrade(MarketOrderRequest trade, Principal principal) {
trade.setUserID(principal.getName());
logger.debug("Trade: " + trade);
this.tradeService.executeTrade(trade);
}
I am sending a JSON string message built using the same MarketOrderRequest POJO as is accepted by the server method. With some Key:Value pairs which are set null (but are still present).
The WebSocketConfig class has configured the following endpoints:
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/app");
}
When i try to send a message to this messagemapping using this code:
MarketOrderRequest request = new MarketOrderRequest();
//{set request variables..}
StompHeaders someHeaders = new StompHeaders();
someHeaders.putAll(sessionHeaders);
someHeaders.setDestination("/app/trade");
session.send(someHeaders, request);
With headers:
{Cookie=[JSESSIONID=8421F536B639126F84F12E655375D790; Path=/spring-websocket-portfolio/; HttpOnly], version=[1.2], heart-beat=[0,0], user-name=[fabrice], destination=[/app/trade]}
The server then prints that a method cannot be found for the request:
Searching methods to handle SEND /app/trade session=397da625042343b4bac1c913b6d8ec22 application/json;charset=UTF-8
payload={"uuid":null,"symbol":"EUR/USD","price":1.10182,"side":"1","qty":50000,"quoteID"...(truncated)
WebSocketAnnotationMethodMessageHandler[DEBUG] - No matching methods.
The server code is lifted from this project and altered slightly to suit my needs: link
I have added some role-based web socket security in an AbstractSecurityWebSocketMessageBrokerConfigurer implementation class as follows:
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.nullDestMatcher().authenticated()
.simpSubscribeDestMatchers("/user/queue/errors").permitAll()
.simpDestMatchers("/app/**").hasAnyRole("roleA", "roleB", "roleC")
//{some more subscribe dest matchers by role which are working}
}
would this possibly effect the WebSocketAnnotationMethodMessageHandler's attempts to map the request? It is pretty much the only change I have made to the config. My subscribe mappings are working perfectly.
To me it seems that there is a problem finding the method due to either the JSON or Principal parameters. I am sending the correct object type so is this possibly a problem with the User principal? Thanks
There was an error in my WebSocketConfig class.
The #componentscan annotation had the wrong package name. I updated the name to the correct value ( the name of my base package eg "com.my.project" ). Now during deployment in the logs, I can see the controller resources being mapped to the methods in my class.
Eg log output for one method:
Mapped "{[/order],messageType=[MESSAGE]}" onto public void com.my.project.web.PortfolioController.executeOrder(tradeObjects.OrderRequest,java.security.Principal)

RMI IIOP client object as callback

I'm trying to build rmi-iiop application (very simple chat).
I need server to be able to communicate with client so my thinking was to create interface of connected user:
public interface UserInterface extends Remote {
public void receiveMessage(String message) throws RemoteException;
}
Then on client side create User class with methods server can execute('receiveMessage'):
public class User extends PortableRemoteObject implements UserInterface {
protected User() throws RemoteException {
super();
}
#Override
public void receiveMessage(String message) throws RemoteException {
client.addMessageToGUI();
}
}
I use rmic -iiop Chat User which generates _Chat_Tie.class _ChatInterface_Stub.class _User_Tie.class _UserInterface_Stub.class
After placing all files on server side and client side and running the application I get following error:
java.rmi.StubNotFoundException: Stub class not found: User_Stub; nested exception is:
I guess the difference here is that Chat class is created on server and then client uses it using interface (which works fine), but user class has to be created on client side, so client works partly as a server.
My question is similar to Java RMI - Making the client a server
but for rmi-ioop implementation.
So in to words - how can I send local object reference to server so it could perform operations on it?
Thanks!
Leonty
you create a server interface like:
public interface ChatServer extends Remote {
public void registerUser(UserInterface user) throws RemoteException;
}
What I was missing is "Tie" class on client side (_User_Tie.class). Usually it's not needed but in case when object is created on client side - I needed to supply it also.
Hope it saves some time for someone else in a future :)

Categories

Resources