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

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

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>