Zoping around: Playing with Zope and XML-RPC

I've been playing with Zope and XML-RPC for a while and now I would like writing down part of my experiments. If you have a ZOPE running on your intranet and you are interested in sharing data with your own applications, you might be tempted to use Zope to do it.

XML-RPC

Waiting for a good (official) support for SOAP in Zope, I decided to investigate what is possible to do using the XML-RPC protocol. I did several experiments and I found very little documentation about it in ZOPE. This is the reason I decided to write this little "how-to". Here is a short list of related documents:

Since it's quite easy to exchange basic data types like integer numbers and strings (real numbers have a different treatment) via XML-RPC, I want to focus here on something bit more complex: binary data.

Zope, LocalFS and XML-RPC: server side

Consider having some files in a folder called "repository", visible to Zope via LocalFS (search Zope website for more informations about it). What I want to do is download these files from my client application using XML-RPC. Lets "myfile.pdf" the file resident into your "repository" directory. What you need is:

Creating the server side script is quite easy: add a new Pyhton script "download" to your Zope accepting the "filename" parameter as input. The script should be something like this:

##parameters=filename

doc = {}

if hasattr(context.repository, filename):    
    # get the obj
    doc['filename']=filename
    obj = getattr(context.repository, filename)
    doc['data']= context.base64_encodestring(obj.data)
    return doc

this simple code looks into the "repository" folder for the "filename" file object. If the object exists this method returns a dictionary with two keys: "filename" containings the file name, and "data" containings the file contents base64-encoded.

Dues to some Zope restrictions we have to add an external method to be able to encode the string. This is easy: create an external method "base64_encodestring" linked to a python script "base64wrapper.py" with this simple content:

import base64

def base64_encodestring(s):
    return base64.encodestring(s)

def base64_decodestring(s):
    return base64.decodestring(s)

Now your server side script is done and working.

 

Sample 1: A Python client

Creating a Python script ables to claim the file and save it in a local folder is a simple task.

import xmlrpclib
import base64
import os

def DownloadFile():
    # connect to the server
    ZopeServer=xmlrpclib.Server('http://localhost:8080/YourFolder')
    # call method
    dummy = ZopeServer.download('myfile.pdf')
    # get 1st parameter: filename
    filename = dummy['filename']
    # get 2nd parameter: file data (base 64 encoded)
    data = base64.decodestring(dummy['data'])
    # create a temporary file
    f = open('c:\\temp\\zope\\' + filename, 'wb')
    f.write(data)
    f.close()

where "YourFolder" is the Zope folder containings the script "download" providing the server side support. Replace "localhost" with your own Zope location as well as you can change the place where you write the new file.

 

Sample 2: A Visual Basic client

The same way we did with Python we can build a little client for your VB application. Obviously you require a Visual Basic XML-RPC library in order to deal with Zope. I found a simple VB class providing all functionalities required. This class "ComXmlRpc" has been written by Ignacio Vazquez ivazquez@users.sourceforge.net and you can easily find it.

This library maps basic data between Python and Visual Basic in a straightforward way, a little note is necessary for complex data structures:

Python Visual Basic
tuples, lists arrays
dictionaries objects

 

Private Sub DownloadFile()
    Dim sDocument As String
    sDocument = "myfile.pdf"

    ' Build a client
    Dim xmlrpcserver As comxmlrpc.rpcClient
    Dim params() As Variant
    ReDim params(0)
    params(0) = sDocument

    'Build a server object
    Set xmlrpcserver = New comxmlrpc.rpcClient
    'Set the server uri
    xmlrpcserver.uri = "http://localhost:8080/YourFolder"

    'Call the method
    Dim obj As Object
    Set obj = xmlrpcserver.execute("download", params)

    'Fetch data
    Dim vFilename As Variant
    Dim vData As Variant
    vFilename = obj.Item("filename")
    vData = obj.Item("data")

    'Decode and make file
    Dim objBase64 As Base64Lib.Base64
    Set objBase64 = New Base64Lib.Base64
    Call objBase64.DecodeToFile(CStr(vData), "C:\\Temp\\Zope\\" + vFilename)
End Sub

 

Sample 3: A LUA client

LUA is a lightweight programming language and, as well as Python, suitable for scripting. You will be able to find more complete details from the language website. Since we currently use this language in our company, I provide an example with this language to show how easy is to communicate between two scripting languages. As well as Python and VB, LUA requires some external modules able to understand XML-RPC. I used the XMLRPC module provided by Jay Carlson. The Lua clients sounds like this:

Lua doesn't make distinction between lists, tuples or dictionaries. The one and only native data structure for complex data is the "table" that can be compared to a Python dictionary (hash table, ...). The XMLRPC library is able to properly translate Python lists, tuples and dictionaries into Lua tables. Since the syntax is quite similar to Python I suppose you can guess what this function actually does.

function DownloadFile()
    -- connect to the remote server
    local ZopeServer = XMLRPC.Server('http://localhost:8080/YourFolder')
    -- call the method
    local res = ZopeServer.download('myfile.pdf')
    -- get data
    local filename = res['filename']
    local data = res['data']
    -- decode and save the file
    local fn = openfile('c:\\temp\\zope\\' .. filename, 'wb')
    write(fn, Code.unbase64(data))
    closefile(fn)
end

 

The code in this document has been provided for didactical purpose only and it is not expected to work "as is". It requires support libraries, but you would be able to find all the necessary documentation about it in order to make it work. Also, as I stated at the beginning of this document, this is the result of my experiments. I'm sure there are several ways to do it in a different way.

Hope this helps.