Call another method after Spring MVC handler returns - spring

I have a requirement where I need to return some status/Long value from a rest controller and then execute code to send push notification.
#RequestMapping(value="/create")
public String createTicket() throws InterruptedException {
// code to create ticket
return "ticket created";
// need to call sendPushNotifiction() after I return status
}
public void sendPushNotifiction() throws InterruptedException {
// code to send push notification
System.out.println("Sent push notification successfully!!");
}
Can some one please tell me how to achieve this? Is it possible to use Spring AOP for this? I don't think thread will guaranteed execution of sendPushNotifiction method only after return. So what are the ways to achieve this effectively?
Thanks in advance

I think it might be a good use case for asynchronous processing. Spring has good support for it. Precisely, you need to
Annotate sendPushNotifiction with #Async.
Annotate some configuration class with #EnableAsync.
Call sendPushNotifiction() before the return statement. The execution flow will not wait for sendPushNotifiction to finish.
If it doesn't work, try coding sendPushNotifiction in a separate service.

Create another method which first calls the createTicket() method and then calls the sendPushNotifiction(). That will do the job. This the simplest way in my humble opinion.

createTicket()is called by spring.You can't call it directly.You can use org.springframework.web.servlet.HandlerInterceptor.Just call your sendPushNotifiction() method from postHandle() or afterCompletion() method of
your HandlerInterceptor
package com.sample.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class NotifictionHandlerInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
//do nothing
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
//call your method here
//call sendPushNotifiction()
}
}
And register you handler in spring-mvc context
<mvc:interceptors>
<bean class="com.sample.NotifictionHandlerInterceptor" />
</mvc:interceptors>

You can call like that:
#RequestMapping(value="/create")
public void create(){
createTicket();
sendPushNotifiction();
}
public String createTicket() throws InterruptedException {
// code to create ticket
return "ticket created";
// need to call sendPushNotifiction() after I return status
}
public void sendPushNotifiction() throws InterruptedException {
// code to send push notification
System.out.println("Sent push notification successfully!!");
}

i agree with youngHobbit solution where you can do like below wh
#RequestMapping(value="/create")
public String entryMethod() throws InterruptedException {
String response = createTicket();
sendPushNotifiction();
return response ;
}
public String createTicket() throws InterruptedException {
// code to create ticket
return "ticket created";
// need to call sendPushNotifiction() after I return status
}
public void sendPushNotifiction() throws InterruptedException {
// code to send push notification
System.out.println("Sent push notification successfully!!");
}
Though another solution can be to forward/redirect the request to another method once you are done with first one. But take the first approach if it solves your purpose as its simple,clear and readable
AOP is for basically dealing cross cutting concerns which you want to handle across application, should not be used for very specific purpose like this. Why to introduce extra complexity when

Another way is to postpone the execution of sendPushNotification() to some thread / thread pool. Before the return, use a queue to enqueue the processing, then in your thread dequeue and process.
However, you should take care to link your requests to the real caller and take care of failures, etc.
Web is full of examples. Look for java.util.concurrent examples, executors, ...

The HandlerInterceptor is the solution, but the code get a little bit more complex than expected. Here's a code suggestion to make it simpler by putting the whole solution in a single class:
#RequestMapping(value = "/create")
public String createTicket() throws InterruptedException {
String ticket = "ticket created";
result.set(ticket); // Save the ticket to be used after response
return ticket;
}
public void sendPushNotifiction(String ticket) throws InterruptedException {
System.out.println("Sent push notification successfully!!");
}
private static final ThreadLocal<String> result = new ThreadLocal<String>();
#Bean
public MappedInterceptor interceptor() {
return new MappedInterceptor(Arrays.array("/create"), new HandlerInterceptor() {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// Get the saved object and clean for the next request
String ticket = result.get();
result.set(null);
// Execute after response
sendPushNotifiction(ticket);
}
});
}

Related

Strange behaviour of the exception handler on spring boot RestControllerAdvice handleExceptionInternal

Trying to save 2 times the same barcode card the method saveBarcodecard (left side image) throw the error: SQLIntegrityConstraintViolationException: "Duplicate entry '1-*****-EAN13' for key 'fcs_barcodecards.UK4ComplexKey" that I was expected to be intercepted by the method: handleExceptionInternal meant to handler ALL the Exceptions that are not specifically custom implemented but not.
Please note that:
SQLIntegrityConstraintViolationException extends ... extends ... Exception
I need to implement a custom handler handleSQLIntegrityConstraintViolationException (please see the image 2 below -green-) to solve this issue.
My simple question is: how come this? :)
Many thanks in advance for your answers, if any :)
#Service
public class FcsBarcodecardServiceImpl implements FcsBarcodecardService {
#Autowired
FcsBarcodecardRepository fcsBarcodecardRepository;
#Autowired
private FcsClientRepository fcsClientRepository;
#Autowired
private FcsBarcodecardMapper fcsBarcodecardMapper;
#Override
public FcsBarcodecardResponse saveBarcodecard(Long clientId, FcsBarcodecard fcsBarcodecard) {
Optional<FcsClient> fcsClient = fcsClientRepository.findById(clientId);
if (!fcsClient.isPresent()) {
throw new UsernameNotFoundException("This client do not exists!");
}
fcsBarcodecard.setFcsClient(fcsClient.get());
return fcsBarcodecardMapper
.fromFcsBarcodecardToFcsBarcodecardResponse(
fcsBarcodecardRepository.save(fcsBarcodecard));
}
}
#RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
private static final String VALIDATION_ERROR_CHECK_ERRORS_FIELD_FOR_DETAILS = "Validation error. Check 'errors' field for details.";
/**
* Predefined: A single place to customize the response body of all exception
* types.
*/
#Override
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<Object> handleExceptionInternal(Exception exception, Object body, HttpHeaders headers,
HttpStatus status, WebRequest request) {
return buildErrorResponse(exception, FCS_EALLTYPES500, status, request);
}
#ExceptionHandler(SQLIntegrityConstraintViolationException.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<Object> handleSQLIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException ex,
WebRequest request) {
return buildErrorResponse(ex, FCS_EALLTYPES500, HttpStatus.INTERNAL_SERVER_ERROR, request);
}
ResponseEntityExceptionHandler#handleExceptionInternal() is a place to customize the response body of only those exception types that are handled by an #ExceptionHandler defined in ResponseEntityExceptionHandler. As of the latest Spring version those are
#ExceptionHandler({
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class,
BindException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class
})
Obviously your SQLIntegrityConstraintViolationException is not among them and requires a separate exception handler to be caught and processed by.
If you'd like to have a single place to handle any expection, you'd have to specifically define an exception handler with e.g. #ExceptionHandler(Exception.class)

How to exchange deprecated getExtraInformation() in context of UsernamePasswordAuthenticationFilter?

I am upgrading older Spring 2.5 code to Spring 3.0 (as a first step). During this I found the following problem:
The method getExtraInformation() from the type AuthenticationException is deprecated
The point is that this happens in a subclass of UsernamePasswordAuthenticationFilter:
#Override
protected void unsuccessfulAuthentication(final HttpServletRequest req, final HttpServletResponse res, final AuthenticationException authException) throws IOException, ServletException
{
req.setAttribute("exception", authException);
super.unsuccessfulAuthentication(req, res, authException);
if (authException instanceof CredentialsExpiredException)
{
final User user = ((UserDetailsImpl)authException.getExtraInformation()).getUser();
if (user.getCredentials().getUserCannotChange())
{
throw authException;
}
req.setAttribute("user", user);
req.setAttribute("msg", this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.credentialsExpiredPleaseChange"));
}
}
Until now I found no way to get the User in another way. So my question is how to get the user when it is no longer transported via the exceptions extra information?
The point is that the User is required here, because a decision has to be made if the exception is only rethrown or if a message should be presented to the user.
Btw. I have found no code that creates a CredentialsExpiredException with ExtraInformation, so I assume this will be done by the Spring/Spring Security Framework?
I think you have to step back and do this "extra information" check when Spring Security checks if there is CredentialsExpiredException. Assuming you are using the default settings , the CredentialsExpiredException is checked in the postAuthenticationChecks UserDetailsChecker in DaoAuthenticationProvider. The default implementation is DefaultPostAuthenticationChecks which you can override it with yours :
public class MyPostAuthenticationChecks extends DefaultPostAuthenticationChecks {
public void check(UserDetails user) {
UserDetailsImpl userImpl = (UserDetailsImpl)user;
if (user.getCredentials().getUserCannotChange()){
throw new CredentialsExpiredException("Some customized error message blalblbal");
}else{
super.check(user);
}
}
}

Spring Boot Callback after client receives resource?

I'm creating an endpoint using Spring Boot which executes a combination of system commands (java.lang.Runtime API) to generate a zip file to return to the client upon request, here's the code.
#GetMapping(value = "generateZipFile")
public ResponseEntity<Resource> generateZipFile(#RequestParam("id") Integer id) throws IOException {
org.springframework.core.io.Resource resource = null;
//generate zip file using commandline
resource = service.generateTmpResource(id);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_TYPE, "application/zip")
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"randomFile.zip\"")
.body(resource);
//somehow delete generated file here after client receives it
}
I cannot keep stacking up the files on the server for obvious disk limit reasons, so I'm looking for a way to delete the files as soon as the client downloads them. Is there a solution in Spring Boot for this? I basically need to hook a callback that would do the cleanup after the user receives the resource.
I'm using Spring Boot 2.0.6
You can create a new thread but a best solution would be create a ThreadPoolExecutor in order to manage threads or also Scheduled annotation helps us.
new Thread() {
#Override
public void run() {
service.cleanup(id);
}
}.start();
UPDATED
A best answer, it would be using a Stack combine with Thread.
Here is the solution that I've done.
https://github.com/jjohxx/example-thread
I ended up using a HandlerInterceptorAdapter, afterCompletion was the callback I needed. The only challenge I had to deal with was passing through the id of the resource to cleanup, which I handled by adding a header in my controller method:
#GetMapping(value = "generateZipFile")
public ResponseEntity<Resource> genereateZipFile(#RequestParam("id") Integer id,
RedirectAttributes redirectAttributes) throws IOException {
org.springframework.core.io.Resource resource = myService.generateTmpResource(id);;
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_TYPE, "application/zip")
.header(MyInterceptor.TMP_RESOURCE_ID_HEADER, id.toString())
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"someFile.zip\"")
.body(resource);
}
The interceptor code:
#Component
public class MyInterceptor extends HandlerInterceptorAdapter {
public static final String TMP_RESOURCE_ID_HEADER = "Tmp-ID";
#Autowired
private MyService myService;
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
if(response == null || !response.containsHeader(TMP_RESOURCE_ID_HEADER)) return;
String tmpFileId = response.getHeader(TMP_RESOURCE_ID_HEADER);
myService.cleanup(tmpFileId);
}
}
For more information about interceptors see here.

Debug Jersey mapping/routing execution before reaching endpoints/resources

I have been working with Glassfish/Jackson for over a year and I always have this problem when introducing a new endpoint implementation: when the endpoint is not reached and I want to understand why, the only hints I have to go on are the returned request, since the execution doesn't reach the desired endpoint or resource (routing/mapping error).
I want to intercept the Jersey mapping/routing execution before reaching endpoints/resources, with the "raw" request, so that I can better understand resource/endpoint mapping and routing problems.
This answer to a different question, by #xeye, solved this problem for me:
Create a filter that implements ContainerRequestFilter, and override its filter method. This will be where we can intercept all requests for debugging.
// Executed whenever a request is sent to the API server.
// Useful for debugging errors that don't reach the desired endpoint implementation.
#Provider
#Priority(value = 0)
public class MyFilter implements ContainerRequestFilter {
#Context // request scoped proxy
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
try {
// put a breakpoint or log desired attributes of requestContext here.
} catch (Exception e) {
// ignore
}
}
}
Then register this new class in your ConfigResource implementation.
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig(){
register(MyFilter.class);
// ...
}
(It's OK to Ask and Answer Your Own Questions)

Asynchronous variation of the service activator EIP?

We have the following Camel route in our application:
from(webServiceUri).routeId("webServiceRoute")
.unmarshal(jaxb)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
final Message in = exchange.getIn();
final DataRequest body = in.getBody(DataRequest.class);
final DataRequest.Items items = body.getItems();
itemValidator.validate(items.getItem());
getContext().createProducerTemplate().sendBody(importUri, body);
DataResponse response = new DataResponse();
response.setReturnCode(ReturnCode.SUCCESS);
in.setBody(response);
}
})
.marshal(jaxb);
We want the "webServiceRoute" to return the response user as soon as the processor has validated the data and forwarded the message to the "importUri". But right now it seems like the response is not returned to the caller until the "importUri" exchange is completed. So my question is what is the "correct" way to asynchronously forward the received request to another queue? There will not be any reply from the "importUri" exchange (i.e. it should be InOnly).
You can replace .sendBody(importUri, body) by .asyncSendBody(importUri, body).
Nevertheless I find your route looks strange to me, why do you use a processor to forward your message. I would write something like:
DataResponse successResponse = new DataResponse();
response.setReturnCode(ReturnCode.SUCCESS);
from(webServiceUri).routeId("webServiceRoute")
.unmarshal(jaxb)
.bean(WebServiceRouteHelper.class,"validate")
.to(importUri)
.setBody(constant(sucessResponse))
.marshal(jaxb);
class WebServiceRouteHelper {
public DataRequest validate(DataRequest dataRequest) throws Exception {
final DataRequest.Items items = body.getItems();
itemValidator.validate(items.getItem());
return dataRequest;
}
}

Resources