Extending on the FAQ -- Accessing your Jython code from Java and Using Reflection to Access

Submitted by: Josh Juneau

One of the most powerful features of Jython is that it is possible to access Jython code directly within a Java application. Many times performing simple tasks within Java can produce bloated code which is difficult to maintain. Jython gives you the ability to take that same code and reduce the complexity, producing easier to read and highly maintainable code.

There are two key components which must be followed in order to access Jython code from Java. One key behind accessing Jython code from within a java appliation is the "jythonc" utility. This utility has the ability to compile Jython code into a compatible java source. jythonc can also create jar files, freeze associated code modules, enable dependency tracking, and much more. The utilty is responsible for performing the task of creating a Java source file from the designated Jython (.py) module or class. It then calls a Java compiler and creates class files from the produced java source.

As stated previously, jythonc has a number of available options. Since there are so many available options (you can see them all using jythonc without any paremeters), I will only mention a couple of the options which I find most useful.

The second key to success for accessing Jython from java is creating java-compatible classes. In order for a Jython class or module to pass as java-compatible, it must adhere to the following rules:

The class must be within a module of the same name The class must subclass a Java class or interface. A standard class would subclass java.lang.Object.

In order to use any of the methods written in jython within a java application, you must also include Java method signature hints, also known as sig-strings. Sig-strings are doc strings which adhere to the following format: "@sig <<method signature>>"

For instance:

{{{import java class Foo(java.util.Date):

}}}

The previous example illustrates the usage of sig-strings specifying a public method with a String object as a parameter and a String object as a return value.

The exception to Java method signature hints are those methods which are overridden. If a method appearing in a Jython class overrides a method which appears in the Java superclass then no method signature is required. The jythonc utility handles those cases by obtaining the information from the Java superclass.

The new class can be used from a Java class like this:

{{{public class FooTest {

} }}}

Using Reflection to Invoke Your Compiled Jython from Java

In order to make things a bit easier for invoking your Jython classes from Java you can use a bit of reflection. You simply create a Java class which accepts a String parameter for the Jython class and method ("class.method"), as well as an array of arguments (or no arguments). You can then use reflection to find your jythonc compiled Jython class utilizing these paremeters.

The advantage of using the reflection technique as opposed to hard coding against your compiled Jython classes is that it affords you the ability to modify the Jython classes without recompiling the Java code. A technique such as this also opens up the possiblity for creating an interface for executing jython stored procedures within a database.

For instance:

The following static method takes two parameters: a String value which represents the Jython class and method ("class.method") as well as a String array of arguments. The following utility only accepts String values as arguments, so the Jython method would have to accomodate for any data type conversions. The following method also does not return any values, it could be modified to return values from the executed Jython method if needed.

    public static void execJython(String filename, String[] args){
        String className = filename.substring(0,filename.indexOf("."));
        String methodName = filename.substring(filename.indexOf(".") + 1);
        Class JythonClass  = null;
        Object[] arguments  = null;
        Class [] parameterTypes = null;
        int argCount        = 0;
        try {
            JythonClass = Class.forName(className);
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        if(args != null){
           
           parameterTypes = new Class[args.length];
                for(int pTypes = 0; pTypes <= args.length -1; pTypes++){
                    parameterTypes[pTypes] = String.class;
                }
            
        } else {
            parameterTypes = new Class[] {String.class};
        }
        String defaultObj   = new String();
        int size           = 0;
        if (args != null){
            size = args.length;
            arguments = new Object[size];
        } else {
            arguments = new Object[size + 1];
        }
        try {
            Method jyMethod = JythonClass.getMethod(methodName, parameterTypes);
            if(args != null){
                for(int x = 0; x <= args.length -1; x++){
                    arguments[x] = args[x];
                }
            } else {
                arguments[0] = "none";
            }
            jyMethod.invoke(JythonClass.newInstance(), arguments);
        } catch (SecurityException ex) {
            ex.printStackTrace();
        } catch (NoSuchMethodException ex) {
            ex.printStackTrace();
        } catch (IllegalAccessException ex){
            ex.printStackTrace();
        } catch (InvocationTargetException ex){
            ex.printStackTrace();
        } catch (InstantiationException ex){
            ex.printStackTrace();
        }
  
      
    }

Suppose you have a Jython class defined as follows:

import java
class Hello(java.lang.Object):
   def __init__(self):
      pass
   def printHello(self, str=None):
      """@sig public void printHello(java.lang.String str)"""
      print 'Hello Jython!'

If you have compiled the above Jython class using jythonc and the resulting class files reside within the classpath or an imported package, you could invoke the Jython using the utility with the command execJython("Hello.hello",args) where args is a String array of arguments.

Resources: