Spring + Kotlin - Is it possible to detect certain functions calls? - spring

I've just started using Spring and Kotlin and wanted to ask is it possible for my app to detect certain function calls?
For example, say I have:
fun getSuccessMessage() : String {
return "great"
}
Then in my app runner I call that:
#Component
class AppRunner: CommandLineRunner {
#Throws(Exception::class)
override fun run(vararg args: String) {
getSuccessMessage()
}
}
Can I have another function thats listening and acts whenever its called:
fun doSomethingWhenSuccessCalled() {
// I'm imaging some magic Spring annotation where I can say
//something like #ListeningTo("getSuccessMessage")
}

No, it is not possible.
To read more about listener of method invoke look at:
Listener on Method.invoke java

Related

EnableBinding, Output, Input deprecated Since version of 3.1 of Spring Cloud Stream

Since version 3.1 the major API for working with queues is deprecated.
In the class comment it says:
Deprecated
as of 3.1 in favor of functional programming model
I searched a lot in the web for a solution but didn't find a solid E2E explanation on how I should migrate.
Looking for examples for:
read from queue
write to queue
If there are a few ways to do that (as I saw in web) I'd be glad for an explanation and the typical use case for each option as well.
I'm assuming you are already familiar with the main concepts, and will focus on the migration.
I'm using kotlin for the demo code, to reduce verbosity
First, some references which may help:
Here is the initial relevant doc: link
This is an explanation for the naming scheme in the new functional format: link
This is a more detailed explanation with some more advanced scenarios: link
TL;DR
Instead of working with annotation-based configuration, spring now uses detected beans of Consumer/Function/Supplier to define your streams for you.
Input/Consumer
Whereas before you had code looking like this:
interface BindableGradesChannel {
#Input
fun gradesChannel(): SubscribableChannel
companion object {
const val INPUT = "gradesChannel"
}
}
and the usage was similar to:
#Service
#EnableBinding(BindableGradesChannel::class)
class GradesListener {
private val log = LoggerFactory.getLogger(GradesListener::class.java)
#StreamListener(BindableScoresChannel.INPUT)
fun listen(grade: Grade) {
log.info("Received $grade")
// do something
}
}
now the entire definition is irrelevant, and can be done like so:
#Service
class GradesListener {
private val log = LoggerFactory.getLogger(GradesListener::class.java)
#Bean
fun gradesChannel(): Consumer<Grade> {
return Consumer { listen(grade = it) }
}
fun listen(grade: Grade) {
log.info("Received $grade")
// do something
}
}
notice how the Consumer bean replaced the #StreamListener and the #Input.
Regarding the configuration, if before in order to configure you had an application.yml looking like so:
spring:
cloud:
stream:
bindings:
gradesChannel:
destination: GradesExchange
group: grades-updates
consumer:
concurrency: 10
max-attempts: 3
now it should be like so:
spring:
cloud:
stream:
bindings:
gradesChannel-in-0:
destination: GradesExchange
group: grades-updates
consumer:
concurrency: 10
max-attempts: 3
notice how gradesChannel was replaced by gradesChannel-in-0 - to understand the full naming convention please see the naming convention link at the top.
Some details:
If you have more than one such bean in your application, you need to define the spring.cloud.function.definition property.
You have the option to give your channels custom names, so if you'd like to continue using gradesChannel you can set spring.cloud.stream.function.bindings.gradesChannel-in-0=gradesChannel and use everywhere in the configuration gradesChannel.
Output/Supplier
The concept here is similar, you replace config and code looking like this:
interface BindableStudentsChannel {
#Output
fun studentsChannel(): MessageChannel
}
and
#Service
#EnableBinding(BindableStudentsChannel::class)
class StudentsQueueWriter(private val studentsChannel: BindableStudentsChannel) {
fun publish(message: Message<Student>) {
studentsChannel.studentsChannel().send(message)
}
}
can now be replaced by:
#Service
class StudentsQueueWriter {
#Bean
fun studentsChannel(): Supplier<Student> {
return Supplier { Student("Adam") }
}
}
As you can see, we have a major difference - when is it called and by who?
Before we could trigger it manually, but now it is triggered by spring, every second (by default). This is fine for use cases such as when you need to publish a sensor data every second, but this is not good when you want to send the message on an event. Besides using Function for whatever reason, spring offers 2 alternatives:
StreamBridge - link
Using StreamBridge you can. define the target explicitly like so:
#Service
class StudentsQueueWriter(private val streamBridge: StreamBridge) {
fun publish(message: Message<Student>) {
streamBridge.send("studentsChannel-out-0", message)
}
}
This way you don't define the target channel as a bean, but you can still send the message. The downside is that you have some explicit configuration in your class.
Reactor API - link
The other way is to use some kind of reactive mechanism such as Sinks.Many, and to return it. Using this your code will look similar to:
#Service
class StudentsQueueWriter {
val students: Sinks.Many<String> = Sinks.many().multicast().onBackpressureBuffer()
#Bean
fun studentsChannel(): Supplier<Flux<Student>> {
return Supplier { students.asFlux() }
}
}
and the usage may be similar to:
class MyClass(val studentsQueueWriter: StudentsQueueWriter) {
fun newStudent() {
studentsQueueWriter.students.tryEmitNext(Student("Adam"))
}
}

How to use kotlin coroutines with reactive spring data

I am trying to migrate some project from Spring Reactor to kotlin coroutines. I have some controller based on spring webflux like that:
#RestController
class Controller(val productRepository: ProductsRepository) {
#GetMapping("/product")
fun find(#RequestParam id: String): Mono<Product> {
return productRepository.findById(id)
}
}
This controller uses reactive spring data repository:
#Repository
interface ProductsRepository : ReactiveMongoRepository<Product, String>
According to this official documentation - https://docs.spring.io/spring/docs/5.2.0.M1/spring-framework-reference/languages.html#how-reactive-translates-to-coroutines, my function find in controller should be translated to suspend fun and this function should return an instance of Product class instead of reactive Mono wrapper of Product. Something like that:
#RestController
class Controller(val productRepository: ProductsRepository) {
#GetMapping("/product")
suspend fun find(#RequestParam id: String): Product {
return productRepository.findById(id)
}
}
But my productRepository deals with Mono and Flux, not suspended functions. How should I use spring data abstraction properly in that case?
This can be achieved with the useful kotlinx-coroutines-reactor helper library which provides useful extensions methods for project reactors Publisher to help between converting Mono or Flux to kotlin coroutines.
First add a dependency on
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-reactor</artifactId>
</dependency>
(if your using spring-boot you do not have to specify a version as it manages it for you)
You can now use kotlinx.coroutines.reactive.awaitFirstOrNull to convert a Mono<Product> to Product? and 'await' the result.
#RestController
class Controller(val productRepository: ProductsRepository) {
#GetMapping("/product")
suspend fun find(#RequestParam id: String): Product? {
return productRepository.findById(id).awaitFirstOrNull()
}
}

Does Spring-Boot handle Kotlin coroutines apart from WebFlux context?

We are trying to use Kotlin coroutines for asynchronous processing inside Spring-Boot backend.
The problem is that it doesn't seem to support it well (At least standard Spring MVC).
Basically, if we have a function that does asynchronous logic:
fun fetchUsersAsync(): Deferred<Users> {
return GlobalScope.async {
...
}
}
and this function is used with await at some point in service, which requires to put suspend annotation in a calling service function:
#Service
class MyService {
suspend fun processUsers(): Users {
return fetchUsersAsync().await()
}
}
Unfortunately it is not possible, and the only reference for suspend functionality in service was connected with WebFlux.
Has anyone faced the same situation? Thanks.
If you want to call await() without declaring a suspend function, wrap it inside a coroutine builder, like this:
#Service
class MyService {
fun processUsers(): Users {
return runBlocking { fetchUsersAsync().await() }
}
}

Convert Spring Cloud Stream to use reactive cloud function

Currently I have Spring Boot application which is something like this.
#Component
#EnableBinding(Source::class)
class EmailMessageProducer(private val source: Source) {
suspend fun send(textMessage: TextMessage) {
source.output().send(
MessageBuilder.withPayload(textMessage).setHeader("service", "test").build()
)
}
}
I would like to use Spring Cloud Function here using reactive pattern.
Furthermore, is my current solution non blocking? I am asking this because this is my first time using Kotlin coroutine in this context.
Java solution works for me as well since I am just trying to understand the concept here.
What you're looking for is a reactive Supplier (e.g., Supplier<Flux>).
In your case it would look something like this:
#SpringBootApplication
public class SomeApplication {
#Bean
public Supplier<Flux<Message<String>>> messageProducer() {
return () -> Flux.just(MessageBuilder.withPayload(textMessage).setHeader("service", "test").build());
}
}
Provide spring.cloud.function.definition=messageProducer property and this is pretty much it.
Obviously the above example produced a finite stream with a single item, but feel free to modify the returned flux. In fact we discuss this in more details here.

Spring framework and java like Object collectors In Scala

In Spring framework and Java world, there is an interesting object collector pattern that I use.
For example consider below -
public interface Calculator {
SomeOutput calculate(SomeInput input);
}
#Component
public class CalImpl1 implements Calculator {
public SomeOutput calculate(SomeInput input){
//some implementation
}
}
#Component
public class CalImpl2 implements Calculator {
public SomeOutput calculate(SomeInput input){
//some implementation
}
}
Now this can easily injected in another class using Spring DI
#Component
public class Main {
//This line collects all to implementors of this and set it here.
#Autowired
public List<Calculator> calculators;
//other methods
}
Now problem is I am not sure how same thing can be achieved in scala. I have done some search and found cake pattern (http://loicdescotte.github.io/posts/scala-di/) used in scala but that didn't seem to achieve same thing as object collectors like above. I also want to follow open close principle which I think gets violated in cake pattern but using object collectors I can easily achieve it.
is there a way achieve same object collectors like implementation in scala?
There are templates in lighbend activator that illustration using spring DI on Play, Akka and Scala applications. Please see this: https://www.lightbend.com/activator/templates#filter:spring
I haven't used Spring as DI, I usually use Guice (explicitly used because it's default on play framework 2) and Implicits parameters both as a compilation DI.
Sample:
class B
class X(x: Int)(implicit c: B)
//DI - mostly define in main method/application
implicit val c: B = new B
val x = new X(2)
Explicitly using java.util.List worked for me. This is not the prettiest solution but it shows that it basically works. Haven't tried that but implementing a corresponding PropertyEditor you could stick with the Scala types.
trait Calculator {
def calculate(input: SomeInput) : SomeOutput
}
#Component
class CalImpl1 extends Calculator {
override def calculate(input: SomeInput): SomeOutput = ...
}
#Component
class CalImpl2 extends Calculator {
override def calculate(input: SomeInput): SomeOutput = ...
}
#Component
class Main #Autowired()(calculators: java.util.List[Calculator]) {
// or inject field if constructor injection is not desired
// #Autowired
// var calculators: java.util.List[Calculator] = _
}
object Main {
def main(args: Array[String]) = {
val ctx = new AnnotationConfigApplicationContext("your package name here")
val main = ctx.getBean(classOf[Main])
// calculators should now be wired in the returned instance
}
}

Resources