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 <jamesnether...@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[]