I'm trying to set message expiration within a Blueprint XML Camel route. The value I want to set as the expiration is held within the message body (a protobuf).
Here's my code:-
<setHeader headerName="AMQ_SCHEDULED_DELAY">
<method bean="NotificationScheduler" method="postponeSending" />
</setHeader>
<setHeader headerName="JMSExpiration">
<method bean="NotificationScheduler" method="getExpiry" />
</setHeader>
<setHeader headerName="ExpirationTest">
<method bean="NotificationScheduler" method="getExpiry" />
</setHeader>
<to uri="activemq:notifications.splitter" />
As you can see from the screen shot below, I'm successfully setting two of the three headers but the setting for "JMSExpiration" (as per this thread) has had no effect.
I know I could alternatively use the Recipient List pattern to dynamically set the uri - i.e. pull the expiry out of the message data and append the ?timeToLive=... option. However, this seems a little clunky to me.
Is there a way to set expiration via setHeader within the XML?
Thanks,
J.
Change your uri to: activemq:notifications.splitter?preserveMessageQos=true and you should be fine.
Some JMS headers can be "manually" overriden like you are trying to by using this option.
Please note that you might want to take some precausion, since if you are listening on one JMS endpoint, the arriving messages will have JMS headers populated, and when you send the message out in your "to", the message will keep JMSDeliveryMode, JMSExpiration and JMSPriority. This might or might not be what you want.
Related
I have to ask about camel route behaviour, which is silly (but simple to understand) logical description.
In main themes - i need to push info from exchange header of one route to another one.
It's all about CMDB system and monitoring tool zabbix.
Well, at the first i have a route which can switch CI state in CMDB:
<route>
<description> route catching CI ID in jms queue, check it on exist and switch CI state to incident
</description>
<from uri="jms:switchCIStateQueue"/>
<filter>
<simple>${body} regex '[\d]+'</simple>
<to uri="bean:otrsCIApi?method=getCIBodyByID(${body})"/>
<filter>
<simple>${body} regex '\{.+?\}'</simple>
<marshal>
<json library="Jackson"/>
</marshal>
<unmarshal>
<json library="Jackson" unmarshalTypeName="ts.team.otrs.ci.OtrsCI"/>
</unmarshal>
<to uri="bean:otrsCIApi?method=switchOTRSCIState(${body})"/>
</filter>
</filter>
</route>
It's working good, but i have to use this action from another route, which have many checks, filters and choices.
My problem is that i don't have a CI ID as a body (but keep it in header) in depth of main logical route.
<route>
<description>Route catch triggerid
and creates a ticket in OTRS, link it to host
</description>
<from uri="direct:zab_trig_2_otrs_tick"/>
<to uri="bean:zabbixApi?method=getTriggerByID(body)"/>
<filter>
<simple>${body} regex '\{.+?\}'</simple>
<marshal>
<json library="Jackson"/>
</marshal>
<unmarshal>
<json library="Jackson" unmarshalTypeName="ts.team.zabbix.trigger.SingleTrigger"/>
</unmarshal>
<setHeader headerName="ZabbixTrigger" id="_setZabbixTrigger">
<simple>${body}</simple>
</setHeader>
<!-- search CI in OTRS -->
<to uri="bean:otrsCIApi?method=searchCI(${body.getHosts().get(0).getName()})"/>
<!-- Array of CI ID like [] or ["1"] -->
<split streaming="true">
<simple>${body}</simple>
<!-- place it in header-->
<setHeader headerName="HostID">
<simple>${body}</simple>
</setHeader>
<to uri="bean:otrsLinkApi?method=ListLinkedTicketsTitleFiltered(${body},${header.ZabbixTrigger.getDescription()})"/>
<!-- return JSONArray with State=open otrs Tickets ID -->
<choice>
<when id="ticketslist_empty">
<simple>${body} == ''</simple>
<!-- Create ticket, connect it to host in OTRS -->
<to uri="bean:otrsTicketApi?method=createNewTicket(${header.ZabbixTrigger.getDescription()},${header.ZabbixTrigger.getPriority()})"/>
<!-- return body body with ticket id, create link with ${header.HostID} -->
<to uri="bean:otrsLinkApi?method=LinkAdd(${header.HostID},${body})"/>
<!-- Here i need to switch CI state if incident priority is higher than 3(Normal)-->
<when>
<simple>${header.ZabbixTrigger.getPriority()} > 3</simple>
<!-- here i need to send ${header.HostID} to previous described route (jms:switchCIStateQueue)-->
</when>
</when>
</choice>
</split>
</filter>
</route>
So, there is piece of this route:
<when>
<simple>${header.ZabbixTrigger.getPriority()} > 3</simple>
<!-- here i need to send ${header.HostID} to previous described route (jms:switchCIStateQueue)-->
</when>
where i need to send some info from my header to jms:switchCIStateQueue (or route direct, it's no matter where to).
I hope, my description of problem is quite full and simple.
OK.
You asked two questions:
i need to push CIID into first described route
You have to push a jms message to jms:switchCIStateQueue
so, in your source route (second "big one") it should be like:
<to uri="jms:switchCIStateQueue"/>
whatever is in Exchange headers will be in JMS message headers. Exchange message body will be JMS message body.
if you will do it in source route with code as is, there will be JMS header HostID and your first route which gets that JMS message has access to it as ${header.HostID}
Then depends on what your otrsCIApi.getCIBodyByID expects and does your call may look like
a. <to uri="bean:otrsCIApi?method=getCIBodyByID(${header.HostID})"/>
b. But if expected parameter for 'getCIBodyByID' has a different structure/format with something more that CIID you have to build it properly either when you send it to the queue (in "big" route) or after you get a message from queue.
How can i place ${header.HostID} into body
again it depends on what is a structure/format of expected JMS message body
a. just place HostID header value in the body as is:
<when>
<simple>${header.ZabbixTrigger.getPriority()} > 3</simple>
<!-- here i set ${header.HostID} into body -->
<body>
<simple>${header.HostID}</simple>
</body>
<!-- here i can set ${header.HostID} into another header if i'd like to -->
<setHeader headerName="CIID">
<simple>${header.HostID}</simple>
</setHeader>
<!-- finally I send message to queue -->
<to uri="jms:switchCIStateQueue"/>
</when>
b. Something more than just CIID value - build it as needed (in place of <body> element there could be processor or another bean method call which will do that.
Did I understand your questions properly and is it what you are looking for?
I am using the claim check pattern from spring-integration, but I would like to have the message stored with a custom ID. This should be easy, as the message store implementations are using the incoming message header id to store the message. Is it possible to overwrite the value of the message header id using a header enricher or/and a header filter?
Message header id and the message store
The SimpleMessageStore, as well as the JdbcMessageStore, are using the incoming message ID to store the message. In the addMessage method (the example is from the SimpleMessageStore) we have:
this.idToMessage.put(message.getHeaders().getId(), message);
To have a custom ID it should be enough to have a header enricher before claim check in, where the value of the id header is replaced with a custom value. For example:
<int:header-enricher input-channel="gateDocCheckInReqChannel"
output-channel="gateDocCheckInEnrichedChannel">
<int:header name="id" expression="payload.getDocumentID()" overwrite="true" />
</int:header-enricher>
<int:claim-check-in input-channel="gateDocCheckInEnrichedChannel"
output-channel="gateDocCheckInReplyChannel" message-store="messageStore" />
It does not work; the message header id is not overwritten. I tried having a header filter on ID before the header enricher, but it does not work either.
Related
I found this old post on removing the headers fields that is undone by some internal logic:
http://forum.spring.io/forum/spring-projects/integration/74099-remove-header-fields
Also, there is this closed issue INT-923 on a message handler that undoes header removals.
https://jira.spring.io/browse/INT-923
It is supposed that issue INT-1135 on header filters fixes this behavior.
https://jira.spring.io/browse/INT-1135
Actually the ID and TIMESTAMP header are read-only (MessageHeaderAccessor):
protected boolean isReadOnly(String headerName) {
return (MessageHeaders.ID.equals(headerName) || MessageHeaders.TIMESTAMP.equals(headerName));
}
They are specified for the concrete Message, which is immutable.
Those header are designed for the internal usage in the framework and they can't be changed.
For such a use-case like yours, there is need to introduce addition businessKey and get deal with that not thinking about those interlan headers.
Since you say that you want to determine somehow a message by the ID from the store after claim-ckeck, I suggest you to consder to use MetadataStore to keep ID <-> businessKey pairs to have an ability to restore them in the future somehow.
Of course, you can try to use MutableMessageBuilder for your specific use-case:
MutableMessageBuilder.fromMessage(message)
.setHeader(MessageHeaders.ID, UUID.randomUUID())
.build()
But ID must be UUID anyway.
And right: HeaderFilter doesn't remove those read-only headers as well.
I have overridden the http_requestMethod by doing as like below. The request coming is Post which I have removed from Headers and added PUT.
<int:header-filter input-channel="headerFilterChannel"
header-names="http_requestMethod" output-channel="closeAndCloneOldTasksHeaderEnricherChannel" />
<int:header-enricher input-channel="closeAndCloneOldTasksHeaderEnricherChannel"
output-channel="caresToSfdc">
<int:header name="http_requestMethod" value="PUT" />
</int:header-enricher>
***Before Overriding Log***
GenericMessage [payload=com.cardinalhealth.chh.exception.model.GenericResponse#1948829c, headers={http_requestMethod=POST, replyChannel=org.springframework.messag
**After Overriding Log :**
GenericResponse#142cd5fd, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#5710c249, http_requestMethod=PUT,
I'm hoping someone can explain a bit more about how to configure Camel to marshal and unmarshal data. I have a route that calls a bean in order to determine a recipientList. This is based on the contents of the message (a protobuf).
The route config looks like:-
<route id="Splitter">
<from uri="activemq:notification.splitter" />
<unmarshal ref="notificationProto" />
<recipientList>
<method bean="NotificationSplitter" method="splitNotification" />
</recipientList>
</route>
The bean works fine, but the downstream routes complain that:-
org.apache.camel.RuntimeCamelException: java.lang.RuntimeException: Unable to find proto buffer class
The downstream routes have exactly the same protobuf dataFormat config as the route above. If I route directly to the downstream queues (i.e. bypass the bean and hardcode the "to" queues), which means I can also skip the unmarshalling step, it works fine.
I guess that I need to re-marshal the data before Camel puts the messages onto the destination queues but I don't know how to configure this within the XML. I've tried simply adding...
<marshal ref="notificationProto" />
...after the recipientList has been determined but it doesn't do it (I assume because Camel has already dispatched the messages by then).
An alternative could be to do the unmarshalling from within the bean as then the data on the exchange will presumably remain unaltered. I'm not quite sure how to do this. Would it work?
Thanks for any tips.
J.
Yeah the data formats are not an endpoint that's easy to send a message to, as otherwise you can use the Routing Slip EIP pattern instead, and send the message to the data format first, and then the destination. http://camel.apache.org/routing-slip.html
Though you could have a little route
<route>
<from uri="direct:marshalMe"/>
<marshal ref="notificationProto" />
</route>
And then use the routing slip, to set to "direct:marshalMe,whereYouWannaGoNext".
An alternative is to use an interceptor, and intercept sending to endpoints (you can filter by wildcards, or reg exps), and then do the marshal first. http://camel.apache.org/intercept
See that link for more details.
while 'playing around' with Camel using Spring DSL, I came across the following problem. Suppose the expected message flow looks like this:
client sends HTTP POST message with XML body to CAMEL
CAMEL proxies HTTP POST message towards server, with the URI slightly adapted using
info from the received XML body (eg: use XPATH to filter out a certain parameter)
after CAMEL has received a reply, CAMEL sends HTTP PUT message towards server, using parameters out of the XML body received in 1
So something like:
<route>
<from uri="...">
<to uri="...">
<to uri="...">
</route>
Question: how do I store the parameters in Spring DSL in step 1, so that I can use them later in step 3 ?
So, I would like to extract XML parameters out of the XML body of the message received in step 1 and put them into variables, which I then later on can use to compose the message to be sent in step 3.
For extracting the parameters, I was thinking of using XPATH. That looks ok, but I just don't see how to put the output of the XPATH into a variable and then use that variable later on ... (syntax ??)
Note: as you can see, my development knowledge is rather limited ... sorry for that. But it would still be great if someone could help with this :).
you can set store data in the Exchange properties or message headers like this...
.setHeader("ID", XPathBuilder.xpath("/order/#id", String.class))
.setProperty("ID", XPathBuilder.xpath("/order/#id", String.class))
and then retrieve them in a bean/processor from the Exchange like this...
String propId = (String) exchange.getProperty("ID");
String headerId = (String) exchange.getIn().getHeader("ID"); }
I leave you some examples:
<setHeader headerName="token">
<constant>someValue</constant>
</setHeader>
<setHeader headerName="userName">
<simple>${properties:userName}</simple> //from config
</setHeader>
<setProperty propertyName="bodyBkp">
<simple>${in.body}</simple>
</setProperty>
<setProperty propertyName="orderNumber">
<xpath resultType="String">//item[1]/orderNumber/text()</xpath>
</setProperty>
Getter
${exchangeProperty[orderNumber]}
${in.headers.token}
Documentation
Check the simple expression language:
http://camel.apache.org/simple.html
Sometimes looking at the test cases of Camel can be helpful as well, in particular for Spring DSL:
setProperty with Spring DSL
setHeader using XPATH with Spring DSL
simple expression language test
I am trying to use CAMEL as HTTP proxy and I would like to extract a parameter from an incoming HTTP message with a XML body. This parameter I would then like to add in the header of a HTTP POST message towards another endpoint (another server).
Example: the XML body contains a parameter called "subscriptionId". The value of this field "subscriptionId" is then to be used in the uri of the outgoing HTTP POST message.
So, if subscriptionId=1234567, I want the uri in the HTTP POST message to be like:
POST /webapp/createnewsubscription?subscriptionId=1234567
I am using Spring DSL to create my Camel routes.
Anyone an idea how to do this ?
Thanks,
Jan
I presume you want to POST to first URL with XML as payload.
First you would need to use XPath component to get value for your XML tag and then setBody to pass parameter to proxied request (optionally you could switch from POST to GET).
Something like this should work:
<route>
<from uri="jetty:http://127.0.0.1:8080/myapp"/>
<setHeader headerName="subscriptionId">
<xpath resultType="java.lang.String">//subscriptionId/text()</xpath>
</setHeader>
<!-- if you need to convert from POST to GET
<setHeader headerName="CamelHttpMethod">
<constant>GET</constant>
</setHeader>
-->
<setBody>
<simple>subscriptionId=${in.headers.subscriptionId}</simple>
</setBody>
<to uri="jetty:http://127.0.0.1:8090/myapp?bridgeEndpoint=true&throwExceptionOnFailure=false"/>
</route>
You should be able to test it from command line say with wget:
$ cat 1.txt
<a>
<subscriptionId>123</subscriptionId>
</a>
$ wget --post-file=1.txt --header="Content-Type:text/xml" http://127.0.0.1:8080/myapp
You could use second route to test responses like this:
<route>
<from uri="jetty:http://127.0.0.1:8090/myapp"/>
<to uri="log:mylog?level=INFO"/>
<setBody>
<simple>OK: ${in.headers.CamelHttpMethod}: ${in.headers.subscriptionId}</simple>
</setBody>
</route>
And if you set camelContext to 'trace' you should see lots of info in your log of what's going on on every step of the processing:
<camel:camelContext id="camel" trace="true" xmlns="http://camel.apache.org/schema/spring">