157.

previous chapter table of contents next chapter
  

Where Are the Class Files?

Clients , servers, and service locators can use class files from a variety of sources. Which source they use can depend on the structure of the client and the server. This section looks at some of the variations that can occur.

Problem Domain

A service may require information about a client before it can (or will) proceed. For example, a banking service may require a user ID and a PIN number. Using the techniques discussed in earlier chapters, this could be done by the client collecting the information and calling suitable methods , such as void setName(String name ) in the service (or more likely, in the service's proxy) running in the client, as shown here:

 public class Client {     String getName() {         ...         service.setName(...);         ...     }; } class Service {     void setName(String name){         ...     }; } 

A service may wish to have more control over the setting of names and passwords than this. For example, it may wish to run verification routines based on the pattern of keystroke entries. More mundanely, it may wish to set time limits on the period between entering the name and the password. Or it may wish to enforce some particular user interface to collect this information. In any case, the service proxy may perform some sort of input processing on the client side before communicating with the real service. The service proxy may need to find extra classes in order to perform this processing.

A standalone application that gets a user name might use a GUI interface as shown in Figure 13-1.


Figure 13-1: User interface for name entry

The implementation for this name entry user interface might look like this:

 package standalone; import java.awt.*; import java.awt.event.*; /**  * NameEntry.java  */ public class NameEntry extends Frame {     public NameEntry() {         super("Name Entry");         addWindowListener(new WindowAdapter() {             public void windowClosing(WindowEvent e) {System.exit(0);}             });         Label label = new Label("Name");         TextField name = new TextField(20);         add(label, BorderLayout.WEST);         add(name, BorderLayout.CENTER);         name.addActionListener(new NameHandler());         pack();     }     public static void main(String[] args) {         NameEntry f = new NameEntry();         f.setVisible(true);     } } // NameEntry class NameHandler implements ActionListener {     public void actionPerformed(ActionEvent evt) {         System.out.println("Name was: " + evt.getActionCommand());     } } 

The classes used in this implementation are these:

  • A set of standard classes: Frame , Label , TextField , ActionListener , ActionEvent , BorderLayout , WindowEvent , and System
  • A couple of new classes: NameEntry and NameHandler

At compile time, and at runtime, these will need to be accessible.

NameEntry Interface

A standalone application needs to have all the class files available to it. In a Jini system, we have already seen that different components may only need access to a subset of the total set of classes. The simple application just shown used a large set of classes. If this is used to form part of a Jini system then some parts of this application will end up in Jini clients, and some will end up in Jini services. Each of them will have requirements about which classes they have access to, and this will depend on how the components are distributed.

We don't want to be overly concerned about the program logic of what is done with the user name once it has been entered ”the interesting part is the location of the classes. All possible ways of distributing this application into services and clients will need an interface definition, which we can make as follows :

 package common; /**  * NameEntry.java   */ public interface NameEntry  {     public void show(); } // NameEntry 

Then the client can call upon an implementation to simply show() itself and collect information in whatever way it chooses.

Note  

We don't want to get involved here in the ongoing discussion about the most appropriate interface definition for GUI classes ”this topic is taken up in Chapter 19 .

Naive Implementation

A simple implementation of this NameEntry interface is as follows:

 package complex; import java.awt.*; import java.awt.event.*; import javax.swing.*; import com.sun.jini.lookup.JoinManager; import net.jini.core.lookup.ServiceID; import com.sun.jini.lookup.ServiceIDListener; import com.sun.jini.lease.LeaseRenewalManager; /**  * NameEntryImpl1.java   */ public class NameEntryImpl1 extends Frame implements common.NameEntry,                                           ActionListener,java.io.Serializable {     public NameEntryImpl1() {         super("Name Entry");         /*         addWindowListener(new WindowAdapter() {             public void windowClosing(WindowEvent e) {System.exit(0);}             public void windowOpened(WindowEvent e) {}});                 */         setLayout(new BorderLayout());         Label label = new Label("Name");         add(label, BorderLayout.WEST);         TextField name = new TextField(20);         add(name, BorderLayout.CENTER;         name.addActionListener(this);         // don't do this here!         // pack();     }     /**      * method invoked on pressing <return> in the TextField      */     public void actionPerformed(ActionEvent evt) {         System.out.println("Name was: " + evt.getActionCommand());     }     public void show() {         pack();         super.show();     } } // NameEntryImpl1 

This implementation of the user interface creates the GUI elements in the constructor. When exported, this entire user interface will be serialized and exported. The instance data isn't too big in this case (about 2,100 bytes), but that is because the example is small. A GUI with several hundred objects will be much larger. This is overhead, which could be avoided by deferring creation to the client side.

Figure 13-2 shows which instances are running in which JVM.

click to expand
Figure 13-2: JVM objects for the naive implementation of the user interface

Another problem with this code is that it first creates an object on the server that has heavy reliance on environmental factors on the server. It then removes itself from that environment and has to reestablish itself on the target client environment.

On my current system, this dependence on environments shows up as a _TextField complaining that it cannot find a whole bunch of fonts on my server. Of course, that doesn't matter because it gets moved to the client machine. (As it happens, the fonts aren't available on my client machine either, so I end up with two batches of complaint messages, from the server and from the client. I should only get the client complaints.) It could matter if the service died because of missing pieces on the server side that exist on the client.

What Files Need to Be Where?

The client needs to know the NameEntry interface class. This must be in its CLASSPATH .

The server needs to know the class files for

  • NameEntry
  • Server1
  • NameEntryImpl1

These must be in its CLASSPATH .

The HTTP server needs to know the class files for NameEntryImpl1 . This must be in the directory of documents for this server.

Factory Implementation

The second implementation minimizes the amount of serialized code that must be shipped around by creating as much as possible on the client side. We don't even need to declare the class as a subclass of Frame , because that class also exists on the client side. The client calls the show() interface method, and all the GUI creation is moved to there. Essentially, what is created on the server side is a factory object, and this object is moved to the client. The client than makes calls on this factory to create the user interface.

 package complex; import java.awt.*; import java.awt.event.*; import javax.swing.*; import com.sun.jini.lookup.JoinManager; import net.jini.core.lookup.ServiceID; import com.sun.jini.lookup.ServiceIDListener; import com.sun.jini.lease.LeaseRenewalManager; /**  * NameEntryImpl2.java  */ public class NameEntryImpl2 implements common.NameEntry,                                        ActionListener, java.io.Serializable {     public NameEntryImpl2() {     }     /**      * method invoked on pressing <return> in the TextField      */     public void actionPerformed(ActionEvent evt) {         System.out.println("Name was: " + evt.getActionCommand());     }     public void show() {         Frame fr = new Frame("Name Entry");         fr.addWindowListener(new WindowAdapter() {             public void windowClosing(WindowEvent e) {System.exit(0);}             public void windowOpened(WindowEvent e) {}});         fr.setLayout(new BorderLayout());         Label label = new Label("Name");         fr.add(label, BorderLayout.WEST);         TextField name = new TextField(20);         fr.add(name, BorderLayout.CENTER);         name.addActionListener(this);         fr.pack();         fr.show();     } } // NameEntryImpl2 

Figure 13-3 shows which instances are running in which JVM.

click to expand
Figure 13-3: JVM objects for the factory implementation of the user interface

There are some standard classes that cannot be serialized: one example is the Swing JTextArea class (as of Swing 1.1). This has frequently been logged as a bug against Swing. Until this is fixed, the only way one of these objects can be used by a service is to create it on the client.

Note  

Swing is the set of user interface classes introduced as part of the Java Foundation Classes in JDK 1.2

What Files Need to Be Where?

For this implementation, the client needs to know the NameEntry interface class.

The server needs to know the class files for

  • NameEntry
  • Server2
  • NameEntryImpl2
  • NameEntryImpl2$1

The last class in the list is an anonymous class that acts as the WindowListener. The class file is produced by the compiler. In the naive implementation earlier in the chapter, this part of the code was commented out for simplicity.

The HTTP server needs to know the class files for

  • NameEntryImpl2
  • NameEntryImpl2$1

Using Multiple Class Files

Apart from the standard classes and a common interface, the previous implementations just used a single class that was uploaded to the lookup service and then passed on to the client. A more realistic situation might require the uploaded service to access a number of other classes that could not be expected to be on the client machine. That is, the server might upload an object from a single class to the lookup service and from there to a client. However, when the object runs, it needs to create other objects using class files that are not known to the client.

For example, the listener object of the last implementation could belong to a separate NameHandler class. The code looks like this:

 package complex; import java.awt.*; import java.awt.event.*; import javax.swing.*; import com.sun.jini.lookup.JoinManager; import net.jini.core.lookup.ServiceID; import com.sun.jini.lookup.ServiceIDListener; import com.sun.jini.lease.LeaseRenewalManager; /**  * NameEntryImpl3.java  */ public class NameEntryImpl3 implements common.NameEntry,                                        java.io.Serializable {     public NameEntryImpl3() {     }     public void show() {         Frame fr = new Frame("Name Entry");         fr.addWindowListener(new WindowAdapter() {             public void windowClosing(WindowEvent e) {System.exit(0);}             public void windowOpened(WindowEvent e) {}});         fr.setLayout(new BorderLayout());         Label label = new Label("Name");         fr.add(label, BorderLayout.WEST);         TextField name = new TextField(20);         fr.add(name, BorderLayout.CENTER);         name.addActionListener(new NameHandler());         fr.pack();         fr.show();     } } // NameEntryImpl3 class NameHandler implements ActionListener {     /**      * method invoked on pressing <return> in the TextField      */     public void actionPerformed(ActionEvent evt) {         System.out.println("Name was: " + evt.getActionCommand());     } } // NameHandler 

This version of the user interface implementation uses a NameHandler class that only exists on the server machine. When the client attempts to deserialize the NameEntryImpl3 instance, it will fail to find this class and be unable to complete deserialization. How is this resolved? Well, in the same way as before, by making it available through the HTTP server.

Figure 13-4 shows which instances are running in which JVM.

click to expand
Figure 13-4: JVM objects for multiple class files implementation

What Files Need to Be Where?

The client needs to know the NameEntry interface class.

The server needs to know the class files for

  • NameEntry
  • Server3
  • NameEntryImpl3
  • NameEntryImpl3$1
  • NameHandler

The NameHandler class file is another one produced by the compiler.

The HTTP server needs to know the class files for

  • NameEntryImpl3
  • NameEntryImpl3$1
  • NameHandler
  


A Programmer[ap]s Guide to Jini Technology
A Programmer[ap]s Guide to Jini Technology
ISBN: 1893115801
EAN: N/A
Year: 2000
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net