JoinManager
public class JoinManager {
public JoinManager(Object obj,
Entry[] attrSets,
ServiceIDListener callback,
DiscoveryManagement discoverMgr,
LeaseRenewalManager leaseMgr)
throws IOException;
public JoinManager(Object obj,
Entry[] attrSets,
ServiceID serviceID,
DiscoveryManagement discoverMgr,
LeaseRenewalManager leaseMgr)
throws IOException;
}
Button
generates an ActionEvent
PropertyChange
events
for Java beans
public interface RemoteEventListener {
public void notify(RemoteEvent evt) throws ...:
}
public class RemoteEvent {
public long getID(); // distinguish event types
public long getSequenceNumber(); // like a timestamp
public MarshalledObject getRegistrationObject(); // handback
addActionListener()
,
addPropertyChangeListener()
, etc. Each object must look
after its own listener list and decide when and how to call it. But...
public class EventRegistration {
public EventRegistration(long eventID, Object source,
Lease lease, long seqNum);
public long getID();
public Object getSource();
public Lease getLease();
public long getSequenceNumber();
}
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);
}
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 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);
}
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);
}
}
echo()
is called, a change event is isued and
sent to listeners
Timer
interface needs methods to add/remove
event listeners
ServiceDiscoveryManager
rmic
ValidityListener
runvalidity:
[exec] Service appeared: class service.TickerTimer_Stub
[exec] Service appeared: class service.ComputerTimer_Stub
[exec] Got event from service.TickerTimer_Stub[RemoteStub [ref: [endpoint:[192.168.1.11:32986](remote),objID:[d42d08:fc9d2377fd:-8000, 0]]]]
[exec] value is valid
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
JFrame
then
JFrame
, with all
client-side properties
Entry
field along with the service
This is all new
This is all new. It puts a label with the current time and a Refresh button inside a frame
rmic
JFrame
class must be
loaded since it is a return type. Otherwise an exception will be thrown
MarshalledObject
Two clocks running with their "local" UIs on the right and their "remote UIs" on the left look like
public class UIDescriptor extends AbstractEntry {
public String role;
public String toolkit;
public Set attributes;
public MarshalledObject factory;
public UIDescriptor();
public UIDescriptor(String role, String toolkit,
Set attributes, MarshalledObject factory);
public final Object getUIFactory(ClassLoader parentLoader)
throws IOException, ClassNotFoundException;
}
MarshalledObject
and all Java's
should know this class
toolkit
field is a string that says what UI classes will
be needed
JFrame
, a
JDialog
or a JPanel
? This info is stored
in the attributes
role
public interface net.jini.lookup.ui.MainUI {
String ROLE = "net.jini.lookup.ui.MainUI";
}
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
Dialog
JFrame
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);
}
Set attribs = new HashSet();
Set typeNames = new HashSet();
typeNames.add(JFrameFactory.TYPE_NAME);
attribs.add(new UIFactoryTypes(typeNames));
JFrameFactory
, to avoid this possible download.
MarshalledObject
,
which should be present in all Java machines
MarshalledObject factory = null;
try {
factory = new MarshalledObject(new FileClassifierFrameFactory());
} catch(Exception e) {
}
toolkit
and the factory type are sent
in the UIDescriptor, so the client can figure out if it can handle
the type before unmarshalling it
FrameFactory
factory for a
which can generate a Frame
for a
FileClassifierUI
could be
FileClassifierFrame
could look like
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 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