by Laura Lemay
Here you are on the last day of the second week, and you're just about finished with applets and the awt. With the information you'll learn today you can create a wide variety of applets and applications using Java. Next week's lessons provide more of the advanced stuff that you'll need if you start doing really serious work in Java.
Today, to finish up this week, we'll cover three very different topics:
Today you'll finish up the last bits of the awt that didn't fit into yesterday's lesson. In addition to all the graphics, events, user interface, and layout mechanisms that the awt provides, it also provides windows, menus, and dialog boxes, enabling to you create fully featured applications either as part of your applet or independently for standalone Java applications.
The Java awt classes to produce windows and dialogs inherit from a single class: Window. The Window class, which itself inherits from Container (and is therefore a standard awt component), provides generic behavior for all window-like things. Generally you don't use instances of Window, however; you use instances of Frame or Dialog. Figure 14.1 shows the simple Window class hierarchy.
Figure 14.1 : The Window class hierarchy.
The Frame class provides a window with a title bar, close boxes, and other platform-specific window features. Frames also let you add menu bars. Dialog is a more limited form of Frame that typically doesn't have a title. FileDialog, a subclass of Dialog, provides a standard file-picker dialog box (usually only usable from inside Java applications because of security restrictions on applets).
When you want to add a new window or dialog to your applet or application, you'll create subclasses of the Frame and Dialog classes.
Frames are windows that are independent of an applet and of the browser that contains it-they are separate windows with their own titles, resize handles, close boxes, and menu bars. You can create frames for your own applets to produce windows, or you can use frames in Java applications to hold the contents of that application.
A frame is a platform-specific window with a title, a menu bar, close boxes, resize handles, and other window features.
To create a frame, use one of the following constructors:
Because frames inherit from Window, which inherits from Container, which inherits from Component, frames are created and used much in the same way that other awt components are created and used. Frames are containers, just like panels are, so you can add other components to them just as you would regular panels, using the add() method. The default layout for frames is BorderLayout. Here's a single example that creates a frame, sets its layout, and adds two buttons:
win = new Frame("My Cool Window"); win.setLayout(new BorderLayout(10, 20)); win.add("North", new Button("Start")); win.add("Center", new Button("Move"));
To set a size for the new frame, use the resize() method with the width and height of the new frame. So, for example, this line of code resizes the window to be 100 pixels wide and 200 pixels high:
win.resize(100, 200);
Note that because different systems have different ideas of what a pixel is and different resolutions for those pixels, it's difficult to create a window that is the "right" size for every platform. Windows that work fine for one may be way too large or too small for another. One way around this is to use the pack() method instead of resize(). The pack() method, which has no arguments, creates a window of the smallest possible size given the current sizes of all the components inside that window and the layout manager and insets in use. Here's an example that creates two buttons, and adds them to a window. The window will then be resized to the smallest possible window that can still hold those buttons:
win = new Frame("My Other Cool Window"); win.setLayout(new FlowLayout())); win.add("North", new Button("OK")); win.add("Center", new Button("Cancel")); win.pack();
When you initially create a window, it's invisible. You need to use the show() method to make the window appear onscreen (you can use hide() to hide it again):
win.show();
Note that when you pop up windows from inside applets, the browser may indicate in some way that the window is not a regular browser window-usually with a warning in the window itself. In Netscape, there's a yellow bar at the bottom of every window that says Untrusted Java Window. This warning is intended to let your users know that your window comes from the applet and not from the browser itself (remember that the frame class produces windows that look just like normal system windows). The warning is to prevent you from creating a malicious applet that might, for example, ask the user for his password. There isn't anything you can do to avoid this warning; it's there to stay as long as you want to use windows with applets.
Listings14.1 and 14.2 show examples of a simple applet with a pop-up window frame (both the applet and the window are shown in Figure 14.2). The applet has two buttons: one to show the window, and one to hide the window. The frame itself, created from a subclass I created called BaseFrame, contains a single label: This is a Window. You'll use this basic window and applet all through this section, so the more you understand what's going on here the easier it will be later.
Listing 14.1. A pop-up window.
1:import java.awt.*; 2: 3:public class PopupWindow extends java.applet.Applet { 4: Frame window; 5: 6: public void init() { 7: add(new Button("Open Window")); 8: add(new Button("Close Window")); 9: 10: window = new BaseFrame("A Popup Window"); 11: window.resize(150,150); 12: window.show(); 13: } 14: 15: public boolean action(Event evt, Object arg) { 16: if (evt.target instanceof Button) { 17: String label = (String)arg; 18: if (label.equals("Open Window")) { 19: if (!window.isShowing()) 20: window.show(); 21: } 22: else if (label.equals("Close Window")) { 23: if (window.isShowing()) 24: window.hide(); 25: } 26: return true; 27: } 28: else return false; 29: } 30:}
Listing 14.2. The BaseFrame class.
1:import java.awt.*; 2: 3:class BaseFrame extends Frame { 4: String message = "This is a Window"; 5: 6: BaseFrame1(String title) { 7: super(title); 8: setFont(new Font("Helvetica", Font.BOLD, 12)); 9: } 10: 11: public void paint(Graphics g) { 12: g.drawString(message, 20, 20); 13: } 14:}
There are two classes that make up this example: The first, PopupWindow, is the applet class that creates and controls the pop-up window. In the init() method for that class (lines 6 to 13), we added two control buttons to the applet to control the window, and then created, resized, and showed the window itself.
The control in this applet occurs when one of the buttons is pressed. Here, the Open Window button simply shows the window if it's hidden (lines 18 to 21), and hides it if it's showing (lines 22 to 25).
The window itself is a special kind of frame called BaseFrame. In this example, the frame is fairly simple; all it does is paint a text message near the top of the frame. Because frames are components, just like other components, you could have just as easily added a layout manager, buttons, text fields, and so on, to this frame.
You may have noticed, if you started up that pop-up window applet to play with it, that the new window's close box doesn't work. Nothing happens when you click the mouse on the box. To implement behavior for closing the window-for pop-up windows as in applets to hide them, or to exit the application altogether for applications-you'll have to use a handleEvent() method in your Frame class to test for the WINDOW_DESTROY event.
In the pop-up window example, choosing the close box should hide the window (call the hide() method). You can then show it again using the Open Window button in the applet. This is a very simple fix; just add the following handleEvent() to your BaseFrame1 class:
public boolean handleEvent(Event evt) { if (evt.id == Event.WINDOW_DESTROY) hide(); return super.handleEvent(evt); }
Each new window you create can have its own menu bar along the top of that window. Each menu bar can have a number of menus, and each menu, in turn, can have menu items. The awt provides classes for all these things called, respectively, MenuBar, Menu, and MenuItem. Figure 14.3 shows the menu classes.
Figure 14.3 : The awt menu classes.
Note that you can have menu bars and individual menus in Java only on components that have title bars-frames in pop-up windows from applets work just fine, as do Java application windows, but you cannot have a menu bar attached to an applet itself.
To create a menu bar for a given window, create a new instance of the class MenuBar:
MenuBar mbar = new MenuBar();
To set this menu bar as the default menu for the window, use the setMenuBar() method (defined in the Frame class):
window.setMenuBar(mbar);
Add individual menus (File, Edit, and so on) to the menu bar by creating them and then adding them to the menu bar using add():
Menu myMenu = new Menu("File"); mbar.add(myMenu);
Some systems provide a special help menu, which is drawn on the right side of the menu bar as opposed to somewhere in the middle. You can indicate that a specific menu is the help menu with the setHelpMenu() method. The given menu should already be added to the menu itself before being made a help menu:
Menu helpmenu = new Menu("Help"); mbar.add(helpmenu); mbar.setHelpMenu(helpmenu);
If, for any reason, you want to prevent a user from selecting a menu, you can use the disable() command on that menu (and the enable() command to make it available again):
myMenu.disable();
There are four kinds of items you can add to individual menus:
Regular menu items are added by using the MenuItem class. Add them to a menu using the add() method:
Menu myMenu = new Menu("Tools"); myMenu.add(new MenuItem("Info")); myMenu.add(new MenuItem("Colors"));
Submenus can be added simply by creating a new instance of Menu and adding it to the first menu. You can then add items to that menu:
Menu submenu = new Menu("Sizes"); myMenu.add(submenu); submenu.add(new MenuItem("Small")); submenu.add(new MenuItem("Medium")); submenu.add(new MenuItem("Large"));
The CheckBoxMenuItem class creates a menu item with a check box on it, enabling the menu state to be toggled on and off (selecting it once makes the check box appear selected; selecting it again unselects the check box). Create and add a check box menu item the same way you create and add regular menu items:
CheckboxMenuItem coords = new CheckboxMenuItem("Show Coordinates"); myMenu.add(coords);
Finally, to add a separator to a menu (a line used to separate groups of items in a menu), create and add a menu item with a single dash (-) as the label. That special menu item will be drawn with a separator line. These next two lines of Java code create a separator menu item and add it to the menu myMenu:
MenuItem msep = new MenuItem("-"); myMenu.add(msep);
Any menu item can be disabled by using the disable() method and enabled again using enable(). Disabled menu items cannot be selected:
MenuItem item = new MenuItem("Fill"); myMenu.addItem(item); item.disable();
You'll add a typical menu and menu bar to the pop-up window applet in a bit; but first let's learn about how to activate menu items when they're selected.
The act of selecting a menu item causes an action event to be generated. You can handle that action the same way you handle other action methods-by overriding action(). Both regular menu items and check box menu items have actions that generate an extra argument representing the label for that menu. You can use that label to determine which action to take. Note, also, that because CheckBoxMenuItem is a subclass of MenuItem, you don't have to treat that menu item as a special case. In this example, the Show Coordinates menu item is a CheckBoxMenuItem, and Fill is a regular menu item:
public boolean action(Event evt, Object arg) { if (evt.target instanceof MenuItem) { String label = (String)arg; if (label.equals("Show Coordinates")) toggleCoords(); else if (label.equals("Fill")) fillcurrentArea(); return true; } else return false; }
Let's add a menu to the pop-up window you created in the previous section. There are two steps here: creating and adding the menu, with all its menu items, to the layout, and then adding an action method to deal with the actions. Here we'll modify the BaseFrame class to include both these things; Listing 14.3 shows the new code. Figure 14.4 shows the menu in action.
Note |
In the sample code on the CD, I created a new class called BaseFrame2 for this part of the example, and a new class PopupWindowMenu.java to be the applet that owns this window. Use PopupWindowMenu.html to view it. |
Listing 14.3. BaseFrame with a menu.
1:import java.awt.*; 2: 3:class BaseFrame2 extends Frame { 4: String message = "This is a Window"; 5: 6: BaseFrame2(String title) { 7: super(title); 8: setFont(new Font("Helvetica", Font.BOLD, 12)); 9: 10: MenuBar mb = new MenuBar(); 11: Menu m = new Menu("Colors"); 12: m.add(new MenuItem("Red")); 13: m.add(new MenuItem("Blue")); 14: m.add(new MenuItem("Green")); 15: m.add(new MenuItem("-")); 16: m.add(new CheckboxMenuItem("Reverse Text")); 17: mb.add(m); 18: setMenuBar(mb); 19: } 20: 21: public boolean action(Event evt, Object arg) { 22: String label = (String)arg; 23: if (evt.target instanceof MenuItem) { 24: if (label.equals("Red")) setBackground(Color.red); 25: else if (label.equals("Blue")) setBackground(Color.blue); 26: else if (label.equals("Green")) setBackground(Color.green); 27: else if (label.equals("Reverse Text")) { 28: if (getForeground() == Color.black) { 29: setForeground(Color.white); 30: } else setForeground(Color.black); 31: } 32: repaint(); 33: return true; 34: } else return false; 35: } 36: 37: public void paint(Graphics g) { 38: g.drawString(message, 20, 20); 39: } 40: 41: public boolean handleEvent(Event evt) { 42: if (evt.id == Event.WINDOW_DESTROY) hide(); 43: return super.handleEvent(evt); 44: } 45:}
This menu has four items: one each for the colors red, blue, and green (which, when selected, change the background of the window), and one check box menu item for reversing the color of the text (to white). All are added as part of the constructor to this class, in lines 6 to 19.
To handle these menu items when they're chosen, you need an action() method. Inside action() you test to see if the action came from a menu item (which includes the one check box menu item), and if so, test for each of the menu labels in turn. For the red, blue, and green menu items, all you need to do is set the background. For the Reverse Text toggle, you need to first find out the current color of the text, and then reverse it.
To finish up, call a repaint() to make sure the background and the text get updated properly and return the appropriate boolean.
Dialog boxes are functionally similar to frames in that they pop up new windows on the screen. However, dialog boxes are intended to be used for transient windows-for example, windows that let you know about warnings, windows that ask you for specific information, and so on. Dialogs don't usually have title bars or many of the more general features that windows have (although you can create one with a title bar), and they can be made nonresizable or modal (modal dialogs prevent input to any other windows on the screen until they are dismissed).
Dialogs are transient windows intended to alert the user to some event or to get input from the user. Unlike frames, dialogs do not generally have a title bar or close boxes.
A modal dialog prevents input to any of the other windows on the screen until that dialog is dismissed. (You won't be able to bring other windows to the front or iconify a modal dialog window; you must actually dismiss the modal dialog before being able to do anything else on the system. Warnings and alerts are typically modal dialogs.)
The awt provides two kinds of dialog boxes: the Dialog class, which provides a generic dialog, and FileDialog, which produces the platform-specific file browser dialog.
Dialogs are created and used in much the same way as windows. To create a generic dialog, use one of these constructors:
The dialog window, like the frame window, is a panel on which you can lay out and draw user interface components and perform graphics operations, just as you would any other panel. Like other windows, the dialog is initially invisible, but you can show it with show() and hide it with hide().
Let's add a dialog to that same example with the pop-up window. Here we'll modify the BaseFrame class once again to include a dialog, and add a new class, TextDialog, which produces a text entry dialog similar to the one shown in Figure 14.5.
Figure 14.5 : The Enter Text dialog.
To add the dialog to the BaseFrame class, the changes are minor. First you'll need an instance variable to hold the dialog, since you'll be referring to it throughout this class:
TextDialog dl;
Next you'll add a menu item to the BaseFrame class's constructor method to change the text the pop-up window displays. This new menu item goes just after the Reverse Text item:
... m.add(new CheckboxMenuItem("Reverse Text")); m.add(new MenuItem("Set Text...")); ...
In that same constructor method, you can create the dialog (an instance of the new class TextDialog you'll create in a bit), assign it to the dl instance variable, and resize it (as shown in the next two lines of code). You don't want to show it yet because it should only appear when the correct menu item is selected:
dl = new TextDialog(this, "Enter Text", true); dl.resize(150,100);
To get the dialog to appear at the appropriate time, you'll add a line to the action() method so that when the Set Text menu item is chosen, the dl.show() method is called. You can put this action in the same if-else block as the rest of the actions:
... else if (label.equals("Green")) setBackground(Color.green); else if (label.equals("Set Text...")) dl.show(); else if (label.equals("Reverse Text")) { ...
That's the end of the behavior you have to add to the window to create a dialog; the rest of the behavior goes into the TextDialog class, the code for which is shown in Listing 14.4.
Listing 14.4. The TextDialog class.
1:import java.awt.*; 2: 3:class TextDialog extends Dialog { 4: TextField tf; 5: BaseFrame3 theFrame; 6: 7: TextDialog(Frame parent, String title, boolean modal) { 8: super(parent, title, modal); 9: 10: theFrame = (BaseFrame3)parent; 11: setLayout(new BorderLayout(10,10)); 12: setBackground(Color.white); 13: tf = new TextField(theFrame.message,20); 14: add("Center", tf); 15: add("South", new Button("OK")); 16: resize(150,75); 17: } 18: 19: public Insets insets() { 20: return new Insets(30,10,10,10); 21: } 22: 23: public boolean action(Event evt, Object arg) { 24: String label = (String)arg; 25: if (evt.target instanceof Button) { 26: if (label == "OK") { 27: hide(); 28: theFrame.message = tf.getText(); 29: theFrame.repaint(); 30: } 31: } 32: else return false; 33: return true; 34: } 35:}
In many ways this dialog class is very nearly the same as the BaseFrame class. It has a constructor that sets up the layout of the components, and an action() method to deal with its behavior. This one also has an insets() method for more layout information, but that's not a significant difference.
There are a few things to note about this code. First of all, note that the TextDialog class has a reference back up to its parent frame. It needs to reference this so it can update that frame with the new text information. Why does the dialog need to update the frame, rather than the frame figuring out when it needs updating? Because only the dialog knows when it's been dismissed. It's the dialog that deals with the change when the user presses OK, not the frame. So the dialog needs to be able to reach back to the original frame. Line 5 defines an instance variable to hold that reference.
The text dialog gets a reference to the parent frame through its constructor. This is actually the standard constructor for dialogs, so nothing new needs to be created here. You can simply call super() to initialize the dialog, and then add other bits to it. The first argument to the constructor is the frame argument. This is that hookup to the frame. But since you're getting a frame object, and you want a BaseFrame object, you'll have to cast it before you can assign it to the theFrame instance variable. Do this in line 10.
The remainder of the constructor for this dialog class simply creates the layout: a text field and a button in a border layout.
The action() method is what tells the dialog to hide itself. Mouse actions are broadcast to the window on which they occur; they do not percolate across windows, which is why you can't test to see if the OK button in the dialog was pressed from inside the BaseFrame class. Here you'll create an action() method to do two things when the OK button is pressed: hide the dialog and update the text message in the frame. Here's where that frame reference is important; in line 28 you're extracting the text that was entered into the dialog's text field and putting it into the frame's message instance variable. The next time the frame goes to paint (and you tell it to repaint() in line 29), the text message will get updated.
Dialogs can only be attached to frames; to create a dialog you have to pass an instance of the Frame class to one of the dialog's constructor methods.
This would imply that you cannot create dialog boxes that are attached to applets. Because applets don't have explicit frames, you cannot give the Dialog class a frame argument. Through a bit of sneaky code, however, you can get ahold of the frame object that contains that applet (often the browser or applet viewer window itself) and then use that object as the dialog's frame.
This sneaky code makes use of the getParent() method, defined for all awt components. The getParent() method returns the object that contains this object. The parent of all awt applications, then, must be a frame. Applets behave in this same way; by calling getParent() repeatedly, eventually you should be able to get ahold of an instance of Frame. Here's the sneaky code to do this that you can put inside your applet:
Object anchorpoint = getParent() while (! (anchorpoint instanceof Frame)) anchorpoint = ((Component)anchorpoint).getParent();
In the first line of this code, you create a local variable, called anchorpoint, to hold the eventual frame for this applet. The object assigned to anchorpoint may be one of many classes, so we'll declare its type to be Object.
The second two lines of this code are a while loop that calls getParent() on each different object up the chain until it gets to an actual Frame object. Note here that since the getParent() method is only defined on objects that inherit from Component, we have to cast the value of anchorpoint to Component each time for the getParent() method to work.
After the loop exits, the object contained in the anchorpoint variable will be an instance of the Frame class (or one of its subclasses). You can then create a Dialog object attached to that frame, casting the anchorpoint one more time to make sure you've got a Frame object:
TextDialog dl = new TextDialog((Frame)anchorpoint, "Enter Text", true);
The FileDialog class provides
a basic file open/save dialog box that enables you to access the
file system. The FileDialog
class is system-independent, but depending on the platform, the
standard Open File or Save File dialog is brought up.
Note |
For applets, whether or not you can even use instances of FileDialog is dependent on the browser (Netscape simply produces an error). FileDialog is much more useful in standalone applications. |
To create a file dialog, use the following constructors:
After you create a FileDialog instance, use show() to display it:
FileDialog fd = new FileDialog(this, "FileDialog"); fd.show();
When the reader chooses a file in the File dialog and dismisses it, you can then access the filename they chose by using the getDirectory() and getFile() methods; both return strings indicating the values the reader chose. You can then open that file by using the stream and file handling methods (which you'll learn about next week) and then read from or write to that file.
If you use frames in your applets or applications, you can also set the cursor's icon at given moments in your program's execution, to signal wait conditions or other events happening in your program.
The getCursorType() and setCursor()
methods are defined in the Frame
class. If you can get at a Frame
object, you can set the cursor (you'll typically set cursors for
windows, but you can also set cursors for applets using the getParent()
method that I explained in the section "Attaching Dialogs
to Applets"). Both of these methods use a set of predefined
cursor types in the Frame
class. Table 14.1 shows the cursor types you can use (and test
for) in your windows.
Note |
Keep in mind that not all platforms use the same cursors. For example, cursors for resizing windows do not exist on Macintoshes. |
Class Variable | Cursor |
Frame.CROSSHAIR_CURSOR | A cross-hair (plus-shaped) cursor |
Frame.DEFAULT_CURSOR | The default cursor (usually a pointer or arrow) |
Frame.E_RESIZE_CURSOR | A cursor to indicate something is being resized |
Frame.HAND_CURSOR | A hand-shaped cursor (to move an object or the background) |
Frame.MOVE_CURSOR | A cursor to indicate that something is being moved |
Frame.N_RESIZE_CURSOR | The top edge of a window is being resized |
Frame.NE_RESIZE_CURSOR | The top-right corner of a window is being resized |
Frame.NW_RESIZE_CURSOR | The top-left corner of a window is being resized |
Frame.S_RESIZE_CURSOR | The bottom edge of a window is being resized |
Frame.SE_RESIZE_CURSOR | The bottom-right corner of the window is being resized |
Frame.SW_RESIZE_CURSOR | The bottom-left corner of the window is being resized |
Frame.TEXT_CURSOR | A text-entry cursor (sometimes called an I-beam) |
Frame.W_RESIZE_CURSOR | The left edge of a window is being resized |
Frame.WAIT_CURSOR | A long operation is taking place (usually an icon for a watch or an hourglass) |
Yesterday you learned about writing your own event handler methods, and you noted that the Event class defines many standard events for which you can test. Window events are part of that list, so if you use windows, these events may be of interest to you, (for example, to hide a window when it's closed, to stop a thread from running when the window is iconified, or to perform some operation when a file is loaded or saved).
You can test the id instance variable of the event object in your handleEvent() method to see if any of these events have occurred:
if (evt.id == Event.WINDOW_DESTROY) hide();
Table 14.2. shows the various Window events.
WINDOW_DESTROY | Generated when a window is destroyed using the close box or the Close menu item |
WINDOW_EXPOSE | Generated when the window is brought forward from behind other windows |
WINDOW_ICONIFY | Generated when the window is iconified |
WINDOW_DEICONIFY | Generated when the window is restored from an icon |
WINDOW_MOVED | Generated when the window is moved |
After all the space and time I've devoted to creating applets up to this point, you may be surprised that I'm sticking a description of graphical Java applications here at the end, and in a fairly small section at that. The reason for this is that other than a few simple lines of code and in the environment each runs in, there's not a lot of difference between a Java applet and a graphical Java application. Everything you've learned up to this point about the awt including the graphics methods, animation techniques, events, UI components, and windows and dialogs, can be used the same way in Java applications as they can in applets. And applications have the advantage of being "outside the sandbox"-they have none of the security restrictions that applets have. You can do just about anything you want to with an application.
So how do you go about creating a graphical Java application? The code to do it is almost trivial. Your main application class should inherit from Frame. If it uses threads (for animation or other processing), it should also implement Runnable:
class MyawtApplication extends Frame implements Runnable { ... }
Inside the main() method for your application, you create a new instance of your class-because your class extends Frame, that'll give you a new awt window that you can then resize and show as you would any awt window. Inside the constructor method for your class you'll set up the usual awt features for a window that you might usually do in an init() method for an applet: Set the title, add a layout manager, create and add components such as a menu bar or other UI elements, start up a thread, and so on. Here's a simple example:
class MyawtApplication extends Frame implements Runnable { MyawtApplication(String title) { super(title); setLayout(new FlowLayout()); add(new Button("OK")); add(new Button("Reset")); add(new Button("Cancel")); } public static void main(String args[]) { MyawtApplications app = new MyawtApplication("Hi! I'm an application"); app.resize(300,300); app.show(); } }
For the most part, you can use any of the methods you've learned about this week to control and manage your application. The only methods you cannot use are those specific to applets (that is, those defined in java.applet.Applet, which includes methods for retrieving URL information and playing audio clips-see the API documentation for that class for more details).
Networking is the capability of making connections from your applet or application to a system over the network. Networking in Java involves classes in the java.net package, which provide cross-platform abstractions for simple networking operations, including connecting and retrieving files by using common Web protocols and creating basic UNIX-like sockets. Used in conjunction with input and output streams (which you'll learn much more about next week), reading and writing files over the network becomes as easy as reading or writing to files on the local disk.
There are restrictions, of course. Java applets usually cannot read or write from the disk on the machine where the browser is running. Java applets cannot connect to systems other than the one on which they were originally stored. Even given these restrictions, you can still accomplish a great deal and take advantage of the Web to read and process information over the Net.
This section describes three ways you can communicate with systems on the Net:
Probably the easiest way to use networking inside an applet is to tell the browser running that applet to load a new page. You can use this, for example, to create animated image maps that, when clicked, load a new page.
To link to a new page, you create a new instance of the class URL. You saw some of this when you worked with images, but let's go over it a little more thoroughly here.
The URL class represents a uniform resource locator. To create a new URL, you can use one of four different forms:
For the last one (creating a URL from a string), you have to catch a malformed URL exception, so surround the URL constructor with a try...catch:
String url = "http://www.yahoo.com/"; try { theURL = new URL(url); } catch ( MalformedURLException e) { System.out.println("Bad URL: " + theURL); }
Getting a URL object is the hard part. Once you have one, all you have to do is pass it to the browser. Do this by using this single line of code, where theURL is the URL object to link to:
getAppletContext().showDocument(theURL);
The browser that contains the Java applet with this code will then load and display the document at that URL.
Listing 14.5 shows two classes: ButtonLink and its helper class Bookmark. ButtonLink is a simple applet that displays three buttons that represent important Web locations (the buttons are shown in Figure 14.6). Clicking on the buttons causes the document to be loaded from the locations to which those buttons refer.
Figure 14.6 : Bookmark buttons.
Listing 14.5. Bookmark buttons.
1: // Buttonlink.java starts here 2: import java.awt.*; 3: import java.net.*; 4: 5: public class ButtonLink extends java.applet.Applet { 6: 7: Bookmark bmlist[] = new Bookmark[3]; 8: 9: public void init() { 10: bmlist[0] = new Bookmark("Laura's Home Page", 11: "http://www.lne.com/lemay/"); 12: bmlist[1] = new Bookmark("Gamelan", 13: "http://www.gamelan.com"); 14: bmlist[2]= new Bookmark("Java Home Page", 15: "http://java.sun.com"); 16: 17: setLayout(new GridLayout(bmlist.length,1, 10, 10)); 18: for (int i = 0; i < bmlist.length; i++) { 19: add(new Button(bmlist[i].name)); 20: } 21: } 22: 23: public boolean action(Event evt, Object arg) { 24: if (evt.target instanceof Button) { 25: linkTo((String)arg); 26: return true; 27: } 28: else return false; 29: } 30: 31: void linkTo(String name) { 32: URL theURL = null; 33: for (int i = 0; i < bmlist.length; i++) { 34: if (name.equals(bmlist[i].name)) 35: theURL = bmlist[i].url; 36: } 37: if (theURL != null) 38: getAppletContext().showDocument(theURL); 39: } 40: } //ButtonLink.java ends here 41: 42: //Bookmark.java starts here 43: import java.net.URL; 44: import java.net.MalformedURLException; 45: 46: class Bookmark { 47: String name; 48: URL url; 49: 50: Bookmark(String name, String theURL) { 51: this.name = name; 52: try { this.url = new URL(theURL); } 53: catch ( MalformedURLException e) { 54: System.out.println("Bad URL: " + theURL); 55: } 56: } 57:} //Bookmark.java ends here
Two classes make up this applet: The first, ButtonLink, implements the actual applet itself; the second, Bookmark, is a class representing a bookmark. Bookmarks have two parts: a name and a URL.
This particular applet creates three bookmark instances (lines 10 through 15) and stores them in an array of bookmarks (this applet could be easily modified to accept bookmarks as parameters from an HTML file). For each bookmark, a button is created whose label is the value of the bookmark's name.
When the buttons are pressed, the linkTo() method is called. linkTo(), defined in lines 31 to 38, extracts the name of the button from the event, uses it to look up the actual URL from the bookmark object, and then tells the browser to load the URL referenced by that bookmark.
Rather than asking the browser to just load the contents of a file, sometimes you might want to get hold of that file's contents so that your applet can use them. If the file you want to grab is stored on the Web, and can be accessed using the more common URL forms (http, ftp, and so on), your applet can use the URL class to get it.
Note that for security reasons, applets can by default connect back only to the same host from which they originally loaded. This means that if you have your applets stored on a system called www.myhost.com, the only machine your applet can open a connection to will be that same host (and that same hostname, so be careful with host aliases). If the file the applet wants to retrieve is on that same system, using URL connections is the easiest way to get it.
This security restriction will change how you've been writing and testing applets up to this point. Because we haven't been dealing with network connections, we've been able to do all our testing on the local disk simply by opening the HTML files in a browser or with the appletviewer tool. You cannot do this with applets that open network connections. In order for those applets to work correctly, you must do one of two things:
You'll know when you're not doing things right in regard to making sure your applet, and the connection it's opening, are on the same server. If you try to load an applet or a file from different servers, you'll get a security exception along with a lot of other scary error messages printed to your screen or to the Java console.
That said, let's move on to the methods and classes for retrieving files from the Web.
The URL class defines a method called openStream(), which opens a network connection using the given URL (an HTTP connection for Web URLs, an FTP connection for FTP URLs, and so on) and returns an instance of the class InputStream (part of the java.io package). If you convert that stream to a DataInputStream (with a BufferedInputStream in the middle for better performance), you can then read characters and lines from that stream (you'll learn all about streams on Day 19, "Streams and I/O"). For example, these lines open a connection to the URL stored in the variable theURL, and then read and echo each line of the file to the standard output:
try { InputStream in = theURL.openStream(); DataInputStream data = new DataInputStream(new BufferedInputStream(in); String line; while ((line = data.readLine()) != null) { System.out.println(line); } } catch (IOException e) { System.out.println("IO Error: " + e.getMessage()); }
Note |
You need to wrap all those lines in a try...catch statement to catch IOExceptions generated. You'll learn more about IOExceptions and the try and catch statements on Day 17, "Exceptions." |
Here's an example of an applet that uses the openStream() method to open a connection to a Web site, reads a file from that connection (Edgar Allen Poe's poem "The Raven"), and displays the result in a text area. Listing 14.6 shows the code; Figure 14.7 shows the result after the file has been read.
Figure 14.7 : The GetRaven applet.
An important note: If you compile this code as written, it won't work-you'll get a security exception. The reason is that this applet opens a connection to the server www.lne.com to get the file raven.txt. When you compile and run this applet, that applet isn't running on www.lne.com (unless you're me, and I already know about this problem). Before you compile this applet, make sure you change line 18 to point to a copy of raven.txt on your server, and install your applet and your HTML files on that same server (you can get raven.txt from the CD or from that very URL).
Alternately, you can use your browser to point to the URL http://www.lne.com/Web/JavaProf/GetRaven.html. That Web page loads this very applet and downloads the file correctly. Because both the applet and the text file are on the same server, it works just fine.
Listing 14.6. The GetRaven class.
1: import java.awt.*; 2: import java.io.DataInputStream; 3: import java.io.BufferedInputStream; 4: import java.io.IOException; 5: import java.net.URL; 6: import java.net.URLConnection; 7: import java.net.MalformedURLException; 8: 9: public class GetRaven extends java.applet.Applet implements Runnable { 10: URL theURL; 11: Thread runner; 12: TextArea ta = new TextArea("Getting text..."); 13: 14: public void init() { 15: setLayout(new GridLayout(1,1)); 16: 17: // chANGE THIS NEXT LINE BEFORE COMPILING!!! 18: String url = "http://www.lne.com/Web/JavaProf/raven.txt"; 19: try { this.theURL = new URL(url); } 20: catch ( MalformedURLException e) { 21: System.out.println("Bad URL: " + theURL); 22: } 23: add(ta); 24: } 25: 26: public Insets insets() { 27: return new Insets(10,10,10,10); 28: } 29: 30: public void start() { 31: if (runner == null) { 32: runner = new Thread(this); 33: runner.start(); 34: } 35: } 36: 37: public void stop() { 38: if (runner != null) { 39: runner.stop(); 40: runner = null; 41: } 42: } 43: 44: public void run() { 45: URLConnection conn = null; 46: DataInputStream data = null; 47: String line; 48: StringBuffer buf = new StringBuffer(); 49: 50: try { 51: conn = this.theURL.openConnection(); 52: conn.connect(); 53: ta.setText("Connection opened..."); 54: data = new DataInputStream(new BufferedInputStream( 55: conn.getInputStream())); 56: ta.setText("Reading data..."); 57: while ((line = data.readLine()) != null) { 58: buf.append(line + "\n"); 59: } 60: ta.setText(buf.toString()); 61: } 62: catch (IOException e) { 63: System.out.println("IO Error:" + e.getMessage()); 64: } 65:} 66:}
The init() method (lines 14 to 24) sets up the URL and the text area in which that file will be displayed. The URL could be easily passed into the applet via an HTML parameter; here, it's just hard coded for simplicity.
Because it might take some time to load the file over the network, you put that routine into its own thread and use the familiar start(), stop(), and run() methods to control that thread.
Inside run() (lines 44 to 64), the work takes place. Here, you initialize a bunch of variables and then open the connection to the URL (using the openStream() method in line 50). Once the connection is open, you set up an input stream in lines 51 to 55 and read from it, line by line, putting the result into an instance of StringBuffer (a string buffer is a modifiable string). I put all this work into a thread because it may take some time for the connection to open and for the file to be read-particularly across slower connections. There may be other things going on in the applet that need to take place concurrently to the file loading.
Once all the data has been read, line 60 converts the StringBuffer object into a real string and then puts that result in the text area.
One other thing to note about this example is that the part of the code that opened a network connection, read from the file, and created a string is surrounded by a try and catch statement. If any errors occur while you're trying to read or process the file, these statements enable you to recover from them without the entire program crashing (in this case, the program exits with an error, because there's little else to be done if the applet can't read the file). try and catch give you the capability of handling and recovering from errors. You'll learn more about exceptions on Day 17.
For networking applications beyond what the URL and URLconnection classes offer (for example, for other protocols or for more general networking applications), Java provides the Socket and ServerSocket classes as an abstraction of standard socket programming techniques. You'll learn more about working with Java sockets on Day 26, "Client/Server Networking in Java," but for now here's a very short rundown of the socket capabilities in Java.
The Socket class provides a client-side socket interface similar to standard UNIX sockets. To open a connection, create a new instance of Socket (where hostname is the host to connect to, and portnum is the port number):
Socket connection = new Socket(hostname, portnum);
Note |
If you use sockets in an applet, you are still subject to the applet security restrictions that prevent you from connecting to any system other than the same one the applet came from. |
Once the socket is open, you can use input and output streams to read and write from that socket (you'll learn all about input and output streams on Day 19):
DataInputStream in = new DataInputStream( new BufferedInputStream(connection.getInputStream())); DataOutputStream out= new DataOutputStream( new BufferedOutputStream(connection.getOutputStream()));
Once you're done with the socket, don't forget to close it (this also closes all the input and output streams you may have set up for that socket):
connection.close();
Server-side sockets work similarly, with the exception of the accept() method. A server socket listens on a TCP port for a connection from a client; when a client connects to that port, the accept() method accepts a connection from that client. By using both client and server sockets, you can create applications that communicate with each other over the network.
To create a server socket and bind it to a port, create a new instance of ServerSocket with the port number:
ServerSocket sconnection = new ServerSocket(8888);
To listen on that port (and to accept a connection from any clients if one is made), use the accept() method:
sconnection.accept();
Once the socket connection is made, you can use input and output streams to read from and write to the client.
See the java.net package for more information about Java sockets.
In the 1.0.2 version of Java, the Socket and ServerSocket classes provide a basic abstract socket implementation. You can create new instances of these classes to make or accept connections and to pass data back and forth from a client to a server.
The problem comes when you try to extend or change Java's socket behavior. The Socket and ServerSocket classes in the java.net package are final classes, which means you cannot create subclasses of those classes (you'll learn more about finalizing classes on Day 15, "Modifiers, Access Control, and Class Design"). To extend the behavior of the socket classes- for example, to allow network connections to work across a firewall or a proxy, you can use the abstract classes SocketImpl and the interface SocketImplFactory to create a new transport-layer socket implementation. This design fits with the original goal of Java's socket classes: to allow those classes to be portable to other systems with different transport mechanisms.
The problem with this mechanism is that while it works for simple cases, it prevents you from adding other protocols on top of TCP (for example, to implement an encryption mechanism such as SSL) or for having multiple socket implementations per Java runtime.
For these reasons, in Java 1.1 sockets will change such that the Socket and ServerSocket classes are nonfinal and extendable. You will be able to create subclasses of these classes in Java 1.1, which use either the default socket implementation or one of your own making. This will allow much more flexible network capabilities to Java in 1.1.
In addition, Java 1.1 has added several other new features to the java.net package:
For more information about all the networking changes between Java 1.02 and 1.1, see the pages at http://java.sun.com/products/JDK/1.1/designspecs/net/index.html.
On this, the last section of the last day of the second week, let's finish with some small hints that didn't fit in anywhere else: using showStatus() to print messages in the browser status window, providing applet information, and communicating between multiple applets on the same page.
The showStatus() method, available in the Applet class, enables you to display a string in the status bar of the browser, which contains the applet. You can use this for printing error, link, help, or other status messages:
getAppletContext().showStatus("Change the color");
The getAppletContext() method
enables your applet to access features of the browser that contains
it. You already saw a use of this with links, wherein you could
use the showDocument() method
to tell the browser to load a page. showStatus()
uses that same mechanism to print status messages.
Note |
showStatus() may not be supported in all browsers, so do not depend on it for your applet's functionality or interface. It is a useful way of communicating optional information to your user-if you need a more reliable method of communication, set up a label in your applet and update it to reflect changes in its message. |
The awt gives you a mechanism for associating information with your applet. Usually, there is a mechanism in the browser viewing the applet to view display information. You can use this mechanism to sign your name or your organization to your applet, or to provide contact information so that users can get hold of you if they want.
To provide information about your applet, override the getAppletInfo() method:
public String getAppletInfo() { return "GetRaven copyright 1995 Laura Lemay"; }
Sometimes you want to have an HTML page that has several different applets on it. To do this, all you have to do is include several different iterations of the applet tag. The browser will create different instances of your applet for each one that appears on the HTML page.
What if you want to communicate between those applets? What if
you want a change in one applet to affect the other applets in
some way? The best way to do this is to use the applet context
to get to different applets on the same page.
Note |
Be forewarned that before you do extensive work with inter-applet communication, the mechanism described in this section is implemented differently (and often unreliably) in different browsers and different Java environments. If you need to rely on communicating between applets for your Web pages, make sure you test those applets extensively in different browsers on different platforms. |
The applet context is defined in a class called, appropriately, AppletContext. To get an instance of this class for you applet, you use the getAppletContext() method. You've already seen the use of the getAppletContext() method for other uses; you can also use it to get hold of the other applets on the page. For example, to call a method named sendMessage() on all the applets on a page (including the current applet), use the getApplets() method and a for loop that looks something like this:
for (Enumeration e = getAppletContext().getApplets(); e.hasMoreElements();) { Applet current = (MyAppletSubclass)(e.nextElement()); current.sendMessage(); }
The getApplets() method returns an Enumeration object with a list of the applets on the page. Iterating over the Enumeration object in this way enables you to access each element in the Enumeration in turn. Note that each element in the Enumeration object is an instance of the Object class; to get that applet to behave the way you want it to (and accept messages from other applets), you'll have to cast it to be an instance of your applet subclass (here, the class MyAppletSubclass).
If you want to call a method in a specific applet, it's slightly more complicated. To do this, you give your applets a name and then refer to them by name inside the body of code for that applet.
To give an applet a name, use the NAME attribute to <APPLET> in your HTML file:
<P>This applet sends information: <APPLET CODE="MyApplet.class" WIDTH=100 HEIGHT=150 NAME="sender"> </APPLET> <P>This applet receives information from the sender: <APPLET CODE="MyApplet.class" WIDTH=100 HEIGHT=150 NAME="receiver"> </APPLET>
To get a reference to another applet on the same page, use the getApplet() method from the applet context with the name of that applet. This gives you a reference to the applet of that name. You can then refer to that applet as if it were just another object: call methods, set its instance variables, and so on. Here's some code to do just that:
// get ahold of the receiver applet Applet receiver = (MyAppletSubclass)getAppletContext().getApplet("receiver"); // tell it to update itself. receiver.update(text, value);
In this example you use the getApplet() method to get a reference to the applet with the name receiver. Note that the object returned by getApplet is an instance of the generic Applet class; you'll most likely want to cast that object to an instance of your subclass. Given the reference to the named applet, you can then call methods in that applet as if it were just another object in your own environment. Here, for example, if both applets have an update() method, you can tell receiver to update itself by using the information the current applet has.
Naming your applets and then referring to them by using the methods described in this section enables your applets to communicate and stay in sync with each other, providing uniform behavior for all the applets on your page.
Congratulations! Take a deep breath-you're finished with Week 2. This week has been full of useful information about creating applets and using the Java awt classes to display, draw, animate, process input, and create fully fledged interfaces in your applets.
Today you finished exploring applets and the awt by learning about three concepts.
First, you learned about windows, frames, menus, and dialogs, which enable you to create a framework for your applets-or enable your Java applications to take advantage of applet features.
Second, you had a brief introduction to Java networking through some of the classes in the java.net package. Applet networking includes things as simple as pointing the browser to another page from inside your applet, but can also include retrieving files from the Web by using standard Web protocols (http, ftp, and so on). For more advanced networking capabilities, Java provides basic socket interfaces that can be used to implement many basic network-oriented applets-client/server interactions, chat sessions, and so on.
Finally, you finished up with the tidbits-small features of the Java awt and of applets that didn't fit anywhere else, including showStatus(), providing information about your applet, and communicating between multiple applets on a single page.
When I create pop-up windows, they all show up with this big yellow bar that says Warning: applet window. What does this mean? | |
The warning is to tell you (and the users of your applet) that the window being displayed was generated by an applet, and not by the browser itself. This is a security feature to keep an applet
programmer from popping up a window that masquerades as a browser window and, for example, asks users for their passwords.
There's nothing you can do to hide or obscure the warning. | |
What good is having a file dialog box if you can't read or write files from the local file system? | |
Applets often can't read or write from the local file system (depending on the browser), but because you can use awt components in Java applications as well as applets, the file dialog box is also very useful for them. | |
How can I mimic an HTML form submission in a Java applet? | |
Currently, applets make it difficult to do this. The best (and easiest way) is to use GET notation to get the browser to submit the form contents for you.
http://www.blah.com/cgi-bin/myscript?foo=1&bar=2&name=Laura Because the form input is encoded in the URL, you can write a Java applet to mimic a form, get input from the user, and then construct a new URL object with the form data included on the end. Then just pass that URL to the browser by using getAppletContext().showDocument(), and the browser will submit the form results itself. For simple forms, this is all you need. | |
How can I do POST form submissions? | |
You'll have to mimic what a browser does to send forms using POST: Open a socket to the server and send the data, which looks something like this (the exact format is determined by the
HTTP protocol; this is only a subset of it):
POST /cgi-bin/mailto.cgi HTTP/1.0 If you've done it right, you get the CGI form output back from the server. It's then up to your applet to handle that output properly. Note that if the output is in HTML, there really isn't a way to pass that output to the browser that is running your applet yet. This capability may end up in future Java releases. If you get back a URL, however, you can redirect the browser to that URL. | |
showStatus() doesn't work in my browser. How can I give my readers status information? | |
As you learned in the section on showStatus(), whether or not a browser supports showStatus() is up to that browser. If you must have status-like behavior in your applet, consider creating a status label in the applet itself that is updated with the information you need to present. | |
I've been trying to communicate between two applets in my Web page using the getAppletContext() and getApplet() methods. My applets keep crashing with NullPointerException errors. What does this mean? | |
The mechanism I described for communicating between applets is how Sun and the Java class library says it's supposed to work. However, like showStatus(), whether or not a browser implements that mechanism, or implements it correctly, depends on that browser. Version of Netscape before 3.0 and Internet Explorer both have strange problems with inter-applet communication. | |
It looks like the openStream() method and the Socket classes implement TCP sockets. Does Java support UDP (User Datagram Protocol, often just called datagram) sockets? | |
The JDK 1.0 provides two classes, DatagramSocket and DatagramPacket, which implement UDP sockets. The DatagramSocket class operates similarly to the Socket
class. Use instances of DatagramPacket for each packet you send or receive over the socket.
See the API documentation for the java.net package for more information. |