Spring Controller - forking request, return value before long run function ends - spring

I have controller and long run function in it, like:
#Controller
#RequestMapping("/deposit")
public class DepositController {
#RequestMapping
public ModelAndView getNewJob(long userId, Model model) {
//execute function that can runs a lot of time ...
longRunFunction();
return new ModelAndView("jobTasks");
}
public void longRunFunction(){
// process long run function
}
}
My question is :
How can I execute the longRunFunction()
and return ModelAndView("jobTasks") answer to the browser without waiting for the end of the function?
Thank you !
Hi, I found nice example here http://krams915.blogspot.co.il/2011/01/spring-3-task-scheduling-via.html

This can be done using Asynch support in Spring Framework, essentially delegate the long running task to another service, the method of which is annotated with #Async annotation, this task would then be executed by a threadpool and control will return back to your caller immediately.
Here is much more detailed reference: http://docs.spring.io/spring-framework/docs/3.2.3.RELEASE/spring-framework-reference/html/scheduling.html#scheduling-annotation-support-async
public class SampleBeanImpl implements SampleBean {
#Async
void longRunFunction() { … }
}

Add #Async to the method declaration of longRunningMethod. But to make this work without AspectJ weaving you need to put this method in an other bean.

Related

Spring Boot #Async method *sometimes* not invoked

My Spring Boot application provides the following REST controller which invokes two methods of a Service. One of these methods is annotated with #Async so it should run asynchronously.
The main application class is annotated with #EnableAsync.
The problem I observed is: Basically, the async method is executed. I can see the corresponding log entries in the production system. But it seems as if sometimes the async method does not get invoked. There are file ids in the database which do not appear in the logs.
Do you have any idea when this behavior could occur?
REST controller
#PostMapping(consumes = MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> uploadDocument(#RequestParam("file") MultipartFile multipartFile) {
long fileId = fileService.save(multipartFile);
file.generateThumbnail(fileId);
return ResponseEntity.ok().build();
}
FileService
#Transactional
public long save(MultipartFile multipartFile) {
// saves the file...
return fileId;
}
#Async
#Transactional
public void generateThumbnail(long fileId) {
// generate thumbnail
log.info("Starting thumbnail generation for fileId {}", fileId);
file.setThumbnailId(thumbnailId);
}
Calling a async method from FileService does not work as async. You must create another Service for async method and call it from your service.
Reason:
self-invocation – calling the async method from within the same class
– won't work the method needs to be public so that it can be proxied.
And self-invocation doesn't work because it bypasses the proxy and
calls the underlying method directly.
Resources: https://www.baeldung.com/spring-async

Mono response from a method which returns void

I have a service method which does not result anything, but can return an HttpException.
example
class Service{
public void myService() throws HttpException{
//do something
}
}
My calling class has a method which is supposed to return a Mono. This method calls myService().
class Caller{
#Autowire
Service service;
public Mono<Response> callMyService(){
return Mono.just("abc")
.doOnSuccess(service.myService())
.thenReturn(new Response()); //this should return Mono<Response>
}
}
My question, is how can I write callMyService() in a good way? Mono.just("abc") doesn't seem right implementation.
You should use Mono<Void> for this purpose. This mono will not forward any data, it will only signal error or completion.
You can create it using then()
Also, remember that doOnSuccess() is side effect. You should not use it for data processing, maybe use map() or flatMap(). For you case, maybe you can use Mono.fromCallable(()->service.myService()), but that may not be correct depending on what service actually does.

Spring AOP does not run as expected [duplicate]

I have several Aspects coded in my application. All others works except for the following.
Service Interface
package com.enbiso.proj.estudo.system.service;
...
public interface MessageService {
...
Message reply(Message message);
Message send(Message message);
...
}
Service Implementation
package com.enbiso.proj.estudo.system.service.impl;
....
#Service("messageService")
public class MessageServiceImpl implements MessageService {
...
#Override
public Message reply(Message message) {
...
return this.send(message);
}
#Override
public Message send(Message message) {
...
}
}
Aspect
#Aspect
#Component
public class NewMessageAspect {
...
#AfterReturning(value = "execution(* com.enbiso.proj.estudo.system.service.impl.MessageServiceImpl.send(..))",
returning = "message")
public void perform(Message message){
...
}
}
When I try to execute the send method the debug point is not getting hit in the aspect perform.
UPDATE
I did some investigations and found that this doesn't work, when the send method is invoked from the reply method as below
#Autowire MessageService messageService;
...
messageService.reply(message);
But if I call the method messageService.send(message) it works fine. But as reply method is calling send method internally, shouldn't it also invoke the aspect?
I have no idea what i have done wrong. Please help me.
Thank you jst for clearing the things up. Just for the information purposes for the future developer in SO, I'm posting the full answer to this question
Lets assume that there is a bean from SimplePojo
public class SimplePojo implements Pojo {
public void foo() {
this.bar();
}
public void bar() {
...
}
}
When we call the method foo(), it reinvokes the method bar() inside it. Even thought the method foo() is invoked from the AOP Proxy, the internal invocation of the bar() is not covered by the AOP Proxy.
So eventually this makes, if there are any advices attached to the method bar() to not get invoked
Solution
Use AopContext.currentProxy() to call the method. Unfortunately this couples the logic with AOP.
public void foo() {
((Pojo) AopContext.currentProxy()).bar();
}
Reference:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies
You are running into a limitation of Spring AOP on self-invocation. You basically can get around it by using AopContext.currentProxy(), refactor code into different beans, or use full ApsectJ weaving.
See explanation here and workaround(s).
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies
I guess the problem is the #args condition.
Spring documentation states the following:
#args - limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given type(s)
Therefore the parameter of #args has to be a type expression. So the correct pointcut expression is
#AfterReturning(value = "execution(* com.enbiso.proj.estudo.system.service.impl.MessageServiceImpl.send(..)) && args(com.enbiso.proj.estudo.system.service.impl.Message")
or simply
#AfterReturning(value = "execution(* com.enbiso.proj.estudo.system.service.impl.MessageServiceImpl.send(com.enbiso.proj.estudo.system.service.impl.Message))")
Please adjust the package of Message if it doesn't fit.

Dropwizard/Jersey sub-resource chaining

I am building REST service using Dropwizard 8.2.0. I have 2 resources: FolderResource and FileResource:
#Path("folder")
public class FolderResource {
#Path("{name}/file")
public FileResource getFileResource() {
return new FileResource();
}
}
public class FileResource() {
#GET
#Path("{id}")
#Produces("application/json")
public Response getFileInfo() {
return Response.ok().entity("{}").build();
}
}
The intention here is that when "folder/xyz/file/5" is called, getFileInfo() method will be invoked.
This Jersey feature is described here:
https://jersey.java.net/documentation/latest/jaxrs-resources.html#d0e2464
However when embedded in Dropwizard not only getFileInfo() not called, the getFileResource() function also not being invoked.
If I add #GET annotation to getFileResource() method, then it does get called, but returns FileResource JSON representation which is of course not the goal and is contrary to the documentation that clearly states that method should NOT be annotated with method designators.
What am I doing wrong ?
#Path("folder") and #Path("{name}/file") results in folder{name}/file.
You need to add a slash in between, i.e. #Path("/{name}/file"). You'll have the same issue on getFileInfo as well, so rename it to #Path("/{id}").

Spring AOP - aspect loop execution

in first and foremost i need to say that I'm new in Spring AOP (well, I'm new in AOP at all).
In my application I have service class which is advised by aspect, unitil this point is everyting fine. Aspect is triggered, everyting works. But I need to call that service method from my aspect, and there is problem. My Aspect is (logicaly) triggered for each call and everyting end on StackOwerflow error.
It is possible to prevent that aspect looping ?
I have idea to create IAspectSandbox interface (or class) and method invocations from class which will implement this interface do not trigger aspects. But I really don't know how to achieve this goal :)
My class schema:
#Service
public class MyService
{
public BarObject update( FooObject item )
{
BarObject barObject = new BarObject();
// save FooObject to database and detect changes against old row
// information about fields, that was changed is in BarObject
return barObject;
}
}
--------------------------------------------------------------------------
#Aspect
public class MyServicePointcut
{
#Pointcut("execution(* cz.package.service.MyService.update(..))")
public void myServiceItemChanged() {}
}
--------------------------------------------------------------------------
#Component
#Aspect
public class PraceZadaniChangeAspect
{
#AutoWire
private MyService myService;
#AfterReturning("cz.package.pointcuts.MyServicePointcut.myServiceItemChanged()", returning = "returnVal")
public void execute( BarObject returnVal )
{
// do something with BarObject ... mostly check changes
// .....
// .....
// at the end I need to save changes
myService.update( returnVal.getFooObject() ); // after this call is this aspect triggered again. I know why, but I don't want to :)
}
}
Answer #1: Calling Advised Method Only (Around Advice)
If you autowire your service back into your aspect, you're still invoking Spring's proxy mechanism, including the AOP aspect that you've applied to your service.
See "Around Advice" in the Spring AOP chapter:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-ataspectj-around-advice
Basically, do something like this:
#AfterReturning("...")
public void execute(ProceedingJoinPoint p, BarObject returnVal)
{
// do something with BarObject
// ...
// call original method with original args
p.proceed(p.getArgs());
}
I am not 100% sure on the code, but proceed() should call the target method directly without invoking the AOP proxy recursively.
Answer #2: Calling Multiple Target Object Methods
If you need to call multiple methods from that service object within your aspect, you'll need access to the unproxied object via getTarget():
#AfterReturning("...")
public void execute(JoinPoint p, BarObject returnVal)
{
// do something with BarObject
// ...
// call various service methods without triggering this AOP proxy again
// by using getTarget() to get the unproxied object:
MyService myService = (MyService) p.getTarget();
myService.update(...); // does not trigger AOP interceptor
myService.otherMethod(...); // neither does this
myService.thirdMethod(...); // nor this
}

Resources