Message mapping not done in spring - java

I am working on spring web socket. i just create a controller for calling apis through WS. but unfortunately its mapping is not done. so i cant call that apis from client.
This is my controller class
#Controller
public class FloorController {
#MessageMapping("/floormapupdate")
public Message greeting(#Header(value = "nativeHeaders") Map s, Principal principal, Message message) throws Exception {
String type = (String) ((List) (s.get("type"))).get(0);
Map headers = new HashMap();
headers.put("type", type);
System.out.println("===================" + principal.getName());
simpMessagingTemplate.convertAndSendToUser(principal.getName(), "/channel/me", message);
return message;
}
}
what is the issue with it ?

Related

Cannot throw error from backend to frontend (from Spring Boot to React)

I have the following endpoint and when I use Postman, I can get the error message thrown from Service method:
Controller:
#PostMapping("/signup")
public ResponseEntity<ApiResponse<CommandResponse>> registerUser(
#RequestBody SignupRequest request) {
final CommandResponse userId = authService.registerUser(request);
return ResponseEntity.ok(new ApiResponse<>(Instant.now(clock).toEpochMilli(),
SUCCESSFULLY_CREATED, userId));
}
Service:
public CommandResponse registerUser(SignupRequest request) {
if (authRepository.existsByUsername(request.getEmail())) {
throw new EntityExistsException(USER_ALREADY_EXISTS);
}
// create a new user and return it if user is not already exists
return CommandResponse.builder().id(savedUser.getId()).build();
}
I use a global exception handler and get the error message properly. But in order to get it from Controller method, what should I do? I have really no experience with fullstack and have trouble. Any idea?

WebSocket subscription not receiving message when using MappingJackson2MessageConverter

I would like to subscribe and receive any events using objects instead of strings when using Spring Boot WebSockets. If my method returns a string and I use the StringMessageConverter my code successfully listens for the /topic/rooms/created event.
If I return a Room object instead and use MappingJackson2MessageConverter then my subscription no longer receives any messages.
#MessageMapping("/rooms/create/{roomName}")
#SendTo("/topic/rooms/created")
#CrossOrigin(origins = "http://localhost:3000")
public Room createRoom(#DestinationVariable final String roomName) {
return roomService.createRoom(roomService.getRooms().size(), roomName);
}
This sends the message and creates the room successfully. The response isn't picked up by the subscription.
final String roomName = "RoomName";
final StompSession.Subscription subscription = stompSession.subscribe("/topic/rooms/created", new StompFrameHandler() {
#Override
public Type getPayloadType(final StompHeaders headers) {
return Room.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
// Not called
System.out.println("Received message");
}
});
System.out.println("Sending message");
stompSession.send("/app/rooms/create/" + roomName, null);
I've also tried creating a Room instance and using a jackson object mapper to convert to JSON with no issues.
How can I resolve this?

Java Websocket / MessageHandler return to global scope?

I'm facing the following problem and I found no working solution yet.
I have 3 different applications that should communicate with each other:
the UI part (1)
the backend application (2)
the microservice "in the cloud" (3)
The backend application provides a Webservice (REST) for the UI to get and put information from/to the microservice.
Everything I want to grab from the microservice works fine, but:
If I want to put data to the microservice, the specs require a websocket connection. This works fine too, but the microservice returns a message after the (un-)successful command, like
{"statusCode":200,"messageId":"1234567890"}
The problem now is: How can I grab this message in my application and send it back to the UI, so the user knows if the command was successful?
For the moment I tried this:
WebSocketClient.java
#OnMessage
public void onMessage(Session session, String msg) {
if (this.messageHandler != null) {
this.messageHandler.handleMessage(msg);
}
}
public void addMessageHandler(MessageHandler msgHandler) {
this.messageHandler = msgHandler;
}
public static interface MessageHandler {
public String handleMessage(String message);
}
MyTotalAwesomeController.java
public class MyTotalAwesomeController {
WebSocketClient wsc = new WebSocketClient();
...
#RequestMapping(value="/add", method={RequestMethod.POST, RequestMethod.OPTIONS})
public ResponseEntity<Object> putDataToMicroservice(#RequestBody Map<String, Object> payload, #RequestHeader(value = "authorization") String authorizationHeader) throws Exception {
...
wsc.addMessageHandler(new WebSocketClient.MessageHandler() {
public String handleMessage(String message) {
System.out.println("RETURN MSG FROM WSS : " + message);
return message;
}
});
return ResponseEntity.ok("worked");
}
I can see the console output from the MessageHandler return, but I don't know how I can pass this to the parent method for return insted of just returning the ResponseEntity.ok().
I'm not very used to WebSocket connections in Java yet, so please don't judge me ;-)
Thank you for your help.
The code below will work under the assumption that the #OnMessage method is executed in a thread managed by the WebSocket client runtime. Please inspect the thread that runs the #OnMessage method.
If the above premise is true, the putDataToMicroservice() method, executed by a thread in the global scope, will wait until the WebSocket response arrives at the WS client thread, which will repass the message to the global scope thread. Then the execution in your controller class will continue.
public class MyTotalAwesomeController {
WebSocketClient wsc = new WebSocketClient();
// Queue for communication between threads.
private BlockingQueue<String> queue;
#PostConstruct
void init() {
queue = new SynchronousQueue<>(true);
// This callback will be invoked by the WebSocket thread.
wsc.addMessageHandler(new WebSocketClient.MessageHandler() {
#Override
public String handleMessage(String message) {
System.out.println("RETURN MSG FROM WSS : " + message);
// Pass message to the controller thread.
queue.put(message);
// Note that the return value is not necessary.
// You can take it out of the interface as well.
return null;
}
});
}
#RequestMapping(value="/add", method={RequestMethod.POST, RequestMethod.OPTIONS})
public ResponseEntity<Object> putDataToMicroservice(#RequestBody Map<String, Object> payload, #RequestHeader(value = "authorization") String authorizationHeader) throws Exception {
// At this point you make a WebSocket request, is that right?
doWebSocketRequest();
// This poll call will block the current thread
// until the WebSocket server responds,
// or gives up waiting after the specified timeout.
//
// When the WebSocket server delivers a response,
// the WS client implementation will execute the
// #OnMessage annotated method in a thread
// managed by the WS client itself.
//
// The #OnMessage method will pass the message
// to this thread in the queue below.
String message = queue.poll(30, TimeUnit.SECONDS);
if (message == null) {
// WebSocket timeout.
}
return ResponseEntity.ok("worked");
}
}

How to get the sender IP Address in spring integration?

I use Spring integration for Web service message handling. Unfortunately the Message does not contains the sender IP Address. How can I get this information?
#Bean
public SimpleWebServiceInboundGateway myInboundGateway() {
SimpleWebServiceInboundGateway simpleWebServiceInboundGateway = new SimpleWebServiceInboundGateway();
simpleWebServiceInboundGateway.setRequestChannelName("testChannel");
simpleWebServiceInboundGateway.setReplyChannelName("testResponseChannel");
return simpleWebServiceInboundGateway;
}
#ServiceActivator(inputChannel = "testChannel", outputChannel = "testResponseChannel")
public Message getHeaders(Message message) {
// how can I reach the sender IP address here
return message;
}
The SimpleWebServiceInboundGateway doesn't map transport headers by default.
See DefaultSoapHeaderMapper.
Of course you can implement your own, but that really might be enough for you to use:
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest()
.getRemoteAddr();
in that your target #ServiceActivator.
Of course that will work if you don't shift message to a different thread before the service activator. The RequestContextHolder.currentRequestAttributes() is tied with ThreadLocal.
You can retrieve it from HttpServletRequest, using getRemoteAddr() to get access to user IP address and getHeader() to get header value. This is assuming you can modify your controller class.
Perhaps this will help:
#Controller
public class MyController {
#RequestMapping(value="/do-something")
public void doSomething(HttpServletRequest request) {
final String userIpAddress = request.getRemoteAddr();
final String userAgent = request.getHeader("user-agent");
System.out.println (userIpAddress);
}
}

spring mvc integrate with reactive stream

i have a RESTful api application build on spring mvc.
recently i was doing something integration between spring mvc and reactive stream (like rxjava and project-reactor) and try to make the application more reactive.
i have just build some demo like this below:
1.for rxjava,i use PublishSubject
private SerializedSubject<StreamResult, StreamResult> subject = PublishSubject.<StreamResult>create().toSerialized();
public ReactiveStreamController() {
this.subject.subscribe(streamResult -> {
String id = streamResult.getRequest().getParameter("id");
System.out.println("[" + Thread.currentThread().getName() + "] request received. id = " + id);
String random = StringUtils.isBlank(id) ? StringUtils.EMPTY : id;
ResponseVO vo = new ResponseVO(200, "success = " + random);
streamResult.getFuture().complete(vo);
}, Throwable::printStackTrace);
}
#ResponseBody
#RequestMapping(value = "/rxJava", method = RequestMethod.GET)
public CompletableFuture<ResponseVO> rxJavaController(HttpServletRequest httpServletRequest) {
StreamResult sr = new StreamResult();
sr.setRequest(httpServletRequest);
subject.onNext(sr);
return sr.getFuture();
}
2.for project reactor
#ResponseBody
#RequestMapping(value = "/reactorCodeNew", method = RequestMethod.GET)
public CompletableFuture<ResponseVO> reactorCoreNewParadigm(HttpServletRequest servletRequest) {
Mono<ResponseVO> mono = Mono.just(servletRequest)
.subscribeOn(executorService)
.map(request -> {
String id = request.getParameter("id");
System.out.println("[" + Thread.currentThread().getName() + "] request received. id = " + id);
String random = StringUtils.isBlank(id) ? StringUtils.EMPTY : id;
ResponseVO vo = new ResponseVO(200, "success = " + random);
return vo;
})
.timeout(Duration.ofSeconds(2), Mono.just(new ResponseVO(500, "error")));
return mono.toCompletableFuture();
}
while running both the demos, i don't quite see too many difference between just using a java's CompletableFuture to supply among the controller method.
what i understand reactive stream and what i want is treating the servlet request as a stream and cosume it with some feature like backpressure.
i wanna know:
1. is there a better way to make the application more reactive?
2. is it correct or compatible to integrate spring mvc with reactive streams? if yes, how can i performce feature like backpressure?
i realize maybe i forgot to declare why/how i return a completablefuture in the controller, actually i inject a customized MethodReturnValueHandler to transform the CompletableFuture to DefferdResult.
public class CompletableFutureMethodReturnValueHandler extends DeferredResultMethodReturnValueHandler {
#Override
public boolean supportsReturnType(MethodParameter returnType) {
return CompletableFuture.class.isAssignableFrom(returnType.getParameterType());
}
#Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
CompletableFuture<?> completableFuture = (CompletableFuture<?>) returnValue;
super.handleReturnValue(CompletableDeferredResult.newInstance(completableFuture), returnType, mavContainer, webRequest);
}
}
Spring MVC is based on the Servlet API and is mostly blocking internally, so it cannot leverage reactive streams behavior. Writing adapters for the Controller layer won't be enough.
The Spring team is working on a separate initiative for this purpose. Follow SPR-14161 and the Spring blog (including this and this) to know more about reactive Spring.

Categories