WSO2Con 2013 CFP Banner

Deploying a Python Service on Axis2

By Heshan Suriyaarachchi
Date: Tue, 22nd Jul, 2008
Level: Intermediate
Reads: 8466 Discuss this article on Stack Overflow

Apache Axis2/Java, is a popular open source Web service engine. It currently supports exposing services written in Java, Javascript and Ruby as Web services. This article by Heshan Suriyaarachchi discusses the Python data Binding that enable exposing Web services written in Python.

First, we will look at the architecture of this solution and the related technologies. Then we will be going into details on writing a service in Python. Those of you who wish to skip the implementation details can go directly to  Writing a Service in Python section.

heshan's picture
Heshan Suriyaarachchi
Software Engineer
WSO2 Inc.

Applies To

WSF/Jython v1.0 alpha or higher
JDK v1.5
Jython v2.2

Table of Contents

Solution

The solution to the requirement of exposing a Python Web service in Axis2, lies within the pluggable deployer concept of Axis2 [1]. In order to expose services written in Python, we will be writing a custom deployer together with a Python message receiver. As you may already know, Python data types are dynamic as opposed to XML, in which data types are static. Therefore, within the deployer we need to map Python data types to XML schema data types. This process is known as data binding. Thereafter, with the help of data binding and method annotations, XML Schema is generated for the Python service. Next, the XML schema generated, together with meta-data pertaining to a AxisService, and given to the Axis2 engine. Axis2 engine will create the WSDL out of it and your Python service will be exposed to the world as a Web service. If you are interested in learning more on deployers, I suggest you take a look at the article on Axis2 deployment – Custom deployers.

Message Receiver

The message receiver consumes SOAP messages and hands them over to applications. The message receiver is the last handler in the in-pipe. For more information on Message Receivers and Axis2 Architecture, please refer this documentation.

Architecture   

 

The figure above shows the architecture of this solution. The incoming SOAP message is received by the Transport Listener and it is passed through the handler chain. Then it is given to the Jython Message Receiver, which traverse through the AXIOM structure and retrieve the relevant information needed. This retrieved information is passed in to the python service. Then the python service gets executed and   the resulting object is passed back in to the Jython Message Receiver. In the Jython Message Receiver an AXIOM structure is created out of the returned python object. Then the response is sent through the handler chain to the Transport Sender. The Transport Sender sends the response to the client.

How Jython Fits Into the Picture

JPython was the first implementation of Python programming language in java. Then the project was moved to SourceForge and renamed Jython. Jython is a programming hybrid. It exhibits the strengths of both its parents, namely Java and Python. Since Jython is written in 100% in java , scripts written using jython will run on top of any compliant Java Virtual Machine (JVM). Jython interpreter will support many shortcuts , which will make it easy to use existing java libraries as if they were your own python modules.


We are using Jython  as the medium to communicate between Python and Java. As you can already see in the architecture diagram, Jython resides in between the Jython Message Receiver and the Python script.

How a Python Service is Deployed

At the deployment time the annotations of the python script are read. Then the mapping of  the  dynamic Python types to static Java types are done. This process is called data binding. After corresponding matching types are mapped, an XML schema is created out of the service. Following steps describe how the XML schema is generated out of the Python service.

  1. Annotations of the python service are read.
  2. AxisService is created for the python service.
  3. AxisOperaion is created for every python method.
  4. AxisMessage is added to the operation. It contains the types of the method parameters.
  5. Each and every AxisOperation is added to the AxisService.
  6. Finally XML Schema for the python message is generated.

The generated AxisService is given to axis2 engine. Then axis2 engine generates the WSDL out of it. 

Writing a Python Service

Lets try to write a simple Python script that has a operation named add. It takes two input variables and returns the addition of those two numbers. 

def add(var1,var2):
	var3 = var1 + var2
	return var3

Annotating the Python Service

Annotating a method

Since we have to perform XML schema generation, we must annotate our Python script. An annotated Python method is given in the example below:

#@annotate("returns=int", "operationName=add", var1="integer", var2="integer")
def add(var1,var2):
  	var3 = var1 + var2
	return var3

When annotating a Python script, you must annotate it as it is shown in the example above. In doing so, there is a simple set of instructions to follow:

  1. The annotation will start as #@annotate.
  2. Each attribute should be within double quotations
  3. return attribute should equate to the return type of the method
  4. operationName attribute should equate to the operation name of the method
  5. Each input parameters should have the variable name and it's value.
  6. Each parameter should have a unique referance name
#@annotate("returns=int", "operationName=add", var1="integer", var2="integer")

Usage Restrictions

When using this deployer for deploying a Python script, you must follow the guidelines given below:

  1. Annotating a script should only be done according to the annotation mechanism specified above.
  2. Parameter names (reference variables) should have unique names
  3. If there is no return type, you must specify it as hashNone
  4. Add the following line to your axis.xml
<deployer extension=".py" directory="scripts" class="org.wso2.wsf.jython.deployer.PythonDeployer"> 
</deployer>

Annotating Methods Within Classes

Let's consider another situation where a method is defined within a class. The following example illustrates such a situation.

class MyClass:
    #@annotate("returns=integer", "operationName=MyClass.multiply", var1="integer", var2="integer")
    def multiply(var1,var2):
        return var1*var2

Then the operationName attribute of the annotation should equate to class_name.method_name

#@annotate("returns=integer", "operationName=MyClass.multiply", var1="integer", var2="integer")

Annotating a Method With Complex Types

#@annotate("returns=int", "operationName=addTwo", var1="integer", var2="integer", var3=(a="string", b="integer"))
def addTwo(var1,var2,var3):
    return var1+var2

Assume that you are having a variable or an object which contains multiple variables. You need to pass it into your Python method. Then it should be annotated  as shown below:

#@annotate("returns=int", "operationName=addTwo", var1="integer", var2="integer", var3=(a="string", b="integer")) 

A Simple Service

Based on the above tutorial on annotations, we can write a simple Python service given below, and expose it as a Web service with the help of the Python deployer.

#@annotate("returns=double", "operationName=f", a="double")
def f(a):
    return a

#@annotate("returns=int", "operationName=add", var1="integer", var2="integer")
def add(var1,var2):
    return var1+var2

#@annotate("returns=double", "operationName=deduct", var1="double", var2="double")
def deduct(var1,var2):
    return var1-var2

#@annotate("returns=int", "operationName=addTwo", var1="integer", var2="integer", var3=(a="string", b="integer"))
def addTwo(var1,var2,var3):
    return var1+var2

#@annotate("returns=int", "operationName=doComplexStuff", var1="integer", var2="(a="integer", b="integer")", var3="(a="string", b="integer")")
def doComplexStuff(var1,var2,var3):
    return var1

class MyClass:
    #@annotate("returns=integer", "operationName=MyClass.multiply", var1="integer", var2="integer")
    def multiply(var1,var2):
        return var1*var2

Conclusion

With the addition of the Python Deployer and the Python Message Receiver explained above, Axis2/Java is now able to expose a service written Python as a Web service. The underlying deployer will read the annotations and perform data-binding, schema generation. Schema generated will be passed on to the Axis2 engine, which will then perform the WSDL generation. Thereafter, a client is able to invoke the service using the WSDL generated.

This illustrates how a user with only knowledge in Python is be able to use the powerful Axis2/Java implementation, to expose Python Web services.

Resources

[1] - http://wso2.org/library/3708

[2] - http://ws.apache.org/axis2/1_4/Axis2ArchitectureGuide.html

[3] - http://www.jython.org/

Author

Heshan Theekshana Suriyaarachchi, Undergraduate Intern, WSO2 Inc. heshan at wso2 dot com

WSO2Con 2014