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
-
toD
andenrich
EIPs 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(): Predicate
converts any expression to predicate type -
Expression.which(): ValueBuilder
converts 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()
}
}