I'm confused how Camel automatically forms messages and correlation identifiers, when using message queues.
For an example, I have two service with following routes
<route id="cxfToJMSRoute">
<from uri="cxf:bean:endpoint" />
<convertBodyTo type="String" />
<log message="..." />
<log message="In message: ${in.body}" />
<log message="In JMSMessageID: ${in.header.JMSMessageID}" />
<log message="In JMSCorrelationID: ${in.header.JMSCorrelationID}" />
<log message="Out message: ${out.body}" />
<log message="Out JMSMessageID: ${out.header.JMSMessageID}" />
<log message="Out JMSCorrelationID: ${out.header.JMSCorrelationID}" />
<log message="Invoking service A by sending InOut message to api queue" />
<to uri="activemq:queue:api?replyTo=result&replyToType=Exclusive"
pattern="InOut" />
<log message="..." />
<log message="Got message from Service A" />
<log message="In message: ${in.body}" />
<log message="In JMSMessageID: ${in.header.JMSMessageID}" />
<log message="In JMSCorrelationID: ${in.header.JMSCorrelationID}" />
<log message="Out message: ${out.body}" />
<log message="Out JMSMessageID: ${out.header.JMSMessageID}" />
<log message="Out JMSCorrelationID: ${out.header.JMSCorrelationID}" />
<!-- Correlation ID? -->
<pollEnrich>
<constant>activemq:queue:reply</constant>
</pollEnrich>
<log message="..." />
<log message="Got message from Service B" />
<log message="In message: ${in.body}" />
<log message="In JMSMessageID: ${in.header.JMSMessageID}" />
<log message="In JMSCorrelationID: ${in.header.JMSCorrelationID}" />
<log message="Out message: ${out.body}" />
<log message="Out JMSMessageID: ${out.header.JMSMessageID}" />
<log message="Out JMSCorrelationID: ${out.header.JMSCorrelationID}" />
<transform>
<simple>${in.body}</simple>
</transform>
<log message="After transform:" />
<log message="In message: ${in.body}" />
<log message="In JMSMessageID: ${in.header.JMSMessageID}" />
<log message="In JMSCorrelationID: ${in.header.JMSCorrelationID}" />
<log message="Out message: ${out.body}" />
<log message="Out JMSMessageID: ${out.header.JMSMessageID}" />
<log message="Out JMSCorrelationID: ${out.header.JMSCorrelationID}" />
<log message="..." />
</route>
--
<route id="apiToServiceARoute">
<from uri="activemq:queue:api"/>
<log message="..." />
<log message="In message: ${in.body}" />
<log message="In JMSMessageID: ${in.header.JMSMessageID}" />
<log message="In JMSCorrelationID: ${in.header.JMSCorrelationID}" />
<log message="Out message: ${out.body}" />
<log message="Out JMSMessageID: ${out.header.JMSMessageID}" />
<log message="Out JMSCorrelationID: ${out.header.JMSCorrelationID}" />
<log message="Mocking service B by sending InOnly message to reply queue" />
<to uri="activemq:queue:reply?exchangePattern=InOnly" pattern="InOnly" />
<transform>
<simple>Delegated</simple>
</transform>
<log message="..." />
<log message="After transform:" />
<log message="In message: ${in.body}" />
<log message="In JMSMessageID: ${in.header.JMSMessageID}" />
<log message="In JMSCorrelationID: ${in.header.JMSCorrelationID}" />
<log message="Out message: ${out.body}" />
<log message="Out JMSMessageID: ${out.header.JMSMessageID}" />
<log message="Out JMSCorrelationID: ${out.header.JMSCorrelationID}" />
<log message="..." />
</route>
Here are my observations according to log messages
OUT message is always empty. Does Camel always use IN by default, until OUT is created by some processor excliptly?
JMSMessageID and JMSCorrelationID doesn't seem to have any connection. Does Camel only use JMSCorrelationID for correlating messages?
Camel manages to retain the JMSCorrelationID when sending InOnly message to another queue (reply) where the request originates from. This is desirable, but how does it actually do it? What are the specific rules of retaining the JMSCorrelationID?
And here are the actual logs of both example routes
2017-12-03 12:16:06.197 INFO 13780 --- [qtp335580595-36] cxfToJMSRoute : ...
2017-12-03 12:16:06.197 INFO 13780 --- [qtp335580595-36] cxfToJMSRoute : In message: testi2
2017-12-03 12:16:06.198 INFO 13780 --- [qtp335580595-36] cxfToJMSRoute : In JMSMessageID:
2017-12-03 12:16:06.198 INFO 13780 --- [qtp335580595-36] cxfToJMSRoute : In JMSCorrelationID:
2017-12-03 12:16:06.198 INFO 13780 --- [qtp335580595-36] cxfToJMSRoute : Out message:
2017-12-03 12:16:06.198 INFO 13780 --- [qtp335580595-36] cxfToJMSRoute : Out JMSMessageID:
2017-12-03 12:16:06.198 INFO 13780 --- [qtp335580595-36] cxfToJMSRoute : Out JMSCorrelationID:
2017-12-03 12:16:06.198 INFO 13780 --- [qtp335580595-36] cxfToJMSRoute : Invoking service A by sending InOut message to api queue
2017-12-03 12:16:06.569 INFO 13780 --- [Manager[result]] cxfToJMSRoute : ...
2017-12-03 12:16:06.570 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Got message from Service A
2017-12-03 12:16:06.570 INFO 13780 --- [Manager[result]] cxfToJMSRoute : In message: Delegated
2017-12-03 12:16:06.582 INFO 13780 --- [Manager[result]] cxfToJMSRoute : In JMSMessageID: ID:DESKTOP-LI5P50P-54036-1512296151920-1:1:1:1:1
2017-12-03 12:16:06.582 INFO 13780 --- [Manager[result]] cxfToJMSRoute : In JMSCorrelationID: Camel-ID-DESKTOP-LI5P50P-1512296077166-0-2
2017-12-03 12:16:06.582 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Out message:
2017-12-03 12:16:06.582 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Out JMSMessageID:
2017-12-03 12:16:06.583 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Out JMSCorrelationID:
2017-12-03 12:16:06.596 INFO 13780 --- [Manager[result]] cxfToJMSRoute : ...
2017-12-03 12:16:06.596 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Got message from Service B
2017-12-03 12:16:06.596 INFO 13780 --- [Manager[result]] cxfToJMSRoute : In message: testi2
2017-12-03 12:16:06.597 INFO 13780 --- [Manager[result]] cxfToJMSRoute : In JMSMessageID: ID:DESKTOP-LI5P50P-54036-1512296151920-3:1:1:1:1
2017-12-03 12:16:06.597 INFO 13780 --- [Manager[result]] cxfToJMSRoute : In JMSCorrelationID: Camel-ID-DESKTOP-LI5P50P-1512296077166-0-2
2017-12-03 12:16:06.597 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Out message:
2017-12-03 12:16:06.597 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Out JMSMessageID:
2017-12-03 12:16:06.597 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Out JMSCorrelationID:
2017-12-03 12:16:06.598 INFO 13780 --- [Manager[result]] cxfToJMSRoute : After transform:
2017-12-03 12:16:06.598 INFO 13780 --- [Manager[result]] cxfToJMSRoute : In message: testi2
2017-12-03 12:16:06.598 INFO 13780 --- [Manager[result]] cxfToJMSRoute : In JMSMessageID: ID:DESKTOP-LI5P50P-54036-1512296151920-3:1:1:1:1
2017-12-03 12:16:06.598 INFO 13780 --- [Manager[result]] cxfToJMSRoute : In JMSCorrelationID: Camel-ID-DESKTOP-LI5P50P-1512296077166-0-2
2017-12-03 12:16:06.598 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Out message:
2017-12-03 12:16:06.598 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Out JMSMessageID:
2017-12-03 12:16:06.598 INFO 13780 --- [Manager[result]] cxfToJMSRoute : Out JMSCorrelationID:
2017-12-03 12:16:06.598 INFO 13780 --- [Manager[result]] cxfToJMSRoute : ...
--
2017-12-03 12:16:06.528 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : ...
2017-12-03 12:16:06.531 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : In message: testi2
2017-12-03 12:16:06.534 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : In JMSMessageID: ID:DESKTOP-LI5P50P-54046-1512296166309-1:1:2:1:1
2017-12-03 12:16:06.534 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : In JMSCorrelationID: Camel-ID-DESKTOP-LI5P50P-1512296077166-0-2
2017-12-03 12:16:06.534 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : Out message:
2017-12-03 12:16:06.535 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : Out JMSMessageID:
2017-12-03 12:16:06.535 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : Out JMSCorrelationID:
2017-12-03 12:16:06.535 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : Mocking service B by sending InOnly message to reply queue
2017-12-03 12:16:06.558 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : ...
2017-12-03 12:16:06.558 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : After transform:
2017-12-03 12:16:06.558 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : In message: Delegated
2017-12-03 12:16:06.558 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : In JMSMessageID: ID:DESKTOP-LI5P50P-54046-1512296166309-1:1:2:1:1
2017-12-03 12:16:06.559 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : In JMSCorrelationID: Camel-ID-DESKTOP-LI5P50P-1512296077166-0-2
2017-12-03 12:16:06.559 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : Out message:
2017-12-03 12:16:06.559 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : Out JMSMessageID:
2017-12-03 12:16:06.559 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : Out JMSCorrelationID:
2017-12-03 12:16:06.559 INFO 16972 --- [msConsumer[api]] apiToServiceARoute : ...
OUT message is always empty. Does Camel always use IN by default, until OUT is created by some processor explicitly?
Yes. Cloning of whole org.apache.camel.Message is really expensive. OUT is created on demand, during first call of Exchange#getOut
See Apache Camel: Message exchange patterns and the Exchange object
JMSMessageID and JMSCorrelationID doesn't seem to have any connection. Does Camel only use JMSCorrelationID for correlating messages?
Not exactly. camel-jms (and camel-activemq, which is extending it) component copies header JMSCorrelationID to header CamelCorrelationId.
Correlation in pollEnrich is again working with CamelCorrelationId and camel-jms translates this header as message selector JMSCorrelationID=CamelCorrelationId.
So Yes, while communicating between camel-jms (and inherited components) endpoints and exchangePattern=InOnly, correlation will be done effectively on header JMSCorrelationID.
See Apache Camel: Correlation Identifier
Camel manages to retain the JMSCorrelationID when sending InOnly message to another queue (reply) where the request originates from. This is desirable, but how does it actually do it? What are the specific rules of retaining the JMSCorrelationID?
Camel does no transformation of JMSCorrelationID when using exchangePattern=InOnly.
On input route, Camel gets correlation id from input javax.jms.Message and saves it to org.apache.camel.Message#headers.JMSCorrelationId.
On output route Camel gets correlation id from org.apache.camel.Message#headers.JMSCorrelationId and sets it to javax.jms.Message.setJMSCorrelationID().
Note: I am not developer of Apache Camel. Every word I have written in this answer is based on user experience while using this framework. There could be minor discrepancies in answer.
Related
<from uri="direct:validating-route" />
<setBody>
<simple>${body}</simple>
</setBody>
<log message="Mapping request from source to destination"
loggingLevel="INFO" />
<to
uri="dozer:transformValidateWrapperType?mappingFile=mappings/validate-mapping.xml&targetModel=in.map.loc.elements.ValidateWrapperType" />
<log message="Completed MApping and sending request"
loggingLevel="INFO" />
<setBody>
<simple>${body}</simple>
</setBody>
<setHeader headerName="operationNamespace">
<constant>http://in.map.com/functions/tele/ap</constant>
</setHeader>
<to uri="cxf:bean:validateOutside" />
The body passed after the set body is null. This is the route for a SOAP request. How to get In the body of the exchange with the needed classes object in the camel route itself?
I am sending a request to a rest api from queue, and getting a response back.if the response is not 201 then I have to push the same request to the queue again for a second time. But when I am pushing it to the queue, I am getting an error, like the one below:
org.apache.camel.ExchangeTimedOutException: The OUT message was not received within: 20000 millis due reply message with correlationID: Camel-ID-01HW828056-64538-1479908182896-32-4 not received on destination: temp-queue://ID:01HW828056-64546-1479908191870-15:3:1.
this is my blueprint.xml
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
xmlns:cxf="http://camel.apache.org/schema/blueprint/cxf"
xmlns:cxf-core="http://cxf.apache.org/blueprint/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd http://camel.apache.org/schema/blueprint/cxf http://camel.apache.org/schema/blueprint/cxf/camel-cxf.xsd http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<camelContext xmlns="http://camel.apache.org/schema/blueprint" trace="false">
<route id="fromqueuecontext">
<from uri="activemq:queue:request?asyncConsumer=true"/>
<unmarshal>
<jaxb partClass="partclassname" prettyPrint="true" contextPath="contextname"/>
</unmarshal>
<setProperty propertyName="capturequeuebean">
<simple>${body}</simple>
</setProperty>
<log message="${property.capturequeuebean.orderNum}"/>
<log message="${property.capturequeuebean.transactionNumber}"></log>
<removeHeaders pattern="*" />
<setHeader headerName="CamelHttpMethod" id="_setHeader1">
<constant>PUT</constant>
</setHeader>
<setHeader headerName="Content-Type" id="_setHeader2">
<constant>application/json</constant>
</setHeader>
<setBody>
<simple>${body.captureRequest}</simple>
</setBody>
<marshal>
<json library="Jackson"/>
</marshal>
<log message="sending body ${body}"/>
<convertBodyTo type="java.io.InputStream" id="_convertBodyTo1" />
<recipientList>
<simple><!--url to be sent --></simple>
</recipientList>
<log message="${header.CamelHttpResponseCode}" />
<choice>
<when>
<simple>${header.CamelHttpResponseCode} != 201 and ${property.capturequeuebean.count} > 0</simple>
<log message="inside 1st when"></log>
<setBody>
<simple>${property.capturequeuebean}</simple>
</setBody>
<bean ref="CaptureBusinessImpl" method="changecount"></bean>
<log message="${body.count}"></log>
<to uri="activemq:queue:request" />
</when>
<otherwise>
<choice>
<when>
<simple>${header.CamelHttpResponseCode} == 201 </simple>
<log message="Sucess"></log>
</when>
<otherwise>
<choice>
<when>
<simple>${property.capturequeuebean.count} == 0</simple>
<log message="exception"></log>
</when>
</choice>
</otherwise>
</choice>
</otherwise>
</choice>
</route>
</camelContext>
</blueprint>
Somewhere a JMSReplyTo header is being specified and the activemq component is creating a consumer on a temp-queue waiting for a response. The response is not being returned in 20,000 ms, so the activemq component is giving up and throwing the error.
The automatic replyTo handling can be disabled by adding the following options to the activemq endpoint:
?disableReplyTo=true&preserveMessageQos=true
See notes here: Camel JMS Component
I have following camel routing configuration.
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<!-- Rest Configuration -->
<restConfiguration component="jetty" port="9092" bindingMode="auto">
<dataFormatProperty key="json.in.disableFeatures" value="FAIL_ON_UNKNOWN_PROPERTIES" />
</restConfiguration>
<rest path="/" consumes="application/json"
produces="application/json">
<post uri="/" type="com.aaa.xxxx.esb.config.xxxxEsbJsonMapping">
<route>
<setHeader headerName="Authorization">
<simple>Basic YWRtaXXXXWRtaW4=</simple>
</setHeader>
<setHeader headerName="CamelHttpMethod">
<constant>POST</constant>
</setHeader>
<setHeader headerName="CamelHttpMethod">
<constant>POST</constant>
</setHeader>
<setHeader headerName="RestEndpointURL">
<simple>
http://${body.serviceURL}?bridgeEndpoint=true
</simple>
</setHeader>
<setBody>
<simple>{"UserDetails": ${body.serviceDataJsonObj}}</simple>
</setBody>
<log message="Exchanged headers : ${headers.RestEndpointURL}" />
<recipientList>
<simple>${headers.RestEndpointURL}</simple>
</recipientList>
</route>
</post>
</rest>
What I need to know is where I can set
matchOnUriPrefix=true
option for the jetty component which I have already configured for camel rest.
According to the Claus Ibsen answer, I changed configuration XML as follows.
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<!-- Rest Configuration -->
<restConfiguration component="jetty" port="9092"
bindingMode="auto">
<dataFormatProperty key="json.in.disableFeatures"
value="FAIL_ON_UNKNOWN_PROPERTIES" />
<componentProperty key="matchOnUriPrefix" value="true" />
</restConfiguration>
<!-- Rest Services -->
<rest path="/" consumes="application/json" produces="application/json">
<post uri="/" type="com.aaa.xxxx.esb.config.xxxxEsbJsonMapping">
<route>
<setHeader headerName="Authorization">
<simple>Basic YWRXXX46YWRtaW4=</simple>
</setHeader>
<setHeader headerName="CamelHttpMethod">
<constant>POST</constant>
</setHeader>
<setHeader headerName="CamelHttpMethod">
<constant>POST</constant>
</setHeader>
<setHeader headerName="RestEndpointURL">
<simple>
http://${body.serviceURL}?bridgeEndpoint=true
</simple>
</setHeader>
<setBody>
<simple>{"SystemUserDetails": ${body.serviceDataJsonObj}}</simple>
</setBody>
<log message="Exchanged headers : ${headers.RestEndpointURL}" />
<recipientList>
<simple>${headers.RestEndpointURL}</simple>
</recipientList>
</route>
</post>
</rest>
</camelContext>
I'm using servicemix apache-servicemix-7.0.0.M2
I upgraded its camel ver 2.16.3 to 2.17.3
Thanks
The rest-dsl is configured using the componentProperty in the restConfiguration:
<restConfiguration component="jetty" port="9092" bindingMode="auto">
<componentProperty key="matchOnUriPrefix" value="true"/>
<dataFormatProperty key="json.in.disableFeatures" value="FAIL_ON_UNKNOWN_PROPERTIES" />
</restConfiguration>
You can find details about this in the documentation: http://camel.apache.org/rest-dsl
U may add inside
route
tag as below:
<route>
<from uri="jetty://foo?matchOnUriPrefix=true"/>
...
</route>
I'm using Apache Camel 2.11.1. It seems that both xml definitions are not equivalent in Camel:
First definition:
<bean id="amq" class="org.apache.activemq.camel.component.ActiveMQComponent"
p:brokerURL="tcp://localhost:61616" p:transacted="true"
p:cacheLevelName="CACHE_CONSUMER" p:concurrentConsumers="20"
p:maxConcurrentConsumers="500" p:idleConsumerLimit="10" />
Second definition:
<bean id="amq" class="org.apache.camel.component.jms.JmsComponent"
p:configuration-ref="jmsConfig" p:transacted="true" p:cacheLevelName="CACHE_CONSUMER" />
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration"
p:connectionFactory-ref="nakedConnectionFactory"
p:concurrentConsumers="20" p:maxConcurrentConsumers="500"
p:idleConsumerLimit="10" />
<bean id="nakedConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
p:brokerURL="tcp://localhost:61616" />
Since the first definition works well on the following route, but the second doesn't.
<route>
<from uri="amq:example.MyQueue" />
<setHeader headerName="myRoutingSlipHeader">
<constant>amq:one#amq:two#amq:three#amq:four</constant>
</setHeader>
<log message="Makan" />
<setExchangePattern pattern="InOut" />
<routingSlip uriDelimiter="#">
<header>myRoutingSlipHeader</header>
</routingSlip>
<setExchangePattern pattern="InOnly" />
<log message="End: ${body}" />
</route>
<route>
<from uri="amq:one" />
<to uri="bean:helloBean?method=stepOne" />
</route>
<route>
<from uri="amq:two" />
<to uri="bean:helloBean?method=stepTwo" />
</route>
<route>
<from uri="amq:three" />
<to uri="bean:helloBean?method=stepThree" />
</route>
<route>
<from uri="amq:four" />
<to uri="bean:helloBean?method=stepFour" />
</route>
The second component definition causes hang during execution.
Seems to work fine for me. I did have to override the spring version with the created archetype. But I used this and it worked fine.
Please find my example project here :
https://github.com/jimternet/camel-activemq
<broker xmlns="http://activemq.apache.org/schema/core" useJmx="true"
persistent="false">
<transportConnectors>
<transportConnector uri="tcp://localhost:61616" />
</transportConnectors>
</broker>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="file:src/data?noop=true" />
<to uri="activemq:personnel.records" />
</route>
<route>
<from uri="activemq:personnel.records" />
<log message="${body}" />
<choice>
<when>
<xpath>/person/city = 'London'</xpath>
<to uri="file:target/messages/uk" />
</when>
<otherwise>
<to uri="file:target/messages/others" />
</otherwise>
</choice>
</route>
</camelContext>
<bean id="activemq" class="org.apache.camel.component.jms.JmsComponent"
p:configuration-ref="jmsConfig" p:transacted="true" p:cacheLevelName="CACHE_CONSUMER" />
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration"
p:connectionFactory-ref="nakedConnectionFactory"
p:concurrentConsumers="20" p:maxConcurrentConsumers="500"
p:idleConsumerLimit="10" />
<bean id="nakedConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
p:brokerURL="tcp://localhost:61616" />
I am unable to split Java objects that are in a List. How I can tokenize / convert Body to to single java objects?
<route id="cleanupMigratedFiles" autoStartup="true">
<from uri="timer://kickoff?period=5s" />
<bean ref="migrationProcessor" method="getCacheDeleteObjects" /> <!-- this gives me a List-of-CacheMigr -->
<log message="\n\t########\n\tCleanupMigrated file: ${body}" />
<pipeline>
<split>
<tokenize /> <!-- How to tokenize a List-of-CacheMigr -->
<convertBodyTo type="era.oddw.entity.CacheMigr" /> <!-- Do I need this? -->
<log message="\n\t########\n\tCleanupMigrated file: ${body}" />
</split>
</pipeline>
</route>
Found the answer after some more reading .. The following tag tokenized List properly : ${body}
Thanks to Camel guys.
<split streaming="true">
<simple>${body}</simple>
<convertBodyTo type="era.oddw.entity.CacheMigr" />
<log message="\n\t########\n\tCleanupMigrated file each: ${body}" />
</split>