| = Servlet Component |
| :doctitle: Servlet |
| :shortname: servlet |
| :artifactid: camel-servlet |
| :description: Serve HTTP requests by a Servlet. |
| :since: 2.0 |
| :supportlevel: Stable |
| :tabs-sync-option: |
| :component-header: Only consumer is supported |
| //Manually maintained attributes |
| :camel-spring-boot-name: servlet |
| |
| *Since Camel {since}* |
| |
| *{component-header}* |
| |
| The Servlet component provides HTTP based |
| endpoints for consuming HTTP requests that arrive at |
| a HTTP endpoint that is bound to a published Servlet. |
| |
| Maven users will need to add the following dependency to their `pom.xml` |
| for this component: |
| |
| [source,xml] |
| --------------------------------------------------------------- |
| <dependency> |
| <groupId>org.apache.camel</groupId> |
| <artifactId>camel-servlet</artifactId> |
| <version>x.x.x</version> |
| <!-- use the same version as your Camel core version --> |
| </dependency> |
| --------------------------------------------------------------- |
| |
| |
| [NOTE] |
| ==== |
| *Stream* |
| |
| Servlet is stream based, which means the input it receives is submitted |
| to Camel as a stream. That means you will only be able to read the |
| content of the stream *once*. If you find a situation where the message |
| body appears to be empty or you need to access the data multiple times |
| (eg: doing multicasting, or redelivery error handling) you should use |
| Stream caching or convert the message body to |
| a `String` which is safe to be read multiple times. |
| ==== |
| |
| == URI format |
| |
| --------------------------------- |
| servlet://relative_path[?options] |
| --------------------------------- |
| |
| |
| // component-configure options: START |
| |
| // component-configure options: END |
| |
| // component options: START |
| include::partial$component-configure-options.adoc[] |
| include::partial$component-endpoint-options.adoc[] |
| // component options: END |
| |
| // endpoint options: START |
| |
| // endpoint options: END |
| |
| |
| |
| == Message Headers |
| |
| Camel will apply the same Message Headers as the xref:http-component.adoc[HTTP] |
| component. |
| |
| Camel will also populate *all* `request.parameter` and |
| `request.headers`. For example, if a client request has the URL, |
| http://myserver/myserver?orderid=123, the exchange will contain a |
| header named `orderid` with the value 123. |
| |
| == Usage |
| |
| You can consume only `from` endpoints generated by the Servlet component. |
| 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 |
| |
| If you put the Camel JARs such as `camel-core`, `camel-servlet`, etc. in |
| the boot classpath of your application server (eg usually in its lib |
| directory), then mind that the servlet mapping list is now shared |
| between multiple deployed Camel application in the app server. |
| |
| Mind that putting Camel JARs in the boot classpath of the application |
| server is generally not best practice! |
| |
| So in those situations you *must* define a custom and unique servlet |
| name in each of your Camel application, eg in the `web.xml` define: |
| |
| [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 fail 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: |
| |
| . 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 a 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: |
| |
| [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> |
| |
| <servlet-mapping> |
| <servlet-name>CamelServlet</servlet-name> |
| <url-pattern>/services/*</url-pattern> |
| </servlet-mapping> |
| |
| </web-app> |
| ------------------------------------------------------------------------- |
| |
| |
| Then you can define your route as follows: |
| |
| [source,java] |
| ------------------------------------------------------------------------- |
| from("servlet:hello?matchOnUriPrefix=true").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>"); |
| } |
| }); |
| ------------------------------------------------------------------------- |
| |
| [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")` |
| ==== |
| |
| === Sample when using Spring |
| |
| 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: |
| |
| [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> |
| ------------------------------------------------------------------------- |
| |
| === Sample when using OSGi |
| |
| 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: |
| |
| [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> |
| ------------------------------------------------------------------------- |
| |
| Then use this service in your Camel route like this: |
| |
| [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"> |
| |
| <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> |
| |
| <bean id="httpRegistry" class="org.apache.camel.component.servlet.DefaultHttpRegistry" /> |
| |
| <bean id="servlet" class="org.apache.camel.component.servlet.ServletComponent"> |
| <property name="httpRegistry" ref="httpRegistry" /> |
| </bean> |
| |
| <bean id="servletProcessor" class="org.apache.camel.example.servlet.ServletProcessor" /> |
| |
| <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> |
| |
| </blueprint> |
| ------------------------------------------------------------------------- |
| |
| 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: |
| |
| [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); |
| } |
| |
| /** |
| * Called when the OSGi framework stops our bundle |
| */ |
| public void stop(BundleContext bc) throws Exception { |
| if (httpServiceRef != null) { |
| bc.ungetService(httpServiceRef); |
| httpServiceRef = null; |
| } |
| } |
| |
| 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; |
| } |
| } |
| } |
| |
| public void setBundleContext(BundleContext bc) { |
| try { |
| registerServlet(bc); |
| } catch (Exception e) { |
| LOG.error("Cannot register the servlet, the reason is {}", e); |
| } |
| } |
| |
| } |
| ------------------------------------------------------------------------- |
| |
| |
| |
| include::spring-boot:partial$starter.adoc[] |