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.
>