Apache Camel / xpath operation result detection - xpath

Given a Camel route that is supposed to extract some inner parts of an XML message, create a new message from it then pass it on.
from(SUB_EXTRACT_XML)
.setExchangePattern(ExchangePattern.InOut)
.setBody().xpath("//mmsg:MyMessage/mmsg:AnyPayload/*", namespaces)
.setBody().simple("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n${in.body}")
.to(...)
For correct input messages like this (an "embedded" xml message is inside defined in schema by xs:any), it's working since the message is what I expect it to be:
<mmsg:MyMessage>
<mmsg:RandomTags/>
...
<mmsg:AnyPayload> <!-- xs:any in xsd -->
<some><xml/><here/></some>
</mmsg:AnyPayload>
</mmsg:MyMessage>
Given there is some issues with the XML message, such as the mmsg:AnyPayload tag is missing, so that the XPATH can't do its job:
<mmsg:MyMessage>
<mmsg:RandomTags/>
...
<some><xml/><here/></some>
</mmsg:MyMessage>
The XPATH will fail to extract the data and the entire XML message (including mmsg:MyMessage) is passed on, which is not intended. I rather throw some exception at this stage.
Question:
Is there a way to check if the xpath expression actually found the element refered to later in the route or if it failed to extract the given element(s)?
I know I could have done some schema validation of the message before and reject rubbish messages, but are there any way to see if a XPath expression fails?

A solution would be to use the choice() DSL in the route like this:
from(SUB_EXTRACT_XML)
.setExchangePattern(ExchangePattern.InOut)
.choice()
.when(xpath("//mmsg:MyMessage/mmsg:AnyPayload", namespaces))
.setHeader("Status", "OK") // just for another example how to transmit some variable between two routes
.setBody().simple("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n${in.body}")
.endChoice()
.otherwise()
.log(LoggingLevel.ERROR, "LoggerName", "Error message; Stop the processing")
.stop()
.endChoice()
.end()
// Just to show the headers are following the route...
.to("DIRECT_GO_FORWARD");
from("DIRECT_GO_FORWARD")
.setExchangePattern(ExchangePattern.InOut)
.choice()
.when(header("Status").isEqualTo("OK"))
.bean(new SampleProcessor())
...
.end()
...
.to("...");
the second route is just there to show you can use the header set in the first route (and the body too).

Related

Storing the output json parameters as variable in jmeter using it in next request

I have a http request which gives json output as :
{
"MESSAGE_CODE":200,
"MESSAGE_DESCRIPTION":"OTP Generated Successfully",
"data":
{
"otp":"123456",
"otpGeneratedDate":"yyyy-mm-dd"
}
}
I want to use otp as the input parameter in json for my next http request.
I have added JSON extractor with following configuration :
enter image description here
Names of created variable : OTP
JSON path expressions : $..data.otp
Match No. : 1
But still when I am calling this parameter as
"otpNumber": "${OTP}" in my next input JSON http request, its not getting called.
and value is passed as ${OTP} for otpNumber
How can I handle this
As per JMeter Documentation:
Variables, functions (and properties) are all case-sensitive
So you need to change this line:
"otpNumber": "${OTP}"
to this one:
"otpNumber": "${otp}"
and your test should start working as expected.
You can observe which JMeter Variables are defined along with their values using Debug Sampler and View Results Tree listener combination.

Spring DSL: Use message header in transformer

I have xml like below, in the runtime i want to dynamically process . First time first then etc
<tag1>
<tag2>
.
.
.
</tag2>
<tag2>
.
.
.
</tag2>
</tag1>
CASE1: Working code sending static xslt parameter:
.transform(Transformers.xslt(this.config.getSome().getXslt(), xsltParameters(1 or 2)))
public Tuple2[] xsltParameters(int number)
{
final SpelExpressionParser parser = new SpelExpressionParser();
final List<Tuple2<String,Expression>> parameters = new ArrayList<>();
parameters.add(Tuples.of("Id1", parser.parseRaw(String.format("headers['%s']",ID1))));
parameters.add(Tuples.of("Id2", parser.parseRaw(String.format("headers['%s']",ID2))));
parameters.add(Tuples.of("tagNumber", parser.parseRaw(String.format("%d",number))));
return parameters.toArray(new Tuple2[]{});
}
Above is working and transformer can trasnform xslt.
CASE2: Getting error when i want to pass dynamic number:
.transform(Message.class, m-> Transformers.xslt(this.config.getSome().getXslt(), xsltParameters())
xsltparametr(){
parameters.add(Tuples.of("tagNumber",
parser.parseRaw(String.format("headers['%s']",COUNT))));
}
ERROR while transforming:
Error on line 158 of test.xslt(Prior to line 158 is working fine)
XPTY0004: A sequence of more than one item is not allowed as the first argument of starts-with() ("xxmmyty", "xxmmyty")
My xslt at line 158
<xsl:if test="starts-with(xc:tag1/xc:tag2[$count]/xc:trade/xcml:header/xcml:Identifier/xcml:tradeId[#IdScheme='urn:abcd.something'], 'XYZ')" >
<xsl:value-of select="tag" />
</xsl:if>
In CASE1 with same xslt file i can pass above code but in case of dynamic passing(CASE2) it is failing.
First of all it looks like you use old enough Spring version.
Would be grate to upgrade at least to 5.0.x.
Secondly it isn't clear what is your xsltParameters(). You hide it from us and we fully don't know what you do over there. Although we can assume that you are fully based on this method:
#SafeVarargs
public static XsltPayloadTransformer xslt(Resource xsltTemplate,
Tuple2<String, Expression>... xslParameterMappings) {
So, you build in that xsltParameters() an array of Tuple2. OK. So, get access to the header in the param you need to build an appropriate expression.
I'm rally sure that it can be a FunctionExpression:
.transform(Transformers.xslt(this.config.getSome().getXslt(),
Tuples.of("foo", new FunctionExpression<Message<?>>(m -> m.getHeader.get("int_val"))))).
What you do with the lambda can't be combined with a transformer from that factory.
UPDATE
You need to modify your xsltParameters() to accept a String instead. Which is going to be just raw expression representation.
So, your CASE1 is going to be like this:
.transform(Transformers.xslt(this.config.getSome().getXslt(), xsltParameters("'1'")))
With meaning as literal expression.
The CASE2 is going to be like this:
.transform(Transformers.xslt(this.config.getSome().getXslt(), xsltParameters("headers.int_val")))

How to conditionally extract data with the if controller after sampling?

I am extracting the HTML response code from a samplier. I would like to use the if controller to conditionally extract more information if the right response code is returned.
So teh Get Message Response Extractor would save the response code to the variable: GetMessageResponse.
Then the If Controller would check if GetMessageResponse is 200:
If this is true then extract more information like this:
However I am not getting anything in ResponseText, what am I doing wrong?
You can do it in one shot if you switch to the JSR223 PostProcessor, the relevant Groovy code would be:
import com.jayway.jsonpath.JsonPath
if (prev.getResponseCode() == '200') {
def responseText = JsonPath.read(prev.getResponseDataAsString(),'$.MessageObj.Text').get(0)
vars.put('ResponseText', responseText)
}
else {
vars.put('ResponseText','Response code is: ' + prev.getResponseCode())
}
References:
Jayway JsonPath
Groovy: Parsing and producing JSON
Apache Groovy - Why and How You Should Use It
In JMeter what you do is extract whatever the response and set Default Value field to something that will be filled when response will not contain extraction, for example for JSON Extractor:
What you show will not work because you put Extractors in IfController, as there is no Sampler, nothing will happen due to scoping rules.
Also when you'll need for another thing to use If Controller, no need to extract response code, just use:
${JMeterThread.last_sample_ok}

<logic:messagesPresent> tag in Struts1 not looping through multiple errors in Action Messages

I have a field in a bean that is failing 2 validations, as such 2 messages are being inserted in ActionMessages with the following command:
validationErrors.add("field1", new ActionMessage("Phone number is greater than 10 digits", false));
validationErrors.add("field1", new ActionMessage("Phone number has invalid characters", false));
Although I see the errors in the ActionMessages object (by setting a breakpoint in the debugger), only the first one gets displayed in my JSP, where I have:
<logic:messagesPresent message="true">
<html:messages id="message" property="field1" message="true">
<logic:present name="message">
<c:out value="${message}"/>
</logic:present>
</html:messages>
</logic:messagesPresent>
Why is only the first message displayed, when <html:messages> should loop through all the messages where the property is "field1"?
I ended up figuring out my issue and it had to do with how I am creating the new ActionMessage.
When you use:
public ActionMessage(<error message>, false)
While it allows you to display a literal value by using the <html:messages> tag in conjunction with either a <bean:write> or <c:out>, it won't iterate over multiple messages for a given property, why I don't know.
I tested and found that if I use a resource bundle and create the ActionMessage with a standard:
public ActionMessage(<key in resource bundle>)
I am able to display multiple messages for a single property.
Unfortunately, because I am using hibernate validator I don't want to use a resource bundle and struts to replace the values (would rather have the hibernate validator annotation replace values) and will likely just display a single message at a time for now.
Struts <logic:messagesPresent> tag checks that the messages exist on the current request.
Messages are found in the request under the key Globals.MESSAGE_KEY. If you use attribute message only messages are checked.
By default the tag will retrieve the request scope bean it will
iterate over from the Globals.ERROR_KEY constant string, but if this
attribute is set to true the request scope bean will be retrieved
from the Globals.MESSAGE_KEY constant string. Also if this is set to
true, any value assigned to the name attribute will be ignored.
The <html:messages> tag is used to display the messages if specified attribute message is true.
Now you have used a property attribute that filters messages for the given property.
Name of the property for which messages should be displayed. If not
specified, all messages (regardless of property) are displayed.
If you have only one message with the field1 property, then only one message will be displayed.
Look here how can you use action messages object
ActionMessages messages = new ActionMessages();
messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage("error.common.field1.required");
saveMessages(request, messages); // storing messages as request attributes
Properties file:
error.common.field1.required = Field1 is required.
And display messages
<logic:messagesPresent message="true">
<html:messages id="message" message="true">
<bean:write name="message"/><br/>
</html:messages>
</logic:messagesPresent>
It will loop all massages under the global message key. If you want to use custom key you can use it with the parameter of ActionMessage
messages.add("field1", new ActionMessage("error.common.field1.required");
to retrieve the message
<logic:messagesPresent message="true">
<html:messages id="message" property="field1" message="true">
<bean:write name="message"/><br/>
</html:messages>
</logic:messagesPresent>

Spring request mapping wildcard exceptions

Can I put /** wildcard in a middle of request mapping such as: "/some/resource/**/somthing"
In Spring 3 I can do this
#RequestMapping("/some/resource/**")
to map
/some/resource/A -> ControllerMethod1
/some/resource/A/B -> ControllerMethod1
/some/resource/A/B/C/D/E/F -> ControllerMethod1
for any number of paths parts
However this mapping is too greedy and will not allow me to map a sub URL #RequestMapping("/some/resource/**/somthing") to another controller such as
/some/resource/A/somthing -> ControllerMethod2
/some/resource/A/B/somthing -> ControllerMethod2
/some/resource/A/B/C/D/E/F/somthing -> ControllerMethod2
How can i do this?
I thinks it's not possible to use that ant style in url mapping as you require, because it will stop on the next path separator character '/'.
I would suggest you to try 16.3.2.2. URI Template Patterns with Regular Expressions in order to map just the last part of the request (haven't tried this approach yet).
Also you can match the rest of the request using PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, and apply some expression there. Check this post.
Otherwise you should use request parameters to match that condition 16.3.2.6. Request Parameters and Header Values.
You can narrow request matching through request parameter conditions such as "myParam", "!myParam", or "myParam=myValue". The first two test for request parameter presense/absence and the third for a specific parameter value. Here is an example with a request parameter value condition.
In this case you will map something like that using params
#RequestMapping(value = {"/some/resource/**"}, params="somthing")
or use the annotation request parameter with not required attribute in method signature:
public void test(#RequestParam(value = "somthing", required=false) String str) {

Resources