Jython and Java: Plug-and-Play


Goal of this talk

When reading this, you will gain an understanding how to easily and efficiently embed Jython code into your Java application. I do not use the term "scripts", since this is not the primary goal here. Instead of "simply calling scripts", we will focus on how Jython objects can interact with Java objects and vice versa. It is really more about lowering barriers between the two worlds (if you thought of two worlds up to now), or to show that in fact integration is quite tight.

You will learn how to seamlessly integrate Jython with your Java applications.

For developers coming from Java, scripting with Jython is introduced using a simple example. The complete source code is available in the download section. Example and utility classes can also be downloaded in binary form, ready to play around with.

Table of contents

First I will explain why a scripting language should be in every developer's toolbox, and why Jython is one of the first choices. An interactive tour will show you how to jump start with Jython. The central part covers how to effectively embed Jython into a Java application. From a Java viewpoint, we will make Jython objects look like Java ones. Since Jython is more than a Java interpreter, we change our viewpoint and show how Java objects can be treated like Jython ones, thus being ready for a natural scripting experience. A description of the easiest way to deploy Jython will finish the "guided tour". A few words about progress made on Jython are included at the end.

Why Jython ?

Let me make a provocative claim:

We do not need scripting - Reflection already has all the dynamics !

Please do not try to read and understand the following code, only look at the picture (it's appearance):

import st.extreme.jython.Clock;
try {
  Class cCls = Class.forName("st.extreme.jython.Clock");
  Class <?> fCls =
    Class.forName("st.extreme.jython.ClockFrame");
  Object frame = fCls.newInstance();
  Object clock = cCls.newInstance();
  Method clockMethod = fCls.getMethod("setClock",
    new Class[] { Clock.class });
  clockMethod.invoke(frame, new Object[] { clock });
  Method visibleMethod = fCls.getMethod("setVisible",
    new Class[] { boolean.class });
  visibleMethod.invoke(frame, new Object[] { true });
} catch (Exception e) {
  e.printStackTrace();
}

Technically there is nothing wrong with this code (it even works), but:
To add dynamic behaviour to a Java application, we rather want
From this you should generally conclude: Reflection is not always the right approach to add dynamic behaviour to a Java application.
This is where Scripting languages come into play.


Jython

Jython (the Java implementation of the Python language) has some advantages:

Python



A working SMTP Server which prints messages to stdout


from smtpd import *
import asyncore
try:
  DebuggingServer(('localhost', 25), ('localhost', 25))
  asyncore.loop(timeout=2)
except KeyboardInterrupt:
  print "Crtl+C pressed. Shutting down."


Getting familiar with Jython


How to get started


Hands on

Play around with the interpreter

$ java -jar jython.jar
Jython 2.2b1 on java1.6.0 (JIT: null)
Type "copyright", "credits" or "license" for more information.
>>> from java.lang import String
>>> s = String("hello")
>>> t = "JavaOne"
>>> print s, t
hello JavaOne
>>> dir(String)
['CASE_INSENSITIVE_ORDER', '__init__', 'bytes', 'codePointAt', 'codePointBefore', 'codePointCount', 'compareTo', 'compareToIgnoreCase', 'concat', 'contains', 'contentEquals', 'copyValueOf', 'empty', 'endsWith', 'equalsIgnoreCase', 'format', 'getBytes', 'getChars', 'indexOf', 'intern', 'isEmpty', 'lastIndexOf', 'matches', 'offsetByCodePoints', 'regionMatches', 'replace', 'replaceAll', 'replaceFirst', 'split', 'startsWith', 'substring', 'toCharArray', 'toLowerCase', 'toUpperCase', 'trim', 'valueOf']
>>> n1 = String("21")
>>> n2 = "22"
>>> type(n1)
<type 'javainstance'>
>>> n1.__class__
<jclass java.lang.String 1>
>>> type(n2)
<type 'str'>
>>> n2.__class__
<type 'str'>
>>> from java.math import BigDecimal
>>> d1 = BigDecimal(n1)
>>> d1
21
>>> d2 = BigDecimal(n2)
>>> d2
22
>>> d2.add(d1)
43
>>> d2 + d1
Traceback (innermost last):
  File "<console>", line 1, in ?
TypeError: unsupported operand type(s) for +: 'javainstance' and 'javainstance'
>>> import sys
>>> sys.exit()

Please notice the last two statements: That is the proper way to get out of here !.
It is also noteworthy that Jython does not add any magic to POJO's: they behave like you would expect. So the + operator is not available for BigDecimal. But please keep that in mind for later.

Play around with your own java objects

We are now going to fire up Don Coleman's Jython Console, which allows us to do code completion.
Try using either
  ./startConsole.sh
or
  startConsole.bat

You should see a window saying:
Jython Completion Shell
Jython 2.2b1 on java1.6.0 (JIT: null)
>>>
Now try typing the following, and notice the suggestions the console makes to you.
Tip: Use the arrow keys to navigate, ENTER to select, and ESC to close a suggestion. And, of course, you don't have to type the prompts (>>>).
>>> from st.extreme.jython import ClockFrame
>>> from st.extreme.jython import Clock
>>> frame = ClockFrame()
>>> frame.setClock(Clock())
>>> frame.setVisible(1)
>>> frame.setTitle("cool!")
>>>
Congratulations! You just successfully played with your application, the Clock example.
To exit, simply close the window. Or, if you like typing:
>>> frame.dispose()
>>> import sys
>>> sys.exit()


What we have learned so far


Using Jython objects in Java

Now we are going to embed dynamic stuff into the clock application. The provider class has slots for certain shapes. The default implementation is in Java, and we will do 3 kinds of implementations in Jython.
  1. Interpreted Mode
    The first naive approach is just to embed an interpreter and execute the script
  2. Compiled Mode
    Compilation when the script is used the first time helps reducing CPU time.
  3. Optimized Mode
    The "nirvana" of embedding ... but please be a bit patient.

Interpreted Mode: The Clock Example


The Java Superclass

package st.extreme.jython;

public class
SecondHand implements IClockPath {
  public GeneralPath getPath() { .. }
  public Color getColor() { .. }
  public boolean isVisible() { .. }
}


SecondHand Usage

This is how the application gets the SecondHand: The provider returns the second hand. If it happens to be visible, it's color is retrieved, and its path transformed:
public class Clock extends JPanel {
  public void paint(Graphics g) {
    Graphics2D = (Graphics2D) g;
    IClockPath secondHand = getProvider().getSecondHand();
    if (secondHand.isVisible()) {
      g2.setPaint(secondHand.getColor());
      g2.fill(secondHand.getPath().
        createTransformedShape(getSecondTransform()));
  }
}


Interpreted Mode using javax.script


import st.extreme.jython.util.JyClass;
public IClockPath
  createSecondHand(String secondHandScript)
{
  return JyClass.newInterpretedInstance(IClockPath.class,
    secondHandScript, "secondHand");
}


Hands on

Interpreted mode

Press the "Customize" button, click into the new empty frame, and type (this time no code completion):
from java.awt import Color
from st.extreme.jython import SecondHand

class MySimpleSecondHand(SecondHand):
 
  def getColor(self):
    return Color.RED
   
# caller expects a variable named 'secondHand':
secondHand = MySimpleSecondHand()
The line beginning with a # is a comment line to remeber you that the caller expects a variable named "secondHand".
When done, press the "Inject" button.

Now, not seen the Tick superclass yet, we also implement the ticks (hour and minute signs around the clock) in Jython. The caller again expects a variable, this time named tick. And the script gets passed in an object named drawingMinute.
Please select customization type = Tick, and enter the following into the editor window:

from st.extreme.jython import Tick

class MyTick(Tick):
 
  def __init__(self, drawingMin):
    Tick.__init__(self, drawingMin)
     
  def isVisible(self):
    min = self.getDrawingMinute()
    sec = self.getRealtimeSecond()
    return min != sec
   
# the caller sets 'drawingMinute'
# the caller expects 'tick'
tick = MyTick(drawingMinute)
Now press "Inject".

The output (in your shell where you started the clock) is driving crazy. To do a reset, empty the editor window, and press "Inject".


What went wrong ?

If you had done nothing, you'd probably hit an OutOfMemoryError, like the analysis tool below might indicate - why ?





Compiled Mode using javax.script

public Tick createTick(String tickScript,
                       int drawingMinute)
{
  Bindings bindings = new SimpleBindings();
  bindings.put("drawingMinute",
               new Integer(drawingMinute));
  // compile only once
  CompiledScript compiledTickScript =
    getCompiledTickScript(tickScript, bindings);

 
return
JyClass.newCompiledInstance(Tick.class,
    compiledTickScript, bindings, "tick");
}


Compile only once

private CompiledScript _compiledTickScript;
private CompiledScript getCompiledTickScript(String
  tickScript, Bindings bindings)
{
  if (_compiledTickScript == null) {
    _compiledTickScript =
      JyClass.compile(tickScript, bindings);
  }
  return _compiledTickScript;
}


Reuse the 60 Ticks


private Tick[] _tickCache = new Tick[60];
public Tick getTick(int drawingMinute,
  int realtimeSecond)
{
  Tick tick = _tickCache[drawingMinute];
  if (tick == null) {
    tick = /* create it as shown ... */
    _tickCache[drawingMinute] = tick;
  }
  tick.setRealtimeSecond(realtimeSecond);
  return tick;
}


Java Developers have a dream:


What if we could just write a class definition ...
from st.extreme.jython import Tick

class OptimizedTick(Tick):
  def __init__(self, drawingMin):
    Tick.__init__(self, drawingMin)
     
  def isVisible(self):
    min = self.getDrawingMinute()
    sec = self.getRealtimeSecond()
    return min != sec
compile this into a Java class ...
and instantiate the same Java class over and over ?



Optimized Mode


Applies the “pattern”:
JyClass jyClass = JyClass.forScript(…)
jyClass.newInstance(…)
This provides a 1:1 Relation between script and class in JVM, pretty much like Java does, isn't it ?



Optimized Mode for Tick creation

private JyClass _tickJyClass;
private JyClass getTickJyClass(String tickScript) {
  if (_tickJyClass == null) {
    _tickJyClass =
      JyClass.forScript(tickScript, Tick.class);
  }
  return _tickJyClass;
}

public Tick createOptimizedTick(String tickScript,
  int drawingMinute)
{
  JyClass tickJyClass = getTickJyClass(tickScript);
  return tickJyClass.newInstance(Tick.class,
     new Integer(drawingMinute));
}



Hands on

Compiled mode

Essentially you only have to change the optimization level to "Compiled" to see the results. There still should be different classes instantiated, but not more than 60.
But we can optimize further

Optimized mode

Since we only require a class definition (without creation), you can delete the last line.
Just for fun, experiment with the visiblity a bit more, e.g.:
  def isVisible(self):
    min = self.getDrawingMinute()
    return min % 15 == 0
Now - for every injection - there should be only one class instantiated, of course 60 times.


What we have learned so far

The JyClass utility class is available for download here (see also the javadoc).
If enough users tell me they find it useful, it might get included in a future Jython release.


Using Java Objects in Jython


The Alarm Class

public class Alarm {
  private JyDecimal _hour;
  private JyDecimal _minute;
  // bean property participant
  public void setHour(Object hour) {
    if (hour instanceof JyDecimal) {
      _hour = (JyDecimal) hour;
    } else {
      _hour.setValue(hour);
    }
  }
  // bean property participant
  public Object getHour() {
    return _hour;
  }
}


The JyDecimal Class

public class JyDecimal {
  private BigDecimal _value;
  // bean property participant
  public void setValue(Object value) {
    _value = makeNumeric(value);
  }
  // bean property participant
  public Object getValue() {
    return _value;
  }
}


Hands on

A simple alarm clock: The super constructor will set the alarm to "now". We shift it  for 2 minutes.
Please note that getMinute() returns a Java object !

from st.extreme.jython import Alarm

class MyAlarm(Alarm):

  def __init__(self):
    self.setMinute(self.getMinute() + 2)






JyDecimal explained (Subtraction)

There are hook methods which enable operators.
Addition would be essentially the same. I wanted to show with a non commutative operation that the __r* method can be different.
public JyDecimal sub(Object subtrahend) {
  return new
    JyDecimal(_value.subtract(makeNumeric(subtrahend)));
}

public Object __sub__(Object subtrahend) {
  return sub(subtrahend);
}

public Object __rsub__(Object minuend) {
  // sub is not commutative
  return new
    JyDecimal(makeNumeric(minuend).subtract(_value));
}

public JyDecimal __isub__(Object subtrahend) {
  setValue(sub(subtrahend));
  return this;
}



What we have learned so far


Script Deployment

Of course there are different sophisticated ways of deploying, e.g. using a maven plugin.
But the goal here is to teach yout the most easiest way of deployment: everything in a single .jar file.

Python modules

It is necessary to give a short definition what a Python module is.

Easy deployment

Jython makes development very easy for you: The only file you have to deploy is jython.jar. We already saw that this makes the Jython runtime available to your application.
If you - in addition - want to deploy *.py files of your own, here is the simplest way:

Tips

These tips are really helpful when using Jython in a Java application:


Progress On Jython






Current development status


Jython Roadmap




Getting involved

Sure we need an active community !



Summary

It is a good choice for your toolbox.

For More Information


Many thanks to