You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ac...@apache.org on 2019/12/03 10:45:33 UTC

[camel] branch master updated (c71e3a3 -> 8de58e2)

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

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


    from c71e3a3  Upgrade AWS Xray to version 2.4.0
     new b5a0b1d  CAMEL-14215: Adding camel-main-osgi.
     new 8de58e2  CAMEL-14125 - Regen

The 2 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:
 apache-camel/pom.xml                               |   4 +
 apache-camel/src/main/descriptors/common-bin.xml   |   1 +
 bom/camel-bom/pom.xml                              |   5 +
 core/camel-main-osgi/pom.xml                       | 182 +++++++++++++++++++++
 .../camel-main-osgi/src/assembly/test-bundles.xml  |  34 ++--
 .../camel/core/osgi/main/internal/Activator.java   | 115 +++++++++++++
 .../apache/camel/core/osgi/main/it/BundleIT.java   | 120 ++++++++++++++
 .../camel/core/osgi/main/it}/PaxExamOptions.java   |  53 +++---
 core/pom.xml                                       |   1 +
 parent/pom.xml                                     |   5 +
 .../karaf/features/src/main/resources/features.xml |   1 +
 .../camel-spring-boot-dependencies/pom.xml         |   5 +
 12 files changed, 482 insertions(+), 44 deletions(-)
 create mode 100644 core/camel-main-osgi/pom.xml
 copy components/camel-dozer/src/test/resources/bean-to-bean-dozer-mappings.xml => core/camel-main-osgi/src/assembly/test-bundles.xml (58%)
 create mode 100644 core/camel-main-osgi/src/main/java/org/apache/camel/core/osgi/main/internal/Activator.java
 create mode 100644 core/camel-main-osgi/src/test/java/org/apache/camel/core/osgi/main/it/BundleIT.java
 copy {examples/camel-example-fhir-osgi/src/test/java/org/apache/camel/example/fhir/osgi => core/camel-main-osgi/src/test/java/org/apache/camel/core/osgi/main/it}/PaxExamOptions.java (59%)


[camel] 02/02: CAMEL-14125 - Regen

Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 8de58e2411b124a5c9fda81528505ad59baa8ee8
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Tue Dec 3 11:40:38 2019 +0100

    CAMEL-14125 - Regen
---
 bom/camel-bom/pom.xml                                                | 5 +++++
 .../spring-boot-dm/camel-spring-boot-dependencies/pom.xml            | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml
index a133acc..035d811 100644
--- a/bom/camel-bom/pom.xml
+++ b/bom/camel-bom/pom.xml
@@ -2544,6 +2544,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-main-osgi</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-management</artifactId>
         <version>${project.version}</version>
       </dependency>
diff --git a/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml b/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml
index 64f99ef..77de07c 100644
--- a/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml
+++ b/platforms/spring-boot/spring-boot-dm/camel-spring-boot-dependencies/pom.xml
@@ -2764,6 +2764,11 @@
       </dependency>
       <dependency>
         <groupId>org.apache.camel</groupId>
+        <artifactId>camel-main-osgi</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.camel</groupId>
         <artifactId>camel-management</artifactId>
         <version>${project.version}</version>
       </dependency>


[camel] 01/02: CAMEL-14215: Adding camel-main-osgi.

Posted by ac...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b5a0b1d13180c2f1bc24c41c210036c4ac784fd9
Author: Bob Paulin <bo...@bobpaulin.com>
AuthorDate: Mon Dec 2 19:07:12 2019 -0600

    CAMEL-14215: Adding camel-main-osgi.
---
 apache-camel/pom.xml                               |   4 +
 apache-camel/src/main/descriptors/common-bin.xml   |   1 +
 core/camel-main-osgi/pom.xml                       | 182 +++++++++++++++++++++
 core/camel-main-osgi/src/assembly/test-bundles.xml |  37 +++++
 .../camel/core/osgi/main/internal/Activator.java   | 115 +++++++++++++
 .../apache/camel/core/osgi/main/it/BundleIT.java   | 120 ++++++++++++++
 .../camel/core/osgi/main/it/PaxExamOptions.java    |  70 ++++++++
 core/pom.xml                                       |   1 +
 parent/pom.xml                                     |   5 +
 .../karaf/features/src/main/resources/features.xml |   1 +
 10 files changed, 536 insertions(+)

diff --git a/apache-camel/pom.xml b/apache-camel/pom.xml
index 7556626..70fd984 100644
--- a/apache-camel/pom.xml
+++ b/apache-camel/pom.xml
@@ -107,6 +107,10 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-main</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.camel</groupId>
+            <artifactId>camel-main-osgi</artifactId>
+        </dependency>
 
         <!-- NOTE: auto-generated list of components when building camel catalog -->
         <!-- camel components: START -->
diff --git a/apache-camel/src/main/descriptors/common-bin.xml b/apache-camel/src/main/descriptors/common-bin.xml
index 1b1db70..eb29501 100644
--- a/apache-camel/src/main/descriptors/common-bin.xml
+++ b/apache-camel/src/main/descriptors/common-bin.xml
@@ -40,6 +40,7 @@
         <include>org.apache.camel:camel-cloud</include>
         <include>org.apache.camel:camel-jaxp</include>
         <include>org.apache.camel:camel-main</include>
+        <include>org.apache.camel:camel-main-osgi</include>
 
         <!-- NOTE: auto-generated list of components when building camel catalog -->
         <!-- camel components: START -->
diff --git a/core/camel-main-osgi/pom.xml b/core/camel-main-osgi/pom.xml
new file mode 100644
index 0000000..52a89da
--- /dev/null
+++ b/core/camel-main-osgi/pom.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.camel</groupId>
+    <artifactId>core</artifactId>
+    <version>3.1.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>camel-main-osgi</artifactId>
+  <packaging>jar</packaging>
+
+  <name>Camel :: Main OSGi</name>
+  <description>Camel Main OSGi</description>
+
+  <properties>
+    <maven.failsafe.version>2.22.1</maven.failsafe.version>
+    <camel.osgi.activator>org.apache.camel.core.osgi.main.internal.Activator</camel.osgi.activator>
+    <camel.osgi.dynamic>*</camel.osgi.dynamic>
+  </properties>
+
+  <dependencies>
+    <!-- osgi -->
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>osgi.cmpn</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>osgi.core</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.camel</groupId>
+      <artifactId>camel-core-osgi</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <!-- test -->
+
+    <!-- PAX Exam -->
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-spi</artifactId>
+      <version>${pax-exam-version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-junit4</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-container-karaf</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.url</groupId>
+      <artifactId>pax-url-aether</artifactId>
+      <version>2.4.5</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.tinybundles</groupId>
+      <artifactId>tinybundles</artifactId>
+      <version>2.1.1</version>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- Karaf & Command Shell -->
+    <dependency>
+      <groupId>org.apache.karaf</groupId>
+      <artifactId>apache-karaf</artifactId>
+      <version>${karaf4-version}</version>
+      <type>zip</type>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.apache.karaf.features</groupId>
+          <artifactId>framework</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
+    <!-- Karaf Features -->
+    <dependency>
+      <groupId>org.apache.camel.karaf</groupId>
+      <artifactId>apache-camel</artifactId>
+      <version>${project.version}</version>
+      <classifier>features</classifier>
+      <type>xml</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.inject</groupId>
+      <artifactId>javax.inject</artifactId>
+      <version>1</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.servicemix.tooling</groupId>
+        <artifactId>depends-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>generate-depends-file</id>
+            <goals>
+              <goal>generate-depends-file</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>pre-integration-test</phase>
+            <goals>
+              <goal>single</goal>
+            </goals>
+            <configuration>
+                <descriptors>
+                    <descriptor>src/assembly/test-bundles.xml</descriptor>
+                </descriptors>
+              <finalName>test</finalName>
+              <attach>false</attach>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <!-- Execute in the integration-test phase so that the packaged JAR 
+        can be used -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>integration-test</phase>
+            <goals>
+              <goal>test</goal>
+            </goals>
+            <configuration>
+              <includes>
+                <include>**/*IT.java</include>
+              </includes>
+              <forkedProcessTimeoutInSeconds>300</forkedProcessTimeoutInSeconds>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/core/camel-main-osgi/src/assembly/test-bundles.xml b/core/camel-main-osgi/src/assembly/test-bundles.xml
new file mode 100644
index 0000000..0350be6
--- /dev/null
+++ b/core/camel-main-osgi/src/assembly/test-bundles.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+  <id>bundles</id>
+  <formats>
+    <format>dir</format>
+  </formats>
+  <includeBaseDirectory>false</includeBaseDirectory>
+  <dependencySets>
+    <dependencySet>
+      <outputDirectory/>
+      <outputFileNameMapping>${artifact.artifactId}.jar</outputFileNameMapping>
+      <includes>
+        <include>org.apache.camel:camel-main-osgi</include>
+      </includes>
+    </dependencySet>
+  </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/core/camel-main-osgi/src/main/java/org/apache/camel/core/osgi/main/internal/Activator.java b/core/camel-main-osgi/src/main/java/org/apache/camel/core/osgi/main/internal/Activator.java
new file mode 100644
index 0000000..719cbf2
--- /dev/null
+++ b/core/camel-main-osgi/src/main/java/org/apache/camel/core/osgi/main/internal/Activator.java
@@ -0,0 +1,115 @@
+/*
+ * 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.core.osgi.main.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.core.osgi.OsgiDefaultCamelContext;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.model.RouteDefinition;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Activator implements BundleActivator, ServiceTrackerCustomizer<RouteBuilder, RouteBuilder> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
+    private ServiceRegistration<CamelContext> camelContextRef;
+    private ModelCamelContext camelContext;
+    private BundleContext bundleContext;
+    private ServiceTracker<RouteBuilder, RouteBuilder> routeServiceTracker;
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void start(BundleContext context) throws Exception {
+        this.bundleContext = context;
+        this.camelContext = new OsgiDefaultCamelContext(context);
+
+        this.routeServiceTracker = new ServiceTracker<RouteBuilder, RouteBuilder>(context, RouteBuilder.class, this);
+
+        camelContext.start();
+
+        camelContextRef = context.registerService(CamelContext.class, camelContext, null);
+
+        ServiceReference<RouteBuilder>[] existingRouteBuildersReferences = (ServiceReference<RouteBuilder>[]) context
+                .getAllServiceReferences(RouteBuilder.class.getName(), null);
+
+        if (existingRouteBuildersReferences != null) {
+            for (ServiceReference<RouteBuilder> currentRouteBuilderReference : existingRouteBuildersReferences) {
+                addingService(currentRouteBuilderReference);
+            }
+        }
+
+        this.routeServiceTracker.open();
+        LOG.info("Camel Main Osgi RouteBuilder ServiceTracker Tracker Open");
+    }
+
+    @Override
+    public RouteBuilder addingService(ServiceReference<RouteBuilder> reference) {
+        RouteBuilder builder = this.bundleContext.getService(reference);
+        try {
+            // need to synchronize here since adding routes is not synchronized
+            synchronized (camelContext) {
+                this.camelContext.addRoutes(builder);
+                LOG.debug("Camel Routes from RouteBuilder Class {} Added to Camel Main", builder.getClass().getName());
+            }
+        } catch (Exception e) {
+            LOG.error("Error Adding Camel Route Builder", e);
+        }
+
+        return builder;
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        this.routeServiceTracker.close();
+        camelContext.stop();
+        camelContext.removeRouteDefinitions(new ArrayList<RouteDefinition>(this.camelContext.getRouteDefinitions()));
+
+        context.ungetService(camelContextRef.getReference());
+    }
+
+    @Override
+    public void modifiedService(ServiceReference<RouteBuilder> reference, RouteBuilder service) {
+        removedService(reference, service);
+        addingService(reference);
+    }
+
+    @Override
+    public void removedService(ServiceReference<RouteBuilder> reference, RouteBuilder service) {
+        List<RouteDefinition> routesToBeRemoved = service.getRouteCollection().getRoutes();
+        try {
+            synchronized (camelContext) {
+                camelContext.removeRouteDefinitions(routesToBeRemoved);
+                LOG.debug("Camel Routes from RouteBuilder Class {} Removed to Camel Main",
+                        service.getClass().getName());
+            }
+        } catch (Exception e) {
+            LOG.error("Error Removing Camel Route Builder", e);
+        }
+
+    }
+
+}
diff --git a/core/camel-main-osgi/src/test/java/org/apache/camel/core/osgi/main/it/BundleIT.java b/core/camel-main-osgi/src/test/java/org/apache/camel/core/osgi/main/it/BundleIT.java
new file mode 100644
index 0000000..35df365
--- /dev/null
+++ b/core/camel-main-osgi/src/test/java/org/apache/camel/core/osgi/main/it/BundleIT.java
@@ -0,0 +1,120 @@
+/*
+ * 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.core.osgi.main.it;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.exam.CoreOptions.options;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class BundleIT {
+    @Inject
+    private BundleContext bc;
+
+    @Configuration
+    public Option[] configuration() throws IOException, URISyntaxException, ClassNotFoundException {
+        return options(
+                PaxExamOptions.KARAF.option(),
+                PaxExamOptions.CAMEL_CORE_OSGI.option(),
+                streamBundle(
+                    TinyBundles.bundle()
+                        .read(
+                            Files.newInputStream(
+                                Paths.get("target/test-bundles")
+                                    .resolve("camel-main-osgi.jar")))
+                        .build()),
+                junitBundles());
+    }
+    
+    @Test
+    public void testBundleLoaded() throws Exception {
+        boolean hasCore = false;
+        boolean hasOsgi = false;
+        boolean hasCamelMainOsgi = false;
+        for (Bundle b : bc.getBundles()) {
+            if ("org.apache.camel.camel-core".equals(b.getSymbolicName())) {
+                hasCore = true;
+                assertEquals("Camel Core not activated", Bundle.ACTIVE, b.getState());
+            }
+            if ("org.apache.camel.camel-core-osgi".equals(b.getSymbolicName())) {
+                hasOsgi = true;
+                assertEquals("Camel Core OSGi not activated", Bundle.ACTIVE, b.getState());
+            }
+            
+            if ("org.apache.camel.camel-main-osgi".equals(b.getSymbolicName())) {
+                hasCamelMainOsgi = true;
+                assertEquals("Camel Main OSGi not activated", Bundle.ACTIVE, b.getState());
+            }
+        }
+        assertTrue("Camel Core bundle not found", hasCore);
+        assertTrue("Camel Core OSGi bundle not found", hasOsgi);
+        assertTrue("Camel Main OSGi bundle not found", hasCamelMainOsgi);
+    }
+
+    @Test
+    public void testRouteLoadAndRemoved() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        ServiceRegistration<RouteBuilder> testServiceRegistration = bc.registerService(RouteBuilder.class,
+                new RouteBuilder() {
+
+                    @Override
+                    public void configure() throws Exception {
+                        from("timer:test?fixedRate=true&period=300").process(exchange -> {
+                            latch.countDown();
+                        });
+                    }
+                }, null);
+
+        latch.await(10, TimeUnit.SECONDS);
+
+        CamelContext camelContext = bc.getService(bc.getServiceReference(CamelContext.class));
+
+        assertEquals("There should be one route in the context.", 1, camelContext.getRoutes().size());
+
+        testServiceRegistration.unregister();
+
+        assertEquals("There should be no routes in the context.", 0, camelContext.getRoutes().size());
+
+    }
+
+}
diff --git a/core/camel-main-osgi/src/test/java/org/apache/camel/core/osgi/main/it/PaxExamOptions.java b/core/camel-main-osgi/src/test/java/org/apache/camel/core/osgi/main/it/PaxExamOptions.java
new file mode 100644
index 0000000..15284c4
--- /dev/null
+++ b/core/camel-main-osgi/src/test/java/org/apache/camel/core/osgi/main/it/PaxExamOptions.java
@@ -0,0 +1,70 @@
+/*
+ * 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.core.osgi.main.it;
+
+import java.io.File;
+
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.karaf.options.LogLevelOption;
+import org.ops4j.pax.exam.options.DefaultCompositeOption;
+
+import static org.ops4j.pax.exam.CoreOptions.maven;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configureConsole;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.features;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
+
+public enum PaxExamOptions {
+    KARAF(
+            karafDistributionConfiguration()
+                .frameworkUrl(
+                    maven()
+                        .groupId("org.apache.karaf")
+                        .artifactId("apache-karaf")
+                        .versionAsInProject()
+                        .type("zip"))
+                .name("Apache Karaf")
+                .useDeployFolder(false)
+                .unpackDirectory(new File("target/paxexam/unpack/")),
+            keepRuntimeFolder(),
+            // Don't bother with local console output as it just ends up cluttering the logs
+            configureConsole().ignoreLocalConsole(),
+            // Force the log level to INFO so we have more details during the test. It defaults to WARN.
+            logLevel(LogLevelOption.LogLevel.INFO)
+        ),
+        CAMEL_CORE_OSGI(
+            features(
+                maven()
+                    .groupId("org.apache.camel.karaf")
+                    .artifactId("apache-camel")
+                    .type("xml")
+                    .classifier("features")
+                    .versionAsInProject(),
+                    "camel-core", "camel-core-osgi")
+        );
+
+    private final Option[] options;
+
+    PaxExamOptions(Option... options) {
+        this.options = options;
+    }
+
+    public Option option() {
+        return new DefaultCompositeOption(options);
+    }
+}
diff --git a/core/pom.xml b/core/pom.xml
index 25db9fd..334e535 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -50,6 +50,7 @@
         <module>camel-core-xml</module>
         <module>camel-cloud</module>
         <module>camel-main</module>
+        <module>camel-main-osgi</module>
     </modules>
 
     <build>
diff --git a/parent/pom.xml b/parent/pom.xml
index 790967a..f301399 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -810,6 +810,11 @@
                 <artifactId>camel-main</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.camel</groupId>
+                <artifactId>camel-main-osgi</artifactId>
+                <version>${project.version}</version>
+            </dependency>
 
             <!-- NOTE: auto-generated list of components when building camel catalog -->
             <!-- camel components: START -->
diff --git a/platforms/karaf/features/src/main/resources/features.xml b/platforms/karaf/features/src/main/resources/features.xml
index ff2448f..5b7d2ac 100644
--- a/platforms/karaf/features/src/main/resources/features.xml
+++ b/platforms/karaf/features/src/main/resources/features.xml
@@ -61,6 +61,7 @@
     <bundle>mvn:org.apache.camel/camel-cloud/${project.version}</bundle>
     <bundle>mvn:org.apache.camel/camel-jaxp/${project.version}</bundle>
     <bundle>mvn:org.apache.camel/camel-main/${project.version}</bundle>
+    <bundle>mvn:org.apache.camel/camel-main-osgi/${project.version}</bundle>
     <bundle>mvn:org.apache.camel/camel-caffeine-lrucache/${project.version}</bundle>
     <!-- core components -->
     <bundle>mvn:org.apache.camel/camel-bean/${project.version}</bundle>