Injection of bean inside ClientHeadersFactory doesn't work - quarkus

I'm building a Quarkus app which handles http requests with resteasy and calls another api with restclient and I need to propagate a header and add another one on the fly so I added a class that implements ClientHeadersFactory.
Here's the code:
#ApplicationScoped
public abstract class MicroServicesHeaderHandler implements ClientHeadersFactory {
#Inject
MicroServicesConfig config;
#Override
public MultivaluedMap<String, String> update(MultivaluedMap<String, String> incomingHeaders,
MultivaluedMap<String, String> clientOutgoingHeaders) {
// Will be merged with outgoing headers
return new MultivaluedHashMap<>() {{
put("Authorization", Collections.singletonList("Bearer " + config.getServices().get(getServiceName()).getAccessToken()));
put("passport", Collections.singletonList(incomingHeaders.getFirst("passport")));
}};
}
protected abstract String getServiceName();
My issue is that the injection of the config doesn't work. I tried both with #Inject and #Context, as mentioned in the javadoc of ClientHeadersFactory. I also tried to make the class non abstract but it doesn't change anything.
MicroServicesConfig is a #Startup bean because it needs to be initialized before Quarkus.run() is called, otherwise the hot reload doesn't work anymore, since it's required to handle requests.
Here's the code FYI:
#Getter
#Startup
#ApplicationScoped
public final class MicroServicesConfig {
private final Map<String, MicroService> services;
MicroServicesConfig(AKV akv, ABS abs) {
// some code to retrieve an encrypted file from a secure storage, decrypt it and initialize the map out of it
}
It appears to be an issue with ClientHeadersFactory because if I inject my bean in my main class (#QuarkusMain), it works. I'm then able to assign the map to a public static map that I can then access from my HeaderHandler with Application.myPublicStaticMap but that's ugly so I would really prefer to avoid that.
I've searched online and saw several people having the same issue but according to this blogpost, or this one, it should work as of Quarkus 1.3 and MicroProfile 3.3 (RestClient 1.4) and I'm using Quarkus 1.5.2.
Even the example in the second link doesn't work for me with the injection of UriInfo so the issue doesn't come from the bean I'm trying to inject.
I've been struggling with this for weeks and I'd really like to get rid of my workaround now.
I'm probably just missing something but it's driving me crazy.
Thanks in advance for your help.

This issue has finally been solved in Quarkus 1.8.

Related

Spring - generic superclass not instantiated properly?

ATM I am in the middle of refactoring our Selenium E2E Test Framework to use Spring.
My class/bean:
package info.fingo.selenium.utils.driver;
#Component
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class ProxyDecorator extends WebDriverDecorator<WebDriver> {
#Autowired
public ProxyDecorator(TestUtils testUtils, DriverManager driverManager) {
super(WebDriver.class);
this.testUtils = testUtils;
this.driverManager = driverManager;
Superclass:
package org.openqa.selenium.support.decorators;
public class WebDriverDecorator<T extends WebDriver> {
private final Class<T> targetWebDriverClass;
private Decorated<T> decorated;
#SuppressWarnings("unchecked")
public WebDriverDecorator() {
this((Class<T>) WebDriver.class);
}
public WebDriverDecorator(Class<T> targetClass) {
this.targetWebDriverClass = targetClass;
}
public final T decorate(T original) {
Require.nonNull("WebDriver", original);
decorated = createDecorated(original);
return createProxy(decorated, targetWebDriverClass);
}
Issue occures on calling this line:
createProxy(decorated, targetWebDriverClass)
Where targetWebDriverClass for unknown reason is null and NullPointerException is later thrown.
This should not EVER happen as targetWebDriverClass is ALWAYS set through constructor - either provided by client (calling super(class)) or defaulted to WebDriver.class in default WebDriverDecorator constructor. Worked fine without Spring, and unfortunately I don't understand Spring enough to get any information through debugging.
My Spring dependencies:
ext.springVersion = '2.7.1'
dependencies {
//SPRING BOOT
api "org.springframework.boot:spring-boot-starter:$springVersion",
"org.springframework.boot:spring-boot-starter-aop:$springVersion",
"org.springframework.boot:spring-boot-starter-test:$springVersion",
decorate method in superclass WebDriverDecorator in marked as final which makes it ineligible for Spring CGLIB proxying as it cannot proxy final methods (& classes) - Sorry, I don't know exact reason why this caused my issue.
This is not my own class, it is taken from inside of dependency so I cannot change this.
This means that this class cannot be managed by Spring. In order for this to somehow work I get rid of inheritance (extends keyword) and replace it with composition. Got to do some reflection magic (for one of its protected method) but this seems to do the trick.

how to register custom converters for spring messaging, web sockets #DestinationVariable or jms #Header

I'm integrating spring web sockets capability into an existing spring mvc application, everything works as expected, except for enabling custom Spring Conversion on my inbound messages via #DestinationVariable.
Now I already have custom converters fully working for the http side, ex #RequestParam or #PathVariable but the same conversion on a websocket controller method throws a ConverterNotFoundException
Ex. I have a custom converter that converts String into Users
public class StringToUserConverter implements Converter<String,User>{
#Autowired UserDAO userDAO;
#Override
public User convert(String id) {
return userDAO.getUser(Integer.parseInt(id));
}
}
And this works exactly as expected in my http controllers, where I can pass in an id, and its automatically converted to the domain class
public String myControllerMethod(#RequestParam User user)
However the same does not work for my websocket controller for a parameter annotated with #DestinationVariable
#MessageMapping("/users/{user}")
#SendTo("/users/greetings")
public String send(#DestinationVariable User user) {
return "hello"
}
I stepped through the code and I can see that the DestinationVariableMethodArgumentResolver has the default conversion service which doesnt include my custom coverters
So how do I register custom converters, or a custom ConversionService so that it works for web sockets like it already does for http controllers
So now I'm running into the same issue with #Header annotation for JmsListener methods.
Same idea, #Header User user, throws the ConverterNotFound exception.
#JmsListener(destination = "testTopic")
public void testJmsListener(Message m, #Header User user)..
Here I was trying to pass the user id on the message header, and have spring convert it, but to no avail, only basic default conversions are supported, like strings or numbers.
I have stepped through quite a bit of initialization code in Spring here, and I can see that a new DefaultConversionService gets instantiated in many places, without any consideration for external configuration.
It looks like these modules are not nearly as mature as Spring MVC or the developers took a shortcut. But based on my inspection there is no way to easily configure custom converters.
Ok and here is the very hacky, not recommended, approach that did work. Its pretty convoluted and brittle, Im not going to use it, but just for illustration purposes here is what it took to register a custom converter for #Header jms mapping.
Here Im passing in a user_email on the jms message header, and wanted spring to automatically convert the id/email into the actual domain object User. I already had a working converter that does this well in mvc/http mode.
public class StringToUserConverter implements Converter<String,User>{
#Autowired
UserDAO userDAO;
public User convert(String email) {
return userDAO.getByEmail(email);
}
}
The above part is pretty standard and straight forward. Here comes the idiotically convoluted part. I stepped through the spring jms listener initialization code and found lowest spot where I could cut-in with my custom converter for jms #Header.
I created a service, that will #Autowire one of springs Jms beans, and then sets a custom conversion service on it using #PostConstruct. Even here some of the properties were private, so I had to use reflection to read them
#Service
public class JmsCustomeConverterSetter {
#Autowired
StringToUserConverter stringToUserConverter;
#Autowired
JmsListenerAnnotationBeanPostProcessor jmsPostProcessor;
#PostConstruct
public void attachCustomConverters() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
//create custom converter service that includes my custom converter
GenericConversionService converterService = new GenericConversionService();
converterService.addConverter(stringToUserConverter); //add custom converter, could add multiple here
DefaultConversionService.addDefaultConverters(converterService); //attach some default converters
//reflection to read the private field so i can use it later
Field field = jmsPostProcessor.getClass().getDeclaredField("beanFactory"); //NoSuchFieldException
field.setAccessible(true);
BeanFactory beanFactory = (BeanFactory) field.get(jmsPostProcessor); //IllegalAccessException
DefaultMessageHandlerMethodFactory f = new DefaultMessageHandlerMethodFactory();
f.setConversionService(converterService);
f.setBeanFactory(beanFactory); //set bean factory read using reflection
f.afterPropertiesSet();
jmsPostProcessor.setMessageHandlerMethodFactory(f);
}
}
Creating the DefaultMessageHandlerMethodFactory was based on code I saw in org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory.
I would definitely not recommend using this in production. It is fairly brittle and unnecessarily complex.
Spring...sometimes it's a breath of fresh air... and sometimes it's convoluted-clap-trap

Configuring Spring MockMvc to use custom argument resolver before built-in ones

I have a straightforward test case. I have a controller which has a parameter of a type Spring doesn't support by default, so I wrote a custom resolver.
I create the mock mvc instance I'm using like so:
mvc = MockMvcBuilders.standaloneSetup(controller).setCustomArgumentResolvers(new GoogleOAuthUserResolver()).build();
However, Spring is also registering almost 30 other argument resolvers, one of which is general enough that it is getting used to resolve the argument before mine. How can I set or sort the resolvers so that mine is invoked first?
This worked for me without reflection:
#RequiredArgsConstructor
#Configuration
public class CustomerNumberArgumentResolverRegistration {
private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
#PostConstruct
public void prioritizeCustomArgumentResolver () {
final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(Objects.requireNonNull(requestMappingHandlerAdapter.getArgumentResolvers()));
argumentResolvers.add(0, new CustomerNumberArgumentResolver());
requestMappingHandlerAdapter.setArgumentResolvers(argumentResolvers);
}
}
The issue was that the People class the Google OAuth library I am using extends Map and the mock servlet API provides no way to manipulate the order in which the handlers are registered.
I ended up using reflection to reach into the mocks guts and remove the offending handler.

How to get all self injected Beans of a special type?

I would like to build a Spring application, where new components can be added easily and without much configuration. For example: You have different kinds of documents. These documents should be able to get exported into different fileformats.
To make this functionality easy to maintain, it should (basically) work the following way:
Someone programs the file format exporter
He/ She writes a component, which checks if the file format exporter is licensed (based on Spring Conditions). If the exporter is licensed a specialized Bean is injected in the application context.
The "whole rest" works dynamically based on the injected beans. Nothing needs to be touched in order to display it on the GUI, etc.
I pictured it the following way:
#Component
public class ExcelExporter implements Condition {
#PostConstruct
public void init() {
excelExporter();
}
#Bean
public Exporter excelExporter(){
Exporter exporter= new ExcelExporter();
return exporter;
}
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}
}
In order to work with those exporters (display them, etc.) I need to get all of them. I tried this:
Map<String, Exporter> exporter =BeanFactoryUtils.beansOfTypeIncludingAncestors(appContext, Exporter.class, true, true);
Unfortunate this does not work (0 beans returned). I am fairly new to this, would anyone mind to tell me how this is properly done in Spring? Maybe there is a better solution for my problem than my approach?
You can get all instances of a given type of bean in a Map effortlessly, since it's a built in Spring feature.
Simply autowire your map, and all those beans will be injected, using as a key the ID of the bean.
#Autowired
Map<String,Exporter> exportersMap;
If you need something more sophisticated, such as a specific Map implementation or a custom key. Consider defining your custom ExporterMap, as follows
#Component
class ExporterMap implements Map{
#Autowired
private Set<Exporter> availableExporters;
//your stuff here, including init if required with #PostConstruct
}

GWT and Spring: EditActivity trying to invoke persist method on a request

I am making some tries with GWT and Spring Roo with a simple project. The code generation seems to work fine, except for the *EditActivity classes; in fact, in such classes I get some code invoking the persist method, i.e. something like:
#Override
protected RequestContext createSaveRequest(MyEntityProxy proxy) {
request.persist().using(proxy);
return request;
}
However, that request (of type MyEntityRequest) doesn't seem to have any persist method. It extends MyEntityRequest_Roo_Gwt, which extends extends RequestContext.
#RooGwtRequest("mybasepackage.shared.domain.MyEntity")
#ServiceName(value = "mybasepackage.service.MyEntityService", locator = "mybasepackage.server.locator.GwtServiceLocator")
public interface MyEntityRequest_Roo_Gwt extends RequestContext
Where should I look for the problem? How can I fix it?
Making the "push in" to remove the aspects resolved the issue.

Resources