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;
}
/**
* FileClassifierServer2.java
*/
public class FileClassifierServer2
implements ServiceIDListener {
public static void main(String argv[]) {
new FileClassifierServer2();
// stay around forever
Object keepAlive = new Object();
synchronized(keepAlive) {
try {
keepAlive.wait();
} catch(InterruptedException e) {
// do nothing
}
}
}
public FileClassifierServer2() {
JoinManager joinMgr = null;
try {
LookupDiscoveryManager mgr =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null, // unicast locators
null); // DiscoveryListener
joinMgr = new JoinManager(new FileClassifierImpl(), // service
null, // 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
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
package service;
import java.rmi.*;
// Change start
import net.jini.core.event.*;
// Change end
// Change start
import net.jini.core.event.*;
// Change end
public interface Echo extends Remote {
// There isn't any state with an echo service, which makes it a bit
// hard to generate events. Implausibly, instead we have an event type
// TEXT_CHANGED, which changes each time echo() is called, and an event
// is generated each time a change occurs
// Change start from echo without events
static final int TEXT_CHANGED = 1;
public EventRegistration addEventListener(RemoteEventListener l)
throws RemoteException;
/* If we have an event, we must be able to find what changed
*/
public String getChangedText()
throws RemoteException;
// Change end
String echo(String in) throws RemoteException;
}
package server;
import service.*;
// Change start
import net.jini.core.event.*;
import java.util.Vector;
import java.rmi.RemoteException;
// Change end
public class EchoImpl implements Echo {
// Change start
private Vector listeners = new Vector();
private int seqNum = 0; // event sequence counter
private String text = "";
// Change end
public EchoImpl() {
}
public String echo(String in) {
// Change start
fireNotify(TEXT_CHANGED);
text = in;
// Change end
return "Echo: " + in;
}
// Change start
public String getChangedText() {
return text;
}
// Change end
// Change start
public EventRegistration addEventListener(RemoteEventListener listener) {
listeners.add(listener);
return new EventRegistration(0L, this, null, 0L);
}
// Change end
// Change start
protected void fireNotify(long eventID) {
RemoteEvent remoteEvent = new RemoteEvent(this, eventID, seqNum++, null);
for (int n = 0; n < listeners.size(); n++) {
RemoteEventListener listener = (RemoteEventListener) listeners.elementAt(n);
try {
listener.notify(remoteEvent);
} catch(UnknownEventException e) {
// ignore
} catch(RemoteException e) {
// ignore
}
}
}
// Change end
}
package server;
import service.*;
import java.io.*;
import java.rmi.*;
import java.rmi.server.ExportException;
import net.jini.export.*;
import net.jini.jrmp.JrmpExporter;
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;
public class EchoServer implements ServiceIDListener {
public static void main(String[] args) {
EchoServer svr = new EchoServer();
// This is a hack to keep the server alive.
Object keepAlive = new Object();
synchronized(keepAlive) {
try {
// Wait for a "notify" from another thread
// that will never be sent.
// So we stay alive for ever
keepAlive.wait();
} catch(java.lang.InterruptedException e) {
// do nothing
}
}
}
public EchoServer() {
System.setSecurityManager(new RMISecurityManager());
Exporter exporter = new JrmpExporter();
Remote echo = new EchoImpl();
// export an Echo proxy
Remote proxy = null;
try {
proxy = exporter.export(echo);
} catch(ExportException e) {
System.exit(1);
}
// Register with all lookup services as they are discovered
JoinManager joinMgr = null;
try {
LookupDiscoveryManager mgr =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null, // unicast locators
null); // DiscoveryListener
joinMgr = new JoinManager(proxy, // service proxy
null, // attr sets
this, // ServiceIDListener
mgr, // DiscoveryManager
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());
}
}
package client;
import service.*;
import java.io.*;
import java.rmi.*;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.lookup.ServiceItem;
import net.jini.lookup.ServiceDiscoveryManager;
import net.jini.lookup.ServiceDiscoveryListener;
import net.jini.lookup.ServiceDiscoveryEvent;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.LookupCache;
public class Client implements ServiceDiscoveryListener {
private LookupCache cache = null;
public static void main(String[] args) {
new Client();
}
public Client() {
System.setSecurityManager(new RMISecurityManager());
// Build a cache of all discovered echo services and monitor changes
ServiceDiscoveryManager serviceMgr = null;
Class [] classes = new Class[] {Echo.class};
ServiceTemplate template = new ServiceTemplate(null, classes,
null);
try {
LookupDiscoveryManager mgr =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null, // unicast locators
null); // DiscoveryListener
serviceMgr = new ServiceDiscoveryManager(mgr,
new LeaseRenewalManager());
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
try {
cache = serviceMgr.createLookupCache(template,
null, // no filter
this); // listener
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
loop();
}
public void loop() {
for (int n = 0; n < 10; n++) {
System.out.println("Sending: hello " + n);
echoToAll("hello " + n);
try {
Thread.currentThread().sleep(1000);
} catch(Exception e) {
}
}
System.exit(0);
}
private void echoToAll(String txt) {
ServiceItem[] items = cache.lookup(null, Integer.MAX_VALUE);
for (int n = 0; n < items.length; n++) {
Echo svc = (Echo) items[n].service;
echoToOne(svc, txt);
}
}
private void echoToOne(Echo svc, String txt) {
String reply = null;
try {
reply = svc.echo(txt);
System.out.println(reply);
} catch(RemoteException e) {
System.out.println(e);
}
}
public void serviceAdded(ServiceDiscoveryEvent evt) {
// evt.getPreEventServiceItem() == null
ServiceItem postItem = evt.getPostEventServiceItem();
System.out.println("Service appeared: " +
postItem.service.getClass().toString());
}
public void serviceChanged(ServiceDiscoveryEvent evt) {
ServiceItem preItem = evt.getPostEventServiceItem();
ServiceItem postItem = evt.getPreEventServiceItem() ;
System.out.println("Service changed: " +
postItem.service.getClass().toString());
}
public void serviceRemoved(ServiceDiscoveryEvent evt) {
// evt.getPostEventServiceItem() == null
ServiceItem preItem = evt.getPreEventServiceItem();
System.out.println("Service disappeared: " +
preItem.service.getClass().toString());
}
}
package client;
import service.*;
import java.io.*;
import java.rmi.*;
import net.jini.discovery.LookupDiscovery;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.lookup.ServiceItem;
import net.jini.lookup.ServiceDiscoveryManager;
import net.jini.lookup.ServiceDiscoveryListener;
import net.jini.lookup.ServiceDiscoveryEvent;
import net.jini.lease.LeaseRenewalManager;
// Change start
import java.rmi.server.ExportException;
import net.jini.export.*;
import net.jini.jrmp.JrmpExporter;
import net.jini.core.event.*;
// Change end
import net.jini.lookup.LookupCache;
// Change start
public class ClientListener implements ServiceDiscoveryListener,
RemoteEventListener {
// Change end
private LookupCache cache = null;
// Change start
private Remote proxy;
// Change end
public static void main(String[] args) {
ClientListener cl = new ClientListener();
// Change start
// This is a hack to keep the server alive.
Object keepAlive = new Object();
synchronized(keepAlive) {
try {
// Wait for a "notify" from another thread
// that will never be sent.
// So we stay alive for ever
keepAlive.wait();
} catch(java.lang.InterruptedException e) {
// do nothing
}
}
// Change end
}
public ClientListener() {
System.setSecurityManager(new RMISecurityManager());
discoverOtherServices();
// When this registers as an event listener it gives an
// object to the service to call notify() on it
// The object must be a proxy for this
Exporter exporter = new JrmpExporter();
try {
proxy = exporter.export(this);
} catch(ExportException e) {
System.exit(1);
}
}
private void discoverOtherServices() {
// Build a cache of all discovered echo services and monitor changes
ServiceDiscoveryManager serviceMgr = null;
Class [] classes = new Class[] {Echo.class};
ServiceTemplate template = new ServiceTemplate(null, classes,
null);
try {
LookupDiscoveryManager mgr =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null, // unicast locators
null); // DiscoveryListener
serviceMgr = new ServiceDiscoveryManager(mgr,
new LeaseRenewalManager());
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
try {
cache = serviceMgr.createLookupCache(template,
null, // no filter
this); // listener
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public void serviceAdded(ServiceDiscoveryEvent evt) {
// evt.getPreEventServiceItem() == null
ServiceItem postItem = evt.getPostEventServiceItem();
System.out.println("Service appeared: " +
postItem.service.getClass().toString());
// Change start
Echo echo = (Echo) postItem.service;
try {
echo.addEventListener((RemoteEventListener) proxy);
} catch(RemoteException e) {
e.printStackTrace();
}
// Change end
}
public void serviceChanged(ServiceDiscoveryEvent evt) {
ServiceItem preItem = evt.getPostEventServiceItem();
ServiceItem postItem = evt.getPreEventServiceItem() ;
System.out.println("Service changed: " +
postItem.service.getClass().toString());
}
public void serviceRemoved(ServiceDiscoveryEvent evt) {
// evt.getPostEventServiceItem() == null
ServiceItem preItem = evt.getPreEventServiceItem();
System.out.println("Service disappeared: " +
preItem.service.getClass().toString());
}
// Change start
public void notify(RemoteEvent e) {
System.out.println("Echo service has been called ");
String text = null;
try {
Echo echoSrc = (Echo) e.getSource();
text = echoSrc.getChangedText();
System.out.println("Text changed to " + text);
} catch(RemoteException re) {
re.printStackTrace();
}
}
// Change end
}
Timer
interface needs methods to add/remove
event listeners
/**
* Timer service
*/
package service;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.Date;
import net.jini.core.event.*;
public interface Timer extends Remote {
// Change start from clock without events
public static final int VALID = 1; // event types
public static final int INVALID = 2;
// Change end
public void setTime(Date t) throws RemoteException;
public Date getTime() throws RemoteException;
public boolean isValidTime() throws RemoteException;
// Change start from clock without events
public EventRegistration addEventListener(RemoteEventListener l)
throws RemoteException;
// Change end
}
package service;
import java.util.Date;
import net.jini.core.event.*;
import java.util.Vector;
import java.rmi.RemoteException;
public class TickerTimer implements Timer {
private Date time;
private boolean isValid;
private Ticker ticker;
// Change start
private Vector listeners = new Vector();
private int seqNum = 0; // event sequence counter
// Change end
/**
* Constructor with no starting time has
* invalid timer and any time
*/
public TickerTimer() {
time = new Date(0);
isValid = false;
ticker = new Ticker(time);
ticker.start();
// Change start
fireNotify(INVALID, seqNum++); // valid has changed state
// Change end
}
public TickerTimer(Date t) {
time = t;
isValid = true;
ticker = new Ticker(time);
ticker.start();
// Change start
fireNotify(VALID, seqNum++); // valid has changed state
// Change end
}
public void setTime(Date t) {
System.out.println("Setting time to " + t);
time = t;
isValid = true;
if (ticker != null) {
ticker.stopRunning();
}
ticker = new Ticker(time);
ticker.start();
// Change start
fireNotify(VALID, seqNum++); // valid has changed state
// Change end
}
public Date getTime() {
return ticker.getTime();
}
public boolean isValidTime() {
if (isValid) {
return true;
} else {
return false;
}
}
// Change start
public EventRegistration addEventListener(RemoteEventListener listener) {
listeners.add(listener);
return new EventRegistration(0L, this, null, 0L);
}
// Change end
// Change start
protected void fireNotify(long eventID,
long seqNum) {
RemoteEvent remoteEvent = new RemoteEvent(this, eventID,
seqNum, null);
for (int n = 0; n < listeners.size(); n++) {
RemoteEventListener listener =
(RemoteEventListener) listeners.elementAt(n);
try {
listener.notify(remoteEvent);
} catch(UnknownEventException e) {
// ignore
} catch(RemoteException e) {
// ignore
}
}
}
// Change end
}
class Ticker extends Thread {
private Date time;
private boolean keepRunning = true;
public Ticker(Date t) {
time = t;
}
public Date getTime() {
return time;
}
public void run() {
while (keepRunning) {
try {
sleep(1000);
} catch(InterruptedException e) {
}
time = new Date(time.getTime() + 1000);
}
}
public void stopRunning() {
keepRunning = false;
}
}
ServiceDiscoveryManager
package client;
import service.*;
import java.io.*;
import java.util.Date;
import java.rmi.*;
import java.rmi.server.ExportException;
import net.jini.export.*;
import net.jini.jrmp.JrmpExporter;
import net.jini.discovery.LookupDiscovery;
import net.jini.core.lookup.ServiceRegistrar;
import java.rmi.RemoteException;
import net.jini.lookup.ServiceDiscoveryListener;
import net.jini.lookup.ServiceDiscoveryEvent;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.lookup.ServiceItem;
import net.jini.lookup.ServiceDiscoveryManager;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.LookupCache;
import net.jini.core.event.RemoteEvent;
import net.jini.core.event.RemoteEventListener;
public class ValidityListener implements ServiceDiscoveryListener,
RemoteEventListener {
private Timer timer;
private Remote proxy;
public static void main(String[] args) {
ValidityListener validListen = new ValidityListener();
// This is a hack to keep the server alive.
Object keepAlive = new Object();
synchronized(keepAlive) {
try {
// Wait for a "notify" from another thread
// that will never be sent.
// So we stay alive for ever
keepAlive.wait();
} catch(java.lang.InterruptedException e) {
// do nothing
}
}
}
public ValidityListener() {
System.setSecurityManager(new RMISecurityManager());
// When this registers as an event listener it gives an
// object to the service to call notify() on it
// The object must be a proxy for this
Exporter exporter = new JrmpExporter();
try {
proxy = exporter.export(this);
} catch(ExportException e) {
System.exit(1);
}
// Build a cache of all discovered clocks and monitor changes
ServiceDiscoveryManager serviceMgr = null;
LookupCache cache = null;
Class [] classes = new Class[] {Timer.class};
ServiceTemplate template = new ServiceTemplate(null, classes,
null);
try {
LookupDiscoveryManager mgr =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null, // unicast locators
null); // DiscoveryListener
serviceMgr = new ServiceDiscoveryManager(mgr,
new LeaseRenewalManager());
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
try {
cache = serviceMgr.createLookupCache(template,
null, // no filter
this); // listener
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
}
// Change start
// an event has arrived
public void notify(RemoteEvent evt) {
System.out.println("Got event from " + evt.getSource());
String value = evt.getID() == Timer.VALID ? "valid" : "invalid";
System.out.println(" value is " + value);
}
// Change end
public void serviceAdded(ServiceDiscoveryEvent evt) {
// evt.getPreEventServiceItem() == null
ServiceItem postItem = evt.getPostEventServiceItem();
System.out.println("Service appeared: " +
postItem.service.getClass().toString());
Timer timerSvc = (Timer) postItem.service;
// Change start
try {
// note the class cast: the proxy implements all the
// interfaces of its service
timerSvc.addEventListener((RemoteEventListener) proxy);
} catch(RemoteException e) {
e.printStackTrace();
}
// Change end
}
public void serviceChanged(ServiceDiscoveryEvent evt) {
ServiceItem preItem = evt.getPostEventServiceItem();
ServiceItem postItem = evt.getPreEventServiceItem() ;
System.out.println("Service changed: " +
postItem.service.getClass().toString());
}
public void serviceRemoved(ServiceDiscoveryEvent evt) {
// evt.getPostEventServiceItem() == null
ServiceItem preItem = evt.getPreEventServiceItem();
System.out.println("Service disappeared: " +
preItem.service.getClass().toString());
// should remove proxy from event listener list
}
}
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
package ui;
import service.*;
import net.jini.core.entry.Entry;
import service.*;
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.awt.*;
import java.util.Date;
import java.rmi.RemoteException;
public interface UIFactory extends Entry {
public JFrame createJFrame(Timer proxy);
}
This is all new. It puts a label with the current time and a Refresh button inside a frame
package ui;
import net.jini.core.entry.Entry;
import service.*;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JButton;
import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import java.rmi.RemoteException;
public class UIFactoryImpl implements UIFactory, ActionListener {
private Timer proxy;
private JLabel label;
public UIFactoryImpl() {
}
public JFrame createJFrame(Timer proxy) {
this.proxy = proxy;
JFrame frame = new JFrame();
Container contentPane = frame.getContentPane();
BorderLayout layout = new BorderLayout();
contentPane.setLayout(new BorderLayout());
Date now = null;
try {
now = proxy.getTime();
} catch(RemoteException e) {
e.printStackTrace();
}
label = new JLabel(now.toString());
contentPane.add(label, BorderLayout.CENTER);
JButton btn = new JButton("Refresh");
contentPane.add(btn, BorderLayout.SOUTH);
btn.addActionListener(this);
frame.pack();
// let the client make this visible
return frame;
}
public void actionPerformed(ActionEvent e) {
// Refresh button was clicked
Date now = null;
try {
now = proxy.getTime();
} catch(RemoteException re) {
re.printStackTrace();
return;
}
label.setText(now.toString());
}
}
package device;
import service.*;
import ui.*;
import java.io.*;
import java.util.Date;
import java.rmi.*;
import java.rmi.server.ExportException;
import net.jini.export.*;
import net.jini.jrmp.JrmpExporter;
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.lookup.ServiceDiscoveryListener;
import net.jini.lookup.ServiceDiscoveryEvent;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.lookup.ServiceItem;
import net.jini.lookup.ServiceDiscoveryManager;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.LookupCache;
import net.jini.core.entry.Entry;
public class ClockDevice implements ServiceIDListener, ServiceDiscoveryListener {
private Timer timer;
public ClockDevice() {
System.setSecurityManager(new RMISecurityManager());
// Build a cache of all discovered clocks and monitor changes
ServiceDiscoveryManager serviceMgr = null;
LookupCache cache = null;
Class [] classes = new Class[] {Timer.class};
ServiceTemplate template = new ServiceTemplate(null, classes,
null);
try {
LookupDiscoveryManager mgr =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null, // unicast locators
null); // DiscoveryListener
serviceMgr = new ServiceDiscoveryManager(mgr,
new LeaseRenewalManager());
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
try {
cache = serviceMgr.createLookupCache(template,
null, // no filter
this); // listener
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public void setTimer(Timer t) {
timer = t;
System.out.println("Our timer service is " + t);
Exporter exporter = new JrmpExporter();
// export a Timer proxy
Remote proxy = null;
try {
proxy = exporter.export(timer);
} catch(ExportException e) {
System.exit(1);
}
// Change start
// Create a UI object
Entry uiEntry = new UIFactoryImpl();
Entry[] entries = {uiEntry};
// Change end
// Register with all lookup services as they are discovered
JoinManager joinMgr = null;
try {
LookupDiscoveryManager mgr =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null, // unicast locators
null); // DiscoveryListener
// Change start
// note UI entry objects added to JoinManager
joinMgr = new JoinManager(proxy, // service proxy
entries, // attr sets - contains UI
this, // ServiceIDListener
mgr, // DiscoveryManager
new LeaseRenewalManager());
// Change end
} 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());
}
public void serviceAdded(ServiceDiscoveryEvent evt) {
// evt.getPreEventServiceItem() == null
ServiceItem postItem = evt.getPostEventServiceItem();
System.out.println("Service appeared: " +
postItem.service.getClass().toString());
tryClockValidation((Timer) postItem.service);
}
public void serviceChanged(ServiceDiscoveryEvent evt) {
ServiceItem preItem = evt.getPostEventServiceItem();
ServiceItem postItem = evt.getPreEventServiceItem() ;
System.out.println("Service changed: " +
postItem.service.getClass().toString());
}
public void serviceRemoved(ServiceDiscoveryEvent evt) {
// evt.getPostEventServiceItem() == null
ServiceItem preItem = evt.getPreEventServiceItem();
System.out.println("Service disappeared: " +
preItem.service.getClass().toString());
}
private void tryClockValidation(Timer otherTimer) {
try {
if (timer.isValidTime() && ! otherTimer.isValidTime()) {
// other clock needs to be set by us
otherTimer.setTime(timer.getTime());
} else if (! timer.isValidTime() && otherTimer.isValidTime()) {
// we need to be set from the other clock
timer.setTime(otherTimer.getTime());
}
} catch(RemoteException e) {
// ignore other timer!
}
}
public void setTime(Date t) throws RemoteException {
timer.setTime(t);
}
public Date getTime() throws RemoteException {
return timer.getTime();
}
public boolean isValidTime() throws RemoteException {
return timer.isValidTime();
}
}
/**
* Listen for state changes in clocks
*/
package client;
import service.*;
import ui.*;
import java.io.*;
import javax.swing.JFrame;
import java.rmi.*;
import java.rmi.server.*;
import net.jini.lookup.ServiceDiscoveryListener;
import net.jini.lookup.ServiceDiscoveryEvent;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.core.lookup.ServiceItem;
import net.jini.lookup.ServiceDiscoveryManager;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.LookupCache;
import net.jini.discovery.LookupDiscovery;
import net.jini.core.entry.Entry;
import net.jini.export.*;
import net.jini.jrmp.JrmpExporter;
public class ClockUI implements Remote, ServiceDiscoveryListener
{
public static void main(String[] args) {
ClockUI client = new ClockUI();
// This is a hack to keep the server alive.
Object keepAlive = new Object();
synchronized(keepAlive) {
try {
// Wait for a "notify" from another thread
// that will never be sent.
// So we stay alive for ever
keepAlive.wait();
} catch(java.lang.InterruptedException e) {
// do nothing
}
}
}
public ClockUI()
{
System.setSecurityManager(new RMISecurityManager());
// Build a cache of all discovered clocks and monitor changes
ServiceDiscoveryManager serviceMgr = null;
LookupCache cache = null;
Class [] classes = new Class[] {Timer.class};
ServiceTemplate template = new ServiceTemplate(null, classes,
null);
try {
LookupDiscoveryManager mgr =
new LookupDiscoveryManager(LookupDiscovery.ALL_GROUPS,
null, // unicast locators
null); // DiscoveryListener
serviceMgr = new ServiceDiscoveryManager(mgr,
new LeaseRenewalManager());
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
try {
cache = serviceMgr.createLookupCache(template,
null, // no filter
this); // listener
} catch(Exception e) {
e.printStackTrace();
System.exit(1);
}
}
// Change start
private void showClockUI(Timer service, Entry[] attributes) {
for (int n = 0; n < attributes.length; n++) {
Entry entry = attributes[n];
System.out.println("Entry is " + entry);
if (entry instanceof UIFactory) {
UIFactory factory = (UIFactory) entry;
JFrame frame = factory.createJFrame(service);
frame.setVisible(true);
}
}
}
// Change end
public void serviceAdded(ServiceDiscoveryEvent evt) {
// assert: evt.getPreEventServiceItem() == null
ServiceItem postItem = evt.getPostEventServiceItem();
System.out.println("Service appeared: " +
postItem.service.getClass().toString());
Timer timerSvc = (Timer) postItem.service;
// Change start
showClockUI(timerSvc, postItem.attributeSets);
// Change end
}
public void serviceChanged(ServiceDiscoveryEvent evt) {
ServiceItem preItem = evt.getPostEventServiceItem();
ServiceItem postItem = evt.getPreEventServiceItem() ;
System.out.println("Service changed: " +
postItem.service.getClass().toString());
}
public void serviceRemoved(ServiceDiscoveryEvent evt) {
// assert: evt.getPostEventServiceItem() == null
ServiceItem preItem = evt.getPreEventServiceItem();
System.out.println("Service disappeared: " +
preItem.service.getClass().toString());
}
}
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
/**
* FileClassifierFrameFactory.java
*
*
* Created: Sun Feb 13 17:15:16 2000
*
* @author Jan Newmarch
* @version 1.0
*/
package ui;
import net.jini.lookup.ui.factory.FrameFactory;
import net.jini.lookup.entry.UIDescriptor;
import java.awt.Frame;
import net.jini.core.entry.Entry;
import net.jini.core.lookup.ServiceItem;
public class FileClassifierFrameFactory implements FrameFactory {
/**
* Return a new FileClassifierFrame that implements the
* MainUI role
*/
public Frame getFrame(Object roleObject) {
// we should check to see what role we have to return
if (! (roleObject instanceof ServiceItem)) {
// unkown role type object
// can we return null?
return null;
}
ServiceItem item = (ServiceItem) roleObject;
// Do sanity checking that the UIDescriptor has a MainUI role
Entry[] entries = item.attributeSets;
for (int n = 0; n < entries.length; n++) {
if (entries[n] instanceof UIDescriptor) {
UIDescriptor desc = (UIDescriptor) entries[n];
if (desc.role.equals(net.jini.lookup.ui.MainUI.ROLE)) {
// Ok, we are in the MainUI role, so return a UI for that
Frame frame = new FileClassifierFrame(item, "File Classifier");
return frame;
}
}
}
// couldn't find a role the factory can create
return null;
}
} // FileClassifierFrameFactory
FileClassifierFrame
could look like
/**
* FileClassifierFrame.java
*
*
* Created: Sun Feb 13 19:29:20 2000
*
* @author Jan Newmarch
* @version 1.0
*/
package ui;
import java.awt.*;
import java.awt.event.*;
import net.jini.lookup.ui.MainUI;
import net.jini.core.lookup.ServiceItem;
import common.MIMEType;
import common.FileClassifier;
import java.rmi.RemoteException;
/**
* Object implementing MainUI for FileClassifier.
*/
public class FileClassifierFrame extends Frame implements MainUI {
ServiceItem item;
TextField text;
public FileClassifierFrame(ServiceItem item, String name) {
super(name);
this.item = item;
Panel top = new Panel();
Panel bottom = new Panel();
add(top, BorderLayout.CENTER);
add(bottom, BorderLayout.SOUTH);
top.setLayout(new BorderLayout());
top.add(new Label("Filename"), BorderLayout.WEST);
text = new TextField(20);
top.add(text, BorderLayout.CENTER);
bottom.setLayout(new FlowLayout());
Button classify = new Button("Classify");
Button quit = new Button("Quit");
bottom.add(classify);
bottom.add(quit);
// listeners
quit.addActionListener(new QuitListener());
classify.addActionListener(new ClassifyListener());
// We pack, but don't make it visible
pack();
}
class QuitListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
System.exit(0);
}
}
class ClassifyListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
String fileName = text.getText();
final Dialog dlg = new Dialog((Frame) text.getParent().getParent());
dlg.setLayout(new BorderLayout());
TextArea response = new TextArea(3, 20);
// invoke service
FileClassifier classifier = (FileClassifier) item.service;
MIMEType type = null;
try {
type = classifier.getMIMEType(fileName);
if (type == null) {
response.setText("The type of file " + fileName +
" is unknown");
} else {
response.setText("The type of file " + fileName +
" is " + type.toString());
}
} catch(RemoteException e) {
response.setText(e.toString());
}
Button ok = new Button("ok");
ok.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dlg.setVisible(false);
}
});
dlg.add(response, BorderLayout.CENTER);
dlg.add(ok, BorderLayout.SOUTH);
dlg.setSize(300, 100);
dlg.setVisible(true);
}
}
} // FileClassifierFrame
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