Tag Archives: SOAP

Python SUDS Library Client

I was trying to create a ‘quick and dirty’ python SOAP client and was finding it difficult. The WSDL I was dealing with wasn’t a simple one, which, of course, made creating a client a bit more difficult. Add to the fact that I’m far from a python expert (have been tired of learning the ‘latest/greatest’ new toy), so finally started learning python reluctantly.

I found this post in StackOverflow that listed a number of python SOAP libraries and I started working through the examples. I finally settled on using SUDS. It’s not the most up-to-date library – the last development done on in was in 2010. But frankly, it’s the only one from which I could obtain results.

That being said, it wasn’t as straightforward as I would have liked. My first language is PERL; my second is Java. So maybe ‘thinking in python’ was not as easy for me. But still, I have found it a good language to work with, which is why I have been forcing myself to use it instead of PERL lately.

So, my code…..

There are groups of parameters in the WSDL I’m using, which required me to create dictionaries of dictionaries:

sort = dict(sortPolicyId='RELEVANCE',order='DESCENDING')
criteria = dict(searchPhrase='test', sortPolicy=sort)
params = dict(maximumResults=1000, searchTimeOut=24000)
contextParams = dict(ContextualCriteria=criteria,SearchCriteriaParameters=params)

The next part, passing them, does come directly from the documentation:

results = client.service.contextualSearch(**contextParams)

Then the next hard part: I couldn’t figure out how to get the values out. What I finally found was an example for pysimplesoap that I could use: Pull the results from the results. That is:

resultList = results.results

At this point, I had something from which I could pull values:

for item in resultList:
    print item.catalogEntry.id

… which gave me the values I needed.

XPath and C#

Our C# application needs to retrieve data from a Java SOAP source. And, anyone who has dealt with SOAP for any time knows that standards aren’t. In our case, the null dates in the data were being passed back with a date that the Java system, I supposed, considered ‘null’: 1899-01-01 00:00:00. But our C# system parsed the incoming XML and saw a ‘Date Format Exception’.

This all happened when we used Visual Studio’s automagic object creator (Add Service Reference/Advanced/Add Web Reference). When we switched to used the ‘standard’ Add Service Reference, this error disappeared. But we then saw a Number Format Exception error. We couldn’t figure out where this one was coming from, so we started seeking other options.

The Java SOAP services are being created using Axis 2, which permits the user to also make REST calls. So I changed my code to use an XPathDocument to make a REST call for the data. The data returned is still in XML format. But this allows me to walk through the elements myself and either ignore the elements if I don’t need them, or handle a single element’s value if it causes issues.

I found about 5 different ways of accomplishing this exercise before settling on the XPathDocument method I found in Using Returned XML with C#. The one ‘gotcha’ that I had to contend with was remembering to use the XMLNamespaceManager once I created it. Using it is optional when searching for nodes.

The one ‘gotcha’ I ran into was remembering to include *all* the namespaces used within the document, and to use the optional namespace argument when searching for nodes. Without it, I ran into expected – and unexpected – errors:

1) The obvious one – it complained about the fact I was using ‘:’ within the node name but not declaring namespaces.
2) I ran into one error – I can’t reproduce it now – but it complained about the ‘:’ and only told me that was an illegal character to use within a node name, without telling me why.
3) I put one namespace in, but not both that were used in the document and ran into problems with that as well.

Creating a CXF Client Application

I’m creating a client that accesses our in-house SOAP web services. Our code base uses Spring, so I need to incorporate that. The SOAP service has security, so I need to get that working. We use maven as our build tool, so I need to get that in here as well. Here’s the steps I’m following to get it to work. I’ll edit these as I go.

  1. Download and install Apache CXF.
  2. Install the security certificate on your system using Install Cert. If that code ever goes missing, we’re all in trouble!
    • compile the code
    • run the instructions as written
    • move the jsscacert file to your /jre/lib/security folder for the JRE you’re using for your system
  3. Create a generic maven jar project (#5) using maven arechetype:generate.
  4. Add the CXF maven entries to your pom.xml.
  5. Run WSDL2JAVAto generate your base code.
  6. Create a password callback class
    import java.io.IOException;
    
    import javax.security.auth.callback.Callback;
    import javax.security.auth.callback.CallbackHandler;
    import javax.security.auth.callback.UnsupportedCallbackException;
    
    import org.apache.ws.security.WSPasswordCallback;
    
    public class ClientPasswordCallback implements CallbackHandler {
    
    	private String system;
    	public ClientPasswordCallback() {
    		// TODO Auto-generated constructor stub
    	}
    
    	public void handle(Callback[] callbacks) throws IOException,
    			UnsupportedCallbackException {
    		WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
    
    		pc.setPassword("My password");
    
    	}
    	public void setSystem(String pVal)
    	{
    		system = pVal;
    	}
  7. Add the security and bean information to your applicationContext.xml file:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:jaxws="http://cxf.apache.org/jaxws"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
    <jaxws:client id="[port bean id]"
                      serviceClass="[my port class]"
                      address="[https://mywsdl]">
       <jaxws:outInterceptors>
         <bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor"/>
         <ref bean="wss4jOutConfiguration"/>
       </jaxws:outInterceptors>
    </jaxws:client>
    
    <bean id="wss4jOutConfiguration" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
      <constructor-arg>
        <map>
           <entry key="action" value="UsernameToken"/>
           <entry key="passwordType" value="PasswordText"/>
           <entry key="user" value="[my login]"/>
           <entry key="passwordCallbackRef">
             <ref bean="passwordCallback"/>
           </entry>
        </map>
      </constructor-arg>
    </bean>
    
    <bean id="passwordCallback" class="[my callback class]">
       <property name="system" value="test"/>
    </bean>
    </beans>
  8. Modify the test case to use your beans:
    ApplicationContext context = new ClassPathXmlApplicationContext(
    		"/applicationContext.xml");
        	port = ([port class]) context.getBean("[beanid]");
    
            ObjectFactory factory = new ObjectFactory();
    
            [request class] myRequest = factory.create[request class]
  9. Delete the [beanname]Service.java class. You don’t need it when you use the Spring stuff.