Open stillya opened 2 years ago
Does injecting any other bean work?
Does injecting any other bean work?
Yes, everything else works fine.
Then please create a @GrpcClientBean
on a @Configuration
class and inject the required stub or channel.
That way you you have the bean in your context and since injection works, it should work there as well.
If that does not work, there must be something else going on. Is your bean a special bean like a spring infrastructure bean? Could you provide an example project that we can use to reproduce the error?
Then please create a @GrpcClientBean on a @Configuration class and inject the required stub or channel. I do exactly this thing.
I created repository which pretty close to my project, where this problem reproducing.
This is common spring project with common-proto(lib where placed some common proto definitions), lib-commons(common lib for all microservices) and two microservices, in auth-service everything is working fine, but ms-config-constructor(it has own api-lib) fail with error on required bean not found.
@GrpcClientBean
placed in lib-commons dataflow.commons.configuration.CommonConfiguration
and inejcts in dataflow.commons.security.AuthorizationGrpcClient
. By the way, this client injects in controllers in AuthService
and ConfigConstructor
just for testing purposes.
I try to look into this this weekend.
Sorry, I didn't have time this weekend. I will try to look into it soon.
It's ok. By the way, thank you for spending your time on this.
Hello, haven't had time to look on this bug(or not) yet?
Hello, haven't had time to look on this bug(or not) yet?
Hey! I tried to investigate the problem.
lib-commons
with @GrpcClientBean
@GrpcClientBean(
beanName = "authStub",
clazz = AuthorizationServiceGrpc.AuthorizationServiceBlockingStub::class,
client = GrpcClient("AuthClient")
)
@Configuration
class CommonConfiguration {
}
lib-commons
@Component
class AuthorizationGrpcClient(private val authStub: AuthorizationServiceGrpc.AuthorizationServiceBlockingStub) {
}
@RestController
in another module ms-config-constructor
with dependency lib-commons
and access to gRPC bean client with getting from the spring context
@RestController
@RequestMapping(path = ["/configs"])
class ConfigRestController(val auth: AuthorizationGrpcClient) {
@Autowired
private lateinit var authStub: AuthorizationServiceGrpc.AuthorizationServiceBlockingStub
@PostConstruct
fun beanPresentLogNotification() {
println("gRPC bean client successfully autowired to another module [$authStub]")
println("Bean wrapper with gRPC bean client successfully autowired to another module [$auth]")
}
}
Parameter 0 of constructor in dataflow.commons.security.AuthorizationGrpcClient required a bean of type 'com.dataflow.commons.AuthorizationServiceGrpc$AuthorizationServiceBlockingStub' that could not be found.
I suppose something strange happen with @Component
constructor and even @Autowired
injection in the SAME
module, but if you'll try to manually create AuthorizationGrpcClient
bean like
@GrpcClientBean(
beanName = "authStub",
clazz = AuthorizationServiceGrpc.AuthorizationServiceBlockingStub::class,
client = GrpcClient("AuthClient")
)
@Configuration
class CommonConfiguration {
@Bean
fun decoder(): Base64.Decoder = Base64.getDecoder()
@Bean
fun authorizationGrpcClient(authStub: AuthorizationServiceGrpc.AuthorizationServiceBlockingStub): AuthorizationGrpcClient {
return AuthorizationGrpcClient(authStub);
}
}
you will get in logs
gRPC bean client successfully autowired to another module [com.dataflow.commons.AuthorizationServiceGrpc$AuthorizationServiceBlockingStub@214fba74]
Bean wrapper with gRPC bean client successfully autowired to another module [dataflow.commons.security.AuthorizationGrpcClient@252c6cdb]
@stillya hope manually bean construction for kt classes would be helpful, at least it works. Another possible workaround is to develop java classes that requires @Grpclient
bean instead of kt classes. Both options definitely not great at all :(
@ST-DDT sadly GrpcClientBeanPostProcessor have 0 reaction for both method and field injections in kt class. Should we use something like runtimeOnly 'org.jetbrains.kotlin:kotlin-reflect:1.2.41'
to have proper access?
processFields
and 0 reaction for@Component
class AuthorizationGrpcClient {
//bean not found
@Autowired
lateinit var authStub: AuthorizationServiceGrpc.AuthorizationServiceBlockingStub
}
processMethods
and 0 reaction for@Component
class AuthorizationGrpcClient(private val authStub: AuthorizationServiceGrpc.AuthorizationServiceBlockingStub) {
//bean not found
...
}
UPD
This is kt too, but here field injection works properly because @GrpcBean authStub
and AuthorizationGrpcClient
already exists in the spring context
@RestController
@RequestMapping(path = ["/configs"])
class ConfigRestController(val auth: AuthorizationGrpcClient) {
@Autowired
private lateinit var authStub: AuthorizationServiceGrpc.AuthorizationServiceBlockingStub
@PostConstruct
fun beanPresentLogNotification() {
println("gRPC bean client successfully autowired to another module [$authStub]")
println("Bean wrapper with gRPC bean client successfully autowired to another module [$auth]")
}
}
@a-simeshin your workaround isn't working if I wanna more than one grpcClientBean to create another bean, i.e.
@Configuration
@GrpcClientBeans(
value = [
GrpcClientBean(
beanName = "authStub",
clazz = AuthorizationServiceGrpc.AuthorizationServiceBlockingStub::class,
client = GrpcClient("AuthorizationService")
),
GrpcClientBean(
beanName = "permissionStub",
clazz = PermissionServiceGrpc.PermissionServiceBlockingStub::class,
client = GrpcClient("PermissionService")
)
]
)
class TestConfiguration {
@Bean
fun authorizationGrpcClient(
authStub: AuthorizationServiceGrpc.AuthorizationServiceBlockingStub,
permissionStub: PermissionServiceGrpc.PermissionServiceBlockingStub
): AuthorizationGrpcClient {
return AuthorizationGrpcClient(authStub, permissionStub)
}
}
We'll haven't even first stub-bean, which previously work. I don't know how to work around this.
hi team any update on this?
This looks like a bug in our code. I have identified the cause, but I haven't had time to fix the bug yet. The relevant code needs to be moved from GrpcClientBeanPostProcessor to a BeanFactoryPostProcessor. I have some time off work in December, so the next release with a fix for this will probably released then.
For now, you can only try to circumvent the problem by adding @DependsOn(YourConfigBean)
Hey @ST-DDT and team! First of all, thanks for excellent work on on this project!
But do you guys think there is a chance you could look into this problem any time soon? Maybe, as a 2.14.1
patch release or something?
@ST-DDT mentioned that you've identified the culprit:
The relevant code needs to be moved from GrpcClientBeanPostProcessor to a BeanFactoryPostProcessor.
And I tend to think so as well.
Spring hides the actual root cause, but if you put a breakpoint at GrpcClientBeanPostProcessor.java#L191 you'd see that the real issue is with the GrpcClientBeanPostProcessor
trying to fetch a couple of Spring beans (GrpcClientBeanPostProcessor.java#L238) - NameResolverRegistration
and GrpcChannelFactory
- yet before the context has been refreshed (i.e. finished initialization) :
java.lang.IllegalStateException: Failed to create channel: <grpc client name here>
at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.processInjectionPoint(GrpcClientBeanPostProcessor.java:218)
at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.processGrpcClientBeansAnnotations(GrpcClientBeanPostProcessor.java:188)
at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.postProcessBeforeInitialization(GrpcClientBeanPostProcessor.java:129)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440)
...
Caused by: java.lang.IllegalStateException: org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext@42b02722 has not been refreshed yet
at org.springframework.context.support.AbstractApplicationContext.assertBeanFactoryActive(AbstractApplicationContext.java:1141)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1171)
at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.getChannelFactory(GrpcClientBeanPostProcessor.java:238)
at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.processInjectionPoint(GrpcClientBeanPostProcessor.java:213)
And I don't see an easy way around this, e.g. enforcing specific sequence / timing or deferring injection till after context refresh. Neither I see a way to apply the @DependsOn
trick mentioned above. Annotation expects a bean name, not a configuration class or anything (I'll tinker with it a little more though).
But meanwhile the client side of the project doesn't seem to work for us, which is a real bummer - the library has proven to be great so far :)
Any thoughts on a fix timing or a functional workaround are highly appreciated!
This issue is manifesting in a number of ways (we just ran into this issue when a dependency needed a gRPC stub wiring (that the parent module declares via GrpcClient). Having the component in the dependency declare @DependsOn
(against the parent module's @Configuration
that includes @GrpcClient
works but it's obviously not ideal.
I plan on fixing this, but I currently don't have any free time. 😢
@ST-DDT - I have also hit this issue and looking forward to getting this issue fixed.
@stillya - I tried reproducing the issue with the sample repo that you had shared and I noticed that with the below small changes (version bump and adding an annotation), the project comes up (I had to fix the flyway locations so that the app comes up)
Here's the patch
diff --git a/build.gradle b/build.gradle
index ebf6f4d..f0fe34a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,6 @@
plugins {
id 'java'
+ id 'idea'
}
group 'com.campaign'
diff --git a/gradle.properties b/gradle.properties
index 4453b2e..5e17cd7 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -28,7 +28,7 @@ lombokVersion=1.18.22
postgresVersion=42.2.14
mapStructVersion=1.5.0.Beta2
kotlinLoggingVersion=2.1.20
-grpcStarterVersion=2.13.1.RELEASE
+grpcStarterVersion=2.14.0.RELEASE
protocVersion=4.0.0-rc-2
protocGenJavaVersion=1.42.1
testContainersVersion=1.16.2
@@ -41,4 +41,4 @@ groovyVersion=3.0.9
jookVersion=3.15.7
springdocOpenApiVersion=1.6.5
springdocOpenApiKotlinVersion=1.6.5
-mockitoInlineVersion=4.3.1
\ No newline at end of file
+mockitoInlineVersion=4.3.1
diff --git a/lib-commons/src/main/kotlin/dataflow/commons/security/AuthorizationGrpcClient.kt b/lib-commons/src/main/kotlin/dataflow/commons/security/AuthorizationGrpcClient.kt
index 28dc104..239f2e2 100644
--- a/lib-commons/src/main/kotlin/dataflow/commons/security/AuthorizationGrpcClient.kt
+++ b/lib-commons/src/main/kotlin/dataflow/commons/security/AuthorizationGrpcClient.kt
@@ -4,10 +4,11 @@ import com.dataflow.commons.AuthorizationServiceGetRuleByNameRequest
import com.dataflow.commons.AuthorizationServiceGrpc
import dataflow.commons.dtos.abac.AbacRule
import dataflow.commons.mappers.toDto
+import net.devh.boot.grpc.client.inject.GrpcClient
import org.springframework.stereotype.Component
@Component
-class AuthorizationGrpcClient(val authStub: AuthorizationServiceGrpc.AuthorizationServiceBlockingStub) {
+class AuthorizationGrpcClient (@GrpcClient("AuthClient") val authStub: AuthorizationServiceGrpc.AuthorizationServiceBlockingStub) {
fun getRuleByName(name: String): AbacRule {
val request: AuthorizationServiceGetRuleByNameRequest =
@@ -17,4 +18,4 @@ class AuthorizationGrpcClient(val authStub: AuthorizationServiceGrpc.Authorizati
return authStub.getRuleByName(request).rule.toDto()
}
-}
\ No newline at end of file
+}
diff --git a/ms-config-constructor/flyway.conf b/ms-config-constructor/flyway.conf
index 4f60615..b4954d3 100644
--- a/ms-config-constructor/flyway.conf
+++ b/ms-config-constructor/flyway.conf
@@ -2,5 +2,5 @@ flyway.user=postgres
flyway.password=postgres
flyway.schemas=public
flyway.url=jdbc:postgresql://localhost:5432/df_config_constructor?currentSchema=public&ApplicationName=config_constructor
-flyway.locations=filesystem:src/main/resources/db/migration
-flyway.baselineOnMigrate=true
\ No newline at end of file
+#flyway.locations=filesystem:src/main/resources/db/migration
+flyway.baselineOnMigrate=true
@krmahadevan yeah, but my point is about to define ClientBean in configuration and use stub as a regular bean.
I have two microservices with common-lib and lib for proto-definition and my grpc client located at common-lib, but it only works with first microservice, where I have GrpcService, in second I haven't grpc client, no such bean definition.
I tried @GrpcClient, @GrpcClientBean and it doesn't work. I checked dependency graph, both have grpc-client dep, both have the same version of spring. It so strange, I added breakpoint on GrpcClientBeanPostProcessor and in second microservice I don't go there, but post processor in classpath. Have any ideas about it?
Both microservices and lib written on Kotlin.