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
- Why Jython ?
- Getting Familiar with Jython
- Using Jython Objects in Java
- Using Java Objects in Jython
- Script Deployment
- What’s Next ?
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:
- it took me a while to write (a few write/compile/test cycles)
- it is verbose
- it's intention is not obvious at first sight
- it needs to be compiled and the .class file has to be in classpath
To add dynamic behaviour to a Java application, we rather want
- a customizer can write the code
- no compilation step for the author
- no extra deployment of .class files
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:
- Dynamic scripting environment
- Access in one environment to:
- all of your Java libraries
- most of the Python libraries
- Stable
- Fast
- Easy to integrate
- Well established
Python
- Easy to learn
- Freely usable and distributable
- Extensive standard libraries and third party modules for virtually every task
- Well documented
- Concise (you can do amazing things in very few lines of code)
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
- Download the installation .jar from www.jython.org
- Execute the .jar file
- Select ‘standalone’ as installation type
- Result: a file named jython.jar
- java –jar jython.jar
- jython.jar can be added to your classpath
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
- Simply add jython.jar to your classpath
- Start the interactive interpreter with the following class:
- Use the interpreter to:
- Play with your Java objects
- Become acquainted with the Python language
- Java objects behave as expected
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.
- Interpreted Mode
The first naive approach is just to embed an interpreter and execute the script
- Compiled Mode
Compilation when the script is used the first time helps reducing CPU time.
- Optimized Mode
The "nirvana" of embedding ... but please be a bit patient.
Interpreted Mode: The Clock Example
- A provider class returns Java2D shapes:
- These shapes rotate around the centre point
- We will implement the second hand in Jython
- We will make our implementation look like a “normal” Java object to the application
- And of course, let's change the behavior of the application at runtime !
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 ?

- Every second, 60 ticks are created
- Each tick compiles and loads a class. The positive thing about that is: Jython compiles to real bytecode ! But of course the impact on the JVM is too heavy.
- Possible improvements are:
- Use compiled scripts
- Reuse the 60 ticks
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
- Jython objects look like Java objects
- Take care when creating objects (reuse if possible)
- Optimize use of embedded scripts
- Use compiled scripts
- Use JyClass.forScript() / newInstance()
- Handle Exceptions
- At compile time
- At run time
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
- Bean properties for setter / getter pair methods
- Hook methods like __sub__(Object obj) “enable” operators
- Java objects behave like Python ones
- Familiar syntax for script authors
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.
- A Python module is a directory
- Containing an __init__.py file (usually empty)
- And any number of *.py files
- Example:
- mymodules/__init__.py
- mymodules/foo/__init__.py
- mymodules/foo/bar.py
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:
- You can pack all your modules in the standalone jython.jar
- Place the /mymodules directory in /Lib
- jython.jar/Lib/mymodules/__init__.py
- jython.jar/Lib/mymodules/foo/__init__.py
- jython.jar/Lib/mymodules/foo/bar.py
- Deploy the jython.jar with your application
- You can then import as follows:
- from mymodules.foo import bar
Tips
These tips are really helpful when using Jython in a Java application:
- Use different names for Java packages and Python modules
- Physically separate directories containing Python modules from those containing Java packages
- When importing Java classes, always use the following pattern:
- from some.cool.java.package import AFancyClass
- Avoid inner classes named py or PyInner in your Java classes
Progress On Jython
- Active mailing lists
- Increasing number of committers
- Build bots
- Automated Tests
- Lots of other activities, for example:
- Jim Baker/Michael Taylor working on an ANTLR parser for Python 2.5
- "leouser" working on jythonx
Current development status
- 2.2b1 released
- 2.2b2 just tagged, should be ready for download within days
- 2.2 (final) targeted for spring 2007
- Many 2.3 features already implemented
- Far less time expected for getting from 2.2 to 2.3, compared to getting from 2.1 to 2.2
Jython Roadmap
- Next release after 2.2
- Primarily clean-up
- Will catch up with latest CPython version (2.3, 2.4 or even 2.5 ?)
- Improve Java integration
- Jython 2.x (release after that)
- Performance improvements
- Enable CPython frameworks (Django ?)
- Jython 3.0 (“Jython 3000”)
- Twin of CPython 3.0
- Backwards incompatible
Getting involved
Sure we need an active community !
Summary
- Jython is easy…
- to install
- to use
- to deploy
- Jython integrates seamlessly with your Java applications
- Jython dynamically extends your Java applications
- You get 3 for 2 :-)
It is a good choice for your toolbox.
For More Information
- Python related links
- Jython related links
- Books
- Jython Essentials; by Samuele Pedroni and Noel Rappin
- Jython for Java Programmers; by Robert W. Bill
- Python Programming with the Java Class Libraries, A Tutorial for Building Web and Enterprise Applications with Jython; by Richard Hightower
- Python in a Nutshell, by Alex Martelli
- Learning Python, Second Edition, by Mark Lutz
- Beginning Python, From Novice to Professional; by Magnus Lie Hetland
Many thanks to
- Charles Oliver Notter, Sun
- for making it possible to speak here
- A. Sundararajan, Sun
- for implementing the javax.script engine
- The Jython developers on the mailing lists
- Ruben Bakker (uncomplex.net)
- for showing me JPython years ago
- for the clock idea and the SMTP server
- Renate Willimann, Gustaf Hansen and Lars Steiger (www.bison-group.com)
- for helping me with the presentation
- Christian Frei (The Jazoon conference)