Objects and Java Seminar
Jini and JavaSpaces
Lecture Handout
Agenda
-
Introduce Jini
-
Discuss concepts and show code examples
-
Discuss runtime infrastructure: discovery, join, lookup
-
Discuss JavaSpaces
Why Jini?
- Jini enables a "plug and play" network
- Jini gives the network a type system
- OO Programming no longer limited to single address space
- Clients request service by their Java interface
- Jini is a network-centric computing architecture
- A fundamental change in the architecture of "computers"
- Enabled by Java -- service can inject code into client
- Defines "rules of federation"; doesn't "know everything"
What is Jini?
- A programming model/infrastructure that
enables building/deploying of distributed systems
- Takes care of common but difficult parts:
- boot-strap (discovery)
- directory (look-up)
-
Jini systems organized
as federations of services
- Service: hardware, software, comm channel, user
- Example: disk drive can offer a storage service
- Once part of a federation, service can be used by other services
or clients
Jini Example
Jini is the Glue
- Jini has two parts:
- Programming Model: Helps you build a distributed system
organized as a federation of clients and services
- Runtime Infrastructure: Resides on the network and
provides mechanisms for adding, subtracting, locating, and accessing services
- How it works:
- Via runtime infrastructure, services make themselves available
- Via runtime infrastructure, clients locate and contact services
- To do work, clients and services can use the programming model
The Runtime Infrastructure
- Resides in two places: lookup services and
Jini-enabled devices
- Lookup service: central organizing mechanism for
Jini-based systems
- A device is plugged into the network
- It registers its service(s) with a lookup service
- The device and its services are now part of a federation
- A client wishes to locate a service to assist with some
task
- Client consults a lookup service
Groups
-
Lookup services organize the services they contain into groups
-
Group: a set of registered services identified by a string
-
"East Conference Room"
group: services offered by
all devices in the East Conference Room
-
A service can be a member of multiple groups
-
Multiple lookup services can maintain the same group
-
Redundancy can help make a Jini system more fault tolerant
Discovery, Join, Lookup
-
Three basic processes defined in specs:
discovery, join, and lookup
-
Discovery: Devices/Clients locate
and obtain references to lookup services
-
Join: Device registers its services with lookup services
-
Lookup: Clients locate and contact services
Discovery - Multicast Request Protocol
- Drop a multicast UDP "presence announcement" onto a well-known port
(recommended: 7 times, 5 seconds apart)
- Groups the device wants to join
- IP address and port number where device can be contacted
- Lookup services it has already heard back from
-
Lookup services listen for presence announcements, contacts sender directly
-
Uses the unicast discovery protocol to obtain
a "lookup service registrar"
Discovery - Multicast Request Protocol
Discovery - Unicast Discovery Protocol
- Used to communicate with a specific lookup service
- To contact non-local lookup services (beyond multicast radius)
- For long term relationships with lookup services
- After multicast protocols result in contact
- TCP connection made either by lookup service or discovering entity
- Two steps:
- Discovering entity sends a unicast request
- Lookup service responds with a lookup service registrar object
(an RMI Marshalled Object)
Discovery - The Unicast Discovery Protocol
Discovery - The Multicast Announcement Protocol
-
Good for new lookup services and after network failures
-
Lookup service sends out a "multicast presence announcement"
(Recommended: every 120 seconds)
- The service ID of this lookup service
- IP address and port number where unicast discovery of this lookup
service can be done
- List of groups it manages
-
Discovering entity listens for announcements, contacts lookup service
-
Performs unicast discovery to obtain a "lookup service registrar"
Discovery - The Multicast Announcement Protocol
- Final result of discovery: service registrar object from
lookup service is injected into discovering entity's JVM
A Discovery Example
-
You plug a Jini-enabled disk drive into network
-
Drive drops a multicast presence announcement
-
Lookup service receives the disk drive's announcement packet
-
Lookup service contacts the disk drive directly (TCP connection)
-
Drive sends a unicast discovery request packet to lookup service
-
Lookup service sends a service registrar object
-
Disk drive can register itself (via join) through the service registrar
Lookup Service Ping
-
Performs unicast discovery given a URL passed as the first argument
-
"Pings" the specified lookup service and prints
out some information about it
1 import net.jini.core.discovery.LookupLocator;
2 import net.jini.core.lookup.ServiceRegistrar;
3 import net.jini.core.lookup.ServiceID;
4
5 import java.io.*;
6 import java.rmi.*;
7 import java.rmi.server.*;
8 import java.util.StringTokenizer;
9
10 public class LSP
11 {
12 public static void main (String[] args) {
13
14 if (args.length == 0) {
15 System.out.println("LSP requires a lookup service URL as first arg.");
16 System.out.println("Example: \"LSP jini://localhost:2000\"");
17 System.exit(0);
18 }
19
20 String urlString = args[0];
21
22 try {
23 System.setSecurityManager (new RMISecurityManager());
24
25 // Instantiate a LookupLocator object set to perform
26 // unicast discovery on the lookup service indicated
27 // by the passed in host and port number. (If no port number
28 // is specified, the default port of 4160 will be used.)
29 LookupLocator lookupLocator = new LookupLocator(urlString);
30 String host = lookupLocator.getHost();
31 int port = lookupLocator.getPort();
32
33 System.out.println("Lookup Service: jini://" + host + ":" + port);
34
35 // Invoke getRegistrar() on the LookupLocator to cause it to
36 // perform unicast discovery on the lookup service indicated
37 // by the host and port number passed to the constructor.
38 // The result is a reference to the registar object sent from
39 // the lookup service.
40 ServiceRegistrar registrar = lookupLocator.getRegistrar();
41
42 // A lookup service is, after all, a service -- a service
43 // so proud of itself that it registers itself with itself. To
44 // get the ID it assigns to itself, call getServicerID() on
45 // the registrar object.
46 ServiceID id = registrar.getServiceID();
47
48 System.out.println ("Lookup Service ID: " + id.toString());
49 }
50 catch (Exception e) {
51 System.out.println("LSP Exception:" + e);
52 }
53 }
54 }
$ java -Djava.security.policy="policy.all" LSP jini://localhost
Lookup Service: jini://localhost:4160
Lookup Service ID: a1366531-5805-4f2e-8b40-e6b9b36f78fb
Lookup Service Find
- LSF performs multicast discovery to locate all nearby lookup
services and prints out a line of information about each
1 import net.jini.discovery.LookupDiscovery;
2 import net.jini.core.discovery.LookupLocator;
3 import net.jini.discovery.DiscoveryListener;
4 import net.jini.discovery.DiscoveryEvent;
5 import net.jini.core.lookup.ServiceRegistrar;
6 import net.jini.core.lookup.ServiceID;
7
8 import java.io.*;
9 import java.rmi.*;
10 import java.rmi.server.*;
11 import java.util.StringTokenizer;
12
13 // Lookup Service Find - Does discovery to find the nearby lookup
14 // services and prints out a list
15 public class LSF implements DiscoveryListener
16 {
17 public static void main (String[] args) {
18
19 try {
20 System.setSecurityManager (new RMISecurityManager());
21
22 LookupDiscovery ld = new LookupDiscovery(LookupDiscovery.NO_GROUPS);
23 ld.addDiscoveryListener(new LSF());
24 ld.setGroups(LookupDiscovery.ALL_GROUPS);
25
26 Thread.currentThread().sleep(1000000000L);
27 }
28 catch (Exception e) {
29 System.out.println("LSF Exception:" + e);
30 }
31 }
32
33 public void discovered(DiscoveryEvent de) {
34
35 try {
36 // Invoke getRegistrar() on the LookupLocator to cause it to
37 // perform unicast discovery on the lookup service indicated
38 // by the host and port number passed to the constructor.
39 // The result is a reference to the registar object sent from
40 // the lookup service.
41 ServiceRegistrar[] registrars = de.getRegistrars();
42
43 for (int i = 0; i < registrars.length; ++i) {
44 // A lookup service is, after all, a service -- a service
45 // so proud of itself that it registers itself with itself. To
46 // get the ID it assigns to itself, call getServicerID() on
47 // the registrar object.
48 ServiceID id = registrars[i].getServiceID();
49
50 LookupLocator lookupLocator = registrars[i].getLocator();
51 String host = lookupLocator.getHost();
52 int port = lookupLocator.getPort();
53
54 System.out.println("Lookup Service: jini://" + host + ":"
55 + port + ", " + id.toString());
56 }
57 }
58 catch (Exception e) {
59 System.out.println("LSF Exception:" + e);
60 }
61 }
62
63 public void discarded(DiscoveryEvent de) {
64 }
65 }
$ java -Djava.security.policy="policy.all" LSF
Lookup Service: jini://bvenners:4160, a1366531-5805-4f2e-8b40-e6b9b36f78fb
Join
-
After discovery, device can register its services via join
-
Service connects to lookup service via the service registrar object
-
Service sends information about itself to the lookup service
-
Lookup service stores the information uploaded from the service
-
At that point, the service has joined that lookup service
The ServiceItem
ServiceRegistrar.register(ServiceItem item, long leaseDuration);
- A
ServiceItem
has 3 parts:
- A
ServiceID
object, or null
if registering
for the first time
- A service object
- An array of attribute sets:
Entry[]
- An entry is an object that implements the
Entry
interface
and contains public fields of reference (object) types
- Example entry:
UIDescriptor
:
1 public class UIDescriptor extends AbstractEntry {
2
3 public String role;
4 public String toolkit;
5 public Set attributes;
6 public MarshalledObject factory;
7
8 public UIDescriptor() {
9 }
10
11 public UIDescriptor(String role, String toolkit, Set attributes,
12 MarshalledObject factory) {
13
14 this.role = role;
15 this.toolkit = toolkit;
16 this.attributes = attributes;
17 this.factory = factory;
18 }
19
20 public final Object getUIFactory(final ClassLoader parentLoader)
21 throws IOException, ClassNotFoundException {
22
23 //...
24 }
25 }
A ServiceItem
Diagram
The Join Process
The SummerService
1 import net.jini.core.discovery.LookupLocator;
2 import net.jini.core.lookup.ServiceRegistrar;
3 import net.jini.core.lookup.ServiceID;
4 import net.jini.core.lookup.ServiceTemplate;
5 import net.jini.core.lookup.ServiceMatches;
6 import net.jini.core.lookup.ServiceItem;
7 import com.sun.jini.lookup.JoinManager;
8 import com.sun.jini.lookup.ServiceIDListener;
9 import net.jini.core.entry.Entry;
10 import net.jini.lookup.entry.Name;
11 import com.sun.jini.lease.LeaseRenewalManager;
12
13 import java.io.*;
14 import java.rmi.*;
15 import java.rmi.server.*;
16 import java.util.StringTokenizer;
17
18 public class SummerService extends UnicastRemoteObject
19 implements Summer, ServiceIDListener, Serializable
20 {
21 public SummerService() throws RemoteException {
22 super ();
23 }
24
25 public long sumString(String s) throws InvalidLongException,
26 RemoteException {
27
28 System.out.println("sumString(\"" + s + "\")");
29 long sum = 0;
30 StringTokenizer st = new StringTokenizer(s);
31 String token;
32 while (st.hasMoreTokens()) {
33 token = st.nextToken();
34 try {
35 sum += Long.parseLong(token);
36 }
37 catch (NumberFormatException e) {
38 throw new InvalidLongException(
39 "Invalid number: " + token);
40 }
41 }
42
43 return sum;
44 }
45
46 public void serviceIDNotify (ServiceID idIn) {
47 }
48
49 public static void main (String[] args) {
50
51 try {
52 System.setSecurityManager(new RMISecurityManager());
53
54 Entry[] attributes = new Entry[1];
55 attributes[0] = new Name("SummerService");
56
57 SummerService summerService = new SummerService();
58
59 JoinManager joinManager = new JoinManager(summerService,
60 attributes, summerService, new LeaseRenewalManager());
61 }
62 catch (Exception e) {
63 System.out.println("SummerService Exception:" + e);
64 }
65 }
66 }
$ java -Djava.security.policy=/sun/jini/bv/policy.all
-Djava.rmi.server.codebase=http://localhost:8080/ SummerService
1 import java.rmi.*;
2
3 public interface Summer extends Remote {
4
5 long sumString(String s)
6 throws InvalidLongException,
7 RemoteException;
8 }
1 public class InvalidLongException
2 extends Exception {
3
4 private String message;
5 InvalidLongException(String s) {
6 message = s;
7 }
8 public String getMessage() {
9 return message;
10 }
11 }
The Lookup Process
Lookup Service Directory
1 import net.jini.core.discovery.LookupLocator;
2 import net.jini.core.lookup.ServiceRegistrar;
3 import net.jini.core.lookup.ServiceID;
4 import net.jini.core.lookup.ServiceTemplate;
5 import net.jini.core.lookup.ServiceMatches;
6 import net.jini.core.lookup.ServiceItem;
7
8 import java.io.*;
9 import java.rmi.*;
10 import java.rmi.server.*;
11 import java.util.StringTokenizer;
12
13 public class LSD
14 {
15 public static void main (String[] args) {
16
17 if (args.length == 0) {
18 System.out.println("LSP requires a lookup service URL as first arg.");
19 System.out.println("Example: \"LSP jini://localhost:2000\"");
20 System.exit(0);
21 }
22
23 String urlString = args[0];
24
25 try {
26 System.setSecurityManager (new RMISecurityManager());
27
28 // Instantiate a LookupLocator object set to perform
29 // unicast discovery on the lookup service indicated
30 // by the passed in host and port number. (If no port number
31 // is specified, the default port of 4160 will be used.)
32 LookupLocator lookupLocator = new LookupLocator(urlString);
33 String host = lookupLocator.getHost();
34 int port = lookupLocator.getPort();
35
36 System.out.println();
37 System.out.println("Lookup Service: jini://" + host + ":" + port);
38
39 // Invoke getRegistrar() on the LookupLocator to cause it to
40 // perform unicast discovery on the lookup service indicated
41 // by the host and port number passed to the constructor.
42 // The result is a reference to the registar object sent from
43 // the lookup service.
44 ServiceRegistrar registrar = lookupLocator.getRegistrar();
45
46 // Create a ServiceTemplate object that will match all the
47 // services in the lookup service.
48 ServiceTemplate serviceTemplate = new ServiceTemplate(null, null,
49 null);
50
51 // Do the lookup. This is bad LSD in a way, because it will
52 // only report at most 1000 services in the lookup service.
53 ServiceMatches matches = registrar.lookup(serviceTemplate, 1000);
54
55 System.out.println("Total Services: " + matches.totalMatches);
56 System.out.println();
57 for (int i = 0; i < matches.totalMatches; ++i) {
58
59 ServiceItem item = matches.items[i];
60 String typeName = item.service.getClass().getName();
61 String idString = item.serviceID.toString();
62
63 System.out.println(typeName + ": " + idString);
64 }
65 System.out.println();
66 }
67 catch (Exception e) {
68 System.out.println("LSD Exception: " + e);
69 }
70 }
71 }
$ java -Djava.security.policy="policy.all" LSD jini://localhost
Lookup Service: jini://localhost:4160
Total Services: 2
SummerService_Stub: f6731f98-9bf2-4434-814f-fee57deadaf4
com.sun.jini.reggie.RegistrarProxy: a1366531-5805-4f2e-8b40-e6b9b36f78fb
Using a Service
The SummerClient
1 import net.jini.entry.*;
2 import net.jini.core.lookup.*;
3 import net.jini.core.entry.*;
4 import net.jini.lookup.entry.*;
5 import net.jini.core.discovery.*;
6 import java.rmi.*;
7 import java.io.*;
8
9 class SummerClient
10 {
11 public static void main (String[] args) {
12
13
14 try {
15 System.setSecurityManager (new RMISecurityManager ());
16
17 // Perform unicast lookup on localhost
18 LookupLocator lookup = new LookupLocator("jini://localhost");
19
20 // Get the service registrar object for the lookup service
21 ServiceRegistrar registrar = lookup.getRegistrar();
22
23 // Search the lookup server to find the service that has the
24 // name attribute of "SummerService".
25 Entry[] attributes = new Entry[1];
26 attributes[0] = new Name("SummerService");
27 ServiceTemplate template = new ServiceTemplate(null, null,
28 attributes);
29
30 // lookup() returns the service object for a service that matches
31 // the search criteria passed in as template
32 // Here, although I searched by Name attribute, I'm assuming that
33 // the object that comes back implements the Summer interface
34 Summer summer = (Summer) registrar.lookup(template);
35
36 LineNumberReader stdinReader = new LineNumberReader(
37 new BufferedReader(new InputStreamReader(System.in)));
38
39 for (;;) {
40
41 String userLine = stdinReader.readLine();
42
43 if (userLine == null || userLine.length() == 0) {
44 break;
45 }
46
47 String outString;
48 try {
49 long sum = summer.sumString(userLine);
50 outString = Long.toString(sum);
51 }
52 catch(InvalidLongException e) {
53 outString = e.getMessage();
54 }
55 System.out.println(outString);
56 }
57 }
58 catch (Exception e)
59 {
60 System.out.println("client: MyClient.main() exception: " + e);
61 e.printStackTrace();
62 }
63 }
64 }
$ java -Djava.security.policy=/sun/jini/bv/policy.all
-Djava.rmi.server.codebase=http://localhost:8080/ SummerClient
1 1
2
2 2
4
3 3 3
9
The SummerClient2
import net.jini.entry.*;
import net.jini.lookup.*;
import net.jini.lookup.entry.*;
import net.jini.discovery.*;
import java.rmi.*;
import java.io.*;
class SummerClient2
{
public static void main (String[] args) {
try {
System.setSecurityManager(new RMISecurityManager ());
// Perform unicast lookup on localhost
LookupLocator lookup = new LookupLocator("jini://localhost");
// Get the service registrar object for the lookup service
ServiceRegistrar registrar = lookup.getRegistrar();
// Search the lookup server to find a service that implements
// the Summer interface.
Class[] types = new Class[1];
types[0] = Summer.class;
ServiceTemplate template = new ServiceTemplate(null, types, null);
// lookup() returns the service object for a service that matches
// the search criteria passed in as template
// Here, because I searched by type, I'm certain that
// the object that comes back implements the Summer interface.
Summer summer = (Summer) registrar.lookup(template);
LineNumberReader stdinReader = new LineNumberReader(
new BufferedReader(new InputStreamReader(System.in)));
for (;;) {
String userLine = stdinReader.readLine();
if (userLine == null || userLine.length() == 0) {
break;
}
String outString;
try {
long sum = summer.sumString(userLine);
outString = Long.toString(sum);
}
catch(InvalidLongException e) {
outString = e.getMessage();
}
System.out.println(outString);
}
}
catch (Exception e)
{
System.out.println("client: MyClient.main() exception: " + e);
e.printStackTrace();
}
}
}
$ java -Djava.security.policy=/sun/jini/bv/policy.all
-Djava.rmi.server.codebase=http://localhost:8080/ SummerClient2
1 1
2
3 -1
2
9999999 1
10000000
The Protocol is Private
Service object can:
- implement the service locally
- implement the service in part locally (smart proxy)
- be an RMI stub
- communicate back to service via sockets and streams
- use DCOM
- use CORBA
- Send bits and bytes directly to a piece of hardware
- Whatever: client doesn't care because it just invokes methods
declared in the Java interface
Jini Programming Model
- Distributed leasing
- Distributed events
- Distributed transactions
JavaSpaces
- Distributed persistence and algorithms
- A JavaSpaces server holds entries: typed groups of objects
- Write an entry - creates a copy
- Look up with template entries:
- fields with values (exact matching)
null
fields are wildcards
- Two kinds of lookup:
- Read: returns an entry or indicates no match
- Take: reads + removes
- Can request a space to notify you (uses Jini distributed events)
- All operations transactionally secure: a write writes and a take takes
- Entries written to a space are leased (uses Jini leasing)
The JavaSpace
Interface
- Interact with a space via
JavaSpace
interface
- Can get the
JavaSpace
object from a Jini lookup service,
RMI registry, or ...
write()
public Lease write(Entry e, Transaction txn, long lease)
throws TransactionException, SecurityException,
java.rmi.RemoteException;
- Can write duplicate entries into a space
read()
public Entry read(Entry tmpl, Transaction txn, long timeout)
throws UnusableEntryException, TransactionException,
SecurityException, InterruptedException, java.rmi.RemoteException;
- Timeout 0: wait forever;
JavaSpace.NO_WAIT
: don't wait at all
- Thread blocks while waiting
- If multiple matches, get an arbitrary matching entry
readIfExists()
public Entry readIfExists(Entry tmpl, Transaction txn, long timeout)
throws UnusableEntryException, TransactionException,
SecurityException, InterruptedException, java.rmi.RemoteException;
readIfExists()
also reads, but only blocks if
waiting for a transactional state to settle
take() and takeIfExists()
public Entry take(Entry tmpl, Transaction txn, long timeout)
throws UnusableEntryException, TransactionException,
SecurityException, InterruptedException, java.rmi.RemoteException;
public Entry takeIfExists(Entry tmpl, Transaction txn, long timeout)
throws UnusableEntryException, TransactionException,
SecurityException, InterruptedException, java.rmi.RemoteException;
- Like corresponding reads, but also removes returned entry
notify()
public EventRegistration notify(Entry tmpl, Transaction txn,
RemoteEventListener listener, long lease,
java.rmi.MarshalledObject handback)
throws TransactionException, SecurityException,
java.rmi.RemoteException;
- Registers listener as interested in match event
Entries and Matching
- Entries are typed sets of objects:
public class CachedEvent extends AbstractEntry {
public Long eventID;
public Object eventSource;
public Object eventListener;
public RemoteEvent event;
public CachedEvent() {
}
public CachedEvent(long eventID, Object eventSource,
Object eventListener, RemoteEvent event) {
this.eventID = new Long(eventID);
this.eventSource = eventSource;
this.eventListener = eventListener;
this.event = event;
}
}
- Each (public, non-static, non-final) field is serialized separately
- Two fields one object --> two fields two objects
- To match, compare serialized images
(
MarshalledObject.equals()
)
- Semantic
equals()
not used in matching
- Object images must match exactly;
null
is wildcard
JavaSpaces Uses
- Persistence: Can use a space to store and retrieve objects on a
remote system
- Distributed Algorithms: JavaSpaces facilitates distributed
algorithms based on "flows of objects"
-
Java Rings and Fractal Points
-
A
RemoteEvent
Mailbox