Http Connection Pooling in Camel - apache-camel

I am using Camel as an Orchestration Engine.
clients sends HTTP request <-> CAMEL code <---- HTTP Req----- > external
server(s)
I am using HTTP4 Component (with default settings) for making HTTP Requests
to external server. I have quite a few http backends.
Right now the way we are making http calls to our backend is as follow:-
// The producer is created during app initialisation. This is actually done
via blueprint.xml
ProducerTemplate producer = camelContext.createProducerTemplate();
// Whenever I need to make a http call I am executing the below code with
URL set as something like:- "http4://order-api:8099/orders/v1/ordersearch/"
Exchange exchange = producer.request(URL, new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
log.info("Executing the HTTP request : URL - " + URL + " Headers -
" + headers + " Body : " + body);
exchange.getIn().setHeaders(headers);
exchange.getIn().setBody(body);
}
});
The query I am having is:-
Does HTTP4 in the default setting camel uses some http connection
pooling while making a call to the external servers?
If yes Is there a way I can configure the connection pooling from
blueprint.xml?
I am using Camel 2.16.1 and the application is deployed in Karaf 3.0.5.

The http4 component use Apache HttpClient, which support pooling with the use of a HttpClientConnectionManager.
By default, camel uses a PoolingHttpClientConnectionManager which is configured with the properties connectionsPerRoute and maxTotalConnections.
If you want to have more control over this clientConnectionManager, you can provide your own implementation of org.apache.http.conn.HttpClientConnectionManager
See HttpClient connection manager

Related

Difference between #JMSListener and #Consume annotations?

I am trying to consume JMS messages sent via spring JmsTemplate using #Consume annotated bean. The consumer is not receiving messages when sent using JmsTemplate.
Whereas, when sent using ProducerTemplate of Camel the messages are received.
What is the difference between #org.springframework.jms.annotation.JmsListener and #org.apache.camel.Consume?
Producer Logic
jmsTemplate.convertAndSend("jms:mailbox", message);
Consumer Logic
#Consume(uri="jms:mailbox")
public void onRequest(String name) {
System.out.println("Received message > "+name);
}
Apache Camel #Consume annotation can consume from any endpoint, which supports consuming. This annotation takes uri as parameter. URI consists of scheme, path and optional params. In case of JMS component the scheme is jms, path is Destination (in your case mailbox) and params are additional options customizing behavior of Consumer.
Spring #JmsListener can consume from JMS and takes Destination as parameter.
Your code does not work because the Destination is mailbox, not jms:mailbox. Spring JmsTemplate does not know about jms scheme, it is Camel specific. So use jmsTemplate.convertAndSend("mailbox", message) on Spring side and #Consume(uri="jms:mailbox") on Camel side.

Apache Camel http mock testing fails with Connection refused

I am testing this Camel route:
from("direct:start")
.setHeader(Exchange.HTTP_METHOD, constant("GET"))
.to("http://127.0.0.1:8088/")
.to("mock:result");
...using this mock server:
mockServer = MockRestServiceServer.createServer(new RestTemplate());
mockServer.expect(
requestTo("http://127.0.0.1:8088/"))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body("")
);
...but receive:
I/O exception (java.net.ConnectException) caught when processing request: Connection refused: connect
Are there anything obvious I am missing? How can I proceed to find the cause?
You cannot use MockRestServiceServer. This does not start real server and therefore can be used only for mocking responses to Spring RestTemplate. Apache Camel does not utilize RestTemplate for sending requests, it uses Apache HttpClient.
You can either:
Advice your http endpoint with mock endpoint - preferred way. Example using isMockEndpointsAndSkip eg here: camel mock - MockEndpoint.whenAnyExchangeReceived process method not execute
Or start any full Http server in your unit test - For this you can extend HttpServerTestSupport containing some prepared methods - example HttpBodyTest

Intercepting cxf web service header with Apache Camel (Java DSL)

I created a web service client to handle cxf soap web services with apache camel.
String serviceUri = "cxf:http://localhost:10000/myservice?serviceClass=" +
MyRequest.class.getCanonicalName();
from(uri).to("mock:xyz");
The web service receives the soap call but throws an exception since the request requires a handling for wss.
org.apache.cxf.binding.soap.SoapFault: MustUnderstand headers: [{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood.
The reason is, that the service requires ws security, which can be seen by lloking at the request.
<SOAP-ENV:Header><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" SOAP-ENV:mustUnderstand="1">
I found out that I need to implement an interceptor to handle header properties.
My questions:
How can I add an interceptor to handle the header attributes with Camel Java-DSL?
Will this be sufficient to get rid of the SOAP Fault?
You can do it through
cxfEndpointConfigurer option #see: Camel-CXF configuration
(I use Spring (it is much easier)), but I guess for DSL URI will look like:
String serviceUri = "cxf:http://localhost:10000/myservice?serviceClass=" +
MyRequest.class.getCanonicalName() +
"&cxfEndpointConfigurer="+ MyConfigurer.class.getCanonicalName();
by implementing org.apache.camel.component.cxf.CxfEndpointConfigurer you have ability to add an Interceptor inside configureServer method
server.getEndpoint().getInInterceptors().add(new MyJAASLoginInterceptor());
if you run your Camel in container with JAAS (like JBOSS) you can use extension from
org.apache.cxf.interceptor.security.JAASLoginInterceptor
with needed callback handler.
Simple example which validates user/password from WSS header against JBOSS users:
public class MyJAASLoginInterceptor extends javax.security.auth.callback.JAASLoginInterceptor {
#Override
protected CallbackHandler getCallbackHandler(String name, String password) {
return new org.apache.cxf.interceptor.security.NamePasswordCallbackHandler(name, password, "setCredential");
}
}

camel producertemplate only sending 2 http requests at a time

I'm using the Camel's ProducerTemplate to make http calls like below.
#Produce
private ProducerTemplate producer;
producer.requestBodyAndHeaders(url, requestString, headers,
String.class);
In my application, I'm using this to call another time taking rest api multiple times for a batch job. But from the access logs of the other server, I found that the server is getting only 2 requests at a time.
Once, it processes these two, it was getting another 2 requests.
Looks like something to do with the connection pooling of Camel's producerTemplate. Is it defaulting to 2 connections?
How can I change this configuration?

Restful API becomes 404 when using the CXF at the same time

I have a project starts up with Spring Boot.
It has some restful API via Spring Integration inbound gateway.
Afterward, some webservice endpoint added to the project with CXF.
When I setup the CXFServlet mapping, all the restful API became 404.
Only I suspend the CXF config the restful API available again.
May I know if there is anything block the restful API or the spring integration inbound gateway during using CXF?
CXFServlet and Bus
#Configuration
#ComponentScan("com.kennie")
#ImportResource("classpath:cxf-services.xml")
public class SimbaAdapterApplicationConfiguration {
#Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
}
#Bean(name=Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
SpringBus bus = new SpringBus();
bus.getInInterceptors().add(new LoggingInInterceptor());
bus.getOutInterceptors().add(new LoggingOutInterceptor());
return bus;
}
XML configuration
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
<jaxws:server id="MyService" address="/ws/MyService"
serviceClass="com.kennie.IMyService" >
<jaxws:serviceBean>
<ref bean="myServiceImpl" />
</jaxws:serviceBean>
</jaxws:server>
Service Interface
#WebService
public interface IMyService{
#WebMethod
public #WebResult(name = "Response") Response doRequest(
#WebParam(name = "Request", mode = WebParam.Mode.IN)
Request request
);
}
I'm not familiar with CXF, but I know that Spring Integration HTTP is fully based on Spring MVC. So, if you can configure Spring MVC over CXF, all those Spring Integration HTTP Inbound Gateways will be available there as well.
I think your problem is somewhere with distinguishing Servlet mapping.
Looks like your REST API is routed through the CXF Servlet and that one doesn't like it, hence rejecting.
When you add CXF to your code, all the RESTful APIs will be routed through it.
I see two contradictory settings with the way you have configured CXF -
The url-mapping . You are injecting CXF with this code:
#Bean
public ServletRegistrationBean dispatcherServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
}
Meaning the url at which CXF is listening is /ws/*.
The jax-ws server! Firstly, you need to change it to jax-rs . WS is for SOAP. RS is for Restful. You have defined its address as:
<jaxws:server id="MyService" address="/ws/MyService"
Meaning the server is listening at /ws/MyService
CXF and the jax-rs server are both listening at ws/ something. Now, this is not really a problem. You just need to add this to the URL you are hitting so that the complete URL is something like this:
http:<server>:<port>/<context-root>/<CXF Endpoint>/<jax-rs server address>/<REST API endpoint>
I am guessing you don't want ws appearing twice in the URL. Remove it from the jax-rs address.
Hope this helps.
You can register more one servlet for http rest api, this method is tested and OK:
#SpringBootApplication(
//scanBasePackages = {"com.huawei.manage"}
)
public class Application {
#Bean
public ServletRegistrationBean dispatcherServlet() {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.scan("com.huawei.manage.hsf.controller");
DispatcherServlet rest_dispatcherServlet = new DispatcherServlet(applicationContext);
ServletRegistrationBean registrationBean = new ServletRegistrationBean(rest_dispatcherServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/*");
return registrationBean;

Resources