|
Last modified: 1 November 1996. |
Reconstructed. Approx 4600 words, including code snippets.
Referenced pages: sidebars/figures in article.
Text in <CODE> tags are class names, while those in <TT> tags are methods.
When published, HTML links in the text can be removed.
This article is a collection of useful - some important - tips for using Java picked up when writing my HelpIndex applet for use in World Wide Web browsers. I assume some basic familiarity with Java.
When writing an applet from scratch, you will want to decide which base classes to use, how best to use the windowing classes AWT, what techniques are appropriate when programming an applet and the differences between the main PC browsers. Sidebars discuss how to do anything useful, Java pros and cons, APIs and IDEs.
PHD's HelpIndex applet reads a list of index keywords (and their links to web pages) from the host. Like various help systems, the user can type in the first few letters of their search string. The matching indices and their pages are listed automatically. The selected page can be displayed. The companion MakeHelpIndex Java application lets you build a help index file from your existing web pages.
Figure 1 shows a screen shot showing that the user has typed in J and all the matching keyword indices have been found. Java applet has been selected and various matching pages have been found. HelpIndex Java Applet is selected, so pressing Display Page will show this page. A standard server based search for Java on the PHD site threw up over 200 hits; it would be difficult to know which page to go to.
Figure 1.
Incidentally, one good use of HelpIndex is to provide an index for all the fields and methods of an API. The standard JDK javadoc program is used to produce web page documentation for an API from suitable source code comments. One of its standard outputs is an alphabetical listing of all fields and methods. If you want to look up the getAddress() method, then you have to click on the G short-cut link at the top of the page, then scroll down through all the get() methods until finally you get to where you want. PHD HelpIndex allows you simply to type in get; then the get() and getAddress() methods are listed as indices - much easier.
If something goes wrong remember that Java is a young language and its API classes are relatively new too. So, always remember to check Sun's known bugs at http://java.sun.com/products/JDK/CurrentRelease/KnownBugs.html and the FAQs (eg http://www.www-net.com/java/faq/) before resorting to the comp.lang.java.* newsgroups, etc. For other resources, check out Gamelan in USA and the UK's Java Centre. Familiarise yourself with the language by looking at the examples and writing your own simple applications. See the sidebar for some Java pros and cons.
Getting an applet to do anything useful is not quite as simple as it may seem as browser security is so tight. See the sidebar for the range of possible options.
For a new development you will have to decide what technology to use. HelpIndex is based on the original 1.0x Java Development Kit (JDK) from Sun. However that soon will have an improved 1.1 version. Then many suppliers have alternatives, which usually build on top of the JDK, mainly to improve the AWT windowing classes. For example, some may choose Netscape's Internet Foundation Classes as the baseline for new development soon (see its separate review) . See the sidebar for a full discussion of APIs.
However you must ask which people out there will have all these latest classes. Unless Netscape's IFC, for example, is built into all browsers, particularly Microsoft's Internet Explorer (IE3.0), then you will be restricting yourself to Netscape Navigator 4 users. No one will want to download the 500kB classes on-line. For intranet applications you can, of course, ensure that your chosen extension classes are available on all user computers.
You will also have to work out how best to test your new applet. As a minimum I suggest that you that you try it out using IE3.0 and N3.0 browsers at a couple of screen resolutions. Ideally you should try other platforms and earlier browser versions.
Be warned that Java runs s-l-o-w-l-y.
Sidebar Doing anything usefulJava's security is so strict that you have a job inventing something useful to do - I mean: solving that business problem. Applets cannot read or write local files and can only access the server they came from. You can see why most applet demos are just pretty animations. In future trusted applets may access other servers. To be useful, you have to write a Java application (which runs outside a browser) or have your applet host do something clever in a CGI or ISAPI program, which could be a server-side Java application. So big projects will inevitably be client-server in nature. Alternatively you can use a server's databases using JDBC (like ODBC). There are already quite a few ways of accessing databases live over the Internet or Intranet, and I am sure JDBC and other Java APIs will feature in each vendor's armoury of tools. Then there are some forms of object RPC. Remote Method Invocation (RMI) uses RPC to invoke class methods on servers. PHD's HelpIndex applet takes another approach to do its relatively simple task. The content developer runs a (Java) program off-line to create some information; the file is then uploaded. The applet can read the file and do some useful work based on its contents. There is currently no support for printing from Java. |
As a C/C++ person, I am used to tracking allocated memory carefully. All this goes out the window with Java, as you allocate willy-nilly, relying on Java to garbage collect. Let's hope it does it efficiently.
I was fooled a bit at first by object variables. Now I tend to think of them as pointers (technically "references"). For instance, I wanted to make a bit of room around one of my screen components. To do this, I tried to override the insets() method as follows:
public Insets insets() { Insets ri = super.insets(); ri.left += 5; ri.right += 5; return ri; }
The problem is that my ri Insets Object
in effect points to
the insets in the parent component, so my changes altered the parent's insets.
This causes a problem as the insets() routine can be called many
times. A better solution is to create your own new Insets
instance to return.
public Insets insets() { Insets ri = super.insets(); return new Insets(ri.top,ri.bottom,ri.left+5,ri.right+5); }
Sidebar: Java pros and cons Java NicenessIt is good that characters are 16 bit UNICODE unsigned integers. There is no clumsy mechanism for saying whether you want 8 or 16 bit characters. So, there is just one set of string routines to use. UNICODE forces you to think about how best to write a character out as an 8 bit byte, if that is what you really want to do. Similarly it is nice to have a boolean basic type. Ditto for 64 bit longs, even though I have not used them yet. When building a string using the + operator, the compiler nicely converts
the basic types and classes to their string representation. Classes are all
objects, and an While on the subject, also implement getAppletInfo() and getParameterInfo() for your applet. Thankfully there is no multiple inheritance to cope with. Instead an
Java ClumsinessSome aspects of Java appear clumsy. Compilers produce one or more .class output files from a .java source. Java
insists that each compiled class or interface exists as a separate output file.
This is just plain silly - imagine if the MFC DLLs were distributed in this way.
More importantly this has serious speed implications for applets. A browser
has to put in a separate request for each class file. Luckily JDK 1.1
introduces Java Archive (JAR) libraries which contain several compressed files
of any type which a
A constant is usually declared as public static final int CONST = 0 or similar, which is a bit of a mouthful. If you have constants you want to share across projects, you cannot simply include a header file. Constants must be declared in a class, so you must import the class; I presume that the class must be loaded at runtime for these constants to be visible. In contrast to the automatic conversion to strings mentioned above, String s = i does not work. String s = ""+i does. However you are supposed to use String s = new Integer(i).toString(). If you had not guessed it, the Initialising simple structures is difficult, If you had a colour lookup
class:
Finally, my early forays indicated that not all Java Virtual Machines were equal, or at least not all Java implementations. Later we shall see that different PC browsers behave in slightly different ways. |
Sidebar: APIs API PrimerA language or operating system is usually seen in terms of the libraries or classes that come as standard. Originally C had printf() and the like. C++ had its streams (which I never used). UNIX had exec(), etc., in 1000+ combinations. PC BIOS had INT 10 to 1F, and DOS INT 21. Macs' initial success partly came about as third party programmers had to use standard user interface routines. For the same reason, the Window 16 bit API became the standard PC programmers had to know. Now there are a gazillion Windows API you could know, from MFC downwards. Similarly, the Internet's success must partly be due to its set of standards, many of them quite simple. Now, rival suppliers are trying to outdo each other, not least with acronyms, to provide easy (huh!) ways to lock in, sorry support users and write programs or content. You could spend you entire programming life keeping up with what is going on, without earning a penny. Anyway how does Java fare in this minefield? In particular what are the baseline class libraries like? JDK, etc.Here are the main JDK API packages:
java.lang has elements which are essentially part of the language,
eg the The java.awt Abstract Window Toolkit (AWT) classes are discussed below. The java.net network classes implement TCP/IP sockets. With the
java.io covers files, various sorts of streams and pipes. For Java applications, there no support for serial ports, etc. The JDK includes a command line compiler and debugger. The new JDK is trying to catch up on other fronts, eg with digital signing and internationalisation. Generally, the Java language and the core JDK are still in their youth. They have not settled down, which will hinder Java's adoption, or at least annoy its practitioners. As there is lots of functionality not provided, there is plenty of scope for projects... java.awt should have been the place to establish a new set of user interface rules. Although its idea of layouts - to cope with different size screens - is good, the rest of the toolkit only just provides enough workable functionality. Now lots of people are trying to do the same job - which Sun should have done in the first place - leaving the developer and user alike in a sea of differing window kits. Sun's Other APIs Sun have a list of their current and proposed Java APIs at http://java.sun.com/products/apiOverview.html. As mentioned before, JDBC provides low-level database access, akin to ODBC. IDL and RMI deal with running objects on other platforms; soon to be supported by a Netscape plug-in. Interestingly, there is a new concept of 'servlets' which users can upload to run on a server. Other APIs cover security, secure financial transactions, network management, multi-media players, application data sharing, 2D and 3D objects and telephony. Java Beans is their contribution to the confusing overcrowded world of software components. Netscape APIs Netscape is the main other Java player of interest at the moment. It has a DevEdge developer support program, sold in two species. Netscape push their own Open Network Environment (ONE) for Java and Javascript, including software development kits. LiveConnect is their way of tying together browser and server objects. Trusted applets may be able to access other hosts and local files. Their servers can already run Java applications. So there is scope for writing server agents that act on a user's behalf. Netscape's Internet Foundation Classes (IFC) add several areas of functionality. Its windowing system sits on top of java.awt. As reviewed elsewhere, there are more user interface controls, simple animation, drag and drop, timers, multi-font text and a persistent store or object archive system. Netscape has licensed Visigenic's VisiBroker for Java ORB. Others Strangely enough, things have been quite quiet from that notorious API generator, Microsoft (apart from cabinets). Their Visual J++ IDE have classes to talk to COM objects, including DAO controls, and applets can be made into ActiveX objects. As IBM seems to be adopting Java, they are sure to contribute APIs to the language's repertoire. |
The JDK Abstract Window Toolkit (AWT) feels limited. It is nowhere near a document and view architecture. It has events and graphics contexts, along with forms and controls.
Despite improvements promised in JDK 1.1, the limitations are such that alternative windowing systems have emerged from other development tool suppliers. These are mostly built on top of AWT. No doubt each one will have its own resource format, eg Java Workshop has a .gui resource file. It is a shame that there cannot be one good system. java.awt will always be there, so it is best to get to know it. So, HelpIndex was based on AWT.
AWT does well to address the problem of each user having a different
display. Its classes lay out Component
s (fields, etc.) in Container
s
(windows/dialog box forms). You are not supposed to give absolute locations,
but tell one of the Layout classes the preferred size and order, and let it get
on with the task of laying out the window.
There is some sort of event model, though I have found no event loop. There
is no resource format and no printing. There is no tabbing between fields and
no default buttons. HelpIndex uses its GridBagForm
class to get
round these last two problems.
There are standard ways of avoiding flicker in windows. update() makes an off-screen bitmap graphics context for paint() to use. update() then blasts the image onto the screen quickly. HelpIndex does not need to use this technique as it does not normally do any painting; it relies on AWT components which draw themselves, laid out automatically.
Event Handling
There are 30+ events, coming from user interactions with components.
Events are first passed to handleEvent(). Either handle them here and return true, or return super.handleEvent(). If super.handleEvent() is not called then the event is ignored by the actual component, eg the key press is ignored.
super.handleEvent() passes the event to the appropriate delegated
applet event handler, then to Component
. action() for
button presses and menu selections, or one of these appropriately named
routines:
keyDown() keyUp() mouseEnter() mouseExit() mouseMove() mouseDown()
mouseDrag() mouseUp() lostFocus() gotFocus(). You should return false from
keyDown(), etc. if you want a component, eg a TextField
,
to receive the key.
Notice that you can either handle an event in handleEvent() or the appropriate delegated handler, eg keyDown().
I found for key events that the underlying component had not processed the
event when you get to look at it. Eg when a key is pressed, if you get the
TextField's text in keyDown() then the new character is not there. So
to process a key (or succession of keys) HelpIndex gets the processed text in
keyUp(), which is slightly naff. You could use keyDown() as
a key filter, eg to implement a number only class derived from TextField
.
Component Ids
Having received an event, how do you know which object it has come from?
The simplest technique is to have an applet variable for each component you create and see if the event target matches that component. Alternatively, the poor JDK examples actually rely on matching a button's text, eg if text is "OK" then do this. Change the button text and change your code; yuck!
A better technique (methinks) is to have an integer id for each component of
your form. HelpIndex's GridBagForm
class - see code box - provides
this facility wrapped round the
GridBagLayout
class. GridBagForm.getIdFromComponent()
and
GridBagForm.getComponentFromId() are used in event handling.
GridBagForm
also has methods to handle tabbing between components,
and to remember the current field when you mouse click to a new tab field.
Obviously you could subclass each component, eg MyButton derived from
Button
and have an id for each button.
As mentioned above, currently it is advisable to cut down the number of classes that you use in applets - to reduce load time. This definitely goes against the grain, as it tends to make programs less understandable. OO enthusiasts will be turning in their swivel chairs, objecting.
Even a simple structure must be a class. If you want a simple class, it is difficult to initialise, as the ColourLookup example class showed earlier. The messier array-of-objects solution avoids generating a new class.
A new exception means a whole new class, so reuse existing exceptions.
Here is a sneaky way to avoid having an extra class in some situations.
Have the static class represent a collection of instances of the class. For
instance, I use this technique in GridBagForm
where each instance
is an individual form component, while the static class is used to manipulate
the whole form. Obviously you could only have one such collection class in your
program. This technique goes wrong with applets if there are multiple instances
of an applet, as the static class will be the same for each applet instance.
Figure 2 shows how GridBagForm
has a static Vector
called itemList. The elements of this vector are each GridBagForm
instances which store information about each component on the form.
Figure 2.
Another trick is to avoid having a class extending Thread
. Get
another class (eg your applet) to implement Runnable and do your
thread stuff in run().
You have to become familiar with a new programming paradigm. It used to be a cardinal sin to put up any sort of child window, even a message box, since these windows could lock up your computer. Even now it is best to try to work within your given bounds. HelpIndex now has two modes. You can either view its search form set within a page, "applet" mode. Or more usefully, you can have it as an icon initially; when you click the icon, the search form appears in a "floating child window" above the browser.
To make a child window you will need to make a Frame
. You
need not make it visible if you just want to then make a Dialog
.
You have to construct dialog boxes by hand. You can either have a class derived
from
Dialog
, or just use Dialog
in your frame, and handle
all the events in the frame. Dialogs can be modal (with respect to their frame,
but not the browser). Only child frame windows can have menu bars, ie applets
cannot.
HelpIndex uses its HelpIndexForm
class to cope with the two
modes. The HelpIndexForm
extends the JDK Panel
class and contains all the form components. In applet mode, this form is simply
added to the applet container. In floating window mode, when the user clicks
the icon, a new HelpIndexFrame
is first created and then the form
is added to it, as shown in figure 3. This approach allows all the user
interaction to be handled in HelpIndexForm
whether in applet or
window mode.
Figure 3.
The following simplified code shows the floating window is created. You can get the preferredSize() for the frame. It is not clear whether the preferred size is supposed to include the borders, but you do need to add something; so add the frame's insets, ie the size of its borders. For IE3.0 you have to do the resize() after the show().
ourFrame = new HelpIndexFrame(this,FrameTitle); ourForm = new HelpIndexForm(this); ourFrame.add("Center",ourForm); ourFrame.show(); Dimension frameSize = ourFrame.preferredSize(); Insets frameInsets = ourFrame.insets(); ourFrame.resize(frameSize.width+frameInsets.left+frameInsets.right, frameSize.height+frameInsets.top+frameInsets.bottom);
If working with your own visible frame, then you will need to derive your
own class from Frame
, at the very least to catch Event.WINDOW_DESTROY
messages. HelpIndex has a small derived class HelpIndexFrame
to
do this. HelpIndexFrame
also sets up the menus in its constructor.
Applets cannot resize themselves or be resized.
If you run an applet twice on the same page (or in different previous pages) then the static classes members are shared betwen the two instances. So be very very careful with you static variables. Note that this clobbers the class reduction technique described above where the static class has a per-applet meaning.
It is worth while checking that your applet works with both common PC browsers N3.0 amd IE3.0, which do have some significant differences.
If you are developing an applet, then you usually need to stop and restart a browser if you want to test any changes. This is a bit of a bore. Changes in applet parameters generally are not visible without a restart. Setting the disk and memory cache size to zero solves this problem at the expense of performance.
IE3.0's Java implementation seems slightly behind N3.0.
In N3.0, you can set the foreground colour of Label
components.
Perhaps more importantly, only N3.0 generates the MOUSE_ENTER, MOUSE_EXIT, GOT_FOCUS and LOST_FOCUS events.
In IE3.0, if you make a Java call to show a web page, where the URL is a local file with an anchor name, then the page is not displayed.
IE3.0 includes a font "ZapfDingbats" which has lots of useful symbols.
How do browsers start and stop applets, eg when its page is selected or when the user moves to another page?
First, it needs to be said that applets seem to keep running when the user moves to another page, which is decidedly poor in my opinion. I think they try to keep the input focus if they have it. At the very least, I would have expected that an applet's threads are suspended.
All classes can have static initialisation and finalize() destructor methods. In addition, applets have init(), destroy(), start() and stop() methods.
When first run, an applet's static initialisation is called, a new instance
of the Applet
is created, init() is called, then
start(). Fine.
If you leave the web page then stop() is called. If you then go back then the applet is still there and start() is called. So start() might be the appropriate place to put the input focus in the desired initial place. You might deem it necessary for all initialisation to happen in start().
In N3.0 but not IE3.0, if you reselect a link to the web page (as opposed to
just going back to it) then the applet is sort-of-restarted. The static
initialisation is not called, but a new Applet
is created,
init() and start() are called. All the applet's components
need to be rebuilt. So be very careful to make sure that your init() handles
this case as you would like. To be completely safe, you might want to
initialise all your variables and other classes in init().
static initialisation | new Applet | init() | start() | |
---|---|---|---|---|
First visit | x | x | x | x |
Go back | x | |||
Second visit (N3.0 only) | x | x | x |
destroy() is supposedly only ever when the browser is shut down. I have not confirmed this yet.
In applets, use threads to speed up the response time for users.
If there is a long task at start-up, then do it in a thread started in
init(). Returning from init() lets the applet show itself.
Obviously let the user know that some work is in progress.
Make sure you
avoid problems that might occur if init() is called more than once, as
explained above.
HelpIndex's init() starts another thread to read its help index from the server, which may take several seconds. A status message is displayed until the index has been read. The user can type in the applet's fields, but a lookup does not occur until the index has arrived.
HelpIndex also uses threads to do the lookup task, as this may take a second or so for large indices. The task is carefully designed so that it can be stopped and restarted when another key is pressed. Note that you should use the synchronized keyword to schedule operations. As well as synchronized methods you can have synchronized blocks, locking the class instance, as this simplified example from HelpIndex shows:
private Thread TaskThread = null; private void DoLookup() { synchronized(this) { if( TaskThread!=null) TaskThread.stop(); TaskThread = new Thread(this); TaskThread.start(); } } public void run() { LookupIndices(QueryField.getText()); synchronized(this) { TaskThread = null; } }
HelpIndex's keyUp() routine simply calls DoLookup(). If there is a search in progress, then it is stopped and a fresh one started. The actual search is done in run(), as the applet implements Runnable to do the thread's work.
There is lots to get right in a fully functional Java program. Both the language and its APIs are new.
When your applet is complete, there are various places where you can get it registered to make it easy for others to find. Apart from Gamelan and the Java Centre, JARS rates applets. HelpIndex got in the top 25% for October 1996.
sidebar Development EnvironmentsEveryone and their dog are writing a Java development environment. Masochists can just get by with the command line tools provided with the JDK. Currently, I am one step up, using Symantec Café Lite (sic), on a CD-ROM from one the SunSoft books. I have looked at Sun's Java Workshop 1.0. This has potential but I found it
unusable, mostly for small but important things. It's also slow. I have not tried Microsoft's J++ (why is that ++ there?), Borland's Latté, Symantec Café or Aimtech's Jamba, not to mention IBM's Visual Age for Java, Scientific Computers' BX-Pro or Software Technologies' X-Designer. Iona has an object request broker, Orbix Web 2.0 and Object Design have a lightweight persistent object store PSE Pro. |
© 1996 Chris Cant, PHD Computer Consultants Ltd
Chris Cant runs PHD Computer Consultants Ltd. You can try HelpIndex at http://www.phdcc.com/helpindex/index.html.