Home > Enterprise >  Spring Mono<User> as constructor param - how to "cache" object
Spring Mono<User> as constructor param - how to "cache" object

Time:03-07

I'm drawing a blank on how to do this in project reactor with Spring Boot:

class BakerUserDetails(val bakerUser: Mono<BakerUser>): UserDetails {
    override fun getPassword(): String {
        TODO("Not yet implemented")
        // return ???.password
    }

    override fun getUsername(): String {
        TODO("Not yet implemented")
        // return ???.username
    }

}

How do I make this work? Do I just put bakerUser.block().password and bakerUser.block().username and all, or is there a better way to implement these methods?

Currently, I'm doing something like this but it seems strange:

    private var _user: BakerUser? = null
    private var user: BakerUser? = null
    get() {
        if(_user == null){
            _user = bakerUser.block()
        }
        return _user
    }

override fun getAuthorities(): MutableCollection<out GrantedAuthority> {
        return mutableSetOf(SimpleGrantedAuthority("USER"))
    }

    override fun getPassword(): String {
        return user!!.password!!
    }

CodePudding user response:

im not well versed at Kotlin, but i can tell you that you should not pass in a Monoto the UserDetails object.

A Mono<T> is sort of like a future/promise. Which means that there is nothing in it. So if you want something out of it, you either block which means we wait, until there is something in it, or we subscribe, which basically means we wait async until there is something in it. Which can be bad. Think of it like starting a job on the side. What happens if you start a job and you quit the program, well the job would not be executed.

Or you do something threaded, and the program returns/exits, well main thread dies, all threads die, and nothing happend.

We usually in the reactive world talk about Publishers and Consumers. So a Flux/Mono is a Publisher and you then declare a pipelinefor what to happen when something is resolved. And to kick off the process the consumerneeds to subscribe to the producer.

Usually in a server world, this means that the webpage, that does the request, is the consumer and it subscribes to the server which in this case is the publisher.

So what im getting at, is that you, should almost never subscribe in your application, unless, your application is the one that starts the consumption. For instance you have a cron job in your server that consumes another server etc.

lets look at your problem:

You have not posted your code so im going to do some guesswork here, but im guessing you are getting a user from a database.

public Mono<BakerUserDetails> loadUserByUsername(String username) {

    Mono<user> user = userRepository.findByUsername(username);
    // Here we declare our pipline, flatMap will map one object to another async
    Mono<BakerUserDetails> bakerUser = user.flatMap(user -> Mono.just(new BakerUserDetails(user));
    return bakerUser;
}

i wrote this without a compiler from the top of my head.

So dont pass in the Mono<T> do your transformations using different operators like map or flatMap etc. And dont subscribe in your application unless your server is the final consumer.

CodePudding user response:

This seems to work:

private var user: BakerUser? = null
    init {
        bakerUser.subscribe {
            if (it != null) {
                user = it
            } else {
                throw IllegalArgumentException("User is null")
            }
        }
    }

Test that is passing:

internal class BakerUserDetailsTest {
    @Test
    fun testGettersWithMono(){
        val bakerUser = BakerUser(
            "asdfasdf",
            "user",
        "password",
            true,
            false,
            false,
            Instant.now(),
            emptyList(),
            emptyMap()
        )
        val bakerUserDetails = BakerUserDetails(Mono.just(bakerUser))
        assertEquals(bakerUser.username, bakerUserDetails.username)
    }
}
  • Related