Java interfacing with Web Browser: Some Details
Last time we have looked at the methods provided by the AppletContext interface:
Today, we want to revisit these methods in some more detail. The easiest method is,
without doubt, the 'showStatus' method. The only thing to say about it is that your applet
is not the only one using this status line: the web browser itself may display
messages there, or other applets might use it. Thus, there is no guaranty that your
message will really be observed by the user, because it might be overridden by another
message any time. Thus, you should use this area only for informative messages, not for
critical ones. Critical messages, or messages that are essential to the user, could be
placed, for example, in a status line dedicated to your applet (which means, of course,
that you have to program for this feature).
The next easiest methods are the two 'showDocument' methods: they simply direct the
browser to load the indicated pages, if necessary into a target area (which could be
a new browser window, or a named frame if it already exists). More information about named
targets can be found, for example, in any 'new' book on HTML. The thing that concerns us
is the input parameter type URL. To create a URL, you need to invoke one of the URL
constructors. Each one throws an exception which need to be caught in the usual try-catch
block. In particular, two of the constructors are perhaps most useful (taken from the
Java API):
public URL(URL context, String spec) throws MalformedURLException
If the spec argument does not specify a protocol: |
If the context argument isn't null, then the protocol is copied from the context argument. | |
If the context argument is null, then a MalformedURLException is thrown. |
If the context argument is null, or specifies a different protocol than the specification argument, the context argument is ignored. |
The first constructor is useful to create an 'absolute' URL, i.e. an address which
specifies each part, including the protocol, the macine name, and the directory and file
names. The second constructor is useful to create 'relative' URL address, i.e.
addresses that work relative to the current location of the web page containing the
applet, or the machine where the applet originates. For example (try-catch blocks
omitted):
are all legal, absolute URL's. Each could be used for the showDocument method. The
first one would direct the browser to bring up Seton Hall's homepage, the second one would
invoke the mail method of the web browser to allow sending an email message to
'wachsmut@shu.edu', and the third one would tell the browser to open an
FTP connection to the machine named 'ftp.shu.edu' and login to that machine using the
username 'anonymous'. Finally, the last would invoke the telnet program that has been
setup with your web browser, connect to machine 'the.machine.edu' on port 23, and login as
JohnDoe. You will have to provide the password as usual for a telnet session.
The second constructor is perhaps most useful in conjuction with the following methods
that are part of the Applet class:
hNote that the URL of the applet does not have to be the same as the URL of the
document containing the applet. After all, the APPLET tag allows you to specify a
CODEBASE indicating the location of the applet. For example:
an HTML document has the URL address http://sciris.shu.edu/~wachsmut/test.html
and contains the following HTML code:
Then getCodeBase() would return "http://www.shu.edu/Java", while
getDocumentBase() would return "http://sciris.shu.edu/~wachsmut/".
Therefore, one could use a URL constructor as follows:
Finally, before we can discuss the remaining two methods for getting images or audio
clips, we need to mention some of the security restrictions for applets:
Security Restrictions for Applets
Since applets can be embedded in any web page, they could execute on your computer any
time you surf the web. Therefore, some security restrictions are placed on applets by the
web browser, to stop the applet from doing any harm to your computer. Some of these
restrictions are as follows:
an applet can not close down the web browser it is executing in, hence it can not execute the 'System.exit' method | |
an applet is prevented to write anything to the local disk of the computer it is executing on | |
an applet can only read documents located in 'web space' | |
an applet can only read from the machine where the applet came from | |
an applet can only connect to servers on the machine where the applet came from |
Note that these restrictions do not prevent an applet to instruct the browser to load
any web page, but it does restrict the applet from loading an image unless the image is
located on the same machine as the applet (but in possibly different directories).
Actually, that is not so bad, because if your applet relies on an image located elsewhere,
your program may not run any longer if the image is removed.
Standalone programs do not have these security restriction. Therefore, if you need to
create a program that saves data to a file, you have two alternatives:
write a standalone program in Java. Such programs can read and write to the local disk just as any other programs can | |
write an applet plus a standalone server. The applet can talk to the server (provided they are located on the same machine) and the server in turn (being a standalone program) can save the data to file. We will explore these details later |
Loading Images
Given the above security restrictions, a Java applet can load images in GIF or
JPG format using the 'getImage' method. In fact, that method is pretty slick: it
returns immediately, and - if the URL did refer to a valid image - the rest of the
program can go on without interruption. For example, if the method
executes, it does not wait until the image is completely retrieved from the URL. Instead, it simply starts loading the image and returns immediately, so that the next method 'setup()' can execute right away. That allows your applet to load the rest of its pieces, if necessary, and to start executing without having to wait for a long file coming from a potentially slow Internet connection. But, when you execute the paint method to draw the image, it may only draw an unfinished picture. Moreover, what if the paint method is drawn before the image has completed loading, but then is not called for a while even though the image is now complete ? To avoid that problem, the 'drawImage' method will inform the "ImageObserver" that the image is now finished, and the "ImageObserver" will then redraw the completed image. Recall the Java API description for the drawImage method:
public abstract boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer)
Thus, a typical call to the 'drawImage' method would be:
so that the 'current object' is notified when the image has loaded completely. However, sometimes you may want wait until the image is entirely loaded before continuing with the rest of the program. For example, when doing animation, you do want to make sure all images are entirely loaded first before starting the animation. Otherwise, the animation may look very strange with partically loaded images. For that purpose, Java provides a 'MediaTracker' class. Here's part of what the Java API says:
To use a MediaTracker, first instatiate one and tie it to the current component using the 'this' keyword. Then add all images that need to be loaded to the MediaTracker. The images still need to be loaded as usual, using 'getImage', and they will start loading 'in the background' immediately. But now the MediaTracker can inform you (or whatever component it was tied to) about the status of the loading process. You can, for example, use the checkAll() method to see if all images are done loading or not, or use the getErrorsAny() method to check for any errors that have occured. Using the 'waitForAll' or 'waitForID(int id)' methods will wait until either all images, or an image with a particular id, have completed loading. This is what's called a 'blocking' method, meaning that once calls it blocks execution of the rest of the program until, in this case, the indicated image(s) have finished loading. Examples:
import java.applet.*; import java.awt.*; import java.net.*; public class ShowImage extends Applet { private Image image; private URL location; private String imageName = "test.jpg"; public void init() { try { location = new URL(getDocumentBase(), imageName); } catch(Exception e) { System.err.println("Invalid URL for image"); } image = getImage(location); } public void paint(Graphics g) { g.drawImage(image,0,0,null); } }
Assuming the image "test.jpg" does exist in the same directory as the HTML file containing this applet, you can compile and run it just fine. But, nothing will appear on the screen until you resize the window. Explain why. Now change the paint method as follows:
public void paint(Graphics g) { g.drawImage(image,0,0,this); }
The image will now be drawn incrementally and (almost) without delay, especially if "test.jpg" is a large file. In other words, you will see more and more of the image, as it is being loaded, until finally the complete image appears. Now change the init method as follows, but keeping the second version of the paint method (using the this identifier).
public void init() { MediaTracker tracker = new MediaTracker(this); try { location = new URL(getDocumentBase(), imageName); image = getImage(location); tracker.addImage(image,0); tracker.waitForID(0); } catch(Exception e) { System.err.println("Invalid URL for image or loading error"); } }
This time there will be a delay (during which you should really use the showStatus message to keep the user informed), but then the image will appear all at once, not incrementally as before. Note that the MediaTracker's addImage and waitForID methods have been placed inside the try-catch block. Aside from the fact that that is really where they should go logically, the waitForID method also throws an Exception that must be caught. In the current implementation, one try-catch block catches both interruptions (from the URL constructor and the waitForID method). Loading Sounds Much of what has been said for loading images also applies to loading sounds. There are just a few differences, however:
Currently, sound support is tied to applets only. That means that stand-alone programs can not read or play a sound clip (unless they use an additional package available for free from SUN). Images can be used in applets and stand-alone programs. | |
Sounds can not be added to the current version of the MediaTracker. SUN will hopefully remedy that situation soon. The getAudioClip method, as the getImage method, will return immediately. It is somewhat unclear to me what exactly happens, when the sound starts loading, whether the sound can be played 'incrementally', or whether a 'play' method will block until the entire sound clip is downloaded, then start playing. Sound support seems to be not as well developed (and documented) as image support so far, sorry. | |
The only sound format currently supported is 'au' format from SUN. That means that WAV or Mac sounds need to be converted to SUN's 'au' format before they can be used by an applet. I have found that many sound converters promise to create true SUN au files, but often they were not acceptable to an applet. The best sound converter is a Unix program called 'sox'. It is fast, has no graphics interface (hence is fast) and converts pretty much anything into everything. |