Another JDK tool that
can be
replicated is the AppletViewer. This uses the reflection
package to load a class that is subclassed from
Applet
, instantiate an instance of it, and
add( )
this to a frame at a given size. This is a
good example of reflection in action: you can use these techniques to
dynamically load any subclass of a given class. Suppose we have a
simple applet like the
HelloApplet
in Example 25-11.
Example 25-11. HelloApplet.java
import java.applet.*; import java.awt.*; import javax.swing.*; import java.awt.event.*; /** * HelloApplet is a simple applet that toggles a message * when you click on a Draw button. */ public class HelloApplet extends JApplet { /** The flag which controls drawing the message. */ protected boolean requested; /** init( ) is an Applet method called by the browser to initialize */ public void init( ) { JButton b; requested = false; Container cp = (Container)getContentPane( ); cp.setLayout(new FlowLayout( )); cp.add(b = new JButton("Draw/Don't Draw")); b.addActionListener(new ActionListener( ) { /* Button - toggle the state of the "requested" flag, to draw or * not to draw. */ public void actionPerformed(ActionEvent e) { String arg = e.getActionCommand( ); // Invert the state of the draw request. requested = !requested; do_the_work( ); } }); } /** paint( ) is an AWT Component method, called when the * component needs to be painted. */ public void do_the_work( ) { /* If the Draw button is selected, draw something */ if (requested) { showStatus("Welcome to Java!"); } else { showStatus(""); // retract welcome? :-) } } }
If we run it in my AppletViewer,[58] it shows up as a window with just the Draw button showing; if you press the button an odd number of times, the screen shows the welcome label (Figure 25-2).
Example 25-12 is the code for the main part
of the
AppletViewer, which
creates a JFrame
, and then loads the
Applet
class dynamically and adds it to the
JFrame
.
Example 25-12. AppletViewer.java main program
import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.applet.*; import java.lang.reflect.*; import java.net.*; import java.util.*; /* * AppletViewer - a simple Applet Viewer program. */ public class AppletViewer { /** The main Frame of this program */ JFrame f; /** The AppletAdapter (gives AppletStub, AppletContext, showStatus) */ static AppletAdapter aa = null; /** The name of the Applet subclass */ String appName = null; /** The Class for the actual applet type */ Class ac = null; /** The Applet instance we are running, or null. Can not be a JApplet * until all the entire world is converted to JApplet. */ Applet ai = null; /** The width of the Applet */ final int WIDTH = 250; /** The height of the Applet */ final int HEIGHT = 200; /** Main is where it all starts. * Construct the GUI. Load the Applet. Start it running. */ public static void main(String[] av) { new AppletViewer(av.length==0?"HelloApplet":av[0]); } /** Construct the GUI for an Applet Viewer */ AppletViewer(String appName) { super( ); this.appName = appName; f = new JFrame("AppletViewer"); f.addWindowListener(new WindowAdapter( ) { public void windowClosing(WindowEvent e) { f.setVisible(false); f.dispose( ); System.exit(0); } }); Container cp = f.getContentPane( ); cp.setLayout(new BorderLayout( )); // Instantiate the AppletAdapter which gives us // AppletStub and AppletContext. if (aa == null) aa = new AppletAdapter( ); // The AppletAdapter also gives us showStatus. // Therefore, must add( ) it very early on, since the Applet's // Constructor or its init() may use showStatus( ) cp.add(BorderLayout.SOUTH, aa); showStatus("Loading Applet " + appName); loadApplet(appName , WIDTH, HEIGHT); // sets ac and ai if (ai == null) return; // Now right away, tell the Applet how to find showStatus et al. ai.setStub(aa); // Connect the Applet to the Frame. cp.add(BorderLayout.CENTER, ai); Dimension d = ai.getSize( ); d.height += aa.getSize( ).height; f.setSize(d); f.setVisible(true); // make the Frame and all in it appear showStatus("Applet " + appName + " loaded"); // Here we pretend to be a browser! ai.init( ); ai.start( ); } /* * Load the Applet into memory. Should do caching. */ void loadApplet(String appletName, int w, int h) { // appletName = ... extract from the HTML CODE= somehow ...; // width = ditto // height = ditto try { // get a Class object for the Applet subclass ac = Class.forName(appletName); // Construct an instance (as if using no-argument constructor) ai = (Applet) ac.newInstance( ); } catch(ClassNotFoundException e) { showStatus("Applet subclass " + appletName + " did not load"); return; } catch (Exception e ){ showStatus("Applet " + appletName + " did not instantiate"); return; } ai.setSize(w, h); } public void showStatus(String s) { aa.getAppletContext( ).showStatus(s); } }
For Applet
methods to work, two
additional
classes
must be defined: AppletStub
and
AppletContext
. The AppletStub
is the tie-in between the applet and the
browser, and the
AppletContext
is a set of methods used by the
applet. Although in a real browser they are probably implemented
separately, I have combined them into one class (see Example 25-13). Note that the scope of
applets that will work without
throwing exceptions is rather limited, since so many of the methods
here are at present dummied out. This
AppletViewer is not a full
replacement for Sun’s AppletViewer; it has only been tested
with a basic Hello World applet, and is simply provided as a starting
point for those who want to fill in the gaps and make a full-blown
applet viewer program.
Example 25-13. AppletAdapter.java, partial AppletStub and AppletContext
import java.awt.*; import java.awt.event.*; import java.applet.*; import java.net.*; import java.util.*; /* * AppletAdaptor: partial implementation of AppletStub and AppletContext. * * This code is far from finished, as you will see. * */ public class AppletAdapter extends Panel implements AppletStub, AppletContext { /** The status window at the bottom */ Label status = null; /** Construct the GUI for an Applet Status window */ AppletAdapter( ) { super( ); // Must do this very early on, since the Applet's // Constructor or its init() may use showStatus( ) add(status = new Label( )); // Give "status" the full width status.setSize(getSize().width, status.getSize( ).height); showStatus("AppletAdapter constructed"); // now it can be said } /****************** AppletStub ***********************/ /** Called when the applet wants to be resized. */ public void appletResize(int w, int h) { // applet.setSize(w, h); } /** Gets a reference to the applet's context. */ public AppletContext getAppletContext( ) { return this; } /** Gets the base URL. */ public URL getCodeBase( ) { return getClass( ).getResource("."); } /** Gets the document URL. */ public URL getDocumentBase( ) { return getClass( ).getResource("."); } /** Returns the value of the named parameter in the HTML tag. */ public String getParameter(String name) { String value = null; return value; } /** Determines if the applet is active. */ public boolean isActive( ) { return true; } /************************ AppletContext ************************/ /** Finds and returns the applet with the given name. */ public Applet getApplet(String an) { return null; } /** Finds all the applets in the document */ public Enumeration getApplets( ) { class AppletLister implements Enumeration { public boolean hasMoreElements( ) { return false; } public Object nextElement( ) { return null; } } return new AppletLister( ); } /** Create an audio clip for the given URL of a .au file */ public AudioClip getAudioClip(URL u) { return null; } /** Look up and create an Image object that can be paint( )ed */ public Image getImage(URL u) { return null; } /** Request to overlay the current page with a new one - ignored */ public void showDocument(URL u) { } /** as above but with a Frame target */ public void showDocument(URL u, String frame) { } /** Called by the Applet to display a message in the bottom line */ public void showStatus(String msg) { if (msg == null) msg = ""; status.setText(msg); } }
It is left as an exercise for the reader to implement
getImage( )
and other methods in terms
of
other recipes
used in this
book.
We have not investigated all the ins and outs of reflection or the
ClassLoader
mechanism, but I hope I’ve given
you
a basic
idea of how it works.
Perhaps the most important omissions are
SecurityManager
and
ProtectionDomain
. Only
one
SecurityManager
can be installed in a given
instance of JVM (e.g., to prevent a malicious
applet from providing its own!). A browser, for example, provides a
SecurityManager
that is far more restrictive than
the standard one. Writing such a SecurityManager
is left as an exercise for the reader, but an important exercise for
anyone planning to load classes over the Internet! (For more
information about security managers and the Java Security APIs, see
O’Reilly’s Java Security, by Scott
Oaks.) A ProtectionDomain
can be provided with a
ClassLoader
to specify all the permissions needed
for the class to run.
I’ve also left unexplored some other topics in the JVM; see the O’Reilly books The Java Virtual Machine and The Java Language, or Sun’s JVM Specification document (http://java.sun.com/docs/books/vmspec/) for a lifetime of reading enjoyment and edification!
The Byte Code Engineering Library (BCEL) is a third-party toolkit for building and manipulating class files. BCEL was written by Markus Dahm and is available for free from http://bcel.sourceforge.net. Source code is included.
[58] My AppletViewer
doesn’t parse the HTML as the real one does, so you invoke it
with just the name of the Applet
subclass on its
command line. The size is therefore hardcoded, at least until
somebody gets around to writing code to extract the CLASS, WIDTH, and
HEIGHT attributes from the APPLET tag in the HTML page like the real
McCoy does.
3.15.34.39