Deploying a Python Service on Axis2
By Heshan Suriyaarachchi
- 22 Jul, 2008
|WSF/Jython||v1.0 alpha or higher|
Table of Contents
- Writing a Service in Python
The solution to the requirement of exposing a Python Web service in Axis2, lies within the pluggable deployer concept of Axis2 . 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.
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.
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.
- Annotations of the python service are read.
- AxisService is created for the python service.
- AxisOperaion is created for every python method.
- AxisMessage is added to the operation. It contains the types of the method parameters.
- Each and every AxisOperation is added to the AxisService.
- 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:
- The annotation will start as #@annotate.
- Each attribute should be within double quotations
- return attribute should equate to the return type of the method
- operationName attribute should equate to the operation name of the method
- Each input parameters should have the variable name and it's value.
- Each parameter should have a unique referance name
#@annotate("returns=int", "operationName=add", var1="integer", var2="integer")
When using this deployer for deploying a Python script, you must follow the guidelines given below:
- Annotating a script should only be done according to the annotation mechanism specified above.
- Parameter names (reference variables) should have unique names
- If there is no return type, you must specify it as hashNone
- 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
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.
 - http://www.jython.org/
Heshan Theekshana Suriyaarachchi, Undergraduate Intern, WSO2 Inc. heshan at wso2 dot com