You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by pp...@apache.org on 2020/11/27 11:21:03 UTC

[camel-quarkus] 01/03: OptaPlanner native support fixes #1721

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

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

commit ee56a0606144cc5989186931dca7dab2a1e856d5
Author: Zineb Bendhiba <be...@gmail.com>
AuthorDate: Fri Sep 11 15:56:35 2020 +0200

    OptaPlanner native support fixes #1721
---
 .../pages/reference/extensions/optaplanner.adoc    |   8 +-
 .../partials/reference/components/optaplanner.adoc |   6 +-
 extensions-jvm/pom.xml                             |   1 -
 .../optaplanner/deployment/pom.xml                 |  16 +++
 .../deployment/OptaplannerProcessor.java           |  14 ---
 {extensions-jvm => extensions}/optaplanner/pom.xml |   1 -
 .../optaplanner/runtime/pom.xml                    |  30 ++++++
 .../main/resources/META-INF/quarkus-extension.yaml |   3 +-
 extensions/pom.xml                                 |   1 +
 .../optaplanner}/pom.xml                           |  74 ++++++++++++-
 .../quarkus/component/optaplanner/it/MyBean.java   |  48 +++++++++
 .../optaplanner/it/OptaplannerResource.java        |  89 ++++++++++++++++
 .../quarkus/component/optaplanner/it/Routes.java   |  39 +++----
 .../optaplanner/it/bootstrap/DataGenerator.java    |  95 +++++++++++++++++
 .../component/optaplanner/it/domain/Lesson.java    | 100 +++++++++++++++++
 .../component/optaplanner/it/domain/Room.java      |  61 +++++++++++
 .../component/optaplanner/it/domain/TimeTable.java |  72 +++++++++++++
 .../component/optaplanner/it/domain/Timeslot.java  |  77 ++++++++++++++
 .../it/solver/TimeTableConstraintProvider.java     | 118 +++++++++++++++++++++
 .../src/main/resources/application.properties      |  32 ++++++
 .../component/optaplanner/it/OptaplannerIT.java    |  16 +--
 .../component/optaplanner/it/OptaplannerTest.java  |  31 +++++-
 integration-tests/pom.xml                          |   1 +
 pom.xml                                            |   2 +
 poms/bom/pom.xml                                   |  10 ++
 tooling/scripts/test-categories.yaml               |   1 +
 26 files changed, 877 insertions(+), 69 deletions(-)

diff --git a/docs/modules/ROOT/pages/reference/extensions/optaplanner.adoc b/docs/modules/ROOT/pages/reference/extensions/optaplanner.adoc
index bc9def7..d4fa4d3 100644
--- a/docs/modules/ROOT/pages/reference/extensions/optaplanner.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/optaplanner.adoc
@@ -2,15 +2,15 @@
 // This file was generated by camel-quarkus-maven-plugin:update-extension-doc-page
 = OptaPlanner
 :cq-artifact-id: camel-quarkus-optaplanner
-:cq-native-supported: false
-:cq-status: Preview
+:cq-native-supported: true
+:cq-status: Stable
 :cq-description: Solve planning problems with OptaPlanner.
 :cq-deprecated: false
 :cq-jvm-since: 1.1.0
-:cq-native-since: n/a
+:cq-native-since: 1.2.0
 
 [.badges]
-[.badge-key]##JVM since##[.badge-supported]##1.1.0## [.badge-key]##Native##[.badge-unsupported]##unsupported##
+[.badge-key]##JVM since##[.badge-supported]##1.1.0## [.badge-key]##Native since##[.badge-supported]##1.2.0##
 
 Solve planning problems with OptaPlanner.
 
diff --git a/docs/modules/ROOT/partials/reference/components/optaplanner.adoc b/docs/modules/ROOT/partials/reference/components/optaplanner.adoc
index e5a1a6b..4e31fd7 100644
--- a/docs/modules/ROOT/partials/reference/components/optaplanner.adoc
+++ b/docs/modules/ROOT/partials/reference/components/optaplanner.adoc
@@ -2,11 +2,11 @@
 // This file was generated by camel-quarkus-maven-plugin:update-extension-doc-page
 :cq-artifact-id: camel-quarkus-optaplanner
 :cq-artifact-id-base: optaplanner
-:cq-native-supported: false
-:cq-status: Preview
+:cq-native-supported: true
+:cq-status: Stable
 :cq-deprecated: false
 :cq-jvm-since: 1.1.0
-:cq-native-since: n/a
+:cq-native-since: 1.2.0
 :cq-camel-part-name: optaplanner
 :cq-camel-part-title: OptaPlanner
 :cq-camel-part-description: Solve planning problems with OptaPlanner.
diff --git a/extensions-jvm/pom.xml b/extensions-jvm/pom.xml
index d13fcc5..dbb2244 100644
--- a/extensions-jvm/pom.xml
+++ b/extensions-jvm/pom.xml
@@ -108,7 +108,6 @@
         <module>nitrite</module>
         <module>ognl</module>
         <module>openstack</module>
-        <module>optaplanner</module>
         <module>printer</module>
         <module>pubnub</module>
         <module>pulsar</module>
diff --git a/extensions-jvm/optaplanner/deployment/pom.xml b/extensions/optaplanner/deployment/pom.xml
similarity index 81%
rename from extensions-jvm/optaplanner/deployment/pom.xml
rename to extensions/optaplanner/deployment/pom.xml
index 1b047be..d2bed0b 100644
--- a/extensions-jvm/optaplanner/deployment/pom.xml
+++ b/extensions/optaplanner/deployment/pom.xml
@@ -29,6 +29,18 @@
     <artifactId>camel-quarkus-optaplanner-deployment</artifactId>
     <name>Camel Quarkus :: OptaPlanner :: Deployment</name>
 
+    <dependencyManagement>
+        <dependencies>
+          <dependency>
+                <groupId>org.optaplanner</groupId>
+                <artifactId>optaplanner-quarkus-deployment</artifactId>
+                <version>${optaplanner.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
     <dependencies>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
@@ -38,6 +50,10 @@
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-optaplanner</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.optaplanner</groupId>
+            <artifactId>optaplanner-quarkus-deployment</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/extensions-jvm/optaplanner/deployment/src/main/java/org/apache/camel/quarkus/component/optaplanner/deployment/OptaplannerProcessor.java b/extensions/optaplanner/deployment/src/main/java/org/apache/camel/quarkus/component/optaplanner/deployment/OptaplannerProcessor.java
similarity index 68%
rename from extensions-jvm/optaplanner/deployment/src/main/java/org/apache/camel/quarkus/component/optaplanner/deployment/OptaplannerProcessor.java
rename to extensions/optaplanner/deployment/src/main/java/org/apache/camel/quarkus/component/optaplanner/deployment/OptaplannerProcessor.java
index d07ab81..3956d43 100644
--- a/extensions-jvm/optaplanner/deployment/src/main/java/org/apache/camel/quarkus/component/optaplanner/deployment/OptaplannerProcessor.java
+++ b/extensions/optaplanner/deployment/src/main/java/org/apache/camel/quarkus/component/optaplanner/deployment/OptaplannerProcessor.java
@@ -17,11 +17,7 @@
 package org.apache.camel.quarkus.component.optaplanner.deployment;
 
 import io.quarkus.deployment.annotations.BuildStep;
-import io.quarkus.deployment.annotations.ExecutionTime;
-import io.quarkus.deployment.annotations.Record;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
-import io.quarkus.deployment.pkg.steps.NativeBuild;
-import org.apache.camel.quarkus.core.JvmOnlyRecorder;
 import org.jboss.logging.Logger;
 
 class OptaplannerProcessor {
@@ -33,14 +29,4 @@ class OptaplannerProcessor {
     FeatureBuildItem feature() {
         return new FeatureBuildItem(FEATURE);
     }
-
-    /**
-     * Remove this once this extension starts supporting the native mode.
-     */
-    @BuildStep(onlyIf = NativeBuild.class)
-    @Record(value = ExecutionTime.RUNTIME_INIT)
-    void warnJvmInNative(JvmOnlyRecorder recorder) {
-        JvmOnlyRecorder.warnJvmInNative(LOG, FEATURE); // warn at build time
-        recorder.warnJvmInNative(FEATURE); // warn at runtime
-    }
 }
diff --git a/extensions-jvm/optaplanner/pom.xml b/extensions/optaplanner/pom.xml
similarity index 97%
rename from extensions-jvm/optaplanner/pom.xml
rename to extensions/optaplanner/pom.xml
index fa0bff5..0e1dede 100644
--- a/extensions-jvm/optaplanner/pom.xml
+++ b/extensions/optaplanner/pom.xml
@@ -33,6 +33,5 @@
     <modules>
         <module>deployment</module>
         <module>runtime</module>
-        <module>integration-test</module>
     </modules>
 </project>
diff --git a/extensions-jvm/optaplanner/runtime/pom.xml b/extensions/optaplanner/runtime/pom.xml
similarity index 77%
rename from extensions-jvm/optaplanner/runtime/pom.xml
rename to extensions/optaplanner/runtime/pom.xml
index ac8340b..5c5fdda 100644
--- a/extensions-jvm/optaplanner/runtime/pom.xml
+++ b/extensions/optaplanner/runtime/pom.xml
@@ -32,6 +32,7 @@
 
     <properties>
         <camel.quarkus.jvmSince>1.1.0</camel.quarkus.jvmSince>
+        <camel.quarkus.nativeSince>1.2.0</camel.quarkus.nativeSince>
     </properties>
 
     <dependencyManagement>
@@ -43,6 +44,20 @@
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
+            <dependency>
+                <groupId>org.optaplanner</groupId>
+                <artifactId>optaplanner-quarkus</artifactId>
+                <version>${optaplanner.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.mvel</groupId>
+                <artifactId>mvel2</artifactId>
+                <version>${mvel2.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
@@ -55,6 +70,21 @@
             <groupId>org.apache.camel</groupId>
             <artifactId>camel-optaplanner</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.optaplanner</groupId>
+            <artifactId>optaplanner-quarkus</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.mvel</groupId>
+                    <artifactId>mvel2</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- delete mvel2 dependency when optaplanner 7.45.0.Final is released-->
+        <dependency>
+            <groupId>org.mvel</groupId>
+            <artifactId>mvel2</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/extensions-jvm/optaplanner/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions/optaplanner/runtime/src/main/resources/META-INF/quarkus-extension.yaml
similarity index 97%
rename from extensions-jvm/optaplanner/runtime/src/main/resources/META-INF/quarkus-extension.yaml
rename to extensions/optaplanner/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 343577a..8951b1f 100644
--- a/extensions-jvm/optaplanner/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/optaplanner/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -24,9 +24,8 @@
 name: "Camel OptaPlanner"
 description: "Solve planning problems with OptaPlanner"
 metadata:
-  unlisted: true
   guide: "https://camel.apache.org/camel-quarkus/latest/reference/extensions/optaplanner.html"
   categories:
   - "integration"
   status:
-  - "preview"
+  - "stable"
diff --git a/extensions/pom.xml b/extensions/pom.xml
index 689c85e..7d7172f 100644
--- a/extensions/pom.xml
+++ b/extensions/pom.xml
@@ -164,6 +164,7 @@
         <module>olingo4</module>
         <module>openapi-java</module>
         <module>opentracing</module>
+        <module>optaplanner</module>
         <module>paho</module>
         <module>pdf</module>
         <module>pg-replication-slot</module>
diff --git a/extensions-jvm/optaplanner/integration-test/pom.xml b/integration-tests/optaplanner/pom.xml
similarity index 54%
rename from extensions-jvm/optaplanner/integration-test/pom.xml
rename to integration-tests/optaplanner/pom.xml
index 4b9af27..8b510b3 100644
--- a/extensions-jvm/optaplanner/integration-test/pom.xml
+++ b/integration-tests/optaplanner/pom.xml
@@ -21,13 +21,12 @@
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.apache.camel.quarkus</groupId>
-        <artifactId>camel-quarkus-build-parent-it</artifactId>
+        <artifactId>camel-quarkus-integration-tests</artifactId>
         <version>1.5.0-SNAPSHOT</version>
-        <relativePath>../../../poms/build-parent-it/pom.xml</relativePath>
     </parent>
 
-    <artifactId>camel-quarkus-optaplanner-integration-test</artifactId>
-    <name>Camel Quarkus :: OptaPlanner :: Integration Test</name>
+    <artifactId>camel-quarkus-integration-test-optaplanner</artifactId>
+    <name>Camel Quarkus :: Integration Tests :: OptaPlanner</name>
     <description>Integration tests for Camel Quarkus OptaPlanner extension</description>
 
     <dependencyManagement>
@@ -51,6 +50,18 @@
             <groupId>io.quarkus</groupId>
             <artifactId>quarkus-resteasy</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-resteasy-jackson</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-direct</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-bean</artifactId>
+        </dependency>
 
         <!-- test dependencies -->
         <dependency>
@@ -67,6 +78,32 @@
         <!-- The following dependencies guarantee that this module is built after them. You can update them by running `mvn process-resources -Pformat -N` from the source tree root directory -->
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-bean-deployment</artifactId>
+            <version>${project.version}</version>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>*</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-direct-deployment</artifactId>
+            <version>${project.version}</version>
+            <type>pom</type>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>*</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-optaplanner-deployment</artifactId>
             <version>${project.version}</version>
             <type>pom</type>
@@ -80,4 +117,33 @@
         </dependency>
     </dependencies>
 
+    <profiles>
+        <profile>
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <properties>
+                <quarkus.package.type>native</quarkus.package.type>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-failsafe-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>integration-test</goal>
+                                    <goal>verify</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
 </project>
diff --git a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/MyBean.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/MyBean.java
new file mode 100644
index 0000000..185cda0
--- /dev/null
+++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/MyBean.java
@@ -0,0 +1,48 @@
+/*
+ * 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.quarkus.component.optaplanner.it;
+
+import javax.enterprise.context.ApplicationScoped;
+
+import io.quarkus.runtime.annotations.RegisterForReflection;
+import org.apache.camel.Exchange;
+import org.apache.camel.component.optaplanner.OptaPlannerConstants;
+import org.apache.camel.quarkus.component.optaplanner.it.domain.TimeTable;
+
+@RegisterForReflection
+@ApplicationScoped
+public class MyBean {
+
+    public TimeTable bestSolution;
+
+    public TimeTable getBestSolution() {
+        return bestSolution;
+    }
+
+    public void setBestSolution(TimeTable bestSolution) {
+        this.bestSolution = bestSolution;
+    }
+
+    public void updateBestSolution(Exchange exchange) {
+        if (exchange != null) {
+            TimeTable newBestSolution = exchange.getMessage().getHeader(OptaPlannerConstants.BEST_SOLUTION, TimeTable.class);
+            if (newBestSolution != null) {
+                this.bestSolution = newBestSolution;
+            }
+        }
+    }
+}
diff --git a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerResource.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerResource.java
new file mode 100644
index 0000000..70f5f9c
--- /dev/null
+++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerResource.java
@@ -0,0 +1,89 @@
+/*
+ * 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.quarkus.component.optaplanner.it;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.component.optaplanner.OptaPlannerConstants;
+import org.apache.camel.quarkus.component.optaplanner.it.bootstrap.DataGenerator;
+import org.apache.camel.quarkus.component.optaplanner.it.domain.TimeTable;
+import org.jboss.logging.Logger;
+import org.optaplanner.core.api.solver.SolverManager;
+import org.optaplanner.core.api.solver.SolverStatus;
+
+@Path("/optaplanner")
+@ApplicationScoped
+@Produces(MediaType.APPLICATION_JSON)
+public class OptaplannerResource {
+
+    private static final Logger LOG = Logger.getLogger(OptaplannerResource.class);
+
+    public static final Long SINGLETON_TIME_TABLE_ID = 1L;
+
+    @Inject
+    SolverManager<TimeTable, Long> solverManager;
+
+    @Inject
+    ProducerTemplate producerTemplate;
+
+    @Inject
+    MyBean bean;
+
+    @GET
+    @Path("solveSync")
+    public TimeTable solveSync() {
+        if (SolverStatus.NOT_SOLVING == solverManager.getSolverStatus(SINGLETON_TIME_TABLE_ID)) {
+            TimeTable finalSolution = producerTemplate.requestBodyAndHeader(
+                    "direct:solveSync", DataGenerator.timeTable,
+                    OptaPlannerConstants.SOLVER_MANAGER, solverManager, TimeTable.class);
+            return finalSolution;
+        }
+        return DataGenerator.timeTable;
+    }
+
+    @GET
+    @Path("solveAsync")
+    public TimeTable solveAsync() throws ExecutionException, InterruptedException {
+        // reset best Solution
+        bean.setBestSolution(null);
+        if (SolverStatus.NOT_SOLVING == solverManager.getSolverStatus(SINGLETON_TIME_TABLE_ID)) {
+            CompletableFuture<TimeTable> response = producerTemplate.asyncRequestBodyAndHeader(
+                    "direct:solveAsync", DataGenerator.timeTable, OptaPlannerConstants.SOLVER_MANAGER,
+                    solverManager, TimeTable.class);
+
+            TimeTable finalSolution = response.get();
+            return finalSolution;
+        }
+        return DataGenerator.timeTable;
+    }
+
+    @GET
+    @Path("newBestSolution")
+    public TimeTable getNewBestSolution() {
+        return bean.getBestSolution();
+    }
+
+}
diff --git a/extensions-jvm/optaplanner/integration-test/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerResource.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/Routes.java
similarity index 50%
rename from extensions-jvm/optaplanner/integration-test/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerResource.java
rename to integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/Routes.java
index 8c7ae86..8f82b9e 100644
--- a/extensions-jvm/optaplanner/integration-test/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerResource.java
+++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/Routes.java
@@ -18,34 +18,27 @@ package org.apache.camel.quarkus.component.optaplanner.it;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
 
-import org.apache.camel.CamelContext;
-import org.jboss.logging.Logger;
+import org.apache.camel.builder.RouteBuilder;
 
-@Path("/optaplanner")
 @ApplicationScoped
-public class OptaplannerResource {
+public class Routes extends RouteBuilder {
+    @Inject
+    MyBean bean;
 
-    private static final Logger LOG = Logger.getLogger(OptaplannerResource.class);
+    @Override
+    public void configure() throws Exception {
 
-    private static final String COMPONENT_OPTAPLANNER = "optaplanner";
-    @Inject
-    CamelContext context;
+        // async producer
+        from("direct:solveAsync").to("optaplanner:anything?useSolverManager=true&async=true&problemId="
+                + OptaplannerResource.SINGLETON_TIME_TABLE_ID);
+
+        // async consumer
+        from("optaplanner:anything?useSolverManager=true&problemId=" + OptaplannerResource.SINGLETON_TIME_TABLE_ID)
+                .bean(bean, "updateBestSolution");
 
-    @Path("/load/component/optaplanner")
-    @GET
-    @Produces(MediaType.TEXT_PLAIN)
-    public Response loadComponentOptaplanner() throws Exception {
-        /* This is an autogenerated test */
-        if (context.getComponent(COMPONENT_OPTAPLANNER) != null) {
-            return Response.ok().build();
-        }
-        LOG.warnf("Could not load [%s] from the Camel context", COMPONENT_OPTAPLANNER);
-        return Response.status(500, COMPONENT_OPTAPLANNER + " could not be loaded from the Camel context").build();
+        // sync producer
+        from("direct:solveSync")
+                .to("optaplanner:anything?useSolverManager=true&problemId=" + OptaplannerResource.SINGLETON_TIME_TABLE_ID);
     }
 }
diff --git a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/bootstrap/DataGenerator.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/bootstrap/DataGenerator.java
new file mode 100644
index 0000000..7538aeb
--- /dev/null
+++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/bootstrap/DataGenerator.java
@@ -0,0 +1,95 @@
+/*
+ * 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.quarkus.component.optaplanner.it.bootstrap;
+
+import java.time.DayOfWeek;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.event.Observes;
+
+import io.quarkus.runtime.StartupEvent;
+import org.apache.camel.quarkus.component.optaplanner.it.domain.Lesson;
+import org.apache.camel.quarkus.component.optaplanner.it.domain.Room;
+import org.apache.camel.quarkus.component.optaplanner.it.domain.TimeTable;
+import org.apache.camel.quarkus.component.optaplanner.it.domain.Timeslot;
+
+/**
+ * adapted from optaplanner quarkus quickstart :
+ * https://github.com/quarkusio/quarkus-quickstarts/blob/master/optaplanner-quickstart/src/main/java/org/acme/optaplanner/bootstrap/DemoDataGenerator.java
+ */
+@ApplicationScoped
+public class DataGenerator {
+
+    public static List<Timeslot> timeslotList;
+
+    public static List<Room> roomList;
+
+    public static List<Lesson> lessonList;
+
+    public static TimeTable timeTable;
+
+    public void generateDemoData(@Observes StartupEvent startupEvent) {
+        timeslotList = new ArrayList<>(10);
+        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(8, 30), LocalTime.of(9, 30)));
+        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(9, 30), LocalTime.of(10, 30)));
+        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(10, 30), LocalTime.of(11, 30)));
+        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(13, 30), LocalTime.of(14, 30)));
+        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(14, 30), LocalTime.of(15, 30)));
+        timeslotList.add(new Timeslot(DayOfWeek.TUESDAY, LocalTime.of(8, 30), LocalTime.of(9, 30)));
+        timeslotList.add(new Timeslot(DayOfWeek.TUESDAY, LocalTime.of(9, 30), LocalTime.of(10, 30)));
+        timeslotList.add(new Timeslot(DayOfWeek.TUESDAY, LocalTime.of(10, 30), LocalTime.of(11, 30)));
+        timeslotList.add(new Timeslot(DayOfWeek.TUESDAY, LocalTime.of(13, 30), LocalTime.of(14, 30)));
+        timeslotList.add(new Timeslot(DayOfWeek.TUESDAY, LocalTime.of(14, 30), LocalTime.of(15, 30)));
+
+        roomList = new ArrayList<>(3);
+        roomList.add(new Room("Room A"));
+        roomList.add(new Room("Room B"));
+        roomList.add(new Room("Room C"));
+
+        lessonList = new ArrayList<>();
+        lessonList.add(new Lesson("Math", "A. Turing", "9th grade"));
+        lessonList.add(new Lesson("Math", "A. Turing", "9th grade"));
+        lessonList.add(new Lesson("Physics", "M. Curie", "9th grade"));
+        lessonList.add(new Lesson("Chemistry", "M. Curie", "9th grade"));
+        lessonList.add(new Lesson("Biology", "C. Darwin", "9th grade"));
+        lessonList.add(new Lesson("History", "I. Jones", "9th grade"));
+        lessonList.add(new Lesson("English", "I. Jones", "9th grade"));
+        lessonList.add(new Lesson("English", "I. Jones", "9th grade"));
+        lessonList.add(new Lesson("Spanish", "P. Cruz", "9th grade"));
+        lessonList.add(new Lesson("Spanish", "P. Cruz", "9th grade"));
+
+        lessonList.add(new Lesson("Math", "A. Turing", "10th grade"));
+        lessonList.add(new Lesson("Math", "A. Turing", "10th grade"));
+        lessonList.add(new Lesson("Math", "A. Turing", "10th grade"));
+        lessonList.add(new Lesson("Physics", "M. Curie", "10th grade"));
+        lessonList.add(new Lesson("Chemistry", "M. Curie", "10th grade"));
+        lessonList.add(new Lesson("French", "M. Curie", "10th grade"));
+        lessonList.add(new Lesson("Geography", "C. Darwin", "10th grade"));
+        lessonList.add(new Lesson("History", "I. Jones", "10th grade"));
+        lessonList.add(new Lesson("English", "P. Cruz", "10th grade"));
+        lessonList.add(new Lesson("Spanish", "P. Cruz", "10th grade"));
+
+        timeTable = new TimeTable(
+                DataGenerator.timeslotList,
+                DataGenerator.roomList,
+                DataGenerator.lessonList);
+    }
+
+}
diff --git a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/Lesson.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/Lesson.java
new file mode 100644
index 0000000..fb7b977
--- /dev/null
+++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/Lesson.java
@@ -0,0 +1,100 @@
+/*
+ * 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.quarkus.component.optaplanner.it.domain;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+import org.optaplanner.core.api.domain.entity.PlanningEntity;
+import org.optaplanner.core.api.domain.lookup.PlanningId;
+import org.optaplanner.core.api.domain.variable.PlanningVariable;
+
+/**
+ * adapted from optaplanner quarkus quickstart :
+ * https://github.com/quarkusio/quarkus-quickstarts/blob/master/optaplanner-quickstart/src/main/java/org/acme/optaplanner/domain/Lesson.java
+ */
+@PlanningEntity
+public class Lesson {
+
+    @PlanningId
+    @NotNull
+    private Long id;
+
+    @NotBlank
+    private String subject;
+    @NotBlank
+    private String teacher;
+    @NotBlank
+    private String studentGroup;
+
+    @PlanningVariable(valueRangeProviderRefs = "timeslotRange")
+    private Timeslot timeslot;
+    @PlanningVariable(valueRangeProviderRefs = "roomRange")
+    private Room room;
+
+    private static AtomicLong increment = new AtomicLong(1);
+
+    public Lesson() {
+    }
+
+    public Lesson(String subject, String teacher, String studentGroup) {
+        this.id = increment.getAndIncrement();
+        this.subject = subject.trim();
+        this.teacher = teacher.trim();
+        this.studentGroup = studentGroup.trim();
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public String getTeacher() {
+        return teacher;
+    }
+
+    public String getStudentGroup() {
+        return studentGroup;
+    }
+
+    public Timeslot getTimeslot() {
+        return timeslot;
+    }
+
+    public void setTimeslot(Timeslot timeslot) {
+        this.timeslot = timeslot;
+    }
+
+    public Room getRoom() {
+        return room;
+    }
+
+    public void setRoom(Room room) {
+        this.room = room;
+    }
+
+    @Override
+    public String toString() {
+        return subject + "(" + id + ")";
+    }
+
+}
diff --git a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/Room.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/Room.java
new file mode 100644
index 0000000..bd93b2d
--- /dev/null
+++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/Room.java
@@ -0,0 +1,61 @@
+/*
+ * 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.quarkus.component.optaplanner.it.domain;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+import org.optaplanner.core.api.domain.lookup.PlanningId;
+
+/**
+ * adapted from optaplanner quarkus quickstart :
+ * https://github.com/quarkusio/quarkus-quickstarts/blob/master/optaplanner-quickstart/src/main/java/org/acme/optaplanner/domain/Room.java
+ */
+public class Room {
+
+    @PlanningId
+    @NotNull
+    private Long id;
+
+    @NotBlank
+    private String name;
+    private static AtomicLong increment = new AtomicLong(1);
+
+    public Room() {
+    }
+
+    public Room(String name) {
+        this.id = increment.getAndIncrement();
+        this.name = name.trim();
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+}
diff --git a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/TimeTable.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/TimeTable.java
new file mode 100644
index 0000000..8fe3800
--- /dev/null
+++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/TimeTable.java
@@ -0,0 +1,72 @@
+/*
+ * 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.quarkus.component.optaplanner.it.domain;
+
+import java.util.List;
+
+import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
+import org.optaplanner.core.api.domain.solution.PlanningScore;
+import org.optaplanner.core.api.domain.solution.PlanningSolution;
+import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
+import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
+import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
+
+/**
+ * adapted from optaplanner quarkus quickstart :
+ * https://github.com/quarkusio/quarkus-quickstarts/blob/master/optaplanner-quickstart/src/main/java/org/acme/optaplanner/domain/Timeslot.java
+ */
+@PlanningSolution
+public class TimeTable {
+
+    @ProblemFactCollectionProperty
+    @ValueRangeProvider(id = "timeslotRange")
+    private List<Timeslot> timeslotList;
+    @ProblemFactCollectionProperty
+    @ValueRangeProvider(id = "roomRange")
+    private List<Room> roomList;
+    @PlanningEntityCollectionProperty
+    private List<Lesson> lessonList;
+
+    @PlanningScore
+    private HardSoftScore score;
+
+    public TimeTable() {
+    }
+
+    public TimeTable(List<Timeslot> timeslotList, List<Room> roomList, List<Lesson> lessonList) {
+        this.timeslotList = timeslotList;
+        this.roomList = roomList;
+        this.lessonList = lessonList;
+    }
+
+    public List<Timeslot> getTimeslotList() {
+        return timeslotList;
+    }
+
+    public List<Room> getRoomList() {
+        return roomList;
+    }
+
+    public List<Lesson> getLessonList() {
+        return lessonList;
+    }
+
+    public HardSoftScore getScore() {
+        return score;
+    }
+
+}
diff --git a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/Timeslot.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/Timeslot.java
new file mode 100644
index 0000000..57fc50f
--- /dev/null
+++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/domain/Timeslot.java
@@ -0,0 +1,77 @@
+/*
+ * 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.quarkus.component.optaplanner.it.domain;
+
+import java.time.DayOfWeek;
+import java.time.LocalTime;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.validation.constraints.NotNull;
+
+import org.optaplanner.core.api.domain.lookup.PlanningId;
+
+/**
+ * adapted from optaplanner quarkus quickstart :
+ * https://github.com/quarkusio/quarkus-quickstarts/blob/master/optaplanner-quickstart/src/main/java/org/acme/optaplanner/domain/TimeTable.java
+ */
+public class Timeslot {
+
+    @PlanningId
+    @NotNull
+    private Long id;
+
+    @NotNull
+    private DayOfWeek dayOfWeek;
+    @NotNull
+    private LocalTime startTime;
+    @NotNull
+    private LocalTime endTime;
+
+    private static AtomicLong increment = new AtomicLong(1);
+
+    public Timeslot() {
+    }
+
+    public Timeslot(DayOfWeek dayOfWeek, LocalTime startTime, LocalTime endTime) {
+        this.id = increment.getAndIncrement();
+        this.dayOfWeek = dayOfWeek;
+        this.startTime = startTime;
+        this.endTime = endTime;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public DayOfWeek getDayOfWeek() {
+        return dayOfWeek;
+    }
+
+    public LocalTime getStartTime() {
+        return startTime;
+    }
+
+    public LocalTime getEndTime() {
+        return endTime;
+    }
+
+    @Override
+    public String toString() {
+        return dayOfWeek + " " + startTime;
+    }
+
+}
diff --git a/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/solver/TimeTableConstraintProvider.java b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/solver/TimeTableConstraintProvider.java
new file mode 100644
index 0000000..b15c6c0
--- /dev/null
+++ b/integration-tests/optaplanner/src/main/java/org/apache/camel/quarkus/component/optaplanner/it/solver/TimeTableConstraintProvider.java
@@ -0,0 +1,118 @@
+/*
+ * 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.quarkus.component.optaplanner.it.solver;
+
+import java.time.Duration;
+
+import org.apache.camel.quarkus.component.optaplanner.it.domain.Lesson;
+import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
+import org.optaplanner.core.api.score.stream.Constraint;
+import org.optaplanner.core.api.score.stream.ConstraintFactory;
+import org.optaplanner.core.api.score.stream.ConstraintProvider;
+import org.optaplanner.core.api.score.stream.Joiners;
+
+/**
+ * adapted from optaplanner quarkus quickstart :
+ * https://github.com/quarkusio/quarkus-quickstarts/blob/master/optaplanner-quickstart/src/main/java/org/acme/optaplanner/solver/TimeTableConstraintProvider.java
+ */
+public class TimeTableConstraintProvider implements ConstraintProvider {
+
+    @Override
+    public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
+        return new Constraint[] {
+                // Hard constraints
+                roomConflict(constraintFactory),
+                teacherConflict(constraintFactory),
+                studentGroupConflict(constraintFactory),
+                // Soft constraints
+                teacherRoomStability(constraintFactory),
+                teacherTimeEfficiency(constraintFactory),
+                studentGroupSubjectVariety(constraintFactory)
+        };
+    }
+
+    private Constraint roomConflict(ConstraintFactory constraintFactory) {
+        // A room can accommodate at most one lesson at the same time.
+        return constraintFactory
+                // Select each pair of 2 different lessons ...
+                .fromUniquePair(Lesson.class,
+                        // ... in the same timeslot ...
+                        Joiners.equal(Lesson::getTimeslot),
+                        // ... in the same room ...
+                        Joiners.equal(Lesson::getRoom))
+                // ... and penalize each pair with a hard weight.
+                .penalize("Room conflict", HardSoftScore.ONE_HARD);
+    }
+
+    private Constraint teacherConflict(ConstraintFactory constraintFactory) {
+        // A teacher can teach at most one lesson at the same time.
+        return constraintFactory
+                .fromUniquePair(Lesson.class,
+                        Joiners.equal(Lesson::getTimeslot),
+                        Joiners.equal(Lesson::getTeacher))
+                .penalize("Teacher conflict", HardSoftScore.ONE_HARD);
+    }
+
+    private Constraint studentGroupConflict(ConstraintFactory constraintFactory) {
+        // A student can attend at most one lesson at the same time.
+        return constraintFactory
+                .fromUniquePair(Lesson.class,
+                        Joiners.equal(Lesson::getTimeslot),
+                        Joiners.equal(Lesson::getStudentGroup))
+                .penalize("Student group conflict", HardSoftScore.ONE_HARD);
+    }
+
+    private Constraint teacherRoomStability(ConstraintFactory constraintFactory) {
+        // A teacher prefers to teach in a single room.
+        return constraintFactory
+                .fromUniquePair(Lesson.class,
+                        Joiners.equal(Lesson::getTeacher))
+                .filter((lesson1, lesson2) -> lesson1.getRoom() != lesson2.getRoom())
+                .penalize("Teacher room stability", HardSoftScore.ONE_SOFT);
+    }
+
+    private Constraint teacherTimeEfficiency(ConstraintFactory constraintFactory) {
+        // A teacher prefers to teach sequential lessons and dislikes gaps between lessons.
+        return constraintFactory
+                .from(Lesson.class)
+                .join(Lesson.class, Joiners.equal(Lesson::getTeacher),
+                        Joiners.equal((lesson) -> lesson.getTimeslot().getDayOfWeek()))
+                .filter((lesson1, lesson2) -> {
+                    Duration between = Duration.between(lesson1.getTimeslot().getEndTime(),
+                            lesson2.getTimeslot().getStartTime());
+                    return !between.isNegative() && between.compareTo(Duration.ofMinutes(30)) <= 0;
+                })
+                .reward("Teacher time efficiency", HardSoftScore.ONE_SOFT);
+    }
+
+    private Constraint studentGroupSubjectVariety(ConstraintFactory constraintFactory) {
+        // A student group dislikes sequential lessons on the same subject.
+        return constraintFactory
+                .from(Lesson.class)
+                .join(Lesson.class,
+                        Joiners.equal(Lesson::getSubject),
+                        Joiners.equal(Lesson::getStudentGroup),
+                        Joiners.equal((lesson) -> lesson.getTimeslot().getDayOfWeek()))
+                .filter((lesson1, lesson2) -> {
+                    Duration between = Duration.between(lesson1.getTimeslot().getEndTime(),
+                            lesson2.getTimeslot().getStartTime());
+                    return !between.isNegative() && between.compareTo(Duration.ofMinutes(30)) <= 0;
+                })
+                .penalize("Student group subject variety", HardSoftScore.ONE_SOFT);
+    }
+
+}
diff --git a/integration-tests/optaplanner/src/main/resources/application.properties b/integration-tests/optaplanner/src/main/resources/application.properties
new file mode 100644
index 0000000..4156b41
--- /dev/null
+++ b/integration-tests/optaplanner/src/main/resources/application.properties
@@ -0,0 +1,32 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+########################
+# OptaPlanner properties
+# adapted from optaplanner quarkus quickstart : https://github.com/quarkusio/quarkus-quickstarts/blob/master/optaplanner-quickstart/src/main/resources/application.properties
+########################
+
+# The solver runs for 30 seconds. To run for 5 minutes use "5m" and for 2 hours use "2h".
+quarkus.optaplanner.solver.termination.spent-limit=30s
+
+########################
+# Test overrides
+########################
+
+# Effectively disable this termination in favor of the best-score-limit
+%test.quarkus.optaplanner.solver.termination.spent-limit=1h
+%test.quarkus.optaplanner.solver.termination.best-score-limit=0hard/*soft
diff --git a/extensions-jvm/optaplanner/integration-test/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java b/integration-tests/optaplanner/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerIT.java
similarity index 69%
copy from extensions-jvm/optaplanner/integration-test/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java
copy to integration-tests/optaplanner/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerIT.java
index ff99e1e..81cc65d 100644
--- a/extensions-jvm/optaplanner/integration-test/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java
+++ b/integration-tests/optaplanner/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerIT.java
@@ -16,19 +16,9 @@
  */
 package org.apache.camel.quarkus.component.optaplanner.it;
 
-import io.quarkus.test.junit.QuarkusTest;
-import io.restassured.RestAssured;
-import org.junit.jupiter.api.Test;
+import io.quarkus.test.junit.NativeImageTest;
 
-@QuarkusTest
-class OptaplannerTest {
-
-    @Test
-    public void loadComponentOptaplanner() {
-        /* A simple autogenerated test */
-        RestAssured.get("/optaplanner/load/component/optaplanner")
-                .then()
-                .statusCode(200);
-    }
+@NativeImageTest
+class OptaplannerIT extends OptaplannerTest {
 
 }
diff --git a/extensions-jvm/optaplanner/integration-test/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java b/integration-tests/optaplanner/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java
similarity index 52%
rename from extensions-jvm/optaplanner/integration-test/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java
rename to integration-tests/optaplanner/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java
index ff99e1e..de30a7e 100644
--- a/extensions-jvm/optaplanner/integration-test/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java
+++ b/integration-tests/optaplanner/src/test/java/org/apache/camel/quarkus/component/optaplanner/it/OptaplannerTest.java
@@ -20,15 +20,38 @@ import io.quarkus.test.junit.QuarkusTest;
 import io.restassured.RestAssured;
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.Matchers.notNullValue;
+
 @QuarkusTest
 class OptaplannerTest {
 
     @Test
-    public void loadComponentOptaplanner() {
-        /* A simple autogenerated test */
-        RestAssured.get("/optaplanner/load/component/optaplanner")
+    public void solveSync() {
+        RestAssured.given()
+                .get("/optaplanner/solveSync")
+                .then()
+                .statusCode(200)
+                .body("lessonList[0].timeslot", notNullValue(null))
+                .body("lessonList[0].room", notNullValue(null));
+    }
+
+    @Test
+    public void solveASyncWithConsumer() {
+        // solve async
+        RestAssured.given()
+                .get("/optaplanner/solveAsync")
+                .then()
+                .statusCode(200)
+                .body("lessonList[0].timeslot", notNullValue(null))
+                .body("lessonList[0].room", notNullValue(null));
+
+        // test consumer data
+        RestAssured.given()
+                .get("/optaplanner/newBestSolution")
                 .then()
-                .statusCode(200);
+                .statusCode(200)
+                .body("lessonList[0].timeslot", notNullValue(null))
+                .body("lessonList[0].room", notNullValue(null));
     }
 
 }
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index 834e5e2..b0303c7 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -138,6 +138,7 @@
         <module>olingo4</module>
         <module>openapi-java</module>
         <module>opentracing</module>
+        <module>optaplanner</module>
         <module>pdf</module>
         <module>pg-replication-slot</module>
         <module>pgevent</module>
diff --git a/pom.xml b/pom.xml
index 08cf44a..c9f9042 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,6 +87,7 @@
         <okhttp.version>3.14.6</okhttp.version><!-- keep in sync with okio -->
         <okio.version>1.17.2</okio.version><!-- keep in sync with okhttp -->
         <opencensus-api.version>0.26.0</opencensus-api.version><!-- Mess in pubsub transitive deps vs google cloud deps -->
+        <optaplanner.version>7.46.0.Final</optaplanner.version>
         <quarkus.version>1.10.1.Final</quarkus.version>
         <quarkus-qpid-jms.version>0.21.0</quarkus-qpid-jms.version>
         <protobuf.version>3.11.1</protobuf.version>
@@ -116,6 +117,7 @@
         <istack-commons-runtime.version>3.0.10</istack-commons-runtime.version>
         <jakarta.mail.version>1.6.5</jakarta.mail.version>
         <mock-javamail.version>1.9</mock-javamail.version>
+        <mvel2.version>2.4.8.Final</mvel2.version>
         <pdfbox.version>2.0.21</pdfbox.version>
         <slf4j-log4j12.version>1.7.30</slf4j-log4j12.version><!-- Mess in the transitive dependencies of hbase-testing-util -->
         <sshd.version>2.3.0</sshd.version>
diff --git a/poms/bom/pom.xml b/poms/bom/pom.xml
index f12e70a..6435c1d 100644
--- a/poms/bom/pom.xml
+++ b/poms/bom/pom.xml
@@ -1530,6 +1530,16 @@
                 <groupId>org.apache.camel</groupId>
                 <artifactId>camel-optaplanner</artifactId>
                 <version>${camel.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.optaplanner</groupId>
+                        <artifactId>optaplanner-core</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.optaplanner</groupId>
+                        <artifactId>optaplanner-persistence-common</artifactId>
+                    </exclusion>
+                </exclusions>
             </dependency>
             <dependency>
                 <groupId>org.apache.camel</groupId>
diff --git a/tooling/scripts/test-categories.yaml b/tooling/scripts/test-categories.yaml
index 5327b7c..b0e8ee7 100644
--- a/tooling/scripts/test-categories.yaml
+++ b/tooling/scripts/test-categories.yaml
@@ -92,6 +92,7 @@ xml-json-olingo4:
   - weather
   - geocoder
   - lumberjack
+  - optaplanner
 dozer-social:
   - crypto
   - dozer