in my program, there's request to an API and then message in the incoming channel is stored to a file. My problem is I get some kind of error in my payload for some request. I want to store such error file in another directory. How can I add a filter expression, so that such payload is stored in another directory. My code is:
<int:chain input-channel="abcconnect">
<int:filter id="errorout" expression="payload.containsKey('errorCode')"/>
<file:outbound-channel-adapter
id="apiResponseWriter"
directory="${error.path}"
filename-generator-expression="'abc-' + T(java.lang.System).currentTimeMillis() + '.json'"
delete-source-files="false" />
</int:chain>
The error in the payload file is: {"TwitterApi":{"errorCode":400,"errorMsg":"beginTime or endTime is later than now(1472041145)","success":false,"cost":11,"params":
The filter lets the message to pass and go ahead in the flow if its predicate returns true. In your case it looks like messages with the errorCode should go down the flow.
So, I guess you have to negate the expression and also add discard-channel to let the false message to be send to different flow for analyze, processing or storing to a different file like your requirement.
Related
My task is to implement a webservice that:
consumes an XML file on a POST endpoint
in happy flow, it returns a DTO as JSON + HTTP 2xx
the incoming XML file is validated against a XSD; if the validation fails, a JSON with a list of all validation errors is returned (including the line, column, error) with HTTP Bad request
the application exposes two endpoints, only one of them should be validated
I have started the implementation with Spring Boot + web, using regular #PostMapping which has "consumes" and "produces" set to application/xml and application/json, respectively. The usual flow works perfectly fine. Now, I stumbled upon the issue of validating the incoming payload. What I figured out:
1) I have to validate the payload before it is converted (marshalled) to an object.
2) Once validated, I have to either:
allow further processing
stop any further processing, write the error object to the response and set the status code to 400 Bad request
My approaches were:
1) using a RequestBodyAdvice, more specifically the beforeBodyRead method implementation. I had the following issue here: I don't know how to write anything to the output in case the validation fails.
2) using a Filter (I've extended OncePerRequestFilter) - fortunately, I can read the request (request.getInputStream()) and write to the response (response.getOutputStream()).
However, how can I do the selective filtering (as mentioned, I only want to validate one single endpoint)?
Are there any other alternatives for placing the incoming request XSD validation? Is spring-web the appropriate choice here? Would you recommend some other library / framework?
To validate xml against xsd schema, my preference is XML Beans. It is very easy to use. Other options are JABX, Castor. Take a look at Java to XML conversions?.
You will need to jar using xsd schmema and will need to put it in the classpath of your application so that it's classes are available for you for validation. Please take a look at this blog.
You can use validation API as mentioned here.
I would prefer to write validation code in the aspect so that it can be reused with other APIs.
If validation fails, throw valid exception from the aspect itself.
If validation is passed, process your input string that you receive.
Please let us know if you need any more information.
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 using the Apache Camel DSL and trying to figure out how to route a message, like so. Here's the logic, without any Camel DSL.
Message enters queue
Call bean
If bean doesn't have exception
Call another bean with original message
My issue is that I'm not sure how to get to step 3.a using the original message. The call to bean changes the message. I can use a multicast, two pipelines, and header variables, but from what I can tell those header variables are in scope within a pipeline.
Any ideas are appreciated! Thanks!
You can use the multicast eip in sequence mode
<from uri="somewhere"/>
<multicast>
<to uri="bean:beanA"/>
<to uri="bean:beanB"/>
</multicast>
The multicast runs in sequence mode by default, so first invoking beanA. And if that is a success, it invokes beanB, but with the original message (a copy of the same message that we called beanA with).
You can read about the multicast EIP here: http://camel.apache.org/multicast.html
from your 2nd bean, just call exchange.getUnitOfWork().getOriginalInMessage()
I ended up storing the content of the message in a variable and setting the message back to that content after it was changed, like so...
<!-- store original content in header variable -->
<camel:setHeader headerName="marc"><camel:simple>${body}</camel:simple></camel:setHeader>
<!-- run some logic, output gets saved as another header variable, message as this point is now that output -->
<camel:bean ref="getPidsForUpdate"/>
<camel:setHeader headerName="pids"><camel:simple>${body}</camel:simple></camel:setHeader>
....set a bunch of variables based on that output, and then
<!-- get original message to run some more logic-->
<camel:setBody><camel:simple>${headers.marc}</camel:simple></camel:setBody>
I have a service need to validate parameters. For example,my request is:
<purchasePackage xmlns="http://bss.internal.service.boss.sysway.com/">
<entitlementId xmlns="">20100812151324</entitlementId>
<subscriberId xmlns="" />
<packageId xmlns="">SZ_VOD</packageId>
<deviceId xmlns="">801830456396</deviceId>
<effectionTime xmlns="">2010-08-25 00:00:00</effectionTime>
<expirationTime xmlns="">2009-08-25 00:00:00</expirationTime>
</purchasePackage>
EffectionTime is later than expirationTime, so I need response a error message out instead of send it to real service. How to do this?Someone can give me some advises?Best regards.
Use WSO2 ESB server and define custom proxy. When proxy receives the above request, pick both date values using xpath (use filter mediator) and compare which is greater value. You can use XPATh function for that(date comparison)
if greater allow to send to the service ..else execute fault sequence..
http://wso2.org/project/esb/java/4.0.3/docs/mediators/filter.html
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