Convert document objects to DTO spring reactive - spring

I'm trying to convert a document object that is retrieved by the ReactiveCrudRepository as a Flux<Client> into Flux<ClientDto>
Now that I figure out a way to do this, I'm not sure if this is blocking or not:
public Mono<ServerResponse> findAll(final ServerRequest serverRequest) {
final Flux<ClientDto> map = clientService.findAll().map(client -> modelMapper.map(client, ClientDto.class)) /*.delayElements(Duration.ofSeconds(10))*/;
return ServerResponse.ok()
.contentType(MediaType.TEXT_EVENT_STREAM)
.body(map, ClientDto.class);
}
I've tried adding the commented delayElements method and it seems it's sending them one by one, so non-blocking.
I think this is more of a nested question, but at the core I want to know how do I figure out if I do something blocking.
Thanks in advance!

You are blocking if you explicitly call to block method or if you are using a standard jdbc connector to connect to the database instead of a reactive one like reactiveMongo provided by Spring Data.
In the snnipet you have posted, there isn't any blocking, but to be totally sure you should review the code of your clientService class and its nested calls (to a repository for example)

Related

"Found ambiguous parameter type Void"? Spring Integration with Project Reactor

I'm using Project Reactor with Spring Integration to read from Kafka and write to MongoDB, and I the Kafka consume works well, but the .handle(MongoDb.reactiveOutboundChannelAdapter(mongoFactory)) stucks. I've seen that the internal code of this function is new ReactiveMongoDbStoringMessageHandler(mongoFactory)), so I've tried the following (I have a transform() method that converts from ConsumerRecord to Mono<String>, with the #Transformer annotation):
public IntegrationFlows writeToMongo() {
return IntegrationFlows.from(kafkaChannel)
.transform(this)
.handle(new ReactiveMongoDbStoringMessageHandler(mongoFactory))
.get();
}
The code follows the docs https://docs.spring.io/spring-integration/reference/html/mongodb.html#mongodb-reactive-channel-adapters.
The error I get is:
java.lang.IllegalArgumentException: Found ambiguous parameter type [class java.lang.Void] for method match: and then a very long list of functions. Any reason this could happen?
You cannot do new ReactiveMongoDbStoringMessageHandler(mongoFactory) if you are not going to subscribe to the returned Mono. A .handle(MongoDb.reactiveOutboundChannelAdapter(mongoFactory)) is the right way to do since it wraps that ReactiveMongoDbStoringMessageHandler into a ReactiveMessageHandlerAdapter for automatic subscription.
However I think your real problem is with the .transform(this). I believe you have a lot of methods in this class, so be more specific with method name. And this has nothing to do with Project Reactor. Not sure though why would one try to convert to Mono before sending to that ReactiveMongoDbStoringMessageHandler... You probably have problem in providing the payload (ConsumerRecord ?) which is not MongoDB mapped entity for saving into the collection.
Happened to me either. Solution was (notice the ReactiveMessageHandlerAdapter):
public IntegrationFlows writeToMongo() {
return IntegrationFlows.from(kafkaChannel)
.handle(new ReactiveMessageHandlerAdapter(new ReactiveMongoDbStoringMessageHandler(mongoFactory)))
.get();
}
This can be replaced by handleReactive() when this issue will be resolve.

Putting Spring WebFlux Publisher inside Model, good or bad practice?

I'm working on a code audit on a SpringBoot Application with Spring WebFlux and the team is putting Publisher directly inside the Model and then resolve the view.
I'm wondering if it is a good or bad practice because it seems to be working but in that case, which component is in charge of executing the Publisher ?
I think that it's the ViewResolver and it should not be its job. What do you think ?
Moreover, if the Publisher is not executed by the Controller, the classes annotated by #ControllerAdvice such like ExceptionHandler won't work if these Publisher return an error, right ?
Extract of the Spring WebFlux documentation :
Spring WebFlux, unlike Spring MVC, explicitly supports reactive types in the model (for example, Mono or io.reactivex.Single). Such asynchronous model attributes can be transparently resolved (and the model updated) to their actual values at the time of #RequestMapping invocation, provided a #ModelAttribute argument is declared without a wrapper, as the following example shows:
#ModelAttribute
public void addAccount(#RequestParam String number) {
Mono<Account> accountMono = accountRepository.findAccount(number);
model.addAttribute("account", accountMono);
}
#PostMapping("/accounts")
public String handle(#ModelAttribute Account account, BindingResult errors) {
// ...
}
In addition, any model attributes that have a reactive type wrapper are resolved to their actual values (and the model updated) just prior to view rendering.
https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-modelattrib-methods
Doesn't come as a shock to me.
Actually seems to be a good trade off between complexity and efficiency when the Publisher is handling complex stuff.
It has the advantage of executing the Publisher only if and when needed.
Although it might be a problem if the ModelMap handler does not have the capacity to use it properly.
As for the exceptional cases, maybe you do not want it to be executed and just printed, thus failing faster.
As for the question about what is executing the Publisher, a specific ViewResolver can be used as it is the component responsible for the "rendering". IMHO that's it's job. I do not know if a standard ViewResolver can be used for detecting values vs publishers and handle those automagically, yet this seems completely doable and efficient.

Spring integration with cache and http.outboundGateway

I am trying to implement the cache to avoid multiple calls to Rest API. Below is the implementation code for API call. Instead of calling API every time, I need to put the API results in hashmap using payload object as key and result as value. I tried different ways but I was not able to find the solution. Please, can you help me with this.
#Bean
public IntegrationFlow read() {
return IntegrationFlows.from("input").split(new PayLoadSplitter()).enrichHeaders(p -> p.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_XML))
.handle(Http.outboundGateway("http://localhost:8080/service/publications/{name}")
.httpMethod(HttpMethod.GET).expectedResponseType(String.class).uriVariable("name", p->p.getPayload())).aggregate().get();
}

ArgumentResolvers within single transaction?

I am wondering if there is a way to wrap all argument resolvers like for #PathVariables or #ModelAttributes into one single transaction? We are already using the OEMIV filter but spring/hibernate is spawning too many transactions (one per select if they are not wrapped within a service class which is be the case in pathvariable resolvers for example).
While the system is still pretty fast I think this is not necessary and neither consistent with the rest of the architecture.
Let me explain:
Let's assume that I have a request mapping including two entities and the conversion is based on a StringToEntityConverter
The actual URL would be like this if we support GET: http://localhost/app/link/User_231/Item_324
#RequestMapping("/link/{user}/{item}", method="POST")
public String linkUserAndItem(#PathVariable("user") User user, #PathVariable("item") Item item) {
userService.addItem(user, item);
return "linked";
}
#Converter
// simplified
public Object convert(String classAndId) {
return entityManager.find(getClass(classAndId), getId(classAndId));
}
The UserService.addItem() method is transactional so there is no issue here.
BUT:
The entity converter is resolving the User and the Item against the database before the call to the Controller, thus creating two selects, each running in it's own transaction. Then we have #ModelAttribute methods which might also issue some selects again and each will spawn a transaction.
And this is what I would like to change. I would like to create ONE readonly Transaction
I was not able to find any way to intercept/listen/etc... by the means of Spring.
First I wanted to override the RequestMappingHandlerAdapter but the resolver calls are well "hidden" inside the invokeHandleMethod method...
The ModelFactory is not a spring bean, so i cannot write an interceptor either.
So currently I only see a way by completely replacing the RequestMappingHandlerAdapter, but I would really like to avoid that.
And ideas?
This seems like a design failure to me. OEMIV is usually a sign that you're doing it wrong™.
Instead, do:
#RequestMapping("/link/User_{userId}/Item_{itemId}", method="POST")
public String linkUserAndItem(#PathVariable("userId") Long userId,
#PathVariable("itemId") Long itemId) {
userService.addItem(userId, itemId);
return "linked";
}
Where your service layer takes care of fetching and manipulating the entities. This logic doesn't belong in the controller.

Spring 3, ReST, #ResponseBody and #ExceptionHandler

I have been trying to get exception handling working in my simple Spring 3 based ReST web services. Based on everything I have seen, there is a bug that prevents this from working automatically with the #ResponseBody and #ExceptionHandler annotations
https://jira.springsource.org/browse/SPR-6902
So given that it isn't supported until Spring 3.1 or 3.0.6, what is the current best method for doing exception handling? I have seen numerous posts but haven't found a clear answer that has worked for me. An ideal solution would be one that automatically provides support for both xml and json
Do I have to manually define the entire marshalling setup? Won't this remove the need for the annotations that make using Spring 3 rest support worth it?
Seems in order to manually define marshalling (i.e. Jaxb2Marshaller) I need to add a new dependency on spring-ws which is a bit of a pain
Is it easier to just define a 'Response' object that all my methods return and wrap all functions in try/catch blocks?
You can redirect on error and then return something in #ResponseBody:
#ExceptionHandler(Exception.class)
public ModelAndView handleMyException(Exception exception) {
return new ModelAndView("redirect:errorMessage?error="+exception.getMessage());
}
#RequestMapping(value="/errorMessage", method=RequestMethod.GET)
#Responsebody
public String handleMyExceptionOnRedirect(#RequestParameter("error") String error) {
return error;
}
Little ugly, but this is just work around till the fix will be available.
This is a good workaround, but with one addition. The #ExceptionHandler(Exception.class)
should be #ExceptionHandler(MyException.class, YourException.class) as you can get into a loop using the general Exception class.
You can then test for (ex instanceof Myexception) to determine the message to display if need be.

Categories

Resources