'Global' Feign Interceptor not picked up by all Feign Clients - java

We are using Open Feign in our application, which is running on Spring Boot 2.0.6 and Spring Cloud Finchley.SR2.
We need all of the Feign Clients to add a token from the security context in the header of every call, so we created a configuration, which produces a global Interceptor for all clients:
#Configuration
#Import({FeignClientsConfiguration.class})
public class FeignConfig {
#Value("${a.spring.config}")
private int minTokenLifespan;
#Autowired
private OAuthContext oAuthContext;
#Autowired
private AuthManager authManager;
#Bean
public RequestInterceptor myCustomInterceptor() {
return new CustomInterceptor(oAuthContext, authManager, minTokenLifespan);
}
}
The interceptor works for all Feign Clients but one. In the debugger we can see, that this special feign client (along with its the SynchronousMessageHandler) is created, before the Bean is created in the class FeignConfig. The CustomIntercepter is only created after the first Feign Client, all other clients are created afterward, know of the Interceptors existence and will apply it.
How can we debug this issue? Has anyone had a different problem in the past?
I can't post production code, but I would be happy to answer any question and try to post obfuscated code.

This points to a problem in creating the interceptor at the time the first client gets created.
Try putting a conditional breakpoint in org.springframework.beans.factory.support.DefaultListableBeanFactory#getBeansOfType on RequestInterceptor.class. You might see that a circular dependency exists which requires the first client to be created before the FeignConfig or CustomInterceptor classes can be instantiated.
Consider the following example:
#Configuration
#EnableFeignClients(
clients = {
MyFirstClient.class, // will NOT have CustomInterceptor registered
MySecondClient.class // will have CustomInterceptor registered
})
public class FeignConfig {
#Autowired
private BeanDependentOnMyFirstClient beanDependentOnMyFirstClient;
#Bean
public RequestInterceptor myCustomInterceptor() {
return new CustomInterceptor();
}
}
This will result in the following circular dependency:
Since the dependency between the clients and the interceptor is weak, it will just fail silently if the dependency cannot be met.

Related

#MockBean uses different instance in JMS listener when running multiple Spring Boot tests

I've written a Spring Boot Test, that writes into a JMS queue and is expecting some processing via an JMS listener. In the listener, I'm trying to read an object from S3. The AmazonS3 class should be replaced by a MockBean. In my test I set up the mock like this:
#SpringBootTest
public class MyTest {
#Autowired
MyJmsPublisher jmsPlublisher;
#MockBean
AmazonS3 amazonS3;
#Test
public void test() {
final S3Object s3Object = mock(S3Object.class);
when(s3Object.getObjectContent()).thenReturn(mock(S3ObjectInputStream.class));
when(amazonS3.getObject(anyString(), anyString())).thenReturn(s3Object);
jmsPlublisher.publishMessage("mymessage");
Awaitility.await().untilAsserted(() -> {
//wait for something here
});
}
}
#Component
#RequiredArgsConstructor
public class MyJmsPublisher {
private final JmsTemplate jmsTemplate;
public void publishMessage(String message) {
jmsTemplate.convertAndSend("destination", message);
}
}
#Component
#RequiredArgsConstructor
public class MyJmsListener {
private final AmazonS3 amazonS3;
#JmsListener(destination = "destination")
public void onMessageReceived(String message) {
final S3ObjectInputStream objectContent = amazonS3.getObject("a", "b").getObjectContent();
// some logic here
}
}
But the issue is that when running multiple Spring Boot tests , the MyJmsListener class contains a mock that is different from the one created in the Test. It's a mock, but for example the getObjectContent() returns null. But when I run the test alone, everything works fine.
I've tried to inject the AmazonS3 bean into the MyJmsPublisher and call the mocked method there and it worked. So I suspect, that it's because the JMS listener operates in a different thread.
I've found this thread and also set the reset to all available options, but does not make any difference. I also tried this OP's approach that worked for them, where I create a mock via the #Bean annotation like this:
#Configuration
public class MyConfig {
#Bean
#Primary
public AmazonS3 amazonS3() {
return Mockito.mock(AmazonS3.class);
}
}
But this just has the same behavior as mentioned above.
So can you actually use the #MockBean annotation when using different Threads like using a #JMSListener? Or am I missing something?
Spring Beans with methods annotated with #JmsListener are injecting beans leaked from previous test executions when activated by a secondary thread. A practical workaround is to configure the test executor to use an isolated VM for each class to avoid this issue.
For Maven executions you can configure the maven-failsafe-plugin or maven-surefire-plugin by setting the reuseForks option. e.g:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<reuseForks>false</reuseForks>
</configuration>
</plugin>
You can also easily change the Fork mode to Class in JUnit IDE tools for multiple tests execution. Example for IntelliJ:
Using #DirtiesContext does not work, and unfortunately, I still couldn't find the root cause for this - My hunch is that it could be something related to using an in-memory instance of the ActiveMQ broker, which lives in the VM instance shared by the executions.
We had a similar issue when using the #JmsListener annotation in combination with #MockBean/#SpyBean. In our case using a separate destination for each test class solved this problem:
#JmsListener(destination = "${my.mq.topic.name}")
void consume(TextMessage message){
...
}
#SpringBootTest
#TestPropertySource(properties = "my.mq.topic.name=UniqueTopicName"})
class MyConsumerIT{
...
}
As far as I understand Spring has to create a separate JMS Consumer for each topic/queue. This configuration forces Spring to create a separate JMS Consumer for this class and Inject it correctly into the TestContext.
In comparison without this configuration, Spring reuses the once created Consumer for all test classes.

Cannot Inject Service in HandlerInterceptorAdapter, Getting NullPointerException

I have a service-client project which is in normal spring application , not spring boot .its used for mainly logging related things.which contains Interceptor , loggingservice impl class and some model classes for logging. I have added this module as a dependency to main application in pom.xml.and i was able to inject and use the loggingService beans within the service layers of the main application.
Am getting NullPointerException while auto-wiring loggingService within the interceptor .The bean is not available within the interceptor.but like i said it can be injected and used within the main application.
Also am not able to read properties using #Value within the interceptor.
This is my Interceptor class .
#Component
public class LoggingInterceptor extends HandlerInterceptorAdapter {
#Autowired
LoggingService loggingService;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
loggingService.info("Am in prehandle");
return true;
}
}
This is my configuration class where i register the interceptor with the main application
#Component
public class LoggingConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getLoginInterceptor());
}
#Bean
public LoggingInterceptor getLoginInterceptor() {
return new LoggingInterceptor();
}
}
My question is almost similar to this post Cannot Autowire Service in HandlerInterceptorAdapter , but its different like am referring the interceptor from another module , and like they suggested i tried to create the bean from the application.
But the issues am facing right now is
getting NullPointerException while injecting loggingService within interceptor, but its working in main application
#Value annotation also return null, not able to read from properties
You have 2 possible solutions.
Mark your LoggingConfig as #Configuration instead of #Copmponent
Inject the LoggingInterceptor instead of referencing the #Bean method
Option 1: LoggingConfig as #Configuration
Your LoggingConfig is marked as an #Component whereas it should be marked as an #Configuration. The difference is that whilst it is allowed to have an #Bean method on an #Component it operates in a so-called lite mode. Meaning you cannot use method references to get the instance of a bean (this is due to no special proxy being created). This will lead to just a new instance of the LoggingInterceptor being created but it isn't a bean.
So in short what you are doing is equivalent to registry.addInterceptor(new LoggingInterceptor()); which just creates an instance without Spring knowing about it.
When marking the LoggingConfig as an #Configuration a special proxy will be created which will make the LoggingInterceptor a proper singleton bean, due to the method call being intercepted. This will register the bean in Spring and you will be able call the method.
NOTE: You actually endup with 2 instances of the LoggingInterceptor one due to the #Component on it the other through the #Bean. Remove the #Component.
Option 2: Inject the LoggingInterceptor.
As your LoggingInterceptor is marked as an #Component Spring will already create an instance (you actually have 2 instances of it created in your current setup). This instance you can inject into your LoggingConfig.
#Component
public class LoggingConfig implements WebMvcConfigurer {
private LoggingInterceptor loggingInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor);
}
}
With this you can remove the #Bean method as you will get the proper one injected into your LoggingConfig class. The class can also remain an #Component in this case. Although I would recommend using #Configuration as to also properly stereotype it.
NOTE: If you are on a recent Spring version you can use #Configuration(proxyBeanMethods=false). This will make a lite-configuration (just like an #Component) but it is still marked properly as a configuration class.

Is it possible to use Spring #Cache* on Feign client?

I have a Feign client that requests a token from a microservice.
Since I am making multiple calls, I would like to cache the responses received from the Feign client and use Spring Guava caching, but it doesn't seem to work. All I have is the configuration class and the annotation on the Feign client.
I read somewhere on stack overflow that Feign might not support #Cacheable annotation. Is this true?
Finally, I managed to solve my issue.
What I did in the end is :
-> Create new #Service annotated class
-> Inject interface with #FeignClient annotation
-> Put all #Cache related annotations ( using #Caching annotation) on the method that calls methods from the interface.
It works! :)
Annotating Feign clients with #Cacheable now works out of the box with Spring Cloud OpenFeign, as per the documentation, since version 3.1.0 (as part of Spring Cloud 2021.0.0).
You only need to make sure that:
you have configured #EnableCaching
you are using spring-cloud-starter-openfeign 3.1.0+ in your dependencies, so this normally means importing spring-cloud-dependencies version 2021.0.0+ (they switched to calver in 2020)
you are using Spring Boot 2.4.1+ (required for this version of Spring Cloud)
What Bianca supposed to do is to add a #Service annotated class to her project where she can use #cacheable annotation.
The traditional way to use FeignClient is to have only an interface annotated with #FeignClient, and then call these methods form other projects/classes. She has added a #Service annotated class, where she call her feignclients methods caching whatever she want.
Traditional:
FeignClient class:
#FeignClient(name="my_feign-client", url = "http://myurl.com/")
public interface MyFeignClient {
#GetMapping("/test")
public ResponseEntity<String> test() throws FeignException;
Class where to call feign client method:
public class TestClass {
#Autowired
private MyFeignClient myFeignClient ;
public String callTest() {
...
return myFeignClient.test();
}
Bianca's method:
Feign client class remains the same.
Service class with cache:
#Service
#CacheConfig(cacheNames={"test"})
public class TestService {
#Autowired
private MyFeignClient myFeignClient ;
#Cacheable
public String callCachedTest() {
...
return myFeignClient.test();
}
And last, the class to call the cached method, that call feignClient:
public class TestClass {
#Autowired
private TestService testService ;
public String callTest() {
...
return testService.callCachedTest();
}

spring boot request endpoints return 404 [duplicate]

The application uses JDK 8, Spring Boot & Spring Boot Jersey starter and is packaged as a WAR (although it is locally run via Spring Boot Maven plugin).
What I would like to do is to get the documentation I generate on the fly (at build time) as a welcome page.
I tried several approaches:
letting Jersey serving the static contents by configuring in application.properties the proper init parameter as described here
introduce a metadata-complete=false web.xml in order to list the generated HTML document as a welcome-file.
None of that worked out.
I would like to avoid having to enable Spring MVC or creating a Jersey resource just for serving a static file.
Any idea?
Here is the Jersey configuration class (I unsuccessfully tried to add a ServletProperties.FILTER_STATIC_CONTENT_REGEX there):
#ApplicationPath("/")
#ExposedApplication
#Component
public class ResourceConfiguration extends ResourceConfig {
public ResourceConfiguration() {
packages("xxx.api");
packages("xxx.config");
property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}
}
And here is Spring Boot application class (I tried adding an application.properties with spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=/.*html but it didn't work, I'm not exactly sure what the property key should be here):
#SpringBootApplication
#ComponentScan
#Import(DataConfiguration.class)
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Let me just first state, that the reason the static content won't be served is because of the default servlet mapping of the Jersey servlet, which is /*, and hogs up all the requests. So the default servlet that serves the static content can't be reached. Beside the below solution, the other solution is to simply change the servlet mapping. You can do that by either annotating your ResourceConfig subclass with #ApplicationPath("/another-mapping") or set the application.properties property spring.jersey.applicationPath.
In regards to your first approach, take a look at the Jersey ServletProperties. The property you are trying to configure is FILTER_STATIC_CONTENT_REGEX. It states:
The property is only applicable when Jersey servlet container is configured to run as a Filter, otherwise this property will be ignored
Spring Boot by default configures the Jersey servlet container as a Servlet (as mentioned here):
By default Jersey will be set up as a Servlet in a #Bean of type ServletRegistrationBean named jerseyServletRegistration. You can disable or override that bean by creating one of your own with the same name. You can also use a Filter instead of a Servlet by setting spring.jersey.type=filter (in which case the #Bean to replace or override is jerseyFilterRegistration).
So just set the property spring.jersey.type=filter in your application.properties, and it should work. I've tested this.
And FYI, whether configured as Servlet Filter or a Servlet, as far as Jersey is concerned, the functionality is the same.
As an aside, rather then using the FILTER_STATIC_CONTENT_REGEX, where you need to set up some complex regex to handle all static files, you can use the FILTER_FORWARD_ON_404. This is actually what I used to test. I just set it up in my ResourceConfig
#Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("...");
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
For anyone who still can't get this to work, I followed the answer provided by #peeskillet, and had to make an additional change.
Previously I had created the following method in Application.java.
#Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/*");
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfig.class.getName());
return registration;
}
The problem is that this registered the servlet for the /* path, and then setup the Jersey ResourceConfig configuration file.
Once I removed the above method, and placed the #Configuration annotation on my ResourceConfig class, I noticed the static resource could be retrieved via Spring Boot.
For completeness, this is a snippet of my ResourceConfig now.
#Configuration
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
// Application specific settings
property(ServletProperties.FILTER_FORWARD_ON_404, true);
}
}
This blog post was helpful in determining the difference approach for the ResourceConfig.
Below setup worked for me
Set
spring .jersey.type: filter
set FILTER_FORWARD_ON_404
#Configuration
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig () {
try {
register(XXX.class);
property(ServletProperties.FILTER_FORWARD_ON_404, true);
} catch (Exception e) {
LOGGER.error("Exception: ", e);
}
}
}
Note: Use #Configuration instead of #component

Spring Framework does not call BeanPostProcessor unless a #autowired dependency exists

I am building a TCP Socket Server which uses Spring Framework (3.2.3.RELEASE) to tie things together.
Everything is configured using Java Config with Annotations. In the Java Config I have a #ComponentScan annotation to scan for components in my classpath.
When messages are received by the TCP Socket Server, I occasionally want to dispatch some events. For this I use Google Guice EventBus. I don't want the TCP Socket Server to know about the event receivers and vice versa, to keep things loosely coupled.
I have registered a EventBusRegisterBeanPostProcessor from the code supplied by Patrick Meade # http://pmeade.blogspot.no/2013/02/using-guava-eventbus-with-spring-part-2.html
This will scan any bean and register it to the EventBus.
To listen for events (subscribe), I just need to create a POJO which looks like the following:
#Component
public class PingMessageReceivedHandler {
static Logger logger = Logger.getLogger(PingMessageReceivedHandler.class);
#Subscribe
public void handleMessageReceivedEvent(MessageReceivedEvent messageReceivedEvent) {
logger.info(messageReceivedEvent.getMessage());
messageReceivedEvent.getChannel().writeAndFlush(new BaseMessage(false, "Pong", null, ""));
}
}
Now, here is the issue: Unless I, somewhere in a different service or component or whatever, place a dependency on PingMessageReceivedHandler (using for instance #Autowired), then the EventBusRegisterBeanPostProcessor will not be aware of the PingMessageReceivedHandler and as such not dispatch messages to it.
I know that Spring is aware of the existence of the PingMessageReceivedHandler as the logs state so:
2013-11-04 17:35:05,391 INFO [main] (DefaultListableBeanFactory.java:596) - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#9695ed7: defining beans [[...],pingMessageReceivedHandler,[...],eventBusRegisterBeanPostProcessor]; root of factory hierarchy
Any ideas of what is causing this, and even more importantly, how I fix it?
Edit:
Context configuration:
#ComponentScan(value = "com.company.product")
#Configuration
public class DeviceManagerServerConfig {
#Bean
public static EventBusRegisterBeanPostProcessor eventBusRegisterBeanPostProcessor() {
return new EventBusRegisterBeanPostProcessor();
}
#Bean
public EventBus eventBus() {
return new EventBus("standard");
}
}
It is initialized using:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(DeviceManagerServerConfig.class);
context.refresh();
The issue was that I attempted to initialize a bean which has a blocking operation (the TCP Socket Server).
This stopped Spring from further bean initialization, and caused the issue in the Original post.
I.e:
#Component
#Profile("default")
public class SocketServerImpl implements SocketServer {
/**
* Instantiates a new socket server.
*/
public SocketServerImpl() {
/* Standard stuff */
}
#PostConstruct
#Override
public void start() {
/* This would block any further progressing */
}
}
My fix was pretty simple, I removed #PostConstruct from the code above, and in my public static void main, after I called context.refresh(), I called context.getBean(SocketServer.class).start().
Try adding
#Lazy(false)
to your handler bean.
I think it is declared lazy by default somehow and is therefore not loaded on startup without a dependency.

Categories

Resources