Looking at the new Integration Activity approach – Part 2

geoffc

By: geoffc

December 10, 2013 3:42 pm

Reads: 310

Comments:0

Rating:5.0

This entry is part 2 of 2 in the series Looking at the new Integration Activity approach

In the first article in this series I explained how the way that the WSDL for a SOAP Web Service is parsed, changed in an online update for Designer 4.01

I started by focusing on the Pre Activity mapping and how the parser change effects how the XML document being manipulated looks like.

When you click on an Integration Activity (IA) in the Workflow tab, if you can be patient and count to two, a fifth tab along the bottom will appear, named Integration. If you are impatient you can right click on the Integration Activity item and select Edit Integration Activity. Both ways work the same, as you get to the Integration Activity editor tab.

When I first saw Composer, which this is based on, we ran it on a Linux server via VNC and at low resolution. It kind of looked like this screen shot.

IntegrationActActionModel-3

You can see how busy and confusing this is, with so many windows and palettes and what not. But if you have the screen real estate and you can space it out better it is much easier to understand.

Along the top we have some XML view windows, for the Input, Output documents. That is the normal SOAP XML that the WSDL says is what you should send in, and predicts what you should get returned.

I am not entirely sure what the Temp XML is, since it is usually strangely empty. The _SystemFault and AdminException XML views are for catching errors. There are communication and permission style errors, or else properly returned errors from the application. I am not completely clear on the distinction between these last two. What is the difference between a _SystemFault vs an AdminException? Maybe the former is connection errors whereas the latter would be returned errors from the service?

In the bottom half of the right side we have the Action Model. This is where the magic happens. The WSDL Editor and Messages tab basically give you better access to the raw XML of the WSDL, and the Input, Output, and AdminException samples messages.

Just to compare, lets look at the Action Model and how they differ between the old approach using the same basic WSDL and same selected function:

IntegrationActActionModel-1

and the new approach:

IntegrationActActionModel-2

Lots of differences if you look in detail in the setup, lots of comments in both (Thank you Designer folk! Thank you for explaining some of what you do out of the box without otherwise telling us why), but the key area is under the Try section, then under the Execute. The old approach used a WS Interchange. I guess Web Services is the WS in this case. The new approach uses an XML Interchange.

What is the difference? Well the XML Interchange seems to be a more generic approach (it was available before) that has less SOAP specific functionality built in than a WS Interchange which was meant to do SOAP specifically. In the context of Web Services, in principal, you ought to be able to use the XML Interchange more simply to do a REST call using XML (instead of JSON) or an XML-RPC call than you could the WS Interchange.

One of the key differences between the two is how they do authentication. If you edit the properties of the WS Interchange there is a Connection tab where you specify the URL, the username and password. Now as Designer has progressed, they have added some clever features where you use a variable for these three things and the IA opens with an Input document that has XML attributes on the parent node for these three variables. Thus you can easily in the Pre-Activity mapping of this action copy from a GCV or flowdata into the XML attribute and in fact Designer pretty much sets it all up for you now in both approaches.

Look at a screen shot I used in an earlier article and you can see the Pre_Activity mapping and how _serverUrl_, _password_, and _userId_ are made available.

IntegrationAct-5

What is interesting is that in the old approach, you used to just specify in the Connection tab the username and password.

IntegrationActivityKonstants

I made that image years ago for another article, but it was perfect and showed how you could use variables there or constants if needed.

But in the new approach, there is no connection tab per se, you need to handle it yourself. So how does Basic authentication work? Well you add an HTTP Header named Authorization, and the contents are “Basic HSHSHSHS==” where the gooble-de-gook is the username:password base 64 encoded.

IA-XML-Interchange1

Thus if you click on the HTTP Header Params button, you see a list of headers like these:

IA-XML-Interchange2

If you edit the Authorization header, you will see the following ECMA which says:

"Basic " + java.lang.String(Packages.org.apache.commons.codec.binary.Base64().encodeBase64(java.lang.String(_userid_+":"+_password_).getBytes("UTF-8")),"UTF-8")

Or more simply, base 64 encode the username in the variable _userid_ then a colon, then base64 the password held in the variable _password_ and precede it with the word Basic.

In other words build the header yourself. What is nice is that Designer does all this fiddly stuff for you.

The action that precedes the XML Interchange is sort of interesting as well. It is an ECMA function that says:

var _userid_ = Input.getDocument().getDocumentElement().getAttribute("_userid_");
Input.getDocument().getDocumentElement().removeAttribute("_userid_");
var _password_ = Input.getDocument().getDocumentElement().getAttribute("_password_");
Input.getDocument().getDocumentElement().removeAttribute("_password_");
var _serverUrl_ = Input.getDocument().getDocumentElement().getAttribute("_serverUrl_");
Input.getDocument().getDocumentElement().removeAttribute("_serverUrl_");

Which does the same two things, three times. Set up a variable _userid_ and get the XML attribute _userid_ from the Input document (that was set by the Pre-Activity mapping), and then as the second step, remove it from the input document. I.e. Clean up. Then do the same for the password and serverUrl variables. It is worth noting that when I try to trace the User App for these sorts of things the SOAP document it shows in trace is NOT removing the XML Attributes properly, but since it seems to work I did not bother following up on it. I could imagine a SOAP endpoint that did not like extra XML attributes, or you might not like sending the password in clear text along with your session (Along with your username and the URL). However as discussed above, the password itself is only base 64 encoded in an HTTP header which if they could sniff the traffic (not using SSL would thus be bad) then decoding the header is trivial and likely built into most sniffers anyway.

All this is basically to answer a complaint I had ages ago, that it was hard with a WS Interchange to make a stagable PRD using an Integration Activity, since the URL, user id, and password were probably all different in each stage of your environment and the values were buried deep inside this section. From the open PRD, click on Workflow, click on your Integration activity, pause for the Integration tab to appear, click on it. Right click the WS Interchange, select Edit Action, click on the Connection tab, and then change the three values. I get 6 clicks and three values to modify. Although ECMA was available to define all three, you could not at the time I complained use a GCV to populate the values which now seems like a simpler approach. I know at one point I tried using the new GCV functions in this area of the Integration Activity and it did not work. I have not since tested, but I suspect that it still might not work, and thus this ECMA approach to work around it. In the grand scheme of things, since they pre-populate all the ECMA and extensions to the SOAP document to do the work, it is no big deal, but I suspect simply fixing the core issue would have been much simpler and more useful long term. Conversely leaving behind these ECMA examples is actually instructive in how the ECMA in this area of the PRD works, since as usual, it is slightly different than Forms, Workflow, or the engine and examples are great things to use as building blocks.

However, this all requires zero effort as it all is just done for you now, which is great. But it helps to understand why they went through these hoops for when something does not quite work for you, thus giving you insight into the next step of troubleshooting.

There is another major change in terms of how namespaces are handled between the two approaches. Like the XML vs WS Interchange action change there are some built in Namespace handling stuff that was designed to make it easier, and my guess is that is not quite 100% matching to what current WSDL’s are looking for in terms of namsepsce support. Even in the old version it uses the Add Namespace action item set to Ignore namespaces on the Input and Output documents, and instead adds them in via ECMA expressions.

You can see what it was meant to be used for, in this example photo:

IA-AddNamespace1

Instead they use three ECMA Function actions items, that say something like:

Input.createXPath("addCommentInput/addCommentRequest").setAttribute("xmlns", "http://www.novell.com/provisioning/service")

I am not clear on why the change, but I would guess it is easier to programmatically populate a new Function action with the needed values than to use the Add Namespace action. I would be interested in better understanding the distinctions between these two approaches. My offhand guess would be that one approach would require updating the Namespace action code, vs just doing it in ECMA. Alas, since I have noted, this is almost all that remains of Composer from Silverstream, and Composer is deprecated, there is little desire to continue working in that code base. Sad, really, I thought Composer was great and really miss it.

Anyway, we then get this pair of functions (addNamespace(element, prefix, namespace, includeChildren) and getElement(doc, elementXpath)) that will be used later.

/* This function adds a namespace prefix to the desired node */  
function addNamespace(element, prefix, namespace, includeChildren) { 	/* if no prefix, see if we have anything to do */ 
	if ((prefix == "") && (element.getPrefix() == null)) { 		  
		/* check if parent is in same namespace */ 		  
		var elementParent = element.getParentNode();
		var isElement =  elementParent instanceof Packages.org.w3c.dom.Element;
		if (isElement) {
			/* if we are in the same namespace as our parent, we don't need to add the namespace attribute */ 			 
			if ((elementParent.getPrefix() == null) && (elementParent.getAttribute("xmlns") == namespace)) {
				return;
		}
	}
	element.setAttribute("xmlns", namespace);		     } 	 	
	else if (prefix != element.getPrefix()) {
		var children = element.getChildNodes();
		var childrenArray = new java.util.ArrayList();
		for (var i = 0; i < children.getLength(); i++) {
			childrenArray.add(children.item(i));
		}	         
		var parent = element.getParentNode();
		var namespacedElement = element.getOwnerDocument().createElementNS(namespace, prefix + ":" + element.getNodeName());
		/* copy the attributes */ 	   
		var attrs = element.getAttributes();
		if (attrs != null) {
			var attrArray = new java.util.ArrayList();
				for (var i = 0; i < attrs.getLength(); i++) {
					attrArray.add(attrs.item(i));
				}  		  
				for (var i = 0; i < attrArray.size(); i++) {
					namespacedElement.setAttribute(attrArray.get(i).getNodeName(), attrArray.get(i).getNodeValue());
				}
			}         
			var namespaceDeclElement = element.getOwnerDocument().getDocumentElement();
			var namespaceAttrName = "xmlns:" + prefix;
			if (!namespaceDeclElement.hasAttribute(namespaceAttrName)) {
				namespaceDeclElement.setAttribute(namespaceAttrName, namespace);
			}        
			parent.removeChild(element);
			parent.appendChild(namespacedElement);
			for (i = 0; i < childrenArray.size(); i++) {
				namespacedElement.appendChild(childrenArray.get(i));
				}
				/* get this setup so we can process recursively */
				element = namespacedElement;
			} 	
			/* process children if instructed to do so */
			if (includeChildren) {
			var children = element.getChildNodes();
			var childrenElementArray = new java.util.ArrayList();
			for (var i = 0; i < children.getLength(); i++) {
				var isElement =  children.item(i) instanceof Packages.org.w3c.dom.Element;
				if (isElement) {
					childrenElementArray.add(children.item(i));
				}
			} 		   
			for (var j = 0; j  0) {
			var xpathLookup = "";
			parentElement = Input.getDocument();
			xpath = Packages.javax.xml.xpath.XPathFactory.newInstance().newXPath();
			for (var i = 0; i  -1) {
						elementToCreate = splitString[i].substring(0, elementToCreate.indexOf('['));
					}
					parentElement.appendChild(parentElement.getOwnerDocument().createElement(elementToCreate));
				}             
				parentElement = xpath.evaluate(xpathLookup, Input.getDocument(),  Packages.javax.xml.xpath.XPathConstants.NODE);
			}       
		}       
		testElement = doc.XPath(elementXpath);
	}
	return testElement.item(0); 
}; 

Then we get some comments that are quite interesting. It seems that if you modify the mappings (in the workflow view, select your Integration Activity, right click, Show Data Item Mappings, then change the Pre or Post Activity Mappings, or maybe just the mappings itself, it is not clear) then this will regenerate the needed namespace mappings at runtime. Which is kind of clever.

The ECMA below does the work, defining, ‘ser’, ‘soapenv’ and then using the just defined addNamespace() function to add them as needed to the SOAP document.

/* This function insures the soap document being sent has the correct namepaces as defined by the WSDL. */   
/* ***Autogenerated namespace creation*** */ 
Input.createXPath("Envelope").setAttribute("xmlns:ser", "http://www.novell.com/provisioning/service"); Input.createXPath("Envelope").setAttribute("xmlns:soapenv", "http://schemas.xmlsoap.org/soap/envelope/"); addNamespace(getElement(Input, "Envelope"), "soapenv", "http://schemas.xmlsoap.org/soap/envelope/", false); addNamespace(getElement(Input, "Envelope/Header"), "soapenv", "http://schemas.xmlsoap.org/soap/envelope/", false); 
addNamespace(getElement(Input, "Envelope/Body"), "soapenv", "http://schemas.xmlsoap.org/soap/envelope/", false); 
addNamespace(getElement(Input, "Envelope/Body/addCommentRequest"), "ser", "http://www.novell.com/provisioning/service", true);

It is interesting that this is autogenerated at runtime, and fairly unclear how this is working, since of course you deploy the PRD to the Identity Vault, and at runtime it should probably not self modify.

This should generically handle SOAP namespace needs, though it is unclear how it will figure out if your specific WSDL needs a namespace declaration. I guess the WSDL will specify it, and then the Integration Activity can read it from there.

Finally we get the Try block, which has two items inside. The first is the Execute action, which starts with some ECMA.

I looked at this ECMA up above, explaining how passing in username, password, and SOAP endpoint URLs had been tricky in the past and this approach was taken to resolve it.

There is some additional ECMA for handling errors, for which the comments say that the XML interchange throws exception on a HTTP 500 error when it should not. Again we see a case of coding around the behavior in ECMA instead of fixing the code which is interesting. I probably will not go into this ECMA in too much detail since I am not really sure what it is doing, however it does reveal some of the internal mechanics it would take to muck about with the error events document.

Overall the change between the two approaches to the Integration Activities WSDL parsing is quite interesting and worthy of looking at. Sometimes your WSDL works better one way than the other, so keep in mind that you do have options out there for you.

VN:F [1.9.22_1171]
Rating: 5.0/5 (2 votes cast)
Looking at the new Integration Activity approach - Part 2, 5.0 out of 5 based on 2 ratings
Series Navigation<< Looking at the new Integration Activity approach – Part 1

Tags: , ,
Categories: Identity Manager, IDM Designer, Technical Solutions

Disclaimer: As with everything else at NetIQ Cool Solutions, this content is definitely not supported by NetIQ, so Customer Support will not be able to help you if it has any adverse effect on your environment.  It just worked for at least one person, and perhaps it will be useful for you too.  Be sure to test in a non-production environment.

Comment