Differences between revisions 6 and 8 (spanning 2 versions)
Revision 6 as of 2007-07-07 09:21:49
Size: 6058
Editor: c-71-204-163-42
Comment:
Revision 8 as of 2014-04-19 07:44:14
Size: 194
Editor: WAlbert
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
= Simple and Efficient Jython Object Factories =

''Submitted By: CharlieGroves''

Writing code in Python that can be easily called by Java code is one of the most attractive features of Jython. In last month's newsletter, Josh wrote [http://wiki.python.org/jython/JythonMonthly/Articles/September2006/1 Accessing Jython from Java Without Using jythonc]. It shows how to easily use Jython objects implementing Java interfaces in Java code. He mentions that "using this technique correctly is more effective than using jythonc". I'll go one step further: unless you run in an environment that requires precompiled classes(e.g. in a servlet engine with security restrictions) or have extreme masochistic tendencies you shouldn't use jythonc. It has no speed benefits over regular Jython, requires the added complexity of a compile step and doesn't have basic features like line numbers in stack traces. jythonc does allow Java types to be defined directly in Python code with the `@sig` comments which requires a separate Java class or interface using this technique. However, the `@sig` system is incomplete, error prone and requires a Jython programmer to learn a new set of syntax beyond Java and Python. Josh mentions a couple drawbacks of his technique at the end of the article: it requires the creation of a new interpreter for every new object and it reevaluates the Python code in every new interpreter created. This article shows how to avoid those problems raising the bar for jythonc even higher.

We use the same basic types that Josh used in his article. The `EmployeeType` he defined is unchanged:

{{{
package jyinterface.interfaces;

public interface EmployeeType {
    
    public String getEmployeeFirst();
    public String getEmployeeLast();
    public String getEmployeeId();
    
}
}}}

It just defines a simple bean with first, last and id fields. Then, take his `Employee.py` and add arguments for the fields to the constructor:
{{{
from jyinterface.interfaces import EmployeeType

class Employee(EmployeeType):
   def __init__(self, first, last, id):
      self.first = first
      self.last = last
      self.id = id

   def getEmployeeFirst(self):
      return self.first

   def getEmployeeLast(self):
      return self.last

   def getEmployeeId(self):
      return self.id
}}}

Finally, create a new factory class that creates these Jython Employees:
{{{
package jyinterface.factory;

import jyinterface.interfaces.EmployeeType;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.util.PythonInterpreter;

public class EmployeeFactory {

    public EmployeeFactory() {
        PythonInterpreter interpreter = new PythonInterpreter();
        interpreter.exec("from Employee import Employee");
        jyEmployeeClass = interpreter.get("Employee");
    }

    public EmployeeType create(String first, String last, String id) {
        PyObject employeeObj = jyEmployeeClass.__call__(new PyString(first),
                                                        new PyString(last),
                                                        new PyString(id));
        return (EmployeeType)employeeObj.__tojava__(EmployeeType.class);
    }

    private PyObject jyEmployeeClass;
}
}}}

This class is where Java and Python felicitously meet. The constructor first creates an interpreter and imports the Employee class into it. This means that Employee.py must be located on `sys.path` for the import to work. After a successful import, the constructor stores a reference to the Employee class. This `PyObject` functions identically to the `Employee` class in Python code. That means `jyEmployeeClass.__call__()` in Java is the same as `Employee()` in Python and `jyEmployeeClass.__getattr__("__doc__")` in Java is the same as `Employee.__doc__` in Python etc. The create method uses this to make instances of Employee. It invokes `__call__` on `jyEmployeeClass` with arguments for first, last and id which translates to `Employee(first, last id)`. It then uses `__tojava__` to extract an object implementing `EmployeeType` from the `PyObject` wrapper.

This new Main shows the factory in action:
{{{
package jyinterface;

import jyinterface.factory.EmployeeFactory;
import jyinterface.interfaces.EmployeeType;

public class Main {

    private static void print(EmployeeType employee) {
        System.out.println("Name: " + employee.getEmployeeFirst() + " "
                + employee.getEmployeeLast());
        System.out.println("Id: " + employee.getEmployeeId());
    }

    public static void main(String[] args) {
        EmployeeFactory factory = new EmployeeFactory();
        print(factory.create("Josh", "Juneau", "1"));
        print(factory.create("Charlie", "Groves", "2"));
    }
}
}}}

Since the factory only creates one interpreter, the cost of creating each employee is reduced to just the work in its constructor.

You could expand this factory to create any Jython object as in Josh's article. Just pass in the class to be imported to the constructor and have create take the generic set of arguments `PyObject[] args, String[] keywords`(see the javadocs for `PyObject.__call__` for details). The type specific factory here has the benefit that the create method has type specific arguments and allows for documentation of the constructor from Java. A user of a generic factory would have to know about the specific constructor of the Jython class and how to turn regular Java objects into the types expected by the Jython system. This factory handles that when it wraps the String arguments in `PyString`. The balance lies between how much Jython knowledge you want users of the factory to have and how loathe you are to create a third class to support a single type. In either case, this system is simpler and more comprehensible than the complexities introduced by jythonc.

Please send any questions or comments to jython-users@lists.sourceforge.net
My name is Winifred Albert. I life in Feluy (Belgium).<<BR>>
<<BR>>
Feel free to surf to my web site [[http://quicklyice.wordpress.com/%E9%97%9C%E6%96%BC%E6%88%91/|洛可幼童腦力開發]]

My name is Winifred Albert. I life in Feluy (Belgium).

Feel free to surf to my web site 洛可幼童腦力開發

JythonMonthly/Articles/October2006/3 (last edited 2014-04-20 01:26:34 by AdamBurke)