Spring Bean Override for Spring-Data REDIS - spring-boot

I am trying to override RedisCustomConversions [initial post] (RedisCustomConversions using Redis Repository noticed some inconsistent behaviour.
When I run the spring boot app from IDE I get the error mentioned in the above post, but it works fine if the app is executed using java -jar myapp.jar. Which sounds okay but that makes me question if this behaviour is consistent? i.e will my overloaded bean will always be wired? or is it random?
I also noticed from /actuator/beans endpoint that when executed in IDE I get this
redisCustomConversions: {
aliases: [ ],
scope: "singleton",
type: "org.springframework.data.redis.core.convert.RedisCustomConversions",
dependencies: []
But running from the command line I see this.
redisCustomConversions: {
aliases: [ ],
scope: "singleton",
type: "org.springframework.data.redis.core.convert.RedisCustomConversions",
resource: "class path resource [com/my/project/cache/config/RedisConfig.class]",
dependencies: [
"MyWriterConverter",
"MyReaderConverter"
]
So the question is, can we rely on this behaviour? or it's random and for some deployments, the default RedisCustomConversions will be loaded and for other my custom RedisCustomConversions will be loaded (this will crash the application as data format in REDIS will be different)?

I was having the same issue as you and after banging my head against wall for longer than I wish to admit I asked in the spring-data gitter. They responded as so:
When using Spring Boot, you need to declare #EnableRedisRepositories and the #Bean definition for RedisCustomConversions on the same configuration class. That's currently a limitation of how #EnableRedisRepositories is built.

Related

spring-integration: SplitterFactoryBean may only be referenced once

I have a Spring project (not using Spring Boot, if that's relevant) that I'm trying to connect to a local database using the Postgres JDBC driver. (The local database is actually Yugabyte, but that should be fully compatible with the Postgres driver.)
When starting the application, I get this error message:
java.lang.IllegalArgumentException: An AbstractMessageProducingMessageHandler may only be referenced once (org.springframework.integration.config.SplitterFactoryBean#0) - use scope="prototype"
at org.springframework.util.Assert.isTrue(Assert.java:118)
at org.springframework.integration.config.AbstractStandardMessageHandlerFactoryBean.checkReuse(AbstractStandardMessageHandlerFactoryBean.java:168)
at org.springframework.integration.config.AbstractStandardMessageHandlerFactoryBean.createHandler(AbstractStandardMessageHandlerFactoryBean.java:137)
at org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean.createHandlerInternal(AbstractSimpleMessageHandlerFactoryBean.java:186)
at org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean.getObject(AbstractSimpleMessageHandlerFactoryBean.java:174)
at org.springframework.integration.config.AbstractSimpleMessageHandlerFactoryBean.getObject(AbstractSimpleMessageHandlerFactoryBean.java:59)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:171)
... 52 more
I can't place this error at all. There is one similar question on Stack Overflow, but there the asker seems to actually know what they're doing and how this is related to spring integration. I, however, am not aware at all that I'm trying to 'reuse' anything. The referenced question also doesn't seem to be related to database configuration.
My setup/configuration is a bit involved, so I'll try to quote the parts that seem relevant.
I have a dao layer project that has the following gradle dependencies (among others):
implementation("org.springframework:spring-context:5.2.2.RELEASE")
implementation("org.springframework:spring-jdbc:5.2.2.RELEASE")
implementation("org.jooq:jooq-kotlin:3.14.11")
runtimeOnly("org.postgresql:postgresql:42.2.19.jre7")
In the same project, I have some configuration (in Kotlin):
#Configuration
open class Config {
#Bean
open fun jdbcTemplate(dataSource: DataSource): JdbcTemplate = JdbcTemplate(dataSource)
#Bean
open fun dslContext(): DSLContext = DefaultDSLContext(SQLDialect.POSTGRES)
#Configuration
#Profile("!unittest")
open inner class NonTestConfig {
#Bean
open fun dataSource(): DataSource {
return DriverManagerDataSource().apply {
// Hardcoded properties to be replaced by values from property file
setDriverClassName("org.postgresql.Driver")
url = "jdbc:postgresql://localhost:5433/demo"
username = "yugabyte"
password = "yugabyte"
}
}
}
}
(Notes: the DSLContext bean is used for JOOQL, included for completeness' sake. The inner class config is there because there is also a separate unit testing config for an embedded database - that one works fine!)
Now, the above project is used in my top-level project that contains the actual application. It's a maven runtime dependency there. I import the config class in this project's XML configuration, using this method:
<context:annotation-config />
<bean class="my.package.Config" />
Then trying to start the application produces the error message.
I figured out what the problem was, but I still don't know how it relates to a <splitter>.
The problem was that the Config class, apart from the database stuff, also included a bean to encrypt data. It turned out that this bean was also defined in another library used by the top-level project. Fixing this duplicate bean problem made the error go away.
I discovered this in a roundabout way: I included the dao project and its configuration in a different top-level project that uses Spring Boot. This led to a clear error message about the encryptor bean having two definitions.
If anyone can explain why the error message is so cryptic in the non-Boot case, that would be a nice complementary answer.

SpringBoot not resolving devtools properties in tests

I'm using spring-boot-devtools with my SpringBoot application (2.2.8) to store secrets outside of my repository. This works for the running application, but integration tests fail with Unexpected exception during bean creation; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'secret.key' in value "${secret.key}".
SecretController.kt
#RestController
class SecretController(
#Value("\${secret.key}") private val secret: String
) {
#GetMapping("/secret")
fun secret(): String {
return secret
}
}
.spring-boot-devtools.properties
secret.key: secret-asdf
In an older application (SpringBoot 2.1) this works fine. I know they changed the path with SpringBoot 2.2, but kept the old path as backwards compatibility - I tried both without success. I also upgraded to 2.3.1, but this does not help either.
Any ideas on how to read properties in integration tests with SpringBoot >= 2.2?
This isn't a bug but a conscious change, see https://github.com/spring-projects/spring-boot/issues/5307
You shouldn't rely on devtools to configure your tests this way.

Micronaut declarative REST client throws an error - #Introduction method interceptor missing

When I autowire the client interface for my Micronaut declarative client, I get this error:
Caused by: java.lang.IllegalStateException: At least one #Introduction method interceptor required, but missing. Check if your #Introduction stereotype annotation is marked with #Retention(RUNTIME) and #Type(..) with the interceptor type. Otherwise do not load #Introduction beans if their interceptor definitions are missing!
at io.micronaut.aop.chain.InterceptorChain.resolveIntroductionInterceptors(InterceptorChain.java:194)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1494)
What's the proper way to fix it?
Details
I have an established Grails application that I recently upgraded from 3.x to 4.0.1.
This app has a service which does several REST calls in parallel, and I am trying to add a new REST call that uses the new Micronaut HTTP declarative client.
I added the client library to dependencies in build.gradle:
compile "io.micronaut:micronaut-http-client"
My client interface looks like this (in src/main/groovy):
package com.mycompany.xyz.rest
import com.mycompany.xyz.rest.myendpoint.Results
import io.micronaut.http.annotation.Get
import io.micronaut.http.annotation.Header
import io.micronaut.http.client.annotation.Client
#Client('xyzRest')
#Header(name = 'myauthkey', value = '${myAuthKey}')
interface XyzRestClient {
#Get('/myendpoint')
Results myendpoint(String param1, String param2)
}
package com.mycompany.xyz.rest.myendpoint
import com.mycompany.xyz.rest.myendpoint.DataItem
import groovy.transform.CompileStatic
#CompileStatic
interface Results extends List<DataItem> {
}
I configured the URL in application.yml:
environments:
development:
micronaut:
http:
services:
xyzRest:
urls:
- http://xyz.mycompany.com/rest/v1
The message about #Introduction makes me think that Micronaut is not doing the process of compiling the declarative client. Is there some
What else am I missing?
Update:
I tried changing the build.gradle dependency to implementation as shown in the Micronaut docs, insteadl of compile, as shown in the Grails docs. No dice.
Update 2:
I found that the constructor for HttpClientIntroductionAdvice is never invoked during startup. I don't know why it's not being included in my project. IntelliJ shows micronaut-http-client:1.1.4 in external libraries, and it's set to compile scope.
A gradlew clean seems to have fixed the issue.
I tried to work backwards and duplicate the problem for posterity's sake, but so far I have not been able to.

In Spring Boot how do you register custom converters that are available when parsing application configuration?

In a Spring Boot application how do you register custom converts to be used when processing application configuration?
I have made a custom convert (org.springframework.core.convert.converter.Converter) so it can be used by the ApplicationConversionService/Binder to parse #ConfiguraitonProperties defined in application.properties and application.yaml configuration files but do not know how to register it.
I have tried the solution here https://stackoverflow.com/a/41205653/45708 but it creates an instance of my converter after the application configuration parameters have been processed.
I ran into this issue myself recently. From what I can tell, the key issue is that binding to configuration properties occurs very early in the Spring startup process, before the Application Context is fully initialized. Therefore the usual methods for registering a converter are not reliable. In fact the ConversionService used for configuration binding appear to be a one-off and not really connected to the ConversionService that is stored in the Application Context.
I was able to get something working but it feels like a hack, as it relies on internal implementation details that may work today but not tomorrow. In any case, this is the code I used:
((ApplicationConversionService) ApplicationConversionService.getSharedInstance()).addConverter(myCustomConverter);
The trick I found was to make sure this gets called as soon as possible at application startup so that it gets called before the configuration binding where it's needed. I put it in a #PostConstruct block inside my main #SpringBootApplication class as this seemed to get invoked early on, at least in my case.

Inject constructor argument Spring Resource file with Grails/Groovy

From our Grails/Groovy app we need to use a service from a legacy Java service class, the constructor of which has a parameter of type org.springframework.core.io.Resource, e.g.
public ServiceClass(Resource someResource)
We need to inject an instance of the service class into a Groovy class of our app using Spring DSL, with the Resource referring to an XML file within our /src/main/resources. I tried to create the Spring config for this purpose, but so far I couldn't find a working solution. The relevant part of the config file looks like this
beans = {
xmlns aop:"http://www.springframework.org/schema/aop",
sec:"http://www.springframework.org/schema/security",
context:"http://www.springframework.org/schema/context"
serviceClass(com.somepackage.ServiceClass) {
//here we need to refer to the constructor arg XML file some way
}
}
I have tried multiple syntaxes found in various tutorials, e.g. closure for beanDefinition.constructorArgs, but unfortunately without success so far. Although neither the app compilation (grails:war) nor the startup (grails:run-app) indicates any problems with the bean wiring, when the app is actually loaded into the browser, we receive a NPE stating that our Groovy class into which the service class is injected, is a null object. So it seems that the bean wiring was not successful after all. Any help is appreciated
After fixing various issues with the project setup itself and multiple cleanups/recompiles, it seems that the following two approaches are both OK
serviceClass(com.somepackage.ServiceClass, '/WEB-INF/constructor-arg-xml-file.xml') {}
and
serviceClass(com.somepackage.ServiceClass) { bean ->
bean.constructorArgs = [
'/WEB-INF/constructor-arg-xml-file.xml'
]
}

Resources