Query Java objects with Jython


March 2004

How to get the examples running

First you need to download the ContactAPIExample.jar which contains the example py and Java classes. If you haven't already downloaded Jython, click here The examples were constructed with version 2.1 . You will need to follow the download instructions and, when finished, edit the jython.bat file to include the ContactAPIExample.jar as well as the XML accessors.

To run the XML accessors, you will need to download JDOM and a suitable Xerces parser unless you wish to modify the py files for another DOM builder/parser combination.

Resources

The first place to head is the Jython site You'll find a complete list of resources, certainly everything you need to get started... if not more.

For documentation on the lanaguage, you might want to keep a link to the official Python site. It has the best language documentation available. If you can't remember how a for/loop works, for example, here is the fastest place to get an answer.

There are two books available: Jython Essentials by Noel Rappin and Samuele Pedroni Jython for Java Programmers by Bill Day

If you like digging around in newsgroups, the ASPN site is a great place to start. If you have a particular problem, chances are someone figured it out and mentioned it here.

Introduction

Jython is an implementation of Python written in Java that runs within a Java Virtual Machine. It has the ability to sit on top of Java objects and act as a creation and querying tool. Jython shares almost all the language keywords and packages of Python. Like its parent language, it is object-oriented or, alternatively, function-oriented if you prefer to go that way.

Jython is dynamic in a way that Java is not: it ships with a command line interface that lets you instantiate and interrogate objects. Unlike Java,classes can inherit from multiple supers. You can in-line functions. And you can also forget your semis!

The big benefit of Jython over Python for Java programmers is that you can inherit from Java classes, instantiate them from a command line and use a very stream-lined script syntax to manipulate them. Unlike many other scripting languages and utilities, Jython has the feel of a very powerful, fully-featured language. You can start being productive within a few short hours, because it shares that Python trait of doing the simple things very simply!

The dynamic aspect of Jython is what attracts many Java programmers. For querying complex Java objects -- for example, business domain objects or DOM trees --, it is a neat fit. You can build scripts that resemble typical shell or batch scripts, but when the time comes to add complexity, Jython is right there with some real power. Jython relies on Java Reflection techniques to permit method invocation on Java objects. You can pretty much manipulate an object in the same way you can with Java.

Syntactically, Jython is quite different from Java. Jython code requires about half the typing for the same Java functionality. Below is an example of a Jython object of type Contact. You don't need to declare the type and the "new" keyword is unnecessary.

>>> from contact import *
>>> c=Contact('1298374')
>>> print c.get('FirstName')

XML Anyone?

The Java ContactAPIExample referenced above in the Jython command line is a typical remote object accessor. It returns data in XML format which a client -- whether written in Java or Jython -- needs to convert into more friendly data types. You can download the example package and look at the simple Jython script. If this were written in Java, it would require considerably more code -- mainly because the activity of converting XML to Java requires that a JDOM tree be constructed and an object "builder" layer convert these to domain-type objects. For simple object models, this can be straightforward. More often, you will see developers defer to third-party packages for the conversion.

Jython is able to put a new face on DOM-type trees largely because, like Python, is not type-safe. There is no explicit casting from a JDOM Element type to custom Java types. You can make a call to a Java API like JDOM, get an object and start calling methods on it without the confusion of casting.

Of course, this can have a down-side. A Java compiler will catch type exceptions earlier. That normally works to save time, even if the code is a little denser.

In a Jython command-line environment, however, if you mistakenly try to invoke getChild() on an object that isn't a JDOM element, Jython is relatively forgiving. You get an exception telling you the method doesn't exist for this object and you get to try it again. The environment doesn't explode. Everything is OK.

Jython's ability to by-pass casting makes it a prime candidate for dynamically querying large data trees. The example below presents a JDOM Tree without actually converting it. You will see in the Contact class constructor

 ( def __init__(self,key))
that we are working with an XML document that is parsed and the root element referenced. Also notice that the constructor starts referencing native Java types as though there are Jython types.

With a few helper methods, the JDOM implementation is hidden and the tree appears as if it were a domain object.

#excerpt from contact.py
class XmlElement:
	
	def __init__(self, object):
		self.root = object
	
	def getList(self,key):
		return XmlElement(self.root.children)

        def getListItem(self,index):
                return XmlElement(self.root.children.get(index))
        
	def getObject(self,name):
            return XmlElement(self.root.getChild(name))
        
        def get(self,name):
            if self.root.getChild(name) == None:
                return 'N/A'
            else :
                return    self.root.getChild(name).text 
   


class Contact(XmlElement):
        

    def __init__(self,key):
	c=ContactData()
	s=c.findContact(key)
	sr=StringReader(s)
	si=InputSource(sr)

        # you can try this in JDK 1.4 but I get an IllegalArgumentException
        # which is a reflection issue...
	#return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(si)
        
        self.root= SAXBuilder('org.apache.xerces.parsers.SAXParser', 0).build(si).getRootElement()

In the Jython script contact.py, the class Contact is a subclass of XmlElement. XmlElement is constructed to either return an Element object or a Element's text representation. Lists require some addition work, but the approach is the same.

A couple of notes about the XmlElement class.

  • in writing Jython scripts, a Java programmer can be a little less diligent about picking up potential NullPointers. If you look at the code in the "get()" method, you see a typical Java approach to handling potential Nulls. The getObject() method is a better approach to Jython scripting where the intended class is a command-line. Basically, there's no need for scads of Null checking code.
  • the command-line client doesn't really need to know that the XmlElement class is returning Element types. In fact, you can use these Jython classes without ever knowing the structure behind it is JDOM-based.

    ContactAPIExample contains a list of Phones to illustrate a XML collection and how Jython can handle traversing it. The "getObject" call returns an Element just below the root structure. This allows for querying substructures, like a List.

    >>> from contact import *
    >>> c=Contact('1298374')
    >>> phones=c.getObject('Phones')
    >>> phone=phones.getListItem(0)
    >>> phone.get('AreaCode')
    '515'
    

    The Phone object (which is a Jython wrapper around a JDOM Element) exposes the text for Child element 'AreaCode'. If the XML doesn't contain an AreaCode child, the command line reports an exception and allows you to continue with another query.

    NOTE: The XML example is constructed with JDOM rather than JDK 1.4's DOM implementation. In its current version (2.1) Jython throws an IllegalArgumentException with trying to access the getElementsByTagName() method. The reason for this is that Jython relies heavily on Java reflection techniques to access objects. In the case of DOM, the offending method is contained in a private class and Java, by default, considers the access illegal. Several Jython developers have patched the current Jython release to get around this problem, but it does indicate that, despite being a highly functional package, Jython is totally at the mercy of Java's (sometimes quirky, but always security conscious) reflection implementation.

    GUIs and complex queries

    Command-line examples like the above are quick to type and the Jython buffer remembers both the commands as well as the instantiated objects. When you create a Contact type, it sticks around without the Garbage Collector reclaiming it. Such long-lived objects can be beneficial for lengthy or intermitttant debugging sessions.

    The ContactAPIExample contains search capability that allows a client to type in example text and pass a query object to the API. The API returns XML.

    From the command line, the typing is a little heavier than previous examples:

    >>> from com.javazoid.article.jython import *
    >>> ex=ContactSearchExample()
    >>> ex.setFirstName('Gervase')
    >>> ex.setLastName('Gallant')
    >>> print ContactData().search(ex)
    1298374GervaseAGallant1222 Westtown RdDes MoinesIA50311USA
    es>12983745155551212<
    Home12983745155
    3Business1298374515
    aCode>5551214Fax12983745155551215Children<
    es>
    
    

    Creating GUIs with Jython

    The ContactAPIExample's ContactSearchExample type allows you to pass up to 9 attributes. This is a prime candidate for a simple GUI that might present all of the attributes, let you provide an example and then represent the data in some type of report-type GUI.

    If you have ever used Java Swing, you are impressed by its power and portability. But the downside is that there is no such thing as a quick user interface. You either sweat through a graphical builder that hoarks out a mountain of spaghetti or you code it by hand. In either case, you have many lines of code to maintain.

    Jython gets around this through several techniques:

  • you can create a one-line-of-code widget by passing in attributes on the constructor If you look in the gui.py sample provided, you will see in the constructor how a JTextArea is created self.return=JTextArea(rows=10, columns=20,editable=0) This way, you reduce the number of lines of code, making reading and editing easier.
  • Jython works around the sometimes bizarre Swing Layout classes with the use of helper functions. For example, the pawt package provides a method of simplifying the GridBagLayout class, which is one of the most powerful and, consequently, byzantine, Swing layout managers through the use of a Jython GridBag class. This wrapper allows you to lay out quick collections of widgets by means of an add() and an addRow(). Add() will put the next widget on the same row as the last. AddRow() puts the next widget on the next line at position 0.
  • Because Swing widgets are beans, you can invoke attributes like the text() method of JTextField with a simple syntax textField.text='hello world' This isn't much easier than typing textField.setText("hello world"); but many programmers find it more readable.

    # gui.py
    
    from javax.swing import *
    from java.awt import Dimension
    from pawt import GridBag
    from java.lang import System
    from com.javazoid.article.jython import *
    
    class gui:
    
    	def __init__(self):
    		self.example = ContactSearchExample()
    		self.exampleTable={'FirstName':'','MiddleName':'','LastName':'', 'Street':'', 'City':'', 'Address':'', 'State':'', 'Zip':'', 'Country':''}
    		self.return=JTextArea(rows=10, columns=20,editable=0)
    				
    		
    	def show(self):
    		self.frame=JFrame(title="Search GUI", windowClosing=self.hide, size=Dimension(600,600))
    		
    		p=JPanel()
    		gridBag=GridBag(p)
    		self.frame.contentPane.add(p)
    		
    		items=self.exampleTable.keys()
    		items.sort()
    	
    		for i in range(len(items)):
    			text=JTextField(20)			
    			gridBag.add(JLabel(items[i]))
    			gridBag.addRow(text)
    			self.exampleTable[items[i]]=text
    	
    		b=JButton('Search', actionPerformed=self.search)
    		gridBag.addRow(b)
    		viewPort=JScrollPane(preferredSize=Dimension(300,300), minimumSize=Dimension(300,300))
    		viewPort.viewportView=self.return
    		gridBag.addRow(viewPort)
    		self.frame.show()
    		return
    
    	def hide(self,e):
    		self.frame.hide()
    		return
    
    	def search(self,e):
    		
    		self.example.firstName=self.exampleTable['FirstName'].text
    		self.example.middleName=self.exampleTable['MiddleName'].text
    		self.example.lastName=self.exampleTable['LastName'].text
    		self.example.city=self.exampleTable['City'].text
    		self.example.address=self.exampleTable['Address'].text
    		self.example.state=self.exampleTable['State'].text
    		self.example.zip=self.exampleTable['Zip'].text
    		self.example.country=self.exampleTable['Country'].text
    		
    		self.return.text=ContactData().search(self.example)
    		
    		return
    

    The gui class provided with the ContactAPIExample project is an example of these Jython techniques. There are other features that can make Jython a winner.

  • In the show() method, you will notice that the class is able to bind the input data by using a Jython dictionary, called exampleTable here. The dictionary holds a reference to the JTextFields and in the search() method the values are pulled from these fields and inserted in the Example object that is passed to the ContactAPIExample search() method. Of course, you can do the exact same gui/data binding in Java Swing. The big difference in terms of readability is that you do not have to extract an Object type from a HashMap like exampleTable and then cast it to JTextField. Jython gets around this by attempting to call the getText() method of the object. It doesn't require the arcane syntax of Java casting.
  • The syntax for dictionary creation is pretty straightforward. Typical Java code requires many more lines.
  • Invocation of the GUI does not require a Main method. From the command line you can call

    >>> from gui import *
    >>> gui().show()
    

    This displays the Search GUI and lets you type in parameters for the Search. When you see the screen, type 50311 in the Zip field and the returned XML is inserted in the TextArea at the bottom of the screen.

    That's not to say there aren't some downsides to GUI creation using Jython. The syntax is a bit simpler, but a sophisticated presentation requires an almost equivalent amount of code. Plus, developing py files such as you see in gui.py is quite time-consuming. Because of its run-time orientation, you don't find many errors that would crop up at compile-time in Java. So, as you add complexity, you have to ask if it wouldn't be easier to put it together using Java Swing in a Java IDE -- even if the resultant code is a little more verbose.


    Copyright (c)2004 Gervase Gallant gervasegallant@yahoo.com