Issue with Spring retry for Exception Block - java

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;
}
}

Related

#Transactional(isolation = Isolation.SERIALIZABLE) retry mechanism

#Transactional(isolation = Isolation.SERIALIZABLE)
I have that annotation on several methods in my spring project. If there is an exception due to the "serialize access problems" what is the best approach if I want to retry the specific transaction. There is annotation #Retryable but it is not very straightforward to me how to use it so that the transaction will rollback and then retry only for that specific exception and just rollback for the other runtime exceptions. Thanks in advance.
A simple solution is to have a method that is the "entry point" for performing your logic; which delegates the actual logic to a transactional method. Typically a nice way of doing this is to have one class that has the Transactional annotations and that does the work and another which is the interface for clients to interact with that delegates; providing a form of indirection.
private static final int MAX_RETRY = 5;
public void doWork(T... parameters) {
doWork(0, parameters);
}
private void doWork(int retryLevel, T... parameters) {
if (retryLevel == MAX_RETRY) {
throw new MaximumRetryCountException(); //or any other exception
} else {
try {
//Get your Spring context through whatever method you usually use
AppContext().getInstance().getBean(classInterestedIn.class).doTransactionalMethod(parameters);
} catch (ExceptionToRetryFor e) {
doWork((retryLevel + 1), parameters);
}
}
}
#Transactional(isolation = Isolation.SERIALIZABLE)
public void doTransactionalMethod(parameters) {
...
}
Please note you may run into problems calling a Transactional method from a different method within that same class (i.e. calling this.doTransactionalMethod()) hence the invocation of Transactional Method is through the Spring Application Context. This is due to the way Spring AOP wraps classes to engage transactional semantics. See: Spring #Transaction method call by the method within the same class, does not work?

Springboot #retryable not retrying

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();
}
}

Java Spring #Transactional method not rolling back as expected

Below is a quick outline of what I'm trying to do. I want to push a record to two different tables in the database from one method call. If anything fails, I want everything to roll back. So if insertIntoB fails, I want anything that would be committed in insertIntoA to be rolled back.
public class Service {
MyDAO dao;
public void insertRecords(List<Record> records){
for (Record record : records){
insertIntoAAndB(record);
}
}
#Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
Where MyDAO dao is an interface that is mapped to the database using mybatis and is set using Spring injections.
Right now if insertIntoB fails, everything from insertIntoA still gets pushed to the database. How can I correct this behavior?
EDIT:
I modified the class to give a more accurate description of what I'm trying to achieve. If I run insertIntoAAndB directly, the roll back works if there are any issues, but if I call insertIntoAAndB from insertRecords, the roll back doesn't work if any issues arise.
I found the solution!
Apparently Spring can't intercept internal method calls to transactional methods. So I took out the method calling the transactional method, and put it into a separate class, and the rollback works just fine. Below is a rough example of the fix.
public class Foo {
public void insertRecords(List<Record> records){
Service myService = new Service();
for (Record record : records){
myService.insertIntoAAndB(record);
}
}
}
public class Service {
MyDAO dao;
#Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
I think the behavior you encounter is dependent on what ORM / persistence provider and database you're using. I tested your case using hibernate & mysql and all my transactions rolled back alright.
If you do use hibernate enable SQL and transaction logging to see what it's doing:
log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.transaction=DEBUG
// for hibernate 4.2.2
// log4j.logger.org.hibernate.engine.transaction=DEBUG
If you're on plain jdbc (using spring JdbcTemplate), you can also debug SQL & transaction on Spring level
log4j.logger.org.springframework.jdbc.core=DEBUG
log4j.logger.org.springframework.transaction=DEBUG
Double check your autocommit settings and database specific peciular (eg: most DDL will be comitted right away, you won't be able to roll it back although spring/hibernate did so)
Just because jdk parses aop annotation not only with the method, also parse annotation with the target class.
For example, you have method A with #transactional, and method B which calls method A but without #transactional, When you invoke the method B with reflection, Spring AOP will check the B method with the target class has any annotations.
So if your calling method in this class is not with the #transactional, it will not parse any other method in this method.
At last, show you the source code:
org.springframework.aop.framework.jdkDynamicAopProxy.class
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
......
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping orfancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
}

Configure interceptor (proxy) on intercepted method

As far as I know, in Spring AOP when we want some method call to be intercepted, we configure an Aspect having such a pointcut configuration that matches wanted method invocation. In other words, we configure interception on the Aspect side.
Is there a way to configure it entirely from the opposite side, that is, on method which invocation is to be intercepted? I expect that something like this is possible:
#Component
class MyClass {
#Intercept(interctptor="myInterceptor", method="invoke")
Object methodThatWillBeIntercepted(Object arg) {
// ..
}
}
#Component(value="myInterceptor")
class MyInterceptor {
Object invoke(MethodInvocation mi) {
// ...
if (someCondition) {
return mi.proceed();
} else {
return someOtherValue;
}
}
}
You can, at least if you're using it with AspectJ. You can use the syntax #annotation(com.mycompany.MyAnnotation) in your pointcut declaration to target elements that are annotated with your annotation. You can read more about it in section 9.2.3 of the Spring reference documentation
If you're not using AspectJ, but a generic proxy based interceptor, a "brute force" approact would be to proxy all the object you want to check and then check the method invocation argument to see if the method is annotated with your annotation, something like this:
class MyInterceptor {
public Object invoke(MethodInvocation mi) {
if(mi.getMethod().getAnnotation(MyAnnotationClass.class) != null) {
// Do the interception
}
else {
return mi.proceed();
}
}
}
Don't remember the exact API for MethodInvocation, but something like that.

Mocking a service that throws an exception?

I want to test a JSF Backing-Bean method "isInProgress" that delegates to a service method "isInProgress". When the service method throws an exception, the bean should put an event on a specific event logger and return false.
When I debug the following test, I get into the catch-block. The mocked service does not throw the exception, but returns a "default answer" which is false for th boolean. What am I doing wrong?
I also wonder if the try-catch around the "when" call can be avoided somehow, as the actual exception is swallowed by the bean under test. In fact I think "declaratively" passing the name of the method to the "when" should suffice. Is there a way to get that cleaner ?
#Test
public void testIsInProgressExeption() {
//prepare object and inputs
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
try {
when(bean.getService().isInProgress()).thenThrow(new Exception());
} catch (Exception e) {
//prepare expected object and result
MyBean expectedBean = new MyBean();
expectedBean.setService(service);
boolean expected = false;
//execute method under test
boolean actual = bean.isInProgress();
//check return values and exceptions
assertEquals(expected, actual);
//check that bean did not change unexpectedly
assertTrue(bean.equals(expectedBean));
//check sideeffects on event log
assertTrue(logEvents.containsMessage("MDI09"));
}
}
For reference here is the updated Test:
#Test
public void testIsInProgressExeption() throws Exception {
//prepare object and inputs
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
when(bean.getService().isInProgress()).thenThrow(new Exception());
//prepare expected object and result
MyBean expectedBean = new MyBean();
expectedBean.setService(service);
boolean expected = false;
//execute method under test
boolean actual = bean.isInProgress();
//check return values and exceptions
assertEquals(expected, actual);
//check that bean did not change unexpectedly
assertTrue(bean.equals(expectedBean));
//check sideeffects on event log
assertTrue(logEvents.containsMessage("MDI09"));
}
Move the when clause out of the try block and change it to:
when(service.isInProgress()).thenThrow(new Exception());
Now it should throw an exception when called.
For the records, I was doing state-base testing. Interestingly, Fowler posted in http://martinfowler.com/articles/mocksArentStubs.html a very nice article that goes quite the same route but then differentiates it from mocking and interaction-based testing.
You are doing it wrong. First you should lay out your test with the BDD or AAA keywords, with BDD :
#Test public void testIsInProgressExeption() {
// given
// when
// then
}
In the given part you will write your fixture, i.e. the setup of your test scenario. In the when part you will call the production code i.e. the tested subject. Lastly in the when part you will write your verifications and or assertions.
Stubs go in the fixture, so this line is misplaced, it doesn't belong here, it' just a definition of the behavior.
when(bean.getService().isInProgress()).thenThrow(new Exception());
However you should directly the service reference instead of the bean.getService(), this is akward.
I don't really understand why you are creating a new instance of the bean in the catch clause, this is weird. But here's how I wold write the test. Note by the way I explain in the unit test name what behavior the test is actully testing, writing this in camel case is way to painful to read, so I use the underscored convention, it's ok in tests.
#Test public void when_service_throw_Exception_InProgress_then_returns_false() throws Exception {
// given
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
when(service.isInProgress()).thenThrow(new Exception());
// when
boolean result = bean.isInProgress();
// then
assertFalse(result);
}
Also I would split the assertion on the event, this a different behavior :
#Test public void when_service_throw_Exception_InProgress_then_log_event_MDI09() throws Exception {
// given
MyBean bean = new MyBean();
MyService service = mock(MyAdapterService.class);
bean.setService(service);
// somehow set up the logEvents collaborator
when(service.isInProgress()).thenThrow(new Exception());
// when
bean.isInProgress();
// then
assertTrue(logEvents.containsMessage("MDI09"));
}
You can even go further to simplify the fixture, if you use JUnit, you write this code :
#RunWith(MockitoJUnitRunner.class)
public class MyBeanTest {
#Mock MyService service;
#Mock LogEvents logEvents;
#InjectMocks MyBean bean;
#Test public void when_service_throw_Exception_InProgress_then_log_event_MDI09() throws Exception {
// given
when(service.isInProgress()).thenThrow(Exception.class);
// when
bean.isInProgress();
// then
verify(logEvents).logEvent("MDI09");
}
}
In the example above I also extrapolated on the log event stuff, but it is just to give the idea of what's possible.

Categories