Springboot #retryable not retrying - java

The following code is not retrying. What am I missing?
#EnableRetry
#SpringBootApplication
public class App implements CommandLineRunner
{
.........
.........
#Retryable()
ResponseEntity<String> authenticate(RestTemplate restTemplate, HttpEntity<MultiValueMap<String, String>> entity) throws Exception
{
System.out.println("try!");
throw new Exception();
//return restTemplate.exchange(auth_endpoint, HttpMethod.POST, entity, String.class);
}
I have added the following to the pom.xml.
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
I also tried providing different combinations of arguments to #Retryable.
#Retryable(maxAttempts=10,value=Exception.class,backoff=#Backoff(delay = 2000,multiplier=2))
Thanks.

In spring boot 2.0.2 Release, I have observed that the #Retryable is not working if you have retryable and called method in same class. On debugging found that the pointcut is not getting built properly. For now, the workaround for this problem is that we need to write the method in a different class and call it.
Working Example could be found here.

For the #Retryable annotation on the method to be discovered it needs to be called correctly from an initialised context. Is the method invoked from a bean from the spring context or called by other means?
If testing this is your runner using the SpringJunit4ClassRunner?

Spring's #Retryable, #Cacheable, #Transaction, etc. are ALL implemented using Aspect Oriented Programming. Spring implements AOP via proxy-based weaving. Proxies intercept calls from one bean to another. Proxies cannot intercept calls from one object's methods to another. This is a general limitation of proxy based weaving.
The following solutions address this limitation: 1) as mentioned above, use #Autowired (or #Resource) to inject a bean with a self reference; calls to this reference transit the proxy. 2) Use AspectJ's ClassLoader instead of Spring's default proxy-based weaving. 3) As mentioned above, place the methods on separate beans. I've done each in various situations, each has pros and cons.

I solved it. I figured out that if return something from the method that you trying to retry, then #Retryable() is not working.
maven dependency in pom.xml
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
Spring boot Application.java
#SpringBootApplication
#EnableTransactionManagement
#EnableRetry
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
in controller.java
#RestController
public class JavaAllDataTypeController {
#Autowired
JavaAllDataTypeService JavaAllDataTypeService;
#RequestMapping(
value = "/springReTryTest",
method = RequestMethod.GET
)
public ResponseEntity<String> springReTryTest() {
System.out.println("springReTryTest controller");
try {
JavaAllDataTypeService.springReTryTest();
} catch (Exception e) {
e.printStackTrace();
}
return new ResponseEntity<String>("abcd", HttpStatus.OK);
}
}
in service.java
#Service
#Transactional
public class JavaAllDataTypeService {
// try the method 9 times with 2 seconds delay.
#Retryable(maxAttempts=9,value=Exception.class,backoff=#Backoff(delay = 2000))
public void springReTryTest() throws Exception {
System.out.println("try!");
throw new Exception();
}
}
output: It' trying 9 times then throwing exception.

I had exactly the same issue as described in the original question.
In my case it turned out that the spring-boot-starter-aop dependency was accidentally not included. After adding it to my pom.xml, my #Retryable methods worked as expected.
Returning values from #Retryable methods works fine for me.

It work for return type as well
#Service
public class RetryService {
private int count = 0;
// try the method 9 times with 2 seconds delay.
#Retryable(maxAttempts = 9, value = Exception.class, backoff = #Backoff(delay = 2000))
public String springReTryTest() throws Exception {
count++;
System.out.println("try!");
if (count < 4)
throw new Exception();
else
return "bla";
}
}

For those who want to call #Retryable block in same class can to this way.
The key here is not to call the method directly and through self-injected bean
#Slf4j
#Service
public class RetryService {
#Resource(name = "retryService")
private RetryService self;
public String getValue(String appender) {
return self.getData(appender);
}
#Retryable(value = NumberFormatException.class, maxAttempts = 4, backoff = #Backoff(500))
public String getData(String appender) {
log.info("Calling getData");
Integer value = Integer.parseInt(appender);
value++;
return value.toString();
}
#Recover
public String recoverData(String appender) {
log.info("Calling recoverData");
return "DEFAULT";
}
}
Can read more about using Retry in detail here

An alternative could be RetryTemplate
#Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(2000l);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(2);
retryTemplate.setRetryPolicy(retryPolicy);
return retryTemplate;
}
and
retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
#Override
public Void doWithRetry(RetryContext arg0) {
myService.templateRetryService();
...
}
});
worked out for me
source

Pretty old thread, but I wanted to share that after changing my method visibility from private to public, Retryable was successfully retrying.
This is in addition to using the self resource mentioned above.

Even I faced the same issue, Later after some investigation and research came to know that along with #Retryable annotation above the method we also need to provide #EnableRetry above the class. This #EnableRetry annotation either can be provided above same class in to which you have provided method you want to retry or above your main spring boot application class. For example like this:
#RequiredArgsConstructor
**#EnableRetry**
#Service
public class SomeService {
**#Retryable(value = { HttpServerErrorException.class, BadRequestException.class},
maxAttempts = maxRetry, backoff = #Backoff(random = true, delay = 1000,
maxDelay = 8000, multiplier = 2))**
public <T> T get( ) throws HttpServerErrorException, BadRequestException {
//write code here which you want to retry
}
}
I hope this will help and resolve your issue.

I got this one solved by moving #Retryable directly in front of the method I wanted to retry.
From this:
public class MyClass {
public String toBeRetried() {
return delegateTo();
}
#Retryable
public String delegateTo() {
throw new Exception();
}
}
To this:
public class MyClass {
#Retryable
public String toBeRetried() {
throw new Exception();
}
}

Related

How to audit methods in Java Spring Boot

I am writing a Spring Boot Application. I want to audit methods with my annotation #AuditMetod: For example I have method foo() with the annotation:
#AuditMetod(name = "SomeValue")
foo() {...}
I want to handle and audit such methods like this (the simplest example):
auditMethod(Method method) {
if (method.hasAnnotation(AuditMethod.class)) {
System.out.println (method.getName() + " was called at " + new Date())
}
}
upd
Thanks to #Karthikeyan #Swapnil Khante and #misha2048 I understood, that I need to use AOP. But I have 2 problems:
The only method in Aspect class in not being called and I don't see the inscription "----------ASPECT METHOD IS CALLED-----------" in log
How can I check in aspect method what method it is intercepting. To get an instance of Method class.
Now I have the following code:
Controller:
#PostMapping
#LoggingRest(executor = "USER", method = "CREATE", model = "SUBSCRIPTION")
public ResponseEntity<?> create(#Valid #RequestBody SubscriptionRequestDto dto) {
...
}
Aspect:
`#Aspect
#Slf4j
#Component
public class AuditAspect {
#Pointcut(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----------ASPECT METHOD IS CALLED------------");
}`
And annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LoggingRest {
String executor() default "SYSTEM";
String method() default "";
String model() default "";
}
Auditing is a cross-cutting concern and can be handled using AOP.
Another solution would be to use a low-level solution by writing a custom annotation and using a Spring interceptorto write your business logic.
To use the Spring interceptor you will need to implement the HandlerInterceptor interface
Example of the annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Audit {
boolean active() default true;
}
Interceptor example
#Component
public class AuditInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Audit annotation = handlerMethod.getMethodAnnotation(Audit.class);
if (annotation != null && annotation.active()) {
// your business logic
}
}
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
check this interceptor example
I think one of the solutions here, as #Karthikeyan mentioned, is to use Spring AOP.
If you are not aware a brief introduction - spring-aop module implements the aspect oriented programming paradigm. We extract some common functionality, that we generally want to apply to some subset of functions/methods, to an entity called Aspect (see class annotated with #Aspect). This class will contain out cross-cutting functionality - such as auditing, for instance we want to audit the methods execution time, lets say. We just put the code to be executed, the condition, which tell the spring what exact beans methods should be affect by this aspect, see below.
For example, if I can audit the method execution duration with the following very simple example (in my case I said that any public method, returning void inside the Class com.example.stackoverflow.BusinessLogicClass must be inspected by this Aspect):
#SpringBootApplication
#EnableAspectJAutoProxy
public class StackoverflowApplication implements ApplicationRunner {
#Autowired
private BusinessLogicClass businessLogicClass;
public static void main(String[] args) {
SpringApplication.run(StackoverflowApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
businessLogicClass.test();
}
}
#Aspect
#Component
class MyAspectLogicClass {
#Around("execution(public void com.example.stackoverflow.BusinessLogicClass.*(..))")
public Object hangAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long before = System.currentTimeMillis();
Object returnedValue = proceedingJoinPoint.proceed();
long after = System.currentTimeMillis();
System.out.printf("Retruned in '%s' ms %n", (after - before));
return returnedValue;
}
}
#Component
class BusinessLogicClass {
public void test() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
In my case, I will get the time before method execution, then by the means of
proceedingJoinPoint.proceed() call I delegate the execution to the real method, and then, once I get the response back, I will get the current system time and calculate the execution time, fairly simple.
I hope I have at least directed you somewhere, if you are looking for documentation, this are the resources I suggest you should look for:
https://docs.spring.io/spring-framework/docs/2.5.x/reference/aop.html offical spring doc (stale a bit, but there are some valuable things to learn)
https://docs.spring.io/spring-framework/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html is more fresh doc
Hope it helped :)
The problem was in right annotation. In Aspect class I tried #Around and everything works as I need.
#Aspect
#Slf4j
#Component
public class AuditAspect {
#Around(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
var method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
log.info("----------ASPECT METHOD IS CALLED------------");
}
}
For getting a Method instance I use fallowing code
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();

Issue with Spring retry for Exception Block

I am trying to use retry https://www.mscharhag.com/spring/spring-retry but facing an issue. I have a method
public String methodA( T Object) {
try{
}catch(someException sme){
return fixit(Object);
}
}
#Retryable(value = {someException.class}, maxAttempts = 3)
public String fixit( T Object) {
return x;
}
Where I need to try fixit 3 times if the same exception is thrown. But somehow retryable is not working for a method I am calling from exception block.
Any pointers of whats missing? If I add the annotation on top of methodA it works, but thats not the behaviour I want. I tried #Recover,but it is called only after the maxAttempts is tried and not each time exception is hit.
I assume that #Retryable is working with AOP interceptors and Java proxies.
The issue you are facing is that you are calling a method within the same class and therefore the AOP proxy does not get involved. This is why your code starts to work as expected when you move the annotation to the method methodA (since I assume that method gets call from another class).
What you can do is move your method fixit to another Spring bean and inject it into the Spring bean with methodA
#Component
class ClassWithMethodA {
#Autowired
private AnotherBean bean;
public String methodA( T o) {
try{
}catch(someException sme){
return bean.fixit(o);
}
}
}
#Component
class AnotherBean {
#Retryable(value = {someException.class}, maxAttempts = 3)
public String fixit( T o) {
return x;
}
}

Hysterix Javanica AsyncResult Future.get Throwing Exception

I have a Spring Cloud set up running on my local tomcat. I am using feign client to invoke a remote service wrapped inside Hysterix command one direct and other async as below.
#HystrixCommand(fallbackMethod = "fallBackEmployeeCall")
public List<EmployeeBean> getEmployees() {
//Call through Feign Client
return empInterface.getEmployees();
}
//Async Version
#HystrixCommand(fallbackMethod = "fallBackEmployeeCall")
public Future<List<EmployeeBean>> getEmployeesAsync() {
return new AsyncResult<List<EmployeeBean>>() {
#Override
public List<EmployeeBean> invoke() {
return empInterface.getEmployees();
}
};
}
When I am calling getEmployeesAsync().get()
I am getting below exception
java.lang.UnsupportedOperationException: AsyncResult is just a stab and cannot be used as complete implementation of Future
It is similar to below issue :-
[https://github.com/Netflix/Hystrix/issues/1179][1]
According to docs the solution is to configure HystrixCommandAspect class, which I did as below :-
#Configuration
#EnableAspectJAutoProxy
public class HystrixConfiguration {
#Bean
public HystrixCommandAspect hystrixAspect() {
return new HystrixCommandAspect();
}
}
But I am still getting the same exception. It seems I am missing some configuration.
Note :- my sync method is working fine.
you can try call getEmployeesAsync in the other class, which injected the instance of the class with getEmployeesAsync. I had this exception, too. Then I do it like this successful.

Method invocation based on custom annotation in Spring?

I have a custom annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Controller {
EventType[] events() default EventType.MESSAGE;
}
And there are methods in class B using them like below:
#Controller(events = {EventType.MESSAGE, EventType.DIRECT_MESSAGE})
public void onMessage(Message msg) { }
#Controller(events = {EventType.STAR_ADDED})
public void onStarAdded(Message msg) { }
Now, I want to invoke the above methods based on the annotation events value from another class A. In other words, when class A receives an event of type STAR_ADDED, I want to invoke all methods in class B with annotation #Controller(events = {EventType.STAR_ADDED}).
I know how to do this in Java but does Spring provide any API to do this? If yes, a code snippet would be helpful too.
Solution 1:
You could also do something like this:
enum EventType {
MESSAGE {
#Override
public void handleMessage(Service service, Message message) {
service.onMessage(message);
}
},
STAR_ADDED {
#Override
public void handleMessage(Service service, Message message) {
service.onStarAdded(message);
}
public abstract void handleMessage(Service service, Message message);
}
}
In your other class, where you know what is the "active" event:
yourEvent.handleMessage(service, message);
Solution 2:
I don't know if spring has anything precisely for that, otherwise you could also use reflection. Here's an example using reflection (I much prefer the solution above => enum without reflection):
for(Method method: Service.class.getDeclaredMethods()){
Controller annotation = m.getAnnotation(Controller.class);
for(EventType event: annotation.events()){
if(event.equals(yourActiveEventType)){
method.invoke(service, message);
}
return ...
}
}
Hint (not a solution) 3:
I really don't think the following applies for your scenario, but I thought I'd mention it... Spring AOP lets you trigger some code when an annotated method is called (it's kind of the opposite of your scenario), check this answer, but it may be worth the read for you: aspectj-pointcut-for-all-methods-of-a-class-with-specific-annotation
#Around("execution(#Controller * com.exemple.YourService.*(..))")
public Object aroundServiceMethodAdvice(final ProceedingJoinPoint pjp)
throws Throwable {
// perform actions before
return pjp.proceed();
// perform actions after
}
Solution 4: (added after comments)
Using org.reflections
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
example:
Service service = ...;
Message message = ...;
Set<Method> methods =
ReflectionUtils.getMethods(Service.class, ReflectionUtils.withAnnotation(Controller.class),ReflectionUtils.withParametersAssignableTo(Message.class));
for(Method m: methods){
Controller controller = m.getAnnotation(Controller.class);
for(EventType eventType: controller.value()){
if(EventType.MESSAGE.equals(eventType)){
m.invoke(service, message);
}
}
}
This assumes that you already hold the reference to the Service object (where your methods are).
Since you are using Spring, if your 'Services' are spring managed, you may get the instance from spring's context, you'll have to try it out for yourself, as this is somewhat bound to your design:
#Autowired
private ApplicationContext appContext;
Reflections r = new Reflections(new MethodAnnotationsScanner(), "com.your.package");
Set<Method> methods = r.getMethodsAnnotatedWith(Controller.class);
for(Method m: methods){
Controller controller = m.getAnnotation(Controller.class);
for(EventType eventType: controller.value()){
if(EventType.MESSAGE.equals(eventType)){
String className = m.getDeclaringClass().getSimpleName();
className = className.replaceFirst(className.substring(0,1), className.substring(0,1).toLowerCase());
Object service = appContext.getBean(className);
m.invoke(service, message);
}
}
}
This works if your Class is spring managed and is added to the context using its default camelcase name.
You may simplify the logic, but I believe the principal elements are there.

Why is my advice/pointcut not running at all?

Both 'aop:aspectj-autoproxy' and 'mvc:annotation-driven' are present in the XML config.
Both of these classes are defined as a bean inside of the same XML.
Using Spring 3.2.3.RELEASE and Google App Engine 1.8.1 in a local/dev environment.
My pointcut does not execute.
My advice. Declared inside a class annotated with #Aspect.
#Component
#Aspect
public class RequestLimiter {
private MemcacheService cache = MemcacheServiceFactory.getMemcacheService();
#Pointcut("within(#pcs.annotations.LimitRequests com.zdware.pcs.controllers.PingCollectorController)")
public void methodRequestLimited(){}
#Around("methodRequestLimited() && args(req,limitRequests)")
public Object requestGateWay(ProceedingJoinPoint jp, HttpServletRequest req,LimitRequests limitRequests) throws Throwable {
// do stuff
}
}
The method I am using to test in the controller layer.
#Controller
public class PingCollectorController {
#RequestMapping(value="/test")
#LimitRequests(requestTimeLimit = 1, functionName = "Test")
public String test(){
return "test"; // this will return me to a jsp that doesnt exist, but my advice is still not executing.
}
}
Is CGLIB in the classpath? It will be needed to generate the proxy (since your controller does not implement an interface, spring cannot use a simpler JDK proxy).

Categories