Consuming a SOAP web service in Jython.
Tags: python, jython, soap
Published: 2019-06-26 11:43:02

A few months ago I was placed on a project that required an automated process to communicate with an API . The automation was done in Jython, an implementation of Python on the JVM which allows for dynamic compilation of java objects. Basically if you want to use a scripting language packed with a library of tools, you should consider Jython.

I went through a few approaches before I landed on this one, my first approach was to consume it using Python libraries but the firewall prevented any pip installs. You could try manually install all the dependencies, but why not watch paint dry instead. There is a way around the firewall but it only works if you have the proxy, which I didn’t so I had to find another way.

The second approach I tried is to consume it directly in java and package it into a jar file and call it in the jython application. This will work but I wouldn’t recommend it because you are managing two separate applications. If something breaks, how do you know where your problem is? Is it Jython or Java? You will have to get the original source code, change it, repackage it and retest. This approach is not easily maintainable, especially if the you leave. All the new developer has is the jar file and debugging becomes painful.

There’s an easier way to solve this. Jython already allows for Java libraries. Using a Java library with Jython syntax is a much easier way to do this.I decided to go with the SAAJ API and this is how I did it.

I created a service that returns Beyonce album release dates given the album name(Yes I’m a huge fan).

The first step is to construct the SOAP message that you’re sending. I did it a little different from the documentation. I added the soap envelope to an xml file, I substituted the parameter (i.e. the album name) with a variable that will be mapped in the code.

```xml
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/">
<soapenv:Header/>
<soapenv:Body>
<tem:GetBeyonceAlbumYear>
<!--Optional:-->
<tem:albumName>${albumName}</tem:albumName></tem:GetBeyonceAlbumYear>
</soapenv:Body>
</soapenv:Envelope>
```

The mapping of the variables is relatively easy, using the StrSubstitutor method from the apache commons library. Have a look at the code below which maps the variable :

```python
def mapVariableToRequestFile(albumName):
  mapping = HashMap()
  mapping.put("albumName",albumName)
  xmlFile = File(pathToXmlRequest)
  fileStream = FileInputStream(xmlFile)
  template = IOUtils.toString(fileStream,"UTF-8")
  sub = StrSubstitutor(mapping)
  request = sub.replace(template)
  return request
```

The next step is to create the actual message that will be sent. The library creates the message from the bytes of the request, we can easily get the bytes from our request returned from mapVariableToRequestFile. Explicit casting is sometimes required, the type that Jython returns might not necessarily be the one you need. In order to use the getBytes() method of the String class, I had to cast request to a String because it was returning a unicode object.

```python
def createSOAPMessage(request):
  messageFactory = MessageFactory.newInstance()
  stream = ByteArrayInputStream(String(request).getBytes())
  soapMessage = messageFactory.createMessage(None,stream)
  soapMessage.saveChanges()
  return soapMessage
```
The final step is to send the actual message. I did not use any authentication in my example(no cert required) and therefore used the library’s connection method.

```python
def sendSoapRequest():
  request = mapVariableToRequestFile("Lemonade")
  soapMessage = createSOAPMessage(request)
  soapConnectionFactory = SOAPConnectionFactory.newInstance()
  soapConnection = soapConnectionFactory.createConnection()
  soapResponse = soapConnection.call(soapMessage,soapEndpointUrl)
```
You can easily see the response by writing to system.out and printing it out as follows.
```python
soapResponse.writeTo(System.out)
System.out.println()
```
This method , unlike doing it in java, doesn’t force you to do any error handling. You can add that in yourself like a good developer, the exception types will also have to be imported and handled accordingly.

Finding a solution for my problem was a whole nightmare for me, here’s to hoping it eliminates that for someone else.