What's So Hot about Java? (Page 3 of 3)




Inter-Applet Communications


To implement the whiteBoard applet, our wbControls object needed to communicate changes in ink color to the wbPanel object. This communication between panels was done simply by having wbControls call wbPanel's setForeground() method. Painless, right? The good news is that inter-applet communications is just as easy. Since we now have two applets, helloWorld and whiteBoard, we can demonstrate Inter-Applet communications.

Here's the plan. We'll display both the helloWorld and whiteBoard applets on a single Web page. We'll add a RESTART button to our helloWorld applet to restart the animation and clear the whiteBoard's drawings. In other words, when the RESTART button is pressed, the helloWorld applet will have to tell the whiteBoard applet to clear its wbPanel.

To make our plan work, we need something to represent our whiteBoard's contents. We need a few methods that both helloWorld and wbPanel can call to manipulate the whiteBoard's contents. The problem is, we don't want to try to force these two classes into the same superclass. We solved a similar problem earlier by using Interfaces. Interfaces are a set of abstract methods that act as a common shared repository. If we use an abstract class to represent our whiteBoard's contents, then both whiteBoard and helloWorld will have access. This means neither applet needs to change, except to invoke our new methods.

Figure 11 illustrates the new wb class. When whiteBoard creates its wbPanel, it will call the setWb() method. Since wb represents the contents of the whiteBoard, we moved our vector of shapes into the abstract wb class. Notice the synchronized modifier used with each of the wb class's methods. The synchronized keyword prevents more than one thread from executing the enclosed block of code at the same time. Insert the wb class in Figure 11 into wb.java after the import statements.

You'll need to make a few other changes to utilize our new wb class. Refer to the original wb.java file in Figure 9. Notice the big green numbers "1" through "5." These numbers mark the lines that will be changed. Since we moved the shapes vector under the protection of wb, we need to remove its declaration from wbPanel, so in the wb.java file:

  1. Delete the line at "1" and at "2" in Figure 9.

  2. Replace the line at "3" with the statement "wb.addShape(s);" ;.
  3. Prefix the word "shapes" with "wb." in the line at "4" and at "5."

In the whiteBoard.java file (Figure 10), add the statement " wb.setWb(wp);" at the line indicated by the big "1." This will initialize the whiteBoard. In the helloWorld.java file, you can:

  1. Insert the line "add(new Button("RESTART"));" after the call to resize in helloWorld's init() method.
  2. Insert the handleEvent() method illustrated in Figure 12 into the helloWorld class after the stop() method. HandleEvent() will restart the animation and clear the whiteBoard (via inter-applet communications) when "RESTART" is clicked.

Now, recompile all three files: wb.java, whiteBoard.java, and helloWorld.java.

We're almost there. Since we need to combine the helloWorld and whiteBoard applets on a single page, we need a new HTML file. Create the both.html file as shown in Figure 13. Open this HTML file in your browser, draw a few lines on the whiteBoard, then test the new RESTART button.

The next segment will feature client-server network (socket) communications. Is adding network communications as simple as adding inter-applet communications? Well, not exactly. If the applets happen to be running on different systems, we can't just invoke the method of another applet.

Because Java's security features restrict applets from making network connections (except to the host that served the applet), we need to implement a whiteBoard server program to forward messages to and from client whiteBoard applets. Unlike our helloWorld and whiteBoard applets, our server won't be invoked from a browser. Instead, the server should run as a daemon on the Web Server so it will always be available for connecting clients. Note that Java programs not invoked from a browser are called Java applications.

Java applications must have a main() method, which is called when the program is invoked. Any command line arguments supplied when the program is started are passed as input arguments to the main() method, just as in C and C++.

Just as an applet has a specific flow, a pair of programs using socket interfaces also has a specific flow. Figure 14 depicts a simple flow in which a server listens for and connects to incoming clients. A server associates itself with a particular logical port by binding itself to that port. The server waits to accept incoming clients. To communicate with a server, a client connects to the port to which the server is bound. For each incoming client, TCP/IP creates a new socket to represent the client. This new socket is passed to the server that is bound to the requested port to handle all communication to this particular client.

The process is repeated for each incoming client. Note that server processes block while waiting for incoming clients. And when reading messages from the socket, both the server and the client processes block.

Because the socket interface will cause our client and server to block at various points, we'll need to implement multithreading in both the client and server. Figure 15 illustrates how the clients will interface to each other through the server.

You'll need to do the following:

  • Create a whiteBoard Server (call it wbServer). Figure 16 contains the source code to be inserted into our wbServer.java file. Notice that the server simply accepts two clients, then creates two threads. Each thread will listen for messages from one client and forward those messages to the other client.
  • Create a serverSockThread class to receive messages from a client and forward those messages to the other client. Figure 17 contains the source code to be inserted in the serverSockThread.java.
  • Create a sockThread class to read incoming messages from wbServer. These messages represent shapes to be drawn on the wbPanel and therefore these messages will be added to wbPanel using the addShape() method. Figure 18 includes the Java code for the sockThread.java source file.
  • Modify the whiteBoard applet to create a new thread (sockThread) to handle the socket when the applet is initialized and to stop the thread when the applet is destroyed. Figure 19 contains the new whiteBoard class to replace the whiteBoard class defined in whiteBoard.java.
  • Modify the wb class so that when shapes are added to the wbPanel, they'll be added to both the local and the remote wbPanel. First, replace the wb class defined in wb.java with the new wb class in Figure 20. Next, modify the wb.addShape(s) in "wb.java" to "wb.addShape(s,true)".
  • Ensure that no other applications are using the port 8888 by issuing the command "netstat-an |grep 8888". If the port is being used, select another port number (find a number greater than 5000 that is not being used in the /etc/services file) and replace the 8888 used in whiteBoard.java and wbServer.java.

Note the import statements in Figures 19 and 20 are in addition to those import statements already included in whiteBoard.java and wb.java.

You may have noticed that the serverSockThread uses the DataInputStream to filter the socket input. As described in the API documentation, if an error occurs on the socket or if the remote disconnects, the readInt() method will throw a IOException or EOFException, respectively. However, note that serverSockThread catches only Throwable. Throwable is the superclass for both IOException and EOFException. By catching Throwable, we are telling Java to do the same thing, regardless of which exception is thrown.

You may have also noticed the finally keyword in serverSockThread's cleanWb method. Finally denotes statements to be executed regardless of whether an exception was generated.

When you complete the changes described, compile each of the modified Java source files. Start the wbServer by entering the command java wbServer. From a separate C window, start two Netscape browsers (netscape &) and set the Location field on both browsers to the both.html file created in the inter-applet communications example. Test your work by drawing lines on either of the whiteBoards; lines drawn on either browser should appear on both browsers.


Linking Native Methods


We've seen that Java provides a very powerful set of class libraries for building Internet applets and applications. However, it's possible that you may require a special capability provided by your operating system but not implemented in the Java class libraries. Java allows native methods to be implemented. Of course, there is a catch.

First, once you link a native method into your Java application, the application is no longer hardware-independent. In other words, you'll need to implement the feature for every operating system on which your application is to run.

The second problem is that, for security reasons, native methods cannot be linked into applets. So if you link native methods, your Java program will have to run as an application. In other words, it cannot be loaded from a browser over the Net.

The good news is that the JDK does provide tools to simplify linking native methods. For our example, assume that we've decided that our wbServer should not have its IP port (8888) hardcoded. Instead, we'd like to be more flexible and allow the system administrator to configure the port.

The standard for configuring IP ports is to associate the port number with a service name in the /etc/services file. The server then calls the getservbyname() subroutine to convert the service name into a port number. Since the Java class libraries do not implement this feature, we'll need to create and link a native method.

Using this requirement as a premise, Figure 21 illustrates the steps required to create and link the proposed native method on AIX. Start by creating a getService.java file containing the getService class. Note the static initializer in the getService class. Static denotes the code in the braces ({}) is be executed exactly once, when the class is loaded. The Java stub (getservice.java) class must include this static method to load the shared library containing the native method. Notice how loadLibrary loads getsvc, which matches the library name (libgetsvc.so) in our makefile (Figure 22.1). The getService.java file also contains the declaration for our native byName() method.

In the second step, generate the getService class file by compiling the getService.java file. We use this class file in Steps 2 and 3 (see Figure 2) to generate the C header file and the C stub file.

In Step 4, we modify our wbServer to create the getService object and call the byName() method we declared in Step 2. This code replaces the line denoted by the big green "1" in the main() method of our wbServer class.

Now we've completed the steps that are generic to linking native methods in any Java implementation. The remaining steps are AIX-specific. We'll discuss those next.
AIX-SPECIFIC STEPS
In Step 5, we create an export file, getService.exp. The export file is required by the AIX linker (ld) command to indicate external symbols to be made available for another executable (Java) to import. Note that we exported the two symbols defined in the C stub file generated by javah.

Finally, in Step 6, we create the native method itself. Notice several key points about this function:
  • 1. To match the stub file getService.h created by javah in Step 3, function name must be the format class_ method. Our function name, then, is getService_byName.
  • 2. Be sure to include #include "getService.h, the C header file generated by javah.

  • 3. The input arguments and returned parameter must be defined to match the declaration in the C header file getService.h generated by javah.

  • 4. The makeCstring() function was used to get a character pointer to the String input argument passed by the Java class. The makeCstring and other useful methods for mapping Java constructs to C are declared in the /usr/lpp/Java/include/native.h file. The native.h file is included in the getService.h file generated by javah.

Now that we've written all our C and Java source code, our only remaining step is to compile and link the code. Figure 22.2 provides a contents of a makefile that simplifies this requirement. Note that we've set up several variables so that our makefile can be easily modified to compile and link other native methods.

If you have not already done so, enter the text from Figure 22 into a file called makefile. Once this is complete, enter the command make, to build our new wbServer. Finally, insert the line "wbSvr 8888/tcp" into the /etc/services file. Test the wbServer and whiteBoard clients as described in the "Network Communications" posted earlier.

This concludes our series on Java examples. So where do you go from here? Well, the final copy of whiteBoard provided with the download package shows images as they are being drawn and allows users to draw circles and text as well as lines, so incorporate these enhancements into your applets. Try dressing up the helloWorld message by using the Blink demo provided with the JDK. Be sure to visit the IBM Centre for Java Technology Development to see what's in store for JDK V1.1. And remember – this is only the beginning!



JavaTM is a trademark of Sun Microsystems, Inc.

Other companies, products, and service names may be trademarks or service marks of others.

Copyright    Trademark



Previous Page Table of Contents Java Education Java Home
IBM HomeOrderEmployment