You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2018/12/20 11:30:37 UTC

[camel-k] 01/03: add resources to an integration #241

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

commit cc40921ab71f3283aed2fbbc22c0bed0451972c5
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Mon Dec 17 18:37:22 2018 +0100

    add resources to an integration #241
---
 examples/resources-data.txt                      |   1 +
 examples/resources-route.groovy                  |  11 +++
 pkg/apis/camel/v1alpha1/types.go                 |  26 ++++--
 pkg/apis/camel/v1alpha1/types_support.go         |  21 +++++
 pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go |  39 +++++++++
 pkg/builder/builder.go                           |  11 ++-
 pkg/builder/builder_steps.go                     |  20 ++---
 pkg/builder/builder_types.go                     |   3 +-
 pkg/builder/springboot/initializer.go            |  16 +++-
 pkg/client/cmd/run.go                            |  73 ++++++++++------
 pkg/metadata/metadata_dependencies_test.go       |  78 ++++++++++-------
 pkg/metadata/metadata_http_test.go               | 101 +++++++++++++++--------
 pkg/metadata/metadata_languages_test.go          |   8 +-
 pkg/metadata/metadata_uri_test.go                |  57 ++++++++-----
 pkg/stub/action/integration/build_image.go       |   6 ++
 pkg/stub/action/integration/deploy.go            |  13 ++-
 pkg/trait/catalog.go                             |  12 ++-
 pkg/trait/classpath.go                           |  83 +++++++++++++++++++
 pkg/trait/deployment.go                          |  94 +++++++++++++++------
 pkg/trait/knative_test.go                        |  10 ++-
 pkg/trait/springboot.go                          |  44 +++++-----
 pkg/trait/trait_test.go                          |   6 +-
 pkg/util/digest/digest.go                        |   8 ++
 pkg/util/envvar/envvar.go                        |  11 +++
 24 files changed, 549 insertions(+), 203 deletions(-)

diff --git a/examples/resources-data.txt b/examples/resources-data.txt
new file mode 100644
index 0000000..dff7947
--- /dev/null
+++ b/examples/resources-data.txt
@@ -0,0 +1 @@
+the file body
\ No newline at end of file
diff --git a/examples/resources-route.groovy b/examples/resources-route.groovy
new file mode 100644
index 0000000..e87c930
--- /dev/null
+++ b/examples/resources-route.groovy
@@ -0,0 +1,11 @@
+//
+// To run this integrations use:
+//
+//     kamel run --resource examples/resources-data.txt examples/resources-route.groovy
+//
+
+from('timer:resources')
+    .routeId('resources')
+    .setBody()
+        .simple("resource:classpath:resources-data.txt")
+    .log('file content is: ${body}')
diff --git a/pkg/apis/camel/v1alpha1/types.go b/pkg/apis/camel/v1alpha1/types.go
index 8965778..0627da1 100644
--- a/pkg/apis/camel/v1alpha1/types.go
+++ b/pkg/apis/camel/v1alpha1/types.go
@@ -55,6 +55,7 @@ type Integration struct {
 type IntegrationSpec struct {
 	Replicas      *int32                          `json:"replicas,omitempty"`
 	Sources       []SourceSpec                    `json:"sources,omitempty"`
+	Resources     []ResourceSpec                  `json:"resources,omitempty"`
 	Context       string                          `json:"context,omitempty"`
 	Dependencies  []string                        `json:"dependencies,omitempty"`
 	Profile       TraitProfile                    `json:"profile,omitempty"`
@@ -65,7 +66,7 @@ type IntegrationSpec struct {
 
 // AddSource --
 func (is *IntegrationSpec) AddSource(name string, content string, language Language) {
-	is.Sources = append(is.Sources, SourceSpec{Name: name, Content: content, Language: language})
+	is.Sources = append(is.Sources, NewSourceSpec(name, content, language))
 }
 
 // AddSources --
@@ -73,6 +74,11 @@ func (is *IntegrationSpec) AddSources(sources ...SourceSpec) {
 	is.Sources = append(is.Sources, sources...)
 }
 
+// AddResources --
+func (is *IntegrationSpec) AddResources(resources ...ResourceSpec) {
+	is.Resources = append(is.Resources, resources...)
+}
+
 // AddConfiguration --
 func (is *IntegrationSpec) AddConfiguration(confType string, confValue string) {
 	is.Configuration = append(is.Configuration, ConfigurationSpec{
@@ -93,12 +99,22 @@ func (is *IntegrationSpec) AddDependency(dependency string) {
 	}
 }
 
+// DataSpec --
+type DataSpec struct {
+	Name        string `json:"name,omitempty"`
+	Content     string `json:"content,omitempty"`
+	Compression bool   `json:"compression,omitempty"`
+}
+
+// ResourceSpec --
+type ResourceSpec struct {
+	DataSpec
+}
+
 // SourceSpec --
 type SourceSpec struct {
-	Name        string   `json:"name,omitempty"`
-	Content     string   `json:"content,omitempty"`
-	Language    Language `json:"language,omitempty"`
-	Compression bool     `json:"compression,omitempty"`
+	DataSpec
+	Language Language `json:"language,omitempty"`
 }
 
 // Language --
diff --git a/pkg/apis/camel/v1alpha1/types_support.go b/pkg/apis/camel/v1alpha1/types_support.go
index 1a9a9c4..21df605 100644
--- a/pkg/apis/camel/v1alpha1/types_support.go
+++ b/pkg/apis/camel/v1alpha1/types_support.go
@@ -40,6 +40,27 @@ func (spec ConfigurationSpec) String() string {
 //
 // **********************************
 
+// NewSourceSpec --
+func NewSourceSpec(name string, content string, language Language) SourceSpec {
+	return SourceSpec{
+		DataSpec: DataSpec{
+			Name:    name,
+			Content: content,
+		},
+		Language: language,
+	}
+}
+
+// NewResourceSpec --
+func NewResourceSpec(name string, content string, destination string) ResourceSpec {
+	return ResourceSpec{
+		DataSpec: DataSpec{
+			Name:    name,
+			Content: content,
+		},
+	}
+}
+
 // NewIntegrationPlatformList --
 func NewIntegrationPlatformList() IntegrationPlatformList {
 	return IntegrationPlatformList{
diff --git a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
index fd37d68..c61ab94 100644
--- a/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/camel/v1alpha1/zz_generated.deepcopy.go
@@ -58,6 +58,22 @@ func (in *ConfigurationSpec) DeepCopy() *ConfigurationSpec {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *DataSpec) DeepCopyInto(out *DataSpec) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataSpec.
+func (in *DataSpec) DeepCopy() *DataSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(DataSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Integration) DeepCopyInto(out *Integration) {
 	*out = *in
 	out.TypeMeta = in.TypeMeta
@@ -373,6 +389,11 @@ func (in *IntegrationSpec) DeepCopyInto(out *IntegrationSpec) {
 		*out = make([]SourceSpec, len(*in))
 		copy(*out, *in)
 	}
+	if in.Resources != nil {
+		in, out := &in.Resources, &out.Resources
+		*out = make([]ResourceSpec, len(*in))
+		copy(*out, *in)
+	}
 	if in.Dependencies != nil {
 		in, out := &in.Dependencies, &out.Dependencies
 		*out = make([]string, len(*in))
@@ -448,8 +469,26 @@ func (in *IntegrationTraitSpec) DeepCopy() *IntegrationTraitSpec {
 }
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ResourceSpec) DeepCopyInto(out *ResourceSpec) {
+	*out = *in
+	out.DataSpec = in.DataSpec
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceSpec.
+func (in *ResourceSpec) DeepCopy() *ResourceSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(ResourceSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *SourceSpec) DeepCopyInto(out *SourceSpec) {
 	*out = *in
+	out.DataSpec = in.DataSpec
 	return
 }
 
diff --git a/pkg/builder/builder.go b/pkg/builder/builder.go
index ffe9f92..58d814e 100644
--- a/pkg/builder/builder.go
+++ b/pkg/builder/builder.go
@@ -143,12 +143,11 @@ 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,
-		ComputeClasspath: true,
-		Image:            "fabric8/s2i-java:2.3", // TODO: externalize
+		C:         b.ctx,
+		Path:      builderPath,
+		Namespace: b.namespace,
+		Request:   request,
+		Image:     "fabric8/s2i-java:2.3", // TODO: externalize,
 	}
 
 	if request.Image != "" {
diff --git a/pkg/builder/builder_steps.go b/pkg/builder/builder_steps.go
index 91035e0..8799d58 100644
--- a/pkg/builder/builder_steps.go
+++ b/pkg/builder/builder_steps.go
@@ -166,7 +166,7 @@ func IncrementalPackager(ctx *Context) error {
 		return StandardPackager(ctx)
 	}
 
-	images, err := ListPublishedImages(ctx.Namespace)
+	images, err := ListPublishedImages(ctx)
 	if err != nil {
 		return err
 	}
@@ -228,27 +228,16 @@ func packager(ctx *Context, selector ArtifactsSelector) error {
 		}
 	}
 
-	if ctx.ComputeClasspath && len(ctx.Artifacts) > 0 {
-		cp := ""
-		for _, entry := range ctx.Artifacts {
-			cp += entry.Target + "\n"
-		}
-
-		if err := tarAppender.AddData([]byte(cp), "classpath"); err != nil {
-			return err
-		}
-	}
-
 	ctx.Archive = tarFileName
 
 	return nil
 }
 
 // ListPublishedImages --
-func ListPublishedImages(namespace string) ([]PublishedImage, error) {
+func ListPublishedImages(context *Context) ([]PublishedImage, error) {
 	list := v1alpha1.NewIntegrationContextList()
 
-	err := sdk.List(namespace, &list, sdk.WithListOptions(&metav1.ListOptions{}))
+	err := sdk.List(context.Namespace, &list, sdk.WithListOptions(&metav1.ListOptions{}))
 	if err != nil {
 		return nil, err
 	}
@@ -257,6 +246,9 @@ func ListPublishedImages(namespace string) ([]PublishedImage, error) {
 		if ctx.Status.Phase != v1alpha1.IntegrationContextPhaseReady || ctx.Labels == nil {
 			continue
 		}
+		if context.ContextFiler != nil && !context.ContextFiler(&ctx) {
+			continue
+		}
 		if ctxType, present := ctx.Labels["camel.apache.org/context.type"]; !present || ctxType != v1alpha1.IntegrationContextTypePlatform {
 			continue
 		}
diff --git a/pkg/builder/builder_types.go b/pkg/builder/builder_types.go
index 54f9774..89e2e86 100644
--- a/pkg/builder/builder_types.go
+++ b/pkg/builder/builder_types.go
@@ -147,8 +147,7 @@ type Context struct {
 	Artifacts         []v1alpha1.Artifact
 	SelectedArtifacts []v1alpha1.Artifact
 	Archive           string
-	ComputeClasspath  bool
-	MainClass         string
+	ContextFiler      func(integrationContext *v1alpha1.IntegrationContext) bool
 }
 
 // HasRequiredImage --
diff --git a/pkg/builder/springboot/initializer.go b/pkg/builder/springboot/initializer.go
index 2b8b5af..1add06e 100644
--- a/pkg/builder/springboot/initializer.go
+++ b/pkg/builder/springboot/initializer.go
@@ -18,14 +18,24 @@ limitations under the License.
 package springboot
 
 import (
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/builder"
 )
 
 // Initialize --
 func Initialize(ctx *builder.Context) error {
-	// no need to compute classpath as we do use spring boot own
-	// loader: PropertiesLauncher
-	ctx.ComputeClasspath = false
+	// do not take into account any image that does not have spring-boot
+	// as required dependency to avoid picking up a base image with wrong
+	// classpath or layout
+	ctx.ContextFiler = func(context *v1alpha1.IntegrationContext) bool {
+		for _, i := range context.Spec.Dependencies {
+			if i == "runtime:spring" {
+				return true
+			}
+		}
+
+		return false
+	}
 
 	return nil
 }
diff --git a/pkg/client/cmd/run.go b/pkg/client/cmd/run.go
index de784b9..376b91a 100644
--- a/pkg/client/cmd/run.go
+++ b/pkg/client/cmd/run.go
@@ -85,6 +85,7 @@ func newCmdRun(rootCmdOptions *RootCmdOptions) *cobra.Command {
 		"E.g. \"--logging-level org.apache.camel=DEBUG\"")
 	cmd.Flags().StringVarP(&options.OutputFormat, "output", "o", "", "Output format. One of: json|yaml")
 	cmd.Flags().BoolVar(&options.Compression, "compression", false, "Enable store source as a compressed binary blob")
+	cmd.Flags().StringSliceVar(&options.Resources, "resource", nil, "Add a resource")
 
 	// completion support
 	configureKnownCompletions(&cmd)
@@ -104,6 +105,7 @@ type runCmdOptions struct {
 	IntegrationName    string
 	Profile            string
 	OutputFormat       string
+	Resources          []string
 	Dependencies       []string
 	Properties         []string
 	ConfigMaps         []string
@@ -247,8 +249,8 @@ func (o *runCmdOptions) syncIntegration(sources []string) error {
 	return nil
 }
 
-func (o *runCmdOptions) createIntegration(args []string) (*v1alpha1.Integration, error) {
-	return o.updateIntegrationCode(args)
+func (o *runCmdOptions) createIntegration(sources []string) (*v1alpha1.Integration, error) {
+	return o.updateIntegrationCode(sources)
 }
 
 func (o *runCmdOptions) updateIntegrationCode(sources []string) (*v1alpha1.Integration, error) {
@@ -285,25 +287,32 @@ func (o *runCmdOptions) updateIntegrationCode(sources []string) (*v1alpha1.Integ
 	}
 
 	for _, source := range sources {
-		code, err := o.loadCode(source)
+		data, err := o.loadData(source, o.Compression)
 		if err != nil {
 			return nil, err
 		}
 
-		if o.Compression {
-			var b bytes.Buffer
-
-			if err := gzip.Compress(&b, []byte(code)); err != nil {
-				return nil, err
-			}
+		integration.Spec.AddSources(v1alpha1.SourceSpec{
+			DataSpec: v1alpha1.DataSpec{
+				Name:        path.Base(source),
+				Content:     data,
+				Compression: o.Compression,
+			},
+		})
+	}
 
-			code = base64.StdEncoding.EncodeToString(b.Bytes())
+	for _, resource := range o.Resources {
+		data, err := o.loadData(resource, o.Compression)
+		if err != nil {
+			return nil, err
 		}
 
-		integration.Spec.AddSources(v1alpha1.SourceSpec{
-			Name:        path.Base(source),
-			Content:     code,
-			Compression: o.Compression,
+		integration.Spec.AddResources(v1alpha1.ResourceSpec{
+			DataSpec: v1alpha1.DataSpec{
+				Name:        path.Base(resource),
+				Content:     data,
+				Compression: o.Compression,
+			},
 		})
 	}
 
@@ -381,23 +390,39 @@ func (o *runCmdOptions) updateIntegrationCode(sources []string) (*v1alpha1.Integ
 	return &integration, nil
 }
 
-func (*runCmdOptions) loadCode(fileName string) (string, error) {
+func (*runCmdOptions) loadData(fileName string, compress bool) (string, error) {
+	var content []byte
+	var err error
+
 	if !strings.HasPrefix(fileName, "http://") && !strings.HasPrefix(fileName, "https://") {
-		content, err := ioutil.ReadFile(fileName)
+		content, err = ioutil.ReadFile(fileName)
+		if err != nil {
+			return "", err
+		}
+	} else {
+		resp, err := http.Get(fileName)
+		if err != nil {
+			return "", err
+		}
+		defer resp.Body.Close()
+
+		content, err = ioutil.ReadAll(resp.Body)
 		if err != nil {
 			return "", err
 		}
-		return string(content), nil
 	}
 
-	resp, err := http.Get(fileName)
-	if err != nil {
-		return "", err
+	if compress {
+		var b bytes.Buffer
+
+		if err := gzip.Compress(&b, content); err != nil {
+			return "", err
+		}
+
+		return base64.StdEncoding.EncodeToString(b.Bytes()), nil
 	}
-	defer resp.Body.Close()
-	bodyBytes, err := ioutil.ReadAll(resp.Body)
-	bodyString := string(bodyBytes)
-	return bodyString, err
+
+	return string(content), nil
 }
 
 func (*runCmdOptions) configureTrait(integration *v1alpha1.Integration, config string) error {
diff --git a/pkg/metadata/metadata_dependencies_test.go b/pkg/metadata/metadata_dependencies_test.go
index 8eea487..9f952f5 100644
--- a/pkg/metadata/metadata_dependencies_test.go
+++ b/pkg/metadata/metadata_dependencies_test.go
@@ -26,14 +26,17 @@ import (
 
 func TestDependenciesJavaSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
+			    from("telegram:bots/cippa").to("log:stash");
+			    from("timer:tick").to("amqp:queue");
+			    from("ine:xistent").to("amqp:queue");
+			`,
+		},
 		Language: v1alpha1.LanguageJavaSource,
-		Content: `
-			from("telegram:bots/cippa").to("log:stash");
-			from("timer:tick").to("amqp:queue");
-			from("ine:xistent").to("amqp:queue");
-		`,
 	}
+
 	meta := Extract(code)
 	// assert all dependencies are found and sorted (removing duplicates)
 	assert.Equal(t, []string{"camel:amqp", "camel:core", "camel:telegram"}, meta.Dependencies)
@@ -41,28 +44,33 @@ func TestDependenciesJavaSource(t *testing.T) {
 
 func TestDependenciesJavaClass(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.class",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.class",
+			Content: `
+			    from("telegram:bots/cippa").to("log:stash");
+			    from("timer:tick").to("amqp:queue");
+			    from("ine:xistent").to("amqp:queue");
+		    `,
+		},
 		Language: v1alpha1.LanguageJavaClass,
-		Content: `
-			from("telegram:bots/cippa").to("log:stash");
-			from("timer:tick").to("amqp:queue");
-			from("ine:xistent").to("amqp:queue");
-		`,
 	}
+
 	meta := Extract(code)
 	assert.Empty(t, meta.Dependencies)
 }
 
 func TestDependenciesJavaScript(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "source.js",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "source.js",
+			Content: `
+			    from('telegram:bots/cippa').to("log:stash");
+			    from('timer:tick').to("amqp:queue");
+			    from("ine:xistent").to("amqp:queue");
+			    '"'
+		    `,
+		},
 		Language: v1alpha1.LanguageJavaScript,
-		Content: `
-			from('telegram:bots/cippa').to("log:stash");
-			from('timer:tick').to("amqp:queue");
-			from("ine:xistent").to("amqp:queue");
-			'"'
-		`,
 	}
 	meta := Extract(code)
 	// assert all dependencies are found and sorted (removing duplicates)
@@ -71,15 +79,18 @@ func TestDependenciesJavaScript(t *testing.T) {
 
 func TestDependenciesGroovy(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "source.groovy",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "source.groovy",
+			Content: `
+			    from('telegram:bots/cippa').to("log:stash");
+			    from('timer:tick').to("amqp:queue");
+			    from("ine:xistent").to("amqp:queue");
+			    '"'
+		    `,
+		},
 		Language: v1alpha1.LanguageGroovy,
-		Content: `
-			from('telegram:bots/cippa').to("log:stash");
-			from('timer:tick').to("amqp:queue");
-			from("ine:xistent").to("amqp:queue");
-			'"'
-		`,
 	}
+
 	meta := Extract(code)
 	// assert all dependencies are found and sorted (removing duplicates)
 	assert.Equal(t, []string{"camel:amqp", "camel:core", "camel:telegram"}, meta.Dependencies)
@@ -87,14 +98,17 @@ func TestDependenciesGroovy(t *testing.T) {
 
 func TestDependencies(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
+			    from("http4:test").to("log:end");
+			    from("https4:test").to("log:end");
+			    from("twitter-timeline:test").to("mock:end");
+		    `,
+		},
 		Language: v1alpha1.LanguageJavaSource,
-		Content: `
-			from("http4:test").to("log:end");
-			from("https4:test").to("log:end");
-			from("twitter-timeline:test").to("mock:end");
-		`,
 	}
+
 	meta := Extract(code)
 	// assert all dependencies are found and sorted (removing duplicates)
 	assert.Equal(t, []string{"camel:core", "camel:http4", "camel:twitter"}, meta.Dependencies)
diff --git a/pkg/metadata/metadata_http_test.go b/pkg/metadata/metadata_http_test.go
index 59cc1cd..adc5cdd 100644
--- a/pkg/metadata/metadata_http_test.go
+++ b/pkg/metadata/metadata_http_test.go
@@ -26,13 +26,15 @@ import (
 
 func TestHttpJavaSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
 			from("telegram:bots/cippa").to("log:stash");
 			from("undertow:uri").to("log:stash");
 			from("ine:xistent").to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -41,13 +43,16 @@ func TestHttpJavaSource(t *testing.T) {
 
 func TestHttpOnlyJavaSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
 			from("direct:bots/cippa").to("log:stash");
 			from("undertow:uri").to("log:stash");
 			from("seda:path").to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -56,12 +61,14 @@ func TestHttpOnlyJavaSource(t *testing.T) {
 
 func TestHttpOnlyJavaSourceRest(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
 			from("direct:bots/cippa").to("log:stash");
 			rest().get("").to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -70,12 +77,14 @@ func TestHttpOnlyJavaSourceRest(t *testing.T) {
 
 func TestHttpOnlyJavaSourceRest2(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+			Content: `
 			from("vm:bots/cippa").to("log:stash");
 			rest( ).get("").to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -84,13 +93,15 @@ func TestHttpOnlyJavaSourceRest2(t *testing.T) {
 
 func TestNoHttpGroovySource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.groovy",
-		Language: v1alpha1.LanguageGroovy,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.groovy",
+			Content: `
 			from('direct:bots/cippa').to("log:stash");
 			from('teelgram:uri').to("log:stash");
 			from('seda:path').to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageGroovy,
 	}
 	meta := Extract(code)
 	assert.False(t, meta.RequiresHTTPService)
@@ -99,13 +110,15 @@ func TestNoHttpGroovySource(t *testing.T) {
 
 func TestHttpOnlyGroovySource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.groovy",
-		Language: v1alpha1.LanguageGroovy,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.groovy",
+			Content: `
 			from('direct:bots/cippa').to("log:stash");
 			from('undertow:uri').to("log:stash");
 			from('seda:path').to("log:stash");
 		`,
+		},
+		Language: v1alpha1.LanguageGroovy,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -114,13 +127,15 @@ func TestHttpOnlyGroovySource(t *testing.T) {
 
 func TestHttpXMLSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "routes.xml",
-		Language: v1alpha1.LanguageXML,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "routes.xml",
+			Content: `
 			<from uri="telegram:ciao" />
 			<rest path="/">
 			</rest>
 		`,
+		},
+		Language: v1alpha1.LanguageXML,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -129,13 +144,16 @@ func TestHttpXMLSource(t *testing.T) {
 
 func TestHttpOnlyXMLSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "routes.xml",
-		Language: v1alpha1.LanguageXML,
-		Content: `
+
+		DataSpec: v1alpha1.DataSpec{
+			Name: "routes.xml",
+			Content: `
 			<from uri="direct:ciao" />
 			<rest path="/">
 			</rest>
 		`,
+		},
+		Language: v1alpha1.LanguageXML,
 	}
 	meta := Extract(code)
 	assert.True(t, meta.RequiresHTTPService)
@@ -145,20 +163,25 @@ func TestHttpOnlyXMLSource(t *testing.T) {
 func TestMultilangHTTPOnlySource(t *testing.T) {
 	codes := []v1alpha1.SourceSpec{
 		{
-			Name:     "routes.xml",
-			Language: v1alpha1.LanguageXML,
-			Content: `
+			DataSpec: v1alpha1.DataSpec{
+				Name: "routes.xml",
+				Content: `
 				<from uri="direct:ciao" />
 				<rest path="/">
 				</rest>
 			`,
+			},
+			Language: v1alpha1.LanguageXML,
 		},
 		{
-			Name:     "routes2.groovy",
-			Language: v1alpha1.LanguageGroovy,
-			Content: `
+
+			DataSpec: v1alpha1.DataSpec{
+				Name: "routes2.groovy",
+				Content: `
 				from('seda:in').to('seda:out')
 			`,
+			},
+			Language: v1alpha1.LanguageGroovy,
 		},
 	}
 	meta := ExtractAll(codes)
@@ -169,21 +192,27 @@ func TestMultilangHTTPOnlySource(t *testing.T) {
 func TestMultilangHTTPSource(t *testing.T) {
 	codes := []v1alpha1.SourceSpec{
 		{
-			Name:     "routes.xml",
-			Language: v1alpha1.LanguageXML,
-			Content: `
+
+			DataSpec: v1alpha1.DataSpec{
+				Name: "routes.xml",
+				Content: `
 				<from uri="direct:ciao" />
 				<rest path="/">
 				</rest>
 			`,
+			},
+			Language: v1alpha1.LanguageXML,
 		},
 		{
-			Name:     "routes2.groovy",
-			Language: v1alpha1.LanguageGroovy,
-			Content: `
+
+			DataSpec: v1alpha1.DataSpec{
+				Name: "routes2.groovy",
+				Content: `
 				from('seda:in').to('seda:out')
 				from('timer:tick').to('log:info')
 			`,
+			},
+			Language: v1alpha1.LanguageGroovy,
 		},
 	}
 	meta := ExtractAll(codes)
diff --git a/pkg/metadata/metadata_languages_test.go b/pkg/metadata/metadata_languages_test.go
index 5382d38..8a11a56 100644
--- a/pkg/metadata/metadata_languages_test.go
+++ b/pkg/metadata/metadata_languages_test.go
@@ -26,7 +26,9 @@ import (
 
 func TestLanguageJavaSource(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name: "Request.java",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+		},
 	}
 	meta := Extract(code)
 	assert.Equal(t, v1alpha1.LanguageJavaSource, meta.Language)
@@ -34,7 +36,9 @@ func TestLanguageJavaSource(t *testing.T) {
 
 func TestLanguageAlreadySet(t *testing.T) {
 	code := v1alpha1.SourceSpec{
-		Name:     "Request.java",
+		DataSpec: v1alpha1.DataSpec{
+			Name: "Request.java",
+		},
 		Language: v1alpha1.LanguageJavaScript,
 	}
 	meta := Extract(code)
diff --git a/pkg/metadata/metadata_uri_test.go b/pkg/metadata/metadata_uri_test.go
index 1214ef6..9dc26d4 100644
--- a/pkg/metadata/metadata_uri_test.go
+++ b/pkg/metadata/metadata_uri_test.go
@@ -26,9 +26,9 @@ import (
 
 func TestJava1(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			import org.apache.camel.builder.RouteBuilder;
 
 			public class Sample extends RouteBuilder {
@@ -40,6 +40,8 @@ func TestJava1(t *testing.T) {
   				}
 			}
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 
 	metadata := Extract(source)
@@ -51,9 +53,9 @@ func TestJava1(t *testing.T) {
 
 func TestJava2(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageJavaSource,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			import org.apache.camel.builder.RouteBuilder;
 
 			public class Sample extends RouteBuilder {
@@ -71,6 +73,8 @@ func TestJava2(t *testing.T) {
   				}
 			}
 		`,
+		},
+		Language: v1alpha1.LanguageJavaSource,
 	}
 
 	metadata := Extract(source)
@@ -84,9 +88,9 @@ func TestJava2(t *testing.T) {
 
 func TestGroovy1(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageGroovy,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			
 		  	from( "timer:tick")
 		    	.setBody().constant("aa")
@@ -97,6 +101,8 @@ func TestGroovy1(t *testing.T) {
 		    	.setBody().constant("aa")
 				.to('uri:3')
 		`,
+		},
+		Language: v1alpha1.LanguageGroovy,
 	}
 
 	metadata := Extract(source)
@@ -111,10 +117,9 @@ func TestGroovy1(t *testing.T) {
 
 func TestGroovy2(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageGroovy,
-		Content: `
-			
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `			
 			rest().get("/")
 				.to   ('log:info?skipBodyLineSeparator=false').to( 'http://url' )
 						.toD('dyn:1')
@@ -122,6 +127,8 @@ func TestGroovy2(t *testing.T) {
 						.toD( "dyn:2")
 						.toF( "f:%s", "2")
 		`,
+		},
+		Language: v1alpha1.LanguageGroovy,
 	}
 
 	metadata := Extract(source)
@@ -136,9 +143,9 @@ func TestGroovy2(t *testing.T) {
 
 func TestXml1(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageXML,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			<routes>
 			    <route id="hello">
         			<from uri="timer:hello?period=3s"/>
@@ -151,6 +158,8 @@ func TestXml1(t *testing.T) {
     			</route>
 			</routes>
 		`,
+		},
+		Language: v1alpha1.LanguageXML,
 	}
 
 	metadata := Extract(source)
@@ -164,9 +173,9 @@ func TestXml1(t *testing.T) {
 
 func TestKotlin1(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageKotlin,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			
 		  	from( "timer:tick")
 		    	.setBody().constant("aa")
@@ -179,6 +188,8 @@ func TestKotlin1(t *testing.T) {
 				.toD("uri:4")
 				.toF("uri:%s", 5)
 		`,
+		},
+		Language: v1alpha1.LanguageKotlin,
 	}
 
 	metadata := Extract(source)
@@ -195,15 +206,17 @@ func TestKotlin1(t *testing.T) {
 
 func TestJavascript1(t *testing.T) {
 	source := v1alpha1.SourceSpec{
-		Name:     "test",
-		Language: v1alpha1.LanguageJavaScript,
-		Content: `
+		DataSpec: v1alpha1.DataSpec{
+			Name: "test",
+			Content: `
 			
 			rest().get("/")
 				.to   ('log:info?skipBodyLineSeparator=false').to( 'http://url' )
 				.toD("uri:2")
 				.toF("uri:%s", "3") 
 		`,
+		},
+		Language: v1alpha1.LanguageJavaScript,
 	}
 
 	metadata := Extract(source)
diff --git a/pkg/stub/action/integration/build_image.go b/pkg/stub/action/integration/build_image.go
index e23e6d0..44440cf 100644
--- a/pkg/stub/action/integration/build_image.go
+++ b/pkg/stub/action/integration/build_image.go
@@ -101,6 +101,12 @@ func (action *buildImageAction) Handle(integration *v1alpha1.Integration) error
 			Target:  path.Join("sources", source.Name),
 		})
 	}
+	for _, resource := range integration.Spec.Resources {
+		r.Resources = append(r.Resources, builder.Resource{
+			Content: []byte(resource.Content),
+			Target:  path.Join("resources", resource.Name),
+		})
+	}
 
 	res := b.Submit(r)
 
diff --git a/pkg/stub/action/integration/deploy.go b/pkg/stub/action/integration/deploy.go
index 1ad22ab..ad0d04a 100644
--- a/pkg/stub/action/integration/deploy.go
+++ b/pkg/stub/action/integration/deploy.go
@@ -22,6 +22,7 @@ import (
 	"github.com/apache/camel-k/pkg/trait"
 	"github.com/apache/camel-k/pkg/util/kubernetes"
 	"github.com/operator-framework/operator-sdk/pkg/sdk"
+	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
 
@@ -42,10 +43,18 @@ func (action *deployAction) CanHandle(integration *v1alpha1.Integration) bool {
 }
 
 func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
-	env, err := trait.Apply(integration, nil)
+	ctxName := integration.Spec.Context
+	ctx := v1alpha1.NewIntegrationContext(integration.Namespace, ctxName)
+
+	if err := sdk.Get(&ctx); err != nil {
+		return errors.Wrapf(err, "unable to find integration context %s, %s", ctxName, err)
+	}
+
+	env, err := trait.Apply(integration, &ctx)
 	if err != nil {
 		return err
 	}
+
 	// TODO we should look for objects that are no longer present in the collection and remove them
 	err = kubernetes.ReplaceResources(env.Resources.Items())
 	if err != nil {
@@ -53,8 +62,8 @@ func (action *deployAction) Handle(integration *v1alpha1.Integration) error {
 	}
 
 	target := integration.DeepCopy()
-	logrus.Info("Integration ", target.Name, " transitioning to state ", v1alpha1.IntegrationPhaseRunning)
 	target.Status.Phase = v1alpha1.IntegrationPhaseRunning
+	logrus.Info("Integration ", target.Name, " transitioning to state ", target.Status.Phase)
 
 	return sdk.Update(target)
 }
diff --git a/pkg/trait/catalog.go b/pkg/trait/catalog.go
index 0fd5668..0e5b925 100644
--- a/pkg/trait/catalog.go
+++ b/pkg/trait/catalog.go
@@ -41,6 +41,7 @@ type Catalog struct {
 	tSpringBoot   Trait
 	tIstio        Trait
 	tEnvironment  Trait
+	tClasspath    Trait
 }
 
 // NewCatalog creates a new trait Catalog
@@ -58,6 +59,7 @@ func NewCatalog() *Catalog {
 		tSpringBoot:   newSpringBootTrait(),
 		tIstio:        newIstioTrait(),
 		tEnvironment:  newEnvironmentTrait(),
+		tClasspath:    newClasspathTrait(),
 	}
 }
 
@@ -75,6 +77,7 @@ func (c *Catalog) allTraits() []Trait {
 		c.tSpringBoot,
 		c.tIstio,
 		c.tEnvironment,
+		c.tClasspath,
 	}
 }
 
@@ -85,8 +88,9 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait {
 			c.tDebug,
 			c.tDependencies,
 			c.tBuilder,
-			c.tSpringBoot,
 			c.tEnvironment,
+			c.tClasspath,
+			c.tSpringBoot,
 			c.tDeployment,
 			c.tService,
 			c.tRoute,
@@ -97,8 +101,9 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait {
 			c.tDebug,
 			c.tDependencies,
 			c.tBuilder,
-			c.tSpringBoot,
 			c.tEnvironment,
+			c.tClasspath,
+			c.tSpringBoot,
 			c.tDeployment,
 			c.tService,
 			c.tIngress,
@@ -109,8 +114,9 @@ func (c *Catalog) traitsFor(environment *Environment) []Trait {
 			c.tDebug,
 			c.tDependencies,
 			c.tBuilder,
-			c.tSpringBoot,
 			c.tEnvironment,
+			c.tClasspath,
+			c.tSpringBoot,
 			c.tKnative,
 			c.tDeployment,
 			c.tIstio,
diff --git a/pkg/trait/classpath.go b/pkg/trait/classpath.go
new file mode 100644
index 0000000..3936584
--- /dev/null
+++ b/pkg/trait/classpath.go
@@ -0,0 +1,83 @@
+/*
+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 (
+	"fmt"
+	"strings"
+
+	"github.com/operator-framework/operator-sdk/pkg/sdk"
+	"github.com/pkg/errors"
+
+	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
+	"github.com/apache/camel-k/pkg/util/envvar"
+)
+
+type classpathTrait struct {
+	BaseTrait `property:",squash"`
+}
+
+func newClasspathTrait() *classpathTrait {
+	return &classpathTrait{
+		BaseTrait: BaseTrait{
+			id: ID("classpath"),
+		},
+	}
+}
+
+func (t *classpathTrait) Configure(e *Environment) (bool, error) {
+	if t.Enabled != nil && !*t.Enabled {
+		return false, nil
+	}
+	if e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseDeploying) {
+		return true, nil
+	}
+
+	return false, nil
+}
+
+func (t *classpathTrait) Apply(e *Environment) error {
+	ctx := e.Context
+
+	if ctx == nil && e.Integration.Spec.Context != "" {
+		name := e.Integration.Spec.Context
+		c := v1alpha1.NewIntegrationContext(e.Integration.Namespace, name)
+
+		if err := sdk.Get(&c); err != nil {
+			return errors.Wrapf(err, "unable to find integration context %s, %s", name, err)
+		}
+
+		ctx = &c
+	}
+
+	if ctx == nil {
+		return fmt.Errorf("unable to find integration context %s", e.Integration.Spec.Context)
+	}
+
+	deps := make([]string, 0, 2+len(ctx.Status.Artifacts))
+	deps = append(deps, "/etc/camel/resources")
+	deps = append(deps, "./resources")
+
+	for _, artifact := range ctx.Status.Artifacts {
+		deps = append(deps, artifact.Target)
+	}
+
+	envvar.SetVal(&e.EnvVars, "JAVA_CLASSPATH", strings.Join(deps, ":"))
+
+	return nil
+}
diff --git a/pkg/trait/deployment.go b/pkg/trait/deployment.go
index 6b33485..ec53ac3 100644
--- a/pkg/trait/deployment.go
+++ b/pkg/trait/deployment.go
@@ -126,8 +126,9 @@ func (t *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object {
 
 	if !t.ContainerImage {
 
-		// do not create 'source' ConfigMap if a docker images for deployment
+		// do not create 'source' or 'resource' ConfigMap if a docker images for deployment
 		// is required
+
 		for i, s := range e.Integration.Spec.Sources {
 			cm := corev1.ConfigMap{
 				TypeMeta: metav1.TypeMeta{
@@ -147,7 +148,32 @@ func (t *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object {
 					},
 				},
 				Data: map[string]string{
-					"integration": s.Content,
+					"content": s.Content,
+				},
+			}
+
+			maps = append(maps, &cm)
+		}
+
+		for i, s := range e.Integration.Spec.Resources {
+			cm := corev1.ConfigMap{
+				TypeMeta: metav1.TypeMeta{
+					Kind:       "ConfigMap",
+					APIVersion: "v1",
+				},
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      fmt.Sprintf("%s-resource-%03d", e.Integration.Name, i),
+					Namespace: e.Integration.Namespace,
+					Labels: map[string]string{
+						"camel.apache.org/integration": e.Integration.Name,
+					},
+					Annotations: map[string]string{
+						"camel.apache.org/resource.name":        s.Name,
+						"camel.apache.org/resource.compression": strconv.FormatBool(s.Compression),
+					},
+				},
+				Data: map[string]string{
+					"content": s.Content,
 				},
 			}
 
@@ -167,8 +193,8 @@ func (t *deploymentTrait) getConfigMapsFor(e *Environment) []runtime.Object {
 func (t *deploymentTrait) getSources(e *Environment) []string {
 	sources := make([]string, 0, len(e.Integration.Spec.Sources))
 
-	for i, s := range e.Integration.Spec.Sources {
-		root := fmt.Sprintf("/etc/camel/integrations/%03d", i)
+	for _, s := range e.Integration.Spec.Sources {
+		root := "/etc/camel/sources"
 
 		if t.ContainerImage {
 
@@ -176,7 +202,8 @@ func (t *deploymentTrait) getSources(e *Environment) []string {
 			root = "/deployments/sources"
 		}
 
-		src := path.Join(root, s.Name)
+		srcName := strings.TrimPrefix(s.Name, "/")
+		src := path.Join(root, srcName)
 		src = "file:" + src
 
 		params := make([]string, 0)
@@ -272,7 +299,6 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 
 	vols := make([]corev1.Volume, 0)
 	mnts := make([]corev1.VolumeMount, 0)
-	cnt := 0
 
 	//
 	// Volumes :: Properties
@@ -311,26 +337,48 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 		// do not need to mount any 'source' ConfigMap to the pod
 
 		for i, s := range e.Integration.Spec.Sources {
+			cmName := fmt.Sprintf("%s-source-%03d", e.Integration.Name, i)
+			refName := fmt.Sprintf("integration-source-%03d", i)
+			resName := strings.TrimPrefix(s.Name, "/")
+
 			vols = append(vols, corev1.Volume{
-				Name: fmt.Sprintf("integration-source-%03d", i),
+				Name: refName,
 				VolumeSource: corev1.VolumeSource{
 					ConfigMap: &corev1.ConfigMapVolumeSource{
 						LocalObjectReference: corev1.LocalObjectReference{
-							Name: fmt.Sprintf("%s-source-%03d", e.Integration.Name, i),
+							Name: cmName,
 						},
-						Items: []corev1.KeyToPath{
-							{
-								Key:  "integration",
-								Path: strings.TrimPrefix(s.Name, "/"),
-							},
+					},
+				},
+			})
+
+			mnts = append(mnts, corev1.VolumeMount{
+				Name:      refName,
+				MountPath: path.Join("/etc/camel/sources", resName),
+				SubPath:   "content",
+			})
+		}
+
+		for i, r := range e.Integration.Spec.Resources {
+			cmName := fmt.Sprintf("%s-resource-%03d", e.Integration.Name, i)
+			refName := fmt.Sprintf("integration-resource-%03d", i)
+			resName := strings.TrimPrefix(r.Name, "/")
+
+			vols = append(vols, corev1.Volume{
+				Name: refName,
+				VolumeSource: corev1.VolumeSource{
+					ConfigMap: &corev1.ConfigMapVolumeSource{
+						LocalObjectReference: corev1.LocalObjectReference{
+							Name: cmName,
 						},
 					},
 				},
 			})
 
 			mnts = append(mnts, corev1.VolumeMount{
-				Name:      fmt.Sprintf("integration-source-%03d", i),
-				MountPath: fmt.Sprintf("/etc/camel/integrations/%03d", i),
+				Name:      refName,
+				MountPath: path.Join("/etc/camel/resources", resName),
+				SubPath:   "content",
 			})
 		}
 	}
@@ -340,10 +388,10 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 	//
 
 	VisitConfigurations("configmap", e.Context, e.Integration, func(cmName string) {
-		cnt++
+		refName := "integration-cm-" + strings.ToLower(cmName)
 
 		vols = append(vols, corev1.Volume{
-			Name: "integration-cm-" + cmName,
+			Name: refName,
 			VolumeSource: corev1.VolumeSource{
 				ConfigMap: &corev1.ConfigMapVolumeSource{
 					LocalObjectReference: corev1.LocalObjectReference{
@@ -354,8 +402,8 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 		})
 
 		mnts = append(mnts, corev1.VolumeMount{
-			Name:      "integration-cm-" + cmName,
-			MountPath: fmt.Sprintf("/etc/camel/conf.d/%03d_%s", cnt, cmName),
+			Name:      refName,
+			MountPath: path.Join("/etc/camel/conf.d", refName),
 		})
 	})
 
@@ -364,10 +412,10 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 	//
 
 	VisitConfigurations("secret", e.Context, e.Integration, func(secretName string) {
-		cnt++
+		refName := "integration-secret-" + strings.ToLower(secretName)
 
 		vols = append(vols, corev1.Volume{
-			Name: "integration-secret-" + secretName,
+			Name: refName,
 			VolumeSource: corev1.VolumeSource{
 				Secret: &corev1.SecretVolumeSource{
 					SecretName: secretName,
@@ -376,8 +424,8 @@ func (t *deploymentTrait) getDeploymentFor(e *Environment) *appsv1.Deployment {
 		})
 
 		mnts = append(mnts, corev1.VolumeMount{
-			Name:      "integration-secret-" + secretName,
-			MountPath: fmt.Sprintf("/etc/camel/conf.d/%03d_%s", cnt, secretName),
+			Name:      refName,
+			MountPath: path.Join("/etc/camel/conf.d", refName),
 		})
 	})
 
diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go
index 4fd5c40..2d50e48 100644
--- a/pkg/trait/knative_test.go
+++ b/pkg/trait/knative_test.go
@@ -50,10 +50,12 @@ func TestKnativeTraitWithCompressedSources(t *testing.T) {
 				Profile: v1alpha1.TraitProfileKnative,
 				Sources: []v1alpha1.SourceSpec{
 					{
-						Language:    v1alpha1.LanguageJavaScript,
-						Name:        "routes.js",
-						Content:     content,
-						Compression: true,
+						DataSpec: v1alpha1.DataSpec{
+							Name:        "routes.js",
+							Content:     content,
+							Compression: true,
+						},
+						Language: v1alpha1.LanguageJavaScript,
 					},
 				},
 			},
diff --git a/pkg/trait/springboot.go b/pkg/trait/springboot.go
index f8d87b9..fb280ba 100644
--- a/pkg/trait/springboot.go
+++ b/pkg/trait/springboot.go
@@ -23,9 +23,6 @@ import (
 
 	"github.com/apache/camel-k/pkg/util/envvar"
 
-	"github.com/operator-framework/operator-sdk/pkg/sdk"
-	"github.com/pkg/errors"
-
 	"github.com/apache/camel-k/pkg/apis/camel/v1alpha1"
 	"github.com/apache/camel-k/pkg/builder"
 	"github.com/apache/camel-k/pkg/builder/springboot"
@@ -52,7 +49,7 @@ func (t *springBootTrait) Configure(e *Environment) (bool, error) {
 	if e.IntegrationContextInPhase(v1alpha1.IntegrationContextPhaseBuilding) {
 		return true, nil
 	}
-	if e.IntegrationInPhase(v1alpha1.IntegrationPhaseDeploying) {
+	if e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseDeploying) {
 		return true, nil
 	}
 	if e.IntegrationInPhase("") {
@@ -68,46 +65,47 @@ func (t *springBootTrait) Apply(e *Environment) error {
 	// Integration
 	//
 
-	if e.Integration != nil && e.Integration.Status.Phase == "" {
+	if e.IntegrationInPhase("") {
 		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 {
+	if e.InPhase(v1alpha1.IntegrationContextPhaseReady, v1alpha1.IntegrationPhaseDeploying) {
+		// Remove classpath
+		envvar.Remove(&e.EnvVars, "JAVA_CLASSPATH")
+
 		// Override env vars
 		envvar.SetVal(&e.EnvVars, "JAVA_MAIN_CLASS", "org.springframework.boot.loader.PropertiesLauncher")
 		envvar.SetVal(&e.EnvVars, "LOADER_PATH", "/deployments/dependencies/")
 
-		if e.Integration.Spec.Context != "" {
-			name := e.Integration.Spec.Context
-			ctx := v1alpha1.NewIntegrationContext(e.Integration.Namespace, name)
+		deps := make([]string, 0, 2+len(e.Context.Status.Artifacts))
+		deps = append(deps, "/etc/camel/resources")
+		deps = append(deps, "./resources")
 
-			if err := sdk.Get(&ctx); err != nil {
-				return errors.Wrapf(err, "unable to find integration context %s, %s", ctx.Name, err)
+		for _, artifact := range e.Context.Status.Artifacts {
+			if strings.HasPrefix(artifact.ID, "org.apache.camel.k:camel-k-runtime-spring-boot:") {
+				// do not include runner jar
+				continue
 			}
-
-			deps := make([]string, 0, len(ctx.Status.Artifacts))
-			for _, artifact := range ctx.Status.Artifacts {
-				if strings.HasPrefix(artifact.ID, "org.apache.camel.k:camel-k-runtime-spring-boot:") {
-					// do not include runner jar
-					continue
-				}
-
-				deps = append(deps, artifact.Target)
+			if strings.HasPrefix(artifact.ID, "org.apache.logging.log4j:") {
+				// do not include logging, deps are embedded in runner jar
+				continue
 			}
 
-			envvar.SetVal(&e.EnvVars, "LOADER_HOME", "/deployments")
-			envvar.SetVal(&e.EnvVars, "LOADER_PATH", strings.Join(deps, ","))
+			deps = append(deps, artifact.Target)
 		}
+
+		envvar.SetVal(&e.EnvVars, "LOADER_HOME", "/deployments")
+		envvar.SetVal(&e.EnvVars, "LOADER_PATH", strings.Join(deps, ","))
 	}
 
 	//
 	// Integration Context
 	//
 
-	if e.Context != nil && e.Context.Status.Phase == v1alpha1.IntegrationContextPhaseBuilding {
+	if e.IntegrationContextInPhase(v1alpha1.IntegrationContextPhaseBuilding) {
 		// add custom initialization logic
 		e.Steps = append(e.Steps, builder.NewStep("initialize/spring-boot", builder.InitPhase, springboot.Initialize))
 		e.Steps = append(e.Steps, builder.NewStep("build/compute-boot-dependencies", builder.ProjectBuildPhase+1, springboot.ComputeDependencies))
diff --git a/pkg/trait/trait_test.go b/pkg/trait/trait_test.go
index 6c9b89a..e2490d2 100644
--- a/pkg/trait/trait_test.go
+++ b/pkg/trait/trait_test.go
@@ -176,9 +176,11 @@ func createTestEnv(cluster v1alpha1.IntegrationPlatformCluster, script string) *
 			Spec: v1alpha1.IntegrationSpec{
 				Sources: []v1alpha1.SourceSpec{
 					{
-						Name:     "file.groovy",
+						DataSpec: v1alpha1.DataSpec{
+							Name:    "file.groovy",
+							Content: script,
+						},
 						Language: v1alpha1.LanguageGroovy,
-						Content:  script,
 					},
 				},
 			},
diff --git a/pkg/util/digest/digest.go b/pkg/util/digest/digest.go
index 5803a97..67054fb 100644
--- a/pkg/util/digest/digest.go
+++ b/pkg/util/digest/digest.go
@@ -49,12 +49,20 @@ func ComputeForIntegration(integration *v1alpha1.Integration) (string, error) {
 		}
 	}
 
+	// Integration resources
+	for _, item := range integration.Spec.Resources {
+		if _, err := hash.Write([]byte(item.Content)); err != nil {
+			return "", err
+		}
+	}
+
 	// Integration dependencies
 	for _, item := range integration.Spec.Dependencies {
 		if _, err := hash.Write([]byte(item)); err != nil {
 			return "", err
 		}
 	}
+
 	// Integration configuration
 	for _, item := range integration.Spec.Configuration {
 		if _, err := hash.Write([]byte(item.String())); err != nil {
diff --git a/pkg/util/envvar/envvar.go b/pkg/util/envvar/envvar.go
index 68f6ba7..d8739cf 100644
--- a/pkg/util/envvar/envvar.go
+++ b/pkg/util/envvar/envvar.go
@@ -30,6 +30,17 @@ func Get(vars []v1.EnvVar, name string) *v1.EnvVar {
 	return nil
 }
 
+// Remove --
+func Remove(vars *[]v1.EnvVar, name string) {
+	v := *vars
+	for i := 0; i < len(v); i++ {
+		if v[i].Name == name {
+			*vars = append(v[:i], v[i+1:]...)
+			return
+		}
+	}
+}
+
 // SetVal --
 func SetVal(vars *[]v1.EnvVar, name string, value string) {
 	envVar := Get(*vars, name)