You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2016/06/15 13:44:17 UTC

[2/9] camel git commit: CAMEL-10052: Spring-boot integration tests

http://git-wip-us.apache.org/repos/asf/camel/blob/c39b9558/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipExporterDelegate.java
----------------------------------------------------------------------
diff --git a/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipExporterDelegate.java b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipExporterDelegate.java
new file mode 100644
index 0000000..7c5a9b8
--- /dev/null
+++ b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipExporterDelegate.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.itest.springboot.arquillian;
+
+import java.io.InputStream;
+
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ArchivePath;
+import org.jboss.shrinkwrap.api.Node;
+import org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate;
+
+/**
+ * A spring-boot compatible zip exporter delegate.
+ * Uses {@link SpringBootZipOnDemandInputStream} not to compress entries for spring-boot
+ * nested jar structure compatibility.
+ */
+public class SpringBootZipExporterDelegate extends AbstractExporterDelegate<InputStream> {
+
+    protected SpringBootZipExporterDelegate(final Archive<?> archive) {
+        super(archive);
+
+        if (archive.getContent().isEmpty()) {
+            throw new IllegalArgumentException("Cannot export a ZIP archive with no content: " + archive.toString());
+        }
+    }
+
+    @Override
+    protected void processNode(final ArchivePath path, final Node node) {
+        // do nothing
+    }
+
+    @Override
+    protected InputStream getResult() {
+        return new SpringBootZipOnDemandInputStream(getArchive());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c39b9558/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipExporterImpl.java
----------------------------------------------------------------------
diff --git a/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipExporterImpl.java b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipExporterImpl.java
new file mode 100644
index 0000000..920fbe5
--- /dev/null
+++ b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipExporterImpl.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.itest.springboot.arquillian;
+
+import java.io.InputStream;
+
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.exporter.ZipExporter;
+import org.jboss.shrinkwrap.impl.base.exporter.AbstractExporterDelegate;
+import org.jboss.shrinkwrap.impl.base.exporter.AbstractStreamExporterImpl;
+
+/**
+ * An implementation of the zip exporter that does not compress entries,
+ * for compatibility with spring-boot nested jar structure.
+ */
+public class SpringBootZipExporterImpl extends AbstractStreamExporterImpl implements ZipExporter {
+
+    public SpringBootZipExporterImpl(Archive<?> archive) {
+        super(archive);
+    }
+
+    @Override
+    public InputStream exportAsInputStream() {
+        // Create export delegate
+        final AbstractExporterDelegate<InputStream> exportDelegate = new SpringBootZipExporterDelegate(this.getArchive());
+
+        // Export and get result
+        return exportDelegate.export();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c39b9558/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipOnDemandInputStream.java
----------------------------------------------------------------------
diff --git a/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipOnDemandInputStream.java b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipOnDemandInputStream.java
new file mode 100644
index 0000000..70aa374
--- /dev/null
+++ b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/arquillian/SpringBootZipOnDemandInputStream.java
@@ -0,0 +1,224 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.itest.springboot.arquillian;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ArchivePath;
+import org.jboss.shrinkwrap.api.Node;
+import org.jboss.shrinkwrap.api.exporter.ArchiveExportException;
+import org.jboss.shrinkwrap.impl.base.io.IOUtil;
+import org.jboss.shrinkwrap.impl.base.path.PathUtil;
+
+/**
+ * A spring-boot compatible on-demand input stream.
+ * It does not compress jar entries for spring-boot nested jar structure compatibility.
+ */
+public class SpringBootZipOnDemandInputStream extends InputStream {
+
+    /**
+     * Created by abstract method.
+     */
+    protected ZipOutputStream outputStream;
+
+    /**
+     * Iterator over nodes contained in base archive.
+     */
+    private final Iterator<Node> nodesIterator;
+
+    /**
+     * Base for outputStream.
+     */
+    private final ByteArrayOutputStream bufferedOutputStream = new ByteArrayOutputStream();
+
+    /**
+     * Stream of currently processed Node.
+     */
+    private InputStream currentNodeStream;
+
+    /**
+     * Stream to the buffer.
+     */
+    private ByteArrayInputStream bufferInputStream;
+
+    /**
+     * If output stream was closed - we should finish.
+     */
+    private boolean outputStreamClosed;
+
+    /**
+     * Currently processed archive path - for displaying exception.
+     */
+    private ArchivePath currentPath;
+
+    /**
+     * Creates stream directly from archive.
+     *
+     * @param archive
+     */
+    public SpringBootZipOnDemandInputStream(final Archive<?> archive) {
+        final Collection<Node> nodes = archive.getContent().values();
+        this.nodesIterator = nodes.iterator();
+    }
+
+    @Override
+    public int read() throws IOException {
+
+        if (outputStream == null && !outputStreamClosed) {
+            // first run
+            outputStream = createOutputStream(bufferedOutputStream);
+        }
+
+        int value = bufferInputStream != null ? bufferInputStream.read() : -1;
+        if (value == -1) {
+            if (currentNodeStream != null) {
+                // current node was not processed completely
+                try {
+                    doCopy();
+                    bufferInputStream = new ByteArrayInputStream(bufferedOutputStream.toByteArray());
+                    bufferedOutputStream.reset();
+                    return this.read();
+                } catch (final Throwable t) {
+                    throw new ArchiveExportException("Failed to write asset to output: " + currentPath.get(), t);
+                }
+            } else if (nodesIterator.hasNext()) {
+                // current node was processed completely, process next one
+                final Node currentNode = nodesIterator.next();
+
+                currentPath = currentNode.getPath();
+                final String pathName = PathUtil.optionallyRemovePrecedingSlash(currentPath.get());
+
+                final boolean isDirectory = currentNode.getAsset() == null;
+                String resolvedPath = pathName;
+
+                if (isDirectory) {
+                    resolvedPath = PathUtil.optionallyAppendSlash(resolvedPath);
+                    startAsset(resolvedPath, 0L, 0L);
+                    endAsset();
+                } else {
+
+                    try {
+                        byte[] content = IOUtil.asByteArray(currentNode.getAsset().openStream());
+                        long size = content.length;
+                        CRC32 crc = new CRC32();
+                        crc.update(content);
+                        long crc32Value = crc.getValue();
+                        startAsset(resolvedPath, size, crc32Value);
+
+                        currentNodeStream = new ByteArrayInputStream(content);
+                        doCopy();
+                    } catch (final Throwable t) {
+                        throw new ArchiveExportException("Failed to write asset to output: " + currentPath.get(), t);
+                    }
+                    bufferInputStream = new ByteArrayInputStream(bufferedOutputStream.toByteArray());
+                    bufferedOutputStream.reset();
+                }
+
+            } else {
+                // each node was processed
+                if (!outputStreamClosed) {
+                    outputStream.close();
+                    outputStreamClosed = true;
+
+                    // output closed, now process what was saved on close
+                    bufferInputStream = new ByteArrayInputStream(bufferedOutputStream.toByteArray());
+                    bufferedOutputStream.close();
+
+                    currentNodeStream = null;
+                    outputStream = null;
+                    return this.read();
+                }
+
+                // everything was read, end
+                return -1;
+            }
+
+            // chosen new node or new data in buffer - read again
+            return this.read();
+        }
+
+        return value;
+    }
+
+    /**
+     * Performs copy operation between currentNodeStream and outputStream using buffer length.
+     *
+     * @throws IOException
+     */
+    private void doCopy() throws IOException {
+        IOUtil.copy(currentNodeStream, outputStream);
+        currentNodeStream.close();
+        currentNodeStream = null;
+        endAsset();
+    }
+
+    /**
+     * Start entry in stream.
+     *
+     * @param path
+     * @throws IOException
+     */
+    private void startAsset(final String path, long size, long crc32) throws IOException {
+        putNextEntry(outputStream, path, size, crc32);
+    }
+
+    /**
+     * Close entry in stream.
+     *
+     * @throws IOException
+     */
+    private void endAsset() throws IOException {
+        closeEntry(outputStream);
+    }
+
+
+    protected ZipOutputStream createOutputStream(final OutputStream outputStream) {
+        ZipOutputStream stream = new ZipOutputStream(outputStream);
+        stream.setMethod(ZipEntry.STORED);
+        stream.setLevel(Deflater.NO_COMPRESSION);
+        return stream;
+    }
+
+
+    protected void closeEntry(final ZipOutputStream outputStream) throws IOException {
+        outputStream.closeEntry();
+    }
+
+
+    protected void putNextEntry(final ZipOutputStream outputStream, final String context, long size, long crc32) throws IOException {
+
+        ZipEntry entry = new ZipEntry(context);
+        entry.setMethod(ZipEntry.STORED);
+        entry.setSize(size);
+        entry.setCrc(crc32);
+
+        outputStream.putNextEntry(entry);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c39b9558/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/ArquillianPackager.java
----------------------------------------------------------------------
diff --git a/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/ArquillianPackager.java b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/ArquillianPackager.java
new file mode 100644
index 0000000..c9d9e9f
--- /dev/null
+++ b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/ArquillianPackager.java
@@ -0,0 +1,237 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.itest.springboot.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.camel.itest.springboot.ITestConfig;
+import org.apache.camel.itest.springboot.arquillian.SpringBootZipExporterImpl;
+import org.apache.commons.io.FileUtils;
+import org.jboss.arquillian.container.se.api.ClassPath;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.Configuration;
+import org.jboss.shrinkwrap.api.ConfigurationBuilder;
+import org.jboss.shrinkwrap.api.Domain;
+import org.jboss.shrinkwrap.api.ExtensionLoader;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.FileAsset;
+import org.jboss.shrinkwrap.api.exporter.ZipExporter;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.jboss.shrinkwrap.impl.base.ServiceExtensionLoader;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.jboss.shrinkwrap.resolver.api.maven.MavenResolvedArtifact;
+import org.jboss.shrinkwrap.resolver.api.maven.PackagingType;
+import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenCoordinate;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenCoordinates;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependencies;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependency;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependencyExclusion;
+import org.junit.Assert;
+
+/**
+ * Packages a module in a spring-boot compatible nested-jar structure.
+ */
+public final class ArquillianPackager {
+
+    /**
+     * A flag to enable system-out logging.
+     * Cannot use logging libraries here.
+     */
+    private static final boolean DEBUG_ENABLED = false;
+
+    private ArquillianPackager() {
+    }
+
+    public static Archive<?> springBootPackage(ITestConfig config) throws Exception {
+
+        ExtensionLoader extensionLoader = new ServiceExtensionLoader(Collections.singleton(getExtensionClassloader()));
+        extensionLoader.addOverride(ZipExporter.class, SpringBootZipExporterImpl.class);
+        ConfigurationBuilder builder = new ConfigurationBuilder().extensionLoader(extensionLoader);
+        Configuration conf = builder.build();
+
+        Domain domain = ShrinkWrap.createDomain(conf);
+
+        JavaArchive ark = domain.getArchiveFactory().create(JavaArchive.class, "test.jar");
+
+        ark = ark.addAsManifestResource("BOOT-MANIFEST.MF", "MANIFEST.MF");
+        ark = ark.addAsResource("spring-boot-itest.properties");
+
+        ark = ark.addAsDirectories("/lib");
+
+        String version = config.getMavenVersion();
+        if (version == null) {
+            // It is missing when launching from IDE
+            List<MavenResolvedArtifact> resolved = Arrays.asList(Maven.resolver().loadPomFromFile("pom.xml").importRuntimeDependencies().resolve().withTransitivity().asResolvedArtifact());
+            Optional<MavenResolvedArtifact> camelDep = resolved.stream().filter(dep -> dep.getCoordinate().getGroupId().equals("org.apache.camel")).findAny();
+            version = camelDep.map(art -> art.getCoordinate().getVersion()).orElse(null);
+            debug("Resolved version: " + version);
+            if (version == null) {
+                throw new IllegalStateException("Cannot determine the current version of the camel component");
+            }
+        }
+
+        // Test dependencies
+        List<MavenDependency> testDependencies = new LinkedList<>();
+        if (config.getIncludeTestDependencies() || config.getUnitTestEnabled()) {
+
+            List<MavenResolvedArtifact> testArtifacts = Arrays.asList(Maven.resolver()
+                    .loadPomFromFile(config.getModulesPath() + config.getModuleName() + "/pom.xml")
+                    .importTestDependencies()
+                    .resolve().withoutTransitivity().asResolvedArtifact());
+
+            MavenDependencyExclusion[] excl = new MavenDependencyExclusion[]{MavenDependencies.createExclusion("org.slf4j", "slf4j-log4j12"), MavenDependencies.createExclusion("log4j", "log4j")};
+
+            testDependencies = testArtifacts.stream()
+                    .map(MavenResolvedArtifact::getCoordinate)
+                    .filter(ArquillianPackager::validTestDependency) // remove direct logging dependencies from test libs
+                    .map(c -> MavenDependencies.createDependency(c, ScopeType.RUNTIME, false, excl)) // remove transitive logging dependencies from test libs
+                    .collect(Collectors.toList());
+        }
+
+
+        MavenCoordinate jar = MavenCoordinates.createCoordinate(config.getMavenGroup(), config.getModuleName(), version, PackagingType.JAR, null);
+        MavenDependency dep = MavenDependencies.createDependency(jar, ScopeType.COMPILE, false);
+
+        List<File> dependencies = new LinkedList<>();
+        dependencies.addAll(Arrays.asList(Maven.resolver()
+                .loadPomFromFile("pom.xml")
+                .importRuntimeDependencies()
+                .addDependencies(dep)
+                .addDependencies(testDependencies)
+                .resolve()
+                .withTransitivity()
+                .asFile()));
+
+
+        // The spring boot-loader dependency will be added to the main jar, so it should be excluded from the embedded ones
+        excludeDependencyRegex(dependencies, "^spring-boot-loader-[0-9].*");
+
+        // Add all dependencies as spring-boot nested jars
+        ark = addDependencies(ark, dependencies);
+
+        if (config.getUnitTestEnabled()) {
+            // Add unit test classes of the module under test
+            ark = addTestResources(ark, config);
+        }
+
+        // Add common packages to main jar
+        ark = ark.addPackages(true, "org.apache.camel.itest.springboot");
+        ark = ark.addPackages(true, "org.springframework.boot.loader");
+        ark = ark.addPackages(true, "org.jboss.shrinkwrap");
+
+        return ClassPath.builder().add(ark).build();
+    }
+
+    public static void copyResource(String folder, String fileNameRegex, String targetFolder) throws IOException {
+
+        Pattern pattern = Pattern.compile(fileNameRegex);
+
+        File sourceFolder = new File(folder);
+        File[] candidates = sourceFolder.listFiles((dir, name) -> pattern.matcher(name).matches());
+        if (candidates.length == 0) {
+            Assert.fail("No file matching regex " + fileNameRegex + " has been found");
+        }
+
+        File f = candidates[0];
+        FileUtils.copyFileToDirectory(f, new File(targetFolder));
+    }
+
+    private static ClassLoader getExtensionClassloader() {
+        ClassLoader cl = AccessController.doPrivileged((PrivilegedAction<ClassLoader>) () -> Thread.currentThread().getContextClassLoader());
+        if (cl == null) {
+            cl = ClassLoader.getSystemClassLoader();
+        }
+
+        return cl;
+    }
+
+    private static boolean validTestDependency(MavenCoordinate coordinate) {
+
+        Pattern log4j = Pattern.compile("^log4j$");
+        Pattern slf4jLog4j = Pattern.compile("^slf4j-log4j12$");
+
+        boolean valid = !log4j.matcher(coordinate.getArtifactId()).matches() && !slf4jLog4j.matcher(coordinate.getArtifactId()).matches();
+
+        if (!valid) {
+            debug("Discarded test dependency " + coordinate.toCanonicalForm());
+        }
+
+        return valid;
+    }
+
+    private static void excludeDependencyRegex(List<File> dependencies, String regex) {
+        Pattern pattern = Pattern.compile(regex);
+        for (Iterator<File> it = dependencies.iterator(); it.hasNext();) {
+            File f = it.next();
+            if (pattern.matcher(f.getName()).matches()) {
+                it.remove();
+                break;
+            }
+        }
+    }
+
+    private static JavaArchive addDependencies(JavaArchive ark, Collection<File> deps) {
+        Set<File> dependencySet = new HashSet<>(deps);
+        for (File d : dependencySet) {
+            debug("Adding spring-boot dependency: " + d.getName());
+            ark = ark.add(new FileAsset(d), "/lib/" + d.getName());
+        }
+
+        return ark;
+    }
+
+    private static JavaArchive addTestResources(JavaArchive ark, ITestConfig config) throws IOException {
+        File test = new File(config.getModulesPath() + config.getModuleName() + "/target/test-classes/");
+        File[] fs = Optional.ofNullable(test.listFiles()).orElse(new File[]{});
+        LinkedList<File> testFiles = new LinkedList<>(Arrays.asList(fs));
+        while (!testFiles.isEmpty()) {
+            File f = testFiles.pop();
+            String relative = test.getCanonicalFile().toURI().relativize(f.getCanonicalFile().toURI()).getPath();
+            if (f.isFile()) {
+                ark = ark.addAsResource(f, relative);
+            } else {
+                ark = ark.addAsDirectory(relative);
+                File[] files = Optional.ofNullable(f.listFiles()).orElse(new File[]{});
+                testFiles.addAll(Arrays.asList(files));
+            }
+        }
+
+        return ark;
+    }
+
+    private static void debug(String str) {
+        if (DEBUG_ENABLED) {
+            System.out.println("DEBUG>>> " + str);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c39b9558/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/JarExporter.java
----------------------------------------------------------------------
diff --git a/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/JarExporter.java b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/JarExporter.java
new file mode 100644
index 0000000..4157559
--- /dev/null
+++ b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/JarExporter.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.itest.springboot.util;
+
+import java.io.File;
+
+import org.apache.camel.itest.springboot.ITestConfigBuilder;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.impl.base.exporter.zip.ZipExporterImpl;
+import org.junit.Test;
+
+/**
+ * Utililty to export a spring-boot jar and check the content.
+ */
+public class JarExporter {
+
+    @Test
+    public void exportJar() throws Exception {
+
+        Archive<?> archive = ArquillianPackager.springBootPackage(new ITestConfigBuilder()
+                .module("camel-jetty9")
+                .build());
+
+
+        new ZipExporterImpl(archive).exportTo(new File("target/export.zip"), true);
+
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c39b9558/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/SpringBootContainerFacade.java
----------------------------------------------------------------------
diff --git a/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/SpringBootContainerFacade.java b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/SpringBootContainerFacade.java
new file mode 100644
index 0000000..190c6cb
--- /dev/null
+++ b/tests/camel-itest-spring-boot/src/test/java/org/apache/camel/itest/springboot/util/SpringBootContainerFacade.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.itest.springboot.util;
+
+import java.lang.reflect.Method;
+
+import org.apache.camel.itest.springboot.ITestConfig;
+
+/**
+ * Provide access to testing methods defined in the Spring-Boot context (that uses a different classloader).
+ */
+public class SpringBootContainerFacade {
+
+    private Class<?> delegateClass;
+
+    public SpringBootContainerFacade(ClassLoader springClassloader) {
+        try {
+            this.delegateClass = springClassloader.loadClass("org.apache.camel.itest.springboot.CommandRouter");
+        } catch (ClassNotFoundException e) {
+            throw new IllegalStateException("Exception while loading target class", e);
+        }
+    }
+
+    public Object executeTest(String test, ITestConfig config, String component) throws Exception {
+        Object resObj = execute(test, config, component);
+        return resObj;
+    }
+
+    private Object execute(String command, Object... args) throws Exception {
+        Method method = delegateClass.getMethod("execute", String.class, byte[].class);
+        byte[] argsSer = null;
+        if (args != null) {
+            argsSer = SerializationUtils.marshal(args);
+        }
+
+        byte[] resByte = (byte[]) method.invoke(null, command, argsSer);
+        if (resByte != null) {
+            Object res = SerializationUtils.unmarshal(resByte);
+            if (res instanceof Exception) {
+                throw (Exception) res;
+            } else if (res instanceof Throwable) {
+                throw new RuntimeException((Throwable) res);
+            } else {
+                return res;
+            }
+        }
+
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/camel/blob/c39b9558/tests/camel-itest-spring-boot/src/test/resources/BOOT-MANIFEST.MF
----------------------------------------------------------------------
diff --git a/tests/camel-itest-spring-boot/src/test/resources/BOOT-MANIFEST.MF b/tests/camel-itest-spring-boot/src/test/resources/BOOT-MANIFEST.MF
new file mode 100644
index 0000000..b8f139f
--- /dev/null
+++ b/tests/camel-itest-spring-boot/src/test/resources/BOOT-MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: org.springframework.boot.loader.JarLauncher
+Start-Class: org.apache.camel.itest.springboot.ITestApplication

http://git-wip-us.apache.org/repos/asf/camel/blob/c39b9558/tests/camel-itest-spring-boot/src/test/resources/spring-boot-itest.properties
----------------------------------------------------------------------
diff --git a/tests/camel-itest-spring-boot/src/test/resources/spring-boot-itest.properties b/tests/camel-itest-spring-boot/src/test/resources/spring-boot-itest.properties
new file mode 100644
index 0000000..a1e2702
--- /dev/null
+++ b/tests/camel-itest-spring-boot/src/test/resources/spring-boot-itest.properties
@@ -0,0 +1,9 @@
+# Test configuration can be overriden here.
+# This file is included in the spring-boot jar built  by Arquillian.
+
+# It is better disabling unit testing for all-modules-verification,
+# as many of them fail for various reasons (unrelated to spring-boot)
+# when running in the arquillian jar.
+unitTestEnabled=false
+
+includeTestDependencies=false

http://git-wip-us.apache.org/repos/asf/camel/blob/c39b9558/tests/pom.xml
----------------------------------------------------------------------
diff --git a/tests/pom.xml b/tests/pom.xml
index b21d46d..d9c6e12 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -66,6 +66,14 @@
       </modules>
     </profile>
 
+    <!-- spring-boot tests -->
+    <profile>
+      <id>spring-boot.test</id>
+      <modules>
+        <module>camel-itest-spring-boot</module>
+      </modules>
+    </profile>
+
     <!-- performance tests -->
     <profile>
       <id>performance.test</id>