Java/Kotlin Integration

This guide shows you how to embed ISL in your Java/Kotlin project.

Prerequisites

  • Java 21 or higher
  • Maven or Gradle build tool

Maven Dependency

<dependency>
    <groupId>com.intuit.isl</groupId>
    <artifactId>isl-transform</artifactId>
    <version>[Version]</version>
</dependency>

Gradle Dependency

dependencies {
    implementation("com.intuit.isl:isl-transform:2.4.20-SNAPSHOT")
}

Hello World Example

Java Example

import com.intuit.isl.common.OperationContext;
import com.intuit.isl.runtime.TransformCompiler;
import com.intuit.isl.runtime.Transformer;
import com.intuit.isl.utils.JsonConvert;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class IslExample {
    public static void main(String[] args) throws Exception {
        // 1. Define your ISL script
        String islScript = """
            fun run($name) {
                greeting: "Hello, $name!",
                timestamp: @.Date.Now()
            }
            """;
        
        // 2. Compile the script
        // Important: In real life scenarios you want to only compile once then cache the transformer and reuse it as many times as possible
        TransformCompiler compiler = new TransformCompiler();
        Transformer transformer = compiler.compileIsl("hello", islScript);

    
        // 3. Create operation context and set variables
        // Important: Do not cache the OperationContext between operations. It is not thread safe. 
        // Every thread and every request should have its own OperationContext
        OperationContext context = new OperationContext();
        context.setVariable("$name", JsonConvert.convert("World"));
        
        
        // 4. Execute the transformation
        // Important: This is thead safe as long as the context is unique per thread.
        JsonNode result = transformer.runTransformSync("run", context);
        
        // 5. Print the result
        ObjectMapper mapper = new ObjectMapper();
        String jsonOutput = mapper.writerWithDefaultPrettyPrinter()
                                   .writeValueAsString(result);
        System.out.println(jsonOutput);
    }
}

Output:

{
  "greeting": "Hello, World!",
  "timestamp": "2024-11-16T10:30:00.000Z"
}

Kotlin Example

import com.intuit.isl.common.OperationContext
import com.intuit.isl.runtime.TransformCompiler
import com.intuit.isl.utils.JsonConvert
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    // 1. Define your ISL script
    val islScript = """
        fun run(${"$"}name) {
            greeting: "Hello, ${"$"}name!",
            timestamp: @.Date.Now()
        }
    """.trimIndent()
    
    // 2. Compile the script
    val compiler = TransformCompiler()
    val transformer = compiler.compileIsl("hello", islScript)
    
    // 3. Create operation context and set variables
    val context = OperationContext()
    context.setVariable("${"$"}name", JsonConvert.convert("World"))
    
    // 4. Execute the transformation
    val result = transformer.runTransformAsync("run", context)
    
    // 5. Print the result
    val mapper = jacksonObjectMapper()
    val jsonOutput = mapper.writerWithDefaultPrettyPrinter()
                          .writeValueAsString(result.result)
    println(jsonOutput)
}

Adding Custom Functions

You can extend ISL by registering custom functions:

Java

OperationContext context = new OperationContext();

// Register a custom function
context.registerExtensionMethod("MyService.CalculateTax", (params) -> {
    Double amount = (Double) params[0];
    Double taxRate = (Double) params[1];
    return amount * taxRate;
});

// Use in ISL script:
// taxAmount: @.MyService.CalculateTax($amount, 0.08)

Kotlin

val context = OperationContext()

// Register a custom function
context.registerExtensionMethod("MyService.CalculateTax") { params ->
    val amount = params[0] as Double
    val taxRate = params[1] as Double
    amount * taxRate
}

// Use in ISL script:
// taxAmount: @.MyService.CalculateTax($amount, 0.08)

Adding Custom Modifiers

Register custom modifiers for fluent data transformations:

context.registerExtensionMethod("Modifier.taxAmount") { params ->
    val price = params[0] as Double
    val taxRate = params.getOrNull(1) as? Double ?: 0.08
    price * taxRate
}

// Use in ISL script:
// tax: $price | taxAmount(0.08)

Working with JSON Data

Loading JSON Input

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue

val mapper = jacksonObjectMapper()
val jsonString = """{"name": "Alice", "age": 30}"""
val inputData: Map<String, Any> = mapper.readValue(jsonString)

val context = OperationContext()
context.setVariable("${"$"}input", JsonConvert.convert(inputData))

Complex Transformation Example

val islScript = """
    fun run(${"$"}orders) {
        totalOrders: ${"$"}orders | length,
        activeOrders: foreach ${"$"}order in ${"$"}orders | filter(${"$"}order.status == "active") {
            id: ${"$"}order.id,
            customer: ${"$"}order.customer,
            total: {{ ${"$"}order.quantity * ${"$"}order.price }}
        }
        endfor,
        grandTotal: ${"$"}orders | reduce({{ ${"$"}acc + (${"$"}it.quantity * ${"$"}it.price) }})
    }
""".trimIndent()

val compiler = TransformCompiler() val transformer = compiler.compileIsl(“orders”, islScript)

val context = OperationContext() context.setVariable(“${“$”}orders”, JsonConvert.convert(ordersData))

val result = transformer.runTransformSync(“run”, context)


## Async Execution

ISL supports async/coroutine execution for better performance:

```kotlin
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking

runBlocking {
    val results = (1..10).map { i ->
        async {
            val context = OperationContext()
            context.setVariable("${"$"}id", JsonConvert.convert(i))
            transformer.runTransformAsync("run", context)
        }
    }.awaitAll()
    
    results.forEach { println(it.result) }
}

Error Handling

try {
    val transformer = compiler.compileIsl("script", islScript)
    val result = transformer.runTransformSync("run", context)
    println(result)
} catch (e: com.intuit.isl.runtime.TransformCompilationException) {
    println("Compilation error: ${e.message}")
    println("Position: ${e.position}")
} catch (e: com.intuit.isl.runtime.TransformException) {
    println("Runtime error: ${e.message}")
}

Next Steps

Resources