Adapt dynamically outbound-gateway with load-balancing - spring

I would like to modify (add/remove) the list of outbound-gateway using the loadbalancing of input channel. My code :
<int:channel id="mainRequestChannel" />
<int-http:outbound-gateway request-channel="mainRequestChannel" message-converters="messageConverters"
request-factory="httpRequestFactory" url="http://localhost:8100/batchfactory-slave/receiveGateway"
http-method="POST" expected-response-type="JobLaunchingResponse" order="1" reply-channel="finishedResponse" />
<int-http:outbound-gateway request-channel="mainRequestChannel" message-converters="messageConverters"
request-factory="httpRequestFactory" url="http://localhost:8090/batchfactory-slave/receiveGateway"
http-method="POST" expected-response-type="JobLaunchingResponse" order="2" reply-channel="finishedResponse" />
I modify this and I have now a dynamicRouter :
<int:channel id="mainRequestChannel" />
<int:router input-channel="mainRequestChannel" expression="#dynamicChannelResolver.resolve()"/>
But there is only 1 subscriber for the mainRequestChannel so the load balancer which is the default behaviour doesn't work.
Thanks for your help.

See the dynamic-ftp sample. You would essentially put the outbound gateway in its own context with parameters for the URL etc. However, unlike that example, you would need to make the context a child of the main context (so it will be able to reference the finishedResponsechannel).
The sample README has a link to some forum discussions where that technique is explained.
Alternatively, you can wire up the necessary classes yourself - you would need a EventDrivenConsumer with mainRequestChannel and a properly configured HttpRequestExecutingMessageHandler in its constructor.

Related

There is any way to iterate over an outbound-gateway on based spring integration application?

<int-http:outbound-gateway
url="${xxx.smartbrain.produto}?startDate={dtInitPosicao}&endDate={dtEnndPosicao}&page=1&recordPerPage=15&cblc={cblc}&cpfCnpj={cpf}"
http-method="GET"
expected-response-type="br.com.bradesco.ciar.xxx.xxx.xxx.integration.response.ResponseTesouroXXXXX"
request-channel="publishSubRequest"
reply-channel="agora-consolidador-splitter-xxxxx-direto-xxxx"
rest-template="restTemplate"
mapped-request-headers="Authorization, cpf, cblc, dtInitPosicao">
<int-http:uri-variable name="cpf" expression="headers.cpf"/>
<int-http:uri-variable name="cblc" expression="headers.cblc"/>
<int-http:uri-variable name="dtInitPosicao" expression="headers.dtinitposicao"/>
</int-http:outbound-gateway>
<int:splitter input-channel="agora-consolidador-splitter-xxxxx-xxxxx-xxxx"
output-channel="channelDirectRequestSmartBrainTesouroDireto">
<bean class="br.com.bradesco.ciar.xxx.xxx.xxx.integration.splitter.TesouroDiretoMovimentacaoSplitter"/>
</int:splitter>
<int:header-value-router input-channel="channelDirectRequestSmartBrainXXXXX" header-name="type" order="1">
<int:mapping value="ativos" channel="requestAtivoXXXX"/>
</int:header-value-router>
<int:header-value-router input-channel="channelDirectRequestSmartBrainXXXXX" header-name="type" order="2">
<int:mapping value="posicao" channel="requestMovimentoXXXXX"/>
</int:header-value-router>
<int:header-value-router input-channel="channelDirectRequestSmartBrainXXXXX" header-name="type" order="3">
<int:mapping value="notifica" channel="notificacaoMovimentoXXXXX"/>
</int:header-value-router>
<int-http:outbound-channel-adapter
http-method="POST"
url="${smartbrain.ativos}"
channel="requestAtivoXXXXXX"
rest-template="restTemplate"
mapped-request-headers="Authorization, DESTINO_URL">
</int-http:outbound-channel-adapter>
<int-http:outbound-channel-adapter
http-method="POST"
url="${smartbrain.xxxxxx}"
channel="requestXXXXXXXX"
rest-template="restTemplate"
mapped-request-headers="Authorization, DESTINO_URL">
</int-http:outbound-channel-adapter>
<int-http:outbound-channel-adapter
http-method="POST"
url="${smartbrain.notifica}"
channel="notificacaoXXXXXXX"
rest-template="restTemplate"
mapped-request-headers="Authorization, DESTINO_URL">
</int-http:outbound-channel-adapter>
I'm consuming the information from the api above, but if I don't paginate to break the response in minor blocks the response is huge and take a long time to process. I'm trying to find a way to iterate over this outbound-gateway component. I'm not sure if I can do it using a router component is possible the create de mechanism that I need.
Based on the amount of pages, I have to call all of them iteratively.
Your current solution is correct in regards carrying page info in the headers.
And idea about a router is also correct. So, when you reach the router you make a decision to go ahead or come back to the publishSubRequest channel, but with a new page number.
It is not clear by your description what you are going to to with the current page, but in any case the publish-subscribe-channel as an output of this HTTP gateway (in your case agora-consolidador-splitter-tesouro-direto-movimentacao) is the way to go. So, you will be able to send the current page for processing downstream (e.g. store it in the aggregator until all the pages are gathered), and at the same time send the result info into the router to determine if you need to iterate back to the HTTP gateway or not. Well, the <filter> also can do you a trick over here since it doesn't look like you need to do anything if pages are exhausted. Still, the crucial part is to have a publish-subscribe-channel with two subscribers: one to process the current page and another to decide if you need to call for the next page.

spring-integration aggregate responses from outbound-gateway

I am looking to combine the responses from two webservice calls. the aggregation would happened based on a key value in the responses.
ws1,ws2(responses from ws1 and ws2 are passed to aggregator)--->aggregator--->response.
I return the following code. The aggregator is not being invoked, not sure what I am missing.
<int-ws:outbound-gateway id="marshallingGateway1" request-channel="RequestChannel1" reply-channel="replyChannel"
uri="https://abc:8080/" message-sender="messageSender"
marshaller="marshaller" unmarshaller="marshaller">
<int-ws:request-handler-advice-chain>
<ref bean="retryAdvice"/>
</int-ws:request-handler-advice-chain>
</int-ws:outbound-gateway>
<int-ws:outbound-gateway id="marshallingGateway2" request-channel="RequestChannel2" reply-channel="replyChannel"
uri="https://abc:8080/" message-sender="messageSender"
marshaller="marshaller" unmarshaller="marshaller">
<int-ws:request-handler-advice-chain>
<ref bean="retryAdvice"/>
</int-ws:request-handler-advice-chain>
</int-ws:outbound-gateway>
<int:aggregator id="responseAggregator" input-channel="replyChannel" ref="responseAggregator" message-store="messageStore" send-partial-result-on-expiry="true"/>
<bean id="messageStore" class="org.springframework.integration.store.SimpleMessageStore"/>
<bean id="responseAggregator" class="abc.cbd.ResponseAggregator"/>
You don't seem to be setting up any correlation between the two results.
By default, a header correlationId is used by the default correlation strategy.
You also need a ReleaseStrategy. It can be as simple as release-strategy-expression="size == 2".
The default release strategy uses sequenceSize and sequenceNumber headers.
If you are using a publish-subscribe channel to send the same message to the two gateways, set the apply-sequence property to true and then you don't need a custom release or correlation strategy.

Spring Integration - Move File After Xpath-splitter

i'm working with spring integration and i have the next case: i'm reading a XML file with an int-file:inbound-channel-adapter and i split the file with a int-xml:xpath-splitter, the thing is that i need to move the file after been splitted.
I want all features of int-xml:xpath-splitter plus moving the file, should i implement a custom splitter extending XPathMessageSplitter? or is there any other way to do that with an out-of-box components?
Thanks.
<int-xml:xpath-splitter id="salesTransSplitter"
input-channel="salesInputChannel"
output-channel="splitterOutChannel" order="1">
<int-xml:xpath-expression expression="/sales_transactions/trans"/>
</int-xml:xpath-splitter>
Something like this should work...
<int-file:inbound ... channel="foo" />
<int:publish-subscribe-channel id="foo" />
<int-xml:xpath-splitter input-channel="foo" ... order="1" />
<int-service-activator input-channel="foo" order="2"
expression="payload.renameTo(new java.io.File('/newDir/' + payload.name)" output-channel="nullChannel" />
If you want to test the rename was successful, send to some other channel other than nullChannel - boolean true means success.
EDIT
Sorry about that; order should be supported on every consuming endpoint, I will open a JIRA issue.
The order is not strictly necessary; if no order is present, the order they appear in the configuration will be used; I just prefer to make it explicit.
There are (at least) two work arounds:
Remvoe the order attribute from BOTH consumers and they will be invoked in the order they appear in the XML.
Configure the XPath splitter as a normal splitter, which does support order...
<int:splitter id="salesTransSplitter" order="1"
input-channel="salesInputChannel"
output-channel="splitterOutChannel" order="1">
<bean class="org.springframework.integration.xml.splitter.XPathMessageSplitter">
<constructor-arg value="/sales_transactions/trans" />
</bean>
</int-xml:xpath-splitter>

Alternative to long URI to configure a Camel Endpoint with Spring beans?

I tried to find a way to configure a Camel endpoint using a spring bean that is referenced from the endpoint declaration in route in a camel context, but it does not work.
For exemple, sometime defining an endpoint URI with many parameters is very horrible (!!), it would be lot more easier to configure the endpoint with a bean and its properties. (Or even better, when configuring a endpoint in XML, maybe the or elements should have sub-elements like a regular beans where we could configure the parameters of the endpoint).
The first approach below work well and is very standard and pretty simple. The second approach, is the one I would like to use instead, but it does not work. I tried with many variations, but without success! The third alternative below would just be an interesting proposal for Camel developers in fact, but it also illustrate my point.
In my example below, I only configured 3 parameters for the file endpoint, but imagine the URI with 10 parameters!! My question is how can I make my second approach working properly, I'm sure there is a simple solution!? I also tried using a factory-bean and a factory method, but it diid not work neither.
1) Standard way to configure a camel endpoint in XML (spring beans):
...
<camel:camelContext id="camelContext" >
<camel:route id="deviceDataLogsPoller" >
<camel:from uri="file://long/path/to/input?preMove=../inprogress&move=../done&moveFailed=../error" />
<camel:log message="Input device data file read from file in input folder {{im.filePoller.folder.input}}." loggingLevel="INFO" />
</camel:route>
</camel:camelContext>
2) Alternative that I expected to be valide but that does not work (for me!):
<bean id="filePoller" class="org.apache.camel.component.file.FileEndpoint" >
<property name="camelContext" ref="camelContext" />
<property name="localWorkDirectory" value="/long/path/to/input" />
<property name="preMove" value="../inprogress" />
<property name="move" value="../done" />
<property name="moveFailed" value="../error" />
...
</bean>
...
<camel:camelContext id="camelContext" >
<camel:route id="deviceDataLogsPoller" >
<camel:from ref="filePoller" />
<camel:log message="Input device data file read from file in input folder {{im.filePoller.folder.input}}." loggingLevel="INFO" />
</camel:route>
</camel:camelContext>
3) Alternative that would be interesting in the future (mixed between two alternatives above):
...
<camel:route id="deviceDataLogsPoller" >
<camel:from uri="file://long/path/to/input" >
<property name="preMove" value="../inprogress" />
<property name="move" value="../done" />
<property name="moveFailed" value="../error" />
...
</camel:from>
<camel:log message="Input device data file read from file in input folder {{im.filePoller.folder.input}}." loggingLevel="INFO" />
</camel:route>
</camel:camelContext>
What did not work for you exactly?
Following setup did the job as expected:
<bean id="filePoller" class="org.apache.camel.component.file.FileEndpoint">
<property name="file" value="src/data/bean-ref" />
<property name="move" ref="moveExpression"/>
</bean>
<bean id="moveExpression" class="org.apache.camel.model.language.SimpleExpression">
<constructor-arg value="${file:parent}/.done/${file:onlyname}" />
</bean>
<camelContext xmlns="http://camel.apache.org/schema/spring" id="camelContext">
<route>
<from ref="filePoller" />
<log message="${body}" />
</route>
</camelContext>
Note:
The property file is mandatory
The properties move, moveFailed, and preMove are not of type java.lang.String but of type org.apache.camel.Expression and have to be initialized accordingly.
The property moveExpression needs a full file expression. If only .done is used instead of ${file:parent}/.done/${file:onlyname} then the file is renamed to .done and not moved to a directory named .done.
As stated in my last comment, I was able to make the bean configuration for an endpoint work (see comments above), but this approach is finally lot more complex and heavy than simply using the URIs after all!!
It would have been more interesting to have a way to configure the endpoints like I proposed in my 3rd alternative above. Maybe if I have time, I will try to create my own and tags that will wrap the existing ones by constructing the full URI from params elements...! I can also propose this to Camel developers.
See an example below of how it could be interesting to configure endpoints in the future (or with the XML wrapper I would like to code):
<camel:route id="deviceDataLogsPoller">
<camel:from uri="file://long/path/to/input" >
<param name="preMove" value="../inprogress" />
<param name="move" value="../done" />
<param name="moveFailed" value="../error" />
...
</camel:from>
...
</camel:route>
Unfortunately, the endpoint configuration as shown above is not possible for the moment, but it would be a nice to have I think! For the moment, the only way is either to specify all the parameters as params in a very long URI, or to configure the endpoint as a regular bean, with with all the complexity it implies (see comments above for details).

How to move property value from Properties file to payload object using spring integration elements

Sample.properties
=================
http.header.amisys.accept.value=arun/vnd.dsths.services-v1+xml
1)Above XSL automatically loaded when my server starts.
2)I have tried <int:enricher> element but it is not helped me.
Sample Code : Below is bit of code I have tried, Can any one suggest me on this.
<int:channel id="PQLegacySecurity-InputChannel" />
<int:chain input-channel="PQLegacySecurity-InputChannel" >
<!-- Split the Search Request Params from Xml -->
<int-xml:xpath-splitter>
<int-xml:xpath-expression expression="//LegacySecurity" namespace map="xmlMessageNamespace" />
</int-xml:xpath-splitter>
<int:enricher >
<int:payload name="testPayload" expression="${http.header.amisys.accept.value}"/>
</int:enricher>
</int:chain>
Actual Payload Object:Below is the xml which does not contain testPayload property.
<?xml version="1.0" encoding="UTF-8"?><LegacySecurity>
<businessArea>%%%%%%</businessArea>
<LegacySystem>%%%%%</LegacySystem>
<LegacyUserID>%%%%%</LegacyUserID>
<LegacyPassword>%%%%%</LegacyPassword>
<OtherLogin/>
<OtherPassword/>
<AddSecurLogin/>
<AddSecurPassword/>
</LegacySecurity>
Expected Payload Object: Below Object contains new element testPayload node which I should able to add
<?xml version="1.0" encoding="UTF-8"?><LegacySecurity>
<businessArea>%%%%%%</businessArea>
<LegacySystem>%%%%%</LegacySystem>
<LegacyUserID>%%%%%</LegacyUserID>
<LegacyPassword>%%%%%</LegacyPassword>
<OtherLogin/>
<OtherPassword/>
**<testPayload>arun/vnd.dsths.services-v1+xml</testPayload>**
<AddSecurLogin/>
<AddSecurPassword/>
</LegacySecurity>
You can use an xslt transformer. Something like the below, though you will need to figure out correct use of the transformer from the spring docs.
Notice you can pass a parameter through to the XSLT
<int-xml:xslt-transformer result-transformer="toDocumentTransformer" result-type="StringResult" xsl-resource="/xslt/addTestPayload.xslt">
<int-xml:xslt-param name="testPayload" value="${http.header.amisys.accept.value}"/>
</int-xml:xslt-transformer>
In the XSLT file, use this to pick up the parameter:
<xsl:param name="testPayload" />
If you have other changes you need to make to the message you can use the same xslt.

Resources