We have a independent bundle creating factory parts based on a few header variables [direct-vm:createPartService].
My application needs loop through a list of parts and call this bundle repetitively. My partBean.update method just retrieve part info from a part list, setup the header variables and then invoke the createPartService.
I uses the following doWhile loop. I find when createPartService return status is ERROR, the following loop works perfectly. The workflow exits from the loop and goes to exception handling section. But, when the createPartService return status never error, i.e., every createPartService is SUCCESS, the while loop never stop.
<loop doWhile="true">
<simple>${header.STATUS} == 'SUCCESS'</simple>
<bean ref="partBean" method="update"/>
<to uri="direct-vm:createPartService"/>
<choice>
<when>
<simple>${header.STATUS} == 'ERROR'</simple>
<throwException exceptionType="java.lang.Exception message="${header.ERROR_MESSAGE}"/>
<to uri="mock:endPartsError"/>
</when>
<otherwise>
<log message="### update part succeeded" loggingLevel="DEBUG"/>
</otherwise>
</choice>
</loop>
Then, I tried and another condition,
<simple>${header.STATUS} == 'SUCCESS' && ${exchangeProperty[CamelLoopIndex]} < ${headers.PART_COUNT}</simple>
Camel seems does not accept logic 'and'. The above evaluation always returns false. I also tried replace '&&' with 'and', or the corresponding html entity. There is no difference. Any suggestion on looping through a list and exit gracefully when there is no error?
You did not set your loop terminate condition properly.
Use a bean to control your looping terminate condition
Camel Spring XML
<loop doWhile="true">
<simple resultType="java.lang.Boolean">${bean:partBean?method=isContinueLoop}</simple>
... do stuff here ...
</loop>
Java Bean
public Boolean isContinueLoop(Exchange exchange) throws Exception {
// System.out.println("LoopIndex: " + exchange.getProperty(Exchange.LOOP_INDEX));
Boolean result = false;
String status = exchange.getIn().getHeader("STATUS", String.class);
Integer loopIndex = exchange.getProperty(Exchange.LOOP_INDEX, -1, Integer.class);
int maxSize = exchange.getIn().getHeader("PART_COUNT", Integer.class);
if (status != null && status.equals("SUCCESS") && loopIndex < maxSize) {
result = Boolean.TRUE;
}
return result;
}
The main problem to the condition ${header.STATUS} == 'SUCCESS' && ${exchangeProperty[CamelLoopIndex]} < ${headers.PART_COUNT} is not on the AND operator, but the value of ${exchangeProperty[CamelLoopIndex]}.
If you uncomment the system print in the java bean, you could observe the CamelLoopIndex's value is change as : null -> 0 -> 1 -> 2 -> ..., the null value is the root problem to make your condition return false. To avoid this in Camel Spring XML Simple language, you need a null check on CamelLoopIndex with a OR operator.
In this sense, you will need both a AND operator and a OR operator. However, this is unsupported by simple language currently as state in Simple language page Section 'Using and / or'.
<split> offers an excellent solution for my use case. In the following code sample, the findParts method returns a list of Part object. The update method setups header variables needed for web service call (createPartService).
<route id="WebServiceUpdateParts">
<from uri="direct:updateParts"/>
<split stopOnException="true">
<method ref="partBean" method="findParts"/>
<bean ref="partBean" method="update"/>
<to uri="direct-vm:createPartService"/>
</split>
<to uri="mock:endUpdateParts"/>
</route>
If you wanna stop the loop after specific round, you can use this:
from("direct:test").setProperty("CamelLoopIndex",constant(0)).loopDoWhile(simple("${exchangeProperty.CamelLoopIndex} <= 3")).log("=====${exchangeProperty.CamelLoopIndex}");
Notice that you must declare CamelLoopIndex property explicitly before the loop, otherwise Camel will not understand the contiditon when starts up the route, because CamelLoopIndex property is created after the loop begins.
Related
I have some XML in the below format.
<user uid="0001">
<FirstName>John</FirstName>
<LastName>Smith</LastName>
<ImagePath>images/0001.jpg</ImagePath>
<flightno>GS1234</flightno>
</user>
<user uid="0002">
<FirstName>Luke</FirstName>
<LastName>Dixon</LastName>
<ImagePath>images/0002.jpg</ImagePath>
<flightno>TD1234</flightno>
</user>
<user uid="0003">
<FirstName>Paul</FirstName>
<LastName>Kerr</LastName>
<ImagePath>images/0003.jpg</ImagePath>
<flightno>GS1234</flightno>
</user>
This is a small sample, there are a couple 100 of these.
I have used E4x filtering to filter down another set of XML data that produces an as3 array. The array contains some flight numbers (eg : [GS1234,PB7367,TD1234].
I'm wondering how I can filter my XML (as shown above) to only show the users whose 'flightno' EXISTS in the AS3 array.
I'm guessing its some sort of E4X query but I can't seem to get it right!
Thanks!
// Don't mind me using this trick with inline XML, it's not the point.
// It's here just to make it possible to copy and paste code
// with multiline XML sample.The actual solution is a one-liner below.
var usersXML:XML = new XML(<x><![CDATA[
<data>
<user uid="0001">
<FirstName>John</FirstName>
<LastName>Smith</LastName>
<ImagePath>images/0001.jpg</ImagePath>
<flightno>GS1234567</flightno>
</user>
<user uid="0002">
<FirstName>Luke</FirstName>
<LastName>Dixon</LastName>
<ImagePath>images/0002.jpg</ImagePath>
<flightno>TD1234</flightno>
</user>
<user uid="0003">
<FirstName>Paul</FirstName>
<LastName>Kerr</LastName>
<ImagePath>images/0003.jpg</ImagePath>
<flightno>GS1234</flightno>
</user>
</data>
]]></x>.toString());
// once again, the way I create sample departingXML
// is not important, it's just for copying and pasting into IDE.
var departingXML:XML = new XML(<x><![CDATA[
<flights>
<flight>
<number>GS1234</number>
<date>10/11/2015</date>
<time>1440</time>
</flight>
<flight>
<number>TD1234</number>
<date>10/11/2015</date>
<time>1450</time>
</flight>
</flights>
]]></x>.toString());
// 1. create filter array
var flightNoArray:Array = [];
departingXML.flight.number.(flightNoArray.push(toString()));
trace(flightNoArray); // GS1234,TD1234
trace(typeof(flightNoArray[0])); // string
// 2. filter users:
var list:XMLList = usersXML.user.(flightNoArray.indexOf(flightno.toString()) >= 0);
trace(list); // traces users 0002 and 0003
I wouldn't call it efficient or at least readable though.
// Note: this line is somewhat queer and I don't like it,
departingXML.flight.number.(flightNoArray.push(toString()));
// but this is the only way I can now think of to get and array
// of strings from an XMLList nodes without a loop.
// I would advise to use a readable and simple loop instead.
usersXML.user -- this gets you an XMLList with all nodes named "user"
usersXML.user.(some condition) -- this filters the XMLList of user nodes given a condition
flightNoArray.indexOf(flightno.toString()) >= 0 -- and this is a filter condition
flightno.toString() -- gets you a string inside flightno child
REFERENCE: Traversing XML structures.
Explanation of the search trick in the note above.
UPDATE: it turned out in comments that it was also the way the filter array was populated that was causing trouble. The code below demonstrates some more E4X.
This is how the filter list was created and what was actually happening:
// once again, the way I create sample departingXML
// is just for the sake of copying and pasting, it's not related to solution.
var departingXML:XML = new XML(<x><![CDATA[
<flights>
<flight>
<number>GS1234</number>
<date>10/11/2015</date>
<time>1440</time>
</flight>
<flight>
<number>TD1234</number>
<date>10/11/2015</date>
<time>1450</time>
</flight>
</flights>
]]></x>.toString());
// the way it was done before
var flightNoArray: Array = [];
for each(var num: XML in departingXML.flight) {
flightNoArray.push(num.number);
// WARNING! num.number is an XMLList! It s NOT a string.
// Addressing nodes by name ALWAYS gets you an XMLList,
// even if there's only one node with that name
// Hence, `flightNoArray.indexOf("string")` could not possibly work,
// as there are lists inside of the array, not strings.
// We can check if this is a fact:
trace(flash.utils.getQualifiedClassName(flightNoArray[flightNoArray.length-1]));
// (traces XMLList)
// Or this way (note toXMLString() method)
trace(flightNoArray[flightNoArray.length-1].toXMLString());
// (traces <number>GS1234</number>)
}
trace(flightNoArray);
trace(flightNoArray); traces GS1234,TD1234 because this is the way toString() method works for xml nodes -- it gets you text, that is inside. This is why there is a special method toXMLString(), that gets you a string representation of a node.
I use the Wordpress function $wpdb->get_results()
https://codex.wordpress.org/Class_Reference/wpdb#SELECT_Generic_Results
It says:
"If no matching rows are found, or if there is a database error, the return value will be an empty array."
Then how can I know if the query failed OR if it's empty?
Use
$results=$wpdb->get_results($yoursql);
if (count($results)> 0){
//do here
}
But if you want to know if query failed
$wpdb -> show_errors ();
$wpdb -> get_results ($wpdb -> prepare($sql));
$wpdb -> print_error ();
Bit late to the party here but I'm just looking for the same thing. I've had a browse through the wp-db.php code on version 4.4.2.
On line 1422, inside the method flush() there's a bit of code which resets the last_error property:
$this->last_error = '';
This flush() method is called in the query() method on line 1693:
$this->flush();
The get_results() method calls query() on line 2322:
if ( $query ) {
$this->query( $query );
} else {
return null;
}
With this we can be pretty sure that more or less every time get_results() (Or get_row() too for that matter) is called, query() and flush() are both called, which ensures that last_error is set to the empty string before the query is executed.
So assuming the query runs (If it doesn't, null is returned - if the query is empty for example), last_error should contain an error message if the query was to fail for some reason.
Since last_error is flush()ed/reset each time, it should only contain an error for the last query that was run, rather than the last error for any query that had been run previously. With this in mind it should be safe to rely on last_error to determine whether something went wrong with the query.
$results = $wpdb->get_results($sql);
if (is_null($results) || !empty($wpdb->last_error)) {
// Query was empty or a database error occurred
} else {
// Query succeeded. $results could be an empty array here
}
Not the most intuitive in my opinion, but it seems to be sufficient.
Personally, I've written my own class around wpdb for my own benefit. This is my getResults() method.
public function getResults($query, $bindings = [])
{
// Prepare the statement (My prepare method inspects $query and just returns it if there's no bindings, otherwise it uses $wpdb->prepare()
$prepared = $this->prepare($query, $bindings);
// Execute the statement
$rows = $this->db->get_results($prepared, ARRAY_A);
// If an array was returned and no errors occurred, return the result set
if (is_array($rows) && empty($this->db->last_error)) {
return $rows;
}
// On failure, return false
return false;
}
Hope this helps.
Wpdb->get_results function from wordpress returns the result if successful otherwise it will return null. There can be many reasons if a query get failed.Refer in-depth article on debugging get_results() returning empty results here
Although you can use functions like wpdb->show_error() to check what was the last error after executing the sql query. sometimes this error returns empty
then try to use wpdb->last_query to check the final query that get formed.
I'm trying to call a SalesForce web service via SSIS, and I am trying to retrieve the value of the sessionID node.
Here is the XML:
<?xml version="1.0" encoding="utf-16"?>
<LoginResult xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<metadataServerUrl xmlns="urn:partner.soap.sforce.com">https://xxxxxx/services/Soap/m/31.0/xxxxxx</metadataServerUrl>
<passwordExpired xmlns="urn:partner.soap.sforce.com">false</passwordExpired>
<sandbox xmlns="urn:partner.soap.sforce.com">true</sandbox>
<serverUrl xmlns="urn:partner.soap.sforce.com">https://xxx/services/Soap/u/31.0/xxx</serverUrl>
<sessionId xmlns="urn:partner.soap.sforce.com">xxxxxxxxxx</sessionId>
<userId xmlns="urn:partner.soap.sforce.com">xxx</userId>
<userInfo xmlns="urn:partner.soap.sforce.com">
<accessibilityMode>false</accessibilityMode>
<currencySymbol>$</currencySymbol>
<orgAttachmentFileSizeLimit>5242880</orgAttachmentFileSizeLimit>
<orgDefaultCurrencyIsoCode>USD</orgDefaultCurrencyIsoCode>
<orgDisallowHtmlAttachments>false</orgDisallowHtmlAttachments>
<orgHasPersonAccounts>false</orgHasPersonAccounts>
<organizationId>xxxxxxxx</organizationId>
<organizationMultiCurrency>false</organizationMultiCurrency>
<organizationName>xxxxx</organizationName>
<profileId>xxxxx</profileId>
<roleId xsi:nil="true" />
<sessionSecondsValid>7200</sessionSecondsValid>
<userDefaultCurrencyIsoCode xsi:nil="true" />
<userEmail>xxxxxxx</userEmail>
<userFullName>xxxxx</userFullName>
<userId>xxxxxxx</userId>
<userLanguage>en_US</userLanguage>
<userLocale>en_US</userLocale>
<userName>xxxxxxx</userName>
<userTimeZone>America/New_York</userTimeZone>
<userType>Standard</userType>
<userUiSkin>Theme3</userUiSkin>
</userInfo>
</LoginResult>
I successfully tested this expression via http://www.xpathtester.com/xpath.
Edit:
Trying a script task now, and still I'm not finding the exact right combination to select this information. There are multiple namespaces in the XML, and the below code returns 0 nodes. Quite frustrating!
public void Main()
{
string loginResult;
string sessionID;
loginResult = Dts.Variables["User::loginResult"].Value.ToString();
XmlDocument doc = new XmlDocument();
doc.LoadXml(loginResult);
var xmlnsManager = new System.Xml.XmlNamespaceManager(doc.NameTable);
//xmlnsManager.AddNamespace("t1", "http://www.w3.org/2001/XMLSchema-instance");
xmlnsManager.AddNamespace("ns", "urn:partner.soap.sforce.com");
XmlNodeList list = doc.SelectNodes("/LoginResult/ns:sessionID", xmlnsManager);
for (int i = 0; i < list.Count; i++)
{
sessionID = list[i].Value;
}
Dts.TaskResult = (int)ScriptResults.Success;
}
}
A correct expression to find the sessionId element is:
//*[local-name() = 'sessionId']/text()
In your input XML, the element you'd like to find:
<sessionId xmlns="urn:partner.soap.sforce.com">xxxxxxxxxx</sessionId>
does not have a prefix - it is in a default namespace that does not require elements to be prefixed.
Therefore, a possible explanation is that an expression like
//*:sessionId
only finds elements that are prefixed in the input XML. That should not be a problem but as far as I can see, SSIS is known for problems with namespaced XML (see e.g. here or here).
As far as the XPath specification is concerned, an expression like //*:root should be able to find an element like
<root xmlns="www.example.com"/>
EDIT: Apparently, you have changed the question alltogether - now there is another problem: The outermost element LoginResult is in no namespace at all, not in the xsi: namespace:
<LoginResult xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
The schema instance namespace just happens to be declared on this element, but it is not used there. So, change your code to:
var xmlnsManager = new System.Xml.XmlNamespaceManager(doc.NameTable);
xmlnsManager.AddNamespace("ns", "urn:partner.soap.sforce.com");
XmlNodeList list = doc.SelectNodes("/LoginResult/ns:sessionId", xmlnsManager);
Does anyone know how to extract parameter from camel URI?
I have a route defined like this
from("SOME_URI")
.to("SOME_URI")
.to("bean:myBean?method=myMethod&myParameter1=val1&myParameter2=val2")
I want to extract parameter1 and parameter2 in "myMethod" like this (I'm implementing camel in Grails)
def myMethod(def inBody, Exchange exchange){
String parameter1 = extractParameter('myParameter1')
String parameter2 = extractParameter('myParameter2')
...//rest of code
return something
}
Thank's in advance!
Main Answer
You can get what you're looking for out of the exchange:
exchange.getFromEndpoint()
Will return the Endpoint defined by "SOME_URI" and:
exchange.getFromEndpoint().getEndpointUri()
will return the String value of "SOME_URI"
Meaning your code could become:
def myMethod(def inBody, Exchange exchange){
def uri = exchange?.fromEndpoint?.endpointUri
if(uri) {
String parameter1 = extractParameter(uri, 'myParameter1')
String parameter2 = extractParameter(uri, 'myParameter2')
//...rest of code
}
return something
}
/*
* do any kind of processing you want here to manipulate the string
* and return the parameter. This code should work just fine in grails
*/
def extractParameter(String uri, String parameterName) {
def m = uri =~ "${parameterName}=([^&]+)"
return m.find() ? m[0][1] : null
}
If a Java equivalent is preferred, this should do the same:
private static String extractParameter(String uri, String parameterName) {
Matcher m = Pattern.compile(parameterName + "=([^&]+)").matcher(uri);
return m.find() ? m.group(1) : null
}
Alternative
Also note that, depending on what exactly you're trying to accomplish, a better approach might be to use the fromF DSL to supply parameters directly to your route. That way, you have the parameters available in code and you don't have to worry about extracting them, afterward.
The code snippet below is taken from the Camel Documentation of FromF.
fromF("file://%s?include=%s", path, pattern).toF("mock:%s", result);
Are val1 and val2 hardcoded values, or should they be some kind of dynamic value, maybe from the message itself?
The Camel bean component allows you to define the binding, and pass in values from the message or fixed values. See more details at: http://camel.apache.org/bean-binding.html
And you would also need to look at the number of parameters in your method signature, and the number of parameters you define in the Camel bean binding uri. They should match up.
If I understand correctly, you are trying to pass parameters into a method that is going to be invoked. The usual way to do this is to modify the Exchange object as it's flowing through the route.
from("SOME_URI")
.to("SOME_URI")
.setHeader("myParameter1", constant("val1"))
.setHeader("myParameter2", constant("val2"))
.to("bean:myBean?method=myMethod")
In your method, you just access the headers of the Exchange.
def myMethod(Exchange exchange) {
String parameter1 = exchange.getHeader("myParameter1", String.class)
String parameter2 = exchange.getHeader("myParameter2", String.class)
//...rest of code
}
Or if you want to get fancy and use Camel's bean binding,
def myMethod(Exchange exchange,
#Header("myParameter1") String parameter1,
#Header("myParameter2") String parameter2) {
//...rest of code
}
Please remember to vote up if this helps.
I have this XML tree that looks like this (I've changed the tag names but if you're really clever you may figure out what I'm actually doing.)
<ListOfThings>
<Thing foo:action="add">
<Bar>doStuff --slowly</Bar>
<Index>1</Index>
</Thing>
<Thing foo:action="add">
<Bar>ping yourMother.net</Bar>
<Index>2</Index>
</Thing>
</ListOfThings>
With libxml2, I want to programmatically insert a new Thing tag into the ListOfThings with the Index being the highest current index, plus one. I do it like this (sanity checking removed for brevity):
xpath = "//urn:myformat[#foo='bar']/"
"urn:mysection[#name='baz']/"
"urn:ListOfThings/urn:Thing/urn:Index";
xpathObj = xmlXPathEvalExpression(xpath, xpathCtx);
nodes = xpathObj->nodesetval;
/* Find last value and snarf the value of the tag */
highest = atoi(nodes->nodeTab[nodes->nodeNr - 1]->children->content);
snprintf(order, sizeof(order), "%d", highest + 1); /* highest index plus one */
/* now move up two levels.. */
cmdRoot = nodes->nodeTab[nodes->nodeNr - 1];
ASSERT(cmdRoot->parent && cmdRoot->parent->parent);
cmdRoot = cmdRoot->parent->parent;
/* build the child tag */
newTag = xmlNewNode(NULL, "Thing");
xmlSetProp(newTag, "foo:action", "add");
/* set new node values */
xmlNewTextChild(newTag, NULL, "Bar", command);
xmlNewChild(newTag, NULL, "Index", order);
/* append this to cmdRoot */
xmlAddChild(cmdRoot, newTag);
But if I call this function twice (to add two Things), the XPath expression doesn't catch the new entry I made. Is there a function I need to call to kick XPath in the shins and get it to make sure it really looks over the whole xmlDocPtr again? It clearly does get added to the document, because when I save it, I get the new tags I added.
To be clear, the output looks like this:
<ListOfThings>
<Thing foo:action="add">
<Bar>doStuff --slowly</Bar>
<Index>1</Index>
</Thing>
<Thing foo:action="add">
<Bar>ping yourMother.net</Bar>
<Index>2</Index>
</Thing>
<Thing foo:action="add">
<Bar>newCommand1</Bar>
<Index>3</Index>
</Thing>
<Thing foo:action="add">
<Bar>newCommand2</Bar>
<Index>3</Index> <!-- this is WRONG! -->
</Thing>
</ListOfThings>
I used a debugger to check what happened after xmlXPathEvalExpression got called and I saw that nodes->nodeNr was the same each time.
Help me, lazyweb, you're my only hope!
Based on your XPath it looks like that is just a snippet of a namespaced document (using default namespace at that). I bet you made a prior call to register "urn" as the prefix for a namespace using xmlXPathRegisterNs. When you add the new nodes, you aren't creating them in namespaces so the XPath is correctly only picking the namespaced "Index" elements.