|
|
|
Sponsored Link •
|
Summary
In this article, I look at ways Jini clients can search for desired services in the Jini lookup service. I describe both the flexibility of the lookup service as well as its inherent limitations. I proposes a way that theServiceRegistrarinterface could evolve, and describes the Jini team's response.
The Jini lookup service, the central component of Jini's runtime infrastructure, offers Jini clients a flexible and powerful way to find Jini services. It enables service providers to advertise their services and enables clients to locate and enlist the help of those services.
To interact with the lookup service, the client must first obtain a
a service registrar object via discovery, a
network-level protocol used by Jini's runtime infrastructure. Discovery
enables clients and services to locate lookup services. (For more
information on discovery, see Resources.) The
service registrar object, which implements the
net.jini.core.lookup.ServiceRegistrar interface, enables
the client to interact with the lookup service. To find desired services,
clients build a
ServiceTemplate, an instance of class
net.jini.core.lookup.ServiceTemplate, and pass it to one of
two
lookup() methods declared in the ServiceRegistrar
interface.
Each lookup() method sends the service template to the
lookup service, which performs the query and returns zero to many
matching service objects to the client.
In general, a client looks up a service by Java type, usually
an interface. For example, if a client needed to use a printer, it
would compose a service template that included a Class object
for a well-known interface to printer services. All printer services
would implement this well-known interface. The lookup service would
return a service object (or objects) that implemented this interface.
You could include attributes in the service template to narrow the number
of matches for such a type-based search. The client would use the
printer service by invoking on the service object methods declared in
the well-known interface.
The ServiceTemplate class, which allows you to express
search criteria for Jini lookups,
consists solely of these three public fields:
public Entry[] attributeSetTemplates; public ServiceID serviceID; public Class[] serviceTypes;
ServiceTemplate has no methods and its instances merely serve
as
struct-like
containers for lookup
service queries. Matches are performed as described by the following excerpt
from
ServiceTemplate's javadoc page:
Items in the lookup service are matched using an instance of [ServiceTemplate]. A service item (item) matches a service template (tmpl) if:
item.serviceIDequalstmpl.serviceID(or iftmpl.serviceIDisnull)item.service[the service object] is an instance of every type intmpl.serviceTypesitem.attributeSetscontains at least one matching entry for each entry template intmpl.attributeSetTemplatesAn entry matches an entry template if the class of the template is the same as, or a superclass of, the class of the entry, and every non-null field in the template equals the corresponding field of the entry. Every entry can be used to match more than one template. Note that in a service template, for
serviceTypesandattributeSetTemplates, a null field is equivalent to an empty array; both represent a wildcard.
As described here, the service template can
include a reference to an array of Class objects. These
Class objects indicate to the lookup service the Java type
(or types) of the service object the client desires. The service
template can also include a service ID, which uniquely
identifies a service, and attributes, which must exactly match the
attributes uploaded by the service provider in the service item. The
service template can also contain wildcards for any of these fields. A
wildcard in the service ID field, for example, will match any service
ID.
The ServiceRegistrar's lookup() methods take
two overloaded forms. The main difference between these two forms is the number
of matches and service items each one returns. The two-parameter form
can return multiple matches of the query expressed in the
ServiceTemplate, while the one-parameter form returns only
one match. In addition, the two-parameter form returns entire
service items; the one-parameter form returns only the service object.
lookup()
Here's a javadoc excerpt that explains the two-parameter form of
lookup():
public ServiceMatches lookup(ServiceTemplate tmpl,
int maxMatches) throws java.rmi.RemoteException;
Returns at mostmaxMatchesitems matching the template, plus the total number of items that match the template. The return value is nevernull, and the returned items array is onlynullifmaxMatchesis zero. For each returned item, if the service object cannot be deserialized, the service field of the item is set tonulland no exception is thrown. Similarly, if an attribute set cannot be deserialized, that element of theattributeSetsarray is set tonulland no exception is thrown.Here is the
ServiceMatchesclass:package net.jini.core.lookup; public class ServiceMatches extends java.lang.Object implements java.io.Serializable { public ServiceItem[] items; public int totalMatches; }And here is the
ServiceItemclass:package net.jini.core.lookup; public class ServiceMatches extends java.lang.Object implements java.io.Serializable { public Entry[] attributeSets; public java.lang.Object service; public ServiceID serviceID; }
As mentioned previously, each element of the items array
returned by the two-parameter form is a complete service item, which
includes the service object, service ID, and all the attribute sets. The
maxMatches field helps clients manage the number of objects
returned by this lookup().
The length of the items array in the returned
ServiceMatches object will be less than or equal
to the value passed to lookup() in maxMatches.
The total number of matching service items, which is returned in
totalMatches, will be greater than or equal to the
length of the items array.
For example, if maxMatches is 50 and the service template
matches 25 items, the length of the returned items array
and the value of totalMatches will both be 25.
Alternatively, if maxMatches is 50 but the service template
matches 100 items, the length of the returned items array
will be 50 and the value of totalMatches will be 100.
When a service template matches more than maxMatches
service items, the service items returned by the two-parameter
lookup() are selected randomly from the
full set of matching service items.
lookup()
The one-parameter lookup() method returns one matching service
object chosen randomly from all the matches. Here's a javadoc excerpt
explaining
this form:
public ServiceMatches lookup(ServiceTemplate tmpl)
throws java.rmi.RemoteException;
Returns the service object (i.e., justServiceItem.service) from an item matching the template, ornullif there is no match. If multiple items match the template, it is arbitrary as to which service object is returned. If the returned object cannot be deserialized, anUnmarshalExceptionis thrown with the standard RMI semantics.
Because the one-parameter lookup() returns only
one matching service object, clients can minimize the
amount of object state and class files that are downloaded.
But because the returned service object is selected arbitrarily, and is not
identified by a service ID or described by associated attribute sets, the
client must be confident that
any matching service object will suffice.
In addition to the two lookup() methods, the
ServiceRegistrar has three methods called browsing
methods that let clients get information about registered
service items. These three methods -- getServiceTypes(),
getEntryClasses(), and getFieldValues() -- are
called "browsing methods" because they enable clients to browse the services
and attributes in the lookup service.
The getServiceTypes() method takes a
ServiceTemplate (the same ServiceTemplate that is
passed to the lookup() methods) and a String
prefix. It returns an array of Class
instances representing the most specific types (classes or interfaces)
of the service objects that match the template. These service objects are
neither equal to, nor a superclass of, any of the types specified in the
template and have names that start
with the specified prefix. The service object or objects for whom
Class instances are returned are all
instances of all the types (if any) passed in the template, but the
Class instances are all more specific than (and are subclasses
or subinterfaces of) those types. Each class appears only once in the
returned array, and in arbitrary order.
Here's what getServiceTypes() looks like:
public java.lang.Class[] getServiceTypes(ServiceTemplate tmpl,
java.lang.String prefix) throws java.rmi.RemoteException;
The getEntryTypes() method takes a
ServiceTemplate,
and returns an array of Class instances
that represent the most specific classes of entries for the service
items that match the template which either
don't match any entry template or are a
subclass of one. Each class appears only once in the returned array, and
again in arbitrary order.
Here's what getEntryClasses() looks like:
public java.lang.Class[] getEntryClasses(ServiceTemplate tmpl)
throws java.rmi.RemoteException;
The getFieldValues() method takes a
ServiceTemplate, an integer index,
and a String field name. It returns an array of
Objects for the named field of all instances of the
entry that appears in the ServiceTemplate's
Entry[] array at any matching service item's passed index. Each
object of a particular class and value
appears only once in the returned array, and in arbitrary order.
Here's what getFieldValues() looks like:
public java.lang.Object[] getFieldValues(ServiceTemplate tmpl,
int setIndex, java.lang.String field)
throws java.lang.NoSuchFieldException, java.rmi.RemoteException;
The behavior and purpose of these browsing methods can be obscure. A good way to think of them is that they enable clients to incrementally narrow queries of the lookup service.
For example, a client, such as a graphical lookup service browser, could
first invoke
getServiceTypes() with
an empty template. The getServiceTemplate() method returns
all possible service types registered in the lookup service, which the
browser could display. The user could select one or more types, then push
the Requery button. The browser would add that type (or types) to the
service template, and invoke getServiceTypes() again. A smaller
list of types would be
returned, and displayed by the browser. The user could select
one and press an Entries button. The browser would form a template with the
most
recently selected service type or types, and invoke
getEntryTypes(). The getEntryTypes() method would
return an array of entry classes, which the browser could then display.
The user could select some entries, and a field of a selected entry, and
push a Fields
button. The browser would build a template using the currently selected
service and
entry types, and pass the index of the entry class in which the user
selected the field,
and the name of the selected field, to getFieldValues(). The
browser
would display all the values that getFieldValues() returned.
The user could use these values to further narrow the search for a service,
eventually using a particular service.
Thus, these methods help clients, whether or not a human user is involved,
to browse the services registered in a lookup service. The arrays returned
from the
browsing methods can help the client further refine its queries,
ultimately resulting in a ServiceTemplate that, when passed to
lookup(), returns the most appropriate
service object.
In addition to the lookup and browsing methods, the
ServiceRegistrar interface also includes a
notify() method that
notifies clients when new services register or unregister with a lookup
service:
public EventRegistration notify(ServiceTemplate tmpl,
int transitions, RemoteEventListener listener,
MarshalledObject handback, long leaseDuration)
throws RemoteException;
You invoke notify() to register yourself (or another
listener) to receive a distributed event whenever the services
that match the passed ServiceTemplate undergo a state
change described by the transitions parameter.
The transitions parameter is a bitwise OR of any non-empty set of
these three values, which are defined as constants in
ServiceRegistrar:
TRANSITION_MATCH_MATCH TRANSITION_MATCH_NOMATCH TRANSITION_NOMATCH_MATCH
You build the ServiceTemplate for notify() in
the same way you build it for lookup(). You can indicate
explicit types, a service ID, attributes (which must exactly match), or
wildcards (which match anything) in any of those fields. The transitions are
based on a change
(or non-change) in the status of whatever matches your
ServiceTemplate before and after any operation is performed on
the lookup service.
For example, TRANSITION_MATCH_MATCH indicates that at
least one service item matched your template before and after an operation.
TRANSITION_MATCH_NOMATCH indicates that, although at least one
particular service item matched your template before an operation,
it no longer matched your template after the operation.
To receive notification when any new services are added to a lookup
service, you simply specify a template that matches any
service, and pass TRANSITION_NOMATCH_MATCH as the
transition to the notify() method.
To fully appreciate the flexibility and power of Jini's lookup service, it's useful to compare it to name servers such as the RMI registry. The RMI registry is similar to the Jini lookup service in that objects can be registered in either server, and clients can query both for objects. When a client queries either server for an object, the server sends the object across the network to the client.
The RMI registry is a name server, because each RMI remote object is associated with a character string name that is unique within that registry. To perform a query on an RMI registry, clients must specify that name. In return for the name, clients receive a stub object that serves as a remote reference to the remote object in the queried RMI registry.
Jini provides a much more expressive way to search for services than RMI.
Jini services are registered with a
service ID that is globally unique and permanently maintained by
each service, a service object, and any number of attribute objects.
Using the ServiceTemplate, clients can look up a service
by service ID, by the Java type or types of the service object, and by
wildcard and exact matching of the attributes. Whereas RMI requires
that a client know the registered name of a desired remote object,
Jini enables a client to just look for the type of service desired --
therefore
enabling spontaneous networking. If you enter a LAN environment
for the first time and you want to use a printer, you don't have to
figure out the printer service's registered name; instead, you
just look up the services that implement a well-known printer
interface. Lookup by type also ensures that Jini clients will know how to
use
whatever is returned from their query, because they had to have knowledge of
the
type before sending their query.
On the other hand, the Jini lookup service does operate similar to a naming server in two ways. First, performing lookups by service ID is really looking up services by name. Just as the character string name of a remote object in RMI registry is unique, a service ID is a unique name for a Jini service. A Jini service ID differs from an RMI registry name only in its form and its scope of uniqueness. Whereas an RMI registry name is a character string, a Jini service ID is a 128-bit number. And whereas an RMI registry name is guaranteed to be unique only within the scope of each particular RMI registry server, a Jini service ID is supposed to be globally unique.
The other way in which a Jini lookup service operates as a naming server is that lookups by type are actually implemented by performing string comparisons of type names. As mentioned previously, to specify the kind of service desired, clients specify Java types. The types specified are most often interfaces, but can also be classes. Because developers of Jini clients must indicate the desired service with a Java type, he or she knows about the type at compile-time, and will therefore know how to use whatever object is returned.
To indicate a Java type, clients must pass to the service registrar's
lookup() method an array of references to Class
instances that represent the desired types in the client's virtual machine.
Any registered service whose service object is an instance of each type
specified by the client
matches the lookup. When the lookup service looks through its set of
registered services, however, it merely compares the type names of
the types appearing in the client's Class[] array with
the type names of the registered service objects. The lookup service
doesn't compare any class information besides the type name.
Because the lookup service compares types by name only, two different types with the same name would match. Nevertheless, were the service object for such a mismatched type to arrive at the client, the deserialization process on the client side would detect the mismatch and throw an exception.
The reason the lookup service can get by with performing merely a string comparison of the type names, and ignoring all other class information, is that:
com.ibm
namespace have the same name, and it is not supposed to let the
world see anything it made whose name doesn't start with
"com.ibm" (or the reverse form of any other Internet
domain name it controls).
The last item in the previous list is one of the most important differences between a traditional name server, such as RMI Registry, and the Jini lookup service. A Java type name is associated with an interface and a semantic contract that gives meaning to the name. That meaning should be the same worldwide because of the first item in the list, the recommended naming convention. The name assigned to a remote object registered in an RMI Registry, by contrast, is an arbitrarily chosen name that by itself says nothing about the services offered by the referenced remote object.
One final difference between a traditional name server and the Jini
lookup server is that Jini service objects can in effect have multiple
names.
Objects can have many names because they
can be instances of many different types. Thus an object that has two
type names, "Printer" and "Copier" can be located
by two different clients looking for either a
"Printer" or "Copier" object.
Although ServiceTemplate gives clients a very powerful
way to express search criteria, it has some limits. The
fields of attribute sets in the ServiceTemplate's
attributeSetTemplates array must match exactly the
corresponding fields in the attribute sets of service items, unless
the template field is null. Null template fields are
wildcards, which match anything. The limit inherent in this forced
choice between exact matching and wildcard matching is that many
conceivable search criteria, such as criteria that include ranges of
values to match or sets of values to match, cannot be expressed with
a single ServiceTemplate.
Some perspective on the extent to which ServiceTemplate
can express search criteria can be found in the following
paragraph from the Jini Lookup Attribute Schema Specification:
No matter how much information it has at its disposal, a client of the lookup service will not always be able to find a single unique match without assistance when it performs a lookup. In many instances we expect that more than one service will match a particular query. Accordingly, both the lookup service and the attribute schema are geared toward reducing the number of matches that are returned on a given lookup to a minimum, and not necessarily to just one.
At the most recent Jini Community Summit, which took place in October
1999 in Annapolis, Alan Kaminsky of Xerox gave a presentation in which
he mentioned that the Printer Working Group encountered the
limitations of ServiceTemplate and
ServiceRegistrar.
Kaminsky said that although the Printer Working Group considered
proposing changes to ServiceRegistrar to obtain richer
lookup capabilities, it settled on proposing helper services to
provide those capabilities.
One reason the printer group decided to propose a helper service is that it wanted to include predicates in the search criteria, such as:
fieldalphais greater12AND fieldbetais less than or equal to98
Unlike any of the existing lookup or browsing methods of
ServiceRegistrar, sending a predicate could require
the lookup server to execute client code.
Ultimately, the goal of the ServiceRegistrar interface is to
provide a base set of lookup and browse capabilities to clients of Jini
lookup services.
More elaborate search capabilities could be implemented directly by
clients or provided by helper services.
One somewhat unusual aspect of Jini technology is that Sun has distributed the Jini source code under the Sun Community Source License (SCSL) and encouraged the formation of a community around the code. If you read the license agreement, you will likely get the idea that SCSL is not the typical open source license agreement, such as the GNU Public License, because the license basically states that Sun owns whatever the community contributes to the code. Ken Arnold made a reasonable case at the Annapolis Jini Community Summit. He said that the SCSL looked like it did because Sun was trying to make sure no one was able to modify the code base and distribute incompatible versions of it. (Although Ken mentioned no names explicitly, it was apparent to me that Sun was primarily worried about a company that Allen Holub often describes as: "A company that will remain unnamed that starts with 'M.'") The realities of SCSL are worth investigating, but I'll save that for a future article.
For now, I'd like to stimulate some thought and discussion about the ways
in which the ServiceRegistrar interface could be modified in
the future. The reason I feel this is justifiable is simply that the
Jini code, including the ServiceRegistrar interface, is
to some extent open source, and therefore the Jini community has
a say in how the code should evolve. I'm going
to describe what I came to consider missing functionality in the
ServiceRegistrar while thinking about how to add a UI
to a service. I'll describe my proposal for filling in this missing
functionality, and the response from the Jini team at Sun.
In the early days of the ServiceUI community project, Bob Scheifler of Sun, in response to a proposal in which clients invoked IU factory methods (methods that produce UIs for a Jini service) declared in the service object, posted these wise words:
Because the JLS allows wide variation in when resolution occurs in a JVM, it seems this style [of placing UI factory methods in the service object] can result in eager download of all of the UI classes, even if none of them are actually ever used. It may be desirable to find a design that can avoid this, if possible.
In response to a later proposal in which the UI factory methods were moved to attribute sets, Scheifler pointed out that eager downloading was still possible:
Deserializing the actual factory instance may also cause significant class loading and downloading. Compound this if the service registers with multiple UI entries. And all of this happens even if the client doesn't use any of the UIs.You may well respond that this is a general design issue with the lookup service: you can get all of the attributes or none, but you can't selectively choose, and you can't delay unmarshalling of them. I would agree this is an interesting area to mull over. But in the meantime, I think the UI design needs to be targeted at how things work today, and the footprint impact of attributes on clients needs to be considered with some care.
Bob Scheifler's and other posted comments got me thinking that perhaps the ServiceUI proposal should provide a way for low-resource clients to find a UI for a given service without requiring that the client download the entire service item. As I describe later in this article, a more direct way to deal with the eager class loading problem is via an appropriate scheme to keep UI and service codebases separate. Nevertheless, I felt it made sense that some clients might not be able to deserialize all the objects contained in a service item because, after all, Jini is targeted at embedded devices, which often have limited memory.
When I went to address this concern for low-resource clients, I was
surprised to discover that the ServiceRegistrar interface
offered
few options for low-resource clients. For a while, I dabbled with an
approach that relied on getFieldValues() to grab a UI factory
object out of an Entry. But in the end, I realized that
getFieldValues() couldn't really help, because there's no
limit to the number of items it will return.
A low-resource client could narrow its search by using
getServiceTypes(), getEntryClasses(), or
by just picking a single Entry class. But because
getEntryClasses() removes duplicates, the low-resource
client has no way of knowing how many entries of that type exist. It could
be one, or a hundred, or a million. Thus, when the client invokes
getFieldValues(), it might get a million objects
back.
Ultimately, I concluded that the best way to address the
low-resource client requirements of ServiceUI would be to add two
new methods to ServiceRegistrar. This suggestion was
somewhat controversial, in part because
some felt we were trying to fulfill special requirements of
finding UIs by changing ServiceRegistrar. When in reality,
these additional methods I proposed have nothing to do
with UI per se. They have to do with enabling resource-poor (especially
memory-poor) clients to interact with the lookup service in general. I
concluded that the options of resource-poor clients are quite limited given
the current
ServiceRegistrar interface no matter what the
clients are
trying to do with the lookup service. I came to this
realization while thinking about ServiceUI, but it is
a general problem, not one that's exclusive to getting
a UI for a service or narrowing a search for services
based on UIs offered.
Here are the two methods I suggested we add to
ServiceRegistrar to empower resource-poor clients.
The first is the getServiceIDs() method:
public ServiceIDMatches getServiceIDs(ServiceTemplate tmpl,
int maxMatches) throws java.rmi.RemoteException;
[This method] looks at all service items that match the specified template, and returns at mostmaxMatchesServiceIDs from that set of matching service items, plus the total number of entries that matched. The return value is nevernull, and the returnedServiceIDarray is onlynullifmaxMatchesis zero. Duplicate entries are not possible because the lookup service enforces that each registered service has a uniqueServiceID. The order ofServiceIDs within the returned array is arbitrary. An empty array is returned if no such entries or matching items exist. If a returnedServiceIDcannot be deserialized (this should never happen), that element of the returned array is set tonulland no exception is thrown.Here is the
ServiceIDMatchesclass:package net.jini.core.lookup; public class ServiceIDMatches extends java.lang.Object implements java.io.Serializable { public ServiceID[] ids; public int totalMatches; }
The second one I suggested is the getEntries() method:
public EntryMatches getEntries(ServiceTemplate tmpl,
Entry[] entryTmpl, int maxMatches) throws java.rmi.RemoteException;
[This method] looks at all service items that match the specified template, finds every entry (among those service items) that matches any entry template that appears in theentryTmplfield, and returns at mostmaxMatchesentries from that set of matching entries, plus the total number of entries that matched. The return value is nevernull, and the returnedEntryarray is onlynullifmaxMatchesis zero. Duplicate entries are eliminated, and the order of entries within the returned array is arbitrary. An empty array is returned if there are no such entries or no matching items. If a returned class cannot be deserialized, it is set tonulland no exception is thrown.Here's the
EntryMatchesclass:package net.jini.core.lookup; public class EntryMatches extends java.lang.Object implements java.io.Serializable { public Entry[] entries; public int totalMatches; }
The idea of these two browsing methods,
getServiceIDs()
and getEntries(), is to enable resource-poor clients
to control how many objects get returned from lookup service. A
resource-poor client needs to be able to trade off memory consumed by
returned objects against the number of lookup or
browsing method invocations it must call on ServiceRegistrar. I
call the proposed methods "browsing methods" because they don't return
a service object, just information (attributes and service IDs) about
services that match a template. Thus, only methods named
lookup() return an actual service object. The browsing
methods -- including the already existing browsing methods
getServiceTypes(), getEntryClasses, and
getFieldValues() -- enable clients to narrow their search
before getting an actual service object via a lookup()
method.
The lookup() method that returns a
ServiceMatches object enables a client to find out the total
number of matches, and to control the maximum number of service items
returned. A resource-poor
client could use this method, if it is willing to download an entire service
item, by specifying a maxMatches of the number one. But if the
returned service turns out to be inappropriate (which it discovers only
after looking through its attributes locally) it must do another
lookup(). Unfortunately, the client can't say "give me a
different match." It must simply hope that the next time it says one in the
maxMatches field and calls lookup(), it is given a
different service. If it is not given a different service, it must say two
in the maxMatches field.
Now if the new service obtained with a maxMatches of two
doesn't
work, the client must requery with a maxMatches of three,
and so forth. But what's worse, all this discussion
assumes the resource-poor client is willing to download entire service
items, which I believe is a questionable assumption.
If the resource-poor client had a getServiceIDs() method,
as I defined previously, it could get back a set of service IDs for services
that match any template. It can bound the number of service IDs by
specifying some maximum in maxMatches. Since class
ServiceID is final, no JAR files would need to be downloaded to
the
client.
Once the client has a list of matching service IDs, it can use the
other browsing methods, such as getEntries(),
getEntryClasses,
and getFieldValues to look for the best service or UI
for a service. The client can do this by looking at one registered service
at a time (by specifying in turn each of the service IDs it got back from
getServiceIDs) until it finds the
appropriate service or UI. In other words, getServiceIDs()
returns an array that can serve as a client's "to-do list" of further
queries to make, each of which involves only one service item.
The getEntries() method enables clients to get ahold of
entire entries, without requiring that the client download all the entries
in
the service item (as is done with the two-parameter lookup()
method, which returns a ServiceMatches). It also enables
clients
to limit the maximum number of entries returned.
Although clients can grab fields, one named field
at a time, via the getFieldValues() method, this approach
has three potential problems. First, if a client wants an entire
entry, getFieldValues() requires more method invocations
(and therefore more network hits) than getEntries(),
because the client must invoke getFieldValues()
for each field of the entry.
Second, it is not always possible to assemble an entire entry from the
objects returned by
getFieldValues(). For example, imagine I invoke
getFieldValues() twice with the same template and
setIndex. First I set my field parameter to
"name", and I get back the values "Bob" and
"Sally". Next time I set the field parameter to
"hobby", and I get back the values "fishing" and
"stamp collecting". First of all, I don't know how many
matching entries had any of these field values, because duplicates are
eliminated. But even assuming only two entries matched my query, I'm still
unable to assemble these
two entries. I know one entry has a name field of
"Bob" and the other has a name field of
"Sally". I also know that one entry has a
hobby field of "fishing" and the other has a
hobby field of "stamp collecting". The trouble is,
because these objects are returned in arbitrary order, I don't know whether
Bob likes fishing or stamp collecting. Likewise, I don't
know whether Sally likes fishing or stamp collecting. All I know is that if
Sally likes fishing, then Bob likes stamp collecting, and vice versa. Thus,
I
can't reconstruct entries from the objects returned by
getFieldValues(). Although I can get at individual attributes
with
getFieldValues(), I am unable to get information about the
relationships between the various attributes collected into a single
attribute set.
The third problem with getFieldValues() is that it only
returns fields. Because Entrys are just objects that adhere
to some conventions, they can contain methods as well as fields. To
access those methods, a client needs to get the entire entry.
Currently, the only way for a client to access a method declared in
an Entry of the attribute sets is to use the
lookup() that returns a ServiceMatches object,
which includes all the entries for all the returned service items.
Rather than changing ServiceRegistrar,
we could define a helper service interface that offers the two
browsing methods, getEntries() and
getServiceIDs(), and register it as a service.
Low-resource clients could perform their queries via the helper
service. Nevertheless, my sense is that these two browsing methods offer
a fundamental enough service to low-resource clients
that they (or something like them) belong in ServiceRegistrar.
In December 1999, members of the Jini team at Sun met to discuss the
current state of the ServiceUI project and write a response to the ServiceUI
proposal. By some twist of fate, the Jini team picked up an old copy of the
ServiceUI proposal and as a result did not see the case made for two new
ServiceRegistrar methods. Nevertheless, the Jini team did
address the low-resource client issue in their response. They stated that
they felt "a careful
treatment of codebase issues will go a long way towards the treatment of
limited
resource devices." Jim Waldo, chief Jini architect at Sun, stated in
his ServiceUI response that:
With [a proper] separation of codebases, our belief is that for now, we
can reasonably assume that clients of all kinds generally should be able to
download an entire ServiceItem from the lookup service,
and that the complexities of the severely resource-constrained client case
can
be put off until such time as the issue of resource constraints can be dealt
with in a broader context, with a more complete set of requirements.
In effect, the Jini team said that for the time being at least, we should assume that any client will be able to download an entire service item. To facilitate this goal in ServiceUI, the Jini team suggested we make sure the class files for UIs can be kept in separate JAR files from those of the service object and attribute sets. This separation helps reduce the memory required to download an entire service item, because a client need not download a UI's class files unless the client actually plans to use the UI.
Upon reading the Jini team's ServiceUI response, I decided I liked the assumption that any Jini client should be able to download an entire service item. The upcoming Jini release will include a ServiceUI specification and the first batch of ServiceUI code. In this release, we'll assume all clients can download any service item in its entirety, and make sure UI codebases can be kept separate from the service object and attribute set codebases.
However, it may still be desirable to eventually add
functionality such as that provided by the two browsing
methods proposed in this article.
Given the current ServiceRegistrar interface, even if a
low-resource client is able to download any service item in
its entirety, it may not be able to find a desired service by
downloading only one service item at a time.
In this column, I want not only to describe Jini technology as it
currently exists, but also to encourage thinking and discussion in
the Jini community about how the technology should evolve. This article
describes the power and flexibility provided by the
ServiceRegistrar interface to the Jini lookup service, and
pointed out some of its limitations.
To discuss the material presented in this article, visit the discussion forum at: http://www.artima.com/jini/jf/lookup/index.html
listserv@java.sun.com. In the body of the message, type subscribe jini-users:
About the author
Bill Venners has been writing software professionally for 14 years.
Based in Silicon Valley, he provides software consulting and training
services and maintains a Web site for Java and Jini developers, artima.com. He is author of the
book: Inside
the Java Virtual Machine, published by McGraw-Hill.
This article was first published under the name Finding services with the Jini lookup service in JavaWorld, a division of Web Publishing, Inc., February 2000.
|
Sponsored Links
|