I'm trying to add resilience4j into my app for exponential backoff, etc.
Service
#Component
public class ResilienceService {
private static final String BACKEND_A = "backendA";
public ResilienceService() throws IOException {
testRetry();
}
#Retry(name = BACKEND_A)
public void testRetry() throws IOException {
System.out.println("Hey it's working!");
throw new IOException();
}
}
Config
resilience4j.retry.instances.backendA.maxAttempts=3
resilience4j.retry.instances.backendA.waitDuration=10s
resilience4j.retry.instances.backendA.enableExponentialBackoff=true
resilience4j.retry.instances.backendA.exponentialBackoffMultiplier=2
resilience4j.retry.instances.backendA.retryExceptions[0]=java.io.IOException
I'm trying to basically see if resilience lib will call this function 3 times. How should I think about both configuring this correctly and also testing that the retries are actually happening? I thought I could put a breakpoint on the method and see it call 3 times, but maybe I'm misunderstanding.
Aside from the comment by #M.Deinum above, you may also have caught out by resilience4j-springboot2 not depending on spring aop by default.
E.g. you might need:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
To be fair, the documentation does state:
Add the Spring Boot 2 Starter of Resilience4j to your compile dependency.
The module expects that org.springframework.boot:spring-boot-starter-actuator and org.springframework.boot:spring-boot-starter-aopare already provided at runtime
Related
I am trying to intercept any method in my application which is annotated with my custom developed annotation.
Initially I used the Spring AOP which works fine. But, it is not intercepting if the method call is in the same target class.
Going through the official docs, I got to know that the Spring AOP uses proxy beans for the same.
One workaround I found was to self inject the target class. But, this seems like too much fuss. Like every time I am adding my custom annotation to a method, I need to make sure that I add the #Scope annotation, set the proxyMode & self inject target class as shown here
Later I moved on to configuring and using native AspectJ.
This is my Custom annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface TrackTime {
String description() default "";
}
Here is the Aspect for TrackTime annotation:
#Configuration
#Slf4j
#Aspect
public class TrackTimeServiceImpl {
#Pointcut("execution(public * *(..))")
public void methodsToBeProfiled(){
}
#Around("methodsToBeProfiled() && #annotation(x.y.z.TrackTime)")
public Object audit(ProceedingJoinPoint joinPoint) throws Exception {
//Business logic
}
}
I would like to mention here that my application is running on Jetty server.
The configuration file:
#Configuration
#EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
#EnableSpringConfigured
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class TrackTimeConfig implements LoadTimeWeavingConfigurer {
#Override
public LoadTimeWeaver getLoadTimeWeaver() {
return new InstrumentationLoadTimeWeaver();
}
}
The aop.xml file:
Path to file: /resources/META-INF/aop.xml
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="in.xxx.*"/>
</weaver>
<aspects>
<!-- weave in just this aspect -->
<aspect name="in.xxx.yyy.zzz.TrackTimeServiceImpl"/>
</aspects>
</aspectj>
Relative dependencies added in parent pom.xml have been mentioned here:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.21</version>
</dependency>
My service class:
#Component
public class SomeService {
public void a(){
b();
}
#TrackTime
public void b(){
//business logic
}
}
Now when the method a() is called from the controller, even though the b() has the #TrackTime annotation, it is not intercepted.
Also, I would like to mention that I have set the following program arguments while running the application
-javaagent:/Users/xxx/.m2/repository/org/springframework/spring-instrument/5.3.6/spring-instrument-5.3.6.jar
-javaagent:/Users/xxx/.m2/repository/org/aspectj/aspectjweaver/1.9.6/aspectjweaver-1.9.6.jar
I have gone through docs, articles, followed solutions on stackoverflow. But, for the above mentioned configuration/code, it is not working as I want.
Requesting help from the community.
Thanks in advance.
I did not try to run your example locally yet, but noticed two details at first glance in your code:
You are configuring both #EnableLoadTimeWeaving (for native AspectJ) and #EnableAspectJAutoProxy (for Spring AOP), which might be conflicting with each other. Try to remove the latter and see if it fixes the problem.
In aop.xml, you are using <include within="in.xxx.*"/>. Please note that this will only include classes directly in the in.xxx package, but not classes in sub-packages. In order to include them as well, please use the double-dot notation in.xxx..*.
Feel free to report back if it is still not working with the suggested changes. I can take a closer look then.
First of all, I would like to thank #hfontanez, #void void & #kriegaex for responding and helping me out in moving forward in solving the problem statement.
So if anyone is looking out on how to intercept nested & private methods, let's have this as a one stop in configuring native AspectJ.
Please check my POC here on github for a working example.
In my case, I added the aspectjweaver JAR in my project structure and passed the arguments through VM Options in IDE.
That all !!
Nested/Private methods will now be intercepted.
Thank you,
Karthik
I'd like to use resilience4j-spring-boot2 in our Spring Boot 2.2.1.RELEASE project to retry the failed requests against a third party service. However, I'm unable to register the fallbackMethod for some reason:
pom.xml (relevant dependencies):
<!-- Resilience4J and dependencies -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
application.yaml:
resilience4j.retry:
instances:
exponentialBackoffBehaviour:
maxRetryAttempts: 6
waitDuration: 1s
enableExponentialBackoff: true
exponentialBackoffMultiplier: 2
My Java code:
#Retry(name = "exponentialBackoffBehaviour", fallbackMethod = "retryfallback")
private List<Applicant> requestApplicantList(HttpHeaders headers) throws JsonProcessingException {
// This always returns 500 because is mocked
ResponseEntity<String> response = restTemplate.exchange(URL, HttpMethod.GET, new HttpEntity(headers), String.class);
return objectMapper.readValue(response.getBody(), new TypeReference<List<Applicant>>() {});
}
private List<Applicant> retryfallback(HttpHeaders headers, Throwable e) {
System.out.println("retryfallback");
return new ArrayList<>();
}
"retryfallback" is never printed in the console.
What am I doing wrong?
The anotations only work on public methods not private.
Due to the proxy-based nature of Spring's AOP framework, private methods are by definition not intercepted. For JDK proxies, only public interface method calls on the proxy can be intercepted. With CGLIB, public and protected method calls on the proxy are intercepted (and even package-visible methods, if necessary). However, common interactions through proxies should always be designed through public signatures.
So I've started setting up a simple SparkJava application from scratch with the basic goal of comparing it to a mirrored SpringBoot (with SpringData) application which I've already built. However I've been struggling with DeltaSpike (which is the alternative to SpringData chosen) setup as that adds a layer of CDI complexity which I didn't have to handle within SpringBoot.
Long story short after trying out some (probably wrong) approaches: now I'm stuck at enabling Weld (CDI) in the SparkJava Jetty container. I've taken a look on how to enable Weld for Jetty but that only got me more confused - and I'm not even sure that it's supposed to work.
What I'm getting now is: Exception in thread "main" java.lang.NoClassDefFoundError: org/jboss/weld/events/WeldEvent.
Since there are many files it's better to just share my github repo: https://github.com/vitorbmiranda/taskapp-sparkjava. Don't mind unused code and stuff like that as it's still a WIP.
A final disclaimer: apart from that error itself I feel like I may be completely missing something or my approach with SparkJava/CDI/DeltaSpike could be completely off, or my Maven dependencies could not make any sense, so any directions on that would also be appreciated. Cheers.
Update Sep 2nd: did a fresh setup on a new repo. Managed to customize the Spark embedded Jetty handler and added the Weld Listener to it.
This is how I've added the listeners:
import org.jboss.weld.environment.servlet.BeanManagerResourceBindingListener;
import org.jboss.weld.environment.servlet.Listener;
...
JettyHandler handler = new JettyHandler(matcherFilter);
...
handler.addEventListener(new BeanManagerResourceBindingListener());
handler.addEventListener(new Listener());
This is from the startup log (using TRACE level within logback.xml)
5:46:50.427 [Thread-0] DEBUG o.e.j.u.component.ContainerLifeCycle - spark.embeddedserver.jetty.JettyHandler1530180168==dftMaxIdleSec=-1 added {org.jboss.weld.environment.servlet.BeanManagerResourceBindingListener#49483272,UNMANAGED}
15:46:50.432 [Thread-0] DEBUG o.e.j.u.component.ContainerLifeCycle - spark.embeddedserver.jetty.JettyHandler1530180168==dftMaxIdleSec=-1 added {org.jboss.weld.environment.servlet.Listener#62383f62,UNMANAGED}
No "Weld" references after that. If I try to use any #Injected bean it won't work as expected (since logs didn't give me anything related to Bean/classpath lookup):
public class TestController {
#Inject
TestService testService;
public static String getTest(Request request, Response response) {
// NPE here
testService.test();
return value;
}
}
#Default
public class TestService {
public String test() {
return "123";
}
}
My pom.xml looks like:
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-core</artifactId>
<version>3.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet-core</artifactId>
<version>3.1.2.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-spi</artifactId>
<version>3.1.SP1</version>
</dependency>
The META-INF/beans.xml (which I would think it's not necessary, anyway) is:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
I have Spring 5.0.4 application with a Dispatcher servlet config file as spring-web-servlet.xml (spring-web being Dispatcher servlet name).
To enable AOP in the application, I made the entry <aop:aspectj-autoproxy /> in spring-web-servlet.xml after including the namespace for aop.
The pom.xml has below entries:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>
To use AOP, I created below Aspect class:
#Aspect
#Component
public class MyAspect {
#Before("getApiPointcut()")
public void logRequest(JoinPoint joinPoint) {
//some logic here
}
#AfterReturning(value = "getApiPointcut()", returning = "returnValue")
public void logResponse(Object returnValue) {
//some logic here
}
#Pointcut("within(com.test.service.controller.*) " +
" || within(com.test.service.api.GenericService)" +
" || within(com.test.service.api.UserService)")
public void getApiPointcut() {
}
}
This class has two methods: logRequest() and logResponse().
Every API call will first go through logRequest() and before returning the response, logResponse() is called.
For the initial few requests (5-6), AOP works fine and I get the flow in MyAspect class for both the methods.
But after that, if I hit any API, flow goes through logRequest(), then the API class, and then it hangs. The API class calls the service class. But the flow never reaches the service class and hence never goes in logResponse() and the server does not return anything to the client. I checked the logs and there are no exceptions. I checked the heap memory usage too and it is not consumed fully. Even the threads are not fully occupied. (Checked these stats using jconsole).
If I disable AOP by removing <aop:aspectj-autoproxy /> from spring-web-servlet.xml file, the server returns response for all the calls without any issue.
So, the conclusion that I made is enabling the AOP is causing the server to hang but I am not able to understand why.
Any help is appreciated.
UPDATE
The APIs (controller) on which I am trying to invoke AOP are annotated with #PreAuthorize. If I comment out the #PreAuthorize annotation, the application does not hang. Uncommenting the #PreAuthorize annotation again results in 5-6 successful requests and then the application hangs.
As #PreAuthorize also uses AOP, is there any case that I need to take care of when using my own AOP with #PreAuthorize?
UPDATE 2
The #PreAuthorize method on the API is as below:
#PreAuthorize("hasAuthority('SOME_AUTH') and #myUtil.canRead(#userId) ")
If I remove the hasAuthority('SOME_AUTH') and keep the second check, the aplication works fine, but if I keep both or keep only hasAuthority() check, the application does not responds after a few successful responses.
UPDATE 3
The issue gets resolved if I annotate the API with #Transactional.
However, the code used to work without #Transactional when aop was disabled. So why does enabling AOP makes it necessary to have #Transactional on the API?
I'm trying to create a SpringBoot application which uses solr repositories. I'm following this tutorial:
http://docs.spring.io/spring-data/solr/docs/current/reference/html/#solr.repositories
which says to configure my application with the following class (Example 43):
#Configuration
#EnableSolrRepositories
class ApplicationConfig {
#Bean
public SolrClient solrClient() {
EmbeddedSolrServerFactory factory = new EmbeddedSolrServerFactory("classpath:com/acme/solr");
return factory.getSolrServer(); // getSolrServer does not exist
}
#Bean
public SolrOperations solrTemplate() {
return new SolrTemplate(solrClient());
}
}
The problem is if I do that it doesn't recognise getSolrServer() as a method of factory. Indeed, if you look at the most recent API for EmbeddedSolrServerFactory you don't find that method, but it apparently existed in a previous version of the same class.
Maybe it was renamed from getSolrServer to getSolrClient, for some reason, from one version to another.
Here's my dependencies in the pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Anyway, I tried to change getSolrServer to getSolrClient, but the return type, i.e. SolrClient, is now incompatible. If I try to return org.apache.solr.client.solrj.embedded.EmbeddedSolrServer, it gives me an error because it doesn't find org.apache.solr.client.solrj.embedded...
Another problem using this would be that SolrTemplate doesn't require a EmbeddedSolrServer, so this is not a good option...
I am using eclipse not spring suite, Assuming spring suite using latest version of spring-boot-starter-data-solr ( 1.4.2 ) and you need to add an entry for solr-core 5.x in your pom.
Since EmbeddedSolrServer is extending SolrClient, follows Java IS-A relationship and it should be compatible with SolrClient. This binary is part of solr-core.
Your code need to use getSolrClient itself and it should be compatible with SolrClient
Dependencies in pom.xml is as follows
Here we go with our code base without any errors.
Instead of creating SolrClient bean, create EmbeddedSolrServerFactoryBean object and pass that object to solr template to create SolrTemplate object. Here is my config file:
#Configuration
#EnableSolrRepositories(basePackages = "com.ida.*.repository")
#Profile("dev")
public class SolrConfigDev {
#Autowired
private Environment environment;
#Bean
public EmbeddedSolrServerFactoryBean solrServerFactoryBean() {
EmbeddedSolrServerFactoryBean factory = new EmbeddedSolrServerFactoryBean();
factory.setSolrHome(environment.getRequiredProperty("solr.solr.home"));
return factory;
}
#Bean
public SolrTemplate solrTemplate() throws Exception {
return new SolrTemplate(solrServerFactoryBean().getObject());
}
}
In addition, you have to add solr-core to you pom.xml file.
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-core</artifactId>
<version>5.5.3</version>
</dependency>
More information about this topic, you can find here in this blog by Petri Kainulainen.