1

I am using Spring boot 2.0.0.Release with gradle and webflux written in Kotlin and I am trying to make ReactiveMongoDB and Elasticsearch work together, I also have JWT implementation. So to validate the AuthenticationToken I implemented ReactiveAuthenticationManager like below:

@Component
class UserAuthenticationManager(@Inject private val service: UserService,
                                @Inject private val encoder: PasswordEncoder) : ReactiveAuthenticationManager {

    override fun authenticate(authentication: Authentication): Mono<Authentication> {
        return service.findByUsernameWithPassword(authentication.name)
                .publishOn(Schedulers.parallel())
                .filter { u -> encoder.matches(authentication.credentials as String, u.password) }
                .switchIfEmpty(Mono.defer<User> { Mono.error<User>(BadCredentialsException("Invalid Credentials")) })
                .map { u -> AuthenticationToken(u.id, u.username, u.password!!, u.roles.map { SimpleGrantedAuthority(it.name) }) }
    }

}

and to enable reactive mongodb repository I did this:

@Configuration
@EnableMongoAuditing(auditorAwareRef = "springSecurityAuditorAware")
@EnableReactiveMongoRepositories(
        basePackages = ["com.example.package.repository.**.nosql"],
        excludeFilters = [
            ComponentScan.Filter(
                    type = FilterType.ASSIGNABLE_TYPE,
                    value = [ElasticsearchRepository::class]
            )
        ]
)
class DatabaseConfiguration

Also I configured ElasticSearchRepositories like this:

@Configuration
@EnableElasticsearchRepositories(
        basePackages = ["com.example.package.repository.**.search"],
        excludeFilters = [
            ComponentScan.Filter(
                    type = FilterType.ASSIGNABLE_TYPE,
                    value = [ReactiveMongoRepository::class]
            )
        ]
)
class ElasticsearchConfiguration {

    @Bean
    fun elasticsearchTemplate(client: Client, builder: Jackson2ObjectMapperBuilder): ElasticsearchTemplate {
        return ElasticsearchTemplate(client, CustomEntityMapper(builder.createXmlMapper(false).build()))
    }

    inner class CustomEntityMapper(private val objectMapper: ObjectMapper) : EntityMapper {

        init {
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
        }

        @Throws(IOException::class)
        override fun mapToString(`object`: Any): String {
            return objectMapper.writeValueAsString(`object`)
        }

        @Throws(IOException::class)
        override fun <T> mapToObject(source: String, clazz: Class<T>): T {
            return objectMapper.readValue(source, clazz)
        }
    }
}

When I run the gradle bootRun it throws exception below:

Caused by: java.lang.IllegalStateException: availableProcessors is already set to [8], rejecting [8]
    at io.netty.util.NettyRuntime$AvailableProcessorsHolder.setAvailableProcessors(NettyRuntime.java:51)
    at io.netty.util.NettyRuntime.setAvailableProcessors(NettyRuntime.java:87)
    at org.elasticsearch.transport.netty4.Netty4Utils.setAvailableProcessors(Netty4Utils.java:85)
    at org.elasticsearch.transport.netty4.Netty4Transport.<init>(Netty4Transport.java:140)
    at org.elasticsearch.transport.Netty4Plugin.lambda$getTransports$0(Netty4Plugin.java:93)
    at org.elasticsearch.client.transport.TransportClient.buildTemplate(TransportClient.java:177)
    at org.elasticsearch.client.transport.TransportClient.<init>(TransportClient.java:268)
    at org.elasticsearch.transport.client.PreBuiltTransportClient.<init>(PreBuiltTransportClient.java:133)
    at org.elasticsearch.transport.client.PreBuiltTransportClient.<init>(PreBuiltTransportClient.java:119)
    at org.elasticsearch.transport.client.PreBuiltTransportClient.<init>(PreBuiltTransportClient.java:109)
    at org.springframework.data.elasticsearch.client.TransportClientFactoryBean.buildClient(TransportClientFactoryBean.java:91)
    at org.springframework.data.elasticsearch.client.TransportClientFactoryBean.afterPropertiesSet(TransportClientFactoryBean.java:86)
    at org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration.elasticsearchClient(ElasticsearchAutoConfiguration.java:59)
    at org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration$$EnhancerBySpringCGLIB$$4eba21d.CGLIB$elasticsearchClient$0(<generated>)
    at org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration$$EnhancerBySpringCGLIB$$4eba21d$$FastClassBySpringCGLIB$$bc4f5c6f.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:361)
    at org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration$$EnhancerBySpringCGLIB$$4eba21d.elasticsearchClient(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
    ... 137 common frames omitted

While debugging, I found out, setAvailableProcessors method is called twice in NettyRuntime, first for MongoReactiveAutoConfiguration and second for ElasticsearchAutoConfiguration where I get exception. Also I found out, if I remove my implementation of UserAuthenticationManager, MongoReactiveAutoConfiguration doesn't call setAvailableProcessors method so everything works fine because it is called once by ElasticsearchAutoConfiguration. Why MongoReactiveAutoConfiguration cares about ReactiveAuthenticationManager. I really confused. How can I fix this problem without removing UserAuthenticationManager implementation?

Any helps?

Solution: With help of @RomanDzhadan I found the way of preventing reactive mongo db to check Netty available processors. The problem was this Bean in MongoReactiveAutoConfiguration

@Bean
@ConditionalOnMissingBean
public MongoClient reactiveStreamsMongoClient(MongoProperties properties,
Environment environment,
ObjectProvider<List<MongoClientSettingsBuilderCustomizer>> builderCustomizers) {
    ReactiveMongoClientFactory factory = new ReactiveMongoClientFactory(properties,
            environment, builderCustomizers.getIfAvailable());
    this.mongo = factory.createMongoClient(this.settings);
    return this.mongo;
}

So I solved it by having my own bean definition of MongoClient for example something like this:

@Bean
fun mongoClient(): MongoClient {
    logger.debug("Configuring mongo client")
    return MongoClients.create(mongoProperties.determineUri())
}

1 Answer 1

1

Try to manage configuration beans by yourself. Spring Auto Configuration - always full of pitfalls. Especially, when you trying to use more then 1 implementation of spring data. (in your case it's elasticsearch and mongo).

Frameworks can't do everything for you. So, I will recommend to setup it explicitly.

  • Cons: 1 or 2 extra classes with beans creation.
  • Pros: Much more control and understanding of how your application works
Sign up to request clarification or add additional context in comments.

7 Comments

I really like simplicity. I will do your suggestion if I have to but before that, I would like to know the reason, I even debugged EnableReactiveMongoRepositories but I really couldn't understand what's going on?
what elasticsearch version do you use? and what version of spring-data-elasticsearch?
I am using 6.1.2 for elasticsearch and spring-data-elasticsearch will be loaded by Spring Boot version 2.0.0.Release dependency management org.springframework.cloud:spring-cloud-dependencies:Finchley.M8
Try to override elasticsearch version in pom.xml or in gradle. link
to what version? Also I appreciate giving me solutions but I need first to know about the reason behind this. in the worse scenario I will load the bean manually as you suggested or set System.setProperty("es.set.netty.runtime.available.processors", "false"); to avoid elasticsearch check for available processors. Do you have any idea why am I getting this error?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.