How to call method Reference in map - kotlin

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)

Related

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

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.

How to pass current ClassLoader to KotlinToJVMBytecodeCompiler for dynamic (runtime) compilation kotlin code programmatically?

I created simple utility for runtime compilation kotlin code:
package com.example
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.com.intellij.openapi.Disposable
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.JvmTarget
import java.io.File
import kotlin.script.experimental.jvm.util.KotlinJars
class KotlinDynamicCompiler {
fun compileScript(moduleName: String,
sourcePath: String,
saveClassesDir: File
): GenerationState {
val stubDisposable = StubDisposable();
val configuration = CompilerConfiguration()
configuration.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, PrintingMessageCollector(System.out, MessageRenderer.PLAIN_FULL_PATHS, true))
configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, saveClassesDir)
configuration.put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
configuration.addKotlinSourceRoot(sourcePath)
configuration.addJvmClasspathRoots(listOf(KotlinJars.stdlib))
val env = KotlinCoreEnvironment.createForProduction(stubDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
return KotlinToJVMBytecodeCompiler.analyzeAndGenerate(env)!!;
}
inner class StubDisposable : Disposable {
#Volatile
var isDisposed: Boolean = false
private set
override fun dispose() {
isDisposed = true
}
};
}
And it works for code as
package com.example.kt
class SimpleClass(val str:String){
fun test(){
}
}
class UsedSimpleClass(val simpleClass: SimpleClass, val file: java.io.File) {
}
But it not works if I want to use no-base package classes as:
package com.example.kt
import com.example.pojo.TestPojo //class have in project that call runtime compilation
class SimpleClass(val str:TestPojo){
}
or:
package com.example.kt
import com.fasterxml.jackson.databind.ObjectMapper //class have in project classpath where called runtime compilation
class SimpleClass(val str:ObjectMapper){
}
How to pass current ClassLoader to KotlinToJVMBytecodeCompiler for dynamic (runtime) compilation kotlin code programmatically?
More details:
Test project on github with crashed test: https://github.com/nekkiy/dynamic-kotlin
Cause:
We need use codegeneration and would like to test generated code. But I don't understand how to pass current classes environment.
Thanks for attention.
Solution:
I have used method fun classpathFromClassloader(currentClassLoader: ClassLoader, unpackJarCollections: Boolean = false): List<File>? from kotlin.script.experimental.jvm.util.jvmClasspathUtil.kt and it works.
Result dynamic compiller:
package com.example
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoots
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.com.intellij.openapi.Disposable
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.JvmTarget
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream
import kotlin.script.experimental.jvm.util.KotlinJars
import kotlin.script.experimental.jvm.util.classpathFromClassloader
class KotlinDynamicCompiler {
fun compileModule(moduleName: String,
sourcePath: List<String>,
saveClassesDir: File,
classLoader: ClassLoader? = null,
forcedAddKotlinStd: Boolean = true
): GenerationState {
val stubDisposable = StubDisposable();
val configuration = CompilerConfiguration()
configuration.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
val baos = ByteArrayOutputStream()
val ps: PrintStream = PrintStream(baos)
configuration.put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, PrintingMessageCollector(ps, MessageRenderer.PLAIN_FULL_PATHS, true))
configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, saveClassesDir)
// configuration.put(JVMConfigurationKeys.RETAIN_OUTPUT_IN_MEMORY, true)
configuration.put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.JVM_1_8)
val classPath = mutableSetOf<File>()
if (classLoader != null) {
classPath.addAll(classpathFromClassloader(classLoader)!!);
}
if (forcedAddKotlinStd) {
classPath.add(KotlinJars.stdlib)
}
configuration.addJvmClasspathRoots(classPath.toList())
configuration.addKotlinSourceRoots(sourcePath)
val env = KotlinCoreEnvironment.createForProduction(stubDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
val result = KotlinToJVMBytecodeCompiler.analyzeAndGenerate(env);
ps.flush();
if (result != null) {
return result
} else {
throw IllegalStateException("Compilation error. Details:\n$baos")
}
}
inner class StubDisposable : Disposable {
#Volatile
var isDisposed: Boolean = false
private set
override fun dispose() {
isDisposed = true
}
};
}
Note: This function is contained in experimental package.
P.S. I also updated github-project.

Problem converting Java class containing a static factory method to Kotlin

I have had no sucess trying to convert this to Kotlin and use it.
It works as Java, just not with my Kotlin (which I converted using IntelliJ Kotlin Plugin)
the problem appears to be this part
#PluginFactory
public static AnsiConsoleAppender createAppender(
I tried to add #JvmStatic and I get this error:
Unable to invoke factory method in class AnsiConsoleAppender for element AnsiConsoleAppender: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method AnsiConsoleAppender$Companion.AnsiConsoleAppender, parameter filter java.lang.reflect.InvocationTargetException
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.io.Serializable;
/**
* <h2>AnsiConsoleAppender</h2>
*
* <h3>notes</h3>
* <li>class name need not match the #Plugin name</li>
* <h3>more<h3/>
* <li>How to Create a Custom Appender in log4j2?</li>
*/
#Plugin(name="AnsiConsoleAppender", category="Core", elementType="appender", printObject=true)
public final class AnsiConsoleAppender extends AbstractAppender {
protected AnsiConsoleAppender(String name, Filter filter,
Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
}
// The append method is where the appender does the work.
// Given a log event, you are free to do with it what you want.
// This example demonstrates:
// 1. Concurrency: this method may be called by multiple threads concurrently
// 2. How to use layouts
// 3. Error handling
//#Override
public void append(LogEvent event) {
try {
final byte[] bytes = getLayout().toByteArray(event);
// output code goes here
} catch (Exception ex) {
if (!ignoreExceptions()) throw new AppenderLoggingException(ex);
}
}
// Your custom appender needs to declare a factory method
// annotated with `#PluginFactory`. Log4j will parse the configuration
// and call this factory method to construct an appender instance with
// the configured attributes.
#PluginFactory
public static AnsiConsoleAppender createAppender(
#PluginAttribute("name") String name,
#PluginElement("Layout") Layout<? extends Serializable> layout,
#PluginElement("Filter") final Filter filter,
#PluginAttribute("otherAttribute") String otherAttribute) {
if (name == null) {
LOGGER.error("No name provided for AnsiConsoleAppenderImpl");
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
return new AnsiConsoleAppender(name, filter, layout, true);
}
}
import org.apache.logging.log4j.core.AbstractLifeCycle
import org.apache.logging.log4j.core.Filter
import org.apache.logging.log4j.core.Layout
import org.apache.logging.log4j.core.LogEvent
import org.apache.logging.log4j.core.appender.AbstractAppender
import org.apache.logging.log4j.core.appender.AppenderLoggingException
import org.apache.logging.log4j.core.config.plugins.Plugin
import org.apache.logging.log4j.core.config.plugins.PluginAttribute
import org.apache.logging.log4j.core.config.plugins.PluginElement
import org.apache.logging.log4j.core.config.plugins.PluginFactory
import org.apache.logging.log4j.core.layout.PatternLayout
import java.io.Serializable
#Plugin(name = "AnsiConsoleAppender", category = "Core", elementType = "appender", printObject = true)
class AnsiConsoleAppender /*protected*/ constructor(
name: String, filter: Filter,
layout: Layout<out Serializable>, ignoreExceptions: Boolean
) : AbstractAppender(name, filter, layout, ignoreExceptions) {
// The append method is where the appender does the work.
// Given a log event, you are free to do with it what you want.
// This example demonstrates:
// 1. Concurrency: this method may be called by multiple threads concurrently
// 2. How to use layouts
// 3. Error handling
//#Override
override fun append(event: LogEvent) {
try {
val bytes = layout.toByteArray(event)
//AnsiColor.out(String(bytes), ColorMaps.ASTERIKSY, null, true)
} catch (ex: Exception) {
if (!ignoreExceptions()) throw AppenderLoggingException(ex)
}
}
companion object {
// Your custom appender needs to declare a factory method
// annotated with `#PluginFactory`. Log4j will parse the configuration
// and call this factory method to construct an appender instance with
// the configured attributes.
#JvmStatic
#PluginFactory
fun createAppender(
#PluginAttribute("name") name: String?,
#PluginElement("Layout") layout: Layout<out Serializable>?,
#PluginElement("Filter") filter: Filter,
#PluginAttribute("otherAttribute") otherAttribute: String
): AnsiConsoleAppender? {
val lay = layout ?: PatternLayout.createDefaultLayout()
if (name == null) {
AbstractLifeCycle.LOGGER.error("No name provided for AnsiConsoleAppenderImpl")
return null
}
return AnsiConsoleAppender(name, filter, lay, true)
}
}
}
what am I missing?
The exception says filter is null.
So it should be nullable type.
companion object {
#PluginFactory
#JvmStatic
fun createAppender(
#PluginAttribute("name") name: String,
#PluginElement("Layout") layout: Layout<out Serializable>,
#PluginElement("Filter") filter: Filter?,
#PluginAttribute("otherAttribute") otherAttribute: String?
): AnsiConsoleAppender {
In Kotlin factory methods are usually defined in a companion object of a class, e.g.:
class AnsiConsoleAppender : AbstractAppender() {
companion object {
// add JvmStatic annotation if you want to call this method from Java file
#JvmStatic
#PluginFactory
fun createAppender(/*params*/): AnsiConsoleAppender {
//...
}
}
}
More on companion object

I can't generate a class using Kotlin processor

I'm implementing a processor to generate kotlin code using custom annotations. The problem is that I cannot find a way to relate the annotation to the field it was declared for, and I cannot find a way to understand if a field is of a nullable type. The processor doesn't succeed to generate the code because the getAnnotationsByType doesn't return the annotations for the current field (the list it's empty). Not even the order is good, fields are passed first and the annotations after all the fields.
package it.kfi.xml.binding.processor
import com.google.auto.service.AutoService
import com.squareup.kotlinpoet.*
import it.kfi.xml.binding.annotations.XmlClass
import it.kfi.xml.binding.annotations.XmlProperty
import java.io.File
import java.lang.reflect.Type
import javax.annotation.Nullable
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.Processor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.SourceVersion
import javax.lang.model.element.Element
import javax.lang.model.element.ElementKind
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement
import javax.lang.model.type.NullType
import javax.lang.model.type.TypeMirror
import javax.print.DocFlavor
import javax.tools.Diagnostic
import kotlin.reflect.KClass
import kotlin.reflect.full.createType
#AutoService(Processor::class)
class XmlBinder : AbstractProcessor() {
companion object {
const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated"
}
override fun getSupportedAnnotationTypes(): MutableSet<String> {
return mutableSetOf(XmlClass::class.java.name)
}
override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
override fun process(annotations: MutableSet<out TypeElement>?, roundEnv: RoundEnvironment): Boolean {
roundEnv.getElementsAnnotatedWith(XmlClass::class.java)
.forEach {
if (it.kind != ElementKind.CLASS) {
processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, "Only classes can be annotated")
return true
}
processClass(it)
}
return false
}
private fun processClass(element: Element) {
val className = element.simpleName.toString() + "Model"
val packageName = processingEnv.elementUtils.getPackageOf(element).toString()
val classBuilder = TypeSpec.classBuilder(className)
classBuilder.addModifiers(KModifier.PUBLIC)
val initFromXml = FunSpec.builder("initFromXml")
initFromXml.addModifiers(KModifier.PUBLIC)
initFromXml.addParameter(ParameterSpec.builder("xml", String::class).build())
val properties = element.enclosedElements
var x: Int = 1
//Look for elements annotated with XmlField and add those elements to the generated class
for (property in properties) {
val annotation = property.getAnnotationsByType(XmlProperty::class.java)
val v = 10
classBuilder.addProperty(PropertySpec.varBuilder(property.simpleName.toString(), String::class, KModifier.PUBLIC).initializer(v.toString()).build())
initFromXml.addStatement("this.${property.simpleName} = \"${v.toString()}\"")
}
classBuilder.addFunction(initFromXml.build())
val fileName = "kfi_generated_$className"
val file = FileSpec.builder(packageName, fileName).addType(classBuilder.build()).build()
val kaptKotlinGeneratedDir = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
file.writeTo(File(kaptKotlinGeneratedDir))
}
}
Can anyone help me found a way to relate annotations to their fields or properties ?

How mock Kotlin extension function in interface?

I have an extension function for interface like the following:
import javax.jms.ConnectionFactory
fun ConnectionFactory.foo() {
println("do some stuff")
}
How can I mock the function foo?
Please note, I have seen approaches for classes and objects in http://mockk.io/#extension-functions, but it does not work. I have tried this one:
import io.mockk.classMockk
import io.mockk.every
import org.junit.Test
import javax.jms.ConnectionFactory
class ExtensionFunctionTest {
#Test
fun mockExtensionFunction() {
val connectionFactory = classMockk(ConnectionFactory::class)
every { connectionFactory.foo() } returns println("do other stuff")
connectionFactory.foo()
}
}
It throws exception:
io.mockk.MockKException: Missing calls inside every { ... } block.
According to the documentation in case of module wide extension functions you need to staticMock "hidden" class created for an extension function.
Here is an example (assuming the file name is com/sample/extmockingtest/SampleTest.kt):
fun <T> Iterable<T>.foo(): String = "do some stuff"
class ExtensionFunctionTest {
#Test
fun mockExtensionFunction() {
val itMock = classMockk(Iterable::class);
staticMockk("com.sample.extmockingtest.SampleTestKt").use {
every {
itMock.foo()
} returns "do other stuff"
assertEquals("do other stuff", itMock.foo())
verify {
itMock.foo()
}
}
}
}