Even more Jini

JOIN MANAGER


Join Manager


Service using Join Manager


REMOTE EVENTS


Event models


Remote events


A single listener list

A list with only one listener on it can be done by

protected RemoteEventListener listener = null;

public EventRegistration addRemoteListener(RemoteEventListener listener)
       throws java.util.TooManyListenersException {
    if (this.listener == null {
        this.listener = listener;
    } else {
        throw new java.util.TooManyListenersException();
    }
    return new EventRegistration(0L, this, null, 0L);
}

Single listener event notification

The source object can send an event to its listener by

protected void fireNotify(long eventID,
                          long seqNum) {
    if (listener == null) {
        return;
    }

    RemoteEvent remoteEvent = new RemoteEvent(this, eventID, 
                                              seqNum, null);
    listener.notify(remoteEvent);
}

A multiple listener list

A list with multiple listeners on it can be done by

protected Vector listeners = new Vector();

public EventRegistration addRemoteListener(RemoteEventListener listener) {
    listeners.add(listener);
    return new EventRegistration(0L, this, null, 0L);
}


Multiple listener event notification

The source object can send an event to its listener list by

protected void fireNotify(long eventID,
                          long seqNum) {
    RemoteEvent remoteEvent = new RemoteEvent(this, eventID, 
                                              seqNum, null);
    for (int n = 0; n < listeners.size(); n++) {
        RemoteListener listener = (RemoteListener) listeners.elementAt(n);
        listener.notify(remoteEvent);
    }
}

Remote events using echo

Echo interface

Echo interface implementation

Echo server

Echo client

Echo listener


Flashing clocks


Timer


Ticker Timer


Listener


Validity Listener


Deployment


Mutable file classifier

Allow the file classifier to change its list of MIME type mappings, and notify listeners when it does so

import java.rmi.server.UnicastRemoteObject;
import java.rmi.MarshalledObject;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.event.RemoteEvent;
import net.jini.core.event.EventRegistration;
import java.rmi.RemoteException;
import net.jini.core.event.UnknownEventException ;

import javax.swing.event.EventListenerList;

import common.MIMEType;
import common.MutableFileClassifier;
import java.util.Map;
import java.util.HashMap;

public class FileClassifierImpl extends  UnicastRemoteObject 
                                implements RemoteFileClassifier {

    /**
     * Map of String extensions to MIME types
     */
    protected Map map = new HashMap();

    /**
     * Listeners for change events
     */
    protected EventListenerList listenerList = new EventListenerList();

    public MIMEType getMIMEType(String fileName) 
	throws java.rmi.RemoteException {

	MIMEType type;
	String fileExtension;
	int dotIndex = fileName.lastIndexOf('.');

	if (dotIndex == -1 || dotIndex + 1 == fileName.length()) {
	    // can't find suitable suffix
	    return null;
	}

	fileExtension= fileName.substring(dotIndex + 1);
	type = (MIMEType) map.get(fileExtension);
	return type; 
    }

    public void addType(String suffix, MIMEType type)
	throws java.rmi.RemoteException {
	map.put(suffix, type);
	fireNotify(ADD_TYPE);
    }

    public void removeMIMEType(String suffix, MIMEType type)
	throws java.rmi.RemoteException {
	if (map.remove(suffix) != null) {
	    fireNotify(REMOVE_TYPE);
	}
    }

    public EventRegistration addRemoteListener(RemoteEventListener listener)
	throws java.rmi.RemoteException {
	listenerList.add(RemoteEventListener.class, listener);

	return new EventRegistration(0, this, null, 0);
    }

    // Notify all listeners that have registered interest for
    // notification on this event type.  The event instance 
    // is lazily created using the parameters passed into 
    // the fire method.

    protected void fireNotify(long eventID) {
	RemoteEvent remoteEvent = null;
	
	// Guaranteed to return a non-null array
	Object[] listeners = listenerList.getListenerList();
	
	// Process the listeners last to first, notifying
	// those that are interested in this event
	for (int i = listeners.length - 2; i >= 0; i -= 2) {
	    if (listeners[i] == RemoteEventListener.class) {
		RemoteEventListener listener = (RemoteEventListener) listeners[i+1];
		if (remoteEvent == null) {
		    remoteEvent = new RemoteEvent(this, eventID, 
						  0L, null);
		}
		try {
		    listener.notify(remoteEvent);
		} catch(UnknownEventException e) {
		    e.printStackTrace();
		} catch(RemoteException e) {
		    e.printStackTrace();
		}
	    }
	}
    }    

    public FileClassifierImpl()  throws java.rmi.RemoteException {
	// load a predefined set of MIME type mappings
	map.put("gif", new MIMEType("image", "gif"));
	map.put("jpeg", new MIMEType("image", "jpeg"));
	map.put("mpg", new MIMEType("video", "mpeg"));
	map.put("txt", new MIMEType("text", "plain"));
	map.put("html", new MIMEType("text", "html"));
    }
} // FileClassifierImpl


USER INTERFACE

User interface

UI or factory?

UI must know the proxy

Factory as Entry

UIFactory interface

This is all new

UIFactory implementation

This is all new. It puts a label with the current time and a Refresh button inside a frame

Service with UI

UI client

Deployment

Problems with this approach

Run

Two clocks running with their "local" UIs on the right and their "remote UIs" on the left look like

(Don't worry that the times are all wrong - we only get the time at one instant.)


User interface - old stuff

Reasons for UIDescriptor


Attributes of UIDescriptor

role
Each UI may have a speecific role, such as system administrator UI, novice UI, general UI. The only UI role currently defined is


public interface net.jini.lookup.ui.MainUI {
    String ROLE = "net.jini.lookup.ui.MainUI";
}   
      
toolkit
A UI built using AWT components will require the java.awt toolkit to be present; using Swing will require the javax.swing toolkit; using MIDP would require the javax.microedition.lcdui. The toolkit is set to one of these strings, so that the client can do a quick check on what the UI requires
factory
The UI will generate objects of a certain type that can be used by the client. These could be e.g. The factory is a generator of one of these objects. It will be sent as a MarshalledObject (see later)

There is a factory interface for each type e.g


package net.jini.lookup.ui.factory;

import javax.swing.JFrame;

public interface JFrameFactory {
    String TOOLKIT = "javax.swing";
    String TYPE_NAME = "net.jini.lookup.ui.factory.JFrameFactory";

    JFrame getJFrame(Object roleObject);
}
      
attributes
These can carry any additional information about a UI. In particular, info about the factory type should be included

        Set attribs = new HashSet();
        Set typeNames = new HashSet();
        typeNames.add(JFrameFactory.TYPE_NAME);
        attribs.add(new UIFactoryTypes(typeNames));
      


Marshalled factory object


Factory implementation


FileClassifierFrame


Server

A server to deliver both a FileClassifier and a UI for it is


import complete.FileClassifierImpl;

import net.jini.lookup.JoinManager;
import net.jini.core.lookup.ServiceID;
import net.jini.discovery.LookupDiscovery;
import net.jini.core.lookup.ServiceRegistrar;
import java.rmi.RemoteException;
import net.jini.lookup.ServiceIDListener;
import net.jini.lease.LeaseRenewalManager;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.DiscoveryListener;
import net.jini.core.entry.Entry;

import net.jini.lookup.ui.MainUI;
import net.jini.lookup.ui.factory.FrameFactory;
import net.jini.lookup.entry.UIDescriptor;
import net.jini.lookup.ui.attribute.UIFactoryTypes;

import java.rmi.MarshalledObject;
import java.io.IOException;
import java.util.Set;
import java.util.HashSet;

public class FileClassifierServer 
    implements ServiceIDListener {
    
    public static void main(String argv[]) {
        new FileClassifierServer();

        // stay around forever
        Object keepAlive = new Object();
        synchronized(keepAlive) {
            try {
                keepAlive.wait();
            } catch(InterruptedException e) {
                // do nothing
            }
        }
    }

    public FileClassifierServer() {

        JoinManager joinMgr = null;

        // The typenames for the factory
        Set typeNames = new HashSet();
        typeNames.add(FrameFactory.TYPE_NAME);

        // The attributes set
        Set attribs = new HashSet();
        attribs.add(new UIFactoryTypes(typeNames));

        // The factory
        MarshalledObject factory = null;
        try {
            factory = new MarshalledObject(new FileClassifierFrameFactory());
        } catch(Exception e) {
            e.printStackTrace();
            System.exit(2);
        }
        UIDescriptor desc = new UIDescriptor(MainUI.ROLE,
                                             FileClassifierFrameFactory.TOOLKIT,
                                             attribs,
                                             factory);

        Entry[] entries = {desc};

        try {
            LookupDiscoveryManager mgr = 
                new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
                                           null /* unicast locators */,
                                           null /* DiscoveryListener */);
            joinMgr = new JoinManager(new FileClassifierImpl(), /* service */
                                      entries /* attr sets */,
                                      this /* ServiceIDListener*/,
                                      mgr /* DiscoveryManagement */,
                                      new LeaseRenewalManager());
        } catch(Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }

    public void serviceIDNotify(ServiceID serviceID) {
        // called as a ServiceIDListener
        // Should save the id to permanent storage
        System.out.println("got service ID " + serviceID.toString());
    }
    
} // FileClassifierServer


A client

A client to get a FileClassifier and use its UI is


import common.FileClassifier;
import common.MIMEType;

import java.rmi.RMISecurityManager;
import net.jini.discovery.LookupDiscovery;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lookup.ServiceDiscoveryManager;
import net.jini.core.lookup.ServiceItem;
import net.jini.lease.LeaseRenewalManager;
import net.jini.core.entry.Entry;

import net.jini.lookup.ui.MainUI;
import net.jini.lookup.ui.factory.FrameFactory;
import net.jini.lookup.entry.UIDescriptor;
import net.jini.lookup.ui.attribute.UIFactoryTypes;

import java.awt.*;
import javax.swing.*;

import java.util.Set;
import java.util.Iterator;
import java.net.URL;

/**
 * TestFrameUI.java
 */

public class TestFrameUI {

    private static final long WAITFOR = 100000L;

    public static void main(String argv[]) {
        new TestFrameUI();

        // stay around long enough to receive replies
        try {
            Thread.currentThread().sleep(2*WAITFOR);
        } catch(java.lang.InterruptedException e) {
            // do nothing
        }
    }

    public TestFrameUI() {
        ServiceDiscoveryManager clientMgr = null;

        System.setSecurityManager(new RMISecurityManager());

        try {
            LookupDiscoveryManager mgr =
                new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
                                           null /* unicast locators */,
                                           null /* DiscoveryListener */);
            clientMgr = new ServiceDiscoveryManager(mgr, 
                                                new LeaseRenewalManager());
        } catch(Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
  
        Class [] classes = new Class[] {FileClassifier.class};
        UIDescriptor desc = new UIDescriptor(MainUI.ROLE, 
                                             FrameFactory.TOOLKIT, 
                                             null, null);
        Entry [] entries = {desc};

        ServiceTemplate template = new ServiceTemplate(null, classes, 
                                                       entries);

        ServiceItem item = null;
        try {
            item = clientMgr.lookup(template, 
                                    null, /* no filter */ 
                                    WAITFOR /* timeout */);
        } catch(Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
        
        if (item == null) {
            // couldn't find a service in time
            System.out.println("no service");
            System.exit(1);
        }

        // We now have a service that plays the MainUI role and
        // uses the FrameFactory toolkit of "java.awt".
        // We now have to find if there is a UIDescriptor
        // with a Factory generating an AWT Frame
        checkUI(item);
    }

    private void checkUI(ServiceItem item) {
        // Find and check the UIDescriptor's
        Entry[] attributes = item.attributeSets;
        for (int m = 0; m < attributes.length; m++) {
            Entry attr = attributes[m];
            if (attr instanceof UIDescriptor) {
                // does it deliver an AWT Frame?
                checkForAWTFrame(item, (UIDescriptor) attr);
            }
        }
    }
   
    private void checkForAWTFrame(ServiceItem item, UIDescriptor desc) {
        Set attributes = desc.attributes;
        Iterator iter = attributes.iterator();
        while (iter.hasNext()) {
            // search through the attributes, to find a UIFactoryTypes
            Object obj = iter.next();
            if (obj instanceof UIFactoryTypes) {
                UIFactoryTypes types = (UIFactoryTypes) obj;
                // see if it produces an AWT Frame Factory
                if (types.isAssignableTo(FrameFactory.class)) {
                    FrameFactory factory = null;
                    try {
                        factory = (FrameFactory) desc.getUIFactory(this.getClass().
                                                                  getClassLoader());
                    } catch(Exception e) {
                        e.printStackTrace();
                        continue;
                    }

                    Frame frame = factory.getFrame(item); 
                    frame.setVisible(true);
                } 
            }
        }
    }
        

} // TestFrameUI

Summary



Jan Newmarch (http://jan.newmarch.name)
jan@newmarch.name
Last modified: Mon May 24 14:22:33 EST 2004
Copyright ©Jan Newmarch