grails / grails-core

The Grails Web Application Framework
http://grails.org
Apache License 2.0
2.79k stars 949 forks source link

Using Micronaut KafkaListener in Grails - unable to inject Grails Services #12807

Closed andrewcanby-finocomp closed 1 year ago

andrewcanby-finocomp commented 1 year ago

Expected Behavior

Ability to use existing grails-app/services in a Micronaut @KafkaListeners.

Actual Behaviour

When trying to introduce a @KafkaListener to an existing Grails application, dependency injection fails.

(full code in linked project)

@Slf4j
@KafkaListener(groupId = "weather-group", offsetReset = OffsetReset.LATEST)
class WeatherConsumer {

    private final WeatherService weatherService

    WeatherConsumer(WeatherService weatherService) {
        this.weatherService = weatherService
    }

    @Topic("weather")
    void consumeWeather(@KafkaKey String key, String value) {
        float temp = value.toFloat()
        weatherService.logTemperature(temp)
    }
}
   io.micronaut.context.exceptions.DependencyInjectionException: Failed to inject value for parameter [weatherService] of class: kafka.example.WeatherConsumer
   Message: No bean of type [kafka.example.WeatherService] exists. Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
   Path Taken: new WeatherConsumer(WeatherService weatherService) --> new WeatherConsumer([WeatherService weatherService])
   at io.micronaut.context.AbstractInitializableBeanDefinition.resolveBean(AbstractInitializableBeanDefinition.java:2091)
   at io.micronaut.context.AbstractInitializableBeanDefinition.getBeanForConstructorArgument(AbstractInitializableBeanDefinition.java:1299)
   at kafka.example.$WeatherConsumer$Definition.build(Unknown Source)
// snip

The bean is seemingly available in both Micronaut and Grails/Spring contexts, albeit with slightly different resolution strategies. I expect this is somehow related to there being two contexts, but I am unable to determine what else should be done to make this work.

Steps To Reproduce

(In the linked project)

  1. ./gradlew bootRun the project, note the startup failure
  2. By adding @Singleton to the grails-app/services the application "works".
    • I don't think this is the right solution, especially in a project with hundreds of interdependent services
    • I also wonder how plugin defined beans (e.g. SpringSecurityService) would interact in this scenario

Environment Information

(Not specific to this combination, just what I use)

Example Application

https://github.com/andrewcanby-finocomp/grails-micronaut-kafka-client-issue

Version

5.2.5

jeffscottbrown commented 1 year ago

Is injecting Spring beans into Micronaut beans supported?

andrewcanby-finocomp commented 1 year ago

Hi @jeffbrown, that's the question isn't it?

The Grails 4.0.0 release notes mention:

Micronaut is now the parent application context of Grails thus allowing using many Micronaut features including the Micronaut HTTP Client and Kafka Client.

Perhaps I'm using it incorrectly, or doing something that is unsupported - I don't know. I can't find a definitive guide for Micronaut integration, other than using the features in isolation.

What is the suggested approach for using Grails services in a situation like the one described above?

puneetbehl commented 1 year ago

I believe it is NOT possible right now to inject Grails/Spring Bean into Micronaut context. I think the only option is to craete a Micronaut Bean or you could experiment something like Micronaut for Spring (Micronaut for Spring allows you to use traditional Spring annotations which are mapped to Micronaut annotations at compilation time.)