You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2024/03/06 04:52:35 UTC

(camel) branch main updated: CAMEL-20519: Servlet documentation improvements (#13385)

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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 0958ebb0f2a CAMEL-20519: Servlet documentation improvements (#13385)
0958ebb0f2a is described below

commit 0958ebb0f2ab51cb5576b62eb27c6be50aec7f17
Author: James Netherton <ja...@users.noreply.github.com>
AuthorDate: Wed Mar 6 04:52:30 2024 +0000

    CAMEL-20519: Servlet documentation improvements (#13385)
---
 .../src/main/docs/servlet-component.adoc           | 379 +++++----------------
 1 file changed, 94 insertions(+), 285 deletions(-)

diff --git a/components/camel-servlet/src/main/docs/servlet-component.adoc b/components/camel-servlet/src/main/docs/servlet-component.adoc
index 7bf03d341b4..2400c355df6 100644
--- a/components/camel-servlet/src/main/docs/servlet-component.adoc
+++ b/components/camel-servlet/src/main/docs/servlet-component.adoc
@@ -84,114 +84,24 @@ Therefore, it should be used only as input into your Camel routes. To
 issue HTTP requests against other HTTP endpoints, use the
 xref:http-component.adoc[HTTP Component].
 
-== Putting Camel JARs in the app server boot classpath
+== Example `CamelHttpTransportServlet` configuration
 
-If you put the Camel JARs such as `camel-core`, `camel-servlet`, etc. in
-the boot classpath of your application server (e.g., usually in its lib
-directory), then mind that the servlet mapping list is now shared
-between multiple deployed Camel application in the app server.
+=== Camel Spring Boot / Camel Quarkus
 
-Mind that putting Camel JARs in the boot classpath of the application
-server is generally not best practice!
+When running camel-servlet on the Spring Boot or Camel Quarkus runtimes, `CamelHttpTransportServlet` is configured for
+you automatically and is driven by configuration properties. Refer to the camel-servlet configuration documentation for these runtimes.
 
-So in those situations you *must* define a custom and unique servlet
-name in each of your Camel applications, e.g., in the `web.xml` define:
+=== Servlet container / application server
 
-[source,xml]
----------------------------------------------------------------------------------------------
-<servlet>
-  <servlet-name>MyServlet</servlet-name>
-  <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
-  <load-on-startup>1</load-on-startup>
-</servlet>
-
-<servlet-mapping>
-  <servlet-name>MyServlet</servlet-name>
-  <url-pattern>/*</url-pattern>
-</servlet-mapping>
----------------------------------------------------------------------------------------------
-
-And in your Camel endpoints then include the servlet name as well
-
-[source,xml]
----------------------------------------------------
-<route>
-  <from uri="servlet://foo?servletName=MyServlet"/>
-  ...
-</route>
----------------------------------------------------
-
-Camel detects this duplicate and fails to
-start the application. You can control to ignore this duplicate by
-setting the servlet init-parameter ignoreDuplicateServletName to true as
-follows:
-
-[source,xml]
------------------------------------------------------------------------------------------------
-  <servlet>
-    <servlet-name>CamelServlet</servlet-name>
-    <display-name>Camel Http Transport Servlet</display-name>
-    <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
-    <init-param>
-      <param-name>ignoreDuplicateServletName</param-name>
-      <param-value>true</param-value>
-    </init-param>
-  </servlet>
------------------------------------------------------------------------------------------------
-
-But it is *strongly advised* to use unique `servlet-name` for each Camel
-application to avoid this duplication clash, as well any unforeseen
-side effects.
-
-== Servlet >= 3.0 and AsyncContext
-
-To enable Camel to benefit from Servlet asynchronous support, you must:
+If you're running Camel standalone on a Servlet container or application server, you can use `web.xml` to configure `CamelHttpTransportServlet`.
 
-. Enable `async` boolean init parameter by setting it to `true`
-. Without more configuration it will reuse servlet thread pool to handle the processing, but you can set `executorRef` to an executor service reference to let another pool handle the processing of the exchange.
-It will use camel context registry by default and potentially fallback on an executor policy or default executor service if no bean matches this name.
-
-Note that to force camel to get back pre-3.7.0 behavior which was to wait in another container background thread, you can set `forceAwait` boolean init parameter to `true`.
-
-Sample async configuration:
-
-[source,xml]
------------------------------------------------------------------------------------------------
-  <servlet>
-    <servlet-name>CamelServlet</servlet-name>
-    <display-name>Camel Http Transport Servlet</display-name>
-    <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
-    <init-param>
-      <param-name>async</param-name>
-      <param-value>true</param-value>
-    </init-param>
-    <init-param>
-      <param-name>executorRef</param-name>
-      <param-value>my-threads</param-value>
-    </init-param>
-  </servlet>
------------------------------------------------------------------------------------------------
-
-
-== Sample
-
-Use xref:servlet-component.adoc[Servlet] in Spring web applications for simplicity's sake.
-In this sample, we define a route that exposes an HTTP service at
-http://localhost:8080/camel/services/hello.
-
-First, you need to publish the
-https://github.com/apache/camel/blob/main/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/CamelHttpTransportServlet.java[CamelHttpTransportServlet]
-through the normal Web Container, or OSGi Service. Use the `Web.xml` file to publish the
-https://github.com/apache/camel/blob/main/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/CamelHttpTransportServlet.java[CamelHttpTransportServlet]
-as follows:
+For example, to define a route that exposes an HTTP service under the path `/services`.
 
 [source,xml]
 -------------------------------------------------------------------------
 <web-app>
-
   <servlet>
     <servlet-name>CamelServlet</servlet-name>
-    <display-name>Camel Http Transport Servlet</display-name>
     <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
   </servlet>
 
@@ -199,226 +109,125 @@ as follows:
     <servlet-name>CamelServlet</servlet-name>
     <url-pattern>/services/*</url-pattern>
   </servlet-mapping>
-
 </web-app>
 -------------------------------------------------------------------------
 
-
-Then you can define your route as follows:
+== Example route
 
 [source,java]
 -------------------------------------------------------------------------
-from("servlet:hello?matchOnUriPrefix=true").process(new Processor() {
+from("servlet:hello").process(new Processor() {
     public void process(Exchange exchange) throws Exception {
-        String contentType = exchange.getIn().getHeader(Exchange.CONTENT_TYPE, String.class);
-        String path = exchange.getIn().getHeader(Exchange.HTTP_URI, String.class);
-        path = path.substring(path.lastIndexOf("/"));
-
-        assertEquals(CONTENT_TYPE, contentType, "Get a wrong content type");
-        // assert camel http header
-        String charsetEncoding = exchange.getIn().getHeader(Exchange.HTTP_CHARACTER_ENCODING, String.class);
-        assertEquals(charsetEncoding, "Get a wrong charset name from the message heaer", "UTF-8");
-        // assert exchange charset
-        assertEquals(exchange.getProperty(Exchange.CHARSET_NAME), "Get a wrong charset naem from the exchange property", "UTF-8");
-        exchange.getOut().setHeader(Exchange.CONTENT_TYPE, contentType + "; charset=UTF-8");
-        exchange.getOut().setHeader("PATH", path);
-        exchange.getOut().setBody("<b>Hello World</b>");
+        // Access HTTP headers sent by the client
+        Message message = exchange.getMessage();
+        String contentType = message.getHeader(Exchange.CONTENT_TYPE, String.class);
+        String httpUri = message.getHeader(Exchange.HTTP_URI, String.class);
+
+        // Set the response body
+        message.setBody("<b>Got Content-Type: " + contentType = ", URI: " + httpUri + "</b>");
     }
 });
 -------------------------------------------------------------------------
 
-[NOTE]
-====
-*Specify the relative path for camel-servlet endpoint*
-
-Since we are binding the HTTP transport with a published servlet, and we
-don't know the servlet's application context path, the `camel-servlet`
-endpoint uses the relative path to specify the endpoint's URL. A client
-can access the `camel-servlet` endpoint through the servlet publish
-address: `("http://localhost:8080/camel/services") + RELATIVE_PATH("/hello")`
-====
+== Camel Servlet HTTP endpoint path
 
-=== Sample when using Spring
+The full path where the camel-servlet HTTP endpoint is published depends on:
 
-When using the Servlet component in a Camel/Spring application, it's
-often required to load the Spring ApplicationContext _after_ the Servlet
-component has started. This can be accomplished by using Spring's
-`ContextLoaderServlet` instead of `ContextLoaderListener`. In that case
-you'll need to start `ContextLoaderServlet` after
-https://github.com/apache/camel/blob/main/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/CamelHttpTransportServlet.java[CamelHttpTransportServlet]
-like this:
+* The Servlet application context path
+* The configured Servlet mapping URL patterns
+* The camel-servlet endpoint URI context path
 
-[source,xml]
--------------------------------------------------------------------------
-<web-app>
-    <servlet>
-        <servlet-name>CamelServlet</servlet-name>
-        <servlet-class>
-            org.apache.camel.component.servlet.CamelHttpTransportServlet
-        </servlet-class>
-        <load-on-startup>1</load-on-startup>
-    </servlet>
-    <servlet>
-        <servlet-name>SpringApplicationContext</servlet-name>
-        <servlet-class>
-            org.springframework.web.context.ContextLoaderServlet
-        </servlet-class>
-        <load-on-startup>2</load-on-startup>
-    </servlet>
-<web-app>
--------------------------------------------------------------------------
+For example, if the application context path is `/camel` and `CamelHttpTransportServlet` is configured with a URL mapping of `/services/*`.
+Then a Camel route like `from("servlet:hello")` would be published to a path like http://localhost:8080/camel/services/hello.
 
-=== Sample when using OSGi
+== Servlet asynchronous support
 
-You can publish the
-https://github.com/apache/camel/blob/main/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/CamelHttpTransportServlet.java[CamelHttpTransportServlet]
-as an OSGi service with Blueprint like this:
+To enable Camel to benefit from Servlet asynchronous support, you must enable the `async` boolean init parameter by setting it to `true`.
 
-[source,xml]
--------------------------------------------------------------------------
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-           xsi:schemaLocation="
-           http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
-
-    <bean id="camelServlet" class="org.apache.camel.component.servlet.CamelHttpTransportServlet" />
-
-    <!--
-        Enlist it in OSGi service registry.
-        This will cause two things:
-        1) As the pax web whiteboard extender is running the CamelServlet will
-           be registered with the OSGi HTTP Service
-        2) It will trigger the HttpRegistry in other bundles so the servlet is
-           made known there too
-    -->
-    <service ref="camelServlet">
-        <interfaces>
-            <value>javax.servlet.Servlet</value>
-            <value>org.apache.camel.http.common.CamelServlet</value>
-        </interfaces>
-        <service-properties>
-            <entry key="alias" value="/camel/services" />
-            <entry key="matchOnUriPrefix" value="true" />
-            <entry key="servlet-name" value="CamelServlet" />
-        </service-properties>
-    </service>
-
-</blueprint>
--------------------------------------------------------------------------
+By default, the servlet thread pool is used for exchange processing. However, to use a custom thread pool, you can configure an init parameter named `executorRef` with the String value set to the name of a bean bound to the Camel registry of type `Executor`.
+If no bean was found in the Camel registry, the Servlet component will attempt to fall back on an executor policy or default executor service.
 
-Then use this service in your Camel route like this:
+If you want to force exchange processing to wait in another container background thread, you can set the `forceAwait` boolean init parameter to `true`.
 
-[source,xml]
--------------------------------------------------------------------------
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-           xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0"
-           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-           xsi:schemaLocation="
-           http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">
+On the Camel Quarkus runtime, these init parameters can be set via configuration properties. Refer to the Camel Quarkus Servlet extension documentation for more information.
 
-    <reference id="servletref" ext:proxy-method="classes" interface="org.apache.camel.http.common.CamelServlet">
-        <reference-listener ref="httpRegistry" bind-method="register" unbind-method="unregister" />
-    </reference>
+On other runtimes you can configure these parameters in `web.xml` as follows.
 
-    <bean id="httpRegistry" class="org.apache.camel.component.servlet.DefaultHttpRegistry" />
+[source,xml]
+-----------------------------------------------------------------------------------------------
+<web-app>
+    <servlet>
+        <servlet-name>CamelServlet</servlet-name>
+        <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
+        <init-param>
+            <param-name>async</param-name>
+            <param-value>true</param-value>
+        </init-param>
+        <init-param>
+            <param-name>executorRef</param-name>
+            <param-value>my-custom-thread-pool</param-value>
+        </init-param>
+    </servlet>
 
-    <bean id="servlet" class="org.apache.camel.component.servlet.ServletComponent">
-        <property name="httpRegistry" ref="httpRegistry" />
-    </bean>
+    <servlet-mapping>
+        <servlet-name>CamelServlet</servlet-name>
+        <url-pattern>/services/*</url-pattern>
+    </servlet-mapping>
+</web-app>
+-----------------------------------------------------------------------------------------------
 
-    <bean id="servletProcessor" class="org.apache.camel.example.servlet.ServletProcessor" />
+== Camel JARs on an application server boot classpath
 
-    <camelContext xmlns="http://camel.apache.org/schema/blueprint">
-        <route>
-            <!-- Notice how we can use the servlet scheme which is that reference above -->
-            <from uri="servlet://hello" />
-            <process ref="servletProcessor" />
-        </route>
-    </camelContext>
+If deploying into an application server / servlet container and you choose to have Camel JARs such as `camel-core`, `camel-servlet`, etc on the boot classpath.
+Then the servlet mapping list will be shared between multiple deployed Camel application in the app server.
 
-</blueprint>
--------------------------------------------------------------------------
+WARNING: Having Camel JARs on the boot classpath of the application server is not best practice.
 
-You can use an `Activator` to publish
-the
-https://github.com/apache/camel/blob/main/components/camel-servlet/src/main/java/org/apache/camel/component/servlet/CamelHttpTransportServlet.java[CamelHttpTransportServlet]
-on the OSGi platform:
+In this scenario, you *must* define a custom and unique servlet name in each of your Camel applications. For example, in `web.xml`:
 
-[source,java]
--------------------------------------------------------------------------
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-import org.apache.camel.component.servlet.CamelHttpTransportServlet;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.http.HttpService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.osgi.context.BundleContextAware;
-
-public final class ServletActivator implements BundleActivator, BundleContextAware {
-    private static final Logger LOG = LoggerFactory.getLogger(ServletActivator.class);
-    private static boolean registerService;
-
-    /**
-     * HttpService reference.
-     */
-    private ServiceReference<?> httpServiceRef;
-
-    /**
-     * Called when the OSGi framework starts our bundle
-     */
-    public void start(BundleContext bc) throws Exception {
-        registerServlet(bc);
-    }
+[source,xml]
+---------------------------------------------------------------------------------------------
+<web-app>
+    <servlet>
+      <servlet-name>MyServlet</servlet-name>
+      <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
+      <load-on-startup>1</load-on-startup>
+    </servlet>
 
-    /**
-     * Called when the OSGi framework stops our bundle
-     */
-    public void stop(BundleContext bc) throws Exception {
-        if (httpServiceRef != null) {
-            bc.ungetService(httpServiceRef);
-            httpServiceRef = null;
-        }
-    }
+    <servlet-mapping>
+      <servlet-name>MyServlet</servlet-name>
+      <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
+---------------------------------------------------------------------------------------------
 
-    protected void registerServlet(BundleContext bundleContext) throws Exception {
-        httpServiceRef = bundleContext.getServiceReference(HttpService.class.getName());
-
-        if (httpServiceRef != null && !registerService) {
-            LOG.info("Register the servlet service");
-            final HttpService httpService = (HttpService)bundleContext.getService(httpServiceRef);
-            if (httpService != null) {
-                // create a default context to share between registrations
-                final HttpContext httpContext = httpService.createDefaultHttpContext();
-                // register the hello world servlet
-                final Dictionary<String, String> initParams = new Hashtable<String, String>();
-                initParams.put("matchOnUriPrefix", "false");
-                initParams.put("servlet-name", "CamelServlet");
-                httpService.registerServlet("/camel/services", // alias
-                    new CamelHttpTransportServlet(), // register servlet
-                    initParams, // init params
-                    httpContext // http context
-                );
-                registerService = true;
-            }
-        }
-    }
+In your Camel servlet endpoints, include the servlet name:
 
-    public void setBundleContext(BundleContext bc) {
-        try {
-            registerServlet(bc);
-        } catch (Exception e) {
-            LOG.error("Cannot register the servlet, the reason is {}", e);
-        }
-    }
+[source,java]
+---------------------------------------------------
+from("servlet://foo?servletName=MyServlet")
+---------------------------------------------------
 
-}
--------------------------------------------------------------------------
+Camel detects duplicate Servlet names and will fail to
+start the application. You can control and ignore such duplicates by
+setting the servlet init parameter `ignoreDuplicateServletName` to `true` as
+follows:
 
+[source,xml]
+-----------------------------------------------------------------------------------------------
+  <servlet>
+    <servlet-name>CamelServlet</servlet-name>
+    <display-name>Camel Http Transport Servlet</display-name>
+    <servlet-class>org.apache.camel.component.servlet.CamelHttpTransportServlet</servlet-class>
+    <init-param>
+      <param-name>ignoreDuplicateServletName</param-name>
+      <param-value>true</param-value>
+    </init-param>
+  </servlet>
+-----------------------------------------------------------------------------------------------
 
+But it is *strongly advised* to use unique `servlet-name` for each Camel
+application to avoid this duplication clash, as well any unforeseen
+side effects.
 
 include::spring-boot:partial$starter.adoc[]