|
IntroductionJython 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. 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 queriesCommand-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)
Creating GUIs with JythonThe 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:
# 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. >>> 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 | ||||