Running a Kotlin DSL from an external file

João Paulo Gomes
WAES
Published in
4 min readJan 4, 2024

This article will explain how to run a Kotlin DSL from an external file in a Spring Boot application running in your IDE or from a JAR file.

Kotlin DSL

Kotlin is a programming language with resources to create Domain-Specific languages.

In this article, we are going to use a very simple DSL written in Kotlin that returns an uppercase version of a message. This is the final example of the content of a file called my-dsl.kts

import com.johnowl.runkotlindslfromexternalfile.dsl.*

myDsl {
message = "Hello World!"
}

Explaining how to create a DSL is out of the scope of this article, but this is the implementation of this simple and useless DSL:

package com.johnowl.runkotlindslfromexternalfile.dsl

data class MyDsl(
var message: String = "Unknown"
)

fun myDsl(block: MyDsl.() -> Unit): MyDsl =
MyDsl().apply(block)

This DSL implementation populates a data class. This is the processor for the DSL:

@Service
class MyDslProcessor {
fun process(dsl: MyDsl): String {
return dsl.message.uppercase()
}
}

The processor receives the MyDSL data class and returns the uppercase message. We have the @Service annotation because we are using the Spring Framework. Now we can use our DSL in a Spring Controller, for example:

@RestController
class MyDslController(
private val processor: MyDslProcessor
) {

@GetMapping("/dsl/v1")
fun processV1(): String {
val myDsl = myDsl { // (1)
message = "Hello World!"
}
return processor.process(myDsl) // (2)
}
}

In the line commented with(1), we can see that we are using our DSL, and in the one with(2), we are returning the result.

This is nice, but what if we need to store the content of our DSL in external .kts files?

How to run a Kotlin DSL from an external file

First, you need to add the dependency below to your project:

Maven:

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-scripting-jsr223</artifactId>
<version>${kotlin.version}</version>
</dependency>

Gradle:

implementation 'org.jetbrains.kotlin:kotlin-scripting-jsr223'

Now, you can use this dependency to create your parser as follows:

@Service
class MyDslParser {
fun parse(filename: String): MyDsl {
val fileContent = File("src/main/resources/$filename").readText()
val scriptEngine = ScriptEngineManager().getEngineByExtension("kts")
val parsedDsl = scriptEngine.eval(fileContent)
if (parsedDsl !is MyDsl) {
throw Exception("Script does not return a MyDsl")
}
return parsedDsl
}
}

This is going to evaluate any Kotlin code inside the file; be careful and use only trusted files.

Create a file called my-dsl.kts in the resources folder of your Spring Boot application with the content:

import com.johnowl.runkotlindslfromexternalfile.dsl.*

myDsl {
message = "Hello World from the external file!"
}

Add a new mapping in your controller:

@GetMapping("/dsl/v2")
fun process(): String {
val dsl = MyDslParser().parse("my-dsl.kts")
return processor.process(dsl)
}

This is going to return the uppercase version of the text "Hello World from the external file!".

This runs fine in Intellij, but what if you create a jar file to run in production? The application will start normally, but when you call the /dsl/v2 endpoint a runtime error similar to the one below will happen:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: javax.script.ScriptException: ERROR Unable to initialize repl compiler:
DEBUG Using JDK home inferred from java.home: /path/to/java/home
ERROR Unable to find kotlin stdlib, please specify it explicitly via "kotlin.java.stdlib.jar" property: java.lang.Exception: Unable to find kotlin stdlib, please specify it explicitly via "kotlin.java.stdlib.jar" property: java.lang.IllegalStateException: Unable to initialize repl compiler:
DEBUG Using JDK home inferred from java.home: /path/to/java/home
ERROR Unable to find kotlin stdlib, please specify it explicitly via "kotlin.java.stdlib.jar" property: java.lang.Exception: Unable to find kotlin stdlib, please specify it explicitly via "kotlin.java.stdlib.jar" property] with root cause

This is not what you want. See how to solve it in the next chapter.

How to run a Kotlin DSL from an external file in a Spring Boot JAR

You need to tell the Spring Boot plugin to unpack some dependencies because some libraries are trying to read files from a specific place, and without unpacking, it doesn't work.

For Maven, add the requiresUnpackto the Spring Boot Maven plugin configuration in the pom.xmlfile:

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<requiresUnpack>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-compiler-embeddable</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-scripting-jsr223</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</dependency>
</requiresUnpack>
</configuration>
</plugin>

For Gradle, add the requiresUnpack configuration at the end of the build.gradle file:

tasks.named("bootJar") {
requiresUnpack '**/kotlin-scripting-jsr223-*.jar', '**/kotlin-stdlib-*.jar', '**/kotlin-compiler-embeddable-*.jar'
}

It was tested and works with Spring Boot 3.2.0 and Kotlin 1.9.2.

Conclusion

We saw it’s possible to run a Kotlin DSL from an external file, and if you work with Spring Boot, with some extra configuration, it's still possible.

This is something that my team learned recently in a project we are working on. So, I have decided to share because it can also be someone else’s question.

Enjoy the possibilities of writing DSLs in Kotlin!

Do you think you have what it takes to be one of us?

At WAES, we are always looking for the best developers and data engineers to help Dutch companies succeed. If you are interested in becoming a part of our team and moving to The Netherlands, look at our open positions here.

WAES publication

Our content creators constantly create new articles about software development, lifestyle, and WAES. So make sure to follow us on Medium to learn more.

Also, make sure to follow us on our social media:
LinkedInInstagramTwitterYouTube

--

--

João Paulo Gomes
WAES
Writer for

Hi! I’m JP! I work as a Kotlin and Java developer and in my spare time I like to cook. My github https://github.com/johnowl