I have a Spring controller that is currently being accessed normally, but i want to change the implementation in such a way that if the task the controller is performing takes more than a defined time for instance 10 seconds then the controller can respond with a "your request is being processed message" to the caller, but if the method returns within time then the response is passed to the calling method from the controller, in other words i want timed asynchronous execution from a Spring controller.
NB: This is not exactly TaskExecutor domain(at least according to my understanding) because i don't want to just hand over execution to the TaskExecutor and return immediately.
I use Spring 3.0 and Java 1.5 and the controllers don't have views, i just want to write the output direct to stream, which the calling client expects.
Well, it is the TaskExecutor domain. In your controller simply wrap your processing logic in Callable, submit it to the AsyncTaskExecutor and wait up to 10 seconds. That's it!
final Future<ModelAndView> future = asyncTaskExecutor.submit(new Callable<ModelAndView>() {
#Override
public ModelAndView call() throws Exception {
//lengthy computations...
return new ModelAndView("done");
}
});
try {
return future.get(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
return new ModelAndView("timeout");
}
Of course this is a bit nasty, especially when it occurs more than once in one controller. If this is the case, you should have a look at servlet 3.0 asynchronous support (see below).
Further reading:
25. Task Execution and Scheduling
How to use Servlet 3 #WebServlet & async with Spring MVC 3?
Support Servlet 3.0 (JSR-315)
Related
My Spring Boot Service will do a job and exit after success with 0 (there is no restcontroller), but i want it aslo to exit on every exception so i added #ControllerAdvice on a class and put this method:
#ControllerAdvice
#RequiredArgsConstructor
#Slf4j
public class ImportInekData {
final InekService inekService;
final ImportDataService dataService;
public void doTheJob(){
log.info("Fetching new list from Inek.");
UpdatedList updatedList = inekService.getUpdatedList();
List<Standort> toBeUpdated = updatedList.getToBeUpdated();
List<String> toBeDeleted = updatedList.getToBeDeleted();
log.info("List fetched with " + toBeUpdated.size() + " valid entries to be updated and " + toBeDeleted.size() + " entries marked for deletion. ");
log.info("Pushing to DB...");
dataService.importAll(toBeUpdated);
}
#EventListener
public void onStart(ContextStartedEvent start){
log.info("Application started.");
doTheJob();
log.info("Import finished.");
SpringApplication.exit(start.getApplicationContext(), () -> 0);
}
#ExceptionHandler(value = Exception.class)
public String outOnException(Exception e){
log.error("Exception occurred see logs. Stopping..");
SpringApplication.exit(context, () -> -1);
return "dying";
}
}
All is working fine but when i throw an IllegalArgumentException the #ExceptionHandler method is not called. First i had a void method with no parameter and then i began trying with String return and at least one parameter - that is not needed.
How get this working? Is there a better way for my case to react on every exception?
Controller Advices in spring is a mechanism intended to properly handle the Exceptions at the level of spring MVC.
Spring MVC in a nutshell is a web framework, and as such, it assumes that you have some kind of web endpoint that is called by the end user or maybe frontend. This endpoint is an "entry-point" to your backend code that can have services, query the database, and so forth. If during this backend flow the exception is thrown in general you don't want that the web endpoint will return 500 internal server error, so spring provides tooling for convenient mapping of these exceptions: translating them to json with a "good-looking" message, with correct HTTP code, and so forth.
If you don't have any controllers, then the whole concept of controller advices is not applicable in your flow, so there is no point in using it...
Now the real question is what exactly do you want to achieve with this exception handling?
If Application context cannot start usually spring boot application will be closed gracefully...
If you want to close the application programmatically, make sure you've read this thread
I have a method in a restcontroller. I need to convert input request to String and send to another destination after method execution is totally completed, ideally I should not affect in method processing execution, method should execute without additional delay.
In non webflux application I can use afterCompletion in HandlerIntercepter. But due to use WebFlux I cannot use that.
How can I make additional processing after controller method execution ends, and response returned to caller?
If the logic you want to call is specific to this one controller, you could use doFinally. It is executed after your Stream (Mono/Flux) terminates. https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html#doFinally-java.util.function.Consumer-
Example:
public class Playground {
ConverterService converterService;
PostProcessor postProcessor;
public Playground(ConverterService converterService, PostProcessor postProcessor) {
this.converterService = converterService;
this.postProcessor = postProcessor;
}
public Mono<String> endpoint(Integer input) {
return Mono.just(input)
.flatMap(integer -> converterService.convert(integer))
.doFinally(signalType -> postProcess(input));
}
private void postProcess(Integer input) {
this.postProcessor.process(input);
}
}
First the converterService is called. After the stream is terminated, the postProcessor is called in any case.
I would like to make my controller (Spring MVC) handle request in parallel. I use #Callable below and it does not work, because next request is handled since first one finished (returns view).
#RequestMapping(method = RequestMethod.GET)
public Callable<String> helloWorld(final Model model) throws InterruptedException {
return new Callable<String>() {
#Override
public String call() throws Exception {
Thread.sleep(5000);
return "helloWorld";
}
};
}
Do I need any special code?
The documentation for Spring MVC states the following about the Callable<?> return type for handler methods
A Callable can be returned when the application wants to produce
the return value asynchronously in a thread managed by Spring MVC.
Spring will take the returned Callable instance, pass it to an ExecutorService (actually does more processing then than that, but for simplicity) and execute it in a different Thread then your initial request. After waiting 5 seconds, it will return the view associated with the view name "helloWorld".
Using latest and greatest Spring 3.2.3, Tomcat 7.0.42, Servlet 3.0 container, Java 7. We need to do JSONP for our response, so we implement that by doing a Servlet Filter just like this:
http://jpgmr.wordpress.com/2010/07/28/tutorial-implementing-a-servlet-filter-for-jsonp-callback-with-springs-delegatingfilterproxy/
but without the web.xml currently..we are using Java Annotation Config.
In our #Controller, we are returning a DeferredResult<String>, and then in our #Service, which is called by our #Controller, we have the #Async annotation. The reason we went the #Async route (not the AysncContext route) was because this is a "fire and forget" type async operation. But I do need to return some JSON that says the operation has begun to the requestor.
#RequestMapping(method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public DeferredResult<String> testing(#RequestParam(value="submit", required=true) String param)
{
final DeferredResult<String> result = new DeferredResult<String>();
service.doIt(param, result);
return result;
}
And in our #Service
#Async
public DeferredResult<String> doIt(String param2, DeferredResult<String> result)
{
log.info("Calling Async doIt Method");
result.setResult("{hello:there}");
}
I'm stuck...I'm trying to figure out if there is a way add async-supported tag for a Servlet Filter in a Java Config? I base that on seeing this presentation (slide 28):
http://rstoyanchev.github.io/spring-mvc-32-update/#28
Some other info:
Have #EnableAsync on the Configuration, and we are using the AbstractAnnotationConfigDispatcherServletInitializer to define our application, but I can't seem to figure out how to tell that to use have async filters (just using the getServletFilters() to return our JsonpFilter).
Basically what is happening is that the JSONP filter is "wrapping" nothing...but I do see the Filter called a second time, after the result has been set in the TaskExecutor thread..but at that point the response has already gone out...so, I see the filter "get Data" twice..once right when the #Controller method exits, and then a second time right after the DeferredResult.setResult() has been set.
We also tried this as well on our Filter class:
#WebFilter(urlPatterns = "/*", asyncSupported = true)
but that to did not work (even looked like it even made 2 separate filters according to the logs..but, could I be wrong there about that).
We could switch to a Callable if we need to..that's not a big deal. I did see that there was some differences between DeferredResult and Callable with regards to what threads Spring knows about.
If the answer is that you need to use web.xml, any really good way to do a Java Config with web.xml hybrid?
EDIT 1:
Based on reading a few different resources I found a couple of things:
From: Spring Aync Preview Blog
Servlet Filters
*All Spring Framework Servlet filter implementations have been modified as necessary to work in asynchronous request processing. As
for any other filters, some will work — typically those that do
pre-processing, and others will need to be modified — typically those
that do post-processing at the end of a request. Such filters will
need to recognize when the initial Servlet container thread is being
exited, making way for another thread to continue processing, and when
they are invoked as part of an asynchronous dispatch to complete
processing.**
From the Spring MVC Ref. docs, I actually RTFM'd:
The sequence of events for async request processing with a DeferredResult is the same in principle except it's up to the
application to produce the asynchronous result from some thread: (1)
Controller returns a DeferredResult and saves it in some in-memory
queue or list where it can be accessed, (2) Spring MVC starts async
processing, (3) the DispatcherServlet and all configured Filter's exit
the request processing thread but the response remains open, (4) the
application sets theDeferredResult from some thread and Spring MVC
dispatches the request back to the Servlet container, (5) the
DispatcherServlet is invoked again and processing resumes with the
asynchronously produced result.
So, basically, i knew/know that the separate thread would be calling the filter...that's not what has been hanging me up..it was how to figure out if the filter should modify the response...and you can't look at the size of the data, because a 0 bytes could be a correct "answer."
So, I now only write if the DispatchType = ASYNC
Not really sure this is correct to do long term..but, it does seem to fix the problem.
Any other suggestions/ideas?
It seems if you're extending AbstractAnnotationConfigDispatcherServletInitializer, you can override this method and set async support:
#Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("dispatchOptionsRequest", "true");
registration.setAsyncSupported(true);
}
You just need to set the asyncSupport flag on each servlet instance like below:
#Override
public void onStartup(final ServletContext servletContext)
throws ServletException {
final AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
root.setServletContext(servletContext);
root.scan("com.my.site");
root.refresh();
final Dynamic servlet = servletContext.addServlet("cf-validator",
new DispatcherServlet(root));
servlet.setLoadOnStartup(1);
servlet.addMapping("/api/*");
servlet.setAsyncSupported(true);
}
I solved this by adding #Async to the controller method. My guess is that doing that takes care of the filters behind the scenes.
You can set async-supported as true in the servlet definition inside your web.xml file:
<servlet>
<servlet-name>my-servlet</servlet-name>
<async-supported>true</async-supported>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
I have Spring MVC application where security is handled by Spring Security.
UI is built using GWT which gets the data from server using RPC approach.
I need to handle on UI the situation when session is expired:
For example RPC AsyncCallback can get SessionExpiredException type of exception and popup the window with message like "You session is expired, please click the refresh link" or something.
Did someone deal with such problem?
Thanks.
I suppose that for processing of incoming GWT call you use some Spring MVC controller or some servlet. It can have following logic
try{
// decode payload from GWT call
com.google.gwt.user.server.rpc.RPC.decodeRequest(...)
// get spring bean responsible for actual business logic
Object bean = applicationContext.getBean(beanName);
// execute business logic and encode response
return RPC.invokeAndEncodeResponse(bean, ….)
} catch (com.google.gwt.user.server.rpc.UnexpectedException ex) {
// send unexpected exception to client
return RPC.encodeResponseForFailure(..., new MyCustomUnexpectedException(), …) ;
}
Solution for this case
HttpServletRequest request = getRequest() ;
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
return RPC.encodeResponseForFailure(..., new MyCustomSessionExpiredException(), …) ;
} else {
// first code snippet goes here
}
Then catch custom session expired exception in a client side code. If you do not use RPC directly then provide more details about your bridge implementation between GWT and Spring.
You will need also force GWT compiler to include MyCustomSessionExpiredException type to a serialization white list (to prevent case when GWT security policy stops propogation of the exception to client side). Solution: include MyCustomSessionExpiredException type to each method signature of each synchronous interface:
#RemoteServiceRelativePath("productRpcService.rpc")
public interface ProductRpcService extends RemoteService {
List<Product> getAllProducts() throws ApplicationException;
void removeProduct(Product product) throws ApplicationException;
}
MyCustomSessionExpiredException extends ApplicationException
Then show pop-up in client side code:
public class ApplicationUncaughtExceptionHandler implements GWT.UncaughtExceptionHandler {
#Override
public void onUncaughtException(Throwable caught) {
if (caught instanceof MyCustomSessionExpiredException) {
Window.alert("Session expired");
}
}
}
// Inside of EntryPoint.onModuleLoad method
GWT.setUncaughtExceptionHandler(new ApplicationUncaughtExceptionHandler());
I researched a bit and uploaded the solution here http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired%253Fstate%253Dclosed.
Use mvn jetty:run-war to see the demo after checking it out and go to rpc-security-sample/index.htm
There are two ways to solve it.
The first is around to pass the delegate proxy for GWT RemoteServlet which throws SessionExpiredException during method invocation. This requires to declare Exception in every RPC service method. Example: http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired%253Fstate%253Dclosed
Steps:
Develop new filter which intercepts first
Declare SessionExpiredException in each RPC method service which could inherit RuntimeException for simplicity (no need to follow this in implementers)
Develop parent generic AsyncCallback handler
Use http://code.google.com/p/gspring/ solution to handle all incoming RCP requests.
The second which is much more simplest: return the 401 HTTP error and handle in UI side (GWT native general exception contains the HTTP status number). Example: http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired-401
The second approach is simplest and does not require declaring Exception in service methods contract. However following the first approach can give you some flexibility: it could contain some additional info like last login time (for SessionExpiredException) etc. Also the second approach can introduce new exceptions which are inherited from SecurityException like blacklisted user (for example if user was blacklisted during his session) or for example if user does the same actions very often like a robot (it could be asked for passing the captcha) etc.