You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ja...@apache.org on 2024/03/05 19:32:36 UTC

(camel-quarkus) branch 3.8.x updated (d9069881aa -> 8243917e6a)

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

jamesnetherton pushed a change to branch 3.8.x
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


    from d9069881aa Upgrade cq-maven-plugin to 4.5.0
     new 3228a32d49 Add multipart configuration options to servlet extension
     new 7386bcd672 Remove registration of Servlet classes with AdditionalBeanBuildItem
     new c0dee92d90 Document basic servlet extension usage and configuration options
     new e77cbd02a8 Support loadOnStartup, async, forceAwait & executorRef configuration options
     new 4f95132e32 Enable web.xml to be used to configure CamelHttpTransportServlet for the servlet extension
     new 4d907e3bd7 Add additional Servlet test coverage
     new 8243917e6a Upgrade cq-maven-plugin to 4.6.1

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../ROOT/pages/reference/extensions/servlet.adoc   | 206 ++++++++++++++++++++
 .../servlet/deployment/ServletProcessor.java       |  53 +++++-
 ...nimalConfigTest.java => WebXmlServletTest.java} |  55 +++---
 extensions/servlet/runtime/src/main/doc/usage.adoc | 103 ++++++++++
 .../servlet/runtime/CamelServletConfig.java        |  63 +++++++
 integration-tests/servlet/pom.xml                  |  17 ++
 .../quarkus/component/servlet/CamelRoute.java      |  73 ++++++--
 .../component/servlet}/CustomException.java        |  11 +-
 .../component/servlet/MultiPartProcessor.java}     |  17 +-
 .../component/servlet/ServletProducers.java        |  71 +++++++
 .../src/main/resources/application.properties      |  50 ++++-
 .../component/servlet/CamelServletTest.java        | 207 +++++++++++++++++++--
 pom.xml                                            |   2 +-
 13 files changed, 844 insertions(+), 84 deletions(-)
 copy extensions/servlet/deployment/src/test/java/org/apache/camel/quarkus/component/servlet/test/{MinimalConfigTest.java => WebXmlServletTest.java} (59%)
 create mode 100644 extensions/servlet/runtime/src/main/doc/usage.adoc
 copy integration-tests/{main-yaml/src/main/java/org/apache/camel/quarkus/main => servlet/src/main/java/org/apache/camel/quarkus/component/servlet}/CustomException.java (83%)
 copy integration-tests/{camel-k-runtime/src/main/java/org/apache/camel/quarkus/k/it/yaml/MyProcessor.java => servlet/src/main/java/org/apache/camel/quarkus/component/servlet/MultiPartProcessor.java} (63%)
 create mode 100644 integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/ServletProducers.java


(camel-quarkus) 03/07: Document basic servlet extension usage and configuration options

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

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

commit c0dee92d90251cd64d04afb9d476e7cd5bb0eaae
Author: James Netherton <ja...@gmail.com>
AuthorDate: Tue Mar 5 07:52:43 2024 +0000

    Document basic servlet extension usage and configuration options
---
 .../ROOT/pages/reference/extensions/servlet.adoc   | 73 ++++++++++++++++++++++
 extensions/servlet/runtime/src/main/doc/usage.adoc | 66 +++++++++++++++++++
 2 files changed, 139 insertions(+)

diff --git a/docs/modules/ROOT/pages/reference/extensions/servlet.adoc b/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
index fd56d2364a..ac9303ed92 100644
--- a/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
@@ -45,6 +45,79 @@ ifeval::[{doc-show-user-guide-link} == true]
 Check the xref:user-guide/index.adoc[User guide] for more information about writing Camel Quarkus applications.
 endif::[]
 
+[id="extensions-servlet-usage"]
+== Usage
+[id="extensions-servlet-usage-configuring-camelhttptransportservlet"]
+=== Configuring CamelHttpTransportServlet
+
+[id="extensions-servlet-usage-minimal-configuration"]
+==== Minimal configuration
+
+The simplest way to configure `CamelHttpTransportServlet` is with configuration properties.
+The most minimal setup requires that you define one or more URL patterns for the Servlet with `quarkus.camel.servlet.url-patterns`.
+
+For example with configuration like the following.
+
+[source,properties]
+----
+quarkus.camel.servlet.url-patterns = /*
+----
+
+And a Camel route.
+
+[source,java]
+----
+from("servlet://greet")
+    .setBody().constant("Hello World");
+----
+
+Produces the message `Hello World`.
+
+[id="extensions-servlet-usage-advanced-configuration"]
+==== Advanced configuration
+
+*Servlet name*
+
+To give a specific name to the Servlet you can use the `quarkus.camel.servlet.servlet-name` configuration option.
+
+[source,properties]
+----
+quarkus.camel.servlet.url-patterns = My Custom Name
+----
+
+*Servlet class*
+
+You may use a custom Servlet class (E.g one that extends `CamelHttpTransportServlet`) in your Camel routes.
+
+[source,properties]
+----
+quarkus.camel.servlet.servlet-class = org.acme.MyCustomServlet
+----
+
+*Multiple named Servlets*
+
+For more advanced use cases you can configure multiple 'named' Servlets.
+
+[source,properties]
+----
+quarkus.camel.servlet.my-servlet-a.servlet-name = my-custom-a
+quarkus.camel.servlet.my-servlet-a.url-patterns = /custom/a/*
+
+quarkus.camel.servlet.my-servlet-b.servlet-name = my-custom-b
+quarkus.camel.servlet.my-servlet-b.servlet-class = org.acme.CustomServletB
+quarkus.camel.servlet.my-servlet-b.url-patterns = /custom/b/*
+----
+
+[source,java]
+----
+from("servlet://greet?servletName=my-custom-a")
+    .setBody().constant("Hello World");
+
+from("servlet://goodbye?servletName=my-custom-b")
+    .setBody().constant("Goodbye World");
+----
+
+
 [id="extensions-servlet-transferexception-option-in-native-mode"]
 == transferException option in native mode
 
diff --git a/extensions/servlet/runtime/src/main/doc/usage.adoc b/extensions/servlet/runtime/src/main/doc/usage.adoc
new file mode 100644
index 0000000000..7ad44a268c
--- /dev/null
+++ b/extensions/servlet/runtime/src/main/doc/usage.adoc
@@ -0,0 +1,66 @@
+=== Configuring CamelHttpTransportServlet
+
+==== Minimal configuration
+
+The simplest way to configure `CamelHttpTransportServlet` is with configuration properties.
+The most minimal setup requires that you define one or more URL patterns for the Servlet with `quarkus.camel.servlet.url-patterns`.
+
+For example with configuration like the following.
+
+[source,properties]
+----
+quarkus.camel.servlet.url-patterns = /*
+----
+
+And a Camel route.
+
+[source,java]
+----
+from("servlet://greet")
+    .setBody().constant("Hello World");
+----
+
+Produces the message `Hello World`.
+
+==== Advanced configuration
+
+*Servlet name*
+
+To give a specific name to the Servlet you can use the `quarkus.camel.servlet.servlet-name` configuration option.
+
+[source,properties]
+----
+quarkus.camel.servlet.url-patterns = My Custom Name
+----
+
+*Servlet class*
+
+You may use a custom Servlet class (E.g one that extends `CamelHttpTransportServlet`) in your Camel routes.
+
+[source,properties]
+----
+quarkus.camel.servlet.servlet-class = org.acme.MyCustomServlet
+----
+
+*Multiple named Servlets*
+
+For more advanced use cases you can configure multiple 'named' Servlets.
+
+[source,properties]
+----
+quarkus.camel.servlet.my-servlet-a.servlet-name = my-custom-a
+quarkus.camel.servlet.my-servlet-a.url-patterns = /custom/a/*
+
+quarkus.camel.servlet.my-servlet-b.servlet-name = my-custom-b
+quarkus.camel.servlet.my-servlet-b.servlet-class = org.acme.CustomServletB
+quarkus.camel.servlet.my-servlet-b.url-patterns = /custom/b/*
+----
+
+[source,java]
+----
+from("servlet://greet?servletName=my-custom-a")
+    .setBody().constant("Hello World");
+
+from("servlet://goodbye?servletName=my-custom-b")
+    .setBody().constant("Goodbye World");
+----


(camel-quarkus) 02/07: Remove registration of Servlet classes with AdditionalBeanBuildItem

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

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

commit 7386bcd6723841adba711e9ddfe4a0056efb297d
Author: James Netherton <ja...@gmail.com>
AuthorDate: Fri Mar 1 13:48:20 2024 +0000

    Remove registration of Servlet classes with AdditionalBeanBuildItem
    
    Fixes #3204
---
 .../component/servlet/deployment/ServletProcessor.java        | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java b/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
index 308f5489db..7e21a43adb 100644
--- a/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
+++ b/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
@@ -20,7 +20,6 @@ import java.util.List;
 import java.util.Map.Entry;
 import java.util.Optional;
 
-import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
 import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
@@ -42,11 +41,11 @@ class ServletProcessor {
     }
 
     @BuildStep
-    void build(BuildProducer<ServletBuildItem> servlet, BuildProducer<AdditionalBeanBuildItem> additionalBean) {
+    void build(BuildProducer<ServletBuildItem> servlet) {
         boolean servletCreated = false;
         if (camelServletConfig.defaultServlet.isValid()) {
             servlet.produce(
-                    newServlet(ServletConfig.DEFAULT_SERVLET_NAME, camelServletConfig.defaultServlet, additionalBean));
+                    newServlet(ServletConfig.DEFAULT_SERVLET_NAME, camelServletConfig.defaultServlet));
             servletCreated = true;
         }
 
@@ -56,7 +55,7 @@ class ServletProcessor {
                         String.format("Use quarkus.camel.servlet.url-patterns instead of quarkus.camel.servlet.%s.url-patterns",
                                 ServletConfig.DEFAULT_SERVLET_NAME));
             }
-            servlet.produce(newServlet(e.getKey(), e.getValue(), additionalBean));
+            servlet.produce(newServlet(e.getKey(), e.getValue()));
             servletCreated = true;
         }
 
@@ -67,8 +66,7 @@ class ServletProcessor {
 
     }
 
-    static ServletBuildItem newServlet(String key, ServletConfig servletConfig,
-            BuildProducer<AdditionalBeanBuildItem> additionalBean) {
+    static ServletBuildItem newServlet(String key, ServletConfig servletConfig) {
         final String servletName = servletConfig.getEffectiveServletName(key);
         final Optional<List<String>> urlPatterns = servletConfig.urlPatterns;
         if (!urlPatterns.isPresent() || urlPatterns.get().isEmpty()) {
@@ -78,7 +76,6 @@ class ServletProcessor {
         }
 
         final Builder builder = ServletBuildItem.builder(servletName, servletConfig.servletClass);
-        additionalBean.produce(new AdditionalBeanBuildItem(servletConfig.servletClass));
         for (String pattern : urlPatterns.get()) {
             builder.addMapping(pattern);
         }


(camel-quarkus) 07/07: Upgrade cq-maven-plugin to 4.6.1

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

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

commit 8243917e6a06722133ba6099b5276e1f97d2c892
Author: James Netherton <ja...@gmail.com>
AuthorDate: Tue Mar 5 14:49:35 2024 +0000

    Upgrade cq-maven-plugin to 4.6.1
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 969b1d1c0d..e9695e5469 100644
--- a/pom.xml
+++ b/pom.xml
@@ -178,7 +178,7 @@
         <maven-utils.version>0.1.0</maven-utils.version>
 
         <!-- Maven plugin versions (keep sorted alphabetically) -->
-        <cq-plugin.version>4.5.0</cq-plugin.version>
+        <cq-plugin.version>4.6.1</cq-plugin.version>
         <cyclonedx-maven-plugin-version>2.7.11</cyclonedx-maven-plugin-version>
         <build-helper-maven-plugin.version>3.5.0</build-helper-maven-plugin.version>
         <exec-maven-plugin.version>3.1.1</exec-maven-plugin.version>


(camel-quarkus) 06/07: Add additional Servlet test coverage

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

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

commit 4d907e3bd71f239a4268c4a508b372bd70d2dc8e
Author: James Netherton <ja...@gmail.com>
AuthorDate: Tue Mar 5 09:48:35 2024 +0000

    Add additional Servlet test coverage
---
 .../quarkus/component/servlet/CamelRoute.java      | 26 +++++-
 .../quarkus/component/servlet/CustomException.java | 28 +++++++
 .../src/main/resources/application.properties      |  6 ++
 .../component/servlet/CamelServletTest.java        | 97 ++++++++++++++++++++--
 4 files changed, 148 insertions(+), 9 deletions(-)

diff --git a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
index 6c8c117c35..2ca381f295 100644
--- a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
+++ b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
@@ -44,10 +44,34 @@ public class CamelRoute extends RouteBuilder {
                 .to("direct:echoMethodPath")
 
                 .post("/rest-post")
+                .to("direct:echoMethodPath")
+
+                .put("/rest-put")
+                .to("direct:echoMethodPath")
+
+                .patch("/rest-patch")
+                .to("direct:echoMethodPath")
+
+                .delete("/rest-delete")
+                .to("direct:echoMethodPath")
+
+                .head("/rest-head")
                 .to("direct:echoMethodPath");
 
         from("servlet://hello?matchOnUriPrefix=true")
-                .setBody(constant("GET: /hello"));
+                .to("direct:echoMethodPath");
+
+        from("servlet://options?servletName=options-method-servlet&optionsEnabled=true")
+                .to("direct:echoMethodPath");
+
+        from("servlet://trace?servletName=trace-method-servlet&traceEnabled=true")
+                .to("direct:echoMethodPath");
+
+        from("servlet://transfer/exception?transferException=true&muteException=false")
+                .throwException(new CustomException());
+
+        from("servlet://params")
+                .setBody().simple("${header.prefix} ${header.suffix}");
 
         from("servlet://configuration")
                 .process(servletConfigInfoProcessor);
diff --git a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CustomException.java b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CustomException.java
new file mode 100644
index 0000000000..33fb3db78d
--- /dev/null
+++ b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CustomException.java
@@ -0,0 +1,28 @@
+/*
+ * 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.servlet;
+
+import io.quarkus.runtime.annotations.RegisterForReflection;
+
+@RegisterForReflection(serialization = true)
+public class CustomException extends Exception {
+    public static final String MESSAGE = "Something went wrong";
+
+    public CustomException() {
+        super(MESSAGE);
+    }
+}
diff --git a/integration-tests/servlet/src/main/resources/application.properties b/integration-tests/servlet/src/main/resources/application.properties
index 06920af73f..76be5f65b3 100644
--- a/integration-tests/servlet/src/main/resources/application.properties
+++ b/integration-tests/servlet/src/main/resources/application.properties
@@ -15,6 +15,9 @@
 ## limitations under the License.
 ## ---------------------------------------------------------------------------
 
+# For transferException
+quarkus.camel.native.reflection.serialization-enabled=true
+
 # Default servlet configuration
 quarkus.camel.servlet.url-patterns=/folder-1/*,/folder-2/*,/debug/*
 
@@ -51,3 +54,6 @@ quarkus.camel.servlet.custom-executor-servlet.url-patterns=/custom-executor/*
 quarkus.camel.servlet.custom-executor-servlet.async=true
 quarkus.camel.servlet.custom-executor-servlet.executor-ref=customServletExecutor
 
+# Servlet to allow OPTIONS & TRACE
+quarkus.camel.servlet.options-method-servlet.url-patterns=/method-options/*
+quarkus.camel.servlet.trace-method-servlet.url-patterns=/method-trace/*
diff --git a/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java b/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
index 73bdd78dbe..b87966872b 100644
--- a/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
+++ b/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
@@ -16,11 +16,18 @@
  */
 package org.apache.camel.quarkus.component.servlet;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 
 import io.quarkus.test.junit.QuarkusTest;
 import io.restassured.RestAssured;
+import org.apache.camel.CamelContext;
+import org.apache.camel.http.common.HttpHelper;
+import org.apache.camel.impl.DefaultCamelContext;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import static org.hamcrest.Matchers.anEmptyMap;
 import static org.hamcrest.Matchers.is;
@@ -28,6 +35,9 @@ import static org.hamcrest.Matchers.nullValue;
 import static org.hamcrest.Matchers.oneOf;
 import static org.hamcrest.Matchers.startsWith;
 import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 @QuarkusTest
 public class CamelServletTest {
@@ -42,22 +52,72 @@ public class CamelServletTest {
                         "loadOnStartup", nullValue());
     }
 
+    @ParameterizedTest
+    @ValueSource(strings = { "GET", "POST", "PUT", "PATCH", "DELETE", "HEAD" })
+    public void multiplePaths(String method) {
+        String lowercaseMethod = method.toLowerCase();
+        String restResponse = method.equals("HEAD") ? "" : method + ": /rest-" + lowercaseMethod;
+        RestAssured.given()
+                .request(method, "/folder-1/rest-" + lowercaseMethod)
+                .then()
+                .statusCode(200)
+                .body(equalTo(restResponse));
+
+        RestAssured.given()
+                .request(method, "/folder-2/rest-" + lowercaseMethod)
+                .then()
+                .statusCode(200)
+                .body(equalTo(restResponse));
+
+        String helloResponse = method.equals("HEAD") ? "" : method + ": /hello";
+        RestAssured.given()
+                .request(method, "/folder-1/hello")
+                .then()
+                .statusCode(200)
+                .body(equalTo(helloResponse));
+
+        RestAssured.given()
+                .request(method, "/folder-2/hello")
+                .then()
+                .statusCode(200)
+                .body(equalTo(helloResponse));
+    }
+
     @Test
-    public void multiplePaths() {
-        RestAssured.get("/folder-1/rest-get").then().body(equalTo("GET: /rest-get"));
-        RestAssured.get("/folder-2/rest-get").then().body(equalTo("GET: /rest-get"));
-        RestAssured.post("/folder-1/rest-post").then().body(equalTo("POST: /rest-post"));
-        RestAssured.post("/folder-2/rest-post").then().body(equalTo("POST: /rest-post"));
-        RestAssured.get("/folder-1/hello").then().body(equalTo("GET: /hello"));
-        RestAssured.get("/folder-2/hello").then().body(equalTo("GET: /hello"));
+    public void options() {
+        RestAssured.given()
+                .request("OPTIONS", "/method-options/options")
+                .then()
+                .statusCode(200)
+                .body(equalTo("OPTIONS: /options"));
+    }
+
+    @Test
+    public void trace() {
+        RestAssured.given()
+                .request("TRACE", "/method-trace/trace")
+                .then()
+                .statusCode(200)
+                .body(equalTo("TRACE: /trace"));
+    }
+
+    @Test
+    public void requestParameters() {
+        RestAssured.given()
+                .formParam("prefix", "Hello")
+                .queryParam("suffix", "World")
+                .post("/folder-1/params")
+                .then()
+                .statusCode(200)
+                .body(equalTo("Hello World"));
     }
 
     @Test
     public void namedWithServletClass() {
         RestAssured.get("/my-custom-folder/custom")
                 .then()
+                .statusCode(200)
                 .body(equalTo("GET: /custom"))
-                .and()
                 .header("x-servlet-class-name", CustomServlet.class.getName());
     }
 
@@ -142,4 +202,25 @@ public class CamelServletTest {
                         "initParams.async", equalTo("true"),
                         "loadOnStartup", nullValue());
     }
+
+    @Test
+    public void transferException() throws IOException, ClassNotFoundException {
+        InputStream response = RestAssured.given()
+                .get("/folder-1/transfer/exception")
+                .then()
+                .statusCode(500)
+                .contentType("application/x-java-serialized-object")
+                .extract()
+                .body()
+                .asInputStream();
+
+        // The CamelContext instance is only needed to ensure the correct ClassLoader is used for deserialization
+        CamelContext context = new DefaultCamelContext();
+        context.setApplicationContextClassLoader(Thread.currentThread().getContextClassLoader());
+        Object exception = HttpHelper.deserializeJavaObjectFromStream(response, context);
+        assertNotNull(exception);
+
+        CustomException cause = assertInstanceOf(CustomException.class, exception);
+        assertEquals(CustomException.MESSAGE, cause.getMessage());
+    }
 }


(camel-quarkus) 05/07: Enable web.xml to be used to configure CamelHttpTransportServlet for the servlet extension

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

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

commit 4f95132e32305e0d0877a59f39ffc9e0b8eeddd2
Author: James Netherton <ja...@gmail.com>
AuthorDate: Mon Mar 4 15:54:57 2024 +0000

    Enable web.xml to be used to configure CamelHttpTransportServlet for the servlet extension
    
    Fixes #5835
---
 .../ROOT/pages/reference/extensions/servlet.adoc   | 37 ++++++++++++
 .../servlet/deployment/ServletProcessor.java       | 14 ++++-
 .../component/servlet/test/WebXmlServletTest.java  | 65 ++++++++++++++++++++++
 extensions/servlet/runtime/src/main/doc/usage.adoc | 37 ++++++++++++
 4 files changed, 152 insertions(+), 1 deletion(-)

diff --git a/docs/modules/ROOT/pages/reference/extensions/servlet.adoc b/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
index 848ac1299c..39fca43e78 100644
--- a/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
@@ -117,6 +117,43 @@ from("servlet://goodbye?servletName=my-custom-b")
     .setBody().constant("Goodbye World");
 ----
 
+*Finer control of Servlet configuration*
+
+If you need more control of the Servlet configuration, for example to configure custom init parameters,
+then you can do this with a custom Servlet class through the `jakarta.servlet.annotation.WebServlet` annotation options.
+
+[source,java]
+----
+import jakarta.servlet.annotation.WebServlet;
+import org.apache.camel.component.servlet.CamelHttpTransportServlet;
+
+@WebServlet(
+    urlPatterns = {"/*"},
+    initParams = {
+        @WebInitParam(name = "myParam", value = "myValue")
+    }
+)
+public class MyCustomServlet extends CamelHttpTransportServlet {
+}
+----
+
+Or you can configure the `CamelHttpTransportServlet` using a `web-app` descriptor placed into `src/main/resources/META-INF/web.xml`.
+
+[source,xml]
+----
+<web-app>
+  <servlet>
+    <servlet-name>CamelServlet</servlet-name>
+    <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>CamelServlet</servlet-name>
+    <url-pattern>/services/*</url-pattern>
+  </servlet-mapping>
+</web-app>
+----
+
 
 [id="extensions-servlet-transferexception-option-in-native-mode"]
 == transferException option in native mode
diff --git a/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java b/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
index 6afaf14202..a7ab69969f 100644
--- a/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
+++ b/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
@@ -25,10 +25,14 @@ import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
 import io.quarkus.undertow.deployment.ServletBuildItem;
 import io.quarkus.undertow.deployment.ServletBuildItem.Builder;
+import io.quarkus.undertow.deployment.WebMetadataBuildItem;
 import jakarta.servlet.MultipartConfigElement;
 import org.apache.camel.quarkus.servlet.runtime.CamelServletConfig;
 import org.apache.camel.quarkus.servlet.runtime.CamelServletConfig.ServletConfig;
 import org.apache.camel.quarkus.servlet.runtime.CamelServletConfig.ServletConfig.MultipartConfig;
+import org.jboss.metadata.web.spec.WebMetaData;
+
+import static org.apache.camel.quarkus.servlet.runtime.CamelServletConfig.ServletConfig.DEFAULT_SERVLET_CLASS;
 
 class ServletProcessor {
     private static final String FEATURE = "camel-servlet";
@@ -41,8 +45,16 @@ class ServletProcessor {
     }
 
     @BuildStep
-    void build(BuildProducer<ServletBuildItem> servlet) {
+    void build(BuildProducer<ServletBuildItem> servlet, WebMetadataBuildItem webMetadata) {
         boolean servletCreated = false;
+
+        WebMetaData metaData = webMetadata.getWebMetaData();
+        if (metaData != null && metaData.getServlets() != null) {
+            servletCreated = metaData.getServlets()
+                    .stream()
+                    .anyMatch(meta -> meta.getServletClass().equals(DEFAULT_SERVLET_CLASS));
+        }
+
         if (camelServletConfig.defaultServlet.isValid()) {
             servlet.produce(
                     newServlet(ServletConfig.DEFAULT_SERVLET_NAME, camelServletConfig.defaultServlet));
diff --git a/extensions/servlet/deployment/src/test/java/org/apache/camel/quarkus/component/servlet/test/WebXmlServletTest.java b/extensions/servlet/deployment/src/test/java/org/apache/camel/quarkus/component/servlet/test/WebXmlServletTest.java
new file mode 100644
index 0000000000..1c39eb7954
--- /dev/null
+++ b/extensions/servlet/deployment/src/test/java/org/apache/camel/quarkus/component/servlet/test/WebXmlServletTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.servlet.test;
+
+import io.quarkus.test.QuarkusUnitTest;
+import io.restassured.RestAssured;
+import org.apache.camel.builder.RouteBuilder;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static org.hamcrest.Matchers.equalTo;
+
+public class WebXmlServletTest {
+    static final String WEB_XML = """
+            <web-app>
+                <servlet>
+                  <servlet-name>my-servlet</servlet-name>
+                  <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
+                  <load-on-startup>1</load-on-startup>
+                </servlet>
+
+                <servlet-mapping>
+                  <servlet-name>my-servlet</servlet-name>
+                  <url-pattern>/*</url-pattern>
+                </servlet-mapping>
+            </web-app>
+            """;
+    static final String MESSAGE = "This servlet was configured from web.xml";
+
+    @RegisterExtension
+    static final QuarkusUnitTest CONFIG = new QuarkusUnitTest()
+            .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+                    .addAsResource(new StringAsset(WEB_XML), "META-INF/web.xml"));
+
+    @Test
+    public void noDefaultServlet() throws Exception {
+        RestAssured.when().get("/web/xml").then()
+                .body(equalTo(MESSAGE));
+    }
+
+    public static final class Routes extends RouteBuilder {
+        @Override
+        public void configure() {
+            from("servlet://web/xml?servletName=my-servlet")
+                    .setBody(constant(MESSAGE));
+        }
+    }
+}
diff --git a/extensions/servlet/runtime/src/main/doc/usage.adoc b/extensions/servlet/runtime/src/main/doc/usage.adoc
index 7ad44a268c..6124e06f72 100644
--- a/extensions/servlet/runtime/src/main/doc/usage.adoc
+++ b/extensions/servlet/runtime/src/main/doc/usage.adoc
@@ -64,3 +64,40 @@ from("servlet://greet?servletName=my-custom-a")
 from("servlet://goodbye?servletName=my-custom-b")
     .setBody().constant("Goodbye World");
 ----
+
+*Finer control of Servlet configuration*
+
+If you need more control of the Servlet configuration, for example to configure custom init parameters,
+then you can do this with a custom Servlet class through the `jakarta.servlet.annotation.WebServlet` annotation options.
+
+[source,java]
+----
+import jakarta.servlet.annotation.WebServlet;
+import org.apache.camel.component.servlet.CamelHttpTransportServlet;
+
+@WebServlet(
+    urlPatterns = {"/*"},
+    initParams = {
+        @WebInitParam(name = "myParam", value = "myValue")
+    }
+)
+public class MyCustomServlet extends CamelHttpTransportServlet {
+}
+----
+
+Or you can configure the `CamelHttpTransportServlet` using a `web-app` descriptor placed into `src/main/resources/META-INF/web.xml`.
+
+[source,xml]
+----
+<web-app>
+  <servlet>
+    <servlet-name>CamelServlet</servlet-name>
+    <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>CamelServlet</servlet-name>
+    <url-pattern>/services/*</url-pattern>
+  </servlet-mapping>
+</web-app>
+----


(camel-quarkus) 04/07: Support loadOnStartup, async, forceAwait & executorRef configuration options

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

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

commit e77cbd02a8cd6c947c12f1f93b5e2371bf42ad6e
Author: James Netherton <ja...@gmail.com>
AuthorDate: Mon Mar 4 15:50:48 2024 +0000

    Support loadOnStartup, async, forceAwait & executorRef configuration options
    
    Fixes #5834
---
 .../ROOT/pages/reference/extensions/servlet.adoc   | 48 ++++++++++++
 .../servlet/deployment/ServletProcessor.java       | 19 ++++-
 .../servlet/runtime/CamelServletConfig.java        | 27 +++++++
 .../quarkus/component/servlet/CamelRoute.java      | 34 +++++++--
 .../component/servlet/ServletProducers.java        | 71 +++++++++++++++++
 .../src/main/resources/application.properties      | 50 ++++++++----
 .../component/servlet/CamelServletTest.java        | 89 ++++++++++++++++++----
 7 files changed, 304 insertions(+), 34 deletions(-)

diff --git a/docs/modules/ROOT/pages/reference/extensions/servlet.adoc b/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
index ac9303ed92..848ac1299c 100644
--- a/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
@@ -156,6 +156,30 @@ A servletName as it would be defined in a `web.xml` file or in the `jakarta.serv
 | `string`
 | `CamelServlet`
 
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.load-on-startup]]`link:#quarkus.camel.servlet.load-on-startup[quarkus.camel.servlet.load-on-startup]`
+
+Sets the loadOnStartup priority on the Servlet. A loadOnStartup is a value greater than or equal to zero, indicates to the container the initialization priority of the Servlet. If loadOnStartup is a negative integer, the Servlet is initialized lazily.
+| `java.lang.Integer`
+| `-1`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.async]]`link:#quarkus.camel.servlet.async[quarkus.camel.servlet.async]`
+
+Enables Camel to benefit from asynchronous Servlet support.
+| `boolean`
+| `false`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.force-await]]`link:#quarkus.camel.servlet.force-await[quarkus.camel.servlet.force-await]`
+
+When set to `true` used in conjunction with `quarkus.camel.servlet.async = true`, this will force route processing to run synchronously.
+| `boolean`
+| `false`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.executor-ref]]`link:#quarkus.camel.servlet.executor-ref[quarkus.camel.servlet.executor-ref]`
+
+The name of a bean to configure an optional custom thread pool for handling Camel Servlet processing.
+| `string`
+| 
+
 |icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.multipart.location]]`link:#quarkus.camel.servlet.multipart.location[quarkus.camel.servlet.multipart.location]`
 
 An absolute path to a directory on the file system to store files temporarily while the parts are processed or when the size of the file exceeds the specified file-size-threshold configuration value.
@@ -198,6 +222,30 @@ A servletName as it would be defined in a `web.xml` file or in the `jakarta.serv
 | `string`
 | `CamelServlet`
 
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.load-on-startup]]`link:#quarkus.camel.servlet.-named-servlets-.load-on-startup[quarkus.camel.servlet."named-servlets".load-on-startup]`
+
+Sets the loadOnStartup priority on the Servlet. A loadOnStartup is a value greater than or equal to zero, indicates to the container the initialization priority of the Servlet. If loadOnStartup is a negative integer, the Servlet is initialized lazily.
+| `java.lang.Integer`
+| `-1`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.async]]`link:#quarkus.camel.servlet.-named-servlets-.async[quarkus.camel.servlet."named-servlets".async]`
+
+Enables Camel to benefit from asynchronous Servlet support.
+| `boolean`
+| `false`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.force-await]]`link:#quarkus.camel.servlet.-named-servlets-.force-await[quarkus.camel.servlet."named-servlets".force-await]`
+
+When set to `true` used in conjunction with `quarkus.camel.servlet.async = true`, this will force route processing to run synchronously.
+| `boolean`
+| `false`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.executor-ref]]`link:#quarkus.camel.servlet.-named-servlets-.executor-ref[quarkus.camel.servlet."named-servlets".executor-ref]`
+
+The name of a bean to configure an optional custom thread pool for handling Camel Servlet processing.
+| `string`
+| 
+
 |icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.multipart.location]]`link:#quarkus.camel.servlet.-named-servlets-.multipart.location[quarkus.camel.servlet."named-servlets".multipart.location]`
 
 An absolute path to a directory on the file system to store files temporarily while the parts are processed or when the size of the file exceeds the specified file-size-threshold configuration value.
diff --git a/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java b/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
index 7e21a43adb..6afaf14202 100644
--- a/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
+++ b/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
@@ -63,7 +63,6 @@ class ServletProcessor {
             throw new IllegalStateException(
                     "Map at least one servlet to a path using quarkus.camel.servlet.url-patterns or quarkus.camel.servlet.[your-servlet-name].url-patterns");
         }
-
     }
 
     static ServletBuildItem newServlet(String key, ServletConfig servletConfig) {
@@ -80,6 +79,24 @@ class ServletProcessor {
             builder.addMapping(pattern);
         }
 
+        // NOTE: We only configure loadOnStartup, async & forceAwait if the default values were overridden
+        if (servletConfig.loadOnStartup > -1) {
+            builder.setLoadOnStartup(servletConfig.loadOnStartup);
+        }
+
+        if (servletConfig.async) {
+            builder.setAsyncSupported(servletConfig.async);
+            builder.addInitParam("async", "true");
+        }
+
+        if (servletConfig.forceAwait) {
+            builder.addInitParam("forceAwait", "true");
+        }
+
+        servletConfig.executorRef.ifPresent(executorRef -> {
+            builder.addInitParam("executorRef", executorRef);
+        });
+
         MultipartConfig multipartConfig = servletConfig.multipart;
         if (multipartConfig != null) {
             builder.setMultipartConfig(new MultipartConfigElement(
diff --git a/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java b/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java
index 62ac5c7131..ff20269995 100644
--- a/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java
+++ b/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java
@@ -64,6 +64,33 @@ public final class CamelServletConfig {
         @ConfigItem(defaultValue = DEFAULT_SERVLET_NAME)
         public String servletName;
 
+        /**
+         * Sets the loadOnStartup priority on the Servlet. A loadOnStartup is a value greater than or equal to zero,
+         * indicates to the container the initialization priority of the Servlet. If loadOnStartup is a negative
+         * integer, the Servlet is initialized lazily.
+         */
+        @ConfigItem(defaultValue = "-1")
+        public Integer loadOnStartup;
+
+        /**
+         * Enables Camel to benefit from asynchronous Servlet support.
+         */
+        @ConfigItem(defaultValue = "false")
+        public boolean async;
+
+        /**
+         * When set to {@code true} used in conjunction with {@code quarkus.camel.servlet.async = true}, this will force
+         * route processing to run synchronously.
+         */
+        @ConfigItem(defaultValue = "false")
+        public boolean forceAwait;
+
+        /**
+         * The name of a bean to configure an optional custom thread pool for handling Camel Servlet processing.
+         */
+        @ConfigItem
+        public Optional<String> executorRef;
+
         /**
          * Servlet multipart request configuration.
          */
diff --git a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
index aabc992357..6c8c117c35 100644
--- a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
+++ b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
@@ -18,6 +18,8 @@ package org.apache.camel.quarkus.component.servlet;
 
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.inject.Inject;
+import jakarta.inject.Named;
+import org.apache.camel.Processor;
 import org.apache.camel.builder.RouteBuilder;
 
 @ApplicationScoped
@@ -25,6 +27,10 @@ public class CamelRoute extends RouteBuilder {
     @Inject
     MultiPartProcessor multiPartProcessor;
 
+    @Inject
+    @Named("servletConfigInfoProcessor")
+    Processor servletConfigInfoProcessor;
+
     @Override
     public void configure() {
         // by default the camel-quarkus-rest component sets platform-http
@@ -43,11 +49,14 @@ public class CamelRoute extends RouteBuilder {
         from("servlet://hello?matchOnUriPrefix=true")
                 .setBody(constant("GET: /hello"));
 
-        from("servlet://custom?servletName=my-named-servlet")
+        from("servlet://configuration")
+                .process(servletConfigInfoProcessor);
+
+        from("servlet://custom?servletName=custom-servlet")
                 .setBody(constant("GET: /custom"));
 
-        from("servlet://favorite?servletName=my-favorite-servlet")
-                .setBody(constant("GET: /favorite"));
+        from("servlet://named?servletName=my-named-servlet")
+                .setBody(constant("GET: /my-named-servlet"));
 
         from("direct:echoMethodPath")
                 .setBody().simple("${header.CamelHttpMethod}: ${header.CamelServletContextPath}");
@@ -55,8 +64,23 @@ public class CamelRoute extends RouteBuilder {
         from("servlet://multipart/default?attachmentMultipartBinding=true")
                 .process(multiPartProcessor);
 
-        from("servlet://multipart?servletName=my-named-servlet&attachmentMultipartBinding=true")
+        from("servlet://multipart?servletName=multipart-servlet&attachmentMultipartBinding=true")
                 .process(multiPartProcessor);
-    }
 
+        from("servlet://eager-init?servletName=eager-init-servlet&matchOnUriPrefix=true")
+                .setHeader("servletName").constant("eager-init-servlet")
+                .process(servletConfigInfoProcessor);
+
+        from("servlet://async?servletName=async-servlet&matchOnUriPrefix=true")
+                .setHeader("servletName").constant("async-servlet")
+                .process(servletConfigInfoProcessor);
+
+        from("servlet://force-await?servletName=sync-async-servlet&matchOnUriPrefix=true")
+                .setHeader("servletName").constant("sync-async-servlet")
+                .process(servletConfigInfoProcessor);
+
+        from("servlet://execute?servletName=custom-executor-servlet&matchOnUriPrefix=true")
+                .setHeader("servletName").constant("custom-executor-servlet")
+                .process(servletConfigInfoProcessor);
+    }
 }
diff --git a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/ServletProducers.java b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/ServletProducers.java
new file mode 100644
index 0000000000..292be43a1e
--- /dev/null
+++ b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/ServletProducers.java
@@ -0,0 +1,71 @@
+/*
+ * 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.servlet;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import io.undertow.servlet.api.Deployment;
+import io.undertow.servlet.api.ServletInfo;
+import io.undertow.servlet.core.ManagedServlet;
+import io.undertow.servlet.core.ManagedServlets;
+import io.undertow.servlet.spec.ServletContextImpl;
+import io.vertx.core.json.JsonObject;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Named;
+import jakarta.inject.Singleton;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpServletRequest;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.Processor;
+import org.apache.camel.spi.MimeType;
+
+import static org.apache.camel.quarkus.servlet.runtime.CamelServletConfig.ServletConfig.DEFAULT_SERVLET_NAME;
+
+@ApplicationScoped
+public class ServletProducers {
+    @Singleton
+    @Named("customServletExecutor")
+    public Executor customServletExecutor() {
+        return Executors.newSingleThreadExecutor(r -> new Thread(r, "custom-executor"));
+    }
+
+    @Singleton
+    @Named("servletConfigInfoProcessor")
+    public Processor servletConfigInfoProcessor() {
+        return exchange -> {
+            JsonObject json = new JsonObject();
+            Message message = exchange.getMessage();
+            HttpServletRequest request = message.getHeader(Exchange.HTTP_SERVLET_REQUEST, HttpServletRequest.class);
+            String servletName = message.getHeader("servletName", DEFAULT_SERVLET_NAME, String.class);
+            ServletContext servletContext = request.getServletContext();
+            Deployment deployment = ((ServletContextImpl) servletContext).getDeployment();
+            ManagedServlets servlets = deployment.getServlets();
+            ManagedServlet servlet = servlets.getManagedServlet(servletName);
+            ServletInfo servletInfo = servlet.getServletInfo();
+
+            json.put("isAsync", request.isAsyncSupported());
+            json.put("threadName", Thread.currentThread().getName());
+            json.put("loadOnStartup", servletInfo.getLoadOnStartup());
+            json.put("initParams", servletInfo.getInitParams());
+
+            message.setHeader(Exchange.CONTENT_TYPE, MimeType.JSON.type());
+            message.setBody(json.encode());
+        };
+    }
+}
diff --git a/integration-tests/servlet/src/main/resources/application.properties b/integration-tests/servlet/src/main/resources/application.properties
index 0251262894..06920af73f 100644
--- a/integration-tests/servlet/src/main/resources/application.properties
+++ b/integration-tests/servlet/src/main/resources/application.properties
@@ -15,17 +15,39 @@
 ## limitations under the License.
 ## ---------------------------------------------------------------------------
 
-#
-# Quarkus :: Camel :: Servlet
-#
-quarkus.camel.servlet.url-patterns=/folder-1/*,/folder-2/*
-
-quarkus.camel.servlet.my-named-servlet.url-patterns=/my-named-folder/*
-quarkus.camel.servlet.my-named-servlet.servlet-class=org.apache.camel.quarkus.component.servlet.CustomServlet
-quarkus.camel.servlet.my-named-servlet.multipart.location=${java.io.tmpdir}/my-named-servlet
-quarkus.camel.servlet.my-named-servlet.multipart.max-file-size=11
-quarkus.camel.servlet.my-named-servlet.multipart.max-request-size=11
-quarkus.camel.servlet.my-named-servlet.multipart.file-size-threshold=5
-
-quarkus.camel.servlet.ignored-key.servlet-name=my-favorite-servlet
-quarkus.camel.servlet.ignored-key.url-patterns=/my-favorite-folder/*
+# Default servlet configuration
+quarkus.camel.servlet.url-patterns=/folder-1/*,/folder-2/*,/debug/*
+
+# Explicit definition of the servlet name
+quarkus.camel.servlet.ignored-key.servlet-name=my-named-servlet
+quarkus.camel.servlet.ignored-key.url-patterns=/my-named-folder/*
+
+# Custom servlet class
+quarkus.camel.servlet.custom-servlet.url-patterns=/my-custom-folder/*
+quarkus.camel.servlet.custom-servlet.servlet-class=org.apache.camel.quarkus.component.servlet.CustomServlet
+
+# Servlet configured with multipart support
+quarkus.camel.servlet.multipart-servlet.url-patterns=/multipart-servlet/*
+quarkus.camel.servlet.multipart-servlet.multipart.location=${java.io.tmpdir}/my-named-servlet
+quarkus.camel.servlet.multipart-servlet.multipart.max-file-size=11
+quarkus.camel.servlet.multipart-servlet.multipart.max-request-size=11
+quarkus.camel.servlet.multipart-servlet.multipart.file-size-threshold=5
+
+# Servlet configured for eager initialization
+quarkus.camel.servlet.eager-init-servlet.url-patterns=/eager-init-servlet/*
+quarkus.camel.servlet.eager-init-servlet.load-on-startup=1
+
+# Servlet configured for async processing
+quarkus.camel.servlet.async-servlet.url-patterns=/async-servlet/*
+quarkus.camel.servlet.async-servlet.async=true
+
+# Servlet configured for async + sync processing
+quarkus.camel.servlet.sync-async-servlet.url-patterns=/sync-async-servlet/*
+quarkus.camel.servlet.sync-async-servlet.async=true
+quarkus.camel.servlet.sync-async-servlet.force-await=true
+
+# Servlet with custom executor
+quarkus.camel.servlet.custom-executor-servlet.url-patterns=/custom-executor/*
+quarkus.camel.servlet.custom-executor-servlet.async=true
+quarkus.camel.servlet.custom-executor-servlet.executor-ref=customServletExecutor
+
diff --git a/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java b/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
index 593e56cb9b..73bdd78dbe 100644
--- a/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
+++ b/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
@@ -20,36 +20,52 @@ import java.nio.charset.StandardCharsets;
 
 import io.quarkus.test.junit.QuarkusTest;
 import io.restassured.RestAssured;
-import org.hamcrest.core.IsEqual;
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.Matchers.anEmptyMap;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
 import static org.hamcrest.Matchers.oneOf;
+import static org.hamcrest.Matchers.startsWith;
+import static org.hamcrest.core.IsEqual.equalTo;
 
 @QuarkusTest
 public class CamelServletTest {
+    @Test
+    public void defaultConfiguration() {
+        RestAssured.get("/debug/configuration")
+                .then()
+                .body(
+                        "isAsync", equalTo(false),
+                        "threadName", startsWith("executor-thread"),
+                        "initParams", anEmptyMap(),
+                        "loadOnStartup", nullValue());
+    }
 
     @Test
     public void multiplePaths() {
-        RestAssured.when().get("/folder-1/rest-get").then().body(IsEqual.equalTo("GET: /rest-get"));
-        RestAssured.when().get("/folder-2/rest-get").then().body(IsEqual.equalTo("GET: /rest-get"));
-        RestAssured.when().post("/folder-1/rest-post").then().body(IsEqual.equalTo("POST: /rest-post"));
-        RestAssured.when().post("/folder-2/rest-post").then().body(IsEqual.equalTo("POST: /rest-post"));
-        RestAssured.when().get("/folder-1/hello").then().body(IsEqual.equalTo("GET: /hello"));
-        RestAssured.when().get("/folder-2/hello").then().body(IsEqual.equalTo("GET: /hello"));
+        RestAssured.get("/folder-1/rest-get").then().body(equalTo("GET: /rest-get"));
+        RestAssured.get("/folder-2/rest-get").then().body(equalTo("GET: /rest-get"));
+        RestAssured.post("/folder-1/rest-post").then().body(equalTo("POST: /rest-post"));
+        RestAssured.post("/folder-2/rest-post").then().body(equalTo("POST: /rest-post"));
+        RestAssured.get("/folder-1/hello").then().body(equalTo("GET: /hello"));
+        RestAssured.get("/folder-2/hello").then().body(equalTo("GET: /hello"));
     }
 
     @Test
-    public void namedWithservletClass() {
-        RestAssured.when().get("/my-named-folder/custom").then()
-                .body(IsEqual.equalTo("GET: /custom"))
-                .and().header("x-servlet-class-name", CustomServlet.class.getName());
+    public void namedWithServletClass() {
+        RestAssured.get("/my-custom-folder/custom")
+                .then()
+                .body(equalTo("GET: /custom"))
+                .and()
+                .header("x-servlet-class-name", CustomServlet.class.getName());
     }
 
     @Test
     public void ignoredKey() {
-        RestAssured.when().get("/my-favorite-folder/favorite").then()
-                .body(IsEqual.equalTo("GET: /favorite"));
+        RestAssured.get("/my-named-folder/named")
+                .then()
+                .body(equalTo("GET: /my-named-servlet"));
     }
 
     @Test
@@ -76,9 +92,54 @@ public class CamelServletTest {
         // Request body exceeding the limits defined on the multipart config
         RestAssured.given()
                 .multiPart("test-multipart", "file", body.repeat(10).getBytes(StandardCharsets.UTF_8))
-                .post("/my-named-folder/multipart")
+                .post("/multipart-servlet/multipart")
                 .then()
                 // TODO: Expect 413 only - https://github.com/apache/camel-quarkus/issues/5830
                 .statusCode(oneOf(413, 500));
     }
+
+    @Test
+    public void eagerInitServlet() {
+        RestAssured.get("/eager-init-servlet/eager-init")
+                .then()
+                .body(
+                        "isAsync", equalTo(false),
+                        "threadName", startsWith("executor-thread"),
+                        "initParams", anEmptyMap(),
+                        "loadOnStartup", equalTo(1));
+    }
+
+    @Test
+    public void asyncServlet() {
+        RestAssured.get("/async-servlet/async")
+                .then()
+                .body(
+                        "isAsync", equalTo(true),
+                        "threadName", startsWith("executor-thread"),
+                        "initParams.async", equalTo("true"),
+                        "loadOnStartup", nullValue());
+    }
+
+    @Test
+    public void asyncWithForceAwaitServlet() {
+        RestAssured.get("/sync-async-servlet/force-await")
+                .then()
+                .body(
+                        "isAsync", equalTo(true),
+                        "threadName", startsWith("executor-thread"),
+                        "initParams.async", equalTo("true"),
+                        "initParams.forceAwait", equalTo("true"),
+                        "loadOnStartup", nullValue());
+    }
+
+    @Test
+    public void asyncWithCustomExecutor() {
+        RestAssured.get("/custom-executor/execute/get")
+                .then()
+                .body(
+                        "isAsync", equalTo(true),
+                        "threadName", equalTo("custom-executor"),
+                        "initParams.async", equalTo("true"),
+                        "loadOnStartup", nullValue());
+    }
 }


(camel-quarkus) 01/07: Add multipart configuration options to servlet extension

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

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

commit 3228a32d49a1e4550601ea24a30229548583f5b1
Author: James Netherton <ja...@gmail.com>
AuthorDate: Fri Mar 1 13:30:51 2024 +0000

    Add multipart configuration options to servlet extension
    
    Fixes #5326
---
 .../ROOT/pages/reference/extensions/servlet.adoc   | 48 ++++++++++++++++++++++
 .../servlet/deployment/ServletProcessor.java       | 11 +++++
 .../servlet/runtime/CamelServletConfig.java        | 36 ++++++++++++++++
 integration-tests/servlet/pom.xml                  | 17 ++++++++
 .../quarkus/component/servlet/CamelRoute.java      | 17 ++++----
 .../component/servlet/MultiPartProcessor.java      | 37 +++++++++++++++++
 .../src/main/resources/application.properties      |  6 +++
 .../component/servlet/CamelServletTest.java        | 41 ++++++++++++++++--
 8 files changed, 202 insertions(+), 11 deletions(-)

diff --git a/docs/modules/ROOT/pages/reference/extensions/servlet.adoc b/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
index 39c76cf246..fd56d2364a 100644
--- a/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/servlet.adoc
@@ -83,6 +83,30 @@ A servletName as it would be defined in a `web.xml` file or in the `jakarta.serv
 | `string`
 | `CamelServlet`
 
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.multipart.location]]`link:#quarkus.camel.servlet.multipart.location[quarkus.camel.servlet.multipart.location]`
+
+An absolute path to a directory on the file system to store files temporarily while the parts are processed or when the size of the file exceeds the specified file-size-threshold configuration value.
+| `string`
+| `${java.io.tmpdir}`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.multipart.max-file-size]]`link:#quarkus.camel.servlet.multipart.max-file-size[quarkus.camel.servlet.multipart.max-file-size]`
+
+The maximum size allowed in bytes for uploaded files. The default size (-1) allows an unlimited size.
+| `long`
+| `-1`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.multipart.max-request-size]]`link:#quarkus.camel.servlet.multipart.max-request-size[quarkus.camel.servlet.multipart.max-request-size]`
+
+The maximum size allowed in bytes for a multipart/form-data request. The default size (-1) allows an unlimited size.
+| `long`
+| `-1`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.multipart.file-size-threshold]]`link:#quarkus.camel.servlet.multipart.file-size-threshold[quarkus.camel.servlet.multipart.file-size-threshold]`
+
+The file size in bytes after which the file will be temporarily stored on disk.
+| `int`
+| `0`
+
 |icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.url-patterns]]`link:#quarkus.camel.servlet.-named-servlets-.url-patterns[quarkus.camel.servlet."named-servlets".url-patterns]`
 
 A comma separated list of path patterns under which the CamelServlet should be accessible. Example path patterns: `/++*++`, `/services/++*++`
@@ -100,6 +124,30 @@ A fully qualified name of a servlet class to serve paths that match `url-pattern
 A servletName as it would be defined in a `web.xml` file or in the `jakarta.servlet.annotation.WebServlet++#++name()` annotation.
 | `string`
 | `CamelServlet`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.multipart.location]]`link:#quarkus.camel.servlet.-named-servlets-.multipart.location[quarkus.camel.servlet."named-servlets".multipart.location]`
+
+An absolute path to a directory on the file system to store files temporarily while the parts are processed or when the size of the file exceeds the specified file-size-threshold configuration value.
+| `string`
+| `${java.io.tmpdir}`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.multipart.max-file-size]]`link:#quarkus.camel.servlet.-named-servlets-.multipart.max-file-size[quarkus.camel.servlet."named-servlets".multipart.max-file-size]`
+
+The maximum size allowed in bytes for uploaded files. The default size (-1) allows an unlimited size.
+| `long`
+| `-1`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.multipart.max-request-size]]`link:#quarkus.camel.servlet.-named-servlets-.multipart.max-request-size[quarkus.camel.servlet."named-servlets".multipart.max-request-size]`
+
+The maximum size allowed in bytes for a multipart/form-data request. The default size (-1) allows an unlimited size.
+| `long`
+| `-1`
+
+|icon:lock[title=Fixed at build time] [[quarkus.camel.servlet.-named-servlets-.multipart.file-size-threshold]]`link:#quarkus.camel.servlet.-named-servlets-.multipart.file-size-threshold[quarkus.camel.servlet."named-servlets".multipart.file-size-threshold]`
+
+The file size in bytes after which the file will be temporarily stored on disk.
+| `int`
+| `0`
 |===
 
 [.configuration-legend]
diff --git a/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java b/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
index c6d159f413..308f5489db 100644
--- a/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
+++ b/extensions/servlet/deployment/src/main/java/org/apache/camel/quarkus/component/servlet/deployment/ServletProcessor.java
@@ -26,8 +26,10 @@ import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.builditem.FeatureBuildItem;
 import io.quarkus.undertow.deployment.ServletBuildItem;
 import io.quarkus.undertow.deployment.ServletBuildItem.Builder;
+import jakarta.servlet.MultipartConfigElement;
 import org.apache.camel.quarkus.servlet.runtime.CamelServletConfig;
 import org.apache.camel.quarkus.servlet.runtime.CamelServletConfig.ServletConfig;
+import org.apache.camel.quarkus.servlet.runtime.CamelServletConfig.ServletConfig.MultipartConfig;
 
 class ServletProcessor {
     private static final String FEATURE = "camel-servlet";
@@ -81,6 +83,15 @@ class ServletProcessor {
             builder.addMapping(pattern);
         }
 
+        MultipartConfig multipartConfig = servletConfig.multipart;
+        if (multipartConfig != null) {
+            builder.setMultipartConfig(new MultipartConfigElement(
+                    multipartConfig.location,
+                    multipartConfig.maxFileSize,
+                    multipartConfig.maxRequestSize,
+                    multipartConfig.fileSizeThreshold));
+        }
+
         return builder.build();
     }
 }
diff --git a/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java b/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java
index 311b95e47f..62ac5c7131 100644
--- a/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java
+++ b/extensions/servlet/runtime/src/main/java/org/apache/camel/quarkus/servlet/runtime/CamelServletConfig.java
@@ -64,6 +64,11 @@ public final class CamelServletConfig {
         @ConfigItem(defaultValue = DEFAULT_SERVLET_NAME)
         public String servletName;
 
+        /**
+         * Servlet multipart request configuration.
+         */
+        public MultipartConfig multipart;
+
         /**
          * @return {@code true} if this {@link ServletConfig} is valid as a whole. This currently translates to
          *         {@link #urlPatterns} being non-empty because {@link #servletClass} and {@link #servletName} have
@@ -87,6 +92,37 @@ public final class CamelServletConfig {
             return DEFAULT_SERVLET_NAME.equals(servletName) ? key : servletName;
         }
 
+        /**
+         * Servlet multipart request configuration.
+         */
+        @ConfigGroup
+        public static class MultipartConfig {
+            /**
+             * An absolute path to a directory on the file system to store files temporarily while the parts are
+             * processed or when the size of the file exceeds the specified file-size-threshold configuration value.
+             */
+            @ConfigItem(defaultValue = "${java.io.tmpdir}")
+            public String location;
+
+            /**
+             * The maximum size allowed in bytes for uploaded files. The default size (-1) allows an unlimited size.
+             */
+            @ConfigItem(defaultValue = "-1")
+            public long maxFileSize;
+
+            /**
+             * The maximum size allowed in bytes for a multipart/form-data request. The default size (-1) allows an unlimited
+             * size.
+             */
+            @ConfigItem(defaultValue = "-1")
+            public long maxRequestSize;
+
+            /**
+             * The file size in bytes after which the file will be temporarily stored on disk.
+             */
+            @ConfigItem(defaultValue = "0")
+            public int fileSizeThreshold;
+        }
     }
 
 }
diff --git a/integration-tests/servlet/pom.xml b/integration-tests/servlet/pom.xml
index c488681fc9..d5337b30f8 100644
--- a/integration-tests/servlet/pom.xml
+++ b/integration-tests/servlet/pom.xml
@@ -31,6 +31,10 @@
     <description>Integration tests for Camel Servlet component</description>
 
     <dependencies>
+        <dependency>
+            <groupId>org.apache.camel.quarkus</groupId>
+            <artifactId>camel-quarkus-attachments</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.camel.quarkus</groupId>
             <artifactId>camel-quarkus-core-cloud</artifactId>
@@ -99,6 +103,19 @@
             </activation>
             <dependencies>
                 <!-- 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-attachments-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-core-cloud-deployment</artifactId>
diff --git a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
index 7e984433ad..aabc992357 100644
--- a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
+++ b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/CamelRoute.java
@@ -17,12 +17,13 @@
 package org.apache.camel.quarkus.component.servlet;
 
 import jakarta.enterprise.context.ApplicationScoped;
-import org.apache.camel.Exchange;
-import org.apache.camel.Processor;
+import jakarta.inject.Inject;
 import org.apache.camel.builder.RouteBuilder;
 
 @ApplicationScoped
 public class CamelRoute extends RouteBuilder {
+    @Inject
+    MultiPartProcessor multiPartProcessor;
 
     @Override
     public void configure() {
@@ -49,13 +50,13 @@ public class CamelRoute extends RouteBuilder {
                 .setBody(constant("GET: /favorite"));
 
         from("direct:echoMethodPath")
-                .process(new Processor() {
-                    @Override
-                    public void process(Exchange exchange) throws Exception {
-                        exchange.toString();
-                    }
-                })
                 .setBody().simple("${header.CamelHttpMethod}: ${header.CamelServletContextPath}");
+
+        from("servlet://multipart/default?attachmentMultipartBinding=true")
+                .process(multiPartProcessor);
+
+        from("servlet://multipart?servletName=my-named-servlet&attachmentMultipartBinding=true")
+                .process(multiPartProcessor);
     }
 
 }
diff --git a/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/MultiPartProcessor.java b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/MultiPartProcessor.java
new file mode 100644
index 0000000000..7d0cff8801
--- /dev/null
+++ b/integration-tests/servlet/src/main/java/org/apache/camel/quarkus/component/servlet/MultiPartProcessor.java
@@ -0,0 +1,37 @@
+/*
+ * 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.servlet;
+
+import jakarta.activation.DataHandler;
+import jakarta.inject.Singleton;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+import org.apache.camel.attachment.AttachmentMessage;
+
+@Singleton
+public class MultiPartProcessor implements Processor {
+    @Override
+    public void process(Exchange exchange) throws Exception {
+        AttachmentMessage message = exchange.getMessage(AttachmentMessage.class);
+        DataHandler file = message.getAttachment("file");
+        if (file == null) {
+            throw new IllegalStateException("Attachment named 'file' is not present");
+        }
+
+        message.setBody(file.getInputStream());
+    }
+}
diff --git a/integration-tests/servlet/src/main/resources/application.properties b/integration-tests/servlet/src/main/resources/application.properties
index 7f11f67737..0251262894 100644
--- a/integration-tests/servlet/src/main/resources/application.properties
+++ b/integration-tests/servlet/src/main/resources/application.properties
@@ -19,7 +19,13 @@
 # Quarkus :: Camel :: Servlet
 #
 quarkus.camel.servlet.url-patterns=/folder-1/*,/folder-2/*
+
 quarkus.camel.servlet.my-named-servlet.url-patterns=/my-named-folder/*
 quarkus.camel.servlet.my-named-servlet.servlet-class=org.apache.camel.quarkus.component.servlet.CustomServlet
+quarkus.camel.servlet.my-named-servlet.multipart.location=${java.io.tmpdir}/my-named-servlet
+quarkus.camel.servlet.my-named-servlet.multipart.max-file-size=11
+quarkus.camel.servlet.my-named-servlet.multipart.max-request-size=11
+quarkus.camel.servlet.my-named-servlet.multipart.file-size-threshold=5
+
 quarkus.camel.servlet.ignored-key.servlet-name=my-favorite-servlet
 quarkus.camel.servlet.ignored-key.url-patterns=/my-favorite-folder/*
diff --git a/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java b/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
index 24e7672309..593e56cb9b 100644
--- a/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
+++ b/integration-tests/servlet/src/test/java/org/apache/camel/quarkus/component/servlet/CamelServletTest.java
@@ -16,16 +16,21 @@
  */
 package org.apache.camel.quarkus.component.servlet;
 
+import java.nio.charset.StandardCharsets;
+
 import io.quarkus.test.junit.QuarkusTest;
 import io.restassured.RestAssured;
 import org.hamcrest.core.IsEqual;
 import org.junit.jupiter.api.Test;
 
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.oneOf;
+
 @QuarkusTest
 public class CamelServletTest {
 
     @Test
-    public void multiplePaths() throws Throwable {
+    public void multiplePaths() {
         RestAssured.when().get("/folder-1/rest-get").then().body(IsEqual.equalTo("GET: /rest-get"));
         RestAssured.when().get("/folder-2/rest-get").then().body(IsEqual.equalTo("GET: /rest-get"));
         RestAssured.when().post("/folder-1/rest-post").then().body(IsEqual.equalTo("POST: /rest-post"));
@@ -35,15 +40,45 @@ public class CamelServletTest {
     }
 
     @Test
-    public void namedWithservletClass() throws Throwable {
+    public void namedWithservletClass() {
         RestAssured.when().get("/my-named-folder/custom").then()
                 .body(IsEqual.equalTo("GET: /custom"))
                 .and().header("x-servlet-class-name", CustomServlet.class.getName());
     }
 
     @Test
-    public void ignoredKey() throws Throwable {
+    public void ignoredKey() {
         RestAssured.when().get("/my-favorite-folder/favorite").then()
                 .body(IsEqual.equalTo("GET: /favorite"));
     }
+
+    @Test
+    public void multipartDefaultConfig() {
+        String body = "Hello World";
+        RestAssured.given()
+                .multiPart("file", "file", body.getBytes(StandardCharsets.UTF_8))
+                .post("/folder-1/multipart/default")
+                .then()
+                .statusCode(200)
+                .body(is(body));
+    }
+
+    @Test
+    public void multipartCustomConfig() {
+        String body = "Hello World";
+        RestAssured.given()
+                .multiPart("file", "file", body.getBytes(StandardCharsets.UTF_8))
+                .post("/folder-1/multipart/default")
+                .then()
+                .statusCode(200)
+                .body(is(body));
+
+        // Request body exceeding the limits defined on the multipart config
+        RestAssured.given()
+                .multiPart("test-multipart", "file", body.repeat(10).getBytes(StandardCharsets.UTF_8))
+                .post("/my-named-folder/multipart")
+                .then()
+                // TODO: Expect 413 only - https://github.com/apache/camel-quarkus/issues/5830
+                .statusCode(oneOf(413, 500));
+    }
 }