We are using http outbound adapter to make http get request and we want to read URL from properties file as it changes from envt to envt. We also append some other path to this url using message payload but then it is giving us this error message "Caused by: java.lang.IllegalArgumentException: Map has no value for URL". All we need is read base url from properties file and generate final url with payload.
Here is our sample config looks like
<int-http:outbound-gateway request-channel="requestChannel"
url="${url}/{payload}"
http-method="GET"
expected-response-type="java.lang.String"
>
</int-http:outbound-gateway>
Actually {payload} in your URL is an URI variable and it can't be resolved automatically. See how it works:
UriComponentsBuilder.fromUriString(uri).buildAndExpand(uriVariables)
Where uriVariables is a Map for those URI variables.
So, in your case the expected configuration must be like this:
<int-http:outbound-gateway request-channel="requestChannel"
url="${url}/{payload}"
http-method="GET"
expected-response-type="java.lang.String">
<int-http:uri-variable name="payload" expression="payload"/>
</int-http:outbound-gateway>
More information you can find in the Reference Manual.
Related
I tried to make a POST restful api call from Spring Integration with http:outbound-gateway. But I couldn't figure out how to pass the request JSON object to the restful API. Can someone show me how to do it? Thanks !
Here is my spring-integration-config.xml
<si:channel id="request.channel" />
<si:channel id="response.channel">
<si:queue capacity="10" />
</si:channel>
<int-http:outbound-gateway id="employeeinfoGateway"
request-channel="request.channel"
url="http://localhost:8080/EmployeeInfo"
http-method="POST"
expected-response-type="java.lang.String"
charset="UTF-8"
reply-timeout="5000"
reply-channel="response.channel">
</int-http:outbound-gateway>
Here is my Java code:
MessageChannel request = (MessageChannel) context.getBean("request.channel");
JSONObject obj = new JSONObject();
obj.put("empId", "100");
request.send(MessageBuilder.withPayload(obj).setHeader("content-type","application/json").build());
Here is the error message I received:
Could not write request: no suitable HttpMessageConverter found for request type [org.json.JSONObject] and content type [application/json], failedMessage=GenericMessage [payload={"empId":"100"}, headers={content-type=application/json, id=a0aba65f-3f49-4dfc-2431-51983666772b, timestamp=1521419315405}]
Not sure what is that JSONObject. Seems for me that's not how Jackson JSON processor works, but I would suggest to try to send just a plain java.util.Map instead.
There is a MappingJackson2HttpMessageConverter in the RestTemplate underneath to convert incoming payloads to a JSON representation.
Maybe you just don't have a com.fasterxml.jackson.core:jackson-databind jar in your classpath?
I am working on the below case study.
Create a Rest Service to accept Reference id.
Use the Reference Id to get data (CLOB) from Database.
Put the data(CLOB) in a channel(queue) for further processing.
Reply to the rest client with response data in JSON format {"status": true,"message": "RECEIVED"}
I have the Rest Service created and using the Ref Id am getting the data from database but I am unable to send the response back to the rest client after putting the message in a channel(queue).
The output received by Rest Client is : No reply received within timeout
Basically I want the request thread to be returned immediately after the data (CLOB) is pushed to the channel(queue).
Below is the configuration.
<int:channel id="responseChannel"/>
<int:channel id="initCalculation">
<int:queue/>
</int:channel>
<!-- GET -->
<int-http:inbound-gateway
request-channel="httpGetChannel"
reply-channel="responseChannel"
supported-methods="GET"
path="/init/{refId}"
payload-expression="#pathVariables.refId">
<int-http:request-mapping produces="application/json"/>
</int-http:inbound-gateway>
<int:chain input-channel="httpGetChannel" output-channel="initCalculation">
<int-jdbc:stored-proc-outbound-gateway
id="outbound-gateway-storedproc-get-forma" data-source="dataSource"
is-function="false"
stored-procedure-name="XX_EMPROC.GET_FRMA"
ignore-column-meta-data="true"
expect-single-result="true">
<int-jdbc:sql-parameter-definition name="V_REF_ID" direction="IN" />
<int-jdbc:sql-parameter-definition name="V_FRMA" direction="OUT" type="#{T(oracle.jdbc.OracleTypes).CLOB}"/>
<int-jdbc:parameter name="V_REF_ID" expression="payload" />
</int-jdbc:stored-proc-outbound-gateway>
<!- Convert to JSON Format -->
<int:service-activator ref="brInitGateway" method="getResponse"/>
</int:chain>
<int:outbound-channel-adapter channel="initCalculation" ref="brInitGateway" method="process"/>
Kindly advise on the corrections needed in the above.
Thanks
Look, there is no body who sends message as a reply to the <int-http:inbound-gateway>. You have declared responseChannel, but who is going to use it as an output-channel?
I'd suggest you to do this:
<publish-subscribe-channel id="responseChannel"/>
<int:chain input-channel="httpGetChannel" output-channel="responseChannel">
<int:bridge input-channel="responseChannel" output-channel="initCalculation"/>
So, what happens here:
The publish-subscribe-channel for the reply-channel makes an internal bridge to the replyChannel header as one of the subscribers.
You send a result from the <chain> to that channel. Therefore the <int-http:inbound-gateway> gets its reply.
Having that <int:bridge> from response to the initCalculation you have a second subscriber and, therefore, send a message to the required queue.
If you are not interested in the brInitGateway.getResponse() as a reply for the HTTP request, you should consider do not have that reply-channel="responseChannel" at all, but still use some <publish-subscribe-channel> to send to the queue and some transformer to prepare a reply, e.g.:
<transformer input-channel="prepareProcess" expression="' {"status": true,"message": "RECEIVED"}'"/>
This transformer is without output-channel because it is going to send its result into the replyChannel header, therefore to the <int-http:inbound-gateway> initiator.
Spring Integration HTTP support.
Hi I am trying channel routing based on the path attribute.
For example if the configuration is as follow
I would like to send the message to different channel based on path that is path="gateway/search" value. Had a look at header based routing and payloadtype routing. Can u please suggest the way to implement.
<!-- Inbound/Outbound Channels -->
<int:channel id="ServiceSearchRequest" />
<int:channel id="ServiceSearchResponse" />
<int-http:inbound-gateway id="inboundEmployeeSearchRequestGateway"
supported-methods="GET, POST" request-channel="ServiceSearchRequest"
reply-channel="ServiceSearchResponse"
mapped-response-headers="Content-Type"
path="ieg/Service/search" request-payload-type="java.lang.String"
reply-timeout="5000">
</int-http:inbound-gateway>
<int-http:outbound-gateway request-channel="ServiceSearchRequest"
reply-channel="ServiceSearchResponse"
url="http://localhost:8080/proj/Service/avgWaitTime123.json"
http-method="POST" expected-response-type="java.lang.String"
mapped-response-headers="Content-Type" />
It's not clear what you are asking for; you only have a single path (ieg/dataService/search) so all messages would go to a single channel if you routed on the path.
If you mean you want to route based on some query string parameter ..?foo=bar then you can add a <header/> element to the inbound gateway to capture it as a header; e.g.
<int-http:header name="foo" expression="#requestParams.foo.get(0)" />
You can then use a header value router. For a full list of variables available for header expressions, see the reference documentation (scroll down to URI Template Variables and Expressions.
I have the following workflow.
inbound-channel
splitter
task executor for split channels - all the threads execute the same workflow.
3.a. construct the request
3.b. service activator wrapper for a gateway message endpoint.
3.c. gateway wrapper over the http-outbound-gateway with the configuration of error-channel (to handle exceptions while invoking http-outbound-gateway)
3.d. http-outbound-gateway
aggregator
response out of spring integration workflow.
If an exception occurs in 3.d, the control goes to the service-activator configured for the gateway error channel.
I copy just the following from the failed message to the new header to the header passed to the error channel.
a. correlationId
b. sequenceNumber
c. sequenceSize
But while aggregating the splitter response, the DefaultAggregatingMessageGroupProcessor.java removes the conflicting headers and by that it removes the error-channel and reply-channel before providing the control to aggregator.
So once the aggregator completes it's operation it is unable to find the reply or error channel and it results in an exception.
I'm using spring-integration-core version 2.2.1 and I'm not sure why the reply-channel and error-channel is being removed during header aggregation.
Any input on resolving this issue will be of great help.
Thank You :)
EDIT 1:
Thank You very much Gary for helping me out with this scenario. I'm sharing my current configuration
<!-- SPLITTER -->
<int:splitter id="dentalSplitter" ref="dentalServiceSplitter"
method="getDentalServiceList" input-channel="dentalServiceSplitterChannel"
output-channel="dentalSplitterTaskChannel" />
<int:channel id="dentalSplitterTaskChannel">
<int:dispatcher task-executor="dentalTaskExecutor" />
</int:channel>
<int:chain input-channel="dentalSplitterTaskChannel" output-channel="dentalGatewayChannel">
<int:header-enricher>
<int:header name="CHAIN_START_TIME" expression="T(System).currentTimeMillis()" overwrite="true" />
<int:object-to-json-transformer content-type="application/json"/>
</int:chain>
<int:service-activator input-channel="dentalGatewayChannel" ref="dentalGatewayWrapper" output-channel="dentalReplyChannel" />
<int:gateway id="dentalGatewayWrapper" default-request-channel="dentalCostEstimateRequestChannel" error-channel="dentalErrorChannel"/>
<int-http:outbound-gateway id="dentalGateway"
url-expression="#urlBuilder.build('${service.endpoint}')"
http-method="POST" request-factory="clientHttpRequestFactory"
request-channel="dentalCostEstimateRequestChannel" extract-request-payload="true"
expected-response-type="com.dental.test.DentalResponse">
<int-http:request-handler-advice-chain>
<ref bean="logChainTimeInterceptor" />
</int-http:request-handler-advice-chain>
</int-http:outbound-gateway>
<!-- EXCEPTION -->
<int:chain input-channel="dentalErrorChannel" output-channel="dentalAggregatorChannel">
<int:transformer ref="commonErrorTransformer" method="dentalGracefulReturn"/>
</int:chain>
<!-- SUCCESS -->
<int:chain input-channel="dentalReplyChannel" output-channel="dentalAggregatorChannel">
<int:filter discard-channel="dentalErrorChannel"
expression="T(com.dental.util.InvocationOutcomeHelper).isOutcomeSuccess(payload?.metadata?.outcome?.code,payload?.metadata?.outcome?.message)" />
</int:chain>
<!-- AGGREGATION -->
<int:chain input-channel="dentalAggregatorChannel" output-channel="wsDentalServiceOutputChannel" >
<int:aggregator ref="dentalServiceAggregator" />
<int:service-activator ref="dentalResponseServiceActivator" />
</int:chain>
What I noticed was this, every split channel when passing through the gateway creates a new temporary channel for error and reply and after getting the response back from the gateway, it retains the preserved (original inbound) error and reply channel header. And as you had mentioned, after the control gets to the error transformer that flow of retaining the preserved headers gets broken and the aggregating message group processor receives three different instances of temporary channel and hence removes them.
I was planning to have a custom message group processor and modify the conflict resolution strategy for aggregating the header and came up with this config.
<bean id="channelPreservingAggregatingMessageHandler" class="org.springframework.integration.aggregator.AggregatingMessageHandler">
<constructor-arg name="processor" ref="channelPreservingMessageGroupProcessor"/>
</bean>
I'm yet to test this out though. But based on this discussion, this does not look like a viable solution.
And looks like my configuration for error handling in gateway is incorrect.
However, I'm confused on this statement of yours "Instead of forwarding the message directly, simply handle the error on your error flow and return the result normally to the gateway "wrapper"". If I remove the error channel how will I get the control back when an exception occurs? May be I'm missing to understand something here. Can you elaborate more on this please?
When asking questions about scenarios such as this, you generally need to show your configuration. However, I suspect you are forwarding the message from the error flow directly to the aggregator.
This is like doing a GOTO in code and breaks the scoping.
It won't work because the replyChannel header in the error message is for the gateway "wrapper", not the original upstream inbound gateway. When the aggregator gets conflicting headers, it has no choice but to drop the headers (you will see a DEBUG log message to that effect).
Instead of forwarding the message directly, simply handle the error on your error flow and return the result normally to the gateway "wrapper" (simply omit the error channel on the last element on the error flow).
The gateway will then fix up the reply so it is consistent with other messages (good and bad) and forward it to the aggregator.
You don't need to mess with headers in your error flow, just return the value you want to be aggregated along with the good results.
You should really update to a current release, or at least the latest in the 2.2.x line (2.2.6).
I have an inbound gateway to process JSON payloads. Since we utilize the same backend service as our SOAP endpoints, I need to have an inbound gateway that maps to all paths. I can see the default HTTP headers come across the into the message, but I specifically need the request URL so I can parse it and route to the appropriate serve as needed.
My inbound-gateway:
<int-http:inbound-gateway id="JSONGateway"
path="*" request-channel="JSONRequestChannel" supported-methods="POST"
reply-timeout="5000"
header-mapper="headerMapper"
request-payload-type="java.lang.String" >
</int-http:inbound-gateway>
I tried adding a header-enricher like I do for SOAP, but the TransportContextListener is null.
<int:header-enricher input-channelJSONequestChannel"
output-channel="JSONRequestChannelWithHeaders">
<int:header name="service"
expression="T(org.springframework.ws.transport.context.TransportContextHolder).transportContext.connection.uri.toString().substring(T(org.springframework.ws.transport.context.TransportContextHolder).transportContext.connection.uri.toString().lastIndexOf('/')+1)" />
</int:header-enricher>
I need a way to get the URL of the request so I can parse out the service into the message headers for my downstream router.
/json/ContactService = "ContactService"
/json/ContactService/insert = "ContactService"
/json/ContactService/get/234 = "ContactService"
I also tried adding a header-mapper class, but still run into the same problem. How can I get a handle to the HTTPServletRequest in code? Once I get that I can get all the headers I need.
Well, you didn't check the message headers.
After converting ServletServerHttpRequest to the Message it gets these headers by default:
Message<?> message = messageBuilder
.setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_URL,
request.getURI().toString())
.setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_METHOD,
request.getMethod().toString())
.setHeader(org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL,
servletRequest.getUserPrincipal())
.build();
So, http_requestUrl is for you to go ahead and parse it appropriately.