How can I configure a JAX-RS application in Kotlin with resources having other dependencies injected - kotlin

I use JAX-RS to build a REST API. To bootstrap all resources I have a overridden an "Application":
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationScoped
#ApplicationPath("/")
open class ApiConfig : Application() {
override fun getSingletons(): MutableSet<Any> {
println("----- init jaxrs -----")
return mutableSetOf(EchoResource())
}
}
As you can see I register the EchoResource() with brackets. It does not work when I use EchoResource::class.
My Problem is, that I want to inject some service into EchoResource:
import dev.elysion.mail.smtp.MailService
import javax.enterprise.context.RequestScoped
import javax.inject.Inject
import javax.ws.rs.GET
import javax.ws.rs.Path
#Path("/echo")
#RequestScoped
class EchoResource #Inject constructor(private val mailService: MailService) {
#GET
fun getHello(): String {
return "Hello World"
}
}
When I add the constructor I get an error in API Config saying that I do not pass a parameter for MailService.
In Java I would register the resource with EchoResource.class which does not care about any parameters.
How can I achieve the same with Kotlin?

It works if getClasses() is used instead of getSingletons():
import javax.enterprise.context.ApplicationScoped
import javax.ws.rs.ApplicationPath
import javax.ws.rs.core.Application
#ApplicationScoped
#ApplicationPath("/")
open class ApiConfig : Application() {
override fun getClasses(): MutableSet<Class<*>> {
return mutableSetOf(EchoResource::class.java)
}
}
Furthermore, all resources and all of their methods need to be open (not final) in order to be proxyable for CDI.

Related

How do I provide my own DateTimeProvider with Spring Data

I am trying to provide a transactionally consistent set of datetimes
import org.springframework.beans.factory.ObjectFactory
import org.springframework.beans.factory.config.BeanFactoryPostProcessor
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Scope
import org.springframework.data.auditing.DateTimeProvider
import org.springframework.transaction.support.SimpleTransactionScope
import java.time.Instant
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.util.Optional
#Configuration
open class TransactionScopeTimeConfiguration {
#Bean
open fun transactionBFPP(): BeanFactoryPostProcessor =
BeanFactoryPostProcessor { it.registerScope("transaction", SimpleTransactionScope()) }
#Bean
#Scope("transaction")
open fun nowInstant(): Instant = Instant.now()
#Bean
#Scope("transaction")
open fun nowOffsetDateTime(nowInstant: Instant): OffsetDateTime = nowInstant.atOffset(ZoneOffset.UTC)
#Bean
open fun transactionDateTimeProvider(factory: ObjectFactory<OffsetDateTime>): DateTimeProvider =
DateTimeProvider { Optional.of(factory.`object`) }
}
However, when debugging my test I note that the function inside of transactionDateTimeProvider is never called (the creation of the Bean is)
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
#DataJpaTest
#Import(TransactionScopeTimeConfiguration)
internal open class ExceptionDaoTest {
#Test
fun save(#Autowired exceptionDao: ExceptionJpaDao) {
val toCreate = ExceptionEntity("someid")
val saved = exceptionDao.save(toCreate)
assertThat(saved).isInstanceOf(ExceptionEntity::class.java)
.extracting({ it.id }, { it.businessDivisionId })
.containsExactly(toCreate.id, "someid")
.doesNotContainNull()
assertThat(saved.lastModifiedOn).isSameAs(saved.createdOn)
}
}
The test actually passes, but I haven't actually exercised this functionality in this test. It's important that any other datetimes are transactionally consistent with the audit traits.

Ktor with Koin DI can't inject, missing clazz

I am setting up Koin DI on Ktor in this way:
https://insert-koin.io/docs/reference/koin-ktor/ktor/
But I am getting an error: No value passed for parameter 'clazz'
My implementation looks like this:
import io.ktor.application.*
import io.ktor.routing.*
import org.koin.java.KoinJavaComponent.inject
import services.SomeService
fun Application.registerPropertyRoutes() {
routing {
bodySectionRoute() // add more routes for Property page here
}
}
fun Route.bodySectionRoute() {
val someService by inject<SomeService>()
get("/bodySection") {
// business logic can be connected here
}
}
Any ideas what I am missing?
Update:
You need to import org.koin.ktor.ext.inject

How to call method Reference in map

I create a class ApUtils . Define two methods in that class . I want
use that two methods in my service class inside the map function by
the help of method reference ,But I am getting un excepted error while
calling these two methods
service.kt
package com.nilmani.reactivespringdemo.services
import com.nilmani.reactivespringdemo.Utils.AppUtils
import com.nilmani.reactivespringdemo.dto.ProductDto
import com.nilmani.reactivespringdemo.entity.Product
import com.nilmani.reactivespringdemo.repository.ProductRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
#Service
class ProductService {
#Autowired
private lateinit var productRepository: ProductRepository
fun getProduct():Flux<ProductDto>{
return productRepository.findAll().map(obj: AppUtils -> obj.entityToDto(product))
}
fun getProduct(id:String):Mono<ProductDto>{
return productRepository.findById(id).map{ obj: AppUtils, product: Product -> obj.entityToDto(product) }
}
}
AppUtils.kt
package com.nilmani.reactivespringdemo.Utils
import com.nilmani.reactivespringdemo.dto.ProductDto
import com.nilmani.reactivespringdemo.entity.Product
import org.springframework.beans.BeanUtils
class AppUtils {
fun entityToDto(product: Product):ProductDto{
val productDto = ProductDto()
BeanUtils.copyProperties(product,productDto)
return productDto
}
fun dtoToEntity(productDto: ProductDto):Product{
val product = Product()
BeanUtils.copyProperties(productDto,product)
return product
}
}
How to call these two function in my service class using method reference
If you change AppUtils to a singleton using Kotlin's object keyword:
object AppUtils { ... }
then you can use method references like so:
fun getProduct(id:String) = productRepository.findById(id).map(AppUtils::entityToDto)

What is the reason for an UninitializedPropertyAccessException in #SpringBootTest with MockK and Kotlin?

I am trying to use MockK 1.10.2 with Kotlin 1.4.10 and #SpringBootTest (Spring Boot 2.2.2.RELEASE) and I can't get it run because of a
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2020-11-05 15:00:37.878 WARN --- [ main] i.m.p.j.t.InliningClassTransformer : Failed to transform class java/lang/Object
java.lang.IllegalArgumentException: Unsupported class file major version 59
at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:195)
at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:176)
at net.bytebuddy.jar.asm.ClassReader.<init>(ClassReader.java:162)
at net.bytebuddy.utility.OpenedClassReader.of(OpenedClassReader.java:86)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:3394)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1933)
at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:217)
at net.bytebuddy.dynamic.scaffold.inline.AbstractInliningDynamicTypeBuilder.make(AbstractInliningDynamicTypeBuilder.java:120)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3404)
at io.mockk.proxy.jvm.transformation.InliningClassTransformer.transform(InliningClassTransformer.kt:77)
at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:246)
at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:167)
at io.mockk.proxy.jvm.transformation.JvmInlineInstrumentation.retransform(JvmInlineInstrumentation.kt:28)
at io.mockk.proxy.common.transformation.RetransformInlineInstrumnetation$execute$1.invoke(RetransformInlineInstrumnetation.kt:19)
at io.mockk.proxy.common.transformation.RetransformInlineInstrumnetation$execute$1.invoke(RetransformInlineInstrumnetation.kt:6)
at io.mockk.proxy.common.transformation.ClassTransformationSpecMap.applyTransformation(ClassTransformationSpecMap.kt:41)
at io.mockk.proxy.common.transformation.RetransformInlineInstrumnetation.execute(RetransformInlineInstrumnetation.kt:16)
at io.mockk.proxy.jvm.ProxyMaker.inline(ProxyMaker.kt:88)
at io.mockk.proxy.jvm.ProxyMaker.proxy(ProxyMaker.kt:30)
kotlin.UninitializedPropertyAccessException: lateinit property taService has not been initialized
exception. (The same happens if I use tmpService. It doesn't matter if the imported service is in the same or a different package as the test class.)
As you can see from the code of my test class I already played around a lot but without any success so far.
package com.xxx.emr.tm
import com.xxx.data.emr.model.PatientAssignment
import com.xxx.data.tm.TMPService
import com.xxx.data.tm.model.MyType
import com.xxx.data.tm.model.MyTypeCode
import com.xxx.data.tm.model.UserAction
import com.xxx.emr.service.model.AuthenticatedUser
import io.mockk.every
import io.mockk.impl.annotations.InjectMockKs
import io.mockk.impl.annotations.SpyK
import io.mockk.junit5.MockKExtension
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.verify
import org.junit.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
#ExtendWith(SpringExtension::class, MockKExtension::class)
#SpringBootTest // (classes = [com.xxx.emr.service.MyServiceSpringBoot::class])
// #RunWith(SpringJUnit4ClassRunner::class)
// #ActiveProfiles("test")
// #EnableAutoConfiguration
// #AutoConfigureMockMvc
class MyTest {
// (
// val tmpService: TMPService,
// val taService: TaService
// )
// #InjectMockKs
// #Autowired
// #SpyK
#MockK
private lateinit var tmpService: TMPService
#InjectMockKs
#Autowired
private lateinit var taService: TaService
#Test
fun assignAToB() {
MockKAnnotations.init(this, relaxUnitFun = true)
val vId = "xxx"
val authUser: AuthenticatedUser = AuthenticatedUser.mockedUser()
val userAction = UserAction(
userId = "user_id",
actionCode = ActionType.GET_IT,
sessionId = "sessionId"
)
val xxx = MyType(
MyTypeCode.ABC.value,
MyTypeCode.ABC.name,
)
val actionResult = UserActionResult(hashMapOf("success" to true))
// every { tmpService.getXxx(any()) } returns xxx
verify( exactly = 1 ) {
tmpService.doSomething(any(), any(), any())
}
verify( exactly = 1 ) {
taService.dowhatYouWant(„vId", authUser, userAction)
}
}
Autowiring does not work...so what is my fault? How can I make the test run at all?
I think you should initialize your mocks first, by adding this in your test file for example:
#org.springframework.test.context.event.annotation.BeforeTestMethod
fun initMocks() {
org.mockito.MockitoAnnotations.initMocks(this)
}
I created a post on another thread about how to setup unit testing, comparable with your requirements:
https://stackoverflow.com/a/64669499/7919904

JUnit5 SpringBootTest NullPointerException when testing Service

I am following a tutorial for unit testing a Service, and I am attempting to use JUnit5 instead of JUnit4 (which is what the tutorial uses). The test works fine when I follow the tutorial exactly with JUnit4, but I get a NullPointerException with JUnit5.
The repository:
public interface CourseRepo extends CrudRepository<Course, Long> {}
The service:
#Service
public class CourseService {
private final CourseRepo courseRepo;
public CourseService(CourseRepo courseRepo) {
this.courseRepo = courseRepo;
}
public Course save(Course course) {
Course newCourse = courseRepo.save(course);
return newCourse;
}
}
The test:
package com.elovell.blackboard.service;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import com.elovell.blackboard.domain.Course;
import com.elovell.blackboard.repository.CourseRepo;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
#ExtendWith(MockitoExtension.class)
#SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class CourseServiceTest {
#Mock
public CourseRepo courseRepo;
#InjectMocks
public CourseService courseService;
#BeforeEach
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testSaveCourse() {
Course mockCourse = new Course();
mockCourse.setPk1(1L);
mockCourse.setCourseId("ENGL101");
when(courseRepo.save(any(Course.class))).thenReturn(mockCourse);
// save(null) should still return mockCourse
Course newCourse = courseService.save(null);
assertEquals("ENGL101", newCourse.getCourseId());
}
}
Here is the first few lines of the Exception:
java.lang.NullPointerException
at com.elovell.blackboard.service.CourseServiceTest.testSaveCourse(CourseServiceTest.java:44)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
The test and dependencies portion of my build file:
test {
useJUnitPlatform {
includeEngines 'junit-jupiter'
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.1.0")
testCompile 'org.mockito:mockito-junit-jupiter:2.23.4'
}
This looks like you trying to mix an integration test with unit tests.
When you use the #SpringBootTest annotation then SpringExtension for Junit5 starts an application context before running your test, and to make mocks in tests you need to use #MockBean annotation instead of #Mock & #InjectMocks. This is a different scope of tests.
As I see, you need just a unit test and you can remove the SpringBootTest annotation.
By the way, I think you got NPE because of you any(Course.class) in when(courseRepo....
Try to use isNull() or any() (without specification concrete class).