Replace Spring Java config with XML for Apache Kafka - apache-kafka

Ho can be replaced a Java configuration like this
#Configuration
#EnableKafka
public class KafkaConfig {
}
With a full XML configuration?
Especially how can be methods (or classes) annotated with
#KafkaListener
public void poll(String msg){}
injected into Spring Kafka context as listeners?

#EnableKafka adds 2 beans to the application context:
#Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(
KafkaListenerConfigUtils.KAFKA_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)) {
registry.registerBeanDefinition(KafkaListenerConfigUtils.KAFKA_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME,
new RootBeanDefinition(KafkaListenerAnnotationBeanPostProcessor.class));
}
if (!registry.containsBeanDefinition(KafkaListenerConfigUtils.KAFKA_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)) {
registry.registerBeanDefinition(KafkaListenerConfigUtils.KAFKA_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME,
new RootBeanDefinition(KafkaListenerEndpointRegistry.class));
}
}
So just add those as <bean/> s with those names.
"org.springframework.kafka.config.internalKafkaListenerAnnotationProcessor"
"org.springframework.kafka.config.internalKafkaListenerEndpointRegistry"

Related

CDI context in Kafka de-/serializer in Quarkus app

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.

Spring Data Rest with Cache

I was learning Spring Data Rest but I didn't find how to use Cache in Spring Data Rest.
How can i use Cache with Spring Data Rest's curd/page .
Or should I use JPA+Cache and ignore Spring Data Rest?
If I misunderstanding anything please remind me.
best regard
You can try the following approach:
1) Override your repos methods findById and findAll, make them Cacheable:
public interface MyEntityRepo extends JpaRepository<MyEntity, Long> {
#Cacheable("myEntities")
#Override
Optional<MyEntity> findById(Long id);
#Cacheable("pagedMyEntities")
#Override
Page<MyEntity> findAll(Pageable pageable);
}
2) Create a RepositoryEventHandler to evict your caches:
#RepositoryEventHandler
public class MyEntityEventHandler {
private final CacheManager cacheManager;
public MyEntityEventHandler(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
#HandleAfterCreate
#HandleAfterSave
#HandleAfterDelete
public void handleCachesEviction(MyEntity entity) {
Optional.ofNullable(cacheManager.getCache("myEntities"))
.ifPresent(c -> c.evict(entity.getId()));
Optional.ofNullable(cacheManager.getCache("pagedMyEntities"))
.ifPresent(c -> c.clear());
}
}
3) And of course create a cache manager bean, for example:
#EnableCaching
#SpringBootApplication
public class Application {
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}

possible to extend SimpleMongoRepository

My current spring boot mongo configuration looks like following
#Configuration
#EnableMongoRepositories(Constants.SCAN_PACKAGE)
#Import(value = MongoAutoConfiguration.class)
public class MongoDatabaseConfiguration {
#Bean
public ValidatingMongoEventListener validatingMongoEventListener() {
return new ValidatingMongoEventListener(validator());
}
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
}
Now i would like to extend the class SimpleMongoRepository which seems to be the default implementation of MongoRepository. What configuration i have to do so my CustomMongoRepository extends SimpleMongoRepository is picked up instead of SimpleMongoRepository which is default shipped.
It's described in the Reference Documentation
You basically extend SimpleMongoRepository and specify that class in the #EnableMongoRepositories annotation as repositoryBaseClass.

#EnableMongoAuditing for MongoDB on Cloud Foundry / mongolab

My setup works on my local but not when I deploy it to CloudFoundry/mongolab.
The config is very similar to the docs.
My local spring config
#Configuration
#Profile("dev")
#EnableMongoAuditing
#EnableMongoRepositories(basePackages = "com.foo.model")
public class SpringMongoConfiguration extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "myDb";
}
#Override
public Mongo mongo() throws Exception {
return new MongoClient("localhost");
}
#Bean
public AuditorAware<User> myAuditorProvider() {
return new SpringSecurityAuditorAware();
}
}
This is the cloud foundry setup
#Configuration
#Profile("cloud")
#EnableMongoAuditing
#EnableMongoRepositories(basePackages = "com.foo.model")
public class SpringCloudMongoDBConfiguration extends AbstractMongoConfiguration {
private Cloud getCloud() {
CloudFactory cloudFactory = new CloudFactory();
return cloudFactory.getCloud();
}
#Bean
public MongoDbFactory mongoDbFactory() {
Cloud cloud = getCloud();
MongoServiceInfo serviceInfo = (MongoServiceInfo) cloud.getServiceInfo(cloud.getCloudProperties().getProperty("cloud.services.mongo.id"));
String serviceID = serviceInfo.getId();
return cloud.getServiceConnector(serviceID, MongoDbFactory.class, null);
}
#Override
protected String getDatabaseName() {
Cloud cloud = getCloud();
return cloud.getCloudProperties().getProperty("cloud.services.mongo.id");
}
#Override
public Mongo mongo() throws Exception {
Cloud cloud = getCloud();
return new MongoClient(cloud.getCloudProperties().getProperty("cloud.services.mongo.connection.host"));
}
#Bean
public MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoDbFactory());
}
#Bean
public AuditorAware<User> myAuditorProvider() {
return new SpringSecurityAuditorAware();
}
}
And the error I'm getting when I try to save a document in Cloud Foundry is:
OUT ERROR: org.springframework.data.support.IsNewStrategyFactorySupport - Unexpected error
OUT java.lang.IllegalArgumentException: Unsupported entity com.foo.model.project.Project! Could not determine IsNewStrategy.
OUT at org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:739)
OUT at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
OUT at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
Any ideas? Is it my config file etc..?
Thanks in advance
Niclas
This is usually caused if the Mongo mapping metadata obtained for entities does not scan entities at application startup. By default, AbstractMongoConfiguration uses the package of the actual configuration class to look for #Document annotated classes at startup.
The exception message makes me assume that SpringCloudMongoDBConfiguration is not located in any of the super packages of com.foo.model.project. There are two solutions to this:
Stick to the convenience of putting application configuration classes into the root package of your application. This will cause your application packages be scanned for domain classes, metadata obtained, and the is-new-detection work as expected.
Manually hand the package containing domain classes to the infrastructure by overriding MongoConfiguration.getMappingBasePackage().
The reason you might see the configuration working in the local environment is that the mapping metadata might be obtained through a non-persisting persistence operation (e.g. a query) and everything else proceeding from there.

Possible to detect when application starts successfully in Wildfly?

Is there a way to detect when a war file is successfully loaded by Wildfly and cause some code to execute?
You have a few options.
If you're leveraging CDI, you can add an observer method for #Observes #Initialized(ApplicationScoped.class) Object o
If you're leveraging EJBs, you can have a #javax.ejb.Singleton #javax.ejb.Startup with a #PostConstruct method that does initialization. Here are two example implementations.
// using a CDI object
#ApplicationScoped
public class SomeStartupBean {
public void initOnStartup(#Observes #Initialized(ApplicationScoped.class) Object obj) {
// do your start up logic here
}
}
or
// using an EJB
#Singleton
#Startup
public class SomeStartupSingleton {
#PostConstruct
public void initOnStartup() {
// do your start up logic here
}
}
You could use an #Startup EJB. That would execute when the application has successfully been deployed.