CDI context in Kafka de-/serializer in Quarkus app - apache-kafka

I have a Quarkus project with Smallrye reactive messaging based on Kafka. Since I want to work with a "complex pojo" I need a custom de-/serializer.
I'd like to make those two classes CDI beans so I can inject and use my custom logger, which is a CDI bean. Is there a way to achieve this?
Right now my injected logger object is simply null:
import org.apache.kafka.common.serialization.Serializer;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
#ApplicationScoped
public class MySerializer implements Serializer<MyDto>
{
#Inject MyLogger logger;
#Override public byte[] serialize(String topicName, MyDto myDto)
{
// this causes a java.lang.NullPointerException
logger.info("serializing");
...
}
}

As far as I know, you can only register a class name with kafka, and it will create that class internally, ie. without using CDI.
Possible workaround: make the registered object a thin wrapper around the CDI-bean, and delegate the work to the bean:
public class MySerializer implements Serializer<MyDto> {
private MySerializerCdi delegate;
public MySerializer() {
delegate = CDI.current().select(MySerializerCdi.class).get();
}
#Override public byte[] serialize(String topicName, MyDto myDto) {
return delegate.serialize(topicName, myDto);
}
...
}
... and rename your original CDI class accordingly.

Related

Datasource is required though it has been specified and worked earlier

I have been working on a spring boot application where the application works fine. When i tried to introduce the following code
#Component
#Slf4j
#Getter
#Setter
public class EmployeeDbCreateWriter extends JdbcBatchItemWriter<Employee> {
#Autowired
DataSource datasource;
#Override
public void write(List<? extends Employee> employeeList) throws Exception {
.....
....
}
}
i end up with the following error
Caused by: java.lang.IllegalArgumentException: A DataSource or a NamedParameterJdbcTemplate is
required.
at org.springframework.util.Assert.notNull(Assert.java:201)
at
org.springframework.batch.item.database.JdbcBatchItemWriter.afterPropertiesSet
(JdbcBatchItemWriter.java:143)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1845)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
initializeBean(AbstractAutowireCapableBeanFactory.java:1782)
... 57 more
Configure and component scan is given is working fine before introducing the above class
#Configuration
#EnableWebMvc
#EnableJpaRepositories(basePackages =
{"com.emp.repositories","com.emp2.repositories"})
#EnableAsync(proxyTargetClass = true)
#ComponentScan(basePackages = {"com.emp","com.emp2"})
#EntityScan(basePackages = {"com.emp.models","com.emp2.models"})
#EnableCaching
#EnableBatchProcessing
#Slf4j
public class EmpConfig implements WebMvcConfigurer{
Where am I going wrong?
Per what I see in the JdbcBatchItemWriter it requires Datasource or JdbcTemplate to be provided and it is not aware of the autowired datasource in the child class.
You could try something like this and provide datasource manually in the PostConstruct method
#Component
#Slf4j
#Getter
#Setter
public class EmployeeDbCreateWriter extends JdbcBatchItemWriter<Employee> {
#Autowired
DataSource dataSource;
#PostConstruct
public void initialize() {
super.setDataSource(dataSource);
}
....
}
However per #M.Deinum comments is it doubtful that there is a good reasons to extend JdbcBatchItemWriter and that this class was meant to be extended.

how it works #BindsInstance dagger 2

I have recently updated dagger 2.8 to 2.9 dagger. and documentation of the last release have been added as follows:
-Added #BindsInstance for component builders to easily bind instances that are constructed outside of the graph.
-Producers: Added ProducerMonitor.ready (), which is called when all of a producer's inputs are available.
-Removed #Provides(type =...) usage. Use the annotations in dagger.multibindings instead. #Produces.type was also removed.
-All binding methods are now validated, even if they are unused in a particular #Component
-#Component.dependencies can no longer include #Modules.
I want to know how these new features:
Thank you!!
Note: I am new to dagger 2, but you want to be able to make maximum use of this library.
#bindsInstance is used for removing constructor from module and chaining modules where you get component.
Without #BindsInstance
#Module
public class AppModule {
private final Application application;
public AppModule(Application application) {
this.application = application;
}
#Provides
#Singleton
Application provideApplication() {
return application;
}
#Provides
#Singleton
public SharedPreferences providePreferences() {
return application.getSharedPreferences("store",
Context.MODE_PRIVATE);
}
}
These modules(ToastMakerModule, and SensorControllerModule) are for learning purposes they get context and instantiate , may not be practical for real examples
public class ToastMaker {
private Application application;
public ToastMaker(Application application) {
this.application = application;
}
public void showToast(String message) {
Toast.makeText(application, message, Toast.LENGTH_SHORT).show();
}
}
#Module
public class ToastMakerModule {
#Singleton
#Provides
ToastMaker provideToastMaker(Application application) {
return new ToastMaker(application);
}
}
#Singleton
#Component(modules = {AppModule.class, ToastMakerModule.class, SensorControllerModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
// DaggerAppComponent.build() returns this Builder interface
#Component.Builder
interface Builder {
AppComponent build();
Builder appModule(AppModule appModule);
Builder sensorControllerModule(SensorControllerModule sensorControllerModule);
Builder toastMakerModule(ToastMakerModule toastMakerModule);
}
}
Build component like this
appComponent = DaggerAppComponent
.builder()
.appModule(new AppModule(this))
.sensorControllerModule(new SensorControllerModule())
.toastMakerModule(new ToastMakerModule())
.build();
With #BindsInstance
#Module
public class AppModule {
#Provides
#Singleton
public SharedPreferences providePreferences(Application application) {
return application.getSharedPreferences("data",
Context.MODE_PRIVATE);
}
}
Component
#Singleton
#Component(modules = {AppModule.class, ToastMakerModule.class, SensorControllerModule.class})
public interface AppComponent {
void inject(MainActivity mainActivity);
#Component.Builder
interface Builder {
AppComponent build();
// #BindsInstance replaces Builder appModule(AppModule appModule)
// And removes Constructor with Application AppModule(Application)
#BindsInstance
Builder application(Application application);
}
}
and build component like this
appComponent = DaggerAppComponent
.builder()
.application(this)
.build();
#BindsInstance lets the component host the dependency directly and hence the lifetime is the lifetime of the component. This can be used to avoid #Singleton scope. Does it even matter? Avoiding singleton scope helps DaggerAppComponent access the provider without a costly DoubleCheck. So yes, it is possible to still use the module and not use any scopes. However, using module still means that DaggerAppComponent is going to use a Provider factory to inject the dependency. Using #BindsInstance, the provider is not needed at all unless the dependency is injected lazily via Lazy<> or Provider<>.
Any dependencies (like String constants etc) that is known as AppComponent is being created are good candidates for #BindsInstance. Note this is based on Dagger 2.19.

How to safely have a #RestController class implementing an interface annotated with #FeignClient

Given the following code sample
-- client library code
#FeignClient("string-service")
public interface StringClient {
#RequestMapping(method = RequestMethod.GET, value = "/microservicestring")
public String home();
}
#Service
public class StringHystrixClient {
private final SpringClient springClient;
//....
}
-- service library code
#RestController
public class StringController implements StringClient {
public String home(){
return "World";
}
}
#SpringBootApplication
#EnableHystrix
#EnableEurekaClient
#EnableFeignClients
public class StringApplication { ....}
If the service library references the client library, when the application gets started, through component scanning we will get to a state where in filling the dependencies from StringHystrixClient, the spring container will not know what to do because there are two beans implementing StringClient.
One solution to avoid this would be to not implement the StringClient in the StringController, but the code duplication from the interface and the rest controller would be error prone. Can somebody point out a more elegant solution to this problem?

#Inject a Morphia DAO with CDI?

I'm writing a Java EE 6 application that makes use of Morphia to persist objects to MongoDB. I'd like to be able to #Inject my DAO classes where necessary, so I created a Factory class that instantiates the DAO appropriately. It looks something like this:
public class MyDAOFactory {
#Inject
private Datastore mongoDatastore = null;
/**
* Creates the DAO
*/
#Produces
#ApplicationScoped
public MyDAO createDAO() {
MyDAO dao = new MyDAO(
this.mongoDatastore);
return dao;
}
}
The code compiles fine, but when I run my application on JBoss EAP 6.1 it complains because MyDAO does not have a no-arg constructor. I would add one, but the Morphia BasicDAO class does not have one either, so I don't know that it would work that way.
Is there a way to #Inject a DAO instance into my EJB, Servlet, etc.? Or do I need to manually instantiate it every time?
It seems that CDI needs the no-arg constructor for MyDAO for some reason. Maybe because of how you use this bean (see specs ch.5.4 "Client Proxies" for possible reasons).
You cannot create a default constructor, because the base class does not have one and, from what I see from the code the super constructors make immediate use of their args. Therefore passing null to super() from a no-arg constructor will throw errors.
My suggestion is to create an interface (optionally extending org.mongodb.morphia.dao.DAO), e.g. MyDAOInterface that has all public business methods of MyDAO. Then modify MyDAO to implement this interface and change your producer to return MyDAOInterface:
public interface MyDAOInterface extends DAO {...}
public class MyDAO implements MyDAOInterface {
// same implementation
}
public class MyDAOFactory {
#Inject
private Datastore mongoDatastore = null;
/**
* Creates the DAO
*/
#Produces
#ApplicationScoped
public MyDAOInterface createDAO() {
MyDAO dao = new MyDAO(this.mongoDatastore);
return dao;
}
}
By the way, programming to interfaces has the extra benefit of making your code more testable, so it is worth the minor hassle.

Can I do setter injection using #Inject annotation

In my GWTP application I need to Inject HttpServletRequest, HttpSession as instance variable of ActionHandler.
My ActionHandler is initialized through Spring.
I can't get current Request object through Spring as it instantiates just POJO.
I am thinking about mixing GIN and Spring.
Would I be able inject HttpServletRequest using GIN in my ActionHandler which is instantiated through Spring?????
Is it possible to do following way??
#Configuration
#Import(DefaultModule.class)
public class ServerModule extends HandlerModule
{
#Bean
public UserVerficationActionHandler getUserVerificationActionActionHandler()
{
return new UserVerficationActionHandler();
}
}
public class UserVerficationActionHandler implements ActionHandler<UserVerficationAction, UserVerficationActionResult>
{
#Autowired
private UserService userService;
private Provider<HttpServletRequest> requestProvider;
#Inject
public UserVerficationActionHandler()
{
}
public UserVerficationActionResult execute(UserVerficationAction action, ExecutionContext context) throws ActionException
{
....
}
#Inject
public Provider<HttpServletRequest> setRequestProvider()
{
return requestProvider;
}
}
-------ActionHandler Ends--------
Can somebody let me know Is it possible to do SetterInjection this way?
Second thing, if above is possible then will I be getting current request object using this method?
Thanks in advance.
Bhavesh.