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 2020/03/24 15:42:01 UTC
[camel-karaf] 01/02: Added camel-osgi-activator component
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-karaf.git
commit b4e74c1b122d75d9555772bd6b2ef78be2187ff0
Author: Andrea Cosentino <an...@gmail.com>
AuthorDate: Tue Mar 24 16:25:17 2020 +0100
Added camel-osgi-activator component
---
components/camel-osgi-activator/pom.xml | 195 ++++++++++++++++++++
.../src/assembly/test-bundles.xml | 48 +++++
.../services/org/apache/camel/other.properties | 7 +
.../src/generated/resources/osgi-activator.json | 13 ++
.../src/main/docs/osgi-activator.adoc | 73 ++++++++
.../camel/osgi/activator/CamelRoutesActivator.java | 181 +++++++++++++++++++
.../activator/CamelRoutesActivatorConstants.java | 26 +++
.../osgi/activator/CamelOsgiActivatorIT.java | 198 +++++++++++++++++++++
.../component/osgi/activator/PaxExamOptions.java | 103 +++++++++++
components/pom.xml | 1 +
10 files changed, 845 insertions(+)
diff --git a/components/camel-osgi-activator/pom.xml b/components/camel-osgi-activator/pom.xml
new file mode 100644
index 0000000..393205a
--- /dev/null
+++ b/components/camel-osgi-activator/pom.xml
@@ -0,0 +1,195 @@
+<?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.karaf</groupId>
+ <artifactId>components</artifactId>
+ <version>3.2.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-osgi-activator</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Camel :: OSGi Activator</name>
+ <description>Camel OSGi Activator for running Camel routes from other bundles</description>
+
+ <properties>
+ <camel.osgi.activator>org.apache.camel.osgi.activator.CamelRoutesActivator</camel.osgi.activator>
+ <camel.osgi.dynamic>*</camel.osgi.dynamic>
+ <firstVersion>3.1.0</firstVersion>
+ </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>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-core-languages</artifactId>
+ </dependency>
+ <!-- test -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-timer</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-log</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- 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/components/camel-osgi-activator/src/assembly/test-bundles.xml b/components/camel-osgi-activator/src/assembly/test-bundles.xml
new file mode 100644
index 0000000..a7798e9
--- /dev/null
+++ b/components/camel-osgi-activator/src/assembly/test-bundles.xml
@@ -0,0 +1,48 @@
+<?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-api</include>
+ <include>org.apache.camel:camel-base</include>
+ <include>org.apache.camel:camel-core-engine</include>
+ <include>org.apache.camel:camel-core-osgi</include>
+ <include>org.apache.camel:camel-core-languages</include>
+ <include>org.apache.camel:camel-management-api</include>
+ <include>org.apache.camel:camel-osgi-activator</include>
+ <include>org.apache.camel:camel-support</include>
+ <include>org.apache.camel:camel-util</include>
+ <include>org.apache.camel:camel-timer</include>
+ <include>org.apache.camel:camel-log</include>
+ </includes>
+ <scope>test</scope>
+ </dependencySet>
+ </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/components/camel-osgi-activator/src/generated/resources/META-INF/services/org/apache/camel/other.properties b/components/camel-osgi-activator/src/generated/resources/META-INF/services/org/apache/camel/other.properties
new file mode 100644
index 0000000..989d3e7
--- /dev/null
+++ b/components/camel-osgi-activator/src/generated/resources/META-INF/services/org/apache/camel/other.properties
@@ -0,0 +1,7 @@
+# Generated by camel build tools - do NOT edit this file!
+name=osgi-activator
+groupId=org.apache.camel
+artifactId=camel-osgi-activator
+version=3.2.0-SNAPSHOT
+projectName=Camel :: OSGi Activator
+projectDescription=Camel OSGi Activator for running Camel routes from other bundles
diff --git a/components/camel-osgi-activator/src/generated/resources/osgi-activator.json b/components/camel-osgi-activator/src/generated/resources/osgi-activator.json
new file mode 100644
index 0000000..26df4ea
--- /dev/null
+++ b/components/camel-osgi-activator/src/generated/resources/osgi-activator.json
@@ -0,0 +1,13 @@
+{
+ "other": {
+ "kind": "other",
+ "name": "osgi-activator",
+ "title": "Osgi Activator",
+ "description": "Camel OSGi Activator for running Camel routes from other bundles",
+ "deprecated": false,
+ "firstVersion": "3.1.0",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-osgi-activator",
+ "version": "3.2.0-SNAPSHOT"
+ }
+}
diff --git a/components/camel-osgi-activator/src/main/docs/osgi-activator.adoc b/components/camel-osgi-activator/src/main/docs/osgi-activator.adoc
new file mode 100644
index 0000000..407e4b6
--- /dev/null
+++ b/components/camel-osgi-activator/src/main/docs/osgi-activator.adoc
@@ -0,0 +1,73 @@
+[[OsgiActivator]]
+= OSGi Camel Routes Activator
+
+*Since Camel 3.1*
+
+A small OSGi activator for starting an OSGi Apache Camel Project.
+
+The bundle starts a shared CamelContext and registers any RouteBuilder instances
+(discovered via the OSGi Service Registry), from any other bundles that gets installed.
+And when the bundles gets uninstalled then the routes are stopped and removed from the shared CamelContext.
+
+== Important
+
+This OSGi activator is a based prototype for quickly starting up an OSGi container with a single shared
+CamelContext and then being able to use OSGi dynamism to deploy and undeploy bundlers with Camel routes.
+
+Beware that this OSGi activator is a basic implementation and has limited support for dealing with errors
+when new routes are added which may clash with existing routes (route ids).
+
+Configuration of the CamelContext is also very limited.
+
+Therefore only use this for prototyping and experiments. This is NOT recommended for production usage.
+
+== Install bundle
+
+Register an Apache Camel RouteBuilder as an OSGi service.
+
+Using OSGi annotations:
+
+[source,java]
+----
+@Component(service = RouteBuilder.class)
+public class MyRouteBuilder extends RouteBuilder {
+ @Override
+ public void configure() throws Exception {
+ from("timer:test?fixedRate=true&period=1000")
+ .log("Hello");
+ }
+}
+----
+
+Or Manually:
+
+[source,java]
+----
+public void start(BundleContext context) throws Exception {
+ context.registerService(RouteBuilder.class, new MyRouteBuilder(), null);
+}
+----
+
+And it's automatically added or removed to the context from any bundle!
+
+[source,text]
+----
+Route: route1 started and consuming from: timer://test?fixedRate=true&period=1000
+----
+
+For routes that need to be started before the CamelContext the "camel.osgi.activator.pre-startup" service property may be added.
+
+[source,java]
+----
+@Component(service = RouteBuilder.class, property = {CamelRoutesActivatorConstants.PRE_START_UP_PROP_NAME + "=true"})
+public class MyStartupRouteBuilder extends RouteBuilder {
+ @Override
+ public void configure() throws Exception {
+ getContext().setStreamCaching(true);
+
+ restConfiguration().component("netty-http").port(8080);
+ }
+}
+----
+
+If this RouteBuilder is added after other non pre startup RouteBuilders then CamelContext will automatically restart. This allows pre start up RouteBuilder to run their configure methods before other RouteBuilders.
diff --git a/components/camel-osgi-activator/src/main/java/org/apache/camel/osgi/activator/CamelRoutesActivator.java b/components/camel-osgi-activator/src/main/java/org/apache/camel/osgi/activator/CamelRoutesActivator.java
new file mode 100644
index 0000000..2b2160a
--- /dev/null
+++ b/components/camel-osgi-activator/src/main/java/org/apache/camel/osgi/activator/CamelRoutesActivator.java
@@ -0,0 +1,181 @@
+/*
+ * 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.osgi.activator;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+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 CamelRoutesActivator implements BundleActivator, ServiceTrackerCustomizer<RouteBuilder, RouteBuilder> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CamelRoutesActivator.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(this.bundleContext);
+
+ camelContextRef = this.bundleContext.registerService(CamelContext.class, camelContext, null);
+
+ camelContext.start();
+
+ this.routeServiceTracker = new ServiceTracker<RouteBuilder, RouteBuilder>(context, RouteBuilder.class, this);
+
+ this.routeServiceTracker.open();
+
+ LOG.info("Camel OSGi Activator RouteBuilder ServiceTracker Tracker Open");
+ }
+
+ @Override
+ public RouteBuilder addingService(ServiceReference<RouteBuilder> reference) {
+ RouteBuilder builder = this.bundleContext.getService(reference);
+ if (isPreStartRouteBuilder(reference)) {
+ reloadTrackedServices(reference);
+ } else {
+ addRoute(builder);
+ }
+ return builder;
+ }
+
+ @Override
+ public void stop(BundleContext context) throws Exception {
+ this.routeServiceTracker.close();
+ stopAndClearCamelRoutes();
+ this.bundleContext.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) {
+ if (isPreStartRouteBuilder(reference)) {
+ reloadTrackedServices();
+ } else {
+ removeRoute(service);
+ }
+ }
+
+ private boolean isPreStartRouteBuilder(ServiceReference<RouteBuilder> reference) {
+
+ boolean result = false;
+
+ Object preStartProperty = reference.getProperty(CamelRoutesActivatorConstants.PRE_START_UP_PROP_NAME);
+
+ if (preStartProperty instanceof Boolean) {
+ result = (Boolean)preStartProperty;
+ } else if (preStartProperty instanceof String) {
+ result = Boolean.parseBoolean((String) preStartProperty);
+ }
+
+ return result;
+ }
+
+ private void loadAndRestartCamelContext(List<ServiceReference<RouteBuilder>> existingRouteBuildersReferences) {
+ if (existingRouteBuildersReferences != null) {
+ List<RouteBuilder> postStartUpRoutes = new ArrayList<>();
+ for (ServiceReference<RouteBuilder> currentRouteBuilderReference : existingRouteBuildersReferences) {
+ RouteBuilder builder = this.bundleContext.getService(currentRouteBuilderReference);
+ if (isPreStartRouteBuilder(currentRouteBuilderReference)) {
+ addRoute(builder);
+ } else {
+ postStartUpRoutes.add(builder);
+ }
+ }
+ camelContext.start();
+ postStartUpRoutes.forEach(this::addRoute);
+ }
+ }
+
+ private void reloadTrackedServices(ServiceReference<RouteBuilder> reference) {
+ LOG.info("Reload Camel Context Routes Triggered");
+ try {
+ synchronized (camelContext) {
+ stopAndClearCamelRoutes();
+ List<ServiceReference<RouteBuilder>> routeServiceReferenceArrayList = new ArrayList<>();
+ if (reference != null) {
+ routeServiceReferenceArrayList.add(reference);
+ }
+ ServiceReference<RouteBuilder>[] existingTrackedRoutes = this.routeServiceTracker.getServiceReferences();
+ if (existingTrackedRoutes != null) {
+ routeServiceReferenceArrayList.addAll(Arrays.asList(existingTrackedRoutes));
+ }
+ loadAndRestartCamelContext(routeServiceReferenceArrayList);
+ }
+ } catch (Exception e) {
+ LOG.error("Error Reloading Camel Context Routes", e);
+ }
+ }
+
+ private void reloadTrackedServices() {
+ reloadTrackedServices(null);
+ }
+
+ private void addRoute(RouteBuilder builder) {
+ 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 OSGi Activator Context", builder.getClass().getName());
+ }
+ } catch (Exception e) {
+ LOG.error("Error Adding Camel RouteBuilder", e);
+ }
+ }
+
+ private void stopAndClearCamelRoutes() throws Exception {
+ camelContext.stop();
+ camelContext.removeRouteDefinitions(new ArrayList<RouteDefinition>(this.camelContext.getRouteDefinitions()));
+ }
+
+ private void removeRoute(RouteBuilder service) {
+ List<RouteDefinition> routesToBeRemoved = service.getRouteCollection().getRoutes();
+ try {
+ synchronized (camelContext) {
+ camelContext.removeRouteDefinitions(routesToBeRemoved);
+ LOG.debug("Camel Routes from RouteBuilder Class {} Removed from Camel OSGi Activator Context",
+ service.getClass().getName());
+ }
+ } catch (Exception e) {
+ LOG.error("Error Removing Camel Route Builder", e);
+ }
+ }
+
+}
diff --git a/components/camel-osgi-activator/src/main/java/org/apache/camel/osgi/activator/CamelRoutesActivatorConstants.java b/components/camel-osgi-activator/src/main/java/org/apache/camel/osgi/activator/CamelRoutesActivatorConstants.java
new file mode 100644
index 0000000..dc01581
--- /dev/null
+++ b/components/camel-osgi-activator/src/main/java/org/apache/camel/osgi/activator/CamelRoutesActivatorConstants.java
@@ -0,0 +1,26 @@
+/*
+ * 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.osgi.activator;
+
+public final class CamelRoutesActivatorConstants {
+
+ public static final String PRE_START_UP_PROP_NAME = "camel.osgi.activator.pre-startup";
+
+ private CamelRoutesActivatorConstants() {
+
+ }
+}
diff --git a/components/camel-osgi-activator/src/test/java/org/apache/camel/component/osgi/activator/CamelOsgiActivatorIT.java b/components/camel-osgi-activator/src/test/java/org/apache/camel/component/osgi/activator/CamelOsgiActivatorIT.java
new file mode 100644
index 0000000..d08df32
--- /dev/null
+++ b/components/camel-osgi-activator/src/test/java/org/apache/camel/component/osgi/activator/CamelOsgiActivatorIT.java
@@ -0,0 +1,198 @@
+/*
+ * 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.component.osgi.activator;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Date;
+import java.util.Dictionary;
+import java.util.Hashtable;
+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.apache.camel.osgi.activator.CamelRoutesActivatorConstants;
+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.options;
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class CamelOsgiActivatorIT {
+ @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-osgi-activator.jar")))
+ .build()),
+ junitBundles());
+ }
+
+ @Test
+ public void testBundleLoaded() throws Exception {
+ boolean hasOsgi = false;
+ boolean hasCamelCoreOsgiActivator = false;
+ for (Bundle b : bc.getBundles()) {
+ 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-osgi-activator".equals(b.getSymbolicName())) {
+ hasCamelCoreOsgiActivator = true;
+ assertEquals("Camel OSGi Activator not activated", Bundle.ACTIVE, b.getState());
+ }
+ }
+ assertTrue("Camel Core OSGi bundle not found", hasOsgi);
+ assertTrue("Camel OSGi Activator bundle not found", hasCamelCoreOsgiActivator);
+ }
+
+ @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());
+
+ }
+
+ @Test
+ public void testPreStartupLoadAndRemoved() throws Exception {
+ CountDownLatch preStartLatch = new CountDownLatch(1);
+
+ CountDownLatch postStartLatch = new CountDownLatch(1);
+
+ CamelContext camelContext = bc.getService(bc.getServiceReference(CamelContext.class));
+
+ Date originalCamelStartTime = camelContext.getStartDate();
+
+ ServiceRegistration<RouteBuilder> testRegularServiceRegistration = bc.registerService(RouteBuilder.class,
+ new RouteBuilder() {
+
+ @Override
+ public void configure() throws Exception {
+ from("timer:test1?fixedRate=true&period=300")
+ .description("PostStartRoute")
+ .process(exchange -> {
+ postStartLatch.countDown();
+ });
+ }
+ }, null);
+
+ postStartLatch.await(10, TimeUnit.SECONDS);
+
+ Date regularRouteAddCamelContextStartTime = camelContext.getStartDate();
+
+ assertEquals("Camel Context Should NOT be restarted when removing regular RouteBuilder", originalCamelStartTime, regularRouteAddCamelContextStartTime);
+
+ assertEquals("There should be one route in the context.", 1, camelContext.getRoutes().size());
+
+ assertEquals("The PostStartRoute should be first.", "PostStartRoute", camelContext.getRoutes().get(0).getDescription());
+
+
+ Dictionary<String, String> preStartUpProperties = new Hashtable<>();
+ preStartUpProperties.put(CamelRoutesActivatorConstants.PRE_START_UP_PROP_NAME, "true");
+ ServiceRegistration<RouteBuilder> testPreStartupServiceRegistration = bc.registerService(RouteBuilder.class,
+ new RouteBuilder() {
+
+ @Override
+ public void configure() throws Exception {
+ getContext().setStreamCaching(true);
+
+ from("timer:test2?fixedRate=true&period=300")
+ .description("PreStartRoute")
+ .process(exchange -> {
+ preStartLatch.countDown();
+ });
+
+ }
+ }, preStartUpProperties);
+
+ preStartLatch.await(10, TimeUnit.SECONDS);
+
+ Date preStartCamelContextStartTime = camelContext.getStartDate();
+
+ assertTrue("Camel Context Should be restarted when adding startup RouteBuilder", preStartCamelContextStartTime.after(originalCamelStartTime));
+
+ assertEquals("There should be two route in the context.", 2, camelContext.getRoutes().size());
+
+ assertEquals("The PreStartRoute should be first.", "PreStartRoute", camelContext.getRoutes().get(0).getDescription());
+
+ testPreStartupServiceRegistration.unregister();
+
+ Date preStartRemovedCamelContextStartTime = camelContext.getStartDate();
+
+ assertEquals("There should be one routes in the context.", 1, camelContext.getRoutes().size());
+
+ assertTrue("Camel Context Should be restarted when removing startup RouteBuilder", preStartRemovedCamelContextStartTime.after(preStartCamelContextStartTime));
+
+ testRegularServiceRegistration.unregister();
+
+ Date regularRouteRemovedCamelContextStartTime = camelContext.getStartDate();
+
+ assertEquals("Camel Context Should NOT be restarted when removing regular RouteBuilder", preStartRemovedCamelContextStartTime, regularRouteRemovedCamelContextStartTime);
+
+ assertEquals("There should be no routes in the context.", 0, camelContext.getRoutes().size());
+
+ }
+
+}
diff --git a/components/camel-osgi-activator/src/test/java/org/apache/camel/component/osgi/activator/PaxExamOptions.java b/components/camel-osgi-activator/src/test/java/org/apache/camel/component/osgi/activator/PaxExamOptions.java
new file mode 100644
index 0000000..38f3a8e
--- /dev/null
+++ b/components/camel-osgi-activator/src/test/java/org/apache/camel/component/osgi/activator/PaxExamOptions.java
@@ -0,0 +1,103 @@
+/*
+ * 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.component.osgi.activator;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.karaf.container.internal.JavaVersionUtil;
+import org.ops4j.pax.exam.karaf.options.LogLevelOption;
+import org.ops4j.pax.exam.options.DefaultCompositeOption;
+import org.ops4j.pax.exam.options.extra.VMOption;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+
+import static org.ops4j.pax.exam.CoreOptions.maven;
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configureConsole;
+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),
+ when(JavaVersionUtil.getMajorVersion() >= 9)
+ .useOptions(
+ new VMOption("-classpath"),
+ new VMOption("lib/jdk9plus/*" + File.pathSeparator + "lib/boot/*")
+ )
+ ),
+ CAMEL_CORE_OSGI(
+ createStreamBundleOption("camel-core-engine.jar"),
+ createStreamBundleOption("camel-core-languages.jar"),
+ createStreamBundleOption("camel-api.jar"),
+ createStreamBundleOption("camel-base.jar"),
+ createStreamBundleOption("camel-management-api.jar"),
+ createStreamBundleOption("camel-support.jar"),
+ createStreamBundleOption("camel-util.jar"),
+ createStreamBundleOption("camel-timer.jar"),
+ createStreamBundleOption("camel-log.jar"),
+ createStreamBundleOption("camel-core-osgi.jar")
+ );
+
+ private final Option[] options;
+
+ PaxExamOptions(Option... options) {
+ this.options = options;
+ }
+
+ public Option option() {
+ return new DefaultCompositeOption(options);
+ }
+
+ public static Option createStreamBundleOption(String fileName) {
+ InputStream bundleInputStream = null;
+ try {
+ bundleInputStream = Files.newInputStream(
+ Paths.get("target/test-bundles")
+ .resolve(fileName));
+
+ } catch (IOException e) {
+ throw new RuntimeException("Error resolving Bundle", e);
+ }
+
+ return streamBundle(
+ TinyBundles.bundle()
+ .read(bundleInputStream)
+ .build());
+ }
+}
diff --git a/components/pom.xml b/components/pom.xml
index 3ccab0a..901ad02 100644
--- a/components/pom.xml
+++ b/components/pom.xml
@@ -37,6 +37,7 @@
<module>camel-cxf-blueprint</module>
<module>camel-cxf-transport-blueprint</module>
<module>camel-kura</module>
+ <module>camel-osgi-activator</module>
<module>camel-paxlogging</module>
</modules>