Chapter 18. Applets

CONTENTS
  •  What Are Applets?
  •  Working with Applets
  •  Using an Applet as a Container
  •  Transforming the Address Book Application into an Applet
  •  Advanced Topic: AppletContext and AppletStub
  •  Summary

Terms in This Chapter

  • Applet

  • Batch file

  • Browser

  • Browser simulator

  • Code base

  • Context

  • HTTP/FTP protocol

  • JAR/zip file

  • Lifecycle management

  • Sandbox

  • Stub

  • Tag

Once upon a time, there was a relatively new company named Netscape that became king of the Internet. It was Netscape's acceptance of Java applets that put Java on the map.

If you follow Java, you know that, ironically, it's had a lot more impact on the server side (J2EE, servlets, JMS, RMI, JAVA IDL, EJB, etc.) than on the client side (applets, Swing). However, change is under way. With later Java releases, Sun Microsystems has introduced a plug-in for browsers that addresses many of the shortcomings of applets by allowing them to be cached.

What Are Applets?

Applets represent a way to deliver safe code for producing lively Internet GUIs. They execute within a browser and are associated with (i.e., embedded in) an HTML page, so they must be inherently portable to run in a variety of browsers on a variety of platforms.

Let's rewind a bit. Applets execute within a browser, which means that code that somebody else wrote is running on your computer. That may sound dangerous, but applets can run only inside a security "sandbox," which won't allow them to do things like read or write to files, read environment variables, or open network connections to any host except the one on which they originated. With current releases of Java, you can tweak the sandbox to give applets more privileges, but they'll never be allowed to do anything that would harm you. What's more, they're virus-resistant.

All applets derive from java.applet.Applet, and so, since Jython compiles to Java bytecode and can subclass a Java class, all we have to do is subclass java.applet.Applet and compile. Sounds easy enough, doesn't it? In fact, if you've worked with applets, most of this chapter will be a review, except for compiling Jython code to an applet.

Working with Applets

Let's start off with an interactive session. Import Applet from the java.applet package.

>>> from java.applet import Applet >>> applet = Applet()

An applet is a panel (java.awt.Panel), and, as we learned earlier, a panel is also a component.

>>> applet.class.superclass <jclass java.awt.Panel at -731859681>

So, if an applet is a panel, and a panel is a component, an applet is also a component (and thus a component container). That means that everything you've learned about components and panels applies to applets as well.

Surveying the Applet Landscape

Applet inherits all of class java.applet.Applet's functionality. What's more, the Applet class employs the Fa ade design pattern, which facilitates the use of many utilities in java.net, java.io, and so forth, so it makes tasks such as Web image downloading as easy as possible.

To see the amount of functionality applets provide, take a look at Applet's methods. Here are just the first fifteen:

>>> for i in range (0, 15): ...      print methods[i] ... public void java.applet.Applet.destroy() public java.applet.AppletContext java.applet.Applet.getAppletContext() public java.lang.String java.applet.Applet.getAppletInfo() public java.applet.AudioClip java.applet.Applet.getAudioClip(java.net.URL) public java.applet.AudioClip java.applet.Applet.getAudioClip(java.net.URL,java.lang.String) public java.net.URL java.applet.Applet.getCodeBase() public java.net.URL java.applet.Applet.getDocumentBase() public java.awt.Image java.applet.Applet.getImage(java.net.URL) public java.awt.Image java.applet.Applet.getImage(java.net.URL,java.lang.String) public java.util.Locale java.applet.Applet.getLocale() public java.lang.String java.applet.Applet.getParameter(java.lang.String) public java.lang.String[][] java.applet.Applet.getParameterInfo() public void java.applet.Applet.init() public boolean java.applet.Applet.isActive() public static final java.applet.AudioClip java.applet.Applet.newAudioClip(java.net.URL)

Here are the rest:

>>> for i in range (15, 23): ...     print methods[i] ... public void java.applet.Applet.play(java.net.URL) public void java.applet.Applet.play(java.net.URL,java.lang.String) public void java.applet.Applet.resize(int,int) public void java.applet.Applet.resize(java.awt.Dimension) public final void java.applet.Applet.setStub(java.applet.AppletStub) public void java.applet.Applet.showStatus(java.lang.String) public void java.applet.Applet.start() public void java.applet.Applet.stop()

That's a lot of methods to learn, and this is just the tip of the iceberg since we're not considering any methods that Applet inherits from its superclasses. (We covered those methods earlier in this book.)

Using an Applet as a Container

Because you can use applets interchangeably with panels they're easy to test and debug. Debugging is even easier if the applet runs as a standalone application rather than within a browser. Let's run one from the interactive interpreter.

Import Applet, List, and Frame.

>>> from java.applet import Applet >>> from java.awt import List, Frame

Create an instance of Applet and List; add the List instance to the applet instance.

>>> applet = Applet() >>> list = List() >>> applet.add(list) java.awt.List[list0,0,0,0x0,invalid,selected=null]

Create an instance of Frame; add applet to it.

>>> frame = Frame("Frame that contains the Applet") >>> frame.add(applet)     java.applet.Applet[panel0,0,0,0x0,invalid,layout=java.awt.FlowLayout]

Pack and show frame.

>>> frame.pack() >>> frame.visible=1

Add some items to list.

>>> list.add("Hello") >>> list.add("Goodbye")

Figure 18-1 shows our first applet. (It may be a stretch to call it an applet since we're using it like a panel.) You can see that working with an applet is much like working with a panel, except that there's a lot more to it.

Figure 18-1. Our First So-Called Applet

graphics/18fig01.gif

Applet Lifecycle Management

The browser lets applets know when they're no longer active or in view. It also tells them when they're created and when they're about to be destroyed so they can create or free resources. Such notification is called lifecycle management. An applet is created and executes within a browser. The browser determines when resources should be created or restored and notifies the applet accordingly.

Here are the browser's methods for notifying an applet of a lifecycle event:

  • init() creates the applet

  • destroy() destroys the applet

  • start() makes the applet visible or active

  • stop() makes the applet invisible or inactive

To implement these methods, we have to subclass the Applet class.

Import the Applet class and the needed java.awt classes.

>>> from java.applet import Applet >>> from java.awt import Frame, List, Button, Panel, BorderLayout

Define a Python class that subclasses Applet (java.applet.Applet).

>>> class MyApplet (Applet): ...     def __init__(self): ...             self.list = List() ...             self.add(self.list) ...     def init(self): ...             self.list.add("Init called") ...     def destroy(self): ...             self.list.add("Destroy called") ...     def start(self): ...             self.list.add("Start called") ...     def stop(self): ...             self.list.add("Stop called") ...

We can also add a browser simulator (a "fake browser"). A simulator allows us to activate the lifecycle notifications, and thus simulate events, by hitting the corresponding buttons.

Create a frame and a MyApplet instance. Add the instance to the frame.

>>> fakeBrowser = Frame("Fake Browser") >>> applet = MyApplet() >>> fakeBrowser.add(applet, BorderLayout.CENTER)

Create four buttons, and add them to the toolbar. The buttons correspond to the individual lifecycle notifications the applet receives from the browser.

>>> b = Button      #Shortcut for Button class >>> start, stop, init, destroy = b("start"), b("stop"), b("init"),                                  b("destroy") >>> buttons = (start, stop, init, destroy) >>> toolbar = Panel() >>> for button in buttons: ...     toolbar.add(button) ... java.awt.Button[button0,0,0,0x0,invalid,label=start] java.awt.Button[button1,0,0,0x0,invalid,label=stop] java.awt.Button[button2,0,0,0x0,invalid,label=init] java.awt.Button[button3,0,0,0x0,invalid,label=destroy]

Add the toolbar to the frame in the north region, and make the frame visible.

>>> fakeBrowser.add(toolbar, BorderLayout.NORTH) >>> fakeBrowser.pack() >>> fakeBrowser.visible=1

Create handlers for the buttons that call the lifecycle methods when pressed.

>>> def __init(event):applet.init() ... >>> def __destroy(event):applet.destroy() ... >>> def __start(event):applet.start() ... >>> def __stop(event):applet.stop() ...

Register the handlers to the buttons.

>>> handlers=(__start,__stop,__init,__destroy) >>> for i in range(0,4): ...     buttons[i].actionPerformed=handlers[i] ...

Test the applet by hitting the buttons. Your browser simulator should look like Figure 18-2.

Figure 18-2. Simulated Lifecycle Event Notification

graphics/18fig02.gif

Compiling an Applet

To compile a Jython class as a Java class, you have to put it in a module of the same name. Thus, MyApplet.py contains the MyApplet class. Here's the code:

from java.applet import Applet from java.awt import List        # Define a Python class that subclasses the Applet class.        # (java.applet.Applet) class MyApplet (Applet):        def __init__(self):              self.list = List()              self.add(self.list)        def init(self):              self.list.add("Init called")        def destroy(self):              self.list.add("Destroy called")        def start(self):              self.list.add("Start called")        def stop(self):              self.list.add("Stop called") if __name__ == "__main__":        from FakeBrowser import FakeBrowser        fakeBrowser = FakeBrowser()        fakeBrowser.addApplet(MyApplet())

Notice that the main block imports the FakeBrowser class, which is based on the browser simulator we created in the last interactive session. (FakeBrowser isn't listed, but you can look it up in FakeBrowser.py. Running MyApplet.py yields the same output as before.)

To compile MyApplet, use jythonc at the command prompt.

jythonc  deep --package com.awl.jython_book --jar myapplet.jar MyApplet.py

We saw how to use Jythonc in Chapter 1. Note that the package is specified as com.awl.Jython_book and that the classes are put in a JAR file called my.applet.jar. (A Java archive file is a zip file but with a manifest denoting that MyApplet is an applet.)

Because we added FakeBrowser to the main block, we can run MyApplet as a standalone application (com.awl.Jython_book.MyApplet) simply by entering the following at the command line:

java -classpath %CLASSPATH%;myapplet.jar com.awl.jython_book.MyApplet

which puts Jython.jar and myapplet.jar in the classpath. MyApplet needs Jython.jar in order to run.

We're not done yet. We want to run this applet "embedded" in an HTML file inside a browser, so we have to configure the Applet tag for applets.

First, some exercises:

  • Add a Clear button to the applet that clears the list in MyApplet.

  • Recompile the applet. Hint: You can use a batch file called compile.bat for this, but be sure to open and study it first.

  • Run the applet in FakeBrowser with the Java interpreter. Hint: Use a batch file called run.bat, but open and study it first.

No Archive Tag

If your browser doesn't support the archive tag, or if you're having problems compiling your applet, try using the all or core arguments to jythonc. These arguments instruct jythonc to include JythonC.jar in the applet's jar file.

jythonc  core --package com.awl.Jython_book --jar myapplet.jar MyApplet.py

or

jythonc  all --package com.awl.Jython_book --jar myapplet.jar MyApplet.py

Embedding an Applet in an HTML Page

To embed MyApplet in a Web page, we need the applet tag, which takes the following form:

<applet   code="com.awl.jython_book.MyApplet"   codebase=".\jpywork"   archive="..\..\lib\jython.jar"   width=400   height=400 >   <param name="hello" value="hello"> If you don't have a Java-enabled browser, you need to get one! </applet>

Each parameter takes a name/value pair, and each has a purpose:

  • Code holds the name of the applet class (in our case, com.awl.Jython_book.MyApplet.class).

  • Codebase holds the relative path of the code base the root directory from which the browser loads the Java classes and resources it needs. It's equivalent to the document base, from which the browser loads the document form. In other words, if an applet is loaded from www.jython.org\demo\applet.html, the document base is loaded from www.jython.org\demo. (Remember, the default operation for jythonc is to put the class files in the jpywork subdirectory of the current directory.)

  • archive holds the name of any zip or jar files this applet uses-in our case, \ lib\jython.jar, which holds all of the classes that Jython needs. Make sure you have the same version of Jython.jar as the one you compile with. You can get the right version for whatever Python version you're working with from c:\Jython; copy it into c:\Jython_book\scripts\lib.

  • width/height holds the width and height of the applet in pixels.

  • <param name=value=> specifies the applet-defined (application-specific) name/value parameters.

The applet Tag and Browsers

The applet tag works in the applet viewer (AppletViewer), but to run other browsers you have to expand it to include the myapplet.jar file in the archive parameter and take out codebase (or set it to a period [.]). The following code, from MyAppletJar.html, works with Internet Explorer and Netscape:

<html> <body> <center> <applet   code="com.awl.jython_book.MyApplet"   archive="..\lib\jython.jar,myapplet.jar"   width=400   height=400 >   <param name="hello" value="hello"> If you don't have a Java-enabled browser, you need to get one! </applet> </center> </body> </html>

Putting the class files in a jar file speeds downloading. Unfortunately, however, AppletViewer doesn't recognize the archive parameter. This code (from MyApplet.html) does work with AppletViewer as well as Internet Explorer and Netscape, although its download time is longer than that of jar files.

<html> <body> <center> <applet   code="com.awl.jython_book.MyApplet"   codebase=".\jpywork"   archive="..\..\lib\jython.jar"   width=400   height=400 >   <param name="hello" value="hello"> If you don't have a Java-enabled browser, you need to get one! </applet> </center> </body> </html>
Running MyApplet with AppletViewer

The AppletViewer utility, which ships with the JDK, allows developers to run applets without a browser. It supports a subset of the Netscape and Internet Explorer HTML tags that are associated with a Java applet.

To run AppletViewer from the command line do this:

appletviewer MyApplet.html

In Netscape or Internet Explorer, open the file from the File\Open menu item. You can use the applet with either MyApplet.html or MyApplet.Jar.html.

Configuring Applet Behavior

An applet can be customized. The following HTML, for example, defines two applet-specific parameters, "color" and "hello":

<html> <body> <center> <applet   code="com.awl.jython_book.MyApplet"   codebase=".\jpywork"   archive="..\..\lib\jython.jar"   width=400   height=400 >   <param name="hello" value="hi mom">   <param name="color" value="blue"> If you don't have a Java-enabled browser, you need to get one! </applet> </center> </body> </html>

Call the getParameter() method of java.applet.Applet to read the applet's parameters. This method in turn gets the parameters from the applet stub (AppletStub).

The Applet Stub

The applet gets the parameter information from AppletStub, which is passed to the Applet class via the setStub() method by AppletContext. Java.applet. Applet implements setStub(), so you don't have to.

AppletBrowser

I've created a fully functioning AppletViewer-like application, called AppletBrowser (which we'll cover later in detail). AppletBrowser implements the needed stub and context. We're going to use it in our interactive sessions.

Import JApplet, and create an instance of it.

>>> from javax.swing import JApplet >>> applet = JApplet()

Import AppletBrowser, and create an instance of it.

>>> from AppletBrowser import AppletBrowser >>> browser = AppletBrowser()

Add the applet instance to AppletBrowser to simulate its loading into AppletViewer or another Java-enabled browser.

>>> browser.addApplet(applet)

Get the color parameter from the applet.

>>> color = applet.getParameter("color") >>> print color "blue"

Get the hello parameter from the applet

>>> hello = applet.getParameter("hello") >>> print hello "hi mom"

The other parameters codebase, code, archive, and so forth which aren't defined by the param keyword, are accessible through applet.getParameter().

Get archive, code, and codebase from the applet, and print them out.

>>> code = applet.getParameter("code") >>> codebase = applet.getParameter("codebase") >>> archive = applet.getParameter("archive") >>> print (code, codebase, archive) ('com.awl.java_book.MyApplet', './jpywork/', '..\..\lib\jython.jar')

Try to get a parameter that doesn't exist. getParameter() will return None.

>>> print applet.getParameter("my_param") None

Use parameters to pass information, such as the URL and driver name of the database to which the applet connects, or the COS naming service where it looks for its stubs. Basically, the applet's parameters customize its behavior.

An Applet Example

The following code (from context\MyApplet.py) shows an applet that does what the applet in the previous session does, but in a module that you can compile and use in a browser or in AppletViewer. (Pay special attention to the showParameters() method.)

from javax.swing import JApplet from javax.swing import JList, DefaultListModel, JScrollPane        # Define a Python class that subclasses the JApplet class        # (javax.swing.JApplet) class MyApplet (JApplet):        def __init__(self):               self.list = DefaultListModel()               _list = JList(self.list)               self.contentPane.add(JScrollPane(_list))        def print(self, str):               self.list.addElement(str)        def init(self):               self.print("Init called")               self.showParameters()        def destroy(self):               self.print("Destroy called")        def start(self):               self.print("Start called")        def stop(self):               self.print("Stop called")        def showParameters(self):                     # Get the color parameter.               color = "color", self.getParameter("color")                     # Get the hello parameter.               hello = "hello", self.getParameter("hello")                     # Get the myparam parameter.               myparam = "myparam", self.getParameter("myparam")               codebase = "codebase", self.getParameter("codebase")               params = (color, hello, myparam, codebase)               for param in params:                self.print("%s equals %s" % param) if __name__ == "__main__":        from AppletBrowser import AppletBrowser        fakeBrowser = AppletBrowser()        fakeBrowser.addApplet(MyApplet())

This version of AppletViewer, which derives from JApplet, uses showParameters() to call the __init__ method. ShowParameters() shows the parameters in a listbox in much the same way as in the interactive session.

As an exercise, compile and run context\MyApplet.py in the applet viewer or Netscape Navigator. (You'll have trouble running it in Internet Explorer because it uses Swing classes. To get it right you'll have to do some extra configuring and download a version of Swing that's compatible with JVM v.1.1.4.)

Assume Nothing with Browsers

With applet development you can make very few assumptions about the browser. For example, J2ME and older browsers support only AWT.

Reading Applet Files

For security reasons, you can't read files from an applet if they're on the client's hard drive (unless you configure Java security to allow this). However, you can read them from the Web server that serves the applet to the client. We won't cover Java security here, but I do recommend that you read the online security documentation at the Javasoft Web site.

The way to read applet files is with a URL. We'll see how with a sample file located on the Jython Web server. (If you have your own Web server, you can skip this next section.)

The Python Web Server

Python's Web server can be found in the SimpleHTTPServer module. Here's what to do to get it up and running:

>>> from SimpleHTTPServer import test >>> test() Serving HTTP on port 8000 ...

SimpleHTTPServer delivers HTML pages from the current working directory, so to run the Web server for our HTML page we have to start a DOS prompt and run the module in the .\scripts\chap18\context subdirectory. Then we can access the applet with Netscape Navigator by entering the URL http://your_ip_address:8000/MyAppletJar.html, replacing "your_ip_address" with your server's address. On Windows NT/98, you can find that address via the ipconfig command.

C:\>ipconfig Windows 98 IP Configuration 0 Ethernet adapter :         IP Address. . . . . . . . . : 0.0.0.0         Subnet Mask . . . . . . . . : 0.0.0.0         Default Gateway . . . . . . :

(To use Internet Explorer you need the Java plug-in or the JFC Swing package. See the Javasoft Web site for more information.)

If you're running the Python Web server and the browser on the same machine, use the URL http://local_host_:8000/MyAppletJar.html. However, you'll be better off using the IP address or the domain name service (DNS) entry. On Windows NT/98, the DNS entry usually corresponds to the name of your machine, which replaces "localhost" in the URL.

If you use another Web server, you may have to adjust the port setting to match it. The default port for HTTP is 80. Also, if you don't have a network card, you can use the applet browser (AppletBrowser), which simulates a browser talking to a Web server. (AppletBrowser can do this even on a separate machine.) Lastly, you can use the File/Open menu item in Netscape Navigator to open the HTML containing the "embedded" applet.

Working with URLs in an Interactive Session

To read a file from the server you need the URL class, which you can find in the java.net package. The Applet class has methods that return both the document base URL and its code base counterpart. URL's openStream() method returns an input stream, which means that if you have a file called data.dat you can append it to one of URL's methods.

Import JApplet from javax.swing.

>>> from javax.swing import JApplet >>> applet = JApplet()

Use AppletBrowser to give the applet a context and a stub (to simulate the applet's environment).

>>> from AppletBrowser import AppletBrowser >>> browser = AppletBrowser() >>> browser.addApplet(applet)

Show the code base (using Java and Jython).

>>> applet.getCodeBase() file:/C:/book/scripts/chap18/context/jpywork/ >>> applet.codeBase file:/C:/book/scripts/chap18/context/jpywork/

Show the document base (using Java and Jython).

>>> applet.getDocumentBase() file:/C:/book/scripts/chap18/context/ >>> applet.documentBase file:/C:/book/scripts/chap18/context/

Java.net.URL

The Java API documentation will tell you what you want to know about java.net.URL. I'll just show you its form:

>>> url = applet.documentBase >>> url.class <jclass java.net.URL at 960293886> >>> dir (url.class) ['ref', 'file', '__init__', 'toExternalForm',  'setURLStreamHandlerFactory', 'content', 'getFile', 'sameFile', 'getProtocol', 'openStream', 'getContent', 'protocol', 'getHost', 'port', 'openConnection', 'host', 'getRef', 'getPort']

There are two files in the context directory with the name data.dat. One is the context itself and is associated with the document base URL; the other is in the jpywork subdirectory and is associated with the code base URL.

Import URL from java.net.

>>> from java.net import URL

Get documentBaseURL from the applet.

>>> documentBaseURL = applet.documentBase

Create a URL instance based on documentBaseURL's context.

>>> data_dat = URL(documentBaseURL, "data.dat") >>> data_dat file:/C:/book/scripts/chap18/context/data.dat

Get the associated input stream.

>>> instream = data_dat.openStream()

Import the classes needed to read the data.dat text file.

>>> from java.io import BufferedReader, InputStreamReader

Create a BufferedReader stream by chaining an InputStreamReader instance to instream. (See the Java API online documentation for more information about these classes.)

>>> data_reader = BufferedReader(InputStreamReader(instream))

Show the contents of the file.

>>> data_reader.readLine() 'Hello from the Document base!'

As an exercise, read data.dat in the code base URL.

The great thing about URL is that it works with the HTTP and FTP protocols. This means that you can download file data from anywhere on the Internet. The question is, why do this?

The answer is that an application that doesn't get data from somewhere is pretty useless. Consider the advantages of adding applet capabilities to the address book application. With them, a user could access her address book from any location on the Internet. We'll see how in the next section.

Transforming the Address Book Application into an Applet

What we're going to do is convert AddressMain, which is currently a form, into an applet. Later we'll make changes to AddressModel so that it can deal with URLs instead of files. This means that it will be able to read and write to any files to which it refers, including any file transmitted over the HTTP protocol.

Changes to AddressMain

First let's make it so AddressMain derives from an applet instead of a frame.

class AddressMain(JApplet):

Now let's add the __init__ and __destroy__ methods. __init__ gives us access to the database, which we need to initialize AddressModel. __destroy__ calls AddressModel's writeAddress() function, which writes addresses out to a file (if the protocol supports this).

def init(self):       url=None       # To hold the document base url.       try:             url = self.getDocumentBase()       except:            print "Unable to get document base URL" def init(self):       url=None       # To hold the document base url.       try:             url = self.getDocumentBase()       except:             print "Unable to get document base URL"

Now, because we have to move some of the code out of __init__, we need to create another method to initialize the GUI components based on AddressModel.

Def initGUI(self):              # Move to the first address in              # the list and table.       If self.model.getSize() > 0:              Self.addresses.setSelectIndex(0)              Address = self.model.getAddressAt(0)              Self.form.set(address)

Changes to AddressModel

We've made our address book application fairly modular, so it's easy to add support for reading from a URL. In fact, it takes just three lines of code. We now have a class, AddressURL (from AddressBook\AddressModel.py), that has its own readAddresses() and writeAddresses() methods, which means that we no longer have to import them from AddressUtils.py.

Here's the modified AddressBook\AddressModel.py module:

from address import Address ... ... from AddressURL import AddressURL class AddressModel(AbstractTableModel, ListModel):        """The AddressModel is both a ListModel and a          TableModel."""        def __init__(self, url):              """Initialize the Address model.              Read the dictionary from the file."""              self.dict = None    # holds the dictionary of addresses.              self.list = None    # holds the sorted list of names,                                  # which are keys                                  # into the dictionary.              self.listeners = [] # to hold list of                                  # ListModelListeners              self.io = AddressURL(url)                     # Read the addresses from the AddressURL class.              self.dict = self.io.readAddresses()           ...           ...      def writeAddresses(self):              """Write the address data to the file."""              self.io.writeAddresses(self.dict)
AddressURL

AddressURL uses the openStream() method to read a file from a URL. It writes to the file as well if its underlying protocol (HTTP, FTP, file, etc.) provides the necessary support. Using getOutputStream() (from java.net.URLConnection), AddressURL gets the file's associated output stream.

Take a look at AddressBook\AddressURL.py. Be sure to read the comments.

from java.io import ObjectInputStream, IOException from java.io import ObjectOutputStream, File, FileOutputStream from java.net import URL, UnknownServiceException from string import split, join class AddressURL:        def __init__(self, url, filename="./addr.dat"):                     # Set the url. If the url does not exist,                     # create one based on the current                     # directory and filename.              if(url):                     self.url = URL(url, filename)              else:                     file = File(filename)                     self.url = file.toURL()        def readAddresses(self):              """              Read in a dictionary of addresses.              Uses URL.openStream()              """              file = None # to hold the file input stream                     # Use try/finally to work with the file.                     # If the file does not work for any                     # reason then close it.              try:                            # Try to read in the addresses from the                            # file. If you can't read the addresses                            # then print an error message.                     try:                            file = self.url.openStream()                            in_stream= ObjectInputStream(file)                            dict = in_stream.readObject()                            return dict                     except IOException, error:                            print "ERROR reading url: " + self.url                            print error.class.name                            print error.message                            return {}              finally:                     if not (file is None): file.close()        def writeAddresses(self, dict):              """              Write the addresses instances              in the dictionary to a              file.              The writeAddresses method uses              URL.openConnection().getOutputStream(), and              attempts to write the dictionary of              addresses to a file.              If the writing fails because the protocol              does not support writing, then              writeAddresses checks to see if              the protocol is file protocol. If it is file              protocol, writeAddresses calls              writeAsFile, which              opens a file using FileOutputStream.              """                     # to hold the output file output stream.              file=None                     # Use try/finally to write the instances                     # to a file.                     # If all else fails then close                     # the file object.              try:                            # Write the address instances in dict                            # to a file specified by filename.                            # If there are any errors then                            # print an error message.                     try:                            urlConnection = self.url.openConnection()                            file = urlConnection.getOutputStream()                            out_stream = ObjectOutputStream(file)                            out_stream.writeObject(dict)                            # If the protocol does not support                            # writing, do this....                     except UnknownServiceException, noservice:                                  # If the protocol is file,                                  # then write it as a file.                            if self.url.protocol=='file':                                  filename=self.url.toString()                                  self.writeAsFile(filename, dict)                            else:                                  print noservice.message + self.url                     except IOException, error:                            print "ERR: writing addresses"+ self.url                            print error.class.name                            print error.message              finally:                     if(file):file.close()        def writeAsFile(self, filename, dict):              """              Writes out a dictionary of addresses by              using              FileOutputStream.              """                     # Extract the filename from the URL                     # string.              filename = filename[5:]              filename = File(filename)              file = None #to hold the file output stream.              try:                            # Write the address instances                            # in dict to the file.                            # If there are any errors                            # then print an error message.                     try:                            file = FileOutputStream(filename)                            out_stream = ObjectOutputStream(file)                            out_stream.writeObject(dict)                    except IOException, error:                            print "ERROR writing: " + filename                            print error.class.name                            print error.message              finally:                     if(file):file.close()

Running the Address Book Application

Our address book application can be run as an application or as an applet. Its main block creates an instance of JFrame and adds an instance of AddressMain to it. Here's part of the code (from AddressBook\AddressMain.py):

def __windowClosing(event):        event.source.dispose()        from java.lang import System        System.exit(0) if __name__ == "__main__":        mainWindow = Frame("Address book application")        applet = AddressMain()        applet.init()        mainWindow.contentPane.add(applet)        mainWindow.windowClosing = __windowClosing              # Pack the frame and make it visible.        mainWindow.pack()        mainWindow.setSize(600, 300)        mainWindow.visible=1

Run the application with AppletBrowser so you can debug and test it as you do the exercises. (The code is from run.py.)

from AppletBrowser import AppletBrowser from AddressMain import AddressMain applet = AddressMain() browser = AppletBrowser() browser.addApplet(applet)

The applet browser provides AppletContext and AppletStub, which make available the getDocumentBase(), resize(), and getParameter() methods, among others.

Try these exercises:

  • Add an applet parameter that specifies the address book application file name. The name should be relative to the document base, and it should correspond to a specific address book. Reviewing the section on AppletBrowser should help you here.

  • Get a type-4 JDBC driver so you can get the application to work with an RDBMS system.

  • Compile and run AddressBook in a browser.

Using AppletBrowser to Develop Applets

To use AppletBrowser, configure the file applet.props in the current working directory. applet.props is a Java properties file that specifies the applet's parameters (codebase, code, archive, etc.). If you want, include a toolbar for invoking the applet's __stop__, __init__, and __destroy__ methods.

AppletBrowser takes the following command-line arguments:

  • -w writes out a sample applet.props file

  • -t runs the browser; includes a toolbar

  • -v runs the browser in verbose mode

  • -r runs the browser

  • -? shows help text

  • -prop writes a single parameter to the property file (-propNAME:VALUE)

  • -HTML converts applet.props to an HTML page (-HTMLFileName)

To use the browser in a new directory, you have to execute it with the w option. This creates an applet.props file with a code base that corresponds to the current working directory.

Here's the code to create an HTML file called AddressBook.html:

C:\jython_book\scripts\chap18\AddressBook>jython AppletBrowser.      py HTMLAddressBook

Here's AddressBook.html's code:

<html> <body> <center> <applet     code=com.awl.jython_book.MyApplet     codebase=.\jpywork     width=400     height=400     archive=..\..\lib\jython.jar > <param name="color" value="blue"> <param name="hello" value="hi mom"> If you don't have a Java-enabled browser, you need to get one! </applet></center> </body> </html>

Here's how to specify additional applet parameters:

C:\jython_book\scripts\chap18\AddressBook>jython AppletBrowser.      py-propFileName:RicksAddrBook

Once you add a parameter, it will be available the next time you create an HTML file or use AppletBrowser.

Advanced Topic: AppletContext and AppletStub

When an applet is created, it's given a stub via its setStub() method. The stub is the interface between the applet and the browser. You don't have to deal directly with the stub because the Applet class implements methods that use it.

Here are AppletStub's methods:

  • appletResize(width, height) resizes the applet

  • getAppletContext() gets the applet's context

  • getCodeBase() gets the code base URL

  • getDocumentBase() gets the document base URL

  • getParameterName() gets an applet parameter

  • isActive() determines if the applet is active

The applet context corresponds to the applet's environment and it can be used to show status, to load other documents, and to get access to other applets. The AppletContext's methods are

  • getApplet(name) gets the applet corresponding to a given name

  • getApplets() gets a collection of specific applets

  • getImage(url) returns an image object corresponding to the specified URL

  • showDocument(url) shows a Web page other than the current page and replaces the current page with it

  • showDocument(url, target) same as above, but specifies the frame target

  • showStatus(status) shows the status text in the browser

AppletBrowser subclasses AppletContext and AppletStub via the classes URL and URLClassLoader.

AppletBrowser.py

Here's some of the code for AppletBrowser.py:

from java.applet import AppletContext, AppletStub from javax.swing import JFrame, JButton, JPanel, JLabel from javax.swing import JTextField, ImageIcon ... ...

When you use the addApplet() method, AppletBrowser passes a context and calls the applet's __init__ method.

def addApplet(self, applet):      ...      ...      # Set the applet as the applet in this instance.      # Pass this instance as the AppletStub (stub).      # Add this applet to the frame. Then pack this frame.      self.applet = applet      if debug:            print "Set the stub, and add the applet to the container"      applet.setStub(self)      self.contentPane.add(self.applet, BorderLayout.CENTER)      ...      applet.init()      applet.start()      self.pack()

As you can see, AppletBrowser passes itself as the stub and in this way implements all of AppletStub's parameters.

... ... def appletResize(self, width, height):                 self.setSize(width, height)                 self.doLayout() def getAppletContext(self):                 return self def isActive(self):                 return 1 def getCodeBase(self):                 codebase=self.props.getProperty("codebase")                 if not codebase:                         return URL(self.docbase, ".")                 else:                         return URL(self.docbase, codebase) def getDocumentBase(self):                 return self.docbase def getParameter(self, name):                return self.props.getProperty(name)

getAppleContext() returns self, that is, the AppletBrowser instance, which means that AppletBrowser implements AppletContext's methods.

... ... def getApplet(self, name):        return self.applet #Should return an applet def getApplets(self):        v = Vector()        v.add(self.applet)        return v.elements() def getImage(self, url):        icon = ImageIcon(url)        return icon.getImage() def showDocument(self, url):        pass def showDocument(self, url, target):        pass def showStatus(self, status):        self.status.text = status

To implement getImage(), AppletBrowser uses javax.swing.ImageIcon. The getApplet() and getApplets() methods return the currently loaded applet; the showStatus() method shows the status in a text field called status.

AppletBrowser loads the applet specified by the code parameter, as well as all of the jar files specified in the archive tag, into the URLClassLoader class.

     ...      ... def loadApplet(self):              # Create the class loader and get              # the class name.      class_loader=self.createClassLoader()      class_name = self.props.getProperty("code")           # Load the class, create an instance           # of it, and add it with addApplet.      if class_name and class_loader:           try:              if debug: print "Loading: "+ class_name                     clazz=class_loader.loadClass(class_name)              if debug: print clazz                     applet = clazz.newInstance()              self.addApplet(applet)           except Exception, exception:              global error              error = exception              print "Unable to load the class "+class_name def getJars(self):      urls=[]           #Parse the list of jarfiles in the archive tag.      jarfiles = self.props.getProperty("archive")      jarfiles = split(jarfiles, ",")           # Iterate through the list of jarfiles, and add           # each jarfile as a URL to the urls list.      for jar in jarfiles:      url = URL(self.docbase, jar)      urls.append(url)           if debug: print "Adding jar file " + `url`      return urls def createClassLoader(self):      try:                     # Get list of URLS for each jar in the                     # archive tag. Get the codebase URL, and                     # add it to the list of URLS.                     # Create a URL jarray to pass to the                     # URLClassLoader constructor.                     # Get the system class loader to pass to the                     # URLClassLoader constructor.                     # Create an instance of URLClassLoader                     # passing the urls and the system class                     # loader.              urls = self.getJars()           codebase=self.getCodeBase()           urls.append(codebase)           urls = array(urls, URL)           loader = ClassLoader.getSystemClassLoader()           url_loader = URLClassLoader(urls, loader)           return url_loader      except Exception, exception:      global error           error = exception           print "Unable to create class loader"

Summary

In this chapter, we covered applets, which are components that execute within a browser. To run Jython code as an applet, you have to compile it to a Java class. You should also put it in a jar file to make downloading easier.

We extended our address book application to work as an applet. We also covered the inner workings of AppletBrowser, which creates Jython applets through a context (AppletContext) and a stub (AppletStub).

CONTENTS


Python Programming with the JavaT Class Libraries. A Tutorial for Building Web and Enterprise Applications with Jython
Python Programming with the Javaв„ў Class Libraries: A Tutorial for Building Web and Enterprise Applications with Jython
ISBN: 0201616165
EAN: 2147483647
Year: 2001
Pages: 25

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net