Configuring password for redis results in an error as @chenjpu described in https://github.com/micronaut-projects/micronaut-core/issues/1800
Application should start and use the configured password to connect to redis.
The endpoint should return 404 because key 'foo' does not exist in redis.
When the issue is fixed you should be able to set a value for a key and retrieve it.
curl -X POST -H 'Content-Type: text/plain' -d "hello world" http://localhost:8080/foo
curl http://localhost:8080/foo
hello world*
Message: Error resolving property value [redis.password]. Property doesn't exist
Path Taken: new RedisController([RedisService redisService]) --> new RedisService([StatefulRedisConnection redis]) --> StatefulRedisConnection.redisConnection([RedisClient redisClient]) --> RedisClient.redisClient([AbstractRedisConfiguration config]) --> DefaultRedisConfiguration.setPassword([char[] password])
at io.micronaut.context.AbstractBeanDefinition.getValueForMethodArgument(AbstractBeanDefinition.java:795)
at io.micronaut.configuration.lettuce.$DefaultRedisConfigurationDefinition.injectBean(Unknown Source)
at io.micronaut.configuration.lettuce.$DefaultRedisConfigurationDefinition.build(Unknown Source)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1598)
at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2307)
at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1989)
at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1963)
at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1082)
at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:1007)
at io.micronaut.configuration.lettuce.$DefaultRedisClientFactory$RedisClientDefinition.build(Unknown Source)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1598)
at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2307)
at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1989)
at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1963)
at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1082)
at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:1007)
at io.micronaut.configuration.lettuce.$DefaultRedisClientFactory$RedisConnectionDefinition.build(Unknown Source)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1598)
at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2307)
at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1989)
at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1963)
at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1082)
at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:1007)
at redis.password.issue.$RedisServiceDefinition.build(Unknown Source)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1598)
at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2307)
at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1989)
at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1963)
at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:1082)
at io.micronaut.context.AbstractBeanDefinition.getBeanForConstructorArgument(AbstractBeanDefinition.java:1007)
at redis.password.issue.$RedisControllerDefinition.build(Unknown Source)
at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:1598)
at io.micronaut.context.DefaultBeanContext.createAndRegisterSingleton(DefaultBeanContext.java:2307)
at io.micronaut.context.DefaultBeanContext.getBeanForDefinition(DefaultBeanContext.java:1989)
at io.micronaut.context.DefaultBeanContext.getBeanInternal(DefaultBeanContext.java:1963)
at io.micronaut.context.DefaultBeanContext.getBean(DefaultBeanContext.java:605)
at io.micronaut.context.DefaultBeanContext$BeanExecutionHandle.getTarget(DefaultBeanContext.java:2821)
at io.micronaut.context.DefaultBeanContext$BeanExecutionHandle.invoke(DefaultBeanContext.java:2842)
at io.micronaut.web.router.AbstractRouteMatch.execute(AbstractRouteMatch.java:294)
at io.micronaut.web.router.RouteMatch.execute(RouteMatch.java:122)
at io.micronaut.http.server.netty.RoutingInBoundHandler.lambda$buildResultEmitter$17(RoutingInBoundHandler.java:1400)
at io.reactivex.internal.operators.flowable.FlowableCreate.subscribeActual(FlowableCreate.java:71)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.internal.operators.flowable.FlowableMap.subscribeActual(FlowableMap.java:37)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.internal.operators.flowable.FlowableSwitchIfEmpty.subscribeActual(FlowableSwitchIfEmpty.java:32)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14868)
at io.micronaut.http.context.ServerRequestTracingPublisher.lambda$subscribe$0(ServerRequestTracingPublisher.java:52)
at io.micronaut.http.context.ServerRequestContext.with(ServerRequestContext.java:52)
at io.micronaut.http.context.ServerRequestTracingPublisher.subscribe(ServerRequestTracingPublisher.java:52)
at io.reactivex.internal.operators.flowable.FlowableFromPublisher.subscribeActual(FlowableFromPublisher.java:29)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.micronaut.reactive.rxjava2.RxInstrumentedFlowable.subscribeActual(RxInstrumentedFlowable.java:68)
at io.reactivex.Flowable.subscribe(Flowable.java:14918)
at io.reactivex.Flowable.subscribe(Flowable.java:14865)
at io.reactivex.internal.operators.flowable.FlowableSubscribeOn$SubscribeOnSubscriber.run(FlowableSubscribeOn.java:82)
at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker$BooleanRunnable.run(ExecutorScheduler.java:288)
at io.reactivex.internal.schedulers.ExecutorScheduler$ExecutorWorker.run(ExecutorScheduler.java:253)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
I suspect that the problem is that RedisURI has two setPasswordmethods. The one taking a String is deprecated and the char[] should be used. Micronaut's DefaultRedisConfiguration gets confused. Just my guess.
https://github.com/lettuce-io/lettuce-core/blob/master/src/main/java/io/lettuce/core/RedisURI.java#L353
could be we don't have a string->char[] converter.
Try add:
@Singleton
class StringToCharArray implements TypeConverter<String,char[]> {
@Override
public Optional<char[]> convert(String object, Class<char[]> targetType, ConversionContext context) {
return Optional.of(object.toCharArray());
}
}
Thanks! That fixed the exception. I will also verify that the password are actually working. Stay tuned...
Unfortunately the problem is bigger than the converter. The redis config impl in Micronaut is broken. Setting config parameters in application.yml has no effect. I know what the problem is. I'm trying to fix it now.
When Micronaut processes the redis configuration (in AbstractRedisConfiguration.java) it creates a RedisURI instance in setUri(). All other configuration parameters (port, password, ...) are set in the configuration instance, but not inte RedisURI instance already created.
https://github.com/micronaut-projects/micronaut-redis/blob/master/redis-lettuce/src/main/java/io/micronaut/configuration/lettuce/AbstractRedisConfiguration.java#L61
A minor issue I noticed when debugging is that both setPassword(String) and setPassword(char[]) in RedisURI are called during configuration. After I added the StringToCharArray converter.
I think generally that is because you either use the redis uri or you don't and set properties directly not both. The reason for this is the redis URI already includes a way to specify the password in the URI string. See https://github.com/lettuce-io/lettuce-core/wiki/Redis-URI-and-connection-details
Which describes the syntax to include the password in the URI string
You are correct! Removing the redis.password and including the password in redis.uri solves the problem. Thanks! What should we do now? Make the configuration more flexible or document the behavior?
The Configuration reference includes redis.xxx properties but they won't work if you configure uri.
https://micronaut-projects.github.io/micronaut-redis/latest/guide/configurationreference.html#io.micronaut.configuration.lettuce.DefaultRedisConfiguration
Yeah I think it is as either or arrangement. When you use the URI you have to specify everything there. Probably worth improving the docs in this area
I can spend some time on this after January 24. Add documentation, add more test cases and adjust impl if needed. But if someone else want to look at it before that, please do.
I was looking at this a bit and would it work it work to have an annotation that would enforce the type?
@ConfigurationProperties('foo.bar')
@EnforceType("foo.bar.charArray", value=char[].class)
public class MyConfig {
public void setCharArray(char[] charArray) {
}
public void setCharArray(String charArray)
}
}