I have an HTTP Inbound Gateway in my Integration Application, which I will call during some save operation. It's like this. If I have one product, I will call the API once, and if I have more than once, then I will call multiple times. The problem is, for single invoke, SI works just fine. But for multiple calls, request and response get messed up. I thought Spring Integration Channels are just like MQ's, but it is not?
Let me explain this. Let's say I have 2 products. First, I invoke SI for Product A and then for B. Response of A got mapped to request B! It happens all the time. I don't want to use some dirty hacks like wait for the first response to come and invoke again. This means the system has to wait for a long time. I guess we can do it in Spring Integration using task executor, but with all the basic samples out there, I can't find the right one. So please help me find out how can I fix this issue!
My Configuration is :
<int:channel id="n2iMotorCNInvokeRequest" />
<int:channel id="n2iMotorCNInvokeResponse" />
<int:channel id="n2iInvoketransformerOut" />
<int:channel id="n2iInvokeobjTransformerOut" />
<int:channel id="n2iInvokegatewayOut" />
<int-http:inbound-gateway id="i2nInvokeFromPOS"
supported-methods="GET"
request-channel="i2nInvokeRequest"
reply-channel="i2nInvokeResponse"
path="/postProduct/{Id}"
mapped-response-headers="Return-Status, Return-Status-Msg, HTTP_RESPONSE_HEADERS"
reply-timeout="50000">
<int-http:header name="Id" expression="#pathVariables.Id"/>
</int-http:inbound-gateway>
<int:service-activator id="InvokeActivator"
input-channel="i2nInvokeRequest"
output-channel="i2nInvokeResponse"
ref="apiService"
method="getProductId"
requires-reply="true"
send-timeout="60000"/>
<int:transformer input-channel="i2nInvokeResponse"
ref="apiTransformer"
method="retrieveProductJson"
output-channel="n2iInvokeRequest"/>
<int-http:outbound-gateway request-channel="n2iInvokeRequest" reply-channel="n2iInvoketransformerOut"
url="http://10.xx.xx.xx/api/index.php" http-method="POST"
expected-response-type="java.lang.String">
</int-http:outbound-gateway>
<int:service-activator
input-channel="n2iInvoketransformerOut"
output-channel="n2iInvokeobjTransformerOut"
ref="apiService"
method="productResponse"
requires-reply="true"
send-timeout="60000"/>
The i2nInvokeFromPOS gateway is what we call from Web Application which is where all the products will be created. This Integration API will fetch that data, and post it to the backend system so that it will get updated to the other POS locations too!
Steps :
I will send the productId to i2nInvokeFromPOS.
apiTransformer -> retrieveProductJson() method will fetch the product details from DB based on the ID
Send the Request JSON to Backend system using http:outbound-gateway
Get the response from Backend and update the product status as uploaded in DB. Happens in apiService -> productResponse()
Once the response for A is received, all I'm getting is HTTP 500 Error for the Request B! But the Backend API is just fine.
The framework is completely thread-safe - if you are seeing cross-talk between different requests/responses then one (or more) of your components that the framework is invoking is not thread-safe.
You can't keep state in fields in, for example, code invoked from a service activator.
Related
I have the following configuration:
<int:channel id="responseXmlChannel">
<int:interceptors>
<int:wire-tap channel="responseXmlLogger" />
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="responseXmlLogger"
logger-name="test.SyncResponseLogger" level="DEBUG"
expression="'message received, headers:' + headers + ' payload:' + payload"/>
I am trying to add some session attributes to be logged. Current user for example.
I was taking a look at the header-enricher approach, but I could not find a good example of adding session attributes to the header using it.
Is there a way to do what I am trying to do? Is there other approach besides using the headers to add custom attributes to be logged?
My goal is to have in the logs something like
current user: $UserName. Payload: $Payload
OK. So, what is your question then? How to use header-enricher? I'm not sure what is your "session" object to be precise with the answer, but header-enricher can be configured to call any arbitrary method on beans. See its docs: https://docs.spring.io/spring-integration/docs/current/reference/html/message-transformation.html#header-enricher.
The other approach is too deal with ThreadLocal. That's exactly what the RequestContextHolder in Spring Web does for RequestAttributes with its HTTP session access. Although you need to keep in mind that moving to a different thread with queue or executor channels in between will lose the current thread context and you won't have access to that ThreadLocal. So, headers in the particular message is not so bad choice to do.
I am working on an application where a lot of camel routes are exposed as restlet routes. Lets call them endpoints. These endpoints are consumed by an angular application. These endpoints calls to a 3rd party system to gather the data and then after processing them, it passes the response to the angular application.
There are times when the 3rd party system is very slow, and in such cases our server's (Websphere 8.5.5.9) thread pool reaches at its maximum size (because most of them are waiting to get a response from 3rd party). Due to this there are no threads available for other parts (which does not interact with server via these endpoints) of application and hence they also suffers due to this.
So basically we want to limit the number of requests to be served by these 'endpoints' if the server is considerably overloaded so that other parts of application won't get affected. So we wanted to play around the number of threads which can process the incoming request on any of the endpoint. To do that as a poc (proof of concept) I used this example https://github.com/apache/camel/tree/master/examples/camel-example-restlet-jdbc
In this example I changed the following configuration
<bean id="RestletComponentService" class="org.apache.camel.component.restlet.RestletComponent">
<constructor-arg ref="RestletComponent" />
<property name="maxQueued" value="0" />
<property name="maxThreads" value="1" />
</bean>
And in the
org.apache.camel.example.restlet.jdbc.MyRouteConfig
I added a sleep of 20 secs on one of the get direct route as following:
from("direct:getPersons")
.process(exchange -> { Thread.sleep(20000);})
.setBody(simple("select * from person"))
.to("jdbc:dataSource");
Now my assumption (which I understood from the camel documentation at http://camel.apache.org/restlet.html) is that only 1 request can be served at a given time and no other requests will be accepted (since maxQueued is set to 0) when the original request is still in process. But that is not happening in real. With this code I can call this endpoint many times concurrently and all of them give response after 20 secs and few millis.
I am searching for similar kind of setup from last few days and I haven't got anything yet. I wanted to understand if I am doing something wrong or if I have understood the documentation incorrectly.
Camel version used here is 2.23.0-SNAPSHOT
Instead of trying to configuring the thread pool of a Camel component, you could try to use Camel Hystrix to control the downstream calls of your application with the Circuit Breaker pattern.
As soon as the downstream service returns errors or responds too slow, you can return an alternative response to the caller.
SOAP WS with trivial characteristics:
- WSDL address:
http://<ip>:<port>/service/name?wsdl
- target method name:
getData(...)
Part of Spring-integration config ('spring-context.xml') for calling WS:
...
<int:chain input-channel="data" output-channel="stdout">
<!-- transform message to request -->
<int:transformer
ref="soapRequestTransformer"
method="createReq">
</int:transformer>
<!-- define the WS method to be called (getData) -->
<int:header-enricher>
<int:header
name="SoapAction"
value="http://<ip>:<port>/service/name/getData"/>
</int:header-enricher>
<!-- call WS -->
<int-ws:outbound-gateway uri="http://<ip>:<port>/service/name"/>
</int:chain>
...
After transformer (successful) i have request object:
request.getHeaders(): {sequenceNumber=1, correlationId=2bfb560c-
96ba-9c35-96dc-ba16104604b9, id=32f43a24-dee2-461c-a9b9-92c8ee37aaec,
sequenceSize=12, timestamp=1532509142590}
request.getPayload(): org.types.GetDataRequest#77409e4b
How to properly setup the spring-integration to correctly call the WS?
Problem is that WS is not called.
How does this problem look? We can't say anything without at least some stack trace.
Is the name of the WS method used correctly?
The SoapAction value is vendor-specific. You have to consult the WS provided to determine what and how should be presented in that header.
How are the parameters passed?
Not sure about nay parameter. Yes, you can send some query params as well, but doesn't look like that is your questions. Please, be more specific.
By the way, if you send some POJO (e.g. your org.types.GetDataRequest), you need to use a marshaling gateway instead of simple one:
<int-ws:outbound-gateway marshaller="someMarshaller" uri="http://<ip>:<port>/service/name"/>
Not sure, of course, if you have a good XSD-genarated model, based on the provided info in that WSDL...
I've read that if exception is thrown in the flow the first thing the framework will do is check message header for error-channel property. Is it always the case?
In my particular case I'm assigning a custom error-channel to a message header and yet the message seems to get propagated up the stream to the nearest error-handler/error-channel.
<int:chain id="buildAggregatedResponseChain" input-channel="aggregatedResultChannel"
output-channel="sendAggregatedChannel">
<int:header-enricher>
<int:error-channel ref="myErrorChannel"/>
</int:header-enricher>
<int:service-activator ref="service" method="doSomething"/>
</int:chain>
I explicitly throw an exception inside doSomething but the exception never ends up in myErrorChannel. Instead, it is "propagated" to the nearest ErrorHandler up the stream or to the error-channel specified up the stream for int-mail:imap-idle-channel-adapter(tried several different flows).
What do I miss? Maybe someone can outline the main principal of error handling/error propagation(for example when talking about several transactions, etc)? There is some information out there, but it is quite scattered and not systematic.
It depends on the upstream flow; if there's an async handoff the header is consulted; otherwise, the exception is thrown back to the inbound endpoint.
In general, I would advise against modifying framework headers such as errorChannel. Instead put an error-channel on the inbound endpoint (such as your imap idle adapter) and handle the errors on that flow.
Modifying the headers directly is rarely needed. If you wish to insert different error-handling mid-flow then you can insert a messaging gateway...
<int:service activator ... ref="gw" />
<int:gateway id="gw" default-request-channel="..."
error-channel="midFlowErrorChannel" />
If the downstream flow (from the gateway) returns no result on success, then be sure to add a default reply timeout of 0 (or use a custom service interface with a method that returns void).
I'm using Spring Integration & SI AMQP 3.0.0-RELEASE.
I have a fairly simple Request-Response over AMQP between two SI instances.
I'm finding that when the response arrives back on the requesting server, that SI is attempting to deserialize the response using the the Request object's type, not the Response object.
ie., Given the gateway interface of:
public AnalyticsReponse getAnalyticsReport(EntityMessage objectUri);
I find that even though the correct JSON of an AnalyticsResponse arrives on the server, SI is attempting to deserialize it as an EntityMessage, which is failing.
I've debugged it through, and I suspect that the cause is that the Responding side is copying the inbound json__TypeId__ header, rather than supplying it's own. However, I can't see where I've misconfigured this.
Here's my config -- what have I done wrong?
Requesting side:
<int:channel id="analytics.reports.requests.channel" />
<int:channel id="analytics.reports.responses.channel" />
<int:gateway service-interface="com.project.analytics.gateway.AnalyticsReportingGateway">
<int:method name="getAnalyticsReport" request-channel="analytics.reports.requests.channel" reply-channel="analytics.reports.responses.channel"/>
</int:gateway>
<int-amqp:outbound-gateway
request-channel="analytics.reports.requests.channel"
reply-channel="analytics.reports.responses.channel"
exchange-name="analytics.reports.exchange" amqp-template="amqpTemplate" />
Responding side:
<int:channel id="analytics.reports.requests.channel" />
<int:channel id="analytics.reports.responses.channel" />
<int-amqp:inbound-gateway request-channel="analytics.reports.requests.channel" reply-channel="analytics.reports.responses.channel"
queue-names="analytics.reports.queue" connection-factory="rabbitConnectionFactory" message-converter="jsonMessageConverter"/>
<int:service-activator input-channel="analytics.reports.requests.channel" output-channel="analytics.reports.responses.channel"
ref="analyticsReporter" method="getAnalytics"/>
<bean class="com.project.analytics.reporters.SimpleAnalyticsReporter" id="analyticsReporter"/>
public class SimpleAnalyticsReporter {
#SneakyThrows
public AnalyticsReponse getAnalytics(EntityMessage message) {
return new AnalyticsReponse("Hello");
}
As far as you aren't interested in org.springframework.integration.mapping.support.JsonHeaders, because you use jsonMessageConverter, you should filter them (<header-filter>) or fully ignore all AMQP headers (mapped-request-headers="-" or mapped-reply-headers="-").
However I see that I wasn't right yesterday (https://jira.springsource.org/browse/INT-3285) and reopen the issue to revise how can we get deal with standard headers by default to allow to work similar scenarios.
Thank you!