1

In my application, I use @LastModifiedBy and it's working perfectly until I decided to use co-routines for performances mather.

Now, since the calls to my repositories are inside a Future and executed in a coroutine, fields tagged with @LastModifiedBy or @LastModifiedDate are no more filled when they are persisted.

When I'm debugging and stop inside my code executed into a coroutine, SecurityContextHolder is empty whereas it's filled outside the coroutine.

My code looks like that :

@RestController
class Controller( val service : MyService){
( ... )

    @PutMapping(...)
        fun saveStuff( val allStuff : List<Stuff>) : String{
         return service.saveStuff(allStuff )
    }

}

and for the coroutine's part :

@Service
class MyService( val repo: MyRepository){

    fun saveStuff( allStuff: List<Stuff>){
        val deferred: List<Deferred<Unit>> =
            allStuff.forEach{
                  GlobalScope.async {  repo.save(stuff)}
            }

        runBlocking {
            val count = deferred.map { it.await() }.count()
            log.info(" all the $count future(s) have finish ")
        }
    }
}
@Entity
data class Stuff(
     @CreatedDate
     var creationDate: Timestamp? = null,

     @LastModifiedBy
     var lastModificationBy: String? = null,

     @LastModifiedDate
     var lastModificationDate: Timestamp? = null
)
adrien olivier
  • 125
  • 1
  • 10
  • 1
    https://stackoverflow.com/questions/46227462/how-to-use-code-that-relies-on-threadlocal-with-kotlin-coroutines/46227463 – sidgate May 18 '20 at 17:03
  • 1
    https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/coroutine-context-and-dispatchers.md#thread-local-data – sidgate May 18 '20 at 17:04

1 Answers1

0

I manage to do it like this :

@Service
class MyService( val repo: MyRepository){
    fun saveStuff( allStuff: List<Stuff>){
        val deferred: List<Deferred<Unit>> =
            allStuff.forEach{
              GlobalScope.async {  repo.save(stuff)}
        }

        val threadLocal = ThreadLocal<SecurityContext>() // declare thread-local variable
        //pass security context to all thread which will execute my code
        GlobalScope.launch(Dispatchers.Default + threadLocal.asContextElement(SecurityContextHolder.getContext())) {
            //set up context for spring
            SecurityContextHolder.setContext(threadLocal.get())
            val count = deferred.map { it.await() }.count()
            log.info(" all the $count future(s) have finish ")
        }
    }
}

And it works like a charm :) Thanks @sidgate for the links !

adrien olivier
  • 125
  • 1
  • 10