You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@camel.apache.org by GitBox <gi...@apache.org> on 2018/11/26 10:27:40 UTC

[GitHub] nicolaferraro closed pull request #239: support for spring boot

nicolaferraro closed pull request #239: support for spring boot
URL: https://github.com/apache/camel-k/pull/239
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/deploy/platform-integration-context-spring-boot.yaml b/deploy/platform-integration-context-spring-boot.yaml
new file mode 100644
index 0000000..e52d9f7
--- /dev/null
+++ b/deploy/platform-integration-context-spring-boot.yaml
@@ -0,0 +1,18 @@
+apiVersion: camel.apache.org/v1alpha1
+kind: IntegrationContext
+metadata:
+  name: spring-boot
+  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:spring-boot
+    - camel:core
+  traits:
+    springboot:
+      configuration:
+        enabled: "true"
diff --git a/deploy/resources.go b/deploy/resources.go
index d67602b..223d55b 100644
--- a/deploy/resources.go
+++ b/deploy/resources.go
@@ -2680,6 +2680,28 @@ spec:
     - runtime:jvm
     - runtime:kotlin
     - camel:core
+`
+	Resources["platform-integration-context-spring-boot.yaml"] =
+		`
+apiVersion: camel.apache.org/v1alpha1
+kind: IntegrationContext
+metadata:
+  name: spring-boot
+  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:spring-boot
+    - camel:core
+  traits:
+    springboot:
+      configuration:
+        enabled: "true"
+
 `
 	Resources["user-cluster-role.yaml"] =
 		`
diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index acb2270..901c001 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -286,4 +286,5 @@ const (
 type Artifact struct {
 	ID       string `json:"id" yaml:"id"`
 	Location string `json:"location,omitempty" yaml:"location,omitempty"`
+	Target   string `json:"target,omitempty" yaml:"target,omitempty"`
 }
diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go
index 1e120f6..a04f3d8 100644
--- a/pkg/builder/builder.go
+++ b/pkg/builder/builder.go
@@ -141,10 +141,12 @@ func (b *defaultBuilder) submit(request Request) {
 	b.request.Store(request.Meta.Name, r)
 
 	c := Context{
-		C:         b.ctx,
-		Path:      builderPath,
-		Namespace: b.namespace,
-		Request:   request,
+		C:                b.ctx,
+		Path:             builderPath,
+		Namespace:        b.namespace,
+		Request:          request,
+		ComputeClasspath: true,
+		Image:            "fabric8/s2i-java:2.3", // TODO: externalize
 	}
 
 	// Sort steps by phase
@@ -152,6 +154,7 @@ func (b *defaultBuilder) submit(request Request) {
 		return request.Steps[i].Phase() < request.Steps[j].Phase()
 	})
 
+	b.log.Infof("steps: %v", request.Steps)
 	for _, step := range request.Steps {
 		if c.Error != nil {
 			break
diff --git a/pkg/builder/builder_steps.go b/pkg/builder/builder_steps.go
index b29e81e..e0c34e7 100644
--- a/pkg/builder/builder_steps.go
+++ b/pkg/builder/builder_steps.go
@@ -154,6 +154,7 @@ func ComputeDependencies(ctx *Context) error {
 		ctx.Artifacts = append(ctx.Artifacts, v1alpha1.Artifact{
 			ID:       e.ID,
 			Location: e.Location,
+			Target:   "dependencies",
 		})
 	}
 
@@ -166,7 +167,7 @@ type ArtifactsSelector func([]v1alpha1.Artifact) (string, []v1alpha1.Artifact, e
 // StandardPackager --
 func StandardPackager(ctx *Context) error {
 	return packager(ctx, func(libraries []v1alpha1.Artifact) (string, []v1alpha1.Artifact, error) {
-		return "fabric8/s2i-java:2.3", libraries, nil
+		return ctx.Image, libraries, nil
 	})
 }
 
@@ -191,15 +192,19 @@ func IncrementalPackager(ctx *Context) error {
 		}
 
 		// return default selection
-		return "fabric8/s2i-java:2.3", libraries, nil
+		return ctx.Image, libraries, nil
 	})
 }
 
+// ClassPathPackager --
 func packager(ctx *Context, selector ArtifactsSelector) error {
 	imageName, selectedArtifacts, err := selector(ctx.Artifacts)
 	if err != nil {
 		return err
 	}
+	if imageName == "" {
+		imageName = ctx.Image
+	}
 
 	tarFileName := path.Join(ctx.Path, "package", "occi.tar")
 	tarFileDir := path.Dir(tarFileName)
@@ -215,38 +220,38 @@ func packager(ctx *Context, selector ArtifactsSelector) error {
 	}
 	defer tarAppender.Close()
 
-	tarDir := "dependencies/"
 	for _, entry := range selectedArtifacts {
 		gav, err := maven.ParseGAV(entry.ID)
 		if err != nil {
 			return err
 		}
 
-		tarPath := path.Join(tarDir, gav.GroupID)
-		_, err = tarAppender.AddFile(entry.Location, tarPath)
+		_, fileName := path.Split(entry.Location)
+
+		_, err = tarAppender.AddFileWithName(gav.GroupID+"."+fileName, entry.Location, entry.Target)
 		if err != nil {
 			return err
 		}
 	}
 
-	cp := ""
-	for _, entry := range ctx.Artifacts {
-		gav, err := maven.ParseGAV(entry.ID)
-		if err != nil {
-			return nil
+	if ctx.ComputeClasspath {
+		cp := ""
+		for _, entry := range ctx.Artifacts {
+			gav, err := maven.ParseGAV(entry.ID)
+			if err != nil {
+				return nil
+			}
+			_, fileName := path.Split(entry.Location)
+			cp += path.Join(entry.Target, gav.GroupID+"."+fileName) + "\n"
 		}
-		tarPath := path.Join(tarDir, gav.GroupID)
-		_, fileName := path.Split(entry.Location)
-		fileName = path.Join(tarPath, fileName)
-		cp += fileName + "\n"
-	}
 
-	err = tarAppender.AppendData([]byte(cp), "classpath")
-	if err != nil {
-		return err
+		err = tarAppender.AppendData([]byte(cp), "classpath")
+		if err != nil {
+			return err
+		}
 	}
 
-	ctx.Image = imageName //"fabric8/s2i-java:2.3"
+	ctx.Image = imageName
 	ctx.Archive = tarFileName
 
 	return nil
@@ -299,7 +304,8 @@ func FindBestImage(images []PublishedImage, entries []v1alpha1.Artifact) (*Publi
 		}
 		numCommonLibs := len(common)
 		surplus := len(image.Classpath) - numCommonLibs
-		if surplus >= numCommonLibs/3 {
+
+		if numCommonLibs != len(image.Classpath) && surplus >= numCommonLibs/3 {
 			// Heuristic approach: if there are too many unrelated libraries, just use the base image
 			continue
 		}
diff --git a/pkg/builder/builder_types.go b/pkg/builder/builder_types.go
index e23dfc0..a81f879 100644
--- a/pkg/builder/builder_types.go
+++ b/pkg/builder/builder_types.go
@@ -31,6 +31,8 @@ import (
 )
 
 const (
+	// IntiPhase --
+	IntiPhase int32 = 0
 	// ProjectGenerationPhase --
 	ProjectGenerationPhase int32 = 10
 	// ProjectBuildPhase --
@@ -59,7 +61,7 @@ type Step interface {
 type stepWrapper struct {
 	id    string
 	phase int32
-	task  func(*Context) error
+	task  StepTask
 }
 
 func (s *stepWrapper) String() string {
@@ -78,8 +80,11 @@ func (s *stepWrapper) Execute(ctx *Context) error {
 	return s.task(ctx)
 }
 
+// StepTask ---
+type StepTask func(*Context) error
+
 // NewStep --
-func NewStep(ID string, phase int32, task func(*Context) error) Step {
+func NewStep(ID string, phase int32, task StepTask) Step {
 	s := stepWrapper{
 		id:    ID,
 		phase: phase,
@@ -123,15 +128,17 @@ type Result struct {
 
 // Context --
 type Context struct {
-	C         context.Context
-	Request   Request
-	Image     string
-	Error     error
-	Namespace string
-	Project   maven.Project
-	Path      string
-	Artifacts []v1alpha1.Artifact
-	Archive   string
+	C                context.Context
+	Request          Request
+	Image            string
+	Error            error
+	Namespace        string
+	Project          maven.Project
+	Path             string
+	Artifacts        []v1alpha1.Artifact
+	Archive          string
+	ComputeClasspath bool
+	MainClass        string
 }
 
 // PublishedImage --
diff --git a/pkg/builder/springboot/dependencies.go b/pkg/builder/springboot/dependencies.go
new file mode 100644
index 0000000..6c3aec1
--- /dev/null
+++ b/pkg/builder/springboot/dependencies.go
@@ -0,0 +1,37 @@
+/*
+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 springboot
+
+import (
+	"strings"
+
+	"github.com/apache/camel-k/pkg/builder"
+)
+
+// ComputeDependencies --
+func ComputeDependencies(ctx *builder.Context) error {
+	for i := 0; i < len(ctx.Artifacts); i++ {
+		if strings.HasPrefix(ctx.Artifacts[i].ID, "org.apache.camel.k:camel-k-runtime-spring-boot:") {
+			// Don't set a target so the jar will be copied to the
+			// deployment root
+			ctx.Artifacts[i].Target = ""
+		}
+	}
+
+	return nil
+}
diff --git a/pkg/builder/springboot/generator.go b/pkg/builder/springboot/generator.go
new file mode 100644
index 0000000..b812046
--- /dev/null
+++ b/pkg/builder/springboot/generator.go
@@ -0,0 +1,161 @@
+/*
+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 springboot
+
+import (
+	"encoding/xml"
+	"fmt"
+	"strings"
+
+	"github.com/apache/camel-k/pkg/builder"
+	"github.com/apache/camel-k/pkg/util/maven"
+	"github.com/apache/camel-k/version"
+)
+
+// GenerateProject --
+func GenerateProject(ctx *builder.Context) error {
+	ctx.Project = maven.Project{
+		XMLName:           xml.Name{Local: "project"},
+		XMLNs:             "http://maven.apache.org/POM/4.0.0",
+		XMLNsXsi:          "http://www.w3.org/2001/XMLSchema-instance",
+		XsiSchemaLocation: "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd",
+		ModelVersion:      "4.0.0",
+		GroupID:           "org.apache.camel.k.integration",
+		ArtifactID:        "camel-k-integration",
+		Version:           version.Version,
+		DependencyManagement: maven.DependencyManagement{
+			Dependencies: maven.Dependencies{
+				Dependencies: []maven.Dependency{
+					{
+						//TODO: camel version should be retrieved from an external request or provided as static version
+						GroupID:    "org.apache.camel",
+						ArtifactID: "camel-bom",
+						Version:    "2.22.2",
+						Type:       "pom",
+						Scope:      "import",
+					},
+				},
+			},
+		},
+		Dependencies: maven.Dependencies{
+			Dependencies: make([]maven.Dependency, 0),
+		},
+	}
+
+	//
+	// set-up dependencies
+	//
+
+	deps := &ctx.Project.Dependencies
+
+	//
+	// common
+	//
+
+	deps.Add(maven.Dependency{
+		GroupID:    "org.apache.camel.k",
+		ArtifactID: "camel-k-runtime-spring-boot",
+		Version:    version.Version,
+		Exclusions: &maven.Exclusions{
+			Exclusions: []maven.Exclusion{
+				{
+					GroupID:    "org.apache.camel",
+					ArtifactID: "*",
+				},
+				{
+					GroupID:    "org.apache.camel.k",
+					ArtifactID: "*",
+				},
+				{
+					GroupID:    "org.springframework.boot",
+					ArtifactID: "*",
+				},
+			},
+		},
+	})
+
+	//
+	// others
+	//
+
+	for _, d := range ctx.Request.Dependencies {
+		if strings.HasPrefix(d, "camel:") {
+			if d == "camel:core" {
+				continue
+			}
+
+			artifactID := strings.TrimPrefix(d, "camel:")
+
+			if !strings.HasPrefix(artifactID, "camel-") {
+				artifactID = "camel-" + artifactID
+			}
+
+			deps.Add(maven.Dependency{
+				GroupID:    "org.apache.camel",
+				ArtifactID: artifactID + "-starter",
+				Version:    "2.22.2",
+				Exclusions: &maven.Exclusions{
+					Exclusions: []maven.Exclusion{
+						{
+							GroupID:    "com.sun.xml.bind",
+							ArtifactID: "*",
+						},
+						{
+							GroupID:    "org.apache.camel",
+							ArtifactID: "camel-core",
+						},
+						{
+							GroupID:    "org.apache.camel",
+							ArtifactID: "camel-core-starter",
+						},
+						{
+							GroupID:    "org.apache.camel",
+							ArtifactID: "camel-spring-boot-starter",
+						},
+						{
+							GroupID:    "org.springframework.boot",
+							ArtifactID: "spring-boot-starter",
+						},
+					},
+				},
+			})
+		} else if strings.HasPrefix(d, "mvn:") {
+			mid := strings.TrimPrefix(d, "mvn:")
+			gav := strings.Replace(mid, "/", ":", -1)
+
+			deps.AddEncodedGAV(gav)
+		} else if strings.HasPrefix(d, "runtime:") {
+			if d == "runtime:jvm" {
+				// common
+				continue
+			}
+			if d == "runtime:spring-boot" {
+				// common
+				continue
+			}
+
+			artifactID := strings.Replace(d, "runtime:", "camel-k-runtime-", 1)
+
+			deps.AddGAV("org.apache.camel.k", artifactID, version.Version)
+		} else {
+			return fmt.Errorf("unknown dependency type: %s", d)
+		}
+	}
+
+	return nil
+}
diff --git a/pkg/builder/springboot/initializer.go b/pkg/builder/springboot/initializer.go
new file mode 100644
index 0000000..de40ea6
--- /dev/null
+++ b/pkg/builder/springboot/initializer.go
@@ -0,0 +1,34 @@
+/*
+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 springboot
+
+import (
+	"github.com/apache/camel-k/pkg/builder"
+)
+
+// Initialize --
+func Initialize(ctx *builder.Context) error {
+	// set the base image
+	//ctx.Image = "kamel-k/s2i-boot:" + version.Version
+
+	// no need to compute classpath as we do use spring boot own
+	// loader: PropertiesLauncher
+	ctx.ComputeClasspath = false
+
+	return nil
+}
diff --git a/pkg/stub/action/integration/build.go b/pkg/stub/action/integration/build.go
index 02387ce..e4660c8 100644
--- a/pkg/stub/action/integration/build.go
+++ b/pkg/stub/action/integration/build.go
@@ -119,6 +119,7 @@ func (action *buildAction) Handle(integration *v1alpha1.Integration) error {
 	platformCtx.Spec = v1alpha1.IntegrationContextSpec{
 		Dependencies: integration.Spec.Dependencies,
 		Repositories: integration.Spec.Repositories,
+		Traits:       integration.Spec.Traits,
 	}
 
 	if err := sdk.Create(&platformCtx); err != nil {
diff --git a/pkg/stub/action/platform/create.go b/pkg/stub/action/platform/create.go
index 0933e2f..5f8ea2f 100644
--- a/pkg/stub/action/platform/create.go
+++ b/pkg/stub/action/platform/create.go
@@ -28,6 +28,7 @@ var resources = []string{
 	"platform-integration-context-jvm.yaml",
 	"platform-integration-context-groovy.yaml",
 	"platform-integration-context-kotlin.yaml",
+	"platform-integration-context-spring-boot.yaml",
 }
 
 // NewCreateAction returns a action that creates resources needed by the platform
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index 9db06ab..4684202 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -21,6 +21,8 @@ import (
 	"reflect"
 	"strings"
 
+	"github.com/sirupsen/logrus"
+
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/platform"
 	"github.com/fatih/structs"
@@ -36,6 +38,7 @@ type Catalog struct {
 	tIngress      Trait
 	tOwner        Trait
 	tBuilder      Trait
+	tSpringBoot   Trait
 }
 
 // NewCatalog creates a new trait Catalog
@@ -49,6 +52,7 @@ func NewCatalog() *Catalog {
 		tIngress:      newIngressTrait(),
 		tOwner:        newOwnerTrait(),
 		tBuilder:      newBuilderTrait(),
+		tSpringBoot:   newSpringBootTrait(),
 	}
 }
 
@@ -62,6 +66,7 @@ func (c *Catalog) allTraits() []Trait {
 		c.tIngress,
 		c.tOwner,
 		c.tBuilder,
+		c.tSpringBoot,
 	}
 }
 
@@ -78,27 +83,30 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait {
 	case v1alpha1.TraitProfileOpenShift:
 		return []Trait{
 			c.tDependencies,
-			c.tDeployment,
 			c.tService,
 			c.tRoute,
-			c.tOwner,
 			c.tBuilder,
+			c.tSpringBoot,
+			c.tDeployment,
+			c.tOwner,
 		}
 	case v1alpha1.TraitProfileKubernetes:
 		return []Trait{
 			c.tDependencies,
-			c.tDeployment,
 			c.tService,
 			c.tIngress,
-			c.tOwner,
 			c.tBuilder,
+			c.tSpringBoot,
+			c.tDeployment,
+			c.tOwner,
 		}
 	case v1alpha1.TraitProfileKnative:
 		return []Trait{
 			c.tDependencies,
 			c.tKnative,
-			c.tOwner,
 			c.tBuilder,
+			c.tSpringBoot,
+			c.tOwner,
 		}
 	}
 
@@ -108,6 +116,7 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait {
 func (c *Catalog) apply(environment *Environment) error {
 	c.configure(environment)
 	traits := c.traitsFor(environment)
+
 	for _, trait := range traits {
 		if !trait.appliesTo(environment) {
 			continue
@@ -119,6 +128,7 @@ func (c *Catalog) apply(environment *Environment) error {
 			}
 		}
 		if trait.IsEnabled() {
+			logrus.Infof("apply trait: %s", trait.ID())
 			if err := trait.apply(environment); err != nil {
 				return err
 			}
@@ -139,13 +149,20 @@ func (c *Catalog) GetTrait(id string) Trait {
 }
 
 func (c *Catalog) configure(env *Environment) {
-	if env.Integration == nil || env.Integration.Spec.Traits == nil {
-		return
+	if env.Context != nil && env.Context.Spec.Traits != nil {
+		for id, traitSpec := range env.Context.Spec.Traits {
+			catTrait := c.GetTrait(id)
+			if catTrait != nil {
+				traitSpec.Decode(catTrait)
+			}
+		}
 	}
-	for id, traitSpec := range env.Integration.Spec.Traits {
-		catTrait := c.GetTrait(id)
-		if catTrait != nil {
-			traitSpec.Decode(catTrait)
+	if env.Integration != nil && env.Integration.Spec.Traits != nil {
+		for id, traitSpec := range env.Integration.Spec.Traits {
+			catTrait := c.GetTrait(id)
+			if catTrait != nil {
+				traitSpec.Decode(catTrait)
+			}
 		}
 	}
 }
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index bc3f121..8411e3a 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -146,6 +146,11 @@ func getDeploymentFor(e *Environment) *appsv1.Deployment {
 	// optimizations
 	environment["AB_JOLOKIA_OFF"] = "true"
 
+	// add env vars from traits
+	for k, v := range e.EnvVars {
+		environment[k] = v
+	}
+
 	labels := map[string]string{
 		"camel.apache.org/integration": e.Integration.Name,
 	}
diff --git a/pkg/trait/springboot.go b/pkg/trait/springboot.go
new file mode 100644
index 0000000..7069d67
--- /dev/null
+++ b/pkg/trait/springboot.go
@@ -0,0 +1,103 @@
+/*
+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 trait
+
+import (
+	"sort"
+
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/builder"
+	"github.com/apache/camel-k/pkg/builder/springboot"
+	"github.com/apache/camel-k/pkg/util"
+)
+
+type springBootTrait struct {
+	BaseTrait `property:",squash"`
+}
+
+func newSpringBootTrait() *springBootTrait {
+	return &springBootTrait{
+		BaseTrait: newBaseTrait("springboot"),
+	}
+}
+
+// IsAuto determines if we should apply automatic configuration
+func (trait *springBootTrait) IsAuto() bool {
+	return false
+}
+
+// IsEnabled is used to determine if the trait needs to be executed
+func (trait *springBootTrait) IsEnabled() bool {
+	if trait.Enabled == nil {
+		return false
+	}
+	return *trait.Enabled
+}
+
+func (trait *springBootTrait) appliesTo(e *Environment) bool {
+	if e.Context != nil && e.Context.Status.Phase == v1alpha1.IntegrationContextPhaseBuilding {
+		return true
+	}
+	if e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying {
+		return true
+	}
+	if e.Integration != nil && e.Integration.Status.Phase == "" {
+		return true
+	}
+
+	return false
+}
+
+func (trait *springBootTrait) apply(e *Environment) error {
+
+	//
+	// Integration
+	//
+
+	if e.Integration != nil && e.Integration.Status.Phase == "" {
+		util.StringSliceUniqueAdd(&e.Integration.Spec.Dependencies, "runtime:spring-boot")
+
+		// sort the dependencies to get always the same list if they don't change
+		sort.Strings(e.Integration.Spec.Dependencies)
+	}
+
+	if e.Integration != nil && e.Integration.Status.Phase == v1alpha1.IntegrationPhaseDeploying {
+		// Override env vars
+		e.EnvVars["JAVA_MAIN_CLASS"] = "org.springframework.boot.loader.PropertiesLauncher"
+		e.EnvVars["LOADER_PATH"] = "/deployments/dependencies/"
+	}
+
+	//
+	// Integration Context
+	//
+
+	if e.Context != nil && e.Context.Status.Phase == v1alpha1.IntegrationContextPhaseBuilding {
+		// add custom initialization logic
+		e.Steps = append(e.Steps, builder.NewStep("initialize/spring-boot", builder.IntiPhase, springboot.Initialize))
+		e.Steps = append(e.Steps, builder.NewStep("build/compute-boot-dependencies", builder.ProjectBuildPhase+1, springboot.ComputeDependencies))
+
+		// replace project generator
+		for i := 0; i < len(e.Steps); i++ {
+			if e.Steps[i].Phase() == builder.ProjectGenerationPhase {
+				e.Steps[i] = builder.NewStep("generate/spring-boot", builder.ProjectGenerationPhase, springboot.GenerateProject)
+			}
+		}
+	}
+
+	return nil
+}
diff --git a/pkg/trait/trait.go b/pkg/trait/trait.go
index 1ab5f9e..042b4d5 100644
--- a/pkg/trait/trait.go
+++ b/pkg/trait/trait.go
@@ -72,5 +72,6 @@ func newEnvironment(integration *v1alpha1.Integration, ctx *v1alpha1.Integration
 		Integration:    integration,
 		ExecutedTraits: make([]ID, 0),
 		Resources:      kubernetes.NewCollection(),
+		EnvVars:        make(map[string]string),
 	}, nil
 }
diff --git a/pkg/trait/types.go b/pkg/trait/types.go
index 8c1d09a..6b87029 100644
--- a/pkg/trait/types.go
+++ b/pkg/trait/types.go
@@ -100,6 +100,7 @@ type Environment struct {
 	Resources      *kubernetes.Collection
 	Steps          []builder.Step
 	ExecutedTraits []ID
+	EnvVars        map[string]string
 }
 
 // IntegrationInPhase --
diff --git a/pkg/util/maven/maven_project.go b/pkg/util/maven/maven_project.go
index 5c7ec5d..57603a1 100644
--- a/pkg/util/maven/maven_project.go
+++ b/pkg/util/maven/maven_project.go
@@ -73,14 +73,26 @@ func (deps *Dependencies) AddEncodedGAV(gav string) {
 	}
 }
 
-// Dependency represent a maven's dependency
-type Dependency struct {
+// Exclusion represent a maven's dependency exlucsion
+type Exclusion struct {
 	GroupID    string `xml:"groupId"`
 	ArtifactID string `xml:"artifactId"`
-	Version    string `xml:"version,omitempty"`
-	Type       string `xml:"type,omitempty"`
-	Classifier string `xml:"classifier,omitempty"`
-	Scope      string `xml:"scope,omitempty"`
+}
+
+// Exclusions --
+type Exclusions struct {
+	Exclusions []Exclusion `xml:"exclusion"`
+}
+
+// Dependency represent a maven's dependency
+type Dependency struct {
+	GroupID    string      `xml:"groupId"`
+	ArtifactID string      `xml:"artifactId"`
+	Version    string      `xml:"version,omitempty"`
+	Type       string      `xml:"type,omitempty"`
+	Classifier string      `xml:"classifier,omitempty"`
+	Scope      string      `xml:"scope,omitempty"`
+	Exclusions *Exclusions `xml:"exclusions,omitempty"`
 }
 
 // NewDependency create an new dependency from the given gav info
diff --git a/pkg/util/tar/appender.go b/pkg/util/tar/appender.go
index 965bbc7..7ffbfad 100644
--- a/pkg/util/tar/appender.go
+++ b/pkg/util/tar/appender.go
@@ -92,6 +92,38 @@ func (t *Appender) AddFile(filePath string, tarDir string) (string, error) {
 	return fileName, nil
 }
 
+// AddFileWithName adds a file content to the tarDir, using the fiven file name.
+// It returns the full path of the file inside the tar.
+func (t *Appender) AddFileWithName(fileName string, filePath string, tarDir string) (string, error) {
+	info, err := os.Stat(filePath)
+	if err != nil {
+		return "", err
+	}
+	if tarDir != "" {
+		fileName = path.Join(tarDir, fileName)
+	}
+
+	t.writer.WriteHeader(&atar.Header{
+		Name:    fileName,
+		Size:    info.Size(),
+		Mode:    int64(info.Mode()),
+		ModTime: info.ModTime(),
+	})
+
+	file, err := os.Open(filePath)
+	if err != nil {
+		return "", err
+	}
+	defer file.Close()
+
+	_, err = io.Copy(t.writer, file)
+	if err != nil {
+		return "", errors.Wrap(err, "cannot add file to the tar archive")
+	}
+
+	return fileName, nil
+}
+
 // AppendData appends the given content to a file inside the tar, creating it if it does not exist
 func (t *Appender) AppendData(data []byte, tarPath string) error {
 	t.writer.WriteHeader(&atar.Header{
diff --git a/runtime/examples/hello.xml b/runtime/examples/hello.xml
index ff9bc8d..4c0535a 100644
--- a/runtime/examples/hello.xml
+++ b/runtime/examples/hello.xml
@@ -14,8 +14,10 @@
     limitations under the License.
 -->
 <routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-              xmlns="http://camel.apache.org/schema/spring"
-              xsi:schemaLocation="http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
+        xmlns="http://camel.apache.org/schema/spring"
+        xsi:schemaLocation="
+            http://camel.apache.org/schema/spring
+            http://camel.apache.org/schema/spring/camel-spring.xsd">
 
     <route id="hello">
         <from uri="timer:hello?period=3s"/>
diff --git a/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/LoaderTest.groovy b/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/LoaderTest.groovy
index 16b4f0f..b7c7c95 100644
--- a/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/LoaderTest.groovy
+++ b/runtime/groovy/src/test/groovy/org/apache/camel/k/groovy/LoaderTest.groovy
@@ -16,9 +16,8 @@
  */
 package org.apache.camel.k.groovy
 
-
 import org.apache.camel.k.jvm.RoutesLoaders
-import org.apache.camel.k.jvm.RuntimeRegistry
+import org.apache.camel.k.jvm.SimpleRuntimeRegistry
 import org.apache.camel.model.ToDefinition
 import spock.lang.Specification
 
@@ -30,7 +29,7 @@ class LoaderTest extends Specification {
 
         when:
             def loader = RoutesLoaders.loaderFor(resource, null)
-            def builder = loader.load(new RuntimeRegistry(), resource)
+            def builder = loader.load(new SimpleRuntimeRegistry(), resource)
 
         then:
             loader instanceof GroovyRoutesLoader
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java
index d26ddef..69cbc96 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Application.java
@@ -21,12 +21,8 @@
 import org.apache.camel.main.MainListenerSupport;
 import org.apache.camel.support.LifecycleStrategySupport;
 import org.apache.camel.util.ObjectHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class Application {
-    private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
-
     static {
         //
         // Load properties as system properties so they are accessible through
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Runtime.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Runtime.java
index d4a755c..ffa71a0 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Runtime.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/Runtime.java
@@ -36,7 +36,7 @@
     private static final Logger LOGGER = LoggerFactory.getLogger(Runtime.class);
 
     private final ConcurrentMap<String, CamelContext> contextMap;
-    private final RuntimeRegistry registry = new RuntimeRegistry();
+    private final RuntimeRegistry registry = new SimpleRuntimeRegistry();
 
     public Runtime() {
         this.contextMap = new ConcurrentHashMap<>();
@@ -53,7 +53,7 @@ public void load(String[] routes) throws Exception {
             final RoutesLoader loader = RoutesLoaders.loaderFor(location, language);
             final RouteBuilder builder = loader.load(registry, location);
 
-            if (routes == null) {
+            if (builder == null) {
                 throw new IllegalStateException("Unable to load route from: " + route);
             }
 
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeRegistry.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeRegistry.java
index 8fc7589..3b41e2b 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeRegistry.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeRegistry.java
@@ -16,85 +16,12 @@
  */
 package org.apache.camel.k.jvm;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.stream.Collectors;
-
-import org.apache.camel.NoSuchBeanException;
 import org.apache.camel.spi.Registry;
 
-public class RuntimeRegistry implements Registry {
-    private final ConcurrentMap<String, Object> registry;
-
-    public RuntimeRegistry() {
-        this.registry = new ConcurrentHashMap<>();
-    }
-
-    public void bind(String name, Object bean) {
-        this.registry.put(name, bean);
-    }
-
-    @Override
-    public Object lookupByName(String name) {
-        return registry.get(name);
-    }
-
-    @Override
-    public <T> T lookupByNameAndType(String name, Class<T> type) {
-        final Object answer = lookupByName(name);
-
-        if (answer != null) {
-            try {
-                return type.cast(answer);
-            } catch (Throwable t) {
-                throw new NoSuchBeanException(
-                    name,
-                    "Found bean: " + name + " in RuntimeRegistry: " + this + " of type: " + answer.getClass().getName() + " expected type was: " + type,
-                    t
-                );
-            }
-        }
-
-        return null;
-    }
-
-    @Override
-    public <T> Map<String, T> findByTypeWithName(Class<T> type) {
-        final Map<String, T> result = new HashMap<>();
-
-        registry.entrySet().stream()
-            .filter(entry -> type.isInstance(entry.getValue()))
-            .forEach(entry -> result.put(entry.getKey(), type.cast(entry.getValue())));
-
-        return result;
-    }
-
-    @Override
-    public <T> Set<T> findByType(Class<T> type) {
-        return registry.values().stream()
-            .filter(type::isInstance)
-            .map(type::cast)
-            .collect(Collectors.toSet());
-    }
-
-    @SuppressWarnings("deprecation")
-    @Override
-    public Object lookup(String name) {
-        return lookupByName(name);
-    }
-
-    @SuppressWarnings("deprecation")
-    @Override
-    public <T> T lookup(String name, Class<T> type) {
-        return lookupByNameAndType(name, type);
-    }
+public interface RuntimeRegistry extends Registry {
 
-    @SuppressWarnings("deprecation")
-    @Override
-    public <T> Map<String, T> lookupByType(Class<T> type) {
-        return findByTypeWithName(type);
-    }
+    /**
+     *
+     */
+    void bind(String name, Object bean);
 }
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
index 8b4db47..423ee08 100644
--- a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
@@ -41,7 +41,7 @@
     private RuntimeSupport() {
     }
 
-    public static void configureSystemProperties() {
+    public static Properties loadProperties() {
         final String conf = System.getenv(Constants.ENV_CAMEL_K_CONF);
         final String confd = System.getenv(Constants.ENV_CAMEL_K_CONF_D);
         final Properties properties = new Properties();
@@ -94,6 +94,13 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
             }
         }
 
+        return properties;
+    }
+
+    public static void configureSystemProperties() {
+        final Properties properties = loadProperties();
+
+        // TODO: sensitive info, maybe better to use properties component ...
         System.getProperties().putAll(properties);
     }
 
diff --git a/runtime/jvm/src/main/java/org/apache/camel/k/jvm/SimpleRuntimeRegistry.java b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/SimpleRuntimeRegistry.java
new file mode 100644
index 0000000..f09b331
--- /dev/null
+++ b/runtime/jvm/src/main/java/org/apache/camel/k/jvm/SimpleRuntimeRegistry.java
@@ -0,0 +1,100 @@
+/**
+ * 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;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+
+import org.apache.camel.NoSuchBeanException;
+import org.apache.camel.spi.Registry;
+
+public class SimpleRuntimeRegistry implements RuntimeRegistry {
+    private final ConcurrentMap<String, Object> registry;
+
+    public SimpleRuntimeRegistry() {
+        this.registry = new ConcurrentHashMap<>();
+    }
+
+    public void bind(String name, Object bean) {
+        this.registry.put(name, bean);
+    }
+
+    @Override
+    public Object lookupByName(String name) {
+        return registry.get(name);
+    }
+
+    @Override
+    public <T> T lookupByNameAndType(String name, Class<T> type) {
+        final Object answer = lookupByName(name);
+
+        if (answer != null) {
+            try {
+                return type.cast(answer);
+            } catch (Throwable t) {
+                throw new NoSuchBeanException(
+                    name,
+                    "Found bean: " + name + " in RuntimeRegistry: " + this + " of type: " + answer.getClass().getName() + " expected type was: " + type,
+                    t
+                );
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public <T> Map<String, T> findByTypeWithName(Class<T> type) {
+        final Map<String, T> result = new HashMap<>();
+
+        registry.entrySet().stream()
+            .filter(entry -> type.isInstance(entry.getValue()))
+            .forEach(entry -> result.put(entry.getKey(), type.cast(entry.getValue())));
+
+        return result;
+    }
+
+    @Override
+    public <T> Set<T> findByType(Class<T> type) {
+        return registry.values().stream()
+            .filter(type::isInstance)
+            .map(type::cast)
+            .collect(Collectors.toSet());
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public Object lookup(String name) {
+        return lookupByName(name);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public <T> T lookup(String name, Class<T> type) {
+        return lookupByNameAndType(name, type);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public <T> Map<String, T> lookupByType(Class<T> type) {
+        return findByTypeWithName(type);
+    }
+}
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 328aaf3..069520a 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
@@ -32,7 +32,7 @@
     public void testLoadClass() throws Exception {
         String resource = "classpath:" + MyRoutes.class.getName() + ".class";
         RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
-        RouteBuilder builder = loader.load(new RuntimeRegistry(), resource);
+        RouteBuilder builder = loader.load(new SimpleRuntimeRegistry(), resource);
 
         assertThat(loader).isInstanceOf(RoutesLoaders.JavaClass.class);
         assertThat(builder).isNotNull();
@@ -49,7 +49,7 @@ public void testLoadClass() throws Exception {
     public void testLoadJava() throws Exception {
         String resource = "classpath:MyRoutes.java";
         RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
-        RouteBuilder builder = loader.load(new RuntimeRegistry(), resource);
+        RouteBuilder builder = loader.load(new SimpleRuntimeRegistry(), resource);
 
         assertThat(loader).isInstanceOf(RoutesLoaders.JavaSource.class);
         assertThat(builder).isNotNull();
@@ -66,7 +66,7 @@ public void testLoadJava() throws Exception {
     public void testLoadJavaScript() throws Exception {
         String resource = "classpath:routes.js";
         RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
-        RouteBuilder builder = loader.load(new RuntimeRegistry(), resource);
+        RouteBuilder builder = loader.load(new SimpleRuntimeRegistry(), resource);
 
         assertThat(loader).isInstanceOf(RoutesLoaders.JavaScript.class);
         assertThat(builder).isNotNull();
@@ -83,7 +83,7 @@ public void testLoadJavaScript() throws Exception {
     public void testLoadJavaScriptWithCustomExtension() throws Exception {
         String resource = "classpath:routes.mytype";
         RoutesLoader loader = RoutesLoaders.loaderFor(resource, "js");
-        RouteBuilder builder = loader.load(new RuntimeRegistry(), resource);
+        RouteBuilder builder = loader.load(new SimpleRuntimeRegistry(), resource);
 
         assertThat(loader).isInstanceOf(RoutesLoaders.JavaScript.class);
         assertThat(builder).isNotNull();
@@ -100,7 +100,7 @@ public void testLoadJavaScriptWithCustomExtension() throws Exception {
     public void testLoadXml() throws Exception {
         String resource = "classpath:routes.xml";
         RoutesLoader loader = RoutesLoaders.loaderFor(resource, null);
-        RouteBuilder builder = loader.load(new RuntimeRegistry(), resource);
+        RouteBuilder builder = loader.load(new SimpleRuntimeRegistry(), resource);
 
         assertThat(loader).isInstanceOf(RoutesLoaders.Xml.class);
         assertThat(builder).isNotNull();
diff --git a/runtime/kotlin/src/test/kotlin/org/apache/camel/k/kotlin/LoaderTest.kt b/runtime/kotlin/src/test/kotlin/org/apache/camel/k/kotlin/LoaderTest.kt
index da4ab18..00dfcea 100644
--- a/runtime/kotlin/src/test/kotlin/org/apache/camel/k/kotlin/LoaderTest.kt
+++ b/runtime/kotlin/src/test/kotlin/org/apache/camel/k/kotlin/LoaderTest.kt
@@ -17,7 +17,7 @@
 package org.apache.camel.k.kotlin
 
 import org.apache.camel.k.jvm.RoutesLoaders
-import org.apache.camel.k.jvm.RuntimeRegistry
+import org.apache.camel.k.jvm.SimpleRuntimeRegistry
 import org.apache.camel.model.ProcessDefinition
 import org.apache.camel.model.ToDefinition
 import org.assertj.core.api.Assertions.assertThat
@@ -29,7 +29,7 @@ class LoaderTest {
     fun `load route from classpath`() {
         val resource = "classpath:routes.kts"
         val loader = RoutesLoaders.loaderFor(resource, null)
-        val builder = loader.load(RuntimeRegistry(), resource)
+        val builder = loader.load(SimpleRuntimeRegistry(), resource)
 
         assertThat(loader).isInstanceOf(KotlinRoutesLoader::class.java)
         assertThat(builder).isNotNull()
diff --git a/runtime/pom.xml b/runtime/pom.xml
index 3ddf47c..3be5314 100644
--- a/runtime/pom.xml
+++ b/runtime/pom.xml
@@ -48,6 +48,7 @@
         <snakeyaml.version>1.23</snakeyaml.version>
         <spock.version>1.0-groovy-2.4</spock.version>
         <jackson.version>2.9.7</jackson.version>
+        <spring-boot.version>2.0.3.RELEASE</spring-boot.version>
 
         <gmavenplus-plugin.version>1.6.1</gmavenplus-plugin.version>
         <fabric8-maven-plugin.version>3.5.40</fabric8-maven-plugin.version>
@@ -100,6 +101,7 @@
         <module>jvm</module>
         <module>groovy</module>
         <module>kotlin</module>
+        <module>spring-boot</module>
         <module>catalog-builder</module>
         <module>dependency-lister</module>
         <module>camel-knative-http</module>
diff --git a/runtime/spring-boot-example/.gitignore b/runtime/spring-boot-example/.gitignore
new file mode 100644
index 0000000..ed92983
--- /dev/null
+++ b/runtime/spring-boot-example/.gitignore
@@ -0,0 +1,10 @@
+target
+
+*.iml
+
+.idea
+.project
+.metadata
+.settings
+.factorypath
+.classpath
diff --git a/runtime/spring-boot-example/pom.xml b/runtime/spring-boot-example/pom.xml
new file mode 100644
index 0000000..4e98e33
--- /dev/null
+++ b/runtime/spring-boot-example/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<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.6-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>camel-k-runtime-spring-boot-example</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-runtime-spring-boot</artifactId>
+            <version>${project.version}</version>
+           <exclusions>
+                <exclusion>
+                    <groupId>org.apache.camel</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.camel.k</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-dns-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.sun.xml.bind</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.camel</groupId>
+                    <artifactId>camel-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.camel</groupId>
+                    <artifactId>camel-core-starter</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.camel</groupId>
+                    <artifactId>camel-spring-boot-starter</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.camel.k</groupId>
+                <artifactId>camel-k-runtime-dependency-lister</artifactId>
+                <version>${project.version}</version>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/runtime/spring-boot/.gitignore b/runtime/spring-boot/.gitignore
new file mode 100644
index 0000000..ed92983
--- /dev/null
+++ b/runtime/spring-boot/.gitignore
@@ -0,0 +1,10 @@
+target
+
+*.iml
+
+.idea
+.project
+.metadata
+.settings
+.factorypath
+.classpath
diff --git a/runtime/spring-boot/pom.xml b/runtime/spring-boot/pom.xml
new file mode 100644
index 0000000..1e822ff
--- /dev/null
+++ b/runtime/spring-boot/pom.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<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.6-SNAPSHOT</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>camel-k-runtime-spring-boot</artifactId>
+
+    <properties>
+        <jar_file>${project.build.directory}/${project.build.finalName}.jar</jar_file>
+    </properties>
+
+    <dependencies>
+
+        <!-- ****************************** -->
+        <!--                                -->
+        <!-- RUNTIME                        -->
+        <!--                                -->
+        <!-- ****************************** -->
+
+        <dependency>
+            <groupId>org.apache.camel.k</groupId>
+            <artifactId>camel-k-runtime-jvm</artifactId>
+            <version>${project.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-core</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.logging.log4j</groupId>
+                    <artifactId>log4j-slf4j-impl</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-spring-boot-starter</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-log4j2</artifactId>
+            <version>${spring-boot.version}</version>
+        </dependency>
+
+        <!-- ****************************** -->
+        <!--                                -->
+        <!-- TESTS                          -->
+        <!--                                -->
+        <!-- ****************************** -->
+
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit-jupiter.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>${junit-jupiter.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <version>${assertj.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <version>${spring-boot.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <mainClass>org.apache.camel.k.spring.boot.Application</mainClass>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/runtime/spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java b/runtime/spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java
new file mode 100644
index 0000000..9ff694c
--- /dev/null
+++ b/runtime/spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java
@@ -0,0 +1,170 @@
+/**
+ * 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.spring.boot;
+
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.k.jvm.Constants;
+import org.apache.camel.k.jvm.RoutesLoader;
+import org.apache.camel.k.jvm.RoutesLoaders;
+import org.apache.camel.k.jvm.RuntimeRegistry;
+import org.apache.camel.k.jvm.RuntimeSupport;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+
+@SpringBootApplication
+public class Application {
+    private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
+
+    public static void main(String[] args) {
+        SpringApplication.run(Application.class, args);
+    }
+
+    // *****************************
+    //
+    // Beans
+    //
+    // *****************************
+
+    @Bean
+    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
+        // load properties using default behaviour
+        final Properties properties = RuntimeSupport.loadProperties();
+
+        // set spring boot specific properties
+        properties.put("camel.springboot.main-run-controller", "true");
+        properties.put("camel.springboot.name", "camel-1");
+        properties.put("camel.springboot.streamCachingEnabled", "true");
+        properties.put("camel.springboot.xml-routes", "false");
+        properties.put("camel.springboot.xml-rests", "false");
+        properties.put("camel.springboot.jmx-enabled", "false");
+
+        // set loaded properties as default properties
+        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
+        configurer.setProperties(properties);
+
+        return configurer;
+    }
+
+    @Bean
+    public RouteBuilder routes(ConfigurableApplicationContext applicationContext) throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                final CamelContext context = getContext();
+                final RuntimeRegistry registry = new RuntimeApplicationContextRegistry(applicationContext, context.getRegistry());
+                final String routes = System.getenv(Constants.ENV_CAMEL_K_ROUTES);
+
+                if (ObjectHelper.isEmpty(routes)) {
+                    throw new IllegalStateException("No valid routes found in " + Constants.ENV_CAMEL_K_ROUTES + " environment variable");
+                }
+
+                for (String route: routes.split(",")) {
+                    // determine location and language
+                    final String location = StringUtils.substringBefore(route, "?");
+                    final String query = StringUtils.substringAfter(route, "?");
+                    final String language = (String) URISupport.parseQuery(query).get("language");
+
+                    // load routes
+                    final RoutesLoader loader = RoutesLoaders.loaderFor(location, language);
+                    final RouteBuilder builder = loader.load(registry, location);
+
+                    if (builder == null) {
+                        throw new IllegalStateException("Unable to load route from: " + route);
+                    }
+
+                    LOGGER.info("Routes: {}", route);
+
+                    context.addRoutes(builder);
+                }
+            }
+        };
+    }
+
+    // *****************************
+    //
+    // Registry
+    //
+    // *****************************
+
+    private static class RuntimeApplicationContextRegistry implements RuntimeRegistry {
+        private final ConfigurableApplicationContext applicationContext;
+        private final Registry registry;
+
+        public RuntimeApplicationContextRegistry(ConfigurableApplicationContext applicationContext, Registry registry) {
+            this.applicationContext = applicationContext;
+            this.registry = registry;
+        }
+
+        @Override
+        public Object lookupByName(String name) {
+            return registry.lookupByName(name);
+        }
+
+        @Override
+        public <T> T lookupByNameAndType(String name, Class<T> type) {
+            return registry.lookupByNameAndType(name, type);
+        }
+
+        @Override
+        public <T> Map<String, T> findByTypeWithName(Class<T> type) {
+            return registry.findByTypeWithName(type);
+        }
+
+        @Override
+        public <T> Set<T> findByType(Class<T> type) {
+            return registry.findByType(type);
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
+        public Object lookup(String name) {
+            return registry.lookup(name);
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
+        public <T> T lookup(String name, Class<T> type) {
+            return registry.lookup(name, type);
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
+        public <T> Map<String, T> lookupByType(Class<T> type) {
+            return registry.lookupByType(type);
+        }
+
+        @Override
+        public void bind(String name, Object bean) {
+            applicationContext.getBeanFactory().registerSingleton(name, bean);
+        }
+    }
+
+}
diff --git a/test/build_manager_integration_test.go b/test/build_manager_integration_test.go
index 09bc0e7..1e8a10c 100644
--- a/test/build_manager_integration_test.go
+++ b/test/build_manager_integration_test.go
@@ -107,5 +107,4 @@ func TestBuildManagerFailedBuild(t *testing.T) {
 
 	assert.Equal(t, builder.StatusError, result.Status)
 	assert.NotEqual(t, builder.StatusCompleted, result.Status)
-	assert.Empty(t, result.Image)
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services