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:36 UTC

[camel-k] branch master updated (82c1f82 -> 7da47e3)

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

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


    from 82c1f82  catalog : include camel-k components #301
     new cc40921  add resources to an integration #241
     new 7de58ca  fix typo
     new 7da47e3  support resources as env var

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 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.go                               |  23 +++++
 pkg/trait/knative_test.go                          |  42 ++++++++-
 pkg/trait/springboot.go                            |  44 +++++----
 pkg/trait/trait_test.go                            |   6 +-
 pkg/util/digest/digest.go                          |   8 ++
 pkg/util/envvar/envvar.go                          |  11 +++
 runtime/camel-k-runtime-jvm/pom.xml                |  15 +++
 .../java/org/apache/camel/k/jvm/Application.java   |   7 ++
 .../org/apache/camel/k/jvm/RuntimeSupport.java     |  98 ++++++++++++++++++++
 .../java/org/apache/camel/k/jvm/RuntimeTest.java   |  24 +++++
 .../src/test/resources/my-resource.txt             |   1 +
 .../apache/camel/k/kotlin/KotlinRoutesLoader.kt    |   2 +-
 .../apache/camel/k/spring/boot/Application.java    |   9 ++
 32 files changed, 759 insertions(+), 204 deletions(-)
 create mode 100644 examples/resources-data.txt
 create mode 100644 examples/resources-route.groovy
 create mode 100644 pkg/trait/classpath.go
 create mode 100644 runtime/camel-k-runtime-jvm/src/test/resources/my-resource.txt


[camel-k] 03/03: support resources as env var

Posted by nf...@apache.org.
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 7da47e324e9126c61a1c2fc9c9bb40d7b95fd4e2
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Tue Dec 18 14:26:44 2018 +0100

    support resources as env var
---
 examples/resources-route.groovy                    |  2 +-
 pkg/trait/knative.go                               | 23 +++++
 pkg/trait/knative_test.go                          | 32 +++++++
 runtime/camel-k-runtime-jvm/pom.xml                | 15 ++++
 .../java/org/apache/camel/k/jvm/Application.java   |  7 ++
 .../org/apache/camel/k/jvm/RuntimeSupport.java     | 98 ++++++++++++++++++++++
 .../java/org/apache/camel/k/jvm/RuntimeTest.java   | 24 ++++++
 .../src/test/resources/my-resource.txt             |  1 +
 .../apache/camel/k/kotlin/KotlinRoutesLoader.kt    |  2 +-
 .../apache/camel/k/spring/boot/Application.java    |  9 ++
 10 files changed, 211 insertions(+), 2 deletions(-)

diff --git a/examples/resources-route.groovy b/examples/resources-route.groovy
index e87c930..4c7d61b 100644
--- a/examples/resources-route.groovy
+++ b/examples/resources-route.groovy
@@ -7,5 +7,5 @@
 from('timer:resources')
     .routeId('resources')
     .setBody()
-        .simple("resource:classpath:resources-data.txt")
+        .simple("resource:platform:resources-data.txt")
     .log('file content is: ${body}')
diff --git a/pkg/trait/knative.go b/pkg/trait/knative.go
index b31ef07..ae2e30d 100644
--- a/pkg/trait/knative.go
+++ b/pkg/trait/knative.go
@@ -156,6 +156,29 @@ func (t *knativeTrait) getServiceFor(e *Environment) *serving.Service {
 		sources = append(sources, src)
 	}
 
+	for i, r := range e.Integration.Spec.Resources {
+		envName := fmt.Sprintf("CAMEL_K_RESOURCE_%03d", i)
+		envvar.SetVal(&environment, envName, r.Content)
+
+		params := make([]string, 0)
+		if r.Compression {
+			params = append(params, "compression=true")
+		}
+
+		envValue := fmt.Sprintf("env:%s", envName)
+		if len(params) > 0 {
+			envValue = fmt.Sprintf("%s?%s", envValue, strings.Join(params, "&"))
+		}
+
+		envName = r.Name
+		envName = strings.ToUpper(envName)
+		envName = strings.Replace(envName, "-", "_", -1)
+		envName = strings.Replace(envName, ".", "_", -1)
+		envName = strings.Replace(envName, " ", "_", -1)
+
+		envvar.SetVal(&environment, envName, envValue)
+	}
+
 	// set env vars needed by the runtime
 	envvar.SetVal(&environment, "JAVA_MAIN_CLASS", "org.apache.camel.k.jvm.Application")
 
diff --git a/pkg/trait/knative_test.go b/pkg/trait/knative_test.go
index 2d50e48..9827ef9 100644
--- a/pkg/trait/knative_test.go
+++ b/pkg/trait/knative_test.go
@@ -58,6 +58,22 @@ func TestKnativeTraitWithCompressedSources(t *testing.T) {
 						Language: v1alpha1.LanguageJavaScript,
 					},
 				},
+				Resources: []v1alpha1.ResourceSpec{
+					{
+						DataSpec: v1alpha1.DataSpec{
+							Name:        "my-resource.txt",
+							Content:     content,
+							Compression: false,
+						},
+					},
+					{
+						DataSpec: v1alpha1.DataSpec{
+							Name:        "my-resource.gz",
+							Content:     content,
+							Compression: true,
+						},
+					},
+				},
 			},
 		},
 		Platform: &v1alpha1.IntegrationPlatform{
@@ -94,6 +110,22 @@ func TestKnativeTraitWithCompressedSources(t *testing.T) {
 		route := util.LookupEnvVar(vars, "CAMEL_K_ROUTE_000")
 		assert.NotNil(t, route)
 		assert.Equal(t, content, route.Value)
+
+		resource := util.LookupEnvVar(vars, "MY_RESOURCE_TXT")
+		assert.NotNil(t, resource)
+		assert.Equal(t, "env:CAMEL_K_RESOURCE_000", resource.Value)
+
+		resource = util.LookupEnvVar(vars, "CAMEL_K_RESOURCE_000")
+		assert.NotNil(t, resource)
+		assert.Equal(t, content, resource.Value)
+
+		resource = util.LookupEnvVar(vars, "MY_RESOURCE_GZ")
+		assert.NotNil(t, resource)
+		assert.Equal(t, "env:CAMEL_K_RESOURCE_001?compression=true", resource.Value)
+
+		resource = util.LookupEnvVar(vars, "CAMEL_K_RESOURCE_001")
+		assert.NotNil(t, resource)
+		assert.Equal(t, content, resource.Value)
 	})
 
 	assert.True(t, services > 0)
diff --git a/runtime/camel-k-runtime-jvm/pom.xml b/runtime/camel-k-runtime-jvm/pom.xml
index aa48603..a4d1832 100644
--- a/runtime/camel-k-runtime-jvm/pom.xml
+++ b/runtime/camel-k-runtime-jvm/pom.xml
@@ -96,4 +96,19 @@
         </dependency>
     </dependencies>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <environmentVariables>
+                        <CAMEL_K_RESOURCE_001>value from env</CAMEL_K_RESOURCE_001>
+                        <MY_OTHER_RESOURCE_TXT>env:CAMEL_K_RESOURCE_001</MY_OTHER_RESOURCE_TXT>
+                    </environmentVariables>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>
diff --git a/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/Application.java b/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/Application.java
index 682609c..b98c95b 100644
--- a/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/Application.java
+++ b/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/Application.java
@@ -34,6 +34,13 @@ public class Application {
         // We now support setting the logging level only
         //
         RuntimeSupport.configureLogging();
+
+        //
+        // Install a custom protocol handler to support discovering resources
+        // from the platform i.e. in knative, resources are provided through
+        // env var as it is not possible to mount config maps / secrets.
+        //
+        RuntimeSupport.configureStreamHandler();
     }
 
     // *******************************
diff --git a/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java b/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
index 700d70d..60d1cf9 100644
--- a/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
+++ b/runtime/camel-k-runtime-jvm/src/main/java/org/apache/camel/k/jvm/RuntimeSupport.java
@@ -16,8 +16,15 @@
  */
 package org.apache.camel.k.jvm;
 
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Reader;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
 import java.nio.file.FileVisitResult;
 import java.nio.file.FileVisitor;
 import java.nio.file.Files;
@@ -25,8 +32,11 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Base64;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
+import java.util.zip.GZIPInputStream;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.NoFactoryAvailableException;
@@ -38,7 +48,9 @@ import org.apache.camel.k.Source;
 import org.apache.camel.spi.FactoryFinder;
 import org.apache.camel.util.IntrospectionSupport;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
 import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.core.LoggerContext;
@@ -203,4 +215,90 @@ public final class RuntimeSupport {
 
         return loader;
     }
+
+    public static void configureStreamHandler() {
+        URL.setURLStreamHandlerFactory(protocol -> "platform".equals(protocol) ? new PlatformStreamHandler() : null);
+    }
+
+    // ***************************************
+    //
+    //
+    //
+    // ***************************************
+
+    private static class PlatformStreamHandler extends URLStreamHandler {
+        @Override
+        protected URLConnection openConnection(URL url) throws IOException {
+            return new URLConnection(url) {
+                @Override
+                public void connect() throws IOException {
+                }
+
+                @Override
+                public InputStream getInputStream() throws IOException {
+                    InputStream is = null;
+
+                    // check if the file exists
+                    Path path = Paths.get(url.getPath());
+                    if (Files.exists(path)) {
+                        is = Files.newInputStream(path);
+                    }
+
+                    // check if the file exists in classpath
+                    if (is == null) {
+                        is = ObjectHelper.loadResourceAsStream(url.getPath());
+                    }
+
+                    if (is == null) {
+                        String name = getURL().getPath().toUpperCase();
+                        name = name.replace(" ", "_");
+                        name = name.replace(".", "_");
+                        name = name.replace("-", "_");
+
+                        String envName = System.getenv(name);
+                        String envType = StringUtils.substringBefore(envName, ":");
+                        String envQuery = StringUtils.substringAfter(envName, "?");
+
+                        envName = StringUtils.substringAfter(envName, ":");
+                        envName = StringUtils.substringBefore(envName, "?");
+
+                        if (envName != null) {
+                            try {
+                                final Map<String, Object> params = URISupport.parseQuery(envQuery);
+                                final boolean compression = Boolean.valueOf((String) params.get("compression"));
+
+                                if (StringUtils.equals(envType, "env")) {
+                                    String data = System.getenv(envName);
+
+                                    if (data == null) {
+                                        throw new IllegalArgumentException("Unknown env var: " + envName);
+                                    }
+
+                                    is = new ByteArrayInputStream(data.getBytes());
+                                } else if (StringUtils.equals(envType, "file")) {
+                                    Path data = Paths.get(envName);
+
+                                    if (!Files.exists(data)) {
+                                        throw new FileNotFoundException(envName);
+                                    }
+
+                                    is = Files.newInputStream(data);
+                                } else if (StringUtils.equals(envType, "classpath")) {
+                                    is = ObjectHelper.loadResourceAsStream(envName);
+                                }
+
+                                if (is != null && compression) {
+                                    is = new GZIPInputStream(Base64.getDecoder().wrap(is));
+                                }
+                            } catch (URISyntaxException e) {
+                                throw new IOException(e);
+                            }
+                        }
+                    }
+
+                    return is;
+                }
+            };
+        }
+    }
 }
diff --git a/runtime/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java b/runtime/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java
index 909ae23..e434c26 100644
--- a/runtime/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java
+++ b/runtime/camel-k-runtime-jvm/src/test/java/org/apache/camel/k/jvm/RuntimeTest.java
@@ -16,11 +16,15 @@
  */
 package org.apache.camel.k.jvm;
 
+import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.List;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.Route;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ResourceHelper;
+import org.apache.commons.io.IOUtils;
 import org.junit.jupiter.api.Test;
 
 import static org.assertj.core.api.Java6Assertions.assertThat;
@@ -48,4 +52,24 @@ public class RuntimeTest {
             runtime.stop();
         }
     }
+
+
+    @Test
+    void testLoadResource() throws Exception {
+        RuntimeSupport.configureStreamHandler();
+
+        CamelContext context = new Runtime().getCamelContext();
+
+        try (InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(context, "platform:my-resource.txt")) {
+            String content = IOUtils.toString(is, Charset.defaultCharset());
+
+            assertThat(content).isEqualTo("value from file resource");
+        }
+
+        try (InputStream is = ResourceHelper.resolveMandatoryResourceAsInputStream(context, "platform:my-other-resource.txt")) {
+            String content = IOUtils.toString(is, Charset.defaultCharset());
+
+            assertThat(content).isEqualTo("value from env");
+        }
+    }
 }
diff --git a/runtime/camel-k-runtime-jvm/src/test/resources/my-resource.txt b/runtime/camel-k-runtime-jvm/src/test/resources/my-resource.txt
new file mode 100644
index 0000000..817b31f
--- /dev/null
+++ b/runtime/camel-k-runtime-jvm/src/test/resources/my-resource.txt
@@ -0,0 +1 @@
+value from file resource
\ No newline at end of file
diff --git a/runtime/camel-k-runtime-kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt b/runtime/camel-k-runtime-kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt
index 7902539..d72bb60 100644
--- a/runtime/camel-k-runtime-kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt
+++ b/runtime/camel-k-runtime-kotlin/src/main/kotlin/org/apache/camel/k/kotlin/KotlinRoutesLoader.kt
@@ -21,7 +21,7 @@ import org.apache.camel.k.Language
 import org.apache.camel.k.RoutesLoader
 import org.apache.camel.k.RuntimeRegistry
 import org.apache.camel.k.Source
-import org.apache.camel.k.jvm.*
+import org.apache.camel.k.jvm.URIResolver
 import org.apache.camel.k.kotlin.dsl.IntegrationConfiguration
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
diff --git a/runtime/camel-k-runtime-spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java b/runtime/camel-k-runtime-spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java
index 427dd9e..a6fbb5a 100644
--- a/runtime/camel-k-runtime-spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java
+++ b/runtime/camel-k-runtime-spring-boot/src/main/java/org/apache/camel/k/spring/boot/Application.java
@@ -42,6 +42,15 @@ import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
 public class Application {
     private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
 
+    static {
+        //
+        // Install a custom protocol handler to support discovering resources
+        // from the platform i.e. in knative, resources are provided through
+        // env var as it is not possible to mount config maps / secrets.
+        //
+        RuntimeSupport.configureStreamHandler();
+    }
+
     public static void main(String[] args) {
         SpringApplication.run(Application.class, args);
     }


[camel-k] 02/03: fix typo

Posted by nf...@apache.org.
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 7de58ca6a3ed33006fbf2450f7edfa28413ec51d
Author: lburgazzoli <lb...@gmail.com>
AuthorDate: Tue Dec 18 10:18:26 2018 +0100

    fix typo
---
 pkg/builder/builder_steps.go          | 2 +-
 pkg/builder/builder_types.go          | 2 +-
 pkg/builder/springboot/initializer.go | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/pkg/builder/builder_steps.go b/pkg/builder/builder_steps.go
index 8799d58..b50fb9b 100644
--- a/pkg/builder/builder_steps.go
+++ b/pkg/builder/builder_steps.go
@@ -246,7 +246,7 @@ func ListPublishedImages(context *Context) ([]PublishedImage, error) {
 		if ctx.Status.Phase != v1alpha1.IntegrationContextPhaseReady || ctx.Labels == nil {
 			continue
 		}
-		if context.ContextFiler != nil && !context.ContextFiler(&ctx) {
+		if context.ContextFilter != nil && !context.ContextFilter(&ctx) {
 			continue
 		}
 		if ctxType, present := ctx.Labels["camel.apache.org/context.type"]; !present || ctxType != v1alpha1.IntegrationContextTypePlatform {
diff --git a/pkg/builder/builder_types.go b/pkg/builder/builder_types.go
index 89e2e86..d0e13b4 100644
--- a/pkg/builder/builder_types.go
+++ b/pkg/builder/builder_types.go
@@ -147,7 +147,7 @@ type Context struct {
 	Artifacts         []v1alpha1.Artifact
 	SelectedArtifacts []v1alpha1.Artifact
 	Archive           string
-	ContextFiler      func(integrationContext *v1alpha1.IntegrationContext) bool
+	ContextFilter     func(integrationContext *v1alpha1.IntegrationContext) bool
 }
 
 // HasRequiredImage --
diff --git a/pkg/builder/springboot/initializer.go b/pkg/builder/springboot/initializer.go
index 1add06e..d8e18f5 100644
--- a/pkg/builder/springboot/initializer.go
+++ b/pkg/builder/springboot/initializer.go
@@ -27,7 +27,7 @@ func Initialize(ctx *builder.Context) error {
 	// 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 {
+	ctx.ContextFilter = func(context *v1alpha1.IntegrationContext) bool {
 		for _, i := range context.Spec.Dependencies {
 			if i == "runtime:spring" {
 				return true


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

Posted by nf...@apache.org.
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)