Quarkus Extension - write fields annotated with a custom annotation - java

At our company we are trying to migrate some of our applications from J2EE to Quarkus.
For the J2EE apps we have a proprietary framework that performs dependency injection based on custom annotations applied on a field. We have a CDI extension in which we override the beans InjectionTarget and at bean's instantiation time we set, through reflection, the field value based on the annotation present, like this:
#ApplicationScoped
public class MyBean {
#CustomAnnotation
private String myField; // injected by our framework
We would like to migrate our applications keeping this behaviour. Is there any way to do this with Quarkus? We have seen that with Quarkus extensions we can transform the qualifiers of an injection point, like this:
#BuildStep
InjectionPointTransformerBuildItem transformPropertyAnnotation(final BeanArchiveIndexBuildItem beanArchiveIndex) {
return new InjectionPointTransformerBuildItem(new InjectionPointsTransformer() {
public void transform(TransformationContext context) {
// do my transformations
}
}
}
But we didn't find a way to "enhance" how a field is being initialized, even with Quarkus recorders.

Related

Spring Custom Converter - To Bean or Not to Bean

I am implementing Custom Converter in Spring so my beans can convert from java.util.Date to java.time.LocalDateTime. I have implemented Converter already (by implementing Spring Converter interface)
Here is bean definition in #Configuration class
#Bean
ConversionService conversionService(){
DefaultConversionService service = new DefaultConversionService();
service.addConverter(new DateToLocalDateTimeConverter());
return service;
}
My question is : shall I pass my custom converter as Java Object or Spring Bean to service.addConverter?
In general what are the guidelines (criterias) whether to bean or not to bean in such scenarios?
Making an object a Spring Bean makes sense as you want that this object may benefit from Spring features (injections, transaction, aop, etc...).
In your case, it seems not required.
As conversionService is a Spring bean singleton that will be instantiated once, creating during its instantiation a plain java instance of DateToLocalDateTimeConverter seems fine : new DateToLocalDateTimeConverter().
Now, if later you want to inject the DateToLocalDateTimeConverter instance in other Spring beans, it would make sense to transform it to a Spring Bean.
For information Spring provides already this utility task in the Jsr310Converters class (included in the spring-data-commons dependency) :
import static java.time.LocalDateTime.*;
public abstract class Jsr310Converters {
...
public static enum DateToLocalDateTimeConverter implements Converter<Date, LocalDateTime> {
INSTANCE;
#Override
public LocalDateTime convert(Date source) {
return source == null ? null : ofInstant(source.toInstant(), ZoneId.systemDefault());
}
}
...
}
You could directly use it.
If you intend to inject this as a dependency of some kind into your application, and/or you intend to reuse it in multiple places, then it makes sense to register it as a bean. If you're not, then newing an instance up is acceptable.
Dependency injection and inversion of control are just that - how you inject dependencies into your app, and an acknowledgment that you no longer control how that's instantiated. Should you desire either of these, beans are suitable; if you don't, then new it up.
In you simple case, it does not seem to be necessary to add DateToLocalDateTimeConverter as a spring bean.
Reasons to add DateToLocalDateTimeConverter as a spring bean:
If it would make the implementation of conversionService() more readable (not the case in the question example)
You need the DateToLocalDateTimeConverter in other beans
The implementation of DateToLocalDateTimeConverter itself would need to have Spring beans injected, i.e. using #Autowired

How do I add a bean to Spring context in my library without breaking consumers who have their own instance of that bean?

I have a library which produces beans into a Spring context for use by clients. The beans I produce are configured by Spring. I need to add a new bean to my context in order to satisfy a dependency of a new bean I'm publishing. However, I believe some of my clients already have an instance of this bean and are autowiring it by type. So I have something like this:
// Code in my Library
#Component
public class PublicUtilityClass {
// This is all new code in my library
private NewDependency newDependency;
public void newCapability() {
newDependency.doNewThing();
}
#AutoWired
public void setNewDependency(NewDependency newDependency) {
this.newDependency = newDependency;
}
// Other library code omitted.
}
How can I use Spring to instantiate NewDependency and inject it into PublicUtilityClass without impacting customers who already have a NewDependency bean in their context?
You should look at #Qualifier annotation. Qualifier allows you to have multiple instance of your bean

Constructor injection preventing custom resource processing

In my (non-trivial) Spring Boot 1.5.4 application with Spring Data REST and HATEOAS leveraging Spring websockets, I have some custom resource processors, some custom controllers, and some custom repositories. Sadly, when I use constructor injection in one particular Spring #Service class for a MessageSendingOperations dependency, my custom resource processors no longer get invoked. Reverting the constructor injection restores the execution of my custom resource processors, i.e. reverting from:
private final MessageSendingOperations<String> messageTemplate;
#Autowired
public ChannelHandler(MessageSendingOperations<String> messageTemplate) {
this.messageTemplate = messageTemplate;
}
to:
#Autowired
private MessageSendingOperations<String> messageTemplate;
"re-enables" my custom resource processors which results in a null messageTemplate. So, there's a problem somewhere...but where??? Any ideas how to track this down?
Have you tried making messageTemplate a lazily injected proxy? For example:
public ChannelHandler(#Lazy MessageSendingOperations<String> messageTemplate) {
this.messageTemplate = requireNonNull(messageTemplate, "messageTemplate");
}
From the Javadoc:
In addition to its role for component initialization, this annotation
may also be placed on injection points marked with Autowired or
Inject: In that context, it leads to the creation of a lazy-resolution
proxy for all affected dependencies, as an alternative to using
ObjectFactory or Provider.
This usually affects the initialization order of your beans, in this case allowing ChannelHandler to be initialized before MessageSendingOperations. Without #Lazy, MessageSendingOperations will be initialized first.
Also: as of Spring 4.3, #Autowired is no longer required for single argument constructors.
+1 for using constructor injection and final fields.

Guice: how to get more than one #Provides for a type?

I'm working on a project and trying to bring two different 'data service' modules together into a web app (currently, the app is a desktop Swing app).
Each module has its own Guice (private) module. Each Guice module contains:
#Provides
#Inject
protected JPQLQuery provideJPQLQuery(EntityManager entityManager) {
return new JPAQuery(entityManager);
}
This is used later in the constructor of the classes that look things up from the db:
#Inject
public SomeClassThatLooksObjectsUpFromDatabase(Provider<JPQLQuery> queryProvider) {
this.queryProvider = queryProvider;
}
The 'queryProvider' is then able to execute queries.
Now, this works fine when only one of the Guice modules is installed, but once both are installed, I (predictably) get this error:
Unable to create binding for com.mysema.query.jpa.JPQLQuery. It was already configured on one or more child injectors or private modules
bound at ServiceOneGuiceModule.provideJPQLQuery()
bound at ServiceTwoGuiceModule.provideJPQLQuery()
Now, I understand why this is broken -- I'm saying that there's two Providers for the type JPQLQuery and Guice doesn't know which one to use.
Is there any way I can get Guice to separate these Providers? I would like to do this because each module has its own properly-configured Hibernate entities, and each has its own unique datasource (multiple databases in this project).
Ideally, it would involve something like somehow naming these providers and injecting them by their name (e.g. I could separately inject "ServiceOneJPQLQueryProvider" and "ServiceTwoJPQLQueryProvider"), but I haven't found any way of achieving anything like this.
(I suppose an alternative is to somehow configure Hibernate so it has all the different datasources it needs and then I'd only need one Provider for my queries, possibly, but that seems like a lot more work than what I'm describing above)
Have a look binding annotations, they're used to solve just the problem you've got.
They're recommended over using #Named because they are type-safe and will produce compilation errors and not runtime errors if you misspell them.
In short:
ServiceOne.java:
#BindingAnnotation #Target({ FIELD, PARAMETER, METHOD }) #Retention(RUNTIME)
public #interface ServiceOne {}
ServiceTwo.java:
#BindingAnnotation #Target({ FIELD, PARAMETER, METHOD }) #Retention(RUNTIME)
public #interface ServiceTwo {}
ServiceOneModule.java:
#Provides
#ServiceOne
#Inject
protected JPQLQuery provideJPQLQuery(EntityManager entityManager) {
return new JPAQuery(entityManager);
}
SomeClass.java:
#Inject
public SomeClassThatLooksObjectsUpFromDatabase(#ServiceOne Provider<JPQLQuery> queryProvider) {
this.queryProvider = queryProvider;
}
First of all, you're incorrect about #Named because providers are ubiquitous in Guice - in fact, when you create a binding like bind(...).to(...) or any other non-provider binding you're implicitly creating a provider. And it is just a shortcut for injecting providers when you inject objects directly - providers injection and direct injection are interchangeable in Guice (when you are not using special scopes like RequestScoped, but this is more like implementation detail of this shortcut). For example, even if you created #Provides method for JPQLQuery, you still can inject it directly, without a provider:
#Inject
public SomeClass(JPQLQuery query) {
...
}
Which approach to use depends on your requirements (do you need several fresh instances of JPQLQuery inside SomeClass methods?) and scopes in use (e.g. when scope of SomeClass is wider than that of JPQLQuery - for example, when SomeClass is singleton, and JPQLQuery is request scoped - sometimes provider usage is mandatory).
You're not getting what you want with #Named yet because you create #Named binding with #Provides, but still inject Provider<...> without annotation. It won't work because binding key consists both of injected type and (possibly absent) binding annotation, so if you have only bindings with assigned annotation but inject provider or type without annotation, Guice will fail. You should put #Named annotation onto the constructor parameter as well:
#Inject
public SomeClass(#Named("ServiceOne") Provider<JPQLQuery> queryProvider) {
...
}
This way it should work.
BTW, I strongly recommend against #Named. You'd better create separate binding annotation for each of your distinct binding for the same class, as described here. With custom annotations you won't have a chance to make a mistake in binding name when you inject it.
Also there is more advanced machinery for manipulating similar bindings. It is called private modules. However, you should rarely need it. It could be needed, for example, to create trees of objects which differ only in few bindings. This does not seem to be your case - binding annotations are your way to go.

How do I configure JSR-330 #Provider and #Inject #Named("foo") String **programmatically** in Spring?

We have decided to use Dependency Injection with JSR-330 annotations for our future modularization efforts, and have been very pleased with the first deliverable based on Guice 2 SVN.
Now we need to ensure and document through unit tests that the constructions we need, also work in Spring when configured programmatically (we want the same refactoring support as with Guice so no XML files). I have problems with #Provider and #Inject #Named("foo") String but I have made plain #Inject work with:
ApplicationContext ctx = new AnnotationConfigApplicationContext(LIBL_Object.class,
CORE_Provider.class);
this.object = ctx.getBean(LIBL_Object.class);
where LIBL_Object is the base class to be injected into, but the CORE_Provider does not register as I hoped within Spring.
The implementation of CORE_Provider is
package qa.jsr330.core;
import javax.inject.Provider;
public class CORE_Provider implements Provider<ProvidedInterface> {
#Override
public ProvidedInterface get() {
return new CORE_Provided();
}
}
and I want it injected into
package qa.jsr330.core;
import javax.inject.Inject;
public class LIBL_Object {
private ProvidedInterface provided;
public ProvidedInterface getProvided() {
return provided;
}
#Inject
public void setProvided(ProvidedInterface provided) {
this.provided = provided;
}
// Other stuff omitted.
}
Also we have found that we can pass configuration values very clearly using the #Named tag. This code looks like:
String hostname;
#Inject
public void setHostname(#Named("as400.hostname") String hostname) {
this.hostname = hostname;
}
where we can then register this string with Guice using
bindConstant().annotatedWith(Names.named("as400.hostname")).to(value);
So the two questions are:
How do I register the #Provider class with Spring 3 programatically?
How do I register a string constant with Spring 3 so that #Named selects it properly?
The short answer is: there is no such thing as programmatic configuration of Spring.
Despite the fact that both Spring and Guice support JSR-330 API and that Spring can be configured without XML now, their ideologies are still very different. Spring relies on static configuration, either in the form of XML files or annotated Java classes. Therefore straightforward attempt to adapt Guice-style configuration to Spring may produce difficulties.
Regarding the problem with Provider - Spring doesn't support javax.inject.Provider in the same way as toProvider() binding in Guice (by the way, this usage of Provider is not specified in JSR-330 docs). Therefore some Spring-specific annotations may be needed, for example
#Configuration
public class CORE_Provider implements Provider<ProvidedInterface> {
#Override #Bean
public ProvidedInterface get() {
return new CORE_Provided();
}
}
Binding value coming from the outside may be difficult due to static nature of Spring configuration. For example, in your case, it can be done like this:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(IBL_Object.class);
ctx.register(CORE_Provider.class);
ctx.registerBeanDefinition("as400.hostname",
BeanDefinitionBuilder.rootBeanDefinition(String.class)
.addConstructorArgValue(value).getBeanDefinition());
ctx.refresh();

Categories

Resources