# Jini(TM) Service Discovery Utilities
v2.0_002 Release Notes

### Description

The class `net.jini.lookup.ServiceDiscoveryManager` is a utility class that can be used to help a Jini technology-enabled service or client (Jini service or Jini client) acquire services of interest that are registered with lookup services with which the service or client wishes to interact.

The specification for the `ServiceDiscoveryManager` utility is contained in the Jini(TM) Service Discovery Utilities Specification, which is available in html.

### Changes since the v1.2.X release

The Service Discovery Filtering Mechanism
  The specification of the `ServiceItemFilter` interface has been changed to allow for an expanded interpretation of what it means to filter a service reference selected as a candidate for discovery. Specifically, instances of `ServiceItemFilter` can now be defined to perform proxy preparation. Thus, through the client-defined filter (in particular, through the `check` method), clients can request that the `ServiceDiscoveryManager`, rather than the client itself, perform any desired proxy preparation as part of the service discovery process.

  This is important because if the client performs preparation of its discovered proxies outside of the `ServiceDiscoveryManager`, the client risks encountering a cycle where a matching service is discovered by the `ServiceDiscoveryManager`, is found to be untrusted when the client prepares the proxy, is discarded by the client because it is untrusted, and is then ultimately re-discovered because it still matches the original discovery criteria. Such a cycle will generally repeat indefinitely because the service is not likely to become trusted at any point in the future. Supplying the `ServiceDiscoveryManager` with the means to perform proxy preparation on the client's behalf provides the client with a mechanism for avoiding such a cycle.

  For more information, please refer to the section titled, SD.5.2 The `ServiceItemFilter` Interface, contained in the Jini(TM) Service Discovery Utilities Specification.

The Service Discovery Event Mechanism
  The specification of the `ServiceDiscoveryListener` interface has been updated to clarify how the filtering mechanism and the event mechanism provided by instances of `net.jini.lookup.LookupCache` interact.

  Additionally, this specification has been changed to specify that instances of the `LookupCache` now use the method `net.jini.io.MarshalledInstance.fullyEquals` to determine when a service has changed in some fundamental way (for example, when a service is replaced with a new version).

  For more information, please refer to the section titled, SD.5.4 The `ServiceDiscoveryListener` Interface, contained in the Jini(TM) Service Discovery Utilities Specification

Configuration
  The `ServiceDiscoveryManager` utility now provides a constructor that takes a `Configuration` argument to configure the newly created instance. This implementation supports configuration entries having names that are associated with the component name `net.jini.lookup.ServiceDiscoveryManager`. The entries recognized by this instance of the `ServiceDiscoveryManager` utility can be used to configure behavior such as:

  - Proxy preparation
  - Exporting remote event listeners
  - Lookup service discovery
  - Lease management
  - Thread allocation and management

  A full list of supported configuration entries is given in this utility's class documentation.

Logging
  This implementation of `ServiceDiscoveryManager` now logs diagnostic information to the `Logger` named `net.jini.lookup.ServiceDiscoveryManager`. A description of what information is logged at what logging levels is given in this utility's class documentation.

System Properties
  This implementation of the `ServiceDiscoveryManager` utility previously supported a number of system properties to control certain aspects of its behavior. Those system properties are no longer supported. The behavior that those system properties affected is now controlled through the configuration model or the `Logger` supported by this utility. The following table lists the system properties that are no longer supported, along with the mechanism (configuration entry or logger) that has replaced the property.

  Old Property Name | New Mechanism
  --- | ---
  `com.sun.jini.sdm.discardWait` | configuration entry named `discardWait`
  `com.sun.jini.sdm.nTaskThreads` | configuration entry named `cacheTaskManager`
  `com.sun.jini.sdm.debug` | logger named `net.jini.lookup.ServiceDiscoveryManager`

Bug Fixes of Interest
  The implementation of the `ServiceDiscoveryManager` has been redesigned in a number of places in this release; in particular, in areas related to the processing and synchronization of the tasks executed by the `ServiceDiscoveryManager`. As such, the following bugs that have been addressed in this release may be of interest:

4675746: ServiceDiscoveryManager may have a race condition in addService method
  In prior releases, the operations performed in the `addService` method were not performed atomically. This could result in a race condition. The `addService` method was removed as part of the redesign; and the code that now performs similar duties no longer encounters the race condition. This bug is related to bug 4707125 (see below).

4712396: SDM LookupCacheImpl.addListener sends addService event to other listeners
  When a `ServiceDiscoveryListener` is added to a lookup cache, only that new listener should be notified of the existing services of interest in that cache. In prior releases, it appeared that when a second (or third or fourth, etc.) listener was added to a cache, all listeners that were currently registered with the cache would receive a `serviceAdded` event for the services of interest; even though the previously registered listeners had already received the expected `serviceAdded` event when each was originally added to the cache. This bug has been fixed.

4707125: SDM LookupCache task synchronization insufficient
  In prior releases, the `ServiceDiscoveryManager.CacheTask.runAfter` method was implemented to always return `false`. This meant tasks could run concurrently that would end up manipulating the same `ServiceItemReg`, but the methods on `ServiceItemReg` aren't synchronized and the tasks tended not to have any additional synchronization when calling those methods. In particular, both `addProxy` and `removeProxy` could be called concurrently, for example if a `LookupTask` for one lookup service and a `ProxyRegDropTask` for another lookup service executed concurrently and they both contained the same service. This issue of task synchronization has been addressed in this release.

4843263: SDM has race condition in LookupCacheImpl.registerListenerTask
  In the `run` method of `LookupCacheImpl.RegisterListenerTask`, there is a call to `registerListener`. In that method, a listener is registered with all discovered lookup services to receive `ServiceEvent`s of interest. That method returns an instance of `EventReg` which is placed in the `eventRegMap`.

  When a `ServiceEvent` does arrive, the `LookupListener.notify` method calls `notifyServiceMap`, which calls `updateSeqNumber`, which retrieves from the `eventRegMap`, the `EventReg` corresponding to the lookup service that sent the event, and then performs appropriate operations on the element retrieved from the map.

  In prior releases, if the lookup service sent the event before the `EventReg` could be constructed and placed in the `eventRegMap`, upon receiving the event, `updateSeqNumber` would attempt to retrieve from the map the `EventReg` corresponding to the lookup service that sent the event. But because the `EventReg` had not yet been placed in the map, `null` would be returned, and `updateSeqNumber` would encounter a `NullPointerException` when it tried to update the sequence number of that `null` item.

  This bug has been fixed.

A Note On Service Reference Comparison
  The ability to appropriately compare two different service references is very important to the `ServiceDiscoveryManager` in general, and the `LookupCache` in particular. Currently, there are three mechanisms used to accurately compare service references. Each mechanism is applied in different situations, to achieve different goals.

  When storing and managing service references, it is important to be able to determine when two different references (proxies) refer to the same back-end service (such references are referred to as duplicates) so that the storage and management of any duplicate references can be avoided. In addition to identifying duplicate references, the ability to determine when a previously discovered service has been replaced with a new version is also important so that consistent state may be maintained, and entities that wish to know about such events can be informed. Finally, when an entity wishes to discard (make eligible for rediscovery) a particular service reference the entity received from the `LookupCache`, it is important for the `LookupCache` to be able to compare the reference provided by the entity to each of the previously stored references so that the appropriate reference can be accurately selected for discard.

  Comparison by `net.jini.core.lookup.ServiceID`

  To identify, and thus avoid storing, duplicate service references, the `LookupCache` compares the instances of `ServiceID` associated with each reference. Recall that an individual well-behaved service of interest will usually register with multiple lookup services, and for each lookup service with which that service registers, the `LookupCache` will receive a separate event containing a reference to the service. When the `LookupCache` receives events from multiple lookup services, the `ServiceID` (retrieved from the service reference in the event) is used to distinguish the service references from each other. In this way, when a new event arrives containing a reference associated with the same service as an already-stored reference, the `LookupCache` can determine whether or not the new reference is a duplicate; in which case, the duplicate is ignored.

  Comparison by `net.jini.io.MarshalledInstance.fullyEquals`

  With respect to determining when a previously discovered service has been replaced with a new version, the `LookupCache` typically relies on the event mechanism of the lookup service(s) with which that service is registered to indicate that such an event has occurred. But there are situations where the events from the lookup services do not provide enough information to make such a determination. In those cases, the `LookupCache` employs `MarshalledInstance.fullyEquals` to make the determination.

  When a well-behaved service is replaced with a new version, the new version is typically re-registered with each lookup service with which the old version is registered. As described in the Jini(TM) Technology Core Platform Sepcification, "Lookup Service", each lookup service with which this re-registration process occurs, first sends a service-removed event (`TRANSITION_MATCH_NOMATCH`), and then sends a separate service-added event (`TRANSITION_NOMATCH_MATCH`). In this case, there is no ambiguity, and thus no need for the `LookupCache` to compare the new and old service references. This is because the combination of service-removed and service-added events from each lookup service is an explicit indication that the service has been replaced. Note that, as described in the specification, the lookup service only sends a service-changed event (`TRANSITION_MATCH_MATCH`) when the attributes of the service have been modified; not when the service itself has been changed (replaced). Thus, if the `LookupCache` receives a `TRANSITION_MATCH_MATCH` event, then it is guaranteed that the service referenced in that event has not been replaced with a new version.

  Whenever the following conditions are satisfied, the `LookupCache` will use `MarshalledInstance.fullyEquals` to compare two service references:

  - The references are duplicates (same `ServiceID`)
  - The possibility exists that the references may refer to different versions of the service

  When determining whether or not these conditions (in particular, the second condition) are satisfied, the `LookupCache` generally takes a conservative approach. That is, only when it is absolutely sure that two duplicates refer to the same version (such as when a `TRANSITION_MATCH_MATCH` event is received), will the `LookupCache` refrain from using `MarshalledInstance.fullyEquals` to compare the duplicate references; otherwise, duplicate references are always compared using `MarshalledInstance.fullyEquals`.

  One example of a situation where the `LookupCache` employs `MarshalledInstance.fullyEquals` is the situation where a lookup service of interest is newly discovered or rediscovered. In this situation, the `LookupCache` retrieves a snapshot of the services of interest that currently reside in that lookup service. This is done so that previously undiscovered service references, as well as new versions of previously discovered service references, can both be stored, duplicates can be ignored, and clients can be sent the appropriate notifications. Whenever a reference from the snapshot is a duplicate of a previously discovered service reference, the `LookupCache` always compares the two references using `MarshalledInstance.fullyEquals`. This is because the possibility always exists that the references may refer to different versions of the service.

  Another example of a situation where the `LookupCache` employs `MarshalledInstance.fullyEquals` is when a `TRANSITION_NOMATCH_MATCH` event is received that contains a reference that is a duplicate of a previously discovered reference. When such an event is received, the `LookupCache` must allow for the possibility that the reference contained in the event refers to a different version of the service than that referenced by the previously discovered service reference. This is because the event may represent either the second half of a `TRANSITION_MATCH_NOMATCH`/`TRANSITION_NOMATCH_MATCH` (remove/add) event pair, or it may be a notification of the initial registration of a new version of the service with one of the (multiple) lookup services targeted by the `LookupCache` during the service discovery process.

  To understand the last example described above, consider the situation where a service initially registers with one lookup service and then registers with a second lookup service. If the same version of the service is registered with each lookup service, both lookup services will send the same event: a `TRANSITION_NOMATCH_MATCH` event to indicate that a new service has registered with the associated lookup service. In this case, there is no ambiguity for the `LookupCache`; the second reference can be safely ignored because the references are duplicates that refer to the exact same service.

  But suppose that prior to registering with the second lookup service, the service is replaced with a new version. In that case, the second lookup service will still send a `TRANSITION_NOMATCH_MATCH` event, but if the appropriate action is not taken to determine that the old version of the service has been replaced, the new version of the service will be ignored by the `LookupCache`. This is because the two references will have the same `ServiceID`, and thus the `LookupCache` will identify the two references as duplicates; ultimately ignoring the new reference. Of course, if the service is well-behaved, because the new version of the service will eventually re-register with the first lookup service, that lookup service will eventually send a `TRANSITION_MATCH_NOMATCH` event followed by a `TRANSITION_NOMATCH_MATCH` event to indicate that the service has been replaced. But the `LookupCache` must still identify and handle this situation in order to prevent possible state corruption, even though that corruption may be only temporary.

  Thus, whenever a `TRANSITION_NOMATCH_MATCH` event is received and the associated service reference is a duplicate of a previously discovered reference, the `LookupCache` will always compare the two references using `MarshalledInstance.fullyEquals` to determine whether or not the references refer to the same version of the service. When they do reference the same version, the `LookupCache` ignores the duplicate reference; otherwise, the `LookupCache` sends a service-removed event followed by a service-added event to indicate that the old version of the service has been replaced.

  Comparison by `equals`

  The mechanism employed by the `LookupCache` to select (from storage) a given reference for discard is the `equals` method provided by the discovered service itself. The `LookupCache` relies on the provider of each service to override the `equals` method with an implementation that allows for the identification of the service reference an entity wishes to be discarded. Although the default implementation of `equals` often times may be sufficient for proper identification, service providers are still encouraged to provide each service with its own well-defined implementation of `equals`.

  In addition to the `equals` method, each service should also provide a proper implementation of the `hashCode` method. This is because references to the service may be stored in, or interact with, container classes (for example, `HashMap`) where the service's `equals` and `hashCode` methods may be invoked "under the covers" by the container object with which the service is interacting. From the point of view of the `ServiceDiscoveryManager` and the `LookupCache`, providing an appropriate implementation for both the `equals` method and the `hashCode` method is a key characteristic of good behavior in a Jini service.

Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.