Camel REST service problem in parallel calls - rest

I have created a REST service with Apache Camel (REST DSL) running on Karaf container that accepts a list of items and returns the availabilities of those them. The problem is that if the service is called in parallel it doesn’t return the correct results. For example, let’s assume that we have the following two calls:
Call 1: item1, item2
Call 2: item3, item4
I would expect the following two answers
Response 1: availabilityOfItem1, availabilityOfItem2
Response 2: availabilityOfItem3, availabilityOfItem4
But instead of that I am getting the two answers with mixed availabilities
Response 1: availabilityOfItem1, availabilityOfItem2, availabilityOfItem3, availabilityOfItem4
Response 2: availabilityOfItem1, availabilityOfItem2, availabilityOfItem3, availabilityOfItem4
Probably this problem occurs due to the related bean that I have for the service, but let me give you an overview of what I did in my Camel context.
<camelContext>
<restConfiguration ... />
<rest path="/" consumes="application/json" produces="application/json">
<post uri="/availabilities" type="com.xxx.ArticleListReq">
<to uri="direct:availabilities"/>
</post
</rest>
<route id="route.Availabilities">
<from uri="direct:availabilities"/>
<bean ref="availabilitiesBean" method="init"/>
<split parallelProcessing="true">
<simple>${body.articles}</simple>
...
SQL calls for each item
<bean ref="availabilitiesBean" method="buildResponse"/>
</split>
<bean ref="availabilitiesBean" method="getResponse"/>
</route>
</camelContext>
As you can notice when the service is called it goes to the direct:availabilities route that does the job. I am using availabilitiesBean in order to initialize the response object (List), then for each request item I am getting availability from db and put the result to the response object that was previously initialized (buildResponse) and at the end I am returning that object with method getResponse.
The problem is that bean has a singleton scope and that means that for both calls the same bean is used. So, call 1 writes the answer for item1 and item2 but the same time call 2 adds also item3 and item 4 in the response.
Is there a possibility to create a request scope bean? Or follow another approach in order to overcome this issue?
Thanks a lot!

Related

How to limit transaction scope to just the JPA updating outbound gateway (sort of "auto-commit") in Spring Integration

I have an integration flow which starts with a poller. This DOES NOT open a transaction.
Down that flow I have a JPA updating outbound gateway:
<int-jpa:updating-outbound-gateway
request-channel="requestChannel"
reply-channel="replyChannel"
named-query="myUpdatingJpqlQuery"
entity-manager-factory="entityManagerFactory">
<int-jpa:transactional
transaction-manager="transactionManager" />
<int-jpa:parameter name="param1" expression="payload" />
<int-jpa:parameter name="param2"
expression="T(java.time.Instant).now()" />
</int-jpa:updating-outbound-gateway>
This works, however, the transaction that this gateway opens embraces the whole downstream flow (unless I break the transaction boundary with an executor). This is not what I want in this case: I would like the transaction to just embrace the updating operation (some sort of "auto-commit"), because the downstream flow will handle transactions in a more granular way and needs to handle independent transactions (not to join an outer one).
This indeed seems to be confirmed by the documentation: https://docs.spring.io/spring-integration/docs/5.4.11/reference/html/messaging-endpoints.html#tx-handle-message-advice
If I understand that linked section well, if I instead use a <request-handler-advice-chain> with a <tx:advice> I should get the desired result.
However, if I use this:
<int-jpa:updating-outbound-gateway
request-channel="requestChannel"
reply-channel="replyChannel"
named-query="myUpdatingJpqlQuery"
entity-manager-factory="entityManagerFactory">
<int-jpa:parameter name="param1" expression="payload" />
<int-jpa:parameter name="param2"
expression="T(java.time.Instant).now()" />
<int-jpa:request-handler-advice-chain>
<tx:advice transaction-manager="transactionManager" />
</int-jpa:request-handler-advice-chain>
</int-jpa:updating-outbound-gateway>
I get a javax.persistence.TransactionRequiredException, so it seems like that advice is not working (at least not in the way I want).
What is the better way to do this? Am I forced to use a dispatcher with an executor on the reply channel to break the transaction boundary?
Make it like this:
<tx:advice>
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
It does not have any method matches by default therefore such an advice is not applied to the internal AdvisedRequestHandler.

Camel cxfrs RESTful client / ProducerTemplate ConnectionTimeout

I am trying to set the 'connectionTimeout' for a Camel CXF-RS component here which produces a RESTful requests on a 3rd party service. The default 30000 miliseconds is to long.
Exchange exchange = template.send("cxfrs://" + url, new Processor() {
public void process(Exchange exchange) throws Exception {
exchange.setPattern(ExchangePattern.InOut);
Message inMessage = exchange.getIn();
setupDestinationURL(inMessage);
// using the http central client API
inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_USING_HTTP_API, Boolean.TRUE);
// set the Http method
inMessage.setHeader(Exchange.HTTP_METHOD, "PUT");
// set the relative path
inMessage.setHeader(Exchange.HTTP_PATH, url);
// Specify the response class , cxfrs will use InputStream as the response object type
inMessage.setHeader(CxfConstants.CAMEL_CXF_RS_RESPONSE_CLASS, Customer.class);
// set a customer header
inMessage.setHeader("key", "value");
// since we use the Get method, so we don't need to set the message body
inMessage.setBody(null);
}
});
I have tried adding this to our application-context as many have suggested, but cannot see it modifying the default values when debugging through the HTTPConduit and HTTPClientPolicy classes:
<http-conf:conduit name="*.http-conduit">
<http-conf:client ConnectionTimeout="5000"/>
</http-conf:conduit>
and I have tried appending
"?httpClientAPI=true&connectionTimeout=5000"
as options to the url string.
Any help or guidance would be much appreciated.
Adding the http-conf:conduit element in application-context as you did is the way to go and should work. What makes you say it does not?
Quite often a backend server take too long to answer, after the connection is made; in this case setting ReceiveTimeout is as important as ConnectionTimeout.
This is a sample camel Route which consumes RS requests and calls a third-party RS server; the ReceiveTimeout and ConnectionTimeout parameters work as expected.
<cxf:rsServer id="rsFrontServer" address="..." serviceClass="..."/>
<cxf:rsClient id="rsBackendClient" address=".../" serviceClass="..."/>
<http-conf:conduit name="*.http-conduit">
<http-conf:client ReceiveTimeout="5000" ConnectionTimeout="5000"/>
</http-conf:conduit>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route id="front">
<from uri="cxfrs:bean:rsFrontServer"/>
<!-- do stuff -->
<to uri="cxfrs:bean:rsBackendClient"/>
</route>
</camelContext>

Saving WS payload into database in Camel

I'm absolutely newbie in Apache Camel. I'd like to create a very simple app in which it would accept WS calls and save the payload into a database using JPA.
The payload's structure is quite simple. The root is a Marriage object. It contain some String and int and Date fields, a wife, a husband and a list of children (Person objects).
My goal is to save these data into two tables of a database: MARRIAGE, PERSON.
I've successfully created a jaxws:endpoint in which I listen and respond a dummy response.
I've created the tables and JPA entities.
I don't know how to "connect" the WS implementation with the spring configured JpaTemplate. Should I solve this problem with Camel routing using somehow a #Converter class or #Injet it into the WS implementing class by Spring. I'm confused.
Should I use cxf endpoint instead of jaxws endpoint?
You need to use camle-cxf endpoint if you want to use camel. What I would do is expose the endpoint as a camle-cxf endpoint. Something like this:
<camel-cxf:cxfEndpoint id="listenerEndpoint"
address="http://0.0.0.0:8022/Dummy/services/Dummy"
wsdlURL="wsdl/DummyService.wsdl"
xmlns:tns="http://dummy.com/ws/Dummy"
serviceName="tns:Dummy"
endpointName="tns:DummyService">
<camel-cxf:properties>
<entry key="schema-validation-enabled" value="true"/>
<entry key="dataFormat" value="PAYLOAD"/>
</camel-cxf:properties>
</camel-cxf:cxfEndpoint>
Then I would have a simple Spring bean like this:
<bean id="processor" class="com.dummy.DummyProcessor">
<property name="..." value="..."/> //there goes your data source of jdbc template or whatever...
</bean>
If you want to use JPA just configure all the configuration and inject your entity manager into this bean.
The actual class would look something like this:
public class DummyProcessor {
#Trancational //If you need transaction to be at this level...
public void processRequest(Exchange exchange) {
YourPayloadObject object = exchange.getIn().getBody(YourPayloadObject.class);
//object - is your object from SOAP request, now you can get all the data and store it in the database.
}
}
The camel route would be like this:
<camel:camelContext trace="true" id="camelContext" >
<camel:route id="listenerEndpointRoute">
<camel:from uri="cxf:bean:listenerEndpoint?dataFormat=POJO&synchronous=true" />
<camel:log message="Got message. The expected operation is :: ${headers.operationName}"/>
<camel:choice>
<camel:when>
<camel:simple>${headers.operationName} == 'YourPayloadObject'</camel:simple>
<camel:bean ref="processor" method="processRequest"/>
</camel:when>
</camel:choice>
<camel:log message="Got message before sending to target: ${headers.operationName}"/>
<camel:to uri="cxf:bean:someTargetEndpointOrSomethingElse"/>
<camel:log message="Got message received from target ${headers.operationName}"/>
</camel:route>
</camel:camelContext>
Hope this helps.

How to invoke a method in restful web service using camel-cxf?

I want to invoke a rest service using apache camel. Currently i am using the cxfrs component to configure my endpoint. My route looks like below:
from("cxfrs://http://127.0.0.1:8080/RestServiceApp/?resourceClasses="com.sample.Server.HelloWorld").log("Route Started");
My problem is that i want to invoke a method present in the server class (HelloWorld in my case). Can you please tell me how do i call a particular method?
Camel doesn't call resource class methods. From the documentation on the Camel web-site http://camel.apache.org/cxfrs.html:
This class is used to configure the JAXRS properties ONLY. The methods
will NOT be executed during the routing of messages to the endpoint,
the route itself is responsible for ALL processing instead.
You need to write a custom processing logic, for example as following:
<from uri="cxfrs://http://127.0.0.1:8080/RestServiceApp/?resourceClasses="com.sample.Server.HelloWorld">
<choice>
<when>
<simple>${header.operationName} == 'operation1'</simple>
<to uri="direct:operation1" />
</when>
<when>
<simple>${header.operationName} == 'operation2'</simple>
<to uri="direct:operation2" />
</when>
....
</choice>

Do not resolve view in RESTful Application

I am building a web application with RESTful web services using Spring MVC 3. The web services will be used by applications, so should never really resolve any requests to a view. Is there any way to specify in the servlet context that no requests should resolve to any view?
At the moment, I have:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
which I know tries to resolve any request to a correspondingly named view in the jsp folder. However, if I remove this, the application just tries to use a default view resolver.
The reason I am concerned about this is that my application logs are going to be full of the following messages (even though it works fine):
SEVERE: Servlet.service() for servlet [DispatcherServlet] in context with path [/vouchd] threw exception [Circular view path [signup]: would dispatch back to the current handler URL [/vouchd/signup] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)] with root cause
javax.servlet.ServletException: Circular view path [signup]: would dispatch back to the current handler URL [/vouchd/signup] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
or with the InternalViewResolver:
WARN [http-bio-8080-exec-4] (DispatcherServlet.java:1057) - No mapping found for HTTP request with URI [/app/WEB-INF/jsp/call.jsp] in DispatcherServlet with name 'DispatcherServlet'
which I guess is the better of the two evils. I don't want to turn off logging WARN level.
Try with #ResponseStatus. This code returns 204 with no content and view resolving skipped:
#ResponseStatus(NO_CONTENT)
void noView() {
//...
}
If you want to return raw data and simply serialize it to JSON or XML, use #ResponseBody:
#ResponseBody
MyPojo noView() {
return new MyPojo();
}