Cool Use of Jython

I just had to share this. I am working on a very large project using BEA‘s WebLogic 7. This project takes a good 10 minutes to go through an entire compile/deploy cycle. This is a real hassle when I need to noodle something out, test some idea, etc. I use Jython for all manner of nifty things including testing things from the client side of my app, but what if I wanted to test something from within the container? Sure I could write a Cactus test (and I have written many), but adding/changing a Cactus test results in a compile/deploy cycle. What I needed was something better.

What I came up with is a truly (dirty) hack that is totally insecure yet really useful. (IOW, don’t put it on a production server.)

I embeded a Jython interpreter inside an Apache Axis web service. I then have a short Jython script on the client side that sends an arbitraty script to the server, which is executed and then the stdout/stderr are bundled up and sent back in the SOAP response. I think this is really cool.

The files involved are

The web service is where the real work occurs, but it’s brain-dead simple. Here’s JythonWebService

package com.joeygibson.soap;

import java.io.StringWriter;

import org.python.util.PythonInterpreter;
import org.python.core.*;

public class JythonWebService
{
    private PythonInterpreter interp =
        new PythonInterpreter();
    Writer out = new StringWriter();
    Writer err = new StringWriter();

    public JythonWebService()
    {
        super();

        interp.setOut(out);
        interp.setErr(err);
    }

    public String exec(String script)
    {
        interp.exec(script);

        StringBuffer results =
            new StringBuffer(
                "----- StdOut -----nn");
        results.append(out.toString());
        results.append(
            "nn----- StdErr -----nn");
        results.append(err.toString());

        return results.toString();
    }
}

Simple, eh? We create the Jython interpreter and then give it two StringWriters that will capture stdout and stderr, respectively. Then in the exec method we accept the script from the SOAP envelope, let the interpreter execute it, and then bundle up the stdout and stderr with nice little markers to differentiate them. That then goes back over the wire to the caller.

Next is remote.py that executes the service from the client side.

import string, sys
from java.io import *
from org.apache.axis import *
from org.apache.axis.client import *
from javax.xml.namespace import QName

global service, call

service = Service()
call = service.createCall()
call.setTargetEndpointAddress(
    "http://localhost:7001/ivr/services/AxisServlet")
call.setOperationName(
    QName("JythonWebService", "exec"))

f = open(sys.argv[1])
scriptLines = f.readlines()
f.close()

script = string.join(scriptLines, "")

ret = call.invoke([script])
print ret

Here we first import the necessary packages/classes from both Java and Jython. We then create the Axis Service and Call objects, setting the endpoint and operation name on the call. Next we load up the specifed file (this could easily be changed to support multiple scripts from the command line), concatenate each line into one big string, and then execute the web service. The script is then passed as the sole argument (wrapped in an array) to the exec method of Call, which executes the remote call. Finally the result is printed.

Next in line is remote.bat the batch file I used to execute the client. It just sets up a reasonable classpath and then invokes Jython.

@ECHO OFF
setlocal

set CLASSPATH=<jars from the axis/lib directory>
call jython remote.py %*

This adds all the jar files in the axis/lib directory to the classpath and then runs our remote.py script through Jython. You may have to get creative with how you set your classpath. I use 4NT as my command prompt, which has much longer command line allowances. The setting of the classpath can be a pretty long string and CMD.exe may barf on it. YMMV.

Finally we need to test something. Here’s test.py that will run on the server via our web service

from javax.naming import *

ic = InitialContext()
vv = ic.lookup("ejb/VVLookup").create()
l = vv.getValidValuesForFieldNumber(1558)

for i in l: print i

Obviosly you would have to tailor this for your setup since it’s doubtful that you have a stateless session bean deployed at ejb/VVLookup… Anyway, you can see that I create an InitialContext (with no parameters since we’re inside the container) do a lookup, execute a method that returns a collection and then iterate over it, printing them out. Since print goes to stdout, the output will be captured and then returned to the client.

That’s it! If anyone want’s a copy, let me know. It’s so simple, though, that you could just do it yourself.

Advertisements

One thought on “Cool Use of Jython

  1. Hi,
    Thank you very much for your very good sample of the AXIS use from JYTHON.
    I have tried to do so but I get the following error :
    java.lang.ClassCircularityError: org/apache/axis/message/SOAPBodyElement
    at java.lang.ClassLoader.resolveClass0(Native Method)
    …….
    It is certainly related to the jython version your are ussing.
    Can you tell me which version your are using ?
    Thanks in advance
    Regards
    Jean-Guillaume LALANNE

Comments are closed.