In our project we are using ShedLock to prevents concurrent execution of scheduled Spring task. Sometimes we need to call this task manually, so to our front end we added a button which when clicked will call(API) this task. Now can we use ShedLock so that when the user clicks the button multiple times it will only execute the task one time and lock until it is complete.
You can certainly do that with dlock. Imagine Controller A and Service A where Controller A calls Service A. If you have a method doSomeWork at service A annotated by dlock, you can safely call the method from controller A as many as you want and it would run the service only once until the timeout. Here's a concrete example.
Service A should be something like this.
#TryLock(name = "doSomeWork", owner = "serviceA", lockFor = ONE_MINUTE)
public void doSomeWork() {
//...
}
Controller A would be something like this.
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public void doSomeWork() {
serviceA.doSomeWork();
}
See this post for more information.
From the API method, uou can use the lock manually as described in the documentation
LockingTaskExecutor executor // injected by Spring
...
Instant lockAtMostUntil = Instant.now().plusSeconds(600);
executor.executeWithLock(runnable, new LockConfiguration("lockName", lockAtMostUntil));
Did You tried to use proxy method?
#EnableSchedulerLock(mode = PROXY_METHOD, defaultLockAtMostFor = "PT30S")
https://github.com/lukas-krecan/ShedLock#scheduled-method-proxy
Related
I can't understand why cache evict is not working in my scenario. I have an application that has a scheduled service in it and has MVC for user to click some stuff.
#Cacheable(value = "applicationToken")
public Optional<String> getToken() {
return settingsRepository.getToken();
}
#CacheEvict(value = "applicationToken", allEntries = true)
public void evictApplicationTokenCache() {
log.info("Evicting token cache.");
}
public void updateToken(String token) {
log.info("Updating token.");
settingsRepository.updateToken(token);
evictApplicationTokenCache();
}
The method getToken() is called inside the scheduled service and when I tried some test to evict cache from there it worked.
However, on the MVC side, if the user updates the token, the method updateToken() gets called and although it goes inside the evictApplicationTokenCache(), on the next retrieval of the token, I still get the same token and it doesn't step into the method getToken() to actually grab the token from the repository.
The only relation I found is that the threads are different for the MVN call and for the Scheduled call. From what I know, the cache should live on the context level, not the thread level. Therefore, it shouldn't matter which thread asks for the cache to be evicted.
It seems that the updateToken() and evictApplicationTokenCache() methods are in the same class.
In that case, the #CacheEvict annotation will be ignored, because cache handling is implemented by interceptors that are only involved when you call a method from one component to a different (injected) component.
If that's the situation, you can move the evictApplicationTokenCache() method to a helper #Component, or put the #CacheEvict annotation on the updateToken() method.
I am autowiring service in controller. And in service, I have a scenario where I need to throw an exception and DB changes also. So, I tried #Async
#Transactional
public void verifyOtp(OtpDto otpDto)
...
if(xyz){
deactivateOtp(emp.getId());
throw new ServException("Mobile no requested is already assigned", "error-code");
}
}
#Async
#Transactional //with or without
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
This is not creating new thread. But, if I gives at repo, it works
public void deactivateOtp(Integer id){
otpRepo.deactivateOtp(id);
}
public interface OtpRepository extends JpaRepository<Otp, Integer> {
#Async
#Transactional
#Modifying
#Query("UPDATE Otp SET isActive = 0 WHERE id = :id")
public void deactiveOtp(#Param("id") Integer id);
First of all check that the service is wrapped into proxy (you can place a breakpoint in controller and see the reference to the service, it will be with proxy). Otherwise there is something wrong with the configuration and #Transactional/#Async won't work till you fix that.
Now, assuming this is not an issue, there is an issue in the code:
When the controller calls service.verifyOtp it goes to the proxy (to handle the transaction) and then to your implementation.
But when it reaches your implementation and you call the method that belongs to the same impl, it doesn't pass through the proxy again, instead it directly goes to the deactivateOtp as if there is no spring at all here. Of course, #Async doesn't work.
In terms of resolution:
Consider using self injection if you work with spring 4.3+. Read this thread for more information.
Alternatively, refactor your code so that the deactivateOtp will be a public method of another class. In this case the call won't be "internal" anymore, it will path through Proxy hence the #Async will work.
This is discussed many times.
Basically, Spring AOP will not intercept local method call
(in your case call deactivateOtp() within the same class)
You can read more about this behavior here: Understanding AOP proxies
Highlight:
The key thing to understand here is that the client code inside the main(..) of the Main class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo reference in this case, any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. This has important implications. It means that self-invocation is not going to result in the advice associated with a method invocation getting a chance to execute.
I downloaded the sample provided for spring micro services orchestration from GITHUB
it works as the details given in the description, but now I am trying t build my own workflow and not able to map how the call flow(code gets exectued) from one activity to other activity.
In the bpnm guide it shows the first activity name as Retrieve Shopping Cart and second one as Validate Address but when I start the workflow with rest call from the below code
public class ShoppingCartRestController {
#Autowired
private ProcessEngine camunda;
#RequestMapping(value = "/{scId}/submit", method = RequestMethod.POST)
public ResponseEntity<?> placeOrderPOST(#PathVariable("scId") String scId) {
ProcessContext context = new ProcessContext();
submitShoppingCart(scId, context);
if (context.getError() != null) {
return new ResponseEntity<>(context.getError(), HttpStatus.FORBIDDEN);
}
return new ResponseEntity<>(context.getResponse(), HttpStatus.OK);
}
private ProcessInstance submitShoppingCart(String scId, ProcessContext context) {
return camunda.getRuntimeService().startProcessInstanceByKey(//
"submitShoppingCart", //
Variables //
.putValue(ProcessConstants.VAR_SC_ID, scId).putValue(ProcessConstants.VAR_CTX, context));
}
}
from the above I am not able to get how it delegates to retrieve address and in turn that delegates to validate address and so on to end the flow ?
And how the process is linked from submitShoppingCart.bpmn (Name in this and Actual classes are not matching ?
Question 2 first: java api and process match via the processes technical id.
you see it in the "startProcessInstanceByKey" call: submitShoppingCart is the technical id of the process. In The modeller, you find it at the very top of the properties panel.
Question 1: The camunda Java API links service Tasks to execution via JavaDelegate interfaces. So for each service task, there is a class that implements what should happen in its execute(DelegateExecution execution) method.
In spring projects, these delegates are in general referred to by their bean names ... in your example, the "Retrieve Shopping Card" service is backed by the ${retrieveShoppingCartActivity} delegate. By convention, the bean name equals the class name, so look for RetrieveShoppingCartActivity to see what's inside.
I have a question about Spring #Async annotation.
I have a controller autowired a service(GnInsuranceDetailsService)
#RequestMapping(value="/agn/AP0W01A_010/insertDetail.do")
public ResponseEntity<?> insertDetail(#ModelAttribute("dto")GnInsuranceDetailsDTO dto,HttpSession session) throws Exception {
gnInsuranceDetailsDTO.setBkFlag(getAgnUserInfo(session).getBkFlag());
gnInsuranceDetailsService.insertGnInsuranceDetail(dto);//open another thread to insert data
MessageDTO dto = new MessageDTO(AgnConstant.INSERT_SUCCESS);
return new ResponseEntity<MessageDTO>(dto,HttpStatus.OK);// increase return time for client
}
And the Service insertGnInsuranceDetail method I declare #Async up method.
#Transactional(readOnly = false)
#Async
public void insertGnInsuranceDetail(GnInsuranceDetailsDTO gnInsuranceDetailsDTO) throws Exception{
GnInsuranceDetails entity = gnInsuranceDetailsDTO.convert();
gnInsuranceDetailsDAO.save(detailsEntity);
}
I put the #Async for the service method to increase controller response time for client side,but it does not work as I think.
Do I lose somethings?Or How can I modify in the easiest way to use?
You would not loose anything, when you put #Async in the method service will be executed in a different thread, Controllers insertDetail method will not be returned until your insertGnInsuranceDetail is returned or thrown any exception.
I put the #Async for the service method to increase controller response time for client side,but it does not work as I think.
#Async - Annotation that marks a method as a candidate for asynchronous execution. Can also be used at the type level, in which case all of the type's methods are considered as asynchronous.
This #Async annotation will not help you in delaying the response time. To introduce a delay, use Thread.sleep(milliseconds);
If you want main thread(controller) to wait for insert data thread to get a result (successful/failure) you can just invoke db layer code from the controller. Whereas if you want the client response to be sent earlier, then create a new thread inside the controller and use that thread to insert data. In the latter approach of creating a new thread for insert data your client will not know about the status of data insertion, since the thread is created in the controller and it will end in the controller, no feedback/response can be given to the client, since we will not be having client details.
Hope it Helps!
Reference: https://www.tutorialspoint.com/java/lang/thread_sleep_millis.htm
#Async should be first enabled in your #Configuration bean:
#Configuration
#EnableAsync
public class AppConfiguration {
[...]
}
for xml configuration add this: <task:annotation-driven>
Spring allows a method annotated with #RequestMapping to return a variety of objects, including a CompletableFuture or a Future. This allows me to spawn off an async method and let spring return the value whenever it is ready. What I am not sure I am understanding is if there are any benefits to this. For instance:
#RestController
public class MyController {
#RequestMapping("/user/{userId}")
public CompletableFuture<User> getUser(#PathVariable("userId") String userId) {
return CompletableFuture.supplyAsync(
() -> this.dataAccess.getUser(userId));
}
In this case, even though the actual computation is happening in the background, the connection will still not close and the request thread will still be active till it is done. How is it better than say:
#RequestMapping("/user/{userId}")
public User getUser(#PathVariableS("userId") String userId) {
return this.dataAccess.getUser(userId);
}
From first glances, this seems to be a better approach as there is no overhead with an additional thread and a watcher that looks for completion.
This takes advantage of Servlet 3 asynchronous request processing, using request.startAsync() method. Read here and here
To achieve this, a Servlet 3 web application can call request.startAsync() and use the returned AsyncContext to continue to write to the response from some other separate thread. At the same time from a client's perspective the request still looks like any other HTTP request-response interaction. It just takes longer to complete. The following is the sequence of events: