diff --git a/pom.xml b/pom.xml index ba054091e7..c06305b8a0 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ 1.3.0 1.0.30.RELEASE - 1.6.21 + 1.8.22 1.9.4 2.6.0 diff --git a/specs/spring-cloud-contract-spec-kotlin/pom.xml b/specs/spring-cloud-contract-spec-kotlin/pom.xml index f986b16419..ce955604dd 100644 --- a/specs/spring-cloud-contract-spec-kotlin/pom.xml +++ b/specs/spring-cloud-contract-spec-kotlin/pom.xml @@ -26,6 +26,18 @@ org.jetbrains.kotlin kotlin-compiler-embeddable + + org.jetbrains.kotlin + kotlin-scripting-common + + + org.jetbrains.kotlin + kotlin-scripting-jvm + + + org.jetbrains.kotlin + kotlin-scripting-jvm-host + org.jetbrains.kotlin kotlin-scripting-compiler-embeddable diff --git a/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/KotlinContractConverter.kt b/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/KotlinContractConverter.kt index 6be6784243..d332462f6d 100644 --- a/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/KotlinContractConverter.kt +++ b/specs/spring-cloud-contract-spec-kotlin/src/main/kotlin/org/springframework/cloud/contract/spec/internal/KotlinContractConverter.kt @@ -22,7 +22,14 @@ import org.springframework.cloud.contract.spec.ContractConverter import java.io.File import java.net.URLClassLoader.newInstance import java.util.concurrent.atomic.AtomicInteger -import javax.script.ScriptEngineManager +import kotlin.script.experimental.api.EvaluationResult +import kotlin.script.experimental.api.ResultValue +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.api.ScriptCompilationConfiguration +import kotlin.script.experimental.host.toScriptSource +import kotlin.script.experimental.jvm.dependenciesFromCurrentContext +import kotlin.script.experimental.jvm.jvm +import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost /** * Converter that will convert the Kotlin DSL to Java DSL. @@ -32,62 +39,73 @@ import javax.script.ScriptEngineManager */ class KotlinContractConverter : ContractConverter> { - private val ext = "kts" + private val ext = "kts" - constructor() { - // Sets an {@code idea.use.native.fs.for.win} system property to {@code false} - // to disable a native engine discovery for Windows: may be resolved in the future Kotlin versions. - System.setProperty("idea.use.native.fs.for.win", "false") - } + constructor() { + // Sets an {@code idea.use.native.fs.for.win} system property to {@code false} + // to disable a native engine discovery for Windows: may be resolved in the future Kotlin versions. + System.setProperty("idea.use.native.fs.for.win", "false") + } - override fun isAccepted(file: File): Boolean { - return ext == file.extension - } + override fun isAccepted(file: File): Boolean { + return ext == file.extension + } - override fun convertFrom(file: File): Collection { - val eval = withUpdatedClassloader(file) { - file.reader().use { - // Get a new engine every time we need to process a file. - // Reusing the script engine could leak context and will fail subsequent evals - ScriptEngineManager().getEngineByExtension(ext).eval(it) - } - } - val contracts = when (eval) { - is Contract -> listOf(eval) - is Iterable<*> -> eval.filterIsInstance(Contract::class.java) - is Array<*> -> eval.filterIsInstance(Contract::class.java) - else -> emptyList() - } + override fun convertFrom(file: File): Collection { + val eval = withUpdatedClassloader(file) { + BasicJvmScriptingHost().eval(file.toScriptSource(), ScriptWithCurrentClasspathConfiguration(), null) + } + when (eval) { + is ResultWithDiagnostics.Success<*> -> { + val contracts = when (val parsedValue = ((eval.value as EvaluationResult).returnValue as ResultValue.Value).value) { + is Contract -> listOf(parsedValue) + is Iterable<*> -> parsedValue.filterIsInstance(Contract::class.java) + is Array<*> -> parsedValue.filterIsInstance(Contract::class.java) + else -> emptyList() + } - return withName(file, contracts) - } + return withName(file, contracts) + } - private fun withName(file: File, contracts: Collection): Collection { - val counter = AtomicInteger(0) - return contracts.onEach { contract -> - if (ObjectUtils.isEmpty(contract.name)) { - contract.name = defaultContractName(file, contracts, counter.get()) - } - counter.incrementAndGet() - } - } + else -> throw IllegalStateException("Failed to parse kotlin script due to ${(eval as ResultWithDiagnostics.Failure).reports}") + } + } - private fun defaultContractName(file: File, contracts: Collection<*>, counter: Int): String { - val lastIndexOfDot = file.name.lastIndexOf(".") - val tillExtension = file.name.substring(0, lastIndexOfDot) - return tillExtension + if (counter > 0 || contracts.size > 1) "_$counter" else "" - } + private fun withName(file: File, contracts: Collection): Collection { + val counter = AtomicInteger(0) + return contracts.onEach { contract -> + if (ObjectUtils.isEmpty(contract.name)) { + contract.name = defaultContractName(file, contracts, counter.get()) + } + counter.incrementAndGet() + } + } - override fun convertTo(contract: Collection) = contract.toList() + private fun defaultContractName(file: File, contracts: Collection<*>, counter: Int): String { + val lastIndexOfDot = file.name.lastIndexOf(".") + val tillExtension = file.name.substring(0, lastIndexOfDot) + return tillExtension + if (counter > 0 || contracts.size > 1) "_$counter" else "" + } - private fun withUpdatedClassloader(file: File, block: ClassLoader.() -> Any): Any { - val currentClassLoader = Thread.currentThread().contextClassLoader - try { - val tempClassLoader = newInstance(arrayOf(file.parentFile.toURI().toURL()), currentClassLoader) - Thread.currentThread().contextClassLoader = tempClassLoader - return tempClassLoader.block() - } finally { - Thread.currentThread().contextClassLoader = currentClassLoader - } - } -} \ No newline at end of file + override fun convertTo(contract: Collection) = contract.toList() + + private fun withUpdatedClassloader(file: File, block: ClassLoader.() -> Any): Any { + val currentClassLoader = Thread.currentThread().contextClassLoader + try { + val tempClassLoader = newInstance(arrayOf(file.parentFile.toURI().toURL()), currentClassLoader) + Thread.currentThread().contextClassLoader = tempClassLoader + return tempClassLoader.block() + } finally { + Thread.currentThread().contextClassLoader = currentClassLoader + } + } + + class ScriptWithCurrentClasspathConfiguration : ScriptCompilationConfiguration( + { + jvm { + // Extract the whole classpath from context classloader and use it as dependencies + dependenciesFromCurrentContext(wholeClasspath = true) + } + } + ) +} diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml index 54c11d2cee..5b0cc91043 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml @@ -56,15 +56,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - - - **/PluginUnitTest.java - - - org.apache.maven.plugins maven-failsafe-plugin