Kotlin generics with Spring Events - spring

I am trying to setup an event driven architecture on my Spring Application. I have an interface that blueprints all my processes and I have multiple #Components that inherited by that interface. The below is a simplification of my code:
interface Process<T: Any> {
val type: String
val name: String
fun complete(data: T)
fun cancel(data:T)
}
#Component
class MyProcess(prival val service) : Process<MyProcessDTO>
...
where
class MyProcessDTO {
val id: string
val isComplex: Boolean
}
The thing is that I have more than 10 beans than inherit from Process interface and each of them has a different *DTO type.
My idea would be to make a generic event from the given Process interface and publish the derived event types within each #Component. See my idea below :
class ProcessEvent<T: Process<T>> {
}
#Component
class MyProcess(private val service, val publisher: ApplicationEventPublisher) : Process<MyProcessDTO> {
fun publishEvent(myProcessEvent: ProcessEvent<MyProcessDTO>) {
this.service.makeRequest()
this.publisher.publish(myProcessEvent)
}
}
In that case MyProcess should publish a ProcessEvent with MyProcessDTO type.
Please note that the above is just an approximation of my goal.
Thanks

Related

spring junit test data preparrer class access repositories trough field

I have simple class annotated with #Component and injected repositories like
#Component
class TestsDataPreparer(
private val carRepository: CarRepository
) {
fun withCar(builder: Car.() -> Unit = {}): Car {
return carRepository.save(
Car(
name = builder.name!!
)
)
}
}
which is clear..
But i wonder if it would be ok to do something like this, or if it is considered as anti-pattern.
#Component
class TestsDataPreparer(
val carRepository: CarRepository
) {
fun withCar(builder: Car.() -> Unit = {}): Car {
return carRepository.save(
Car(
name = builder.name!!
)
)
}
}
#Test
fun testCar() {
testsDataPreparer.withCar{this.name="Super-cool-car!"}
assertThat(testsDataPreparer.carRepository.findAll()).hasSize(1)
}
So the question is if it is okay to not inject repository in test class itself, but reuse it from TestsDataPreparer class
Yes, making an originally private field public just for testing can be considered an antipattern. Instead, you can create a CarRepository instance and then pass it to TestsDataPreparer when you create it. But for unit testing, you don't actually need that, you can use a mock and verify that the correct method was called (CarRepository.save).

Kotlin, Spring Boot, JPA - take value of a Generic Enum (E.valueOf(stringValue))

Background
I'm developing a Spring Boot application and I'm using Kotlin, IntelliJ and Gradle (Groovy). I have some enum class in my code and I need to persist them (with JPA). I used a simple global converter.
// Sample Enum
enum class Policy {
PUBLIC,
INVITE_ONLY
}
// Sample Converter
#Converter(autoApply = true)
class PolicyConverter : AttributeConverter<Policy, String> {
override fun convertToDatabaseColumn(attribute: Policy): String {
return attribute.name
}
override fun convertToEntityAttribute(dbData: String): Policy {
return Policy.valueOf(dbData.toUpperCase())
}
}
Problem
Since I have 5-6 enums and I hate duplicated code, I thought about a generic converter that should do the work for every given enum. I tried to code something, but nothing worked. This is what I was thinking about:
abstract class EnumConverter<E: Enum<E>> : AttributeConverter<E, String> {
override fun convertToDatabaseColumn(attribute: E): String {
return attribute.name
}
override fun convertToEntityAttribute(dbData: String): E {
return E.valueOf(dbData.toUpperCase())
}
}
In this way I can only extend from one abstract class every enum converter, like so:
#Converter(autoApply = true)
class PolicyConverter : EnumConverter<Policy>() {}
Problem with this code is that I have two errors:
E is red because: Type parameter 'E' cannot have or inherit a companion object, so it cannot be on the left hand side of dot
valueOf is red because: unresolved reference (there are like 150+ types of .valueOf).
As suggested from this I tried to use following function:
private inline fun <reified E : Enum<E>> getValue(string: String): E {
return enumValueOf(string.toUpperCase())
}
But when called from the .convertToEntityAttribute, the result is that "Cannot use 'E' as reified type parameter. Use a class instead."
Question
So the question is simple: how can I implement an easy and fast way to make one converter for all my enums, that all follows the same principle? I just need a return E.valueOf(<value>) function, but it's not present.
A simply workaround of this problem is to define an abstract method that every class will implement and it will return the correct type, given a string.
// Inside EnumConverter, the Generic Class
abstract class EnumConverter<E: Enum<E>> : AttributeConverter<E, String> {
abstract fun getValueFromString(string: String) : E
override fun convertToEntityAttribute(dbData: String): E {
return getValueFromString(dbData)
}
[...]
}
// Inside Policy Enum, implementing
class PolicyConverter : EnumConverter<Policy>() {
override fun getValueFromString(string: String): Policy {
return Policy.valueOf(string.toUpperCase())
}
}
But it's a workaround that I really dislike.

Autowired not working in Scala Spring Boot project

Taking into account the following example where I'm trying to use the Sample configuration bean within SampleStarter to start the service with the bean properly filled. The .scala file has SampleStarter.scala as name, with Sample being defined within that exact same file.
#SpringBootApplication
#Service
object SampleStarter {
#(Autowired #setter)
var sample: Sample = _ // always null, #Autowired not working?
def getInstance() = this
def main(args: Array[String]): Unit = {
SpringApplication.run(classOf[Sample], args: _*)
sample.run()
}
}
#Configuration
#ConfigurationProperties("sample")
#EnableConfigurationProperties
class Sample {
#BeanProperty
var myProperty: String = _
def run(): Unit = { // bean config seems OK, when debugging with #PostConstruct the 'myProperty' value is filled properly
print(myProperty)
}
}
Whenever I hit sample.run() after SpringApplication.run(classOf[Sample], args: _*), sample property is always null. I reckon this has something to do with object in Scala since all their members are static AFAIK. I took this SO question How to use Spring Autowired (or manually wired) in Scala object? as inspiration but still can't make my code to work.
Is there something wrong the way I'm instantiating the classes or is it something related to Scala?
EDIT
Following #Rob's answer, this is what I did trying to replicate his solution.
#SpringBootApplication
#Service
object SampleStarter {
#(Autowired #setter)
var sample: Sample = _
def getInstance() = this
def main(args: Array[String]): Unit = {
SpringApplication.run(classOf[Sample], args: _*)
sample.run()
}
}
#Configuration // declares this class a source of beans
#ConfigurationProperties("sample") // picks up from various config locations
#EnableConfigurationProperties // not certain this is necessary
class AppConfiguration {
#BeanProperty
var myProperty: String = _
#Bean
// Error -> Parameter 0 of constructor in myproject.impl.Sample required a bean of type 'java.lang.String' that could not be found.
def sample: Sample = new Sample(myProperty)
}
class Sample(#BeanProperty var myProperty: String) {
def run(): Unit = {
print(myProperty)
}
}
#Configuration declares a source file that is capable of providing #Beans. This is not what you want. You want/need to have Sample as a POJO class (POSO?) and then have another class "AppConfiguration" (say) annotated with #Configuration that creates an #Bean of type Sample
My scala is very rusty so this may not compile at all but the structure should be roughly correct.
#Configuration // declares this class a source of beans
#ConfigurationProperties("sample") // picks up from various config locations
#EnableConfigurationProperties // not certain this is necessary
class AppConfiguration {
#Bean
def getSample(#BeanProperty myProperty: String): Sample = new Sample(myProperty)
}
class Sample {
var myProperty: String
def Sample(property : String) = {
this.myProperty = myProperty
}
def run(): Unit = {
print(myProperty)
}
}
Now you have a Sample class as an #Bean and it can be #Autowired in where ever necessary.
#SpringBootApplication
#Service
object SampleStarter {
// note its typically better to inject variables into a constructor
// rather than directly into fields as the behaviour is more predictable
#Autowired
var sample: Sample
def getInstance() = this // not required ??
def main(args: Array[String]): Unit = {
SpringApplication.run(classOf[Sample], args: _*)
sample.run()
}
}
FWIW, you can start the SpringBoot application independently and have the logic for #Service entirely separate. An #Service is another type of #Bean and is made available to the rest of the SpringBootApplication.

Spring Kotlin DSL: get all beans of certain type

Suppose I have an interface Yoyo and different realizations of this interface:
interface Yoyo {
fun haha() {
println("hello world")
}
}
#Component class Yoyo1 : Yoyo
#Component class Yoyo2 : Yoyo
#Component class Yoyo3 : Yoyo
#Component class YoyoN : Yoyo
Now I would like to instantiate all beans and do some logic after the context has been initialized:
#SpringBootApplication
class YoyoApp
fun main(args: Array<String>) {
SpringApplicationBuilder()
.sources(YoyoApp::class.java)
.initializers(beans {
bean {
CommandLineRunner {
val y1 = ref<Yoyo1>()
val y2 = ref<Yoyo2>()
val y3 = ref<Yoyo3>()
val yN = ref<YoyoN>()
arrayOf(y1, y2, y3, yN).forEach { it.haha() }
}
}
})
.run(*args)
}
Instead of manually getting ref to all beans (which is rather tedious), I would like to do this:
val list = ref<List<Yoyo>>()
list.forEach { it.haha() }
However I get an exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.util.List<?>' available
I know I could do this instead, but I would like use the new Kotlin DSL instead:
#Component
class Hoho : CommandLineRunner {
#Autowired
lateinit var list: List<Yoyo>
override fun run(vararg args: String?) {
list.forEach { it.haha() }
}
}
Is it possible? Any ideas?
P.S. Here is the gist.
The context mentioned in the previous answer by #zsmb13 was left internal in favor of the provider<Any>() function (starting with Spring 5.1.1). So in the end I ended up with the following:
interface Yoyo {
fun haha() {
println("hello world from: ${this.javaClass.canonicalName}")
}
}
#Component class Yoyo1 : Yoyo
#Component class Yoyo2 : Yoyo
#Component class Yoyo3 : Yoyo
#Component class YoyoN : Yoyo
#SpringBootApplication
class YoyoApp
fun main(args: Array<String>) {
SpringApplicationBuilder()
.sources(YoyoApp::class.java)
.initializers(beans {
bean {
CommandLineRunner {
val list = provider<Yoyo>().toList()
list.forEach { it.haha() }
}
}
})
.run(*args)
}
The ref function used in the DSL can be found here in the source of the framework. There is no equivalent for getting all beans of a type, but you could add your own extension to the BeanDefinitionDsl class to do this:
inline fun <reified T : Any> BeanDefinitionDsl.refAll() : Map<String, T> {
return context.getBeansOfType(T::class.java)
}
Only problem is that the context required for this is internal in the currently released version of the framework. This commit from 8 days ago makes it publicly available "for advanced use-cases", but there hasn't been a new release of the framework since, so it's not available yet.
(The same commit also makes the class to extend directly the BeanDefinitionDsl class and not BeanDefinitionDsl.BeanDefinitionContext.)
Conclusion: you'll probably have to wait for the next release that includes the commit mentioned above, and then you'll be able to create this extension for yourself. I've also submitted a pull request in hopes that this could be included in the framework itself.

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