blob: e4437e55711c8d98d193ac931173235c355a12ba [file] [log] [blame]
= 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[]