This is an automated email from the ASF dual-hosted git repository.
jbonofre pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karaf.git
The following commit(s) were added to refs/heads/main by this push:
new 6d76f41c2 fix(#567): Replace init() with build() in
OsgiDefaultCamelContext (#690)
6d76f41c2 is described below
commit 6d76f41c273733466c2726ab2c87cb669231188d
Author: JB Onofré <[email protected]>
AuthorDate: Thu Mar 12 18:11:44 2026 +0100
fix(#567): Replace init() with build() in OsgiDefaultCamelContext (#690)
OsgiDefaultCamelContext was calling init() in its constructor, but
BlueprintCamelContext was updated to use build() in Camel 3.2. This
mismatch caused routes not to be added to the context in some
circumstances. Align OsgiDefaultCamelContext with BlueprintCamelContext
by calling build() instead of init().
Also add unit tests for OsgiDefaultCamelContext covering lifecycle
states, route addition, start/stop, and the build vs init behavior.
---
core/camel-core-osgi/pom.xml | 38 ++++
.../camel/karaf/core/OsgiDefaultCamelContext.java | 2 +-
.../karaf/core/OsgiDefaultCamelContextTest.java | 212 +++++++++++++++++++++
3 files changed, 251 insertions(+), 1 deletion(-)
diff --git a/core/camel-core-osgi/pom.xml b/core/camel-core-osgi/pom.xml
index 955fb61c2..04218721f 100644
--- a/core/camel-core-osgi/pom.xml
+++ b/core/camel-core-osgi/pom.xml
@@ -53,6 +53,44 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>${junit-jupiter-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-engine</artifactId>
+ <version>${junit-jupiter-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>${mockito-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-core</artifactId>
+ <version>${camel-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-direct</artifactId>
+ <version>${camel-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-log</artifactId>
+ <version>${camel-version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/core/camel-core-osgi/src/main/java/org/apache/camel/karaf/core/OsgiDefaultCamelContext.java
b/core/camel-core-osgi/src/main/java/org/apache/camel/karaf/core/OsgiDefaultCamelContext.java
index 91dbad3fa..fbe82fb87 100644
---
a/core/camel-core-osgi/src/main/java/org/apache/camel/karaf/core/OsgiDefaultCamelContext.java
+++
b/core/camel-core-osgi/src/main/java/org/apache/camel/karaf/core/OsgiDefaultCamelContext.java
@@ -45,7 +45,7 @@ public class OsgiDefaultCamelContext extends
AbstractOsgiDefaultCamelContext {
getCamelContextExtension().setName(bundleContext.getBundle().getSymbolicName());
}
- init();
+ build();
}
@Override
diff --git
a/core/camel-core-osgi/src/test/java/org/apache/camel/karaf/core/OsgiDefaultCamelContextTest.java
b/core/camel-core-osgi/src/test/java/org/apache/camel/karaf/core/OsgiDefaultCamelContextTest.java
new file mode 100644
index 000000000..582c50d45
--- /dev/null
+++
b/core/camel-core-osgi/src/test/java/org/apache/camel/karaf/core/OsgiDefaultCamelContextTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.karaf.core;
+
+import org.apache.camel.ServiceStatus;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.engine.DefaultComponentResolver;
+import org.apache.camel.impl.engine.DefaultLanguageResolver;
+import org.apache.camel.spi.ComponentResolver;
+import org.apache.camel.spi.LanguageResolver;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.*;
+
+class OsgiDefaultCamelContextTest {
+
+ private OsgiDefaultCamelContext context;
+
+ private BundleContext createMockBundleContext() {
+ BundleContext bundleContext = mock(BundleContext.class);
+ Bundle bundle = mock(Bundle.class);
+ when(bundleContext.getBundle()).thenReturn(bundle);
+ when(bundle.getSymbolicName()).thenReturn("test-bundle");
+
+ // OsgiFactoryFinder iterates over all bundles looking for META-INF
resources.
+ // Return a bundle whose getEntry delegates to the classloader so that
+ // Camel's default factory-finder resources on the classpath are found.
+ Bundle resourceBundle = mock(Bundle.class);
+ when(resourceBundle.getEntry(anyString())).thenAnswer(invocation -> {
+ String path = invocation.getArgument(0);
+ return
OsgiDefaultCamelContextTest.class.getClassLoader().getResource(path);
+ });
+ try {
+ when(resourceBundle.loadClass(anyString())).thenAnswer(invocation
-> {
+ String className = invocation.getArgument(0);
+ return Class.forName(className);
+ });
+ } catch (ClassNotFoundException e) {
+ // won't happen - this is stubbing
+ }
+
+ when(bundleContext.getBundles()).thenReturn(new
Bundle[]{resourceBundle});
+ return bundleContext;
+ }
+
+ /**
+ * Replace OSGi-based resolvers with classpath-based defaults so the
context
+ * can resolve components and languages without a real OSGi service
registry.
+ */
+ private void useClasspathResolvers(OsgiDefaultCamelContext ctx) {
+
ctx.getCamelContextExtension().addContextPlugin(ComponentResolver.class, new
DefaultComponentResolver());
+
ctx.getCamelContextExtension().addContextPlugin(LanguageResolver.class, new
DefaultLanguageResolver());
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ if (context != null) {
+ if (context.isStarted()) {
+ context.stop();
+ }
+ context = null;
+ }
+ }
+
+ @Test
+ void contextShouldBeInBuiltStateAfterConstruction() {
+ BundleContext bundleContext = createMockBundleContext();
+ context = new OsgiDefaultCamelContext(bundleContext);
+
+ assertTrue(context.isBuild(), "Context should be in Built state after
construction");
+ assertFalse(context.isNew(), "Context should no longer be in New state
after construction");
+ }
+
+ @Test
+ void contextShouldNotBeInInitializedStateAfterConstruction() {
+ BundleContext bundleContext = createMockBundleContext();
+ context = new OsgiDefaultCamelContext(bundleContext);
+
+ // build() transitions: NEW -> BUILT
+ // init() transitions: NEW -> INITIALIZED (skipping BUILT)
+ // After build(), isInit() should be false — the context has been
built but not yet initialized.
+ // This is the key behavioral difference: build() is the correct
lifecycle step here,
+ // allowing the caller to add routes before init/start fully wires the
context.
+ assertFalse(context.isInit(), "Context should not be initialized yet,
only built");
+ }
+
+ @Test
+ void contextShouldStartSuccessfully() throws Exception {
+ BundleContext bundleContext = createMockBundleContext();
+ context = new OsgiDefaultCamelContext(bundleContext);
+ useClasspathResolvers(context);
+
+ context.start();
+
+ assertTrue(context.isStarted(), "Context should be started");
+ assertEquals(ServiceStatus.Started, context.getStatus());
+ }
+
+ @Test
+ void contextShouldAcceptRoutesAfterConstruction() throws Exception {
+ BundleContext bundleContext = createMockBundleContext();
+ context = new OsgiDefaultCamelContext(bundleContext);
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("log:test")
+ .routeId("test-route");
+ }
+ });
+
+ assertFalse(context.getRouteDefinitions().isEmpty(), "Route
definitions should be added");
+ assertEquals("test-route",
context.getRouteDefinitions().get(0).getRouteId());
+ }
+
+ @Test
+ void routesShouldBeStartedWhenContextStarts() throws Exception {
+ BundleContext bundleContext = createMockBundleContext();
+ context = new OsgiDefaultCamelContext(bundleContext);
+ useClasspathResolvers(context);
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("log:test")
+ .routeId("test-route");
+ }
+ });
+
+ context.start();
+
+ assertNotNull(context.getRoute("test-route"), "Route should exist
after start");
+ assertEquals(1, context.getRoutes().size(), "There should be one
running route");
+ }
+
+ @Test
+ void multipleRoutesShouldBeAddedAndStarted() throws Exception {
+ BundleContext bundleContext = createMockBundleContext();
+ context = new OsgiDefaultCamelContext(bundleContext);
+ useClasspathResolvers(context);
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("log:test")
+ .routeId("route-1");
+
+ from("direct:other")
+ .to("log:other")
+ .routeId("route-2");
+ }
+ });
+
+ context.start();
+
+ assertEquals(2, context.getRoutes().size(), "Both routes should be
running");
+ assertNotNull(context.getRoute("route-1"));
+ assertNotNull(context.getRoute("route-2"));
+ }
+
+ @Test
+ void bundleContextShouldBeAccessible() {
+ BundleContext bundleContext = createMockBundleContext();
+ context = new OsgiDefaultCamelContext(bundleContext);
+
+ assertSame(bundleContext, context.getBundleContext());
+ }
+
+ @Test
+ void contextShouldStopCleanly() throws Exception {
+ BundleContext bundleContext = createMockBundleContext();
+ context = new OsgiDefaultCamelContext(bundleContext);
+ useClasspathResolvers(context);
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("log:test")
+ .routeId("test-route");
+ }
+ });
+
+ context.start();
+ assertTrue(context.isStarted());
+
+ context.stop();
+ assertTrue(context.isStopped(), "Context should be stopped");
+ }
+}