This is an automated email from the ASF dual-hosted git repository.

nferraro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel-k.git


The following commit(s) were added to refs/heads/master by this push:
     new 6d9d529  runtime: support for kotlin runtime
6d9d529 is described below

commit 6d9d529bb4257036bad924be72f9e7f43e7cf218
Author: lburgazzoli <lburgazz...@gmail.com>
AuthorDate: Tue Sep 25 10:32:40 2018 +0200

    runtime: support for kotlin runtime
---
 README.adoc                                        |   1 +
 deploy/platform-integration-context-groovy.yaml    |   5 +-
 ....yaml => platform-integration-context-jvm.yaml} |   5 +-
 ...ml => platform-integration-context-kotlin.yaml} |   6 +-
 deploy/resources.go                                |  31 ++++-
 pkg/apis/camel/v1alpha1/types.go                   |   2 +
 pkg/build/assemble/maven_assembler.go              |   4 +
 pkg/client/cmd/completion_bash.go                  |  22 +++-
 pkg/client/cmd/context_create.go                   |  11 ++
 pkg/client/cmd/run.go                              |  23 +++-
 pkg/discover/dependencies.go                       |   7 +-
 pkg/discover/language.go                           |   6 +-
 pkg/install/operator.go                            |   2 +-
 pkg/stub/action/integration/initialize.go          |   9 +-
 pkg/stub/action/platform/create.go                 |   8 +-
 pkg/util/util.go                                   |  13 +++
 runtime/examples/kotlin-routes.kts                 |  20 ++++
 runtime/groovy/.gitignore                          |  10 ++
 runtime/groovy/pom.xml                             |  45 ++++++++
 .../apache/camel/k/groovy/GroovyRoutesLoader.java  |  63 ++++++++++
 .../services/org.apache.camel.k.jvm.RoutesLoader   |   1 +
 .../apache/camel/k/groovy/RoutesLoadersTest.java   |  47 ++++++++
 .../src/test/resources/routes.groovy               |   0
 runtime/jvm/pom.xml                                |  24 +++-
 .../main/java/org/apache/camel/k/jvm/Language.java |  44 ++++---
 .../java/org/apache/camel/k/jvm/RoutesLoaders.java | 127 ++++-----------------
 .../org/apache/camel/k/jvm/dsl/Components.java     |  47 ++++++++
 .../java/org/apache/camel/k/jvm/dsl/Scripting.java |  47 ++++++++
 .../services/org.apache.camel.k.jvm.RoutesLoader   |   4 +
 .../org/apache/camel/k/jvm/RoutesLoadersTest.java  |  27 +----
 runtime/kotlin/.gitignore                          |  10 ++
 runtime/kotlin/pom.xml                             |  56 +++++++++
 .../apache/camel/k/kotlin/KotlinRoutesLoader.java  |  77 +++++++++++++
 .../services/javax.script.ScriptEngineFactory      |   1 +
 .../services/org.apache.camel.k.jvm.RoutesLoader   |   1 +
 .../apache/camel/k/kotlin/RoutesLoadersTest.java   |  50 ++++++++
 runtime/kotlin/src/test/resources/routes.kts       |  21 ++++
 runtime/pom.xml                                    |   3 +
 38 files changed, 702 insertions(+), 178 deletions(-)

diff --git a/README.adoc b/README.adoc
index 24d0779..fadd6d1 100644
--- a/README.adoc
+++ b/README.adoc
@@ -114,6 +114,7 @@ Camel K supports multiple languages for writing 
integrations:
 | XML                          | Integrations written in plain XML DSL are 
supported (Spring XML or Blueprint not supported).
 | Groovy                       | Groovy `.groovy` files are supported 
(experimental).
 | JavaScript           | JavaScript `.js` files are supported (experimental).
+| Kotlin                       | Kotlin Script `.kts` files are supported 
(experimental).
 |=======================
 
 Integrations written in different languages are provided in the 
link:/runtime/examples[examples] directory.
diff --git a/deploy/platform-integration-context-groovy.yaml 
b/deploy/platform-integration-context-groovy.yaml
index cabe24a..31aaeff 100644
--- a/deploy/platform-integration-context-groovy.yaml
+++ b/deploy/platform-integration-context-groovy.yaml
@@ -9,5 +9,6 @@ metadata:
     camel.apache.org/context.type: platform
 spec:
   dependencies:
-    - camel:core
-    - camel:groovy
\ No newline at end of file
+    - runtime:jvm
+    - runtime:groovy
+    - camel:core
\ No newline at end of file
diff --git a/deploy/platform-integration-context-core.yaml 
b/deploy/platform-integration-context-jvm.yaml
similarity index 69%
copy from deploy/platform-integration-context-core.yaml
copy to deploy/platform-integration-context-jvm.yaml
index d0d3289..28e8ff5 100644
--- a/deploy/platform-integration-context-core.yaml
+++ b/deploy/platform-integration-context-jvm.yaml
@@ -1,12 +1,13 @@
 apiVersion: camel.apache.org/v1alpha1
 kind: IntegrationContext
 metadata:
-  name: core
+  name: jvm
   labels:
     app: "camel-k"
     camel.apache.org/context.created.by.kind: Operator
-    camel.apache.org/context.created.by.name: core
+    camel.apache.org/context.created.by.name: jvm
     camel.apache.org/context.type: platform
 spec:
   dependencies:
+    - runtime:jvm
     - camel:core
\ No newline at end of file
diff --git a/deploy/platform-integration-context-core.yaml 
b/deploy/platform-integration-context-kotlin.yaml
similarity index 65%
rename from deploy/platform-integration-context-core.yaml
rename to deploy/platform-integration-context-kotlin.yaml
index d0d3289..822b4a4 100644
--- a/deploy/platform-integration-context-core.yaml
+++ b/deploy/platform-integration-context-kotlin.yaml
@@ -1,12 +1,14 @@
 apiVersion: camel.apache.org/v1alpha1
 kind: IntegrationContext
 metadata:
-  name: core
+  name: kotlin
   labels:
     app: "camel-k"
     camel.apache.org/context.created.by.kind: Operator
-    camel.apache.org/context.created.by.name: core
+    camel.apache.org/context.created.by.name: jvm
     camel.apache.org/context.type: platform
 spec:
   dependencies:
+    - runtime:jvm
+    - runtime:kotlin
     - camel:core
\ No newline at end of file
diff --git a/deploy/resources.go b/deploy/resources.go
index 7749be3..a3274cb 100644
--- a/deploy/resources.go
+++ b/deploy/resources.go
@@ -2467,12 +2467,12 @@ metadata:
     app: "camel-k"
 
 `
-       Resources["platform-integration-context-core.yaml"] =
+       Resources["platform-integration-context-groovy.yaml"] =
                `
 apiVersion: camel.apache.org/v1alpha1
 kind: IntegrationContext
 metadata:
-  name: core
+  name: groovy
   labels:
     app: "camel-k"
     camel.apache.org/context.created.by.kind: Operator
@@ -2480,23 +2480,42 @@ metadata:
     camel.apache.org/context.type: platform
 spec:
   dependencies:
+    - runtime:jvm
+    - runtime:groovy
     - camel:core
 `
-       Resources["platform-integration-context-groovy.yaml"] =
+       Resources["platform-integration-context-jvm.yaml"] =
                `
 apiVersion: camel.apache.org/v1alpha1
 kind: IntegrationContext
 metadata:
-  name: groovy
+  name: jvm
   labels:
     app: "camel-k"
     camel.apache.org/context.created.by.kind: Operator
-    camel.apache.org/context.created.by.name: core
+    camel.apache.org/context.created.by.name: jvm
     camel.apache.org/context.type: platform
 spec:
   dependencies:
+    - runtime:jvm
+    - camel:core
+`
+       Resources["platform-integration-context-kotlin.yaml"] =
+               `
+apiVersion: camel.apache.org/v1alpha1
+kind: IntegrationContext
+metadata:
+  name: kotlin
+  labels:
+    app: "camel-k"
+    camel.apache.org/context.created.by.kind: Operator
+    camel.apache.org/context.created.by.name: jvm
+    camel.apache.org/context.type: platform
+spec:
+  dependencies:
+    - runtime:jvm
+    - runtime:kotlin
     - camel:core
-    - camel:groovy
 `
        Resources["user-cluster-role.yaml"] =
                `
diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index fa27617..114942f 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -77,6 +77,8 @@ const (
        LanguageJavaScript Language = "js"
        // LanguageXML --
        LanguageXML Language = "xml"
+       // LanguageKotlin --
+       LanguageKotlin Language = "kts"
 )
 
 // IntegrationStatus --
diff --git a/pkg/build/assemble/maven_assembler.go 
b/pkg/build/assemble/maven_assembler.go
index 9094181..9b2674e 100644
--- a/pkg/build/assemble/maven_assembler.go
+++ b/pkg/build/assemble/maven_assembler.go
@@ -162,6 +162,10 @@ func generateProject(source *build.Request) 
(maven.Project, error) {
                        gav := strings.Replace(mid, "/", ":", -1)
 
                        deps.AddEncodedGAV(gav)
+               } else if strings.HasPrefix(d, "runtime:") {
+                       artifactID := strings.Replace(d, "runtime:", 
"camel-k-runtime-", 1)
+
+                       deps.AddGAV("org.apache.camel.k", artifactID, 
version.Version)
                } else {
                        return maven.Project{}, fmt.Errorf("unknown dependency 
type: %s", d)
                }
diff --git a/pkg/client/cmd/completion_bash.go 
b/pkg/client/cmd/completion_bash.go
index c689f8e..513a821 100644
--- a/pkg/client/cmd/completion_bash.go
+++ b/pkg/client/cmd/completion_bash.go
@@ -50,24 +50,29 @@ __kamel_dependency_type() {
         COMPREPLY=( $( compgen -W "${type_list}" -- "$cur") )
         ;;
     m*)
-        local type_list="mvn:""
+        local type_list="mvn:"
         COMPREPLY=( $( compgen -W "${type_list}" -- "$cur") )
                compopt -o nospace
         ;;
     f*)
-        local type_list="file:""
+        local type_list="file:"
         COMPREPLY=( $( compgen -W "${type_list}" -- "$cur") )
                compopt -o nospace
         ;;
     *)
-        local type_list="camel: mvn: file:"
-        COMPREPLY=( $( compgen -W "camel mvn: file:" -- "$cur") )
+        local type_list="camel mvn: file:"
+        COMPREPLY=( $( compgen -W "${type_list}" -- "$cur") )
            compopt -o nospace
     esac
 }
 
 __kamel_languages() {
-    local type_list="js groovy java xml"
+    local type_list="js groovy kotlin java xml"
+    COMPREPLY=( $( compgen -W "${type_list}" -- "$cur") )
+}
+
+__kamel_runtimes() {
+    local type_list="jvm groovy kotlin"
     COMPREPLY=( $( compgen -W "${type_list}" -- "$cur") )
 }
 
@@ -195,6 +200,13 @@ func configureKnownBashCompletions(command *cobra.Command) 
{
                        cobra.BashCompCustom: {"__kamel_languages"},
                },
        )
+       configureBashAnnotationForFlag(
+               command,
+               "runtime",
+               map[string][]string{
+                       cobra.BashCompCustom: {"__kamel_runtimes"},
+               },
+       )
 }
 
 func configureBashAnnotationForFlag(command *cobra.Command, flagName string, 
annotations map[string][]string) {
diff --git a/pkg/client/cmd/context_create.go b/pkg/client/cmd/context_create.go
index e95ae73..739fc8d 100644
--- a/pkg/client/cmd/context_create.go
+++ b/pkg/client/cmd/context_create.go
@@ -23,6 +23,8 @@ import (
        "strconv"
        "strings"
 
+       "github.com/apache/camel-k/pkg/util"
+
        "github.com/operator-framework/operator-sdk/pkg/sdk"
 
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
@@ -46,6 +48,7 @@ func newContextCreateCmd(rootCmdOptions *RootCmdOptions) 
*cobra.Command {
                RunE:  impl.run,
        }
 
+       cmd.Flags().StringVarP(&impl.runtime, "runtime", "r", "jvm", "Runtime 
provided by the context")
        cmd.Flags().StringSliceVarP(&impl.dependencies, "dependency", "d", nil, 
"Add a dependency")
        cmd.Flags().StringSliceVarP(&impl.properties, "property", "p", nil, 
"Add a camel property")
        cmd.Flags().StringSliceVar(&impl.configmaps, "configmap", nil, "Add a 
ConfigMap")
@@ -60,6 +63,7 @@ func newContextCreateCmd(rootCmdOptions *RootCmdOptions) 
*cobra.Command {
 type contextCreateCommand struct {
        *RootCmdOptions
 
+       runtime      string
        dependencies []string
        properties   []string
        configmaps   []string
@@ -105,6 +109,13 @@ func (command *contextCreateCommand) run(cmd 
*cobra.Command, args []string) erro
                }
        }
 
+       // jvm runtime required by default
+       util.StringSliceUniqueAdd(&ctx.Spec.Dependencies, "runtime:jvm")
+
+       if command.runtime != "" {
+               util.StringSliceUniqueAdd(&ctx.Spec.Dependencies, 
"runtime:"+command.runtime)
+       }
+
        for _, item := range command.properties {
                ctx.Spec.Configuration = append(ctx.Spec.Configuration, 
v1alpha1.ConfigurationSpec{
                        Type:  "property",
diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index ca4e52f..b9f40eb 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -56,6 +56,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command 
{
        }
 
        cmd.Flags().StringVarP(&options.Language, "language", "l", "", 
"Programming Language used to write the file")
+       cmd.Flags().StringVarP(&options.Runtime, "runtime", "r", "jvm", 
"Runtime used by the integration")
        cmd.Flags().StringVar(&options.IntegrationName, "name", "", "The 
integration name")
        cmd.Flags().StringSliceVarP(&options.Dependencies, "dependency", "d", 
nil, "The integration dependency")
        cmd.Flags().BoolVarP(&options.Wait, "wait", "w", false, "Waits for the 
integration to be running")
@@ -78,6 +79,7 @@ type runCmdOptions struct {
        *RootCmdOptions
        IntegrationContext        string
        Language                  string
+       Runtime                   string
        IntegrationName           string
        Dependencies              []string
        Properties                []string
@@ -269,13 +271,22 @@ func (o *runCmdOptions) updateIntegrationCode(filename 
string) (*v1alpha1.Integr
                }
        }
 
-       // special handling for groovy
-       // TODO: we should define handlers for languages and/or file extensions
-       if o.Language == "groovy" && !util.StringSliceExists(o.Dependencies, 
"camel:groovy") {
-               integration.Spec.Dependencies = 
append(integration.Spec.Dependencies, "camel:groovy")
+       if o.Language == "groovy" || strings.HasSuffix(filename, ".groovy") {
+               util.StringSliceUniqueAdd(&integration.Spec.Dependencies, 
"runtime:groovy")
        }
-       if o.Language == "" && strings.HasSuffix(filename, ".groovy") {
-               integration.Spec.Dependencies = 
append(integration.Spec.Dependencies, "camel:groovy")
+       if o.Language == "kotlin" || strings.HasSuffix(filename, ".kts") {
+               util.StringSliceUniqueAdd(&integration.Spec.Dependencies, 
"runtime:kotlin")
+       }
+
+       // jvm runtime required by default
+       util.StringSliceUniqueAdd(&integration.Spec.Dependencies, "runtime:jvm")
+
+       if o.Runtime != "" {
+               util.StringSliceUniqueAdd(&integration.Spec.Dependencies, 
"runtime:"+o.Runtime)
+       }
+
+       switch o.Runtime {
+
        }
 
        for _, item := range o.Properties {
diff --git a/pkg/discover/dependencies.go b/pkg/discover/dependencies.go
index 82ef757..b6daa63 100644
--- a/pkg/discover/dependencies.go
+++ b/pkg/discover/dependencies.go
@@ -18,11 +18,12 @@ limitations under the License.
 package discover
 
 import (
-       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
-       "github.com/apache/camel-k/pkg/util/camel"
        "regexp"
        "sort"
        "strings"
+
+       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+       "github.com/apache/camel-k/pkg/util/camel"
 )
 
 var (
@@ -65,6 +66,8 @@ func getRegexpsForLanguage(language v1alpha1.Language) 
[]*regexp.Regexp {
                return []*regexp.Regexp{singleQuotedURI, doubleQuotedURI}
        case v1alpha1.LanguageJavaScript:
                return []*regexp.Regexp{singleQuotedURI, doubleQuotedURI}
+       case v1alpha1.LanguageKotlin:
+               return []*regexp.Regexp{doubleQuotedURI}
        }
        return []*regexp.Regexp{}
 }
diff --git a/pkg/discover/language.go b/pkg/discover/language.go
index e38d583..cafb6c3 100644
--- a/pkg/discover/language.go
+++ b/pkg/discover/language.go
@@ -19,8 +19,9 @@ limitations under the License.
 package discover
 
 import (
-       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
        "strings"
+
+       "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 )
 
 // Language discovers the code language from file extension if not set
@@ -33,7 +34,8 @@ func Language(source v1alpha1.SourceSpec) v1alpha1.Language {
                v1alpha1.LanguageJavaClass,
                v1alpha1.LanguageJavaScript,
                v1alpha1.LanguageGroovy,
-               v1alpha1.LanguageJavaScript} {
+               v1alpha1.LanguageJavaScript,
+               v1alpha1.LanguageKotlin} {
 
                if strings.HasSuffix(source.Name, "."+string(l)) {
                        return l
diff --git a/pkg/install/operator.go b/pkg/install/operator.go
index 0f6039c..eb3ac4c 100644
--- a/pkg/install/operator.go
+++ b/pkg/install/operator.go
@@ -30,7 +30,7 @@ func Operator(namespace string) error {
 
 // Platform installs the platform custom resource
 func Platform(namespace string) error {
-       return Resource(namespace,"platform-cr.yaml")
+       return Resource(namespace, "platform-cr.yaml")
 }
 
 // Example --
diff --git a/pkg/stub/action/integration/initialize.go 
b/pkg/stub/action/integration/initialize.go
index d9ab6c0..f0b5e7f 100644
--- a/pkg/stub/action/integration/initialize.go
+++ b/pkg/stub/action/integration/initialize.go
@@ -22,6 +22,8 @@ import (
        "github.com/sirupsen/logrus"
        "sort"
 
+       "github.com/apache/camel-k/pkg/util"
+
        "github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
        "github.com/apache/camel-k/pkg/discover"
        "github.com/apache/camel-k/pkg/util/digest"
@@ -46,7 +48,7 @@ func (action *initializeAction) CanHandle(integration 
*v1alpha1.Integration) boo
        return integration.Status.Phase == ""
 }
 
-// Handle handles the integratios
+// Handle handles the integrations
 func (action *initializeAction) Handle(integration *v1alpha1.Integration) 
error {
        // The integration platform needs to be ready before starting to create 
integrations
        if pl, err := platform.GetCurrentPlatform(integration.Namespace); err 
!= nil || pl.Status.Phase != v1alpha1.IntegrationPlatformPhaseReady {
@@ -63,6 +65,11 @@ func (action *initializeAction) Handle(integration 
*v1alpha1.Integration) error
        // set the correct language
        language := discover.Language(target.Spec.Source)
        target.Spec.Source.Language = language
+
+       if !util.StringSliceExists(target.Spec.Dependencies, "camel:core") {
+               target.Spec.Dependencies = append(target.Spec.Dependencies, 
"camel:core")
+       }
+
        // discover dependencies
        if target.Spec.DependenciesAutoDiscovery == nil {
                var autoDiscoveryDependencies = true
diff --git a/pkg/stub/action/platform/create.go 
b/pkg/stub/action/platform/create.go
index c313ccf..0933e2f 100644
--- a/pkg/stub/action/platform/create.go
+++ b/pkg/stub/action/platform/create.go
@@ -24,6 +24,12 @@ import (
        "github.com/sirupsen/logrus"
 )
 
+var resources = []string{
+       "platform-integration-context-jvm.yaml",
+       "platform-integration-context-groovy.yaml",
+       "platform-integration-context-kotlin.yaml",
+}
+
 // NewCreateAction returns a action that creates resources needed by the 
platform
 func NewCreateAction() Action {
        return &createAction{}
@@ -41,7 +47,7 @@ func (action *createAction) CanHandle(platform 
*v1alpha1.IntegrationPlatform) bo
 }
 
 func (action *createAction) Handle(platform *v1alpha1.IntegrationPlatform) 
error {
-       err := install.Resources(platform.Namespace, 
"platform-integration-context-core.yaml", 
"platform-integration-context-groovy.yaml")
+       err := install.Resources(platform.Namespace, resources...)
        if err != nil {
                return err
        }
diff --git a/pkg/util/util.go b/pkg/util/util.go
index c2a2631..7792210 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -38,3 +38,16 @@ func StringSliceExists(slice []string, item string) bool {
 
        return false
 }
+
+// StringSliceUniqueAdd append the given item if not already present in the 
slice
+func StringSliceUniqueAdd(slice *[]string, item string) bool {
+       for _, i := range *slice {
+               if i == item {
+                       return false
+               }
+       }
+
+       *slice = append(*slice, item)
+
+       return true
+}
diff --git a/runtime/examples/kotlin-routes.kts 
b/runtime/examples/kotlin-routes.kts
new file mode 100644
index 0000000..8c49d44
--- /dev/null
+++ b/runtime/examples/kotlin-routes.kts
@@ -0,0 +1,20 @@
+//
+// To run this integrations use:
+//
+//     kamel run --runtime kotlin runtime/examples/kotlin-routes.kts
+//
+// Or leveraging runtime detection
+//
+//     kamel run runtime/examples/kotlin-routes.kts
+//
+
+val rnd = java.util.Random()
+
+from("timer:kotlin?period=1s")
+    .routeId("kotlin")
+    .setBody()
+        .constant("Hello Camel K!")
+    .process().message {
+        m -> m.headers["RandomValue"] = rnd.nextInt()
+    }
+    .to("log:info?showHeaders=true")
\ No newline at end of file
diff --git a/runtime/groovy/.gitignore b/runtime/groovy/.gitignore
new file mode 100644
index 0000000..ed92983
--- /dev/null
+++ b/runtime/groovy/.gitignore
@@ -0,0 +1,10 @@
+target
+
+*.iml
+
+.idea
+.project
+.metadata
+.settings
+.factorypath
+.classpath
diff --git a/runtime/groovy/pom.xml b/runtime/groovy/pom.xml
new file mode 100644
index 0000000..a3fb478
--- /dev/null
+++ b/runtime/groovy/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <parent>
+        <groupId>org.apache.camel.k</groupId>
+        <artifactId>camel-k-runtime-parent</artifactId>
+        <version>0.0.3-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-k-runtime-groovy</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-runtime-jvm</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-groovy</artifactId>
+        </dependency>
+
+        <!-- ****************************** -->
+        <!--                                -->
+        <!-- TESTS                          -->
+        <!--                                -->
+        <!-- ****************************** -->
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git 
a/runtime/groovy/src/main/java/org/apache/camel/k/groovy/GroovyRoutesLoader.java
 
b/runtime/groovy/src/main/java/org/apache/camel/k/groovy/GroovyRoutesLoader.java
new file mode 100644
index 0000000..ee8a512
--- /dev/null
+++ 
b/runtime/groovy/src/main/java/org/apache/camel/k/groovy/GroovyRoutesLoader.java
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.k.groovy;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Collections;
+import java.util.List;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyShell;
+import groovy.util.DelegatingScript;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.k.jvm.Language;
+import org.apache.camel.k.jvm.RoutesLoader;
+import org.apache.camel.k.jvm.dsl.Scripting;
+import org.apache.camel.util.ResourceHelper;
+import org.codehaus.groovy.control.CompilerConfiguration;
+
+public class GroovyRoutesLoader implements RoutesLoader {
+    @Override
+    public List<Language> getSupportedLanguages() {
+        return Collections.singletonList(Language.Groovy);
+    }
+
+    @Override
+    public RouteBuilder load(String resource) throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                CompilerConfiguration cc = new CompilerConfiguration();
+                cc.setScriptBaseClass(DelegatingScript.class.getName());
+
+                ClassLoader cl = 
Thread.currentThread().getContextClassLoader();
+                GroovyShell sh = new GroovyShell(cl, new Binding(), cc);
+
+                try (InputStream is = 
ResourceHelper.resolveMandatoryResourceAsInputStream(getContext(), resource)) {
+                    Reader reader = new InputStreamReader(is);
+                    DelegatingScript script = (DelegatingScript) 
sh.parse(reader);
+
+                    // set the delegate target
+                    script.setDelegate(new Scripting(this));
+                    script.run();
+                }
+            }
+        };
+    }
+}
diff --git 
a/runtime/groovy/src/main/resources/META-INF/services/org.apache.camel.k.jvm.RoutesLoader
 
b/runtime/groovy/src/main/resources/META-INF/services/org.apache.camel.k.jvm.RoutesLoader
new file mode 100644
index 0000000..db214e0
--- /dev/null
+++ 
b/runtime/groovy/src/main/resources/META-INF/services/org.apache.camel.k.jvm.RoutesLoader
@@ -0,0 +1 @@
+org.apache.camel.k.groovy.GroovyRoutesLoader
\ No newline at end of file
diff --git 
a/runtime/groovy/src/test/java/org/apache/camel/k/groovy/RoutesLoadersTest.java 
b/runtime/groovy/src/test/java/org/apache/camel/k/groovy/RoutesLoadersTest.java
new file mode 100644
index 0000000..8a64faa
--- /dev/null
+++ 
b/runtime/groovy/src/test/java/org/apache/camel/k/groovy/RoutesLoadersTest.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.k.groovy;
+
+import java.util.List;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.k.jvm.RoutesLoader;
+import org.apache.camel.k.jvm.RoutesLoaders;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.ToDefinition;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RoutesLoadersTest {
+    @Test
+    public void testLoadGroovy() throws Exception {
+        String resource = "classpath:routes.groovy";
+        RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
+        RouteBuilder builder = loader.load(resource);
+
+        assertThat(loader).isInstanceOf(GroovyRoutesLoader.class);
+        assertThat(builder).isNotNull();
+
+        builder.configure();
+
+        List<RouteDefinition> routes = 
builder.getRouteCollection().getRoutes();
+        assertThat(routes).hasSize(1);
+        
assertThat(routes.get(0).getInputs().get(0).getEndpointUri()).isEqualTo("timer:tick");
+        
assertThat(routes.get(0).getOutputs().get(0)).isInstanceOf(ToDefinition.class);
+    }
+}
diff --git a/runtime/jvm/src/test/resources/routes.groovy 
b/runtime/groovy/src/test/resources/routes.groovy
similarity index 100%
rename from runtime/jvm/src/test/resources/routes.groovy
rename to runtime/groovy/src/test/resources/routes.groovy
diff --git a/runtime/jvm/pom.xml b/runtime/jvm/pom.xml
index 2ea58ff..9dc3918 100644
--- a/runtime/jvm/pom.xml
+++ b/runtime/jvm/pom.xml
@@ -11,7 +11,18 @@
 
     <artifactId>camel-k-runtime-jvm</artifactId>
 
+    <properties>
+        <kotlin.version>1.2.71</kotlin.version>
+    </properties>
+
     <dependencies>
+
+        <!-- ****************************** -->
+        <!--                                -->
+        <!-- RUNTIME                        -->
+        <!--                                -->
+        <!-- ****************************** -->
+
         <dependency>
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-core</artifactId>
@@ -25,6 +36,7 @@
             <groupId>org.apache.logging.log4j</groupId>
             <artifactId>log4j-slf4j-impl</artifactId>
             <version>${log4j2.version}</version>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.jooq</groupId>
@@ -41,12 +53,12 @@
             <artifactId>commons-lang3</artifactId>
             <version>${commons-lang.version}</version>
         </dependency>
-        <dependency>
-            <groupId>org.codehaus.groovy</groupId>
-            <artifactId>groovy</artifactId>
-            <version>${groovy.version}</version>
-            <optional>true</optional>
-        </dependency>
+
+        <!-- ****************************** -->
+        <!--                                -->
+        <!-- TESTS                          -->
+        <!--                                -->
+        <!-- ****************************** -->
 
         <dependency>
             <groupId>junit</groupId>
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Language.java 
b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Language.java
index a2a8377..d041faa 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Language.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Language.java
@@ -16,29 +16,45 @@
  */
 package org.apache.camel.k.jvm;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
 import org.apache.commons.lang3.StringUtils;
 
 public enum Language {
-    Unknow("", Collections.emptyList()),
-    JavaClass("class", Collections.singletonList("class")),
-    JavaSource("java", Collections.singletonList("java")),
-    JavaScript("js", Collections.singletonList("js")),
-    Groovy("groovy", Collections.singletonList("groovy")),
-    Xml("xml", Collections.singletonList("xml"));
+    Unknown(
+        Collections.emptyList(),
+        Collections.emptyList()),
+    JavaClass(
+        Collections.singletonList("class"),
+        Collections.singletonList("class")),
+    JavaSource(
+        Collections.singletonList("java"),
+        Collections.singletonList("java")),
+    JavaScript(
+        Arrays.asList("js", "javascript"),
+        Collections.singletonList("js")),
+    Groovy(
+        Collections.singletonList("groovy"),
+        Collections.singletonList("groovy")),
+    Xml(
+        Collections.singletonList("xml"),
+        Collections.singletonList("xml")),
+    Kotlin(
+        Arrays.asList("kotlin", "kts"),
+        Collections.singletonList("kts"));
 
-    private final String name;
+    private final List<String> names;
     private final List<String> extensions;
 
-    Language(String name, List<String> extensions) {
-        this.name = name;
+    Language(List<String> names, List<String> extensions) {
+        this.names = names;
         this.extensions = extensions;
     }
 
-    public String getName() {
-        return name;
+    public List<String> getNames() {
+        return names;
     }
 
     public List<String> getExtensions() {
@@ -47,12 +63,12 @@ public enum Language {
 
     public static Language fromLanguageName(String name) {
         for (Language language: values()) {
-            if (language.getName().equals(name)) {
+            if (language.getNames().contains(name)) {
                 return language;
             }
         }
 
-        return Unknow;
+        return Unknown;
     }
 
     public static Language fromResource(String resource) {
@@ -66,6 +82,6 @@ public enum Language {
             }
         }
 
-        return Unknow;
+        return Unknown;
     }
 }
diff --git 
a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RoutesLoaders.java 
b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RoutesLoaders.java
index 448c2bb..2581d48 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RoutesLoaders.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RoutesLoaders.java
@@ -18,10 +18,10 @@ package org.apache.camel.k.jvm;
 
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.Reader;
 import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.List;
+import java.util.ServiceLoader;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import javax.script.Bindings;
@@ -30,12 +30,9 @@ import javax.script.ScriptEngineManager;
 import javax.script.SimpleBindings;
 import javax.xml.bind.UnmarshalException;
 
-import groovy.lang.Binding;
-import groovy.lang.GroovyShell;
-import groovy.util.DelegatingScript;
 import org.apache.camel.CamelContext;
-import org.apache.camel.Component;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.k.jvm.dsl.Components;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.model.rest.RestConfigurationDefinition;
 import org.apache.camel.model.rest.RestDefinition;
@@ -43,7 +40,6 @@ import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.ResourceHelper;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.codehaus.groovy.control.CompilerConfiguration;
 import org.joor.Reflect;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -51,8 +47,13 @@ import org.slf4j.LoggerFactory;
 import static org.apache.camel.k.jvm.Constants.SCHEME_CLASSPATH;
 import static org.apache.camel.k.jvm.Constants.SCHEME_FILE;
 
-public enum RoutesLoaders implements RoutesLoader {
-    JavaClass {
+public final class RoutesLoaders {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(RoutesLoaders.class);
+
+    private RoutesLoaders() {
+    }
+
+    public static class JavaClass implements RoutesLoader {
         @Override
         public List<Language> getSupportedLanguages() {
             return Collections.singletonList(Language.JavaClass);
@@ -72,8 +73,9 @@ public enum RoutesLoaders implements RoutesLoader {
 
             return (RouteBuilder)type.newInstance();
         }
-    },
-    JavaSource {
+    }
+
+    public static class JavaSource implements RoutesLoader {
         @Override
         public List<Language> getSupportedLanguages() {
             return Collections.singletonList(Language.JavaSource);
@@ -93,15 +95,16 @@ public enum RoutesLoaders implements RoutesLoader {
                         }
 
                         // Wrap routes builder
-                        includeRoutes(
+                        addRoutes(
                             Reflect.compile(name, IOUtils.toString(is, 
StandardCharsets.UTF_8)).create().get()
                         );
                     }
                 }
             };
         }
-    },
-    JavaScript {
+    }
+
+    public static class JavaScript implements RoutesLoader {
         @Override
         public List<Language> getSupportedLanguages() {
             return Collections.singletonList(Language.JavaScript);
@@ -119,6 +122,7 @@ public enum RoutesLoaders implements RoutesLoader {
 
                     // Exposed to the underlying script, but maybe better to 
have
                     // a nice dsl
+                    bindings.put("builder", this);
                     bindings.put("context", context);
                     bindings.put("components", new Components(context));
                     bindings.put("from", (Function<String, RouteDefinition>) 
uri -> from(uri));
@@ -131,37 +135,9 @@ public enum RoutesLoaders implements RoutesLoader {
                 }
             };
         }
-    },
-    Groovy {
-        @Override
-        public List<Language> getSupportedLanguages() {
-            return Collections.singletonList(Language.Groovy);
-        }
-
-        @Override
-        public RouteBuilder load(String resource) throws Exception {
-            return new RouteBuilder() {
-                @Override
-                public void configure() throws Exception {
-                    CompilerConfiguration cc = new CompilerConfiguration();
-                    cc.setScriptBaseClass(DelegatingScript.class.getName());
-
-                    ClassLoader cl = 
Thread.currentThread().getContextClassLoader();
-                    GroovyShell sh = new GroovyShell(cl, new Binding(), cc);
-
-                    try (InputStream is = 
ResourceHelper.resolveMandatoryResourceAsInputStream(getContext(), resource)) {
-                        Reader reader = new InputStreamReader(is);
-                        DelegatingScript script = (DelegatingScript) 
sh.parse(reader);
+    }
 
-                        // set the delegate target
-                        script.setDelegate(new ScriptingDsl(this));
-                        script.run();
-                    }
-                }
-            };
-        }
-    },
-    Xml {
+    public static class Xml implements RoutesLoader {
         @Override
         public List<Language> getSupportedLanguages() {
             return Collections.singletonList(Language.Xml);
@@ -192,9 +168,8 @@ public enum RoutesLoaders implements RoutesLoader {
                 }
             };
         }
-    };
+    }
 
-    private static final Logger LOGGER = 
LoggerFactory.getLogger(RoutesLoaders.class);
 
     public static RoutesLoader loaderFor(String resource, String languageName) 
{
         if (!resource.startsWith(SCHEME_CLASSPATH) && 
!resource.startsWith(SCHEME_FILE)) {
@@ -205,7 +180,7 @@ public enum RoutesLoaders implements RoutesLoader {
             ? Language.fromLanguageName(languageName)
             : Language.fromResource(resource);
 
-        for (RoutesLoader loader: RoutesLoaders.values()) {
+        for (RoutesLoader loader: ServiceLoader.load(RoutesLoader.class)) {
             if (loader.getSupportedLanguages().contains(language)) {
                 return loader;
             }
@@ -213,64 +188,4 @@ public enum RoutesLoaders implements RoutesLoader {
 
         throw new IllegalArgumentException("Unable to find loader for: 
resource=" + resource + " language=" + languageName);
     }
-
-    // ********************************
-    //
-    // Helpers
-    //
-    // TODO: move to a dedicate class
-    // ********************************
-
-
-    public static class Components {
-        private CamelContext context;
-
-        public Components(CamelContext context) {
-            this.context = context;
-        }
-
-        public Component get(String scheme) {
-            return context.getComponent(scheme, true);
-        }
-
-        public Component put(String scheme, Component instance) {
-            context.addComponent(scheme, instance);
-
-            return instance;
-        }
-
-        public Component make(String scheme, String type) {
-            final Class<?> clazz = 
context.getClassResolver().resolveClass(type);
-            final Component instance = 
(Component)context.getInjector().newInstance(clazz);
-
-            context.addComponent(scheme, instance);
-
-            return instance;
-        }
-    }
-
-    private static class ScriptingDsl {
-        private final RouteBuilder builder;
-
-        public final CamelContext context;
-        public final Components components;
-
-        public ScriptingDsl(RouteBuilder builder) {
-            this.builder = builder;
-            this.context = builder.getContext();
-            this.components = new Components(builder.getContext());
-        }
-
-        public RouteDefinition from(String endpoint) {
-            return builder.from(endpoint);
-        }
-
-        public RestDefinition rest() {
-            return builder.rest();
-        }
-
-        public RestConfigurationDefinition restConfiguration() {
-            return builder.restConfiguration();
-        }
-    }
 }
diff --git 
a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/dsl/Components.java 
b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/dsl/Components.java
new file mode 100644
index 0000000..bac1390
--- /dev/null
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/dsl/Components.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.k.jvm.dsl;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Component;
+
+public class Components {
+    private CamelContext context;
+
+    public Components(CamelContext context) {
+        this.context = context;
+    }
+
+    public Component get(String scheme) {
+        return context.getComponent(scheme, true);
+    }
+
+    public Component put(String scheme, Component instance) {
+        context.addComponent(scheme, instance);
+
+        return instance;
+    }
+
+    public Component make(String scheme, String type) {
+        final Class<?> clazz = context.getClassResolver().resolveClass(type);
+        final Component instance = 
(Component)context.getInjector().newInstance(clazz);
+
+        context.addComponent(scheme, instance);
+
+        return instance;
+    }
+}
diff --git 
a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/dsl/Scripting.java 
b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/dsl/Scripting.java
new file mode 100644
index 0000000..3988290
--- /dev/null
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/dsl/Scripting.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.k.jvm.dsl;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.rest.RestConfigurationDefinition;
+import org.apache.camel.model.rest.RestDefinition;
+
+public class Scripting {
+    public final RouteBuilder builder;
+    public final CamelContext context;
+    public final Components components;
+
+    public Scripting(RouteBuilder builder) {
+        this.builder = builder;
+        this.context = builder.getContext();
+        this.components = new Components(builder.getContext());
+    }
+
+    public RouteDefinition from(String endpoint) {
+        return builder.from(endpoint);
+    }
+
+    public RestDefinition rest() {
+        return builder.rest();
+    }
+
+    public RestConfigurationDefinition restConfiguration() {
+        return builder.restConfiguration();
+    }
+}
diff --git 
a/runtime/jvm/src/main/resources/META-INF/services/org.apache.camel.k.jvm.RoutesLoader
 
b/runtime/jvm/src/main/resources/META-INF/services/org.apache.camel.k.jvm.RoutesLoader
new file mode 100644
index 0000000..5a57927
--- /dev/null
+++ 
b/runtime/jvm/src/main/resources/META-INF/services/org.apache.camel.k.jvm.RoutesLoader
@@ -0,0 +1,4 @@
+org.apache.camel.k.jvm.RoutesLoaders$JavaClass
+org.apache.camel.k.jvm.RoutesLoaders$JavaSource
+org.apache.camel.k.jvm.RoutesLoaders$JavaScript
+org.apache.camel.k.jvm.RoutesLoaders$Xml
diff --git 
a/runtime/jvm/src/test/java/org/apache/camel/k/jvm/RoutesLoadersTest.java 
b/runtime/jvm/src/test/java/org/apache/camel/k/jvm/RoutesLoadersTest.java
index 4b2090f..54b492b 100644
--- a/runtime/jvm/src/test/java/org/apache/camel/k/jvm/RoutesLoadersTest.java
+++ b/runtime/jvm/src/test/java/org/apache/camel/k/jvm/RoutesLoadersTest.java
@@ -33,7 +33,7 @@ public class RoutesLoadersTest {
         RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
         RouteBuilder builder = loader.load(resource);
 
-        assertThat(loader).isSameAs(RoutesLoaders.JavaClass);
+        assertThat(loader).isInstanceOf(RoutesLoaders.JavaClass.class);
         assertThat(builder).isNotNull();
 
         builder.configure();
@@ -50,7 +50,7 @@ public class RoutesLoadersTest {
         RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
         RouteBuilder builder = loader.load(resource);
 
-        assertThat(loader).isSameAs(RoutesLoaders.JavaSource);
+        assertThat(loader).isInstanceOf(RoutesLoaders.JavaSource.class);
         assertThat(builder).isNotNull();
 
         builder.configure();
@@ -67,7 +67,7 @@ public class RoutesLoadersTest {
         RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
         RouteBuilder builder = loader.load(resource);
 
-        assertThat(loader).isSameAs(RoutesLoaders.JavaScript);
+        assertThat(loader).isInstanceOf(RoutesLoaders.JavaScript.class);
         assertThat(builder).isNotNull();
 
         builder.configure();
@@ -84,24 +84,7 @@ public class RoutesLoadersTest {
         RoutesLoader loader = RoutesLoaders.loaderFor(resource, "js");
         RouteBuilder builder = loader.load(resource);
 
-        assertThat(loader).isSameAs(RoutesLoaders.JavaScript);
-        assertThat(builder).isNotNull();
-
-        builder.configure();
-
-        List<RouteDefinition> routes = 
builder.getRouteCollection().getRoutes();
-        assertThat(routes).hasSize(1);
-        
assertThat(routes.get(0).getInputs().get(0).getEndpointUri()).isEqualTo("timer:tick");
-        
assertThat(routes.get(0).getOutputs().get(0)).isInstanceOf(ToDefinition.class);
-    }
-
-    @Test
-    public void testLoadGroovy() throws Exception {
-        String resource = "classpath:routes.groovy";
-        RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
-        RouteBuilder builder = loader.load(resource);
-
-        assertThat(loader).isSameAs(RoutesLoaders.Groovy);
+        assertThat(loader).isInstanceOf(RoutesLoaders.JavaScript.class);
         assertThat(builder).isNotNull();
 
         builder.configure();
@@ -118,7 +101,7 @@ public class RoutesLoadersTest {
         RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
         RouteBuilder builder = loader.load(resource);
 
-        assertThat(loader).isSameAs(RoutesLoaders.Xml);
+        assertThat(loader).isInstanceOf(RoutesLoaders.Xml.class);
         assertThat(builder).isNotNull();
 
         builder.configure();
diff --git a/runtime/kotlin/.gitignore b/runtime/kotlin/.gitignore
new file mode 100644
index 0000000..ed92983
--- /dev/null
+++ b/runtime/kotlin/.gitignore
@@ -0,0 +1,10 @@
+target
+
+*.iml
+
+.idea
+.project
+.metadata
+.settings
+.factorypath
+.classpath
diff --git a/runtime/kotlin/pom.xml b/runtime/kotlin/pom.xml
new file mode 100644
index 0000000..9036f76
--- /dev/null
+++ b/runtime/kotlin/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <parent>
+        <groupId>org.apache.camel.k</groupId>
+        <artifactId>camel-k-runtime-parent</artifactId>
+        <version>0.0.3-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>camel-k-runtime-kotlin</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-runtime-jvm</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-stdlib-jdk8</artifactId>
+            <version>${kotlin.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-script-util</artifactId>
+            <version>${kotlin.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-compiler-embeddable</artifactId>
+            <version>${kotlin.version}</version>
+        </dependency>
+
+        <!-- ****************************** -->
+        <!--                                -->
+        <!-- TESTS                          -->
+        <!--                                -->
+        <!-- ****************************** -->
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git 
a/runtime/kotlin/src/main/java/org/apache/camel/k/kotlin/KotlinRoutesLoader.java
 
b/runtime/kotlin/src/main/java/org/apache/camel/k/kotlin/KotlinRoutesLoader.java
new file mode 100644
index 0000000..0b887ef
--- /dev/null
+++ 
b/runtime/kotlin/src/main/java/org/apache/camel/k/kotlin/KotlinRoutesLoader.java
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.k.kotlin;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Collections;
+import java.util.List;
+import javax.script.Bindings;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.SimpleBindings;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.k.jvm.dsl.Components;
+import org.apache.camel.k.jvm.Language;
+import org.apache.camel.k.jvm.RoutesLoader;
+import org.apache.camel.util.ResourceHelper;
+
+public class KotlinRoutesLoader implements RoutesLoader {
+
+    @Override
+    public List<Language> getSupportedLanguages() {
+        return Collections.singletonList(Language.Kotlin);
+    }
+
+    @Override
+    public RouteBuilder load(String resource) throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                final CamelContext context = getContext();
+                final ScriptEngineManager manager = new ScriptEngineManager();
+                final ScriptEngine engine = 
manager.getEngineByExtension("kts");
+                final Bindings bindings = new SimpleBindings();
+
+                // Exposed to the underlying script, but maybe better to have
+                // a nice dsl
+                bindings.put("builder", this);
+                bindings.put("context", context);
+                bindings.put("components", new Components(context));
+
+                try (InputStream is = 
ResourceHelper.resolveMandatoryResourceAsInputStream(context, resource)) {
+                    StringBuilder builder = new StringBuilder();
+
+                    // extract global objects from 'bindings'
+                    builder.append("val builder = bindings[\"builder\"] as 
org.apache.camel.builder.RouteBuilder").append('\n');
+                    builder.append("val context = bindings[\"context\"] as 
org.apache.camel.CamelContext").append('\n');
+                    builder.append("val components = bindings[\"components\"] 
as org.apache.camel.k.jvm.dsl.Components").append('\n');
+
+                    // create aliases for common functions
+                    builder.append("fun from(uri: String): 
org.apache.camel.model.RouteDefinition = builder.from(uri)").append('\n');
+                    builder.append("fun rest(): 
org.apache.camel.model.rest.RestDefinition = builder.rest()").append('\n');
+                    builder.append("fun restConfiguration(): 
org.apache.camel.model.rest.RestConfigurationDefinition = 
builder.restConfiguration()").append('\n');
+
+                    engine.eval(builder.toString(), bindings);
+                    engine.eval(new InputStreamReader(is), bindings);
+                }
+            }
+        };
+    }
+}
diff --git 
a/runtime/kotlin/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
 
b/runtime/kotlin/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
new file mode 100644
index 0000000..f8f5900
--- /dev/null
+++ 
b/runtime/kotlin/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
@@ -0,0 +1 @@
+org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory
\ No newline at end of file
diff --git 
a/runtime/kotlin/src/main/resources/META-INF/services/org.apache.camel.k.jvm.RoutesLoader
 
b/runtime/kotlin/src/main/resources/META-INF/services/org.apache.camel.k.jvm.RoutesLoader
new file mode 100644
index 0000000..83c3f09
--- /dev/null
+++ 
b/runtime/kotlin/src/main/resources/META-INF/services/org.apache.camel.k.jvm.RoutesLoader
@@ -0,0 +1 @@
+org.apache.camel.k.kotlin.KotlinRoutesLoader
\ No newline at end of file
diff --git 
a/runtime/kotlin/src/test/java/org/apache/camel/k/kotlin/RoutesLoadersTest.java 
b/runtime/kotlin/src/test/java/org/apache/camel/k/kotlin/RoutesLoadersTest.java
new file mode 100644
index 0000000..5c23d67
--- /dev/null
+++ 
b/runtime/kotlin/src/test/java/org/apache/camel/k/kotlin/RoutesLoadersTest.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.k.kotlin;
+
+import java.util.List;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.k.jvm.RoutesLoader;
+import org.apache.camel.k.jvm.RoutesLoaders;
+import org.apache.camel.model.ProcessDefinition;
+import org.apache.camel.model.RouteDefinition;
+import org.apache.camel.model.ToDefinition;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RoutesLoadersTest {
+
+    @Test
+    public void testLoadKotlin() throws Exception {
+        String resource = "classpath:routes.kts";
+        RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
+        RouteBuilder builder = loader.load(resource);
+
+        assertThat(loader).isInstanceOf(KotlinRoutesLoader.class);
+        assertThat(builder).isNotNull();
+
+        builder.configure();
+
+        List<RouteDefinition> routes = 
builder.getRouteCollection().getRoutes();
+        assertThat(routes).hasSize(1);
+        
assertThat(routes.get(0).getInputs().get(0).getEndpointUri()).isEqualTo("timer:tick");
+        
assertThat(routes.get(0).getOutputs().get(0)).isInstanceOf(ProcessDefinition.class);
+        
assertThat(routes.get(0).getOutputs().get(1)).isInstanceOf(ToDefinition.class);
+    }
+}
diff --git a/runtime/kotlin/src/test/resources/routes.kts 
b/runtime/kotlin/src/test/resources/routes.kts
new file mode 100644
index 0000000..22b0e14
--- /dev/null
+++ b/runtime/kotlin/src/test/resources/routes.kts
@@ -0,0 +1,21 @@
+
+// ********************
+//
+// setup
+//
+// ********************
+
+//val builder = bindings["builder"] as org.apache.camel.builder.RouteBuilder
+//fun from(uri: String): org.apache.camel.model.RouteDefinition = 
builder.from(uri)
+
+// ********************
+//
+// routes
+//
+// ********************
+
+from("timer:tick")
+    .process().message {
+        m -> m.headers["MyHeader"] = "MyHeaderValue"
+    }
+    .to("log:info")
\ No newline at end of file
diff --git a/runtime/pom.xml b/runtime/pom.xml
index 0948ca3..8278b4f 100644
--- a/runtime/pom.xml
+++ b/runtime/pom.xml
@@ -24,6 +24,7 @@
         <log4j2.version>2.11.0</log4j2.version>
         <slf4j.version>1.7.25</slf4j.version>
         <groovy.version>2.5.2</groovy.version>
+        <kotlin.version>1.2.71</kotlin.version>
         <snakeyaml.version>1.23</snakeyaml.version>
 
         <fabric8-maven-plugin.version>3.5.40</fabric8-maven-plugin.version>
@@ -55,6 +56,8 @@
 
     <modules>
         <module>jvm</module>
+        <module>groovy</module>
+        <module>kotlin</module>
         <module>catalog-builder</module>
         <module>dependency-lister</module>
     </modules>

Reply via email to