previous chapter table of contents next chapter

Using Other Services

In all the examples so far, a proxy has been created in a server and registered with a lookup service. Meanwhile, a service backend has usually been left behind in the server to handle calls from the proxy. However, there may be no need for the service to exist on the server, and the proxy could make use of other services elsewhere. This may be subject to security restrictions imposed by the client, which may disallow connections to some hosts .

In this section, we shall give an example of using a non-Jini service on another host. Recently an Australian, Pat Farmer, attempted to set a world record for jogging the longest distance. While he was running around, I became involved in a small project to broadcast his heartbeat live to the Web; a heart monitor was attached to him, which talked via an RS232 link to a mobile phone he was carrying. This did a data transfer to a program running at http://www.micromed.com.au located at the Gold Coast, which forwarded the data to a machine at the Distributed Systems Technology Centre (DSTC) in Brisbane. This ran a Web server delivering an applet, and the applet talked back to a server on the DSTC machine, which sent out the data to each applet as it was received from the heart monitor.

Now that the experiment is over, the broadcast data is sitting as a file at http://www.micromed.com.au/patfarmer/v2/patfhr.ecg , and it can be viewed on the applet from http://www.micromed.com.au/patfarmer/v2/heart.html . We can make it into a Jini service as follows :

  1. Create a service that we can locate using the service type ("display a heart monitor trace") and information about it, such as whose heart trace it is showing.
  2. Have the service connect to an HTTP address encoded into the service by its constructor (or other means), and read from this and display the contents, assuming it is heart cardiograph data.
  3. The information about whose trace it is can be given by a Name entry.

The client shows what you see in Figure 9-9. The break towards the right-hand side shows where the current trace is being written (it scans from left to right, over-writing as it goes). Cardiologists do not seem to be concerned about the lack of horizontal or vertical scales , as long as the trace is physically the right size !

click to expand
Figure 9-9: Heart monitor trace service

The heart monitor service can be regarded in a couple of ways:

  • It is a full-blown service uploaded to the client that just happens to use an external data source supplied from an HTTP server.
  • It is a "fat" proxy to the HTTP service, and it acts as a client to this service by displaying the data.

Many other non-RMI services can be built that act in this "fat proxy" style.

Heart Interface

The Heart interface only has one method, and that is to show() the heart trace in some manner:

 /**  * Heart.java  */ package heart; public interface Heart extends java.io.Serializable {     public void show(); } // Heart 


The HeartServer is similar to the method discussed in Chapter 8, of uploading a complete implementation of the service. This service, of type HeartImpl , is primed with a URL identifying where the heart data is stored. An HTTP server will later deliver this data.

This implementation is enough to locate the service. However, rather than just getting anyone 's heart data, a client may wish to search for a particular person's data. This can be done by adding a Name entry as additional information about the service. A server that exports the complete service, plus the entry information, is as follows:

 package heart; import java.rmi.RMISecurityManager; import net.jini.discovery.LookupDiscovery; import net.jini.discovery.DiscoveryListener; import net.jini.discovery.DiscoveryEvent; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.lookup.ServiceItem; import net.jini.core.lookup.ServiceRegistration; import net.jini.core.lease.Lease; // import com.sun.jini.lease.LeaseRenewalManager; // import com.sun.jini.lease.LeaseListener; // import com.sun.jini.lease.LeaseRenewalEvent; import net.jini.lease.LeaseRenewalManager; import net.jini.lease.LeaseListener; import net.jini.lease.LeaseRenewalEvent; import net.jini.core.entry.Entry; import net.jini.lookup.entry.Name; /**  * HeartServer.java  */ public class HeartServer implements DiscoveryListener,                                              LeaseListener {     protected LeaseRenewalManager leaseManager = new LeaseRenewalManager();     public static void main(String argv[]) {         new HeartServer();         // keep server running forever to         // - allow time for locator discovery and         // - keep re-registering the lease         Object keepAlive = new Object();         synchronized(keepAlive) {             try {                 keepAlive.wait();             } catch(InterruptedException e) {                 // do nothing             }         }     }     public HeartServer() {         LookupDiscovery discover = null;         try {             discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);         } catch(Exception e) {             System.err.println(e.toString());             System.exit(1);         }         discover.addDiscoveryListener(this);     }     public void discovered(DiscoveryEvent evt) {         ServiceRegistrar[] registrars = evt.getRegistrars();         for (int n = 0; n < registrars.length; n++) {             ServiceRegistrar registrar = registrars[n];             ServiceItem item = new ServiceItem(null,                                                new HeartImpl("http://_   www.micromed.com.au/patfarmer/v2/patfhr.ecg");                                              new Entry[] {new Name("Pat Farmer")});             ServiceRegistration reg = null;             try {                 reg = registrar.register(item, Lease.FOREVER);             } catch(java.rmi.RemoteException e) {                 System.err.println("Register exception: " + e.toString());                 continue;             }             System.out.println("service registered");             // set lease renewal in place             leaseManager.renewUntil(reg.getLease(), Lease.FOREVER, this);         }     }     public void discarded(DiscoveryEvent evt) {     }     public void notify(LeaseRenewalEvent evt) {         System.out.println("Lease expired " + evt.toString());     } } // HeartServer 


The client searches for a service implementing the Heart interface, with the additional requirement that it be for a particular person. Once it has this, the client just calls the show() method on the service to display this in some manner:

 package heart; import heart.Heart; import java.rmi.RMISecurityManager; import net.jini.discovery.LookupDiscovery; import net.jini.discovery.DiscoveryListener; import net.jini.discovery.DiscoveryEvent; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.lookup.ServiceTemplate; import net.jini.core.entry.Entry; import net.jini.lookup.entry.Name; /**  * HeartClient.java  */ public class HeartClient implements DiscoveryListener {     public static void main(String argv[]) {         new HeartClient();         // stay around long enough to receive replies         try {             Thread.currentThread().sleep(1000000L);         } catch(java.lang.InterruptedException e) {             // do nothing         }     }     public HeartClient() {         System.setSecurityManager(new RMISecurityManager());           LookupDiscovery discover = null;         try {             discover = new LookupDiscovery(LookupDiscovery.ALL_GROUPS);         } catch(Exception e) {             System.err.println(e.toString());             System.exit(1);         }         discover.addDiscoveryListener(this);     }     public void discovered(DiscoveryEvent evt) {         ServiceRegistrar[] registrars = evt.getRegistrars();         Class [] classes = new Class[] {Heart.class};         Entry [] entries = new Entry[] {new Name("Pat Farmer")};         Heart heart = null;         ServiceTemplate template = new ServiceTemplate(null, classes,                                                        entries)         for (int n = 0; n < registrars.length; n++) {             System.out.println("Service found");             ServiceRegistrar registrar = registrars[n];             try {                 heart = (Heart) registrar.lookup(template);             } catch(java.rmi.RemoteException e) {                 e.printStackTrace();                 continue;             }             if (heart == null) {                 System.out.println("Heart null");                 continue;             }             heart.show();             System.exit(0);         }     }     public void discarded(DiscoveryEvent evt) {         // empty     } } // HeartClient 

Heart Implementation

The HeartImpl class opens a connection to an HTTP server and requests delivery of a file. Heart data needs to be displayed at a reasonable rate, so it reads, draws, and sleeps, in a loop. It acts as a fat client to the HTTP server, displaying the data in a suitable format (in this case, it uses HTTP as a transport mechanism for data delivery). As a "client-aware" service, it customizes this delivery to the characteristics of the client platform, just occupying a "reasonable" amount of screen space and using local colors and fonts.

 /**  * HeartImpl.java  */ package heart; import java.io.*; import java.net.*; import java.awt.*; public class HeartImpl implements Heart {     protected String url;     /*      * If we want to run it standalone we can use this      */     public static void main(String argv[]) {         HeartImpl impl =             new HeartImpl("file:/home/jan/projects/jini/doc/heart/TECG3.ecg");         impl.show();     }     public HeartImpl(String u) {         url = u;     }     double[] points = null;     Painter painter = null;       String heartRate = "--";     public void setHeartRate(int rate) {         if (rate > 20 && rate <= 250) {             heartRate = "Heart Rate: " + rate;         } else {             heartRate = "Heart Rate: --";         }     }     public void quit(Exception e, String s) {         System.err.println(s);         e.printStackTrace();         System.exit(1)     }     public void show() {         int SAMPLE_SIZE = 300 / Toolkit.getDefaultToolkit().                                                     getScreenResolution();         Dimension size = Toolkit.getDefaultToolkit().                                  getScreenSize();         int width = (int) size.getWidth();         // capture points in an array, for redrawing in app if needed         points = new double[width * SAMPLE_SIZE];         for (int n = 0; n < width; n++) {             points[n] = 1;         }         URL dataUrl = null;         InputStream in = null;           try {             dataUrl = new URL(url);             in = dataUrl.openStream();         } catch (Exception ex) {             quit(ex, "connecting to ECG server");             return;         }         Frame frame = new Frame("Heart monitor");         frame.setSize((int) size.getWidth()/2, (int) size.getHeight()/2);         try {             painter = new Painter(this, frame, in);             painter.start();         } catch (Exception ex) {             quit(ex, "fetching data from ECG server");             return;         }         frame.setVisible(true);     } } // HeartImpl class Painter extends Thread {     static final int DEFAULT_SLEEP_TIME = 25; // milliseconds     static final int CLEAR_AHEAD = 15;     static final int MAX = 255;     static final int MIN = 0;     final int READ_SIZE = 10;     protected HeartImpl app;     protected Frame frame;     protected InputStream in;     protected final int RESOLUTION = Toolkit.getDefaultToolkit().                                                     getScreenResolution();     protected final int UNITS_PER_INCH = 125;     protected final int SAMPLE_SIZE = 300 / RESOLUTION;     protected int sleepTime = DEFAULT_SLEEP_TIME;     public Painter(HeartImpl app, Frame frame, InputStream in) throws Exception {         this.app = app;         this.frame = frame;         this.in = in;     }     public void run() {         while (!frame.isVisible()) {            try {                Thread.sleep(1000);            } catch(Exception e) {                // ignore            }         }         int height = frame.getSize().height;         int width = frame.getSize().width;         int x = 1; // start at 1 rather than 0 to avoid drawing initial line                    // from 128 .. 127         int magnitude;         int nread;         int max = MIN; // top bound of magnitude         int min = MAX;  // bottom bound of magnitude         int oldMax = MAX + 1;         byte[] data = new byte[READ_SIZE];         Graphics g = frame.getGraphics();         g.setColor(Color.red);         try {             Font f = new Font("Serif", Font.BOLD, 20);             g.setFont(f);         } catch (Exception ex) {             // ....         }         try {             boolean expectHR = false;   // true ==> next byte is heartrate             while ((nread = in.read(data)) != 1) {                 for (int n = 0; n < nread; n++) {                     int thisByte = data[n] & 0xFF;                     if (expectHR) {                         expectHR = false;                         app.setHeartRate(thisByte);                         continue;                     } else if (thisByte == 255) {                         expectHR = true;                         continue;                     }                     // we are reading bytes, from 127..128                     // convert to unsigned                     magnitude = thisByte;                     // then convert to correct scale                     magnitude -= 128;                     // scale and convert to window coord from the top downwards                     int y = ((128 - magnitude) * RESOLUTION) / UNITS_PER_INCH;                     app.points[x] = y;                     // draw only on multiples of sample size                     if (x % SAMPLE_SIZE == 0) {                         // delay to draw at a reasonable rate                         Thread.sleep(sleepTime);                         int x0 = x / SAMPLE_SIZE;                         g.clearRect(x0, 0, CLEAR_AHEAD, height);                         if (oldMax != MAX + 1) {                             g.drawLine(x0-1, oldMax, x0, min);                         }                         g.drawLine(x0, min, x0, max);                         oldMax = max;                         min = 1000;                         max = 1000;                         if (app.heartRate != null) {                             g.setColor(Color.black);                             g.clearRect(0, 180, 200, 100);                             g.drawString(app.heartRate, 0, 220);                             g.setColor(Color.red);                         }                     } else {                         if (y > max) max = y;                         if (y < min) min = y;                     }                     if (++x >= width * SAMPLE_SIZE) {                         x = 0;                     }                 }             }         } catch(Exception ex) {             if (! (ex instanceof SocketException)) {                 System.out.println("Applet quit -- got " + ex);             }         } finally {             try {                 if (in != null) {                     in.close();                     in = null;                 }             } catch (Exception ex) {                 // hide it             }         }     } } 

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

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