WSO2Con 2013 CFP Banner

Invoking Enterprise Web Services using Jython

By Heshan Suriyaarachchi
Date: Thu, 21st Aug, 2008
Level: Intermediate
Reads: 9621 Discuss this article on Stack Overflow
heshan's picture
Heshan Suriyaarachchi
Software Engineer
WSO2 Inc.

This article by Heshan Suriyaarachchi discusses the usage of WSO2 WSF/Jython API to write Web service clients in Jython. First, we will look at the architecture of this solution and related technologies. Then we will be going into details on writing a service client in Jython.Those of you who wish to skip the implementation details can go directly to Quick Start Guide section. If you are interested in writing a Python Service and deploying it on Axis2 you can have a look at the Deploying a Python Service on Axis2 article.

Applies To

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

 

Content

 

Solution Architecture


 

Above figure shows the architecture of the API. When your Jython client script is executed, a mapping Java service client is created and executed.  Then a Web service call is made and the result is passed back to your client script. Bottom line is that WSF/Jython extends Axis2 service client API. If you are interested in Axis2 service client API, please refer the article titled Invoking Web Services using Apache Axis2.

What is Jython

JPython was the first implementation of Python programming language in Java. Then the project was moved to SourceForge and renamed to 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 see it in the architecture diagram, Jython jar resides in between WSF/Jython and Axis2 Service Client API.

Writing a Jython Service Client

Simple Client

This section will discuss how you can write a Jython service client to consume a service. The client has to prepare the payload, send a request to the service and then receive and process the response.

The steps to be followed when implementing a client with WSO2 WSF/Jython include:

  1. Set the desired request payload and options. You can set the desired payload either in plain text form or as a WSMessage.
  2. Create a WSClient instance. You can use the WSClient instance to consume the service. Then set options as arguments to the constructor.
  3. Send request and receive response. Invoke the request() method passing the message as a parameter. This method returns a WSMessage instance, representing the response message.
  4. Consume the response. Process response in line with client business logic.

Preparing the Request Message

req_message = WSMessage(req_payload_string, {"to" :END_POINT})

In the above code fragment, a WSMessage instance is created with a payload to be sent in the request and the service endpoint. The "to" element of the option hash is mapped to the address location of the service. In other words, the "to" address indicates where the request should be sent to.

Sending a Request and Receiving a Response

client = WSClient({}, LOG_FILE_NAME)   
res_message = client.request(req_message)

For sending a request with the input message created earlier, we need a WSClient instance. We pass the message to be sent to the service to the request() method. This will send the payload contained in the given message and receive the response and return a message instance with the response payload.

Quick Start Guide

This section is aimed to help you write a service client quickly and easily using WSO2 WSF/Jython API. First, you must set up the environment by adding Jython jar and necessary axis2 jars to the classpath. Then add the WSF/Jython jar to the classpath. Once you have set up the environment correctly, you should be able to run Jython scripts that are compliant with WSO2 WSF/Jython API specifications. WSO2 WSF/Jython is shipped with a shell script that will set this environment for you. This makes it possible to execute a client script by simply providing its absolute path. For e.g.

sh wsfjython.sh /home/heshan/wsf-jython/jython/distribution/target/wsf-jython-SNAPSHOT-bin/samples/amazon.py

To start simple, let's write a simple client to consume a Yahoo service.

step 1

First, we need to prepare the payload as expected by the service.

req_payload_string = "<deduct><var1>1.8</var1><var2>4.87594</var2></deduct>"

NyoOTE: remember to import WSF/Jython classes

from org.wso2.wsf.jython.client import WSClient
from org.wso2.wsf.jython.client import WSFault
from org.wso2.wsf.jython.client import WSMessage

step 2

Next, we need to create a WSClient, with the options to be used by the client.

client = WSClient({}, LOG_FILE_NAME)

Here is the complete source code for the service client:

from org.wso2.wsf.jython.jythonwsclient import *

req_payload_string = "<deduct><var1>1.8</var1><var2>4.87594</var2></deduct>"
LOG_FILE_NAME = "/home/heshan/IdeaProjects/MRclient/src/python_client.log"
END_POINT = "http://localhost:8070/axis2/services/annotationScript/deduct"

try:
   client = WSClient({}, LOG_FILE_NAME)
   req_message = WSMessage(req_payload_string, {"to" :END_POINT})    
   print " Sending OM      : " , req_payload_string
   res_message = client.request(req_message)
   print " Response Message: " , res_message

except WSFault, e:
   e.printStackTrace();

step 3

Execute the Jython script.

Consuming Services

Minimum requirements for consuming a Web service using the WSO2 WSF/Jython API are the payload and service endpoint URI. We discussed how these can be done in the Quick Start Guide section.The advantage of using the WSO2 WSF/Jython extension is that it supports more than just SOAP. You can use WS-Addressing, MTOM and WS-Security when consuming Web services. You can also invoke services using REST style calls. The way in which these options can be associated with the WSMessage and WSClient is described below.

Using SOAP

You can use the "use_soap" option at client level to specify the SOAP version to be used.

client = WSClient({"use_soap" : "true"}, LOG_FILE_NAME)

Using REST

If you want to consume Web services using REST style calls, it can be done by setting the "use_soap" option to "false". In case of REST style of invocation, you can use either the HTTP POST method or the HTTP GET method.

# REST with HTTP POST
client = WSClient({  "to" : END_POINT,
                     "http_method" : "POST",
                     "use_soap" : "false"},
                  LOG_FILE_NAME)
# REST with HTTP GET
client = WSClient({  "to" : END_POINT,
                     "http_method" : "GET",
                     "use_soap" : "false"},
                  LOG_FILE_NAME)

Attachments with MTOM

req_message = WSMessage(req_payload_string, {"to" :END_POINT,
                                             "attachments" : {"myid1" : "first attachment", "myid2" : "second attachment"}})

When sending attachments, you can configure a client either to send the attachment in an optimized format or a non-optimized format. If the attachment is sent in binary optimized format, file content will be sent as it is, out of the SOAP body, using MIME headers and the payload would have an XOP:Include element, referring to the MIME part that contains the binary attachment.In case of binary non-optimized format, attachment content will be sent in the payload itself, as a base64 encoded string.

# send attachments binary optimized
client = WSClient({"use_mtom" : "true"})

# send attachments binary non-optimized
client = WSClient({"use_mtom" : "false"})

Using WS-Addressing

There are two basic requirements that you have to specify when using WS-Addressing on the client side with WSO2 WSF/Jython. One is that you have to provide a WS-Addressing action at message level. The other is that, you have to enable the use of WS-Addressing at client level.

req_message = WSMessage(req_payload_string,
		                {"to" : "http://localhost/echo_service_addr/echo",
                                 "action" : "http://jython.wsf.wso2.org/samples/echoString"})
              
client = WSClient({"use_wsa" : "true"})

In the above sample code fragment, WS-Addressing action is set using the "action" element of the options dictionary passed to the WSMessage constructor. WS-Addressing is enabled with the "use_wsa" option passed to the WSClient constructor.
In addition to action, there are other WS-Addressing related SOAP headers that can be sent in a message. WSO2 WSF/Jython supports these headers as properties at message level or as options at the client level. An example is shown below:

req_message = WSMessage(req_payload_string,
	                        {"to" : "http://www.company.com/order_processing/process",
	                         "action" : "http://jython.wsf.wso2.org/samples/order",
	                         "from" : "http://www.company.com/order_placing/place",
	                         "reply_to" : "http://www.company.com/billing/bill",
	                         "fault_to" : "http://www.company.com/re_odering/order"})

client = WSClient({"use_wsa" : "true"})

Using WS-Security

Note that in order to run security clients or services, you should engage WS-Addressing

req_message = WSMessage(req_payload_string,
                                {"to" : "http://localhost/samples/security_service/callback",
                                 "action" : "http://jython.axis2.org/samples/echoString"})

Then create the client using the policy object.

client = WSClient({"use_wsa" : "true",
                   "policy" : "Policy_Path"})

res_message = client.request(req_message) 

A Sample Client 

from org.wso2.wsf.jython.jythonwsclient import *

req_payload_string = "<webSearch><appid>ApacheRestDemo</appid><query>SriLanka</query><form/></webSearch>"
LOG_FILE_NAME = "/home/heshan/IdeaProjects/MRclient/src/jython_yahoo.log"
END_POINT = "http://search.yahooapis.com/WebSearchService/V1/webSearch"

try:
    client = WSClient({  "to" : END_POINT,
                         "http_method" : "GET",
                         "use_soap" : "false"},
                      LOG_FILE_NAME)
    req_message = WSMessage(req_payload_string, {})
    print " Sending OM      : " , req_payload_string
    res_message = client.request(req_message)
    print " Response Message: " , res_message

except WSFault, e:
    e.printStackTrace();

Running Samples

First download the wsf-jython-SNAPSHOT-bin.zip. Then unzip it.

Method I

Then run the samples. Below command will run the amazon.py sample.

NOTE: you should specify the full path of the sample script to be executed.

sh wsfjython.sh /home/heshan/wsf-jython/jython/distribution/target/wsf-jython-SNAPSHOT-bin/samples/amazon.py

The response is as follows.

<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2005-10-05">
    <OperationRequest>
        <HTTPHeaders><Header Name="UserAgent" Value="Axis2" /></HTTPHeaders>
        <RequestId>114HR356NB5H18RR5WNM</RequestId>
        <Arguments><Argument Name="SearchIndex" Value="Books" />
        <Argument Name="Service" Value="AWSECommerceService" />
        <Argument Name="Keywords" Value="sri lanka travel books" /><Argument Name="Operation" Value="ItemSearch" />
        <Argument Name="AWSAccessKeyId" Value="XXXXXXXXXXXXXXXXXXX" />
        </Arguments><Errors><Error><Code>AWS.InvalidParameterValue</Code>
        <Message>XXXXXXXXXXXXXXXXXXX is not a valid value for AWSAccessKeyId. Please change this value and retry your request.</Message>
        </Error></Errors>
    </OperationRequest>
</ItemSearchResponse>

Method II

You can use a Java class to run your Jython script as well.

NOTE: you should specify the full path of the sample script to be executed.

import org.python.util.PythonInterpreter;

public class test {
    static PythonInterpreter interp = new PythonInterpreter();

    public static void main(String args[]) {        
        interp.execfile("/home/heshan/IdeaProjects/MRclient/src/samples/amazon.py");
    }
}

Conclusion

WSF/Jython's Jython-Client API can be used to write a Jython service clients. It supports attachments with MTOM, SOAP, REST and WS-* standards such as WS-Addressing, WS-Security.

Resources

[1] - http://today.java.net/pub/a/today/2006/12/13/invoking-web-services-using-apache-axis2.html

[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