Kotlin API
Since Camel 4.4
| This API is experimental support level and is not recommended being used for production |
Kotlin API provides alternative approach to define routes.
Defining a route
To define route using Kotlin API you need to retrieve CamelContext and pass it to function camel:
import org.apache.camel.kotlin.camel
val ctx: CamelContext = ...
camel(ctx) { ... }
Then you can define route in route block:
camel(ctx) {
route { (1)
from { (2)
component("direct")
url("input")
}
steps { (3)
to {
component("mock")
url("output")
}
}
}
}
| 1 | Definition of route |
| 2 | Definition of consuming endpoint |
| 3 | Definition of processing steps |
You can find a number of handful methods in route block, for example, setting route id:
camel(ctx) {
route {
id("my-route")
}
}
Defining endpoints
Here and further camel(ctx) block will be omitted to make code less annoying.
Raw endpoints
Raw endpoint constructs from three components: component, url and a number of property-es. They all connected with each other and form resulting uri by the following rule:
"$component:$url?${property1.key}=${property1.value}&..."
So to define consumer from netty-http you can write the following:
route {
from {
component("netty-http")
url("http://localhost:8080")
property("keepAlive", "false")
property("reuseAddress", "false")
}
}
Also you can omit component and write only url by the following schema:
route {
from {
url("netty-http:http://localhost:8080")
property("keepAlive", "false")
property("reuseAddress", "false")
}
}
Moreover, you can omit property at all and write full uri in url function:
route {
from {
url("netty-http:http://localhost:8080?keepAlive=false&reuseAddress=false")
}
}
This three definitions are equivalent.
property method accepts only String value type. This is because it builds raw uri, not Endpoint. That means that if you need to define property of complex type, you must define bean in registry. Also that behaviour may be very useful in the following situations:
-
usage of property placeholders
-
toDandenrichEIPs accepts simple language in uri, so it will be handful to use simple in property values
Endpoint DSL
Defining string-based uris may not be very handy. So there is Endpoint DSL for building uris. For each Camel component exists an extension function with the name of that component. An example:
import org.apache.camel.kotlin.components.`netty-http`
import org.apache.camel.kotlin.components.mock
route {
from {
`netty-http` {
protocol("http")
host("localhost")
port(8080)
keepAlive(false)
reuseAddress(false)
}
}
steps {
to { mock { name("output") } }
}
}
Rules remain the same: all non-primitive types must be defined as beans in registry and referenced in properties by #name.
Defining EIPs
In that section we will take a look at several important EIPs to demonstrate logic of their definition.
Marshal, Unmarshal and DataFormat DSL
Marshal and Unmarshal EIPs come with DataFormat DSL. This DSL is the only way to define both EIPs. Example:
import org.apache.camel.kotlin.dataformats.csv
route {
from { direct { name("input") } }
steps {
unmarshal {
csv {
delimiter(";")
}
}
}
}
LoadBalance and nested DSLs
Some of EIPs provide additional complex configuration for their fields. For example, Load Balance EIP: there we can define various variants of which algorithm to use. So all that options are wrapped into their own DSL. Example:
route {
from { direct { name("input") } }
steps {
loadBalance {
failover {
maximumFailoverAttempts(1)
}
}
}
}
Filter, Multicast, Pipeline and outputs
Some of EIPs defines their own subroutes, for example, Filter and Multicast. In that cases use outputs property of EIP’s block. Filter example:
route {
from { direct { name("input") } }
steps {
filter(constant("true")) {
outputs {
log("only calls in filter block")
}
}
log("calls after filter block executed")
}
}
Multicast example:
route {
from { direct { name("input") } }
steps {
multicast {
outputs {
to { direct { name("first") } }
to { direct { name("second") } }
}
}
}
}
That behaviour differs for Pipeline EIP, which has not any properties and so all nested steps defines in pipeline block:
route {
from { direct { name("input") } }
steps {
pipeline {
log("first pipeline")
}
pipeline {
log("second pipeline")
}
}
}
Defining beans
Direct object binding
You can just provide instance of any type for Camel context:
val map = mapOf<String, String>()
camel(ctx) {
bean("myMap", map)
}
Or use supplier-function:
camel(ctx) {
bean("myMap") {
mapOf<String, String>()
}
}
Or construct bean using builder:
camel(ctx) {
bean<MyClass>("myBean") {
myField = "value"
}
}
Runtime object binding
Use other way is to use declarative approach of defining beans, without referencing beans at compile-time:
class Example {
lateinit var map: MutableMap<String, String>
}
camel(ctx) {
bean("map", mutableMapOf(Pair("key", "value")))
bean {
name("test")
type("org.apache.camel.kotlin.Example")
property("map", "#map")
}
}
When you cross-reference beans like in example (bean test reference map) be sure you declare beans in the dependency order: first dependencies, last dependents.
Using languages, expressions and predicates
There are a number of functions that provides Camel languages like constant or simple and a number of helper functions for building predicates/expressions like body or header. All of them are in the package org.apache.camel.kotlin.languages.
There are two useful extension functions:
-
Expression.toPredicate(): Predicateconverts any expression to predicate type -
Expression.which(): ValueBuilderconverts any expression toValueBuiler, makes possible to write expressions like:body().which().isInstanceOf(String::class)
Fallback to Java API
For camel block there is routeBuilder field which can help to define any Camel entity using Java API. For each other block there is def field present. It can be used if some functionality is missing. Example:
route {
from { direct { name("input") } }
steps {
def.pipeline().log("Java API").end()
}
}