You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@river.apache.org by Peter Firmstone <ji...@zeus.net.au> on 2013/05/07 12:17:18 UTC

ServiceDiscoveryManager lookp specification

This test smells broken?

I modified the output to read in milliseconds.

Relevant ServiceDiscoveryManager method under test:

public ServiceItem[] lookup(ServiceTemplate tmpl,
                                 int minMatches,
                                 int maxMatches,
                                 ServiceItemFilter filter,
                                 long waitDur)
                                  throws InterruptedException,
                                         RemoteException {...}



To summarise what's happens (occassionally):

CASE 1:

    * ServiceDiscoveryManager.lookup  is called and blocks waiting until
      it receives the minimum number of ServiceItems.
    * If the required number of ServiceItem's are discovered late, the
      waitDur limit will be approached when lookup returns.
          o The resolution of the test is to the nearest second, not
            millisecond.


    Firstly I don't believe this is the intent of the Jini Standard
    SD4.1.3 The blocking feature of lookup, which states:

        As noted above, each category contains a version of |lookup|
        that provides a feature in which the entity can request that if
        the number of service references found throughout the available
        lookup services does not fall into a desired range, the method
        will wait a finite period of time until either an acceptable
        minimum number of service references are discovered or the
        specified time period has passed.


    In this case the specified time period hasn't quite passed, however
    an acceptable minimum number of service references are returned and
    the test fails.  This failure is rare.


CASE 2:

              * 3. while lookup() is blocking, if enough new services are
              *    registered so that the acceptable minimum is achieved,
              *    lookup() will return immediately; that is, even if there
              *    is more time left on the wait period, lookup() will not
              *    wait for more services beyond the minimum.
              *
              *    For example, if 3 services are initially registered and
              *    lookup is called with min = 4 and max = 7, then lookup()
              *    will find the 3 services and then wait for more 
services to
              *    be registered. Suppose that while lookup() is blocking
              *    another 5 services are registered, bringing the total 
number
              *    of services to 8. In this case, lookup() will stopping
              *    waiting and return 4 services (the minimum), not the
              *    maximum 7.

While the test performs this check, it isn't implemented in 
ServiceDiscoveryManager, instead it's left up to chance that the lookup 
method, waiting on an object monitor will obtain the monitor as soon as 
it's notified.  However if multiple threads contend for the lock, it's 
very unlikely the thread scheduler will give the object monitor to the 
waiting thread, instead the most recently blocking thread is likely to 
get the monitor.

This requirment in the test appears to be additional to the 
specification and would require ServiceDiscoveryManager be modified, to 
use some kind of fair Lock scheme.  However doing this would 
unnecessarily complicate ServiceDiscoveryManager, instead it would be 
simpler to return the number of ServiceItem's found up to the maxMatches 
at the time the lock is obtained.

CASE 1 test results:

Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
Time is Mon May 06 22:17:31 EST 2013
Starting test in separate process with command:
'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java' 
-Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager 
-Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy 
-Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp 
C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3.jar 
-ea -esa -client '-Djava.ext.dirs=C:\Program 
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
-Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081 
-Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy 
-Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
-Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar 
-Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar 
-Dcom.sun.jini.qa.harness.runjiniserver=true 
-Dcom.sun.jini.qa.harness.runkitserver=true 
-Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties 
-Dcom.sun.jini.qa.harness.testhosts= 
-Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging 
-Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
-Dcom.sun.jini.test.port=9082 
-Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy 
'-Djava.ext.dirs=C:\Program 
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
com.sun.jini.qa.harness.MasterTest 
com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
com.sun.jini.qa.harness.TestException:  -- blocked longer than expected 
-- requested block = 60000 millisecond(s), actual block = 59999 
millisecond(s)
     at 
com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
     at 
com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
     at 
com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
     at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
     at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)

TIME: 10:20:02 PM

Test process was destroyed and returned code 1
com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
Test Failed: Test Failed: com.sun.jini.qa.harness.TestException:  -- 
blocked longer than expected -- requested block = 60000 millisecond(s), 
actual block = 59999 millisecond(s)

Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
Time is Tue May 07 10:40:30 EST 2013
Time is Tue May 07 10:40:30 EST 2013
Starting test in separate process with command:
Starting test in separate process with command:
'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java' 
-Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager 
-Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy 
-Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp 
C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3.jar 
-ea -esa -client '-Djava.ext.dirs=C:\Program 
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
-Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081 
-Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy 
-Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
-Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar 
-Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar 
-Dcom.sun.jini.qa.harness.runjiniserver=true 
-Dcom.sun.jini.qa.harness.runkitserver=true 
-Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties 
-Dcom.sun.jini.qa.harness.testhosts= 
-Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging 
-Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
-Dcom.sun.jini.test.port=9082 
-Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy 
'-Djava.ext.dirs=C:\Program 
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
com.sun.jini.qa.harness.MasterTest 
com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java' 
-Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager 
-Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy 
-Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp 
C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3.jar 
-ea -esa -client '-Djava.ext.dirs=C:\Program 
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
-Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081 
-Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy 
-Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
-Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar 
-Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar 
-Dcom.sun.jini.qa.harness.runjiniserver=true 
-Dcom.sun.jini.qa.harness.runkitserver=true 
-Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties 
-Dcom.sun.jini.qa.harness.testhosts= 
-Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging 
-Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
-Dcom.sun.jini.test.port=9082 
-Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy 
'-Djava.ext.dirs=C:\Program 
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
com.sun.jini.qa.harness.MasterTest 
com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
com.sun.jini.qa.harness.TestException:  -- blocked longer than expected 
-- requested block = 60000 millisecond(s), actual block = 59997 
millisecond(s) wait error = 0
com.sun.jini.qa.harness.TestException:  -- blocked longer than expected 
-- requested block = 60000 millisecond(s), actual block = 59997 
millisecond(s) wait error = 0
     at 
com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
     at 
com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
     at 
com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
     at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
     at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)




Relevant Section of Jini Specification:


          SD.4.1.3 The |lookup| Method

The |lookup| method queries each available lookup service in the managed 
set for service reference(s) that match criteria defined by the entity 
that invokes this method. Entities typically employ this method when 
they need infrequent access to services and when the cost of making 
remote queries is outweighed by the overhead of maintaining a local 
cache (for example, because of resource limitations).

The |lookup| method has four versions, each version falling into one of 
two categories: those versions of this method that return a single 
instance of |ServiceItem| and those versions that return a set of 
service references as an array of |ServiceItem| objects.

Two arguments are common to all versions of this method: an instance of 
|ServiceTemplate| and an instance of |ServiceItemFilter|.

Within each category, the versions of |lookup| differ only in whether or 
not a particular version provides what is referred to as a "wait" (or 
blocking) feature. That is, each category contains both a non-blocking 
version of |lookup| which returns immediately when unable to find the 
desired service, and a blocking version which returns only after waiting 
a specified amount of time for the desired service to be discovered. The 
particular version of |lookup| that an entity employs is typically 
determined by the entity's intended usage pattern.

The descriptions that follow refer to all versions of the |lookup| 
method, except where explicitly noted.

The |tmpl| argument and the |filter| argument both have semantics 
identical to that defined for these arguments in the description of the 
|createLookupCache| method above. In particular,

    * A |null| reference value for the |tmpl| parameter is treated as
      the equivalent of a "wildcarded" |ServiceTemplate|.

    * If |null| is the value for the |filter| parameter, only template
      matching will be employed to find the desired services.

    * The effects of modifying the contents of the |tmpl| parameter
      while the invocation is in progress are unpredictable and undefined.

If no service can be found that matches the desired criteria, then the 
versions of |lookup| from the first category--those that return a single 
instance of |ServiceItem|--will return |null|, whereas the versions from 
the second category--those that return an array of |ServiceItem| 
instances--will return an empty array.

The versions of |lookup| from the first category can be used in a 
fashion similar to the first form of the |lookup| method defined in the 
|ServiceRegistrar| interface described in the /Jini Lookup Service 
Specification/. That is, an entity would typically invoke one of these 
versions of |lookup| when it wishes to find a /single/ service reference 
and the particular lookup service with which that service reference is 
registered is unimportant to the entity.

Each version of |lookup| defined in the |ServiceDiscoveryManager| 
differs with the corresponding version of |lookup| in |ServiceRegistrar| 
in the following ways:

    * The versions of |lookup| defined in the |ServiceDiscoveryManager|
      query /multiple/ lookup services (the order in which the lookup
      services are queried is dependent on the implementation).

    * The versions of |lookup| defined in the |ServiceDiscoveryManager|
      can apply additional selection criteria, in the form of a filter
      object, when deciding whether a service reference found through
      standard template matching should be returned to the entity.

The versions of |lookup| that return an array of |ServiceItem| objects 
can be used in a fashion similar to the second form of |lookup| defined 
in the |ServiceRegistrar| interface. That is, an entity would typically 
invoke these versions of |lookup| when it wishes to find /multiple/ 
service references that satisfy the input criteria. Each of the versions 
of |lookup| that return an array of |ServiceItem| objects takes as one 
of its arguments an |int| parameter, |maxMatches|, that represents the 
maximum number of matches that should be returned. The array returned by 
these methods will contain no more than |maxMatches| service references, 
although it may contain fewer than that number.

As with the versions of |lookup| that return a single instance of 
|ServiceItem|, multiple queries and filtering are also notable 
differences between the second-category versions of this method and 
their counterpart in |ServiceRegistrar|.

For each version of |lookup|, whenever a lookup service query returns a 
|null| service reference, the filter is bypassed, and the service 
reference is excluded from the return object. On the other hand, if the 
query returns a non-|null| service reference in which the associated 
array of attribute contains one or more |null| elements, the filter is 
still applied and the service reference is included in the return object.

Each version of |lookup| may be confronted with duplicate references 
during a search for a service of interest. This is because the same 
service may register with more than one lookup service in the managed 
set. As with the cache, when a set of service references is returned by 
|lookup|, each service reference in the return set will be unique with 
respect to all other service references in the set, as determined by the 
|equals| method provided by each reference.

If it is determined that a lookup service is unavailable (due to an 
exception or some other non-fatal error) while interacting with a lookup 
service from the managed set, all versions of |lookup| will invoke the 
|discard| method on the instance of |DiscoveryManagement| being employed 
by the |ServiceDiscoveryManager|. Doing so will result in the 
unavailable lookup service being discarded and made eligible for 
rediscovery.

Recall that the propagation of modifications to a service's attributes 
across a set of lookup services typically occurs asynchronously. It is 
for this reason that while invoking |lookup| to find a set of matching 
services, it is possible that the set returned may contain multiple 
references having the same service ID with different attributes. Note 
that although this sort of inconsistent state can also occur if the 
entity employs a cache, the cache will eventually reflect the correct 
state.

The Blocking Feature of |lookup|

As noted above, each category contains a version of |lookup| that 
provides a feature in which the entity can request that if the number of 
service references found throughout the available lookup services does 
not fall into a desired range, the method will wait a finite period of 
time until either an acceptable minimum number of service references are 
discovered or the specified time period has passed.

The versions of |lookup| providing this blocking feature each takes as 
one of its parameters a value of type long that represents the number of 
milliseconds to wait for the service to be discovered. In addition to 
|RemoteException| (described previously for these methods), each of 
these versions of |lookup| may throw an |InterruptedException|.

One of these blocking versions of |lookup| implicitly uses a value of 
one for both the acceptable minimum and the allowable maximum number of 
service references to discover. The other blocking version requires that 
the entity specify the range through the |minMatches| and |maxMatches| 
parameters, respectively.

Prior to blocking, each of these versions of |lookup| first queries each 
available lookup service in an attempt to retrieve a satisfactory number 
of matching services. Whether or not the method actually blocks is 
dependent on how many matching service references are found during the 
query process. Blocking occurs only if after querying /all/ of the 
available lookup services, the number of matching services found is less 
than the acceptable minimum. If the waiting period (measured from when 
blocking first begins) passes before that minimum number of service 
references is found, the method will return the service references that 
have been discovered up to that point. If the waiting period passes and 
no services have been found, |null| or an empty array (depending on the 
version of |lookup|) will be returned.

If, after querying all of the available lookup services, the number of 
services found to satisfy the desired criteria is greater than or equal 
to the specified minimum but less than the specified maximum, the method 
will return the currently discovered service references without 
blocking. If the initial query process produces the desired maximum 
number of service references, the method will return the results 
immediately.

The blocking versions of |lookup| are quite useful to entities that 
cannot proceed until such a service of interest is found. If a 
non-positive value is input to the |waitDur| argument, then the method 
will not wait. It will simply query the available lookup services and 
employ the return semantics described above.

The values of the |minMatches| and |maxMatches| arguments must both be 
positive, and |maxMatches| must be greater than or equal to 
|minMatches|; otherwise, an |IllegalArgumentException| will be thrown.

The blocking versions of |lookup| make a concurrency guarantee with 
respect to the discovery of new lookup services during the wait period. 
That is, while waiting for the desired service reference(s) to be 
discovered, if one or more of the targeted--but previously 
unavailable--lookup services is discovered and added to the managed set, 
those new lookup services will also be queried for the service(s) of 
interest.

In addition, the blocking versions of |lookup| throw 
|InterruptedException|. When an entity invokes either version with valid 
parameters, the entity may decide during the wait period that it no 
longer wishes to wait the entire period for the method to return. Thus, 
while the method is blocking on the discovery of matching service(s), it 
may be interrupted by invoking the |interrupt| method from the |Thread| 
class. The intent of this mechanism is to allow the entity to interrupt 
a blocking |lookup| in the same way it would a sleeping thread.



Re: ServiceDiscoveryManager lookp specification

Posted by Peter Firmstone <ji...@zeus.net.au>.
On 7/05/2013 8:17 PM, Peter Firmstone wrote:
> This test smells broken?
>
> I modified the output to read in milliseconds.
>
> Relevant ServiceDiscoveryManager method under test:
>
> public ServiceItem[] lookup(ServiceTemplate tmpl,
>                                 int minMatches,
>                                 int maxMatches,
>                                 ServiceItemFilter filter,
>                                 long waitDur)
>                                  throws InterruptedException,
>                                         RemoteException {...}
>
>
>
> To summarise what's happens (occassionally):
>
> CASE 1:
>
>    * ServiceDiscoveryManager.lookup  is called and blocks waiting until
>      it receives the minimum number of ServiceItems.
>    * If the required number of ServiceItem's are discovered late, the
>      waitDur limit will be approached when lookup returns.
>          o The resolution of the test is to the nearest second, not
>            millisecond.
>
>
>    Firstly I don't believe this is the intent of the Jini Standard
>    SD4.1.3 The blocking feature of lookup, which states:
>
>        As noted above, each category contains a version of |lookup|
>        that provides a feature in which the entity can request that if
>        the number of service references found throughout the available
>        lookup services does not fall into a desired range, the method
>        will wait a finite period of time until either an acceptable
>        minimum number of service references are discovered or the
>        specified time period has passed.
>
>
>    In this case the specified time period hasn't quite passed, however
>    an acceptable minimum number of service references are returned and
>    the test fails.  This failure is rare.
>
>
> CASE 2:
>
>              * 3. while lookup() is blocking, if enough new services are
>              *    registered so that the acceptable minimum is achieved,
>              *    lookup() will return immediately; that is, even if 
> there
>              *    is more time left on the wait period, lookup() will not
>              *    wait for more services beyond the minimum.
>              *
>              *    For example, if 3 services are initially registered and
>              *    lookup is called with min = 4 and max = 7, then 
> lookup()
>              *    will find the 3 services and then wait for more 
> services to
>              *    be registered. Suppose that while lookup() is blocking
>              *    another 5 services are registered, bringing the 
> total number
>              *    of services to 8. In this case, lookup() will stopping
>              *    waiting and return 4 services (the minimum), not the
>              *    maximum 7.
>
> While the test performs this check, it isn't implemented in 
> ServiceDiscoveryManager, instead it's left up to chance that the 
> lookup method, waiting on an object monitor will obtain the monitor as 
> soon as it's notified.  However if multiple threads contend for the 
> lock, it's very unlikely the thread scheduler will give the object 
> monitor to the waiting thread, instead the most recently blocking 
> thread is likely to get the monitor.

Furthermore, if this does occur, the existing ServiceDiscoveryManager 
can return more ServiceItem's than maxMatches because it assumes it will 
always obtain the object monitor, I've changed the implementation with 
the most recent commit; additional ServiceItem's returned will be 
discarded if more than maxMatches are found.

>
> This requirement in the test appears to be additional to the 
> specification and would require ServiceDiscoveryManager be modified, 
> to use some kind of fair Lock scheme.  However doing this would 
> unnecessarily complicate ServiceDiscoveryManager, instead it would be 
> simpler to return the number of ServiceItem's found up to the 
> maxMatches at the time the lock is obtained.
>
> CASE 1 test results:
>
> Running 
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> Time is Mon May 06 22:17:31 EST 2013
> Starting test in separate process with command:
> 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java' 
> -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager 
> -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy 
> -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar 
> -cp 
> C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3.jar 
> -ea -esa -client '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081 
> -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy 
> -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar 
> -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar 
> -Dcom.sun.jini.qa.harness.runjiniserver=true 
> -Dcom.sun.jini.qa.harness.runkitserver=true 
> -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties 
> -Dcom.sun.jini.qa.harness.testhosts= 
> -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging 
> -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.test.port=9082 
> -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy 
> '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> com.sun.jini.qa.harness.MasterTest 
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> com.sun.jini.qa.harness.TestException:  -- blocked longer than 
> expected -- requested block = 60000 millisecond(s), actual block = 
> 59999 millisecond(s)
>     at 
> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
>     at 
> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
>     at 
> com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
>     at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
>     at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)
>
> TIME: 10:20:02 PM
>
> Test process was destroyed and returned code 1
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> Test Failed: Test Failed: com.sun.jini.qa.harness.TestException:  -- 
> blocked longer than expected -- requested block = 60000 
> millisecond(s), actual block = 59999 millisecond(s)
>
> Running 
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> Running 
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> Time is Tue May 07 10:40:30 EST 2013
> Time is Tue May 07 10:40:30 EST 2013
> Starting test in separate process with command:
> Starting test in separate process with command:
> 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java' 
> -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager 
> -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy 
> -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar 
> -cp 
> C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3.jar 
> -ea -esa -client '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081 
> -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy 
> -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar 
> -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar 
> -Dcom.sun.jini.qa.harness.runjiniserver=true 
> -Dcom.sun.jini.qa.harness.runkitserver=true 
> -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties 
> -Dcom.sun.jini.qa.harness.testhosts= 
> -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging 
> -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.test.port=9082 
> -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy 
> '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> com.sun.jini.qa.harness.MasterTest 
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java' 
> -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager 
> -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy 
> -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar 
> -cp 
> C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3.jar 
> -ea -esa -client '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081 
> -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy 
> -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar 
> -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar 
> -Dcom.sun.jini.qa.harness.runjiniserver=true 
> -Dcom.sun.jini.qa.harness.runkitserver=true 
> -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties 
> -Dcom.sun.jini.qa.harness.testhosts= 
> -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging 
> -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.test.port=9082 
> -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy 
> '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> com.sun.jini.qa.harness.MasterTest 
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> com.sun.jini.qa.harness.TestException:  -- blocked longer than 
> expected -- requested block = 60000 millisecond(s), actual block = 
> 59997 millisecond(s) wait error = 0
> com.sun.jini.qa.harness.TestException:  -- blocked longer than 
> expected -- requested block = 60000 millisecond(s), actual block = 
> 59997 millisecond(s) wait error = 0
>     at 
> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
>     at 
> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
>     at 
> com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
>     at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
>     at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)
>
>
>
>
> Relevant Section of Jini Specification:
>
>
>          SD.4.1.3 The |lookup| Method
>
> The |lookup| method queries each available lookup service in the 
> managed set for service reference(s) that match criteria defined by 
> the entity that invokes this method. Entities typically employ this 
> method when they need infrequent access to services and when the cost 
> of making remote queries is outweighed by the overhead of maintaining 
> a local cache (for example, because of resource limitations).
>
> The |lookup| method has four versions, each version falling into one 
> of two categories: those versions of this method that return a single 
> instance of |ServiceItem| and those versions that return a set of 
> service references as an array of |ServiceItem| objects.
>
> Two arguments are common to all versions of this method: an instance 
> of |ServiceTemplate| and an instance of |ServiceItemFilter|.
>
> Within each category, the versions of |lookup| differ only in whether 
> or not a particular version provides what is referred to as a "wait" 
> (or blocking) feature. That is, each category contains both a 
> non-blocking version of |lookup| which returns immediately when unable 
> to find the desired service, and a blocking version which returns only 
> after waiting a specified amount of time for the desired service to be 
> discovered. The particular version of |lookup| that an entity employs 
> is typically determined by the entity's intended usage pattern.
>
> The descriptions that follow refer to all versions of the |lookup| 
> method, except where explicitly noted.
>
> The |tmpl| argument and the |filter| argument both have semantics 
> identical to that defined for these arguments in the description of 
> the |createLookupCache| method above. In particular,
>
>    * A |null| reference value for the |tmpl| parameter is treated as
>      the equivalent of a "wildcarded" |ServiceTemplate|.
>
>    * If |null| is the value for the |filter| parameter, only template
>      matching will be employed to find the desired services.
>
>    * The effects of modifying the contents of the |tmpl| parameter
>      while the invocation is in progress are unpredictable and undefined.
>
> If no service can be found that matches the desired criteria, then the 
> versions of |lookup| from the first category--those that return a 
> single instance of |ServiceItem|--will return |null|, whereas the 
> versions from the second category--those that return an array of 
> |ServiceItem| instances--will return an empty array.
>
> The versions of |lookup| from the first category can be used in a 
> fashion similar to the first form of the |lookup| method defined in 
> the |ServiceRegistrar| interface described in the /Jini Lookup Service 
> Specification/. That is, an entity would typically invoke one of these 
> versions of |lookup| when it wishes to find a /single/ service 
> reference and the particular lookup service with which that service 
> reference is registered is unimportant to the entity.
>
> Each version of |lookup| defined in the |ServiceDiscoveryManager| 
> differs with the corresponding version of |lookup| in 
> |ServiceRegistrar| in the following ways:
>
>    * The versions of |lookup| defined in the |ServiceDiscoveryManager|
>      query /multiple/ lookup services (the order in which the lookup
>      services are queried is dependent on the implementation).
>
>    * The versions of |lookup| defined in the |ServiceDiscoveryManager|
>      can apply additional selection criteria, in the form of a filter
>      object, when deciding whether a service reference found through
>      standard template matching should be returned to the entity.
>
> The versions of |lookup| that return an array of |ServiceItem| objects 
> can be used in a fashion similar to the second form of |lookup| 
> defined in the |ServiceRegistrar| interface. That is, an entity would 
> typically invoke these versions of |lookup| when it wishes to find 
> /multiple/ service references that satisfy the input criteria. Each of 
> the versions of |lookup| that return an array of |ServiceItem| objects 
> takes as one of its arguments an |int| parameter, |maxMatches|, that 
> represents the maximum number of matches that should be returned. The 
> array returned by these methods will contain no more than |maxMatches| 
> service references, although it may contain fewer than that number.
>
> As with the versions of |lookup| that return a single instance of 
> |ServiceItem|, multiple queries and filtering are also notable 
> differences between the second-category versions of this method and 
> their counterpart in |ServiceRegistrar|.
>
> For each version of |lookup|, whenever a lookup service query returns 
> a |null| service reference, the filter is bypassed, and the service 
> reference is excluded from the return object. On the other hand, if 
> the query returns a non-|null| service reference in which the 
> associated array of attribute contains one or more |null| elements, 
> the filter is still applied and the service reference is included in 
> the return object.
>
> Each version of |lookup| may be confronted with duplicate references 
> during a search for a service of interest. This is because the same 
> service may register with more than one lookup service in the managed 
> set. As with the cache, when a set of service references is returned 
> by |lookup|, each service reference in the return set will be unique 
> with respect to all other service references in the set, as determined 
> by the |equals| method provided by each reference.
>
> If it is determined that a lookup service is unavailable (due to an 
> exception or some other non-fatal error) while interacting with a 
> lookup service from the managed set, all versions of |lookup| will 
> invoke the |discard| method on the instance of |DiscoveryManagement| 
> being employed by the |ServiceDiscoveryManager|. Doing so will result 
> in the unavailable lookup service being discarded and made eligible 
> for rediscovery.
>
> Recall that the propagation of modifications to a service's attributes 
> across a set of lookup services typically occurs asynchronously. It is 
> for this reason that while invoking |lookup| to find a set of matching 
> services, it is possible that the set returned may contain multiple 
> references having the same service ID with different attributes. Note 
> that although this sort of inconsistent state can also occur if the 
> entity employs a cache, the cache will eventually reflect the correct 
> state.
>
> The Blocking Feature of |lookup|
>
> As noted above, each category contains a version of |lookup| that 
> provides a feature in which the entity can request that if the number 
> of service references found throughout the available lookup services 
> does not fall into a desired range, the method will wait a finite 
> period of time until either an acceptable minimum number of service 
> references are discovered or the specified time period has passed.
>
> The versions of |lookup| providing this blocking feature each takes as 
> one of its parameters a value of type long that represents the number 
> of milliseconds to wait for the service to be discovered. In addition 
> to |RemoteException| (described previously for these methods), each of 
> these versions of |lookup| may throw an |InterruptedException|.
>
> One of these blocking versions of |lookup| implicitly uses a value of 
> one for both the acceptable minimum and the allowable maximum number 
> of service references to discover. The other blocking version requires 
> that the entity specify the range through the |minMatches| and 
> |maxMatches| parameters, respectively.
>
> Prior to blocking, each of these versions of |lookup| first queries 
> each available lookup service in an attempt to retrieve a satisfactory 
> number of matching services. Whether or not the method actually blocks 
> is dependent on how many matching service references are found during 
> the query process. Blocking occurs only if after querying /all/ of the 
> available lookup services, the number of matching services found is 
> less than the acceptable minimum. If the waiting period (measured from 
> when blocking first begins) passes before that minimum number of 
> service references is found, the method will return the service 
> references that have been discovered up to that point. If the waiting 
> period passes and no services have been found, |null| or an empty 
> array (depending on the version of |lookup|) will be returned.
>
> If, after querying all of the available lookup services, the number of 
> services found to satisfy the desired criteria is greater than or 
> equal to the specified minimum but less than the specified maximum, 
> the method will return the currently discovered service references 
> without blocking. If the initial query process produces the desired 
> maximum number of service references, the method will return the 
> results immediately.
>
> The blocking versions of |lookup| are quite useful to entities that 
> cannot proceed until such a service of interest is found. If a 
> non-positive value is input to the |waitDur| argument, then the method 
> will not wait. It will simply query the available lookup services and 
> employ the return semantics described above.
>
> The values of the |minMatches| and |maxMatches| arguments must both be 
> positive, and |maxMatches| must be greater than or equal to 
> |minMatches|; otherwise, an |IllegalArgumentException| will be thrown.
>
> The blocking versions of |lookup| make a concurrency guarantee with 
> respect to the discovery of new lookup services during the wait 
> period. That is, while waiting for the desired service reference(s) to 
> be discovered, if one or more of the targeted--but previously 
> unavailable--lookup services is discovered and added to the managed 
> set, those new lookup services will also be queried for the service(s) 
> of interest.
>
> In addition, the blocking versions of |lookup| throw 
> |InterruptedException|. When an entity invokes either version with 
> valid parameters, the entity may decide during the wait period that it 
> no longer wishes to wait the entire period for the method to return. 
> Thus, while the method is blocking on the discovery of matching 
> service(s), it may be interrupted by invoking the |interrupt| method 
> from the |Thread| class. The intent of this mechanism is to allow the 
> entity to interrupt a blocking |lookup| in the same way it would a 
> sleeping thread.
>
>


Re: ServiceDiscoveryManager lookp specification - Patch

Posted by Peter Firmstone <ji...@zeus.net.au>.
I've attached a patch for review, I'll create a Jira issue shortly.

Regards,

Peter.


On 8/05/2013 7:40 AM, Peter wrote:
> I agree, I think we need to review this TCK test against the specification.
>
> I suspect more than one person created this test, perhaps there was confusion; at some point the resolution was changed to seconds (before Apache River's time).
>
> This meant if insufficient ServiceItem's are found, lookup could return up to 3 seconds early or 30 seconds later than the waitDur (specified in ms).  This seems unreasonable, if the method waits that 3 seconds, it might find more ServiceItems.
>
> On the other hand if sufficient ServiceItems are found, it checks they return as soon as possible by confirming the number of services returned.  To pass on this occassion, the test must return more than one second prior to waitDur's expiry.  This also seems unreasonable, one because another thread may obtain the monitor first (if so it finds more than minMatches) and two because it should pass if it returns in the waitDur time window.
>
> I think the ms time ranges you've mentioned are more appropriate, they are achievable in practise (I checked).
>
> I'll submit a patch for review.
>
> Peter.
>
>
>
>
>
> ----- Original message -----
>> Hi Peter:
>>
>> I'd certainly apply some tolerance band to the test result.  Failing for
>> 3 ms difference is unreasonable.  I'd expect probably 50-100ms
>> variance.  The ms clock isn't assumed to be that accurate.  Matter of
>> fact, waiting for a task switch could cause at least that much variance.
>>
>> Cheers,
>>
>> Greg.
>>
>> On Tue, 2013-05-07 at 06:17, Peter Firmstone wrote:
>>> This test smells broken?
>>>
>>> I modified the output to read in milliseconds.
>>>
>>> Relevant ServiceDiscoveryManager method under test:
>>>
>>> public ServiceItem[] lookup(ServiceTemplate tmpl,
>>>                                                                    int minMatches,
>>>                                                                    int maxMatches,
>>>                                                                    ServiceItemFilter filter,
>>>                                                                    long waitDur)
>>>                                                                      throws InterruptedException,
>>>                                                                                    RemoteException {...}
>>>
>>>
>>>
>>> To summarise what's happens (occassionally):
>>>
>>> CASE 1:
>>>
>>>          * ServiceDiscoveryManager.lookup  is called and blocks waiting until
>>>              it receives the minimum number of ServiceItems.
>>>          * If the required number of ServiceItem's are discovered late, the
>>>              waitDur limit will be approached when lookup returns.
>>>                      o The resolution of the test is to the nearest second, not
>>>                          millisecond.
>>>
>>>
>>>          Firstly I don't believe this is the intent of the Jini Standard
>>>          SD4.1.3 The blocking feature of lookup, which states:
>>>
>>>                  As noted above, each category contains a version of |lookup|
>>>                  that provides a feature in which the entity can request that if
>>>                  the number of service references found throughout the available
>>>                  lookup services does not fall into a desired range, the method
>>>                  will wait a finite period of time until either an acceptable
>>>                  minimum number of service references are discovered or the
>>>                  specified time period has passed.
>>>
>>>
>>>          In this case the specified time period hasn't quite passed, however
>>>          an acceptable minimum number of service references are returned and
>>>          the test fails.  This failure is rare.
>>>
>>>
>>> CASE 2:
>>>
>>>                              * 3. while lookup() is blocking, if enough new services are
>>>                              *      registered so that the acceptable minimum is achieved,
>>>                              *      lookup() will return immediately; that is, even if there
>>>                              *      is more time left on the wait period, lookup() will not
>>>                              *      wait for more services beyond the minimum.
>>>                              *
>>>                              *      For example, if 3 services are initially registered and
>>>                              *      lookup is called with min = 4 and max = 7, then lookup()
>>>                              *      will find the 3 services and then wait for more
>>> services to
>>>                              *      be registered. Suppose that while lookup() is blocking
>>>                              *      another 5 services are registered, bringing the total
>>> number
>>>                              *      of services to 8. In this case, lookup() will stopping
>>>                              *      waiting and return 4 services (the minimum), not the
>>>                              *      maximum 7.
>>>
>>> While the test performs this check, it isn't implemented in
>>> ServiceDiscoveryManager, instead it's left up to chance that the lookup
>>> method, waiting on an object monitor will obtain the monitor as soon as
>>> it's notified.  However if multiple threads contend for the lock, it's
>>> very unlikely the thread scheduler will give the object monitor to the
>>> waiting thread, instead the most recently blocking thread is likely to
>>> get the monitor.
>>>
>>> This requirment in the test appears to be additional to the
>>> specification and would require ServiceDiscoveryManager be modified, to
>>> use some kind of fair Lock scheme.  However doing this would
>>> unnecessarily complicate ServiceDiscoveryManager, instead it would be
>>> simpler to return the number of ServiceItem's found up to the maxMatches
>>> at the time the lock is obtained.
>>>
>>> CASE 1 test results:
>>>
>>> Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
>>> Time is Mon May 06 22:17:31 EST 2013
>>> Starting test in separate process with command:
>>> 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java'
>>> -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager
>>> -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy
>>> -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp
>>> C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.ja r;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3 .jar
>>> -ea -esa -client '-Djava.ext.dirs=C:\Program
>>> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
>>> -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081
>>> -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy
>>> -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
>>> -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar
>>> -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar
>>> -Dcom.sun.jini.qa.harness.runjiniserver=true
>>> -Dcom.sun.jini.qa.harness.runkitserver=true
>>> -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties
>>> -Dcom.sun.jini.qa.harness.testhosts=
>>> -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging
>>> -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
>>> -Dcom.sun.jini.test.port=9082
>>> -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy
>>> '-Djava.ext.dirs=C:\Program
>>> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
>>> com.sun.jini.qa.harness.MasterTest
>>> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
>>> com.sun.jini.qa.harness.TestException:  -- blocked longer than expected
>>> -- requested block = 60000 millisecond(s), actual block = 59999
>>> millisecond(s)
>>>            at
>>> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
>>>            at
>>> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
>>>            at
>>> com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
>>>            at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
>>>            at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)
>>>
>>> TIME: 10:20:02 PM
>>>
>>> Test process was destroyed and returned code 1
>>> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
>>> Test Failed: Test Failed: com.sun.jini.qa.harness.TestException:  --
>>> blocked longer than expected -- requested block = 60000 millisecond(s),
>>> actual block = 59999 millisecond(s)
>>>
>>> Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
>>> Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
>>> Time is Tue May 07 10:40:30 EST 2013
>>> Time is Tue May 07 10:40:30 EST 2013
>>> Starting test in separate process with command:
>>> Starting test in separate process with command:
>>> 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java'
>>> -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager
>>> -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy
>>> -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp
>>> C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.ja r;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3 .jar
>>> -ea -esa -client '-Djava.ext.dirs=C:\Program
>>> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
>>> -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081
>>> -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy
>>> -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
>>> -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar
>>> -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar
>>> -Dcom.sun.jini.qa.harness.runjiniserver=true
>>> -Dcom.sun.jini.qa.harness.runkitserver=true
>>> -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties
>>> -Dcom.sun.jini.qa.harness.testhosts=
>>> -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging
>>> -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
>>> -Dcom.sun.jini.test.port=9082
>>> -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy
>>> '-Djava.ext.dirs=C:\Program
>>> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
>>> com.sun.jini.qa.harness.MasterTest
>>> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
>>> 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java'
>>> -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager
>>> -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy
>>> -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp
>>> C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.ja r;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3 .jar
>>> -ea -esa -client '-Djava.ext.dirs=C:\Program
>>> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
>>> -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081
>>> -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy
>>> -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
>>> -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar
>>> -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar
>>> -Dcom.sun.jini.qa.harness.runjiniserver=true
>>> -Dcom.sun.jini.qa.harness.runkitserver=true
>>> -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties
>>> -Dcom.sun.jini.qa.harness.testhosts=
>>> -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging
>>> -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
>>> -Dcom.sun.jini.test.port=9082
>>> -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy
>>> '-Djava.ext.dirs=C:\Program
>>> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
>>> com.sun.jini.qa.harness.MasterTest
>>> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
>>> com.sun.jini.qa.harness.TestException:  -- blocked longer than expected
>>> -- requested block = 60000 millisecond(s), actual block = 59997
>>> millisecond(s) wait error = 0
>>> com.sun.jini.qa.harness.TestException:  -- blocked longer than expected
>>> -- requested block = 60000 millisecond(s), actual block = 59997
>>> millisecond(s) wait error = 0
>>>            at
>>> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
>>>            at
>>> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
>>>            at
>>> com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
>>>            at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
>>>            at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)
>>>
>>>
>>>
>>>
>>> Relevant Section of Jini Specification:
>>>
>>>
>>>                      SD.4.1.3 The |lookup| Method
>>>
>>> The |lookup| method queries each available lookup service in the managed
>>> set for service reference(s) that match criteria defined by the entity
>>> that invokes this method. Entities typically employ this method when
>>> they need infrequent access to services and when the cost of making
>>> remote queries is outweighed by the overhead of maintaining a local
>>> cache (for example, because of resource limitations).
>>>
>>> The |lookup| method has four versions, each version falling into one of
>>> two categories: those versions of this method that return a single
>>> instance of |ServiceItem| and those versions that return a set of
>>> service references as an array of |ServiceItem| objects.
>>>
>>> Two arguments are common to all versions of this method: an instance of
>>> |ServiceTemplate| and an instance of |ServiceItemFilter|.
>>>
>>> Within each category, the versions of |lookup| differ only in whether or
>>> not a particular version provides what is referred to as a "wait" (or
>>> blocking) feature. That is, each category contains both a non-blocking
>>> version of |lookup| which returns immediately when unable to find the
>>> desired service, and a blocking version which returns only after waiting
>>> a specified amount of time for the desired service to be discovered. The
>>> particular version of |lookup| that an entity employs is typically
>>> determined by the entity's intended usage pattern.
>>>
>>> The descriptions that follow refer to all versions of the |lookup|
>>> method, except where explicitly noted.
>>>
>>> The |tmpl| argument and the |filter| argument both have semantics
>>> identical to that defined for these arguments in the description of the
>>> |createLookupCache| method above. In particular,
>>>
>>>          * A |null| reference value for the |tmpl| parameter is treated as
>>>              the equivalent of a "wildcarded" |ServiceTemplate|.
>>>
>>>          * If |null| is the value for the |filter| parameter, only template
>>>              matching will be employed to find the desired services.
>>>
>>>          * The effects of modifying the contents of the |tmpl| parameter
>>>              while the invocation is in progress are unpredictable and undefined.
>>>
>>> If no service can be found that matches the desired criteria, then the
>>> versions of |lookup| from the first category--those that return a single
>>> instance of |ServiceItem|--will return |null|, whereas the versions from
>>> the second category--those that return an array of |ServiceItem|
>>> instances--will return an empty array.
>>>
>>> The versions of |lookup| from the first category can be used in a
>>> fashion similar to the first form of the |lookup| method defined in the
>>> |ServiceRegistrar| interface described in the /Jini Lookup Service
>>> Specification/. That is, an entity would typically invoke one of these
>>> versions of |lookup| when it wishes to find a /single/ service reference
>>> and the particular lookup service with which that service reference is
>>> registered is unimportant to the entity.
>>>
>>> Each version of |lookup| defined in the |ServiceDiscoveryManager|
>>> differs with the corresponding version of |lookup| in |ServiceRegistrar|
>>> in the following ways:
>>>
>>>          * The versions of |lookup| defined in the |ServiceDiscoveryManager|
>>>              query /multiple/ lookup services (the order in which the lookup
>>>              services are queried is dependent on the implementation).
>>>
>>>          * The versions of |lookup| defined in the |ServiceDiscoveryManager|
>>>              can apply additional selection criteria, in the form of a filter
>>>              object, when deciding whether a service reference found through
>>>              standard template matching should be returned to the entity.
>>>
>>> The versions of |lookup| that return an array of |ServiceItem| objects
>>> can be used in a fashion similar to the second form of |lookup| defined
>>> in the |ServiceRegistrar| interface. That is, an entity would typically
>>> invoke these versions of |lookup| when it wishes to find /multiple/
>>> service references that satisfy the input criteria. Each of the versions
>>> of |lookup| that return an array of |ServiceItem| objects takes as one
>>> of its arguments an |int| parameter, |maxMatches|, that represents the
>>> maximum number of matches that should be returned. The array returned by
>>> these methods will contain no more than |maxMatches| service references,
>>> although it may contain fewer than that number.
>>>
>>> As with the versions of |lookup| that return a single instance of
>>> |ServiceItem|, multiple queries and filtering are also notable
>>> differences between the second-category versions of this method and
>>> their counterpart in |ServiceRegistrar|.
>>>
>>> For each version of |lookup|, whenever a lookup service query returns a
>>> |null| service reference, the filter is bypassed, and the service
>>> reference is excluded from the return object. On the other hand, if the
>>> query returns a non-|null| service reference in which the associated
>>> array of attribute contains one or more |null| elements, the filter is
>>> still applied and the service reference is included in the return object.
>>>
>>> Each version of |lookup| may be confronted with duplicate references
>>> during a search for a service of interest. This is because the same
>>> service may register with more than one lookup service in the managed
>>> set. As with the cache, when a set of service references is returned by
>>> |lookup|, each service reference in the return set will be unique with
>>> respect to all other service references in the set, as determined by the
>>> |equals| method provided by each reference.
>>>
>>> If it is determined that a lookup service is unavailable (due to an
>>> exception or some other non-fatal error) while interacting with a lookup
>>> service from the managed set, all versions of |lookup| will invoke the
>>> |discard| method on the instance of |DiscoveryManagement| being employed
>>> by the |ServiceDiscoveryManager|. Doing so will result in the
>>> unavailable lookup service being discarded and made eligible for
>>> rediscovery.
>>>
>>> Recall that the propagation of modifications to a service's attributes
>>> across a set of lookup services typically occurs asynchronously. It is
>>> for this reason that while invoking |lookup| to find a set of matching
>>> services, it is possible that the set returned may contain multiple
>>> references having the same service ID with different attributes. Note
>>> that although this sort of inconsistent state can also occur if the
>>> entity employs a cache, the cache will eventually reflect the correct
>>> state.
>>>
>>> The Blocking Feature of |lookup|
>>>
>>> As noted above, each category contains a version of |lookup| that
>>> provides a feature in which the entity can request that if the number of
>>> service references found throughout the available lookup services does
>>> not fall into a desired range, the method will wait a finite period of
>>> time until either an acceptable minimum number of service references are
>>> discovered or the specified time period has passed.
>>>
>>> The versions of |lookup| providing this blocking feature each takes as
>>> one of its parameters a value of type long that represents the number of
>>> milliseconds to wait for the service to be discovered. In addition to
>>> |RemoteException| (described previously for these methods), each of
>>> these versions of |lookup| may throw an |InterruptedException|.
>>>
>>> One of these blocking versions of |lookup| implicitly uses a value of
>>> one for both the acceptable minimum and the allowable maximum number of
>>> service references to discover. The other blocking version requires that
>>> the entity specify the range through the |minMatches| and |maxMatches|
>>> parameters, respectively.
>>>
>>> Prior to blocking, each of these versions of |lookup| first queries each
>>> available lookup service in an attempt to retrieve a satisfactory number
>>> of matching services. Whether or not the method actually blocks is
>>> dependent on how many matching service references are found during the
>>> query process. Blocking occurs only if after querying /all/ of the
>>> available lookup services, the number of matching services found is less
>>> than the acceptable minimum. If the waiting period (measured from when
>>> blocking first begins) passes before that minimum number of service
>>> references is found, the method will return the service references that
>>> have been discovered up to that point. If the waiting period passes and
>>> no services have been found, |null| or an empty array (depending on the
>>> version of |lookup|) will be returned.
>>>
>>> If, after querying all of the available lookup services, the number of
>>> services found to satisfy the desired criteria is greater than or equal
>>> to the specified minimum but less than the specified maximum, the method
>>> will return the currently discovered service references without
>>> blocking. If the initial query process produces the desired maximum
>>> number of service references, the method will return the results
>>> immediately.
>>>
>>> The blocking versions of |lookup| are quite useful to entities that
>>> cannot proceed until such a service of interest is found. If a
>>> non-positive value is input to the |waitDur| argument, then the method
>>> will not wait. It will simply query the available lookup services and
>>> employ the return semantics described above.
>>>
>>> The values of the |minMatches| and |maxMatches| arguments must both be
>>> positive, and |maxMatches| must be greater than or equal to
>>> |minMatches|; otherwise, an |IllegalArgumentException| will be thrown.
>>>
>>> The blocking versions of |lookup| make a concurrency guarantee with
>>> respect to the discovery of new lookup services during the wait period.
>>> That is, while waiting for the desired service reference(s) to be
>>> discovered, if one or more of the targeted--but previously
>>> unavailable--lookup services is discovered and added to the managed set,
>>> those new lookup services will also be queried for the service(s) of
>>> interest.
>>>
>>> In addition, the blocking versions of |lookup| throw
>>> |InterruptedException|. When an entity invokes either version with valid
>>> parameters, the entity may decide during the wait period that it no
>>> longer wishes to wait the entire period for the method to return. Thus,
>>> while the method is blocking on the discovery of matching service(s), it
>>> may be interrupted by invoking the |interrupt| method from the |Thread|
>>> class. The intent of this mechanism is to allow the entity to interrupt
>>> a blocking |lookup| in the same way it would a sleeping thread.
>>>
>


Re: ServiceDiscoveryManager lookp specification

Posted by Peter <ji...@zeus.net.au>.
I agree, I think we need to review this TCK test against the specification.

I suspect more than one person created this test, perhaps there was confusion; at some point the resolution was changed to seconds (before Apache River's time).

This meant if insufficient ServiceItem's are found, lookup could return up to 3 seconds early or 30 seconds later than the waitDur (specified in ms).  This seems unreasonable, if the method waits that 3 seconds, it might find more ServiceItems.

On the other hand if sufficient ServiceItems are found, it checks they return as soon as possible by confirming the number of services returned.  To pass on this occassion, the test must return more than one second prior to waitDur's expiry.  This also seems unreasonable, one because another thread may obtain the monitor first (if so it finds more than minMatches) and two because it should pass if it returns in the waitDur time window.

I think the ms time ranges you've mentioned are more appropriate, they are achievable in practise (I checked).

I'll submit a patch for review.

Peter.


 


----- Original message -----
>
> Hi Peter:
>
> I'd certainly apply some tolerance band to the test result.  Failing for
> 3 ms difference is unreasonable.  I'd expect probably 50-100ms
> variance.  The ms clock isn't assumed to be that accurate.  Matter of
> fact, waiting for a task switch could cause at least that much variance.
>
> Cheers,
>
> Greg.
>
> On Tue, 2013-05-07 at 06:17, Peter Firmstone wrote:
> > This test smells broken?
> >
> > I modified the output to read in milliseconds.
> >
> > Relevant ServiceDiscoveryManager method under test:
> >
> > public ServiceItem[] lookup(ServiceTemplate tmpl,
> >                                                                  int minMatches,
> >                                                                  int maxMatches,
> >                                                                  ServiceItemFilter filter,
> >                                                                  long waitDur)
> >                                                                    throws InterruptedException,
> >                                                                                  RemoteException {...}
> >
> >
> >
> > To summarise what's happens (occassionally):
> >
> > CASE 1:
> >
> >        * ServiceDiscoveryManager.lookup  is called and blocks waiting until
> >            it receives the minimum number of ServiceItems.
> >        * If the required number of ServiceItem's are discovered late, the
> >            waitDur limit will be approached when lookup returns.
> >                    o The resolution of the test is to the nearest second, not
> >                        millisecond.
> >
> >
> >        Firstly I don't believe this is the intent of the Jini Standard
> >        SD4.1.3 The blocking feature of lookup, which states:
> >
> >                As noted above, each category contains a version of |lookup|
> >                that provides a feature in which the entity can request that if
> >                the number of service references found throughout the available
> >                lookup services does not fall into a desired range, the method
> >                will wait a finite period of time until either an acceptable
> >                minimum number of service references are discovered or the
> >                specified time period has passed.
> >
> >
> >        In this case the specified time period hasn't quite passed, however
> >        an acceptable minimum number of service references are returned and
> >        the test fails.  This failure is rare.
> >
> >
> > CASE 2:
> >
> >                            * 3. while lookup() is blocking, if enough new services are
> >                            *      registered so that the acceptable minimum is achieved,
> >                            *      lookup() will return immediately; that is, even if there
> >                            *      is more time left on the wait period, lookup() will not
> >                            *      wait for more services beyond the minimum.
> >                            *
> >                            *      For example, if 3 services are initially registered and
> >                            *      lookup is called with min = 4 and max = 7, then lookup()
> >                            *      will find the 3 services and then wait for more
> > services to
> >                            *      be registered. Suppose that while lookup() is blocking
> >                            *      another 5 services are registered, bringing the total
> > number
> >                            *      of services to 8. In this case, lookup() will stopping
> >                            *      waiting and return 4 services (the minimum), not the
> >                            *      maximum 7.
> >
> > While the test performs this check, it isn't implemented in
> > ServiceDiscoveryManager, instead it's left up to chance that the lookup
> > method, waiting on an object monitor will obtain the monitor as soon as
> > it's notified.  However if multiple threads contend for the lock, it's
> > very unlikely the thread scheduler will give the object monitor to the
> > waiting thread, instead the most recently blocking thread is likely to
> > get the monitor.
> >
> > This requirment in the test appears to be additional to the
> > specification and would require ServiceDiscoveryManager be modified, to
> > use some kind of fair Lock scheme.  However doing this would
> > unnecessarily complicate ServiceDiscoveryManager, instead it would be
> > simpler to return the number of ServiceItem's found up to the maxMatches
> > at the time the lock is obtained.
> >
> > CASE 1 test results:
> >
> > Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> > Time is Mon May 06 22:17:31 EST 2013
> > Starting test in separate process with command:
> > 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java'
> > -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager
> > -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy
> > -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp
> > C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.ja r;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3 .jar
> > -ea -esa -client '-Djava.ext.dirs=C:\Program
> > Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
> > -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081
> > -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy
> > -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
> > -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar
> > -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar
> > -Dcom.sun.jini.qa.harness.runjiniserver=true
> > -Dcom.sun.jini.qa.harness.runkitserver=true
> > -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties
> > -Dcom.sun.jini.qa.harness.testhosts=
> > -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging
> > -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
> > -Dcom.sun.jini.test.port=9082
> > -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy
> > '-Djava.ext.dirs=C:\Program
> > Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
> > com.sun.jini.qa.harness.MasterTest
> > com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> > com.sun.jini.qa.harness.TestException:  -- blocked longer than expected
> > -- requested block = 60000 millisecond(s), actual block = 59999
> > millisecond(s)
> >          at
> > com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
> >          at
> > com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
> >          at
> > com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
> >          at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
> >          at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)
> >
> > TIME: 10:20:02 PM
> >
> > Test process was destroyed and returned code 1
> > com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> > Test Failed: Test Failed: com.sun.jini.qa.harness.TestException:  --
> > blocked longer than expected -- requested block = 60000 millisecond(s),
> > actual block = 59999 millisecond(s)
> >
> > Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> > Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> > Time is Tue May 07 10:40:30 EST 2013
> > Time is Tue May 07 10:40:30 EST 2013
> > Starting test in separate process with command:
> > Starting test in separate process with command:
> > 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java'
> > -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager
> > -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy
> > -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp
> > C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.ja r;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3 .jar
> > -ea -esa -client '-Djava.ext.dirs=C:\Program
> > Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
> > -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081
> > -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy
> > -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
> > -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar
> > -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar
> > -Dcom.sun.jini.qa.harness.runjiniserver=true
> > -Dcom.sun.jini.qa.harness.runkitserver=true
> > -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties
> > -Dcom.sun.jini.qa.harness.testhosts=
> > -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging
> > -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
> > -Dcom.sun.jini.test.port=9082
> > -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy
> > '-Djava.ext.dirs=C:\Program
> > Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
> > com.sun.jini.qa.harness.MasterTest
> > com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> > 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java'
> > -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager
> > -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy
> > -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp
> > C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.ja r;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3 .jar
> > -ea -esa -client '-Djava.ext.dirs=C:\Program
> > Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
> > -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081
> > -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy
> > -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
> > -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar
> > -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar
> > -Dcom.sun.jini.qa.harness.runjiniserver=true
> > -Dcom.sun.jini.qa.harness.runkitserver=true
> > -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties
> > -Dcom.sun.jini.qa.harness.testhosts=
> > -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging
> > -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
> > -Dcom.sun.jini.test.port=9082
> > -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy
> > '-Djava.ext.dirs=C:\Program
> > Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
> > com.sun.jini.qa.harness.MasterTest
> > com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> > com.sun.jini.qa.harness.TestException:  -- blocked longer than expected
> > -- requested block = 60000 millisecond(s), actual block = 59997
> > millisecond(s) wait error = 0
> > com.sun.jini.qa.harness.TestException:  -- blocked longer than expected
> > -- requested block = 60000 millisecond(s), actual block = 59997
> > millisecond(s) wait error = 0
> >          at
> > com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
> >          at
> > com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
> >          at
> > com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
> >          at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
> >          at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)
> >
> >
> >
> >
> > Relevant Section of Jini Specification:
> >
> >
> >                    SD.4.1.3 The |lookup| Method
> >
> > The |lookup| method queries each available lookup service in the managed
> > set for service reference(s) that match criteria defined by the entity
> > that invokes this method. Entities typically employ this method when
> > they need infrequent access to services and when the cost of making
> > remote queries is outweighed by the overhead of maintaining a local
> > cache (for example, because of resource limitations).
> >
> > The |lookup| method has four versions, each version falling into one of
> > two categories: those versions of this method that return a single
> > instance of |ServiceItem| and those versions that return a set of
> > service references as an array of |ServiceItem| objects.
> >
> > Two arguments are common to all versions of this method: an instance of
> > |ServiceTemplate| and an instance of |ServiceItemFilter|.
> >
> > Within each category, the versions of |lookup| differ only in whether or
> > not a particular version provides what is referred to as a "wait" (or
> > blocking) feature. That is, each category contains both a non-blocking
> > version of |lookup| which returns immediately when unable to find the
> > desired service, and a blocking version which returns only after waiting
> > a specified amount of time for the desired service to be discovered. The
> > particular version of |lookup| that an entity employs is typically
> > determined by the entity's intended usage pattern.
> >
> > The descriptions that follow refer to all versions of the |lookup|
> > method, except where explicitly noted.
> >
> > The |tmpl| argument and the |filter| argument both have semantics
> > identical to that defined for these arguments in the description of the
> > |createLookupCache| method above. In particular,
> >
> >        * A |null| reference value for the |tmpl| parameter is treated as
> >            the equivalent of a "wildcarded" |ServiceTemplate|.
> >
> >        * If |null| is the value for the |filter| parameter, only template
> >            matching will be employed to find the desired services.
> >
> >        * The effects of modifying the contents of the |tmpl| parameter
> >            while the invocation is in progress are unpredictable and undefined.
> >
> > If no service can be found that matches the desired criteria, then the
> > versions of |lookup| from the first category--those that return a single
> > instance of |ServiceItem|--will return |null|, whereas the versions from
> > the second category--those that return an array of |ServiceItem|
> > instances--will return an empty array.
> >
> > The versions of |lookup| from the first category can be used in a
> > fashion similar to the first form of the |lookup| method defined in the
> > |ServiceRegistrar| interface described in the /Jini Lookup Service
> > Specification/. That is, an entity would typically invoke one of these
> > versions of |lookup| when it wishes to find a /single/ service reference
> > and the particular lookup service with which that service reference is
> > registered is unimportant to the entity.
> >
> > Each version of |lookup| defined in the |ServiceDiscoveryManager|
> > differs with the corresponding version of |lookup| in |ServiceRegistrar|
> > in the following ways:
> >
> >        * The versions of |lookup| defined in the |ServiceDiscoveryManager|
> >            query /multiple/ lookup services (the order in which the lookup
> >            services are queried is dependent on the implementation).
> >
> >        * The versions of |lookup| defined in the |ServiceDiscoveryManager|
> >            can apply additional selection criteria, in the form of a filter
> >            object, when deciding whether a service reference found through
> >            standard template matching should be returned to the entity.
> >
> > The versions of |lookup| that return an array of |ServiceItem| objects
> > can be used in a fashion similar to the second form of |lookup| defined
> > in the |ServiceRegistrar| interface. That is, an entity would typically
> > invoke these versions of |lookup| when it wishes to find /multiple/
> > service references that satisfy the input criteria. Each of the versions
> > of |lookup| that return an array of |ServiceItem| objects takes as one
> > of its arguments an |int| parameter, |maxMatches|, that represents the
> > maximum number of matches that should be returned. The array returned by
> > these methods will contain no more than |maxMatches| service references,
> > although it may contain fewer than that number.
> >
> > As with the versions of |lookup| that return a single instance of
> > |ServiceItem|, multiple queries and filtering are also notable
> > differences between the second-category versions of this method and
> > their counterpart in |ServiceRegistrar|.
> >
> > For each version of |lookup|, whenever a lookup service query returns a
> > |null| service reference, the filter is bypassed, and the service
> > reference is excluded from the return object. On the other hand, if the
> > query returns a non-|null| service reference in which the associated
> > array of attribute contains one or more |null| elements, the filter is
> > still applied and the service reference is included in the return object.
> >
> > Each version of |lookup| may be confronted with duplicate references
> > during a search for a service of interest. This is because the same
> > service may register with more than one lookup service in the managed
> > set. As with the cache, when a set of service references is returned by
> > |lookup|, each service reference in the return set will be unique with
> > respect to all other service references in the set, as determined by the
> > |equals| method provided by each reference.
> >
> > If it is determined that a lookup service is unavailable (due to an
> > exception or some other non-fatal error) while interacting with a lookup
> > service from the managed set, all versions of |lookup| will invoke the
> > |discard| method on the instance of |DiscoveryManagement| being employed
> > by the |ServiceDiscoveryManager|. Doing so will result in the
> > unavailable lookup service being discarded and made eligible for
> > rediscovery.
> >
> > Recall that the propagation of modifications to a service's attributes
> > across a set of lookup services typically occurs asynchronously. It is
> > for this reason that while invoking |lookup| to find a set of matching
> > services, it is possible that the set returned may contain multiple
> > references having the same service ID with different attributes. Note
> > that although this sort of inconsistent state can also occur if the
> > entity employs a cache, the cache will eventually reflect the correct
> > state.
> >
> > The Blocking Feature of |lookup|
> >
> > As noted above, each category contains a version of |lookup| that
> > provides a feature in which the entity can request that if the number of
> > service references found throughout the available lookup services does
> > not fall into a desired range, the method will wait a finite period of
> > time until either an acceptable minimum number of service references are
> > discovered or the specified time period has passed.
> >
> > The versions of |lookup| providing this blocking feature each takes as
> > one of its parameters a value of type long that represents the number of
> > milliseconds to wait for the service to be discovered. In addition to
> > |RemoteException| (described previously for these methods), each of
> > these versions of |lookup| may throw an |InterruptedException|.
> >
> > One of these blocking versions of |lookup| implicitly uses a value of
> > one for both the acceptable minimum and the allowable maximum number of
> > service references to discover. The other blocking version requires that
> > the entity specify the range through the |minMatches| and |maxMatches|
> > parameters, respectively.
> >
> > Prior to blocking, each of these versions of |lookup| first queries each
> > available lookup service in an attempt to retrieve a satisfactory number
> > of matching services. Whether or not the method actually blocks is
> > dependent on how many matching service references are found during the
> > query process. Blocking occurs only if after querying /all/ of the
> > available lookup services, the number of matching services found is less
> > than the acceptable minimum. If the waiting period (measured from when
> > blocking first begins) passes before that minimum number of service
> > references is found, the method will return the service references that
> > have been discovered up to that point. If the waiting period passes and
> > no services have been found, |null| or an empty array (depending on the
> > version of |lookup|) will be returned.
> >
> > If, after querying all of the available lookup services, the number of
> > services found to satisfy the desired criteria is greater than or equal
> > to the specified minimum but less than the specified maximum, the method
> > will return the currently discovered service references without
> > blocking. If the initial query process produces the desired maximum
> > number of service references, the method will return the results
> > immediately.
> >
> > The blocking versions of |lookup| are quite useful to entities that
> > cannot proceed until such a service of interest is found. If a
> > non-positive value is input to the |waitDur| argument, then the method
> > will not wait. It will simply query the available lookup services and
> > employ the return semantics described above.
> >
> > The values of the |minMatches| and |maxMatches| arguments must both be
> > positive, and |maxMatches| must be greater than or equal to
> > |minMatches|; otherwise, an |IllegalArgumentException| will be thrown.
> >
> > The blocking versions of |lookup| make a concurrency guarantee with
> > respect to the discovery of new lookup services during the wait period.
> > That is, while waiting for the desired service reference(s) to be
> > discovered, if one or more of the targeted--but previously
> > unavailable--lookup services is discovered and added to the managed set,
> > those new lookup services will also be queried for the service(s) of
> > interest.
> >
> > In addition, the blocking versions of |lookup| throw
> > |InterruptedException|. When an entity invokes either version with valid
> > parameters, the entity may decide during the wait period that it no
> > longer wishes to wait the entire period for the method to return. Thus,
> > while the method is blocking on the discovery of matching service(s), it
> > may be interrupted by invoking the |interrupt| method from the |Thread|
> > class. The intent of this mechanism is to allow the entity to interrupt
> > a blocking |lookup| in the same way it would a sleeping thread.
> >
>


Re: ServiceDiscoveryManager lookp specification

Posted by Greg Trasuk <tr...@stratuscom.com>.
Hi Peter:

I'd certainly apply some tolerance band to the test result.  Failing for
3 ms difference is unreasonable.  I'd expect probably 50-100ms
variance.  The ms clock isn't assumed to be that accurate.  Matter of
fact, waiting for a task switch could cause at least that much variance.

Cheers,

Greg.

On Tue, 2013-05-07 at 06:17, Peter Firmstone wrote:
> This test smells broken?
> 
> I modified the output to read in milliseconds.
> 
> Relevant ServiceDiscoveryManager method under test:
> 
> public ServiceItem[] lookup(ServiceTemplate tmpl,
>                                  int minMatches,
>                                  int maxMatches,
>                                  ServiceItemFilter filter,
>                                  long waitDur)
>                                   throws InterruptedException,
>                                          RemoteException {...}
> 
> 
> 
> To summarise what's happens (occassionally):
> 
> CASE 1:
> 
>     * ServiceDiscoveryManager.lookup  is called and blocks waiting until
>       it receives the minimum number of ServiceItems.
>     * If the required number of ServiceItem's are discovered late, the
>       waitDur limit will be approached when lookup returns.
>           o The resolution of the test is to the nearest second, not
>             millisecond.
> 
> 
>     Firstly I don't believe this is the intent of the Jini Standard
>     SD4.1.3 The blocking feature of lookup, which states:
> 
>         As noted above, each category contains a version of |lookup|
>         that provides a feature in which the entity can request that if
>         the number of service references found throughout the available
>         lookup services does not fall into a desired range, the method
>         will wait a finite period of time until either an acceptable
>         minimum number of service references are discovered or the
>         specified time period has passed.
> 
> 
>     In this case the specified time period hasn't quite passed, however
>     an acceptable minimum number of service references are returned and
>     the test fails.  This failure is rare.
> 
> 
> CASE 2:
> 
>               * 3. while lookup() is blocking, if enough new services are
>               *    registered so that the acceptable minimum is achieved,
>               *    lookup() will return immediately; that is, even if there
>               *    is more time left on the wait period, lookup() will not
>               *    wait for more services beyond the minimum.
>               *
>               *    For example, if 3 services are initially registered and
>               *    lookup is called with min = 4 and max = 7, then lookup()
>               *    will find the 3 services and then wait for more 
> services to
>               *    be registered. Suppose that while lookup() is blocking
>               *    another 5 services are registered, bringing the total 
> number
>               *    of services to 8. In this case, lookup() will stopping
>               *    waiting and return 4 services (the minimum), not the
>               *    maximum 7.
> 
> While the test performs this check, it isn't implemented in 
> ServiceDiscoveryManager, instead it's left up to chance that the lookup 
> method, waiting on an object monitor will obtain the monitor as soon as 
> it's notified.  However if multiple threads contend for the lock, it's 
> very unlikely the thread scheduler will give the object monitor to the 
> waiting thread, instead the most recently blocking thread is likely to 
> get the monitor.
> 
> This requirment in the test appears to be additional to the 
> specification and would require ServiceDiscoveryManager be modified, to 
> use some kind of fair Lock scheme.  However doing this would 
> unnecessarily complicate ServiceDiscoveryManager, instead it would be 
> simpler to return the number of ServiceItem's found up to the maxMatches 
> at the time the lock is obtained.
> 
> CASE 1 test results:
> 
> Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> Time is Mon May 06 22:17:31 EST 2013
> Starting test in separate process with command:
> 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java' 
> -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager 
> -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy 
> -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp 
> C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3.jar 
> -ea -esa -client '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081 
> -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy 
> -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar 
> -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar 
> -Dcom.sun.jini.qa.harness.runjiniserver=true 
> -Dcom.sun.jini.qa.harness.runkitserver=true 
> -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties 
> -Dcom.sun.jini.qa.harness.testhosts= 
> -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging 
> -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.test.port=9082 
> -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy 
> '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> com.sun.jini.qa.harness.MasterTest 
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> com.sun.jini.qa.harness.TestException:  -- blocked longer than expected 
> -- requested block = 60000 millisecond(s), actual block = 59999 
> millisecond(s)
>      at 
> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
>      at 
> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
>      at 
> com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
>      at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
>      at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)
> 
> TIME: 10:20:02 PM
> 
> Test process was destroyed and returned code 1
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> Test Failed: Test Failed: com.sun.jini.qa.harness.TestException:  -- 
> blocked longer than expected -- requested block = 60000 millisecond(s), 
> actual block = 59999 millisecond(s)
> 
> Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> Time is Tue May 07 10:40:30 EST 2013
> Time is Tue May 07 10:40:30 EST 2013
> Starting test in separate process with command:
> Starting test in separate process with command:
> 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java' 
> -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager 
> -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy 
> -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp 
> C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3.jar 
> -ea -esa -client '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081 
> -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy 
> -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar 
> -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar 
> -Dcom.sun.jini.qa.harness.runjiniserver=true 
> -Dcom.sun.jini.qa.harness.runkitserver=true 
> -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties 
> -Dcom.sun.jini.qa.harness.testhosts= 
> -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging 
> -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.test.port=9082 
> -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy 
> '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> com.sun.jini.qa.harness.MasterTest 
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> 'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java' 
> -Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager 
> -Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy 
> -Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp 
> C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3.jar 
> -ea -esa -client '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> -Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081 
> -Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy 
> -Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar 
> -Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar 
> -Dcom.sun.jini.qa.harness.runjiniserver=true 
> -Dcom.sun.jini.qa.harness.runkitserver=true 
> -Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties 
> -Dcom.sun.jini.qa.harness.testhosts= 
> -Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging 
> -Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa 
> -Dcom.sun.jini.test.port=9082 
> -Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy 
> '-Djava.ext.dirs=C:\Program 
> Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext' 
> com.sun.jini.qa.harness.MasterTest 
> com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
> com.sun.jini.qa.harness.TestException:  -- blocked longer than expected 
> -- requested block = 60000 millisecond(s), actual block = 59997 
> millisecond(s) wait error = 0
> com.sun.jini.qa.harness.TestException:  -- blocked longer than expected 
> -- requested block = 60000 millisecond(s), actual block = 59997 
> millisecond(s) wait error = 0
>      at 
> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
>      at 
> com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
>      at 
> com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
>      at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
>      at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)
> 
> 
> 
> 
> Relevant Section of Jini Specification:
> 
> 
>           SD.4.1.3 The |lookup| Method
> 
> The |lookup| method queries each available lookup service in the managed 
> set for service reference(s) that match criteria defined by the entity 
> that invokes this method. Entities typically employ this method when 
> they need infrequent access to services and when the cost of making 
> remote queries is outweighed by the overhead of maintaining a local 
> cache (for example, because of resource limitations).
> 
> The |lookup| method has four versions, each version falling into one of 
> two categories: those versions of this method that return a single 
> instance of |ServiceItem| and those versions that return a set of 
> service references as an array of |ServiceItem| objects.
> 
> Two arguments are common to all versions of this method: an instance of 
> |ServiceTemplate| and an instance of |ServiceItemFilter|.
> 
> Within each category, the versions of |lookup| differ only in whether or 
> not a particular version provides what is referred to as a "wait" (or 
> blocking) feature. That is, each category contains both a non-blocking 
> version of |lookup| which returns immediately when unable to find the 
> desired service, and a blocking version which returns only after waiting 
> a specified amount of time for the desired service to be discovered. The 
> particular version of |lookup| that an entity employs is typically 
> determined by the entity's intended usage pattern.
> 
> The descriptions that follow refer to all versions of the |lookup| 
> method, except where explicitly noted.
> 
> The |tmpl| argument and the |filter| argument both have semantics 
> identical to that defined for these arguments in the description of the 
> |createLookupCache| method above. In particular,
> 
>     * A |null| reference value for the |tmpl| parameter is treated as
>       the equivalent of a "wildcarded" |ServiceTemplate|.
> 
>     * If |null| is the value for the |filter| parameter, only template
>       matching will be employed to find the desired services.
> 
>     * The effects of modifying the contents of the |tmpl| parameter
>       while the invocation is in progress are unpredictable and undefined.
> 
> If no service can be found that matches the desired criteria, then the 
> versions of |lookup| from the first category--those that return a single 
> instance of |ServiceItem|--will return |null|, whereas the versions from 
> the second category--those that return an array of |ServiceItem| 
> instances--will return an empty array.
> 
> The versions of |lookup| from the first category can be used in a 
> fashion similar to the first form of the |lookup| method defined in the 
> |ServiceRegistrar| interface described in the /Jini Lookup Service 
> Specification/. That is, an entity would typically invoke one of these 
> versions of |lookup| when it wishes to find a /single/ service reference 
> and the particular lookup service with which that service reference is 
> registered is unimportant to the entity.
> 
> Each version of |lookup| defined in the |ServiceDiscoveryManager| 
> differs with the corresponding version of |lookup| in |ServiceRegistrar| 
> in the following ways:
> 
>     * The versions of |lookup| defined in the |ServiceDiscoveryManager|
>       query /multiple/ lookup services (the order in which the lookup
>       services are queried is dependent on the implementation).
> 
>     * The versions of |lookup| defined in the |ServiceDiscoveryManager|
>       can apply additional selection criteria, in the form of a filter
>       object, when deciding whether a service reference found through
>       standard template matching should be returned to the entity.
> 
> The versions of |lookup| that return an array of |ServiceItem| objects 
> can be used in a fashion similar to the second form of |lookup| defined 
> in the |ServiceRegistrar| interface. That is, an entity would typically 
> invoke these versions of |lookup| when it wishes to find /multiple/ 
> service references that satisfy the input criteria. Each of the versions 
> of |lookup| that return an array of |ServiceItem| objects takes as one 
> of its arguments an |int| parameter, |maxMatches|, that represents the 
> maximum number of matches that should be returned. The array returned by 
> these methods will contain no more than |maxMatches| service references, 
> although it may contain fewer than that number.
> 
> As with the versions of |lookup| that return a single instance of 
> |ServiceItem|, multiple queries and filtering are also notable 
> differences between the second-category versions of this method and 
> their counterpart in |ServiceRegistrar|.
> 
> For each version of |lookup|, whenever a lookup service query returns a 
> |null| service reference, the filter is bypassed, and the service 
> reference is excluded from the return object. On the other hand, if the 
> query returns a non-|null| service reference in which the associated 
> array of attribute contains one or more |null| elements, the filter is 
> still applied and the service reference is included in the return object.
> 
> Each version of |lookup| may be confronted with duplicate references 
> during a search for a service of interest. This is because the same 
> service may register with more than one lookup service in the managed 
> set. As with the cache, when a set of service references is returned by 
> |lookup|, each service reference in the return set will be unique with 
> respect to all other service references in the set, as determined by the 
> |equals| method provided by each reference.
> 
> If it is determined that a lookup service is unavailable (due to an 
> exception or some other non-fatal error) while interacting with a lookup 
> service from the managed set, all versions of |lookup| will invoke the 
> |discard| method on the instance of |DiscoveryManagement| being employed 
> by the |ServiceDiscoveryManager|. Doing so will result in the 
> unavailable lookup service being discarded and made eligible for 
> rediscovery.
> 
> Recall that the propagation of modifications to a service's attributes 
> across a set of lookup services typically occurs asynchronously. It is 
> for this reason that while invoking |lookup| to find a set of matching 
> services, it is possible that the set returned may contain multiple 
> references having the same service ID with different attributes. Note 
> that although this sort of inconsistent state can also occur if the 
> entity employs a cache, the cache will eventually reflect the correct 
> state.
> 
> The Blocking Feature of |lookup|
> 
> As noted above, each category contains a version of |lookup| that 
> provides a feature in which the entity can request that if the number of 
> service references found throughout the available lookup services does 
> not fall into a desired range, the method will wait a finite period of 
> time until either an acceptable minimum number of service references are 
> discovered or the specified time period has passed.
> 
> The versions of |lookup| providing this blocking feature each takes as 
> one of its parameters a value of type long that represents the number of 
> milliseconds to wait for the service to be discovered. In addition to 
> |RemoteException| (described previously for these methods), each of 
> these versions of |lookup| may throw an |InterruptedException|.
> 
> One of these blocking versions of |lookup| implicitly uses a value of 
> one for both the acceptable minimum and the allowable maximum number of 
> service references to discover. The other blocking version requires that 
> the entity specify the range through the |minMatches| and |maxMatches| 
> parameters, respectively.
> 
> Prior to blocking, each of these versions of |lookup| first queries each 
> available lookup service in an attempt to retrieve a satisfactory number 
> of matching services. Whether or not the method actually blocks is 
> dependent on how many matching service references are found during the 
> query process. Blocking occurs only if after querying /all/ of the 
> available lookup services, the number of matching services found is less 
> than the acceptable minimum. If the waiting period (measured from when 
> blocking first begins) passes before that minimum number of service 
> references is found, the method will return the service references that 
> have been discovered up to that point. If the waiting period passes and 
> no services have been found, |null| or an empty array (depending on the 
> version of |lookup|) will be returned.
> 
> If, after querying all of the available lookup services, the number of 
> services found to satisfy the desired criteria is greater than or equal 
> to the specified minimum but less than the specified maximum, the method 
> will return the currently discovered service references without 
> blocking. If the initial query process produces the desired maximum 
> number of service references, the method will return the results 
> immediately.
> 
> The blocking versions of |lookup| are quite useful to entities that 
> cannot proceed until such a service of interest is found. If a 
> non-positive value is input to the |waitDur| argument, then the method 
> will not wait. It will simply query the available lookup services and 
> employ the return semantics described above.
> 
> The values of the |minMatches| and |maxMatches| arguments must both be 
> positive, and |maxMatches| must be greater than or equal to 
> |minMatches|; otherwise, an |IllegalArgumentException| will be thrown.
> 
> The blocking versions of |lookup| make a concurrency guarantee with 
> respect to the discovery of new lookup services during the wait period. 
> That is, while waiting for the desired service reference(s) to be 
> discovered, if one or more of the targeted--but previously 
> unavailable--lookup services is discovered and added to the managed set, 
> those new lookup services will also be queried for the service(s) of 
> interest.
> 
> In addition, the blocking versions of |lookup| throw 
> |InterruptedException|. When an entity invokes either version with valid 
> parameters, the entity may decide during the wait period that it no 
> longer wishes to wait the entire period for the method to return. Thus, 
> while the method is blocking on the discovery of matching service(s), it 
> may be interrupted by invoking the |interrupt| method from the |Thread| 
> class. The intent of this mechanism is to allow the entity to interrupt 
> a blocking |lookup| in the same way it would a sleeping thread.
>