View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package net.jini.lookup;
19  
20  import java.io.IOException;
21  import java.rmi.RemoteException;
22  import java.security.AccessController;
23  import java.util.ArrayList;
24  import java.util.HashSet;
25  import java.util.Iterator;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Random;
29  import java.util.Set;
30  import java.util.concurrent.BlockingQueue;
31  import java.util.concurrent.ExecutorService;
32  import java.util.concurrent.Executors;
33  import java.util.concurrent.LinkedBlockingQueue;
34  import java.util.concurrent.locks.ReentrantReadWriteLock;
35  import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
36  import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
37  import java.util.logging.Level;
38  import java.util.logging.LogRecord;
39  import java.util.logging.Logger;
40  import net.jini.config.Configuration;
41  import net.jini.config.ConfigurationException;
42  import net.jini.config.EmptyConfiguration;
43  import net.jini.config.NoSuchEntryException;
44  import net.jini.core.entry.Entry;
45  import net.jini.core.event.EventRegistration;
46  import net.jini.core.event.RemoteEventListener;
47  import net.jini.core.lease.Lease;
48  import net.jini.core.lookup.ServiceID;
49  import net.jini.core.lookup.ServiceItem;
50  import net.jini.core.lookup.ServiceMatches;
51  import net.jini.core.lookup.ServiceRegistrar;
52  import net.jini.core.lookup.ServiceTemplate;
53  import net.jini.discovery.DiscoveryEvent;
54  import net.jini.discovery.DiscoveryListener;
55  import net.jini.discovery.DiscoveryManagement;
56  import net.jini.discovery.LookupDiscoveryManager;
57  import net.jini.lookup.ServiceAttributesAccessor;
58  import net.jini.lookup.ServiceIDAccessor;
59  import net.jini.lookup.ServiceProxyAccessor;
60  import net.jini.lease.LeaseListener;
61  import net.jini.lease.LeaseRenewalEvent;
62  import net.jini.lease.LeaseRenewalManager;
63  import net.jini.security.BasicProxyPreparer;
64  import net.jini.security.ProxyPreparer;
65  import org.apache.river.action.GetBooleanAction;
66  import org.apache.river.action.GetLongAction;
67  import org.apache.river.logging.Levels;
68  import org.apache.river.lookup.entry.LookupAttributes;
69  import org.apache.river.thread.NamedThreadFactory;
70  
71  /**
72   * The <code>ServiceDiscoveryManager</code> class is a helper utility class that
73   * any client-like entity can use to "discover" services registered with any
74   * number of lookup services of interest. On behalf of such entities, this class
75   * maintains - as much as possible - up-to-date state information about both the
76   * lookup services the entity wishes to query, and the services the entity
77   * wishes to acquire and use. By maintaining current service state information,
78   * the entity can implement efficient mechanisms for service access and usage.
79   * <p>
80   * There are three basic usage patterns for this class. In order of importance
81   * and typical usage, those patterns are:
82   * <p>
83   * <ul>
84   * <li> The entity requests that the <code>ServiceDiscoveryManager</code> create
85   * a cache (an instance of {@link net.jini.lookup.LookupCache LookupCache})
86   * which will asynchronously "discover", and locally store, references to
87   * services that match criteria defined by the entity; services which are
88   * registered with one or more lookup services managed by the
89   * <code>ServiceDiscoveryManager</code> on behalf of the entity. The cache can
90   * be viewed as a set of service references that the entity can access locally
91   * as needed through one of the public, non-remote methods provided in the
92   * cache's interface. Thus, rather than making costly remote queries of multiple
93   * lookup services at the point in time when the entity needs the service, the
94   * entity can simply make local queries on the cache for the services that the
95   * cache acquired and stored at a prior time. An entity should employ this
96   * pattern when the entity must make <i>frequent</i>
97   * queries for multiple services. By populating the cache with multiple
98   * instances of the desired services, redundancy in the availability of those
99   * services can be provided. Thus, if an instance of a service is found to be
100  * unavailable when needed, the entity can execute a local query on the cache
101  * rather than one or more remote queries on the lookup services to acquire an
102  * instance that is available. To employ this pattern, the entity invokes the
103  * method  {@link net.jini.lookup.ServiceDiscoveryManager#createLookupCache
104  *        createLookupCache}.
105  * <li> The entity can register with the event mechanism provided by the
106  * <code>ServiceDiscoveryManager</code>. This event mechanism allows the entity
107  * to request that it be notified when a service of interest is discovered for
108  * the first time, or has encountered a state change such as removal from all
109  * lookup services, or attribute set changes. Although interacting with a local
110  * cache of services in the way described in the first pattern can be very
111  * useful to entities that need frequent access to multiple services, some
112  * client-like entities may wish to interact with the cache in a reactive
113  * manner. For example, an entity such as a service browser typically wishes to
114  * be notified of the arrival of new services of interest as well as any changes
115  * in the state of the current services in the cache. In these situations,
116  * polling for such changes is usually viewed as undesirable. If the cache were
117  * to also provide an event mechanism with notification semantics, the needs of
118  * entities that employ either pattern can be satisfied. To employ this pattern,
119  * the entity must create a cache and supply it with an instance of the  {@link net.jini.lookup.ServiceDiscoveryListener
120  *        ServiceDiscoveryListener} interface that will receive instances of
121  * {@link net.jini.lookup.ServiceDiscoveryEvent ServiceDiscoveryEvent} when
122  * events of interest, related to the services in the cache, occur.
123  * <li> The entity, through the public API of the
124  * <code>ServiceDiscoveryManager</code>, can directly query the lookup services
125  * managed by the <code>ServiceDiscoveryManager</code> for services of interest;
126  * employing semantics similar to the semantics employed in a typical lookup
127  * service query made through the
128  * {@link net.jini.core.lookup.ServiceRegistrar ServiceRegistrar} interface.
129  * Such queries will result in a remote call being made at the same time the
130  * service is needed (unlike the first pattern, in which remote calls typically
131  * occur prior to the time the service is needed). This pattern may be useful to
132  * entities needing to find services on an infrequent basis, or when the cost of
133  * making a remote call is outweighed by the overhead of maintaining a local
134  * cache (for example, due to limited resources). Although an entity that needs
135  * to query lookup service(s) can certainly make such queries through the
136  * {@link net.jini.core.lookup.ServiceRegistrar ServiceRegistrar} interface, the
137  * <code>ServiceDiscoveryManager</code> provides a broad API with semantics that
138  * are richer than the semantics of the
139  * {@link net.jini.core.lookup.ServiceRegistrar#lookup lookup} methods provided
140  * by the {@link net.jini.core.lookup.ServiceRegistrar
141  *        ServiceRegistrar}. This API encapsulates functionality that many client-like
142  * entities may find more useful when managing both the set of desired lookup
143  * services, and the service queries made on those lookup services. To employ
144  * this pattern, the entity simply instantiates this class with the desired
145  * parameters, and then invokes the appropriate version of the
146  * {@link net.jini.lookup.ServiceDiscoveryManager#lookup lookup} method when the
147  * entity wishes to acquire a service that matches desired criteria.
148  * </ul>
149  * <p>
150  * All three mechanisms just described - local queries on the cache, service
151  * discovery notification, and remote lookups - employ the same
152  * template-matching scheme as that employed in the
153  * {@link net.jini.core.lookup.ServiceRegistrar ServiceRegistrar} interface.
154  * Additionally, each mechanism allows the entity to supply an object referred
155  * to as a <i>filter</i>; an instance of
156  * {@link net.jini.lookup.ServiceItemFilter ServiceItemFilter}. A filter is a
157  * non-remote object that defines additional matching criteria that the
158  * <code>ServiceDiscoveryManager</code> applies when searching for the entity's
159  * services of interest. Employing a filter is particularly useful to entities
160  * that wish to extend the capabilities of the standard template-matching
161  * scheme.
162  * <p>
163  * In addition to (or instead of) employing a filter to apply additional
164  * matching criteria to candidate service proxies initially found through
165  * template matching, filters can also be used to extend the selection process
166  * so that only proxies that are <i>safe</i> to use are returned to the entity.
167  * To do this, the entity would use the
168  * {@link net.jini.lookup.ServiceItemFilter ServiceItemFilter} interface to
169  * supply the <code>ServiceDiscoveryManager</code> or
170  * {@link net.jini.lookup.LookupCache LookupCache} with a filter that, when
171  * applied to a candidate proxy, performs a set of operations that is referred
172  * to as <i>proxy preparation</i>. As described in the documentation for
173  * {@link net.jini.security.ProxyPreparer}, proxy preparation typically includes
174  * operations such as, verifying trust in the proxy, specifying client
175  * constraints, and dynamically granting necessary permissions to the proxy.
176  * <p>
177  * Note that this utility class is not remote. Clients and services that wish to
178  * use this class will create an instance of this class in their own address
179  * space to manage the state of discovered services and their associated lookup
180  * services locally.
181  *
182  *  <!-- Implementation Specifics -->
183  *
184  * The following implementation-specific items are discussed below:
185  * <ul><li> <a href="#sdmConfigEntries">Configuring ServiceDiscoveryManager</a>
186  * <li> <a href="#sdmLogging">Logging</a>
187  * </ul>
188  *
189  * <a name="sdmConfigEntries">
190  * 
191  * <b><font size="+1">Configuring ServiceDiscoveryManager</font></b>
192  * 
193  * </a>
194  *
195  * This implementation of <code>ServiceDiscoveryManager</code> supports the
196  * following configuration entries; where each configuration entry name is
197  * associated with the component name
198  * <code>net.jini.lookup.ServiceDiscoveryManager</code>. Note that the
199  * configuration entries specified here are specific to this implementation of
200  * <code>ServiceDiscoveryManager</code>. Unless otherwise stated, each entry is
201  * retrieved from the configuration only once per instance of this utility,
202  * where each such retrieval is performed in the constructor.
203  * <p>
204  * It is important to note that in addition to allowing a client of this utility
205  * to request - through the public API - the creation of a cache that is used
206  * externally by the client, this utility also creates instances of the cache
207  * that are used internally by the utility itself. As such, in addition to the
208  * configuration entries that are used only in this utility (and not in any
209  * cache), and the configuration entries that are retrieved during the
210  * construction of each new cache (and used by only that cache), there are
211  * configuration entries specified below that are retrieved once during the
212  * construction of this utility, but which are shared with, and used by, the
213  * caches that are created.
214  *
215  *
216  * <a name="cacheExecutorService"></a>
217  * <table summary="Describes the cacheExecutorService configuration entry"
218  * border="0" cellpadding="2">
219  * <tr valign="top">
220  * <th scope="col" > <font size="+1">&#X2022;</font>
221  * <th scope="col" align="left" colspan="2"> <font size="+1">
222  * <code>cacheExecutorService</code></font>
223  *
224  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
225  * Type: <td> {@link java.util.concurrent.ExecutorService ExecutorService}
226  *
227  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
228  * Default: <td> <code>new
229  *             {@link java.util.concurrent.ThreadPoolExecutor
230  *                     ThreadPoolExecutor}( 6, 6, 15, TimeUnit.SECONDS, new LinkedBlockingQueue(),
231  * new NamedThreadFactory( "SDM lookup cache", false ))</code>
232  *
233  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
234  * Description:
235  * <td> The object that pools and manages the various threads executed by each
236  * of the lookup caches created by this utility. There is one such
237  * ExecutorService created for each cache. For each cache that is created in
238  * this utility, a single, separate instance of this ExecutorService will be
239  * retrieved and employed by that cache. This object should not be shared with
240  * other components in the application that employs this utility.
241  * </table>
242  * 
243  * <a name="discardExecutorService"></a>
244  * <table summary="Describes the discardExecutorService configuration entry"
245  * border="0" cellpadding="2">
246  * <tr valign="top">
247  * <th scope="col" > <font size="+1">&#X2022;</font>
248  * <th scope="col" align="left" colspan="2"> <font size="+1">
249  * <code>discardExecutorService</code></font>
250  *
251  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
252  * Type: <td> {@link java.util.concurrent.ScheduledExecutorService ScheduledExecutorService}
253  *
254  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
255  * Default: <td> <code>new
256  *             {@link java.util.concurrent.ScheduledThreadPoolExecutor
257  *                     ScheduledThreadPoolExecutor}( 4,
258  * new NamedThreadFactory( "SDM discard timer", false ))</code>
259  *
260  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
261  * Description:
262  * <td> The object that pools and manages the threads, executed by a cache, that
263  * wait on verification events after a previousy discovered service has been
264  * discarded. For each cache that is created in this utility, a single, separate
265  * instance of this ExecutorService will be retrieved and employed by that
266  * cache. This object should not be shared with other components in the
267  * application that employs this utility.
268  * </table>
269  * 
270  *  *
271  * <a name="ServiceEventExecutorService"></a>
272  * <table summary="Describes the ServiceEventExecutorService configuration entry"
273  * border="0" cellpadding="2">
274  * <tr valign="top">
275  * <th scope="col" > <font size="+1">&#X2022;</font>
276  * <th scope="col" align="left" colspan="2"> <font size="+1">
277  * <code>ServiceEventExecutorService</code></font>
278  *
279  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
280  * Type: <td> {@link java.util.concurrent.ExecutorService ExecutorService}
281  *
282  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
283  * Default: <td> <code>new
284  *             {@link java.util.concurrent.ThreadPoolExecutor
285  *                     ThreadPoolExecutor}( 2, 2, 15, TimeUnit.SECONDS, new PriorityBlockingQueue(256),
286  * new NamedThreadFactory( "SDM ServiceEvent: " +toString(), false ))</code>
287  *
288  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
289  * Description:
290  * <td> The ExecutorService object that processes incoming ServiceEvent's.  This
291  * executor is wrapped by an {@link org.apache.river.thread.ExtensibleExecutorService}
292  * that implements a {@link java.lang.Comparable} {@link java.util.concurrent.FutureTask},
293  * which orders the ServiceEvent's in the {@link java.util.concurrent.PriorityBlockingQueue}.
294  * <p>
295  * A single threaded executor is usually sufficient for most purposes, and is 
296  * usually seldom found in a running state, testing has shown a single threaded
297  * executor is very unlikely to perform unnecessary lookup's, while a
298  * ThreadPoolExecutor with 4 threads will perform lookup for about 1% of cases.  
299  * This can be measured by setting the SDM logger to fine.
300  * <p>
301  * There is however an issue that does occur when running a single threaded
302  * executor, the lookup events that occur, seem to occur because of a problem
303  * when registering 
304  * <p>
305  * For the purpose of testing, it is beneficial to use a ThreadPoolExecutor with
306  * numerous threads, as this will test the alternate execution paths that 
307  * include lookup.
308  * </table>
309  * 
310  * 
311  * <a name="discardWait"></a>
312  * <table summary="Describes the discardWait configuration entry" border="0"
313  * cellpadding="2">
314  * <tr valign="top">
315  * <th scope="col" > <font size="+1">&#X2022;</font>
316  * <th scope="col" align="left" colspan="2"> <font size="+1">
317  * <code>discardWait</code></font>
318  *
319  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
320  * Type: <td> <code>long</code>
321  *
322  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
323  * Default: <td> <code>2*(5*60*1000)</code>
324  *
325  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
326  * Description:
327  * <td> The value used to affect the behavior of the mechanism that handles the
328  * <i>service discard problem</i> described in this utility's specification.
329  * This item allows each entity that uses this utility to define how long (in
330  * milliseconds) to wait for verification from the lookup service(s) that a
331  * discarded service is actually down before committing or un-committing a
332  * requested service discard. The current implementation of this utility
333  * defaults to waiting 10 minutes (twice the maximum lease duration granted by
334  * the Reggie implementation of the lookup service). Note that this item is used
335  * only by the caches (both internal and external) that are created by this
336  * utility, and not by the utility itself.
337  * </table>
338  * 
339  * <a name="discoveryManager"></a>
340  * <table summary="Describes the discoveryManager configuration entry"
341  * border="0" cellpadding="2">
342  * <tr valign="top">
343  * <th scope="col" > <font size="+1">&#X2022;</font>
344  * <th scope="col" align="left" colspan="2"> <font size="+1">
345  * <code>discoveryManager</code></font>
346  *
347  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
348  * Type: <td> {@link net.jini.discovery.DiscoveryManagement}
349  *
350  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
351  * Default: <td> <code> new
352  *    {@link net.jini.discovery.LookupDiscoveryManager#LookupDiscoveryManager(
353  *      java.lang.String[],
354  *      net.jini.core.discovery.LookupLocator[],
355  *      net.jini.discovery.DiscoveryListener,
356  *      net.jini.config.Configuration) LookupDiscoveryManager}( new
357  * java.lang.String[] {""}, new
358  * {@link net.jini.core.discovery.LookupLocator}[0], null, config)</code>
359  *
360  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
361  * Description:
362  * <td> The object used to manage the discovery processing performed by this
363  * utility. This entry will be retrieved from the configuration only if no
364  * discovery manager is specified in the constructor. Note that this object
365  * should not be shared with other components in the application that employs
366  * this utility. This item is used only by the service discovery manager, and
367  * not by any cache that is created.
368  * </table>
369  * 
370  * <a name="eventLeasePreparer"></a>
371  * <table summary="Describes the eventLeasePreparer configuration entry"
372  * border="0" cellpadding="2">
373  * <tr valign="top">
374  * <th scope="col" > <font size="+1">&#X2022;</font>
375  * <th scope="col" align="left" colspan="2"> <font size="+1">
376  * <code>eventLeasePreparer</code></font>
377  *
378  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
379  * Type: <td> {@link net.jini.security.ProxyPreparer}
380  *
381  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
382  * Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
383  * </code>
384  *
385  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
386  * Description:
387  * <td> Preparer for the leases returned when a cache registers with the event
388  * mechanism of any of the discovered lookup services. This item is used only by
389  * the caches (both internal and external) that are created by this utility, and
390  * not by the utility itself.
391  * <p>
392  * Currently, no methods of the returned proxy are invoked by this utility.
393  * </table>
394  * 
395  * <a name="eventListenerExporter"></a>
396  * <table summary="Describes the eventListenerExporter configuration entry"
397  * border="0" cellpadding="2">
398  * <tr valign="top">
399  * <th scope="col" > <font size="+1">&#X2022;</font>
400  * <th scope="col" align="left" colspan="2"> <font size="+1">
401  * <code>eventListenerExporter</code></font>
402  *
403  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
404  * Type: <td> {@link net.jini.export.Exporter}
405  *
406  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
407  * Default: <td> <code> new
408  *                {@link net.jini.jeri.BasicJeriExporter#BasicJeriExporter(
409  *                                        net.jini.jeri.ServerEndpoint,
410  *                                        net.jini.jeri.InvocationLayerFactory,
411  *                                        boolean,
412  *                                        boolean) BasicJeriExporter}(
413  *              {@link net.jini.jeri.tcp.TcpServerEndpoint#getInstance
414  *                                      TcpServerEndpoint.getInstance}(0),<br>
415  * &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new
416  * {@link net.jini.jeri.BasicILFactory}(),<br>
417  * &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
418  * false, false)</code>
419  *
420  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
421  * Description:
422  * <td> Exporter for the remote event listener that each cache supplies to the
423  * lookup services whose event mechanisms those caches register with. Note that
424  * for each cache that is created in this utility, a single, separate instance
425  * of this exporter will be retrieved and employed by that cache. Note also that
426  * the default exporter defined here will disable distributed garbage collection
427  * (DGC) for the server endpoint associated with the exported listener, and the
428  * listener backend (the "impl") will be strongly referenced. This means that
429  * the listener will not "go away" unintentionally. Additionally, that exporter
430  * also sets the <code>keepAlive</code> flag to <code>false</code> to allow the
431  * VM in which this utility runs to "go away" when desired; and not be kept
432  * alive simply because the listener is still exported.
433  * </table>
434  *
435  * <a name="leaseManager"></a>
436  * <table summary="Describes the leaseManager configuration entry" border="0"
437  * cellpadding="2">
438  * <tr valign="top">
439  * <th scope="col" > <font size="+1">&#X2022;</font>
440  * <th scope="col" align="left" colspan="2"> <font size="+1">
441  * <code>leaseManager</code></font>
442  *
443  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
444  * Type: <td> {@link net.jini.lease.LeaseRenewalManager}
445  *
446  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
447  * Default: <td> <code> new
448  *       {@link net.jini.lease.LeaseRenewalManager#LeaseRenewalManager(
449  *        net.jini.config.Configuration) LeaseRenewalManager}(config)</code>
450  *
451  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
452  * Description:
453  * <td> The object used to manage any event leases returned to a cache that has
454  * registered with the event mechanism of the various discovered lookup
455  * services. This entry will be retrieved from the configuration only if no
456  * lease renewal manager is specified in the constructor. This item is used only
457  * by the caches (both internal and external) that are created by this utility,
458  * and not by the utility itself.
459  * </table>
460  *
461  * <a name="registrarPreparer"></a>
462  * <table summary="Describes the registrarPreparer configuration entry"
463  * border="0" cellpadding="2">
464  * <tr valign="top">
465  * <th scope="col" > <font size="+1">&#X2022;</font>
466  * <th scope="col" align="left" colspan="2"> <font size="+1">
467  * <code>registrarPreparer</code></font>
468  *
469  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
470  * Type: <td> {@link net.jini.security.ProxyPreparer}
471  *
472  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
473  * Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
474  * </code>
475  *
476  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
477  * Description:
478  * <td> Preparer for the proxies to the lookup services that are discovered and
479  * used by this utility. This item is used only by the service discovery
480  * manager, and not by any cache that is created.
481  * <p>
482  * The following methods of the proxy returned by this preparer are invoked by
483  * this utility:
484  * <ul>
485  * <li>{@link net.jini.core.lookup.ServiceRegistrar#lookup lookup}
486  * <li>{@link net.jini.core.lookup.ServiceRegistrar#notify notify}
487  * </ul>
488  *
489  * </table>
490  * 
491  * 
492  * <a name="bootstrapPreparer"></a>
493  * <table summary="Describes the bootstrapPreparer configuration entry"
494  * border="0" cellpadding="2">
495  * <tr valign="top">
496  * <th scope="col" > <font size="+1">&#X2022;</font>
497  * <th scope="col" align="left" colspan="2"> <font size="+1">
498  * <code>bootstrapPreparer</code></font>
499  *
500  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
501  * Type: <td> {@link net.jini.security.ProxyPreparer}
502  *
503  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
504  * Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
505  * </code>
506  *
507  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
508  * Description:
509  * <td> Preparer for bootstrap proxy results returned by lookup services 
510  * that are discovered and used by this utility.
511  * <p>
512  * The following methods of the proxy returned by this preparer are invoked by
513  * this utility:
514  * <ul>
515  * <li>{@link SafeServiceRegistrar#lookUp(net.jini.core.lookup.ServiceTemplate, int)  lookup}
516  * <li>{@link SafeServiceRegistrar#notiFy notiFy}
517  * </ul>
518  *
519  * </table>
520  * 
521  * 
522  *
523  * <a name="useInsecureLookup"></a>
524  * <table summary="Describes the useInsecureLookup configuration entry"
525  * border="0" cellpadding="2">
526  * <tr valign="top">
527  * <th scope="col" > <font size="+1">&#X2022;</font>
528  * <th scope="col" align="left" colspan="2"> <font size="+1">
529  * <code>useInsecureLookup</code></font>
530  *
531  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
532  * Type: <td> {@link java.lang.Boolean}
533  *
534  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
535  * Default: <td> <code>new {@link java.lang.Boolean}("FALSE")
536  * </code>
537  *
538  * <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
539  * Description:
540  * <td> When true, ServiceDiscoveryManager and LookupCache use 
541  * {@link net.jini.core.lookup.ServiceRegistrar#lookup(net.jini.core.lookup.ServiceTemplate, int)}
542  * instead of {@link SafeServiceRegistrar#lookUp(net.jini.core.lookup.ServiceTemplate, int)}
543  * to perform service discovery.
544  * </table>
545  * 
546  * 
547  * <a name="sdmLogging">
548  * 
549  * <b><font size="+1">Logging</font></b>
550  * 
551  * </a>
552  *
553  * This implementation of <code>ServiceDiscoveryManager</code> uses the
554  * {@link Logger} named <code>net.jini.lookup.ServiceDiscoveryManager</code> to
555  * log information at the following logging levels:
556  * <p>
557  *
558  * <table border="1" cellpadding="5" summary="Describes the information logged
559  * by ServiceDiscoveryManager, and the levels at which that information is
560  * logged">
561  *
562  *
563  * <caption>
564  * <b><code>net.jini.lookup.ServiceDiscoveryManager</code></b>
565  * </caption>
566  *
567  * <tr> <th scope="col"> Level</th>
568  * <th scope="col"> Description</th>
569  * </tr>
570  *
571  * <tr>
572  * <td>{@link java.util.logging.Level#INFO INFO}</td>
573  * <td>
574  * when any exception occurs while querying a lookup service, or upon applying a
575  * filter to the results of such a query
576  * </td>
577  * </tr>
578  * <tr>
579  * <td>{@link java.util.logging.Level#INFO INFO}</td>
580  * <td>
581  * when any exception occurs while attempting to register with the event
582  * mechanism of a lookup service, or while attempting to prepare the lease on
583  * the registration with that event mechanism
584  * </td>
585  * </tr>
586  * <tr>
587  * <td>{@link java.util.logging.Level#INFO INFO}</td>
588  * <td>when any exception occurs while attempting to prepare a proxy</td>
589  * </tr>
590  * <tr>
591  * <td>{@link java.util.logging.Level#INFO INFO}</td>
592  * <td>
593  * when an <code>IllegalStateException</code> occurs while discarding a lookup
594  * service proxy after logging a failure that has occurred in one of the tasks
595  * executed by this utility
596  * </td>
597  * </tr>
598  * <tr>
599  * <td>{@link java.util.logging.Level#INFO INFO}</td>
600  * <td>upon failure of the lease renewal process</td>
601  * </tr>
602  * <tr>
603  * <td>{@link org.apache.river.logging.Levels#HANDLED HANDLED}</td>
604  * <td>
605  * when an exception occurs because a remote call to a lookup service has been
606  * interrupted as a result of the termination of a cache
607  * </td>
608  * </tr>
609  * <tr>
610  * <td>{@link org.apache.river.logging.Levels#HANDLED HANDLED}</td>
611  * <td>
612  * when a "gap" is encountered in an event sequence from a lookup service
613  * </td>
614  * </tr>
615  * <tr>
616  * <td>{@link java.util.logging.Level#FINER FINER}</td>
617  * <td>upon failure of the lease cancellation process</td>
618  * </tr>
619  * <tr>
620  * <td>{@link java.util.logging.Level#FINEST FINEST}</td>
621  * <td>whenever any task is started</td>
622  * </tr>
623  * <tr>
624  * <td>{@link java.util.logging.Level#FINEST FINEST}</td>
625  * <td>whenever any task completes successfully</td>
626  * </tr>
627  * <tr>
628  * <td>{@link java.util.logging.Level#FINEST FINEST}</td>
629  * <td>whenever a lookup cache is created</td>
630  * </tr>
631  * <tr>
632  * <td>{@link java.util.logging.Level#FINEST FINEST}</td>
633  * <td>whenever a lookup cache is terminated</td>
634  * </tr>
635  * <tr>
636  * <td>{@link java.util.logging.Level#FINEST FINEST}</td>
637  * <td>whenever a proxy is prepared</td>
638  * </tr>
639  * <tr>
640  * <td>{@link java.util.logging.Level#FINEST FINEST}</td>
641  * <td>
642  * when an exception (that is, <code>IllegalStateException</code>) occurs while
643  * unexporting a cache's remote event listener while the cache is being
644  * terminated
645  * </td>
646  * </tr>
647  * </table>
648  * <p>
649  * See the {@link org.apache.river.logging.LogManager} class for one way to use the
650  * logging level {@link org.apache.river.logging.Levels#HANDLED HANDLED} in standard
651  * logging configuration files.
652  * <p>
653  *
654  * @author Sun Microsystems, Inc.
655  *
656  * @see net.jini.discovery.DiscoveryManagement
657  * @see net.jini.lookup.LookupCache
658  * @see net.jini.lookup.ServiceDiscoveryListener
659  * @see net.jini.lookup.ServiceDiscoveryEvent
660  * @see net.jini.core.lookup.ServiceRegistrar
661  */
662 public class ServiceDiscoveryManager {
663     
664     final ProxyPreparer bootstrapProxyPreparer;
665     final boolean useInsecureLookup;
666     private final static String DISCARD_PROPERTY = "org.apache.river.sdm.discardWait";
667     private final static String INSECURE_LOOKUP_PROPERTY = "org.apache.river.sdm.insecureLookup";
668     private static final ExecutorService logExec = Executors.newSingleThreadExecutor(new NamedThreadFactory("SDM logger", false));
669     
670     static void log(Level level, String message){
671         log(level, message, null, null);
672     }
673     
674     static void log(Level level, String message, Throwable thrown){
675         log(level, message, null, thrown);
676     }
677     
678     static void log(Level level, String message, Object[] parameters){
679         log(level, message, parameters, null);
680     }
681     
682     static void log(Level level, String message, Object[] parameters, Throwable thrown){
683         final LogRecord record = new LogRecord(level, message);
684         record.setParameters(parameters);
685         record.setThrown(thrown);
686 	logExec.submit(new Runnable(){
687 	    public void run() {
688 		logger.log(record);
689 	    }
690 	});
691     }
692 
693     static void logp(Level logLevel, String sourceClass, String sourceMethod, String message, Throwable thrown) {
694         final LogRecord record = new LogRecord(logLevel, message);
695         record.setSourceClassName(sourceClass);
696         record.setSourceMethodName(sourceMethod);
697         record.setThrown(thrown);
698 	logExec.submit(new Runnable(){
699 	    public void run() {
700 		logger.log(record);
701 	    }
702 	});
703     }
704     /**
705      * @return the discardWait
706      */
707     long getDiscardWait() {
708         long disWait = AccessController.doPrivileged(new GetLongAction(DISCARD_PROPERTY, discardWait));
709         if (logger.isLoggable(Level.FINEST))
710             log(Level.FINEST, "discard wait = {0}", new Object[]{ disWait});
711         return disWait;
712     }
713 
714     /**
715      * @return the useInsecureLookup
716      */
717     boolean useInsecureLookup() {
718         if (AccessController.doPrivileged(new GetBooleanAction(INSECURE_LOOKUP_PROPERTY))) {
719             if (logger.isLoggable(Level.CONFIG)){
720                 log(Level.CONFIG, "useInsecureLookup = {0}", new Object[]{true});
721             }
722             return true;
723         }
724         return useInsecureLookup;
725     }
726 
727     
728 
729 
730     /**
731      * Class that defines the listener that will receive local events from the
732      * internal LookupCache used in the blocking versions of lookup().
733      */
734     private final static class ServiceDiscoveryListenerImpl
735             implements ServiceDiscoveryListener {
736 
737         private final List<ServiceItem> items = new LinkedList<ServiceItem>();
738 
739         @Override
740         public synchronized void serviceAdded(ServiceDiscoveryEvent event) {
741             items.add(event.getPostEventServiceItem());
742             this.notifyAll();
743         }
744 
745         @Override
746         public void serviceRemoved(ServiceDiscoveryEvent event) {
747         }
748 
749         @Override
750         public void serviceChanged(ServiceDiscoveryEvent event) {
751         }
752 
753         public synchronized ServiceItem[] getServiceItem() {
754             ServiceItem[] r = new ServiceItem[items.size()];
755             items.toArray(r);
756             items.clear();
757             return r;
758         }
759     }//end class ServiceDiscoveryManager.ServiceDiscoveryListenerImpl
760 
761     /**
762      * The Listener class for the LeaseRenewalManager.
763      */
764     private final class LeaseListenerImpl implements LeaseListener {
765 
766         private final ServiceRegistrar proxy;
767 
768         public LeaseListenerImpl(ServiceRegistrar proxy) {
769             this.proxy = proxy;
770         }
771         /* When lease renewal fails, we discard the proxy  */
772 
773         @Override
774         public void notify(LeaseRenewalEvent e) {
775             fail(e.getException(), proxy, this.getClass().getName(), "notify",
776                     "failure occurred while renewing an event lease", false);
777         }
778     }//end class ServiceDiscoveryManager.LeaseListenerImpl
779 
780     /**
781      * Allows termination of LookupCacheImpl so blocking lookup can return
782      * quickly
783      */
784     private static final class LookupCacheTerminator implements Runnable {
785 
786         private final BlockingQueue<LookupCacheImpl> cacheList = new LinkedBlockingQueue<LookupCacheImpl>(20);
787 
788         @Override
789         public void run() {
790             while (!cacheList.isEmpty() || !Thread.currentThread().isInterrupted()) {
791                 try {
792                     LookupCacheImpl cache = cacheList.take();
793                     synchronized (cache) {
794                         cache.terminate();
795                     }
796                 } catch (InterruptedException ex) {
797                     if (logger.isLoggable(Level.FINEST))
798                         log(Level.FINEST, "SDM lookup cache terminator interrupted", ex);
799                     Thread.currentThread().interrupt();
800                 }
801             }
802         }
803 
804         void terminate(LookupCacheImpl cache) {
805             boolean added = cacheList.offer(cache);
806             if (!added) { // happens if cacheList is full.
807                 // Do it yourself you lazy caller thread!  Can't you see I'm busy?
808                 synchronized (cache) {
809                     cache.terminate();
810                 }
811             }
812         }
813 
814     }
815 
816 
817 
818     /* Name of this component; used in config entry retrieval and the logger.*/
819     static final String COMPONENT_NAME
820             = "net.jini.lookup.ServiceDiscoveryManager";
821     /* Logger used by this utility. */
822     static final Logger logger = Logger.getLogger(COMPONENT_NAME);
823     /* The discovery manager to use (passed in, or create one). */
824     final DiscoveryManagement discMgr;
825     /* Indicates whether the discovery manager was created internally or not */
826     final boolean discMgrInternal;
827     /* The listener added to discMgr that receives DiscoveryEvents */
828     final DiscMgrListener discMgrListener;
829     /* The LeaseRenewalManager to use (passed in, or create one). */
830     final LeaseRenewalManager leaseRenewalMgr;
831     /* Contains all of the discovered lookup services (ServiceRegistrar). */
832     final WriteLock proxyRegSetWrite;
833     final ReadLock proxyRegSetRead;
834     final Set<ProxyReg> proxyRegSet;
835     /* Random number generator for use in lookup. */
836     final Random random = new Random();
837     /* Contains all of the instances of LookupCache that are requested. */
838     final WriteLock cachesWrite;
839     final ReadLock cachesRead;
840     private final List<LookupCache> caches;
841 
842     /* Flag to indicate if the ServiceDiscoveryManager has been terminated. */
843     private boolean bTerminated = false; //sync on this
844 
845     private final Thread terminatorThread;
846     private final LookupCacheTerminator terminator;
847     /* Flag to indicate LookupCacheTerminator has been started */
848     private boolean started = false; // sync on terminatorThread
849     /* Object used to obtain the configuration items for this utility. */
850     final Configuration thisConfig;
851     /* Preparer for the proxies to the lookup services that are discovered
852      * and used by this utility.
853      */
854     private final ProxyPreparer registrarPreparer;
855     /* Preparer for the proxies to the leases returned to this utility when
856      * it registers with the event mechanism of any of the discovered lookup
857      * services.
858      */
859     private final ProxyPreparer eventLeasePreparer;
860     /* Wait value used when handling the "service discard problem". */
861     final long discardWait;
862 
863     /* Listener class for lookup service discovery notification. */
864     private class DiscMgrListener implements DiscoveryListener {
865         /* New or previously discarded proxy has been discovered. */
866 
867         @Override
868         public void discovered(DiscoveryEvent e) {
869             ServiceRegistrar[] proxys = e.getRegistrars();
870             for (int i = 0, l = proxys.length; i < l; i++) {
871                 /* Prepare each lookup service proxy before using it. */
872 		if ( !useInsecureLookup() && 
873 			!(proxys[i] instanceof SafeServiceRegistrar)) continue;
874                 try {
875                     proxys[i]
876                             = (ServiceRegistrar) registrarPreparer.prepareProxy(proxys[i]);
877                     if (logger.isLoggable(Level.FINEST)){
878                         log(Level.FINEST, "ServiceDiscoveryManager - "
879                             + "discovered lookup service proxy prepared: {0}",
880                             new Object []{proxys[i]}
881                         );
882                     }
883                 } catch (Exception e1) {
884                     if (logger.isLoggable(Level.INFO))
885                         log(Level.INFO,
886                             "failure preparing discovered ServiceRegistrar "
887                             + "proxy, discarding the proxy",
888                             e1);
889                     discard(proxys[i]);
890                     continue;
891                 }
892                 ProxyReg reg = new ProxyReg(proxys[i]);
893                 boolean added;
894                 // Changed to only add to newProxys if actually new 7th Jan 2014
895                 proxyRegSetWrite.lock();
896                 try{
897                     added = proxyRegSet.add(reg);
898                 } finally {
899                     proxyRegSetWrite.unlock();
900                 }
901                 if (added) cacheAddProxy(reg);
902             }//end loop
903         }//end DiscMgrListener.discovered
904 
905         /* Previously discovered proxy has been discarded. */
906         @Override
907         public void discarded(DiscoveryEvent e) {
908             ServiceRegistrar[] proxys = e.getRegistrars();
909             List<ProxyReg> drops = new LinkedList<ProxyReg>();
910             for (int i = 0, l = proxys.length; i < l; i++) {
911                 ProxyReg reg = removeReg(proxys[i]);
912                 if (reg != null) { // this check can be removed.
913                     drops.add(reg);
914                 } else {
915                     //River-337
916                     if (logger.isLoggable(Level.SEVERE))
917                         log(Level.SEVERE, "discard error, proxy was null");
918                     //throw new RuntimeException("discard error");
919                 }//endif
920             }//end loop
921             Iterator<ProxyReg> iter = drops.iterator();
922             while (iter.hasNext()) {
923                 dropProxy(iter.next());
924             }//end loop
925         }//end DiscMgrListener.discarded
926 
927         /**
928          * Discards a ServiceRegistrar through the discovery manager.
929          */
930         private void discard(ServiceRegistrar proxy) {
931             discMgr.discard(proxy);
932         }//end discard
933 
934     }//end class ServiceDiscoveryManager.DiscMgrListener
935 
936     /**
937      * Adds the given proxy to all the caches maintained by the SDM.
938      */
939     private void cacheAddProxy(ProxyReg reg) {
940         cachesRead.lock();
941         try{
942             Iterator iter = caches.iterator();
943             while (iter.hasNext()) {
944                 LookupCacheImpl cache = (LookupCacheImpl) iter.next();
945                 cache.addProxyReg(reg);
946             }//end loop
947         } finally { 
948             cachesRead.unlock();
949         }
950     }//end cacheAddProxy
951 
952     /**
953      * Removes the given proxy from all the caches maintained by the SDM.
954      */
955     private void dropProxy(ProxyReg reg) {
956         cachesRead.lock();
957         try {
958             Iterator iter = caches.iterator();
959             while (iter.hasNext()) {
960                 LookupCacheImpl cache = (LookupCacheImpl) iter.next();
961                 cache.removeProxyReg(reg);
962             }//end loop
963         } finally {
964             cachesRead.unlock();
965         }
966     }//end dropProxy
967 
968     /**
969      * Constructs an instance of <code>ServiceDiscoveryManager</code> which
970      * will, on behalf of the entity that constructs this class, discover and
971      * manage a set of lookup services, as well as discover and manage sets of
972      * services registered with those lookup services. The entity indicates
973      * which lookup services to discover and manage through the parameters input
974      * to this constructor.
975      * <p>
976      * As stated in the class description, this class has three usage patterns:
977      * <p>
978      * <ul>
979      * <li> the entity uses a {@link net.jini.lookup.LookupCache
980      *        LookupCache} to locally store and manage discovered services so that
981      * those services can be accessed quickly
982      * <li> the entity registers with the event mechanism provided by a
983      * {@link net.jini.lookup.LookupCache LookupCache} to be notified when
984      * services of interest are discovered
985      * <li> the entity uses the <code>ServiceDiscoveryManager</code> to perform
986      * remote queries of the lookup services, employing richer semantics than
987      * that provided through the standard
988      * {@link net.jini.core.lookup.ServiceRegistrar ServiceRegistrar} interface
989      * </ul>
990      * <p>
991      * Although the first two usage patterns emphasize the use of a cache
992      * object, that cache is acquired only through an instance of the
993      * <code>ServiceDiscoveryManager</code> class.
994      * <p>
995      * It is important to note that some of the methods of this class      ({@link net.jini.lookup.ServiceDiscoveryManager#createLookupCache
996      * createLookupCache} and the <i>blocking</i> versions of
997      * {@link net.jini.lookup.ServiceDiscoveryManager#lookup lookup} to be
998      * exact) can throw a {@link java.rmi.RemoteException} when invoked. This is
999      * because each of these methods may attempt to register with the event
1000      * mechanism of at least one lookup service, a process that requires a
1001      * remote object (a listener) to be exported to the lookup service(s). Both
1002      * the process of registering with a lookup service's event mechanism and
1003      * the process of exporting a remote object are processes that can result in
1004      * a {@link java.rmi.RemoteException}.
1005      * <p>
1006      * In order to facilitate the exportation of the remote listener just
1007      * described, the <code>ServiceDiscoveryManager</code> class instantiates an
1008      * inner class that implements the
1009      * {@link net.jini.core.event.RemoteEventListener RemoteEventListener}
1010      * interface. Although this class defines, instantiates, and exports this
1011      * remote listener, <i>it is the entity's responsibility</i> to provide a
1012      * mechanism for any lookup service to acquire the proxy to the exported
1013      * listener. One way to do this is to configure this utility to export the
1014      * listener using the Jini(TM) Extensible Remote Invocation (Jini ERI)
1015      * communication framework. When the listener is exported to use Jini ERI,
1016      * and no proxy customizations (such as a custom invocation handler or
1017      * transport endpoint) are used, no other action is necessary to make the
1018      * proxy to the listener available to the lookup service(s) with which that
1019      * listener is registered.
1020      * <p>
1021      * The <a href="#eventListenerExporter">default exporter</a> for this
1022      * utility will export the remote event listener under Jini ERI, specifying
1023      * that the port and object ID with which the listener is to be exported
1024      * should be chosen by the Jini ERI framework, not the deployer.
1025      * <p>
1026      * If it is required that the remote event listener be exported under JRMP
1027      * instead of Jini ERI, then the entity that employs this utility must
1028      * specify this in its configuration. For example, the entity's
1029      * configuration would need to contain something like the following:
1030      * <p>
1031      * <blockquote>
1032      * <pre>
1033      * import net.jini.jrmp.JrmpExporter;
1034      *
1035      * application.configuration.component.name {
1036      *    .......
1037      *    .......
1038      *    // configuration items specific to the application
1039      *    .......
1040      *    .......
1041      * }//end application.configuration.component.name
1042      *
1043      * net.jini.lookup.ServiceDiscoveryManager {
1044      *
1045      *    serverExporter = new JrmpExporter();
1046      *
1047      * }//end net.jini.lookup.ServiceDiscoveryManager
1048      * </pre>
1049      * </blockquote>
1050      * <p>
1051      * It is important to note that when the remote event listener is exported
1052      * under JRMP, unlike Jini ERI, the JRMP remote communication framework does
1053      * <b><i>not</i></b> provide a mechanism that automatically makes the
1054      * listener proxy available to the lookup service(s) with which the listener
1055      * is registered; the deployer of the entity, or the entity itself, must
1056      * provide such a mechanism.
1057      * <p>
1058      * When exported under JRMP, one of the more common mechanisms for making
1059      * the listener proxy available to the lookup service(s) with which the
1060      * listener is registered consists of the following:
1061      * <p>
1062      * <ul><li> store the necessary class files in a JAR file
1063      * <li> make the class files in the JAR file <i>preferred</i>
1064      * (see package <code>net.jini.loader.pref </code> for details)
1065      * <li> run an HTTP server to serve up the JAR file to any requesting lookup
1066      * service
1067      * <li> advertise the location of that JAR file by setting the
1068      * <code>java.rmi.server.codebase</code> property of the entity to "point"
1069      * at the JAR file
1070      * </ul>
1071      * <p>
1072      * For example, suppose an application consists of an entity that intends to
1073      * use the <code>ServiceDiscoveryManager</code> will run on a host named
1074      * <b><i>myHost</i></b>. And suppose that the <i>down-loadable</i> JAR file
1075      * named <b><i>sdm-dl.jar</i></b> that is provided in the distribution is
1076      * located in the directory <b><i>/files/jini/lib</i></b>, and will be
1077      * served by an HTTP server listening on port
1078      * <b><i>8082</i></b>. If the application is run with its codebase property
1079      * set to
1080      * <code>-Djava.rmi.server.codebase="http://myHost:8082/sdm-dl.jar"</code>,
1081      * the lookup service(s) should then be able to access the remote listener
1082      * exported under JRMP by the <code>ServiceDiscoveryManager</code> on behalf
1083      * of the entity.
1084      * <p>
1085      * If a mechanism for lookup services to access the remote listener exported
1086      * by the <code>ServiceDiscoveryManager</code> is not provided (either by
1087      * the remote communication framework itself, or by some other means), the
1088      * remote methods of the <code>ServiceDiscoveryManager</code> - the methods
1089      * involved in the two most important usage patterns of that utility - will
1090      * be of no use.
1091      * <p>
1092      * This constructor takes two arguments: an object that implements the
1093      * <code>DiscoveryManagement</code> interface and a reference to a
1094      * <code>LeaseRenewalManager</code> object. The constructor throws an
1095      * <code>IOException</code> because construction of a
1096      * <code>ServiceDiscoveryManager</code> may initiate the multicast discovery
1097      * process, a process that can throw an <code>IOException</code>.
1098      *
1099      * @param discoveryMgr the <code>DiscoveryManagement</code> implementation
1100      * through which notifications that indicate a lookup service has been
1101      * discovered or discarded will be received. If the value of the argument is
1102      * <code>null</code>, then an instance of the
1103      * <code>LookupDiscoveryManager</code> utility class will be constructed to
1104      * listen for events announcing the discovery of only those lookup services
1105      * that are members of the public group.
1106      *
1107      * @param leaseMgr the <code>LeaseRenewalManager</code> to use. A value of
1108      * <code>null</code> may be passed as the <code>LeaseRenewalManager</code>
1109      * argument. If the value of the argument is <code>null</code>, an instance
1110      * of the <code>LeaseRenewalManager</code> class will be created, initially
1111      * managing no <code>Lease</code> objects.
1112      *
1113      * @throws IOException because construction of a
1114      * <code>ServiceDiscoveryManager</code> may initiate the multicast discovery
1115      * process which can throw an <code>IOException</code>.
1116      *
1117      * @see net.jini.discovery.DiscoveryManagement
1118      * @see net.jini.core.event.RemoteEventListener
1119      * @see net.jini.core.lookup.ServiceRegistrar
1120      */
1121     public ServiceDiscoveryManager(DiscoveryManagement discoveryMgr,
1122             LeaseRenewalManager leaseMgr)
1123             throws IOException {
1124 
1125         this(initial(discoveryMgr, leaseMgr, EmptyConfiguration.INSTANCE));
1126 
1127     }//end constructor
1128 
1129     /**
1130      * Constructs an instance of this class, which is configured using the items
1131      * retrieved through the given <code>Configuration</code>, that will, on
1132      * behalf of the entity that constructs this class, discover and manage a
1133      * set of lookup services, as well as discover and manage sets of services
1134      * registered with those lookup services. Through the parameters input to
1135      * this constructor, the client of this utility indicates which lookup
1136      * services to discover and manage, and how it wants the utility
1137      * additionally configured.
1138      * <p>
1139      * For a more details, refer to the description of the alternate constructor
1140      * of this class.
1141      * <p>
1142      * This constructor takes three arguments: an object that implements the
1143      * <code>DiscoveryManagement</code> interface, a reference to an instance of
1144      * the <code>LeaseRenewalManager</code> class, and a
1145      * <code>Configuration</code> object. The constructor throws an
1146      * <code>IOException</code> because construction of a
1147      * <code>ServiceDiscoveryManager</code> may initiate the multicast discovery
1148      * process, a process that can throw an <code>IOException</code>. The
1149      * constructor also throws a <code>ConfigurationException</code> when an
1150      * exception occurs while retrieving an item from the given
1151      * <code>Configuration</code>
1152      *
1153      * @param discoveryMgr the <code>DiscoveryManagement</code> implementation
1154      * through which notifications that indicate a lookup service has been
1155      * discovered or discarded will be received. If the value of the argument is
1156      * <code>null</code>, then an instance of the
1157      * <code>LookupDiscoveryManager</code> utility class will be constructed to
1158      * listen for events announcing the discovery of only those lookup services
1159      * that are members of the public group.
1160      *
1161      * @param leaseMgr the <code>LeaseRenewalManager</code> to use. A value of
1162      * <code>null</code> may be passed as the <code>LeaseRenewalManager</code>
1163      * argument. If the value of the argument is <code>null</code>, an instance
1164      * of the <code>LeaseRenewalManager</code> class will be created, initially
1165      * managing no <code>Lease</code> objects.
1166      * @param config the <code>Configuration</code>
1167      *
1168      * @throws IOException because construction of a
1169      * <code>ServiceDiscoveryManager</code> may initiate the multicast discovery
1170      * process which can throw an <code>IOException</code>.
1171      *
1172      * @throws net.jini.config.ConfigurationException indicates an exception
1173      * occurred while retrieving an item from the given
1174      * <code>Configuration</code>
1175      *
1176      * @throws java.lang.NullPointerException if <code>null</code> is input for
1177      * the configuration
1178      *
1179      * @see net.jini.discovery.DiscoveryManagement
1180      * @see net.jini.core.event.RemoteEventListener
1181      * @see net.jini.core.lookup.ServiceRegistrar
1182      * @see net.jini.config.Configuration
1183      * @see net.jini.config.ConfigurationException
1184      */
1185     public ServiceDiscoveryManager(DiscoveryManagement discoveryMgr,
1186             LeaseRenewalManager leaseMgr,
1187             Configuration config)
1188             throws IOException,
1189             ConfigurationException {
1190         this(init(discoveryMgr, leaseMgr, config));
1191     }//end constructor
1192 
1193     private ServiceDiscoveryManager(Initializer init) {
1194         // Key's added only if absent.
1195         this.proxyRegSet = new HashSet<ProxyReg>();
1196         ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
1197         proxyRegSetRead = rwl.readLock();
1198         proxyRegSetWrite = rwl.writeLock();
1199         this.caches = new ArrayList<LookupCache>(32);
1200         ReentrantReadWriteLock rwl2 = new ReentrantReadWriteLock();
1201         cachesWrite = rwl2.writeLock();
1202         cachesRead = rwl2.readLock();
1203         thisConfig = init.thisConfig;
1204         registrarPreparer = init.registrarPreparer;
1205         eventLeasePreparer = init.eventLeasePreparer;
1206 	bootstrapProxyPreparer = init.bootstrapProxyPreparer;
1207 	useInsecureLookup = init.useInsecureLookup;
1208         leaseRenewalMgr = init.leaseRenewalMgr;
1209         discardWait = init.discardWait;
1210         discMgr = init.discMgr;
1211         discMgrInternal = init.discMgrInternal;
1212         discMgrListener = new DiscMgrListener();
1213         discMgr.addDiscoveryListener(discMgrListener);
1214         terminator = new LookupCacheTerminator();
1215         terminatorThread = new Thread(terminator, "SDM lookup cache terminator");
1216         terminatorThread.setDaemon(false);
1217     }
1218 
1219     /**
1220      * Returns array of ServiceRegistrar created from the proxyRegSet
1221      */
1222     private ServiceRegistrar[] buildServiceRegistrar() {
1223         List<ServiceRegistrar> proxys = new LinkedList<ServiceRegistrar>();
1224         proxyRegSetRead.lock();
1225         try {
1226             Iterator<ProxyReg> iter = proxyRegSet.iterator();
1227             while (iter.hasNext()) {
1228                 ProxyReg reg = iter.next();
1229                 proxys.add(reg.getProxy());
1230             }//end loop
1231         } finally {
1232             proxyRegSetRead.unlock();
1233         }
1234         return proxys.toArray(new ServiceRegistrar[proxys.size()]);
1235     }//end buildServiceRegistrar
1236 
1237     /**
1238      * Queries each available lookup service in the set of lookup services
1239      * managed by the <code>ServiceDiscoveryManager</code> (the <i>managed
1240      * set</i>) for a service reference that matches criteria defined by the
1241      * entity that invokes this method. The semantics of this method are similar
1242      * to the semantics of the <code>lookup</code> method provided by the
1243      * <code>ServiceRegistrar</code> interface; employing the same
1244      * template-matching scheme. Additionally, this method allows any entity to
1245      * supply an object referred to as a <i>filter</i>. Such an object is a
1246      * non-remote object that defines additional matching criteria that the
1247      * <code>ServiceDiscoveryManager</code> applies when searching for the
1248      * entity's services of interest. This filtering facility is particularly
1249      * useful to entities that wish to extend the capabilities of standard
1250      * template-matching.
1251      * <p>
1252      * Entities typically employ this method when they need infrequent access to
1253      * services, and when the cost of making remote queries is outweighed by the
1254      * overhead of maintaining a local cache (for example, because of resource
1255      * limitations).
1256      * <p>
1257      * This version of <code>lookup</code> returns a <i>single</i> instance of
1258      * <code>ServiceItem</code> corresponding to one of possibly many service
1259      * references that satisfy the matching criteria. If multiple services
1260      * matching the input criteria happen to exist, it is arbitrary as to which
1261      * reference is actually returned. It is for this reason that entities that
1262      * invoke this method typically care only that <i>a</i>
1263      * service is returned, not <i>which</i> service.
1264      * <p>
1265      * Note that, unlike other versions of <code>lookup</code> provided by the
1266      * <code>ServiceDiscoveryManager</code>, this version does not
1267      * <i>block</i>. That is, this version will return immediately upon failure
1268      * (or success) to find a service matching the input criteria.
1269      *
1270      * It is important to understand this characteristic because there is a
1271      * common usage scenario that can cause confusion when this version of
1272      * <code>lookup</code> is used but fails to discover the expected service of
1273      * interest. Suppose an entity creates a service discovery manager and then
1274      * immediately calls this version of <code>lookup</code>, which simply
1275      * queries the currently discovered lookup services for the service of
1276      * interest. If the discovery manager employed by the service discovery
1277      * manager has not yet disovered any lookup services (thus, there are no
1278      * lookup services to query) the method will immediately return a value of
1279      * <code>null</code>. This can be confusing when one verifies that such a
1280      * service of interest has indeed been started and registered with the
1281      * existing lookup service(s). To address this issue, one of the blocking
1282      * versions of <code>lookup</code> could be used instead of this version, or
1283      * the entity could simply wait until the discovery manager has been given
1284      * enough time to complete its own (lookup) discovery processing.
1285      *
1286      * @param tmpl an instance of <code>ServiceTemplate</code> corresponding to
1287      * the object to use for template-matching when searching for desired
1288      * services. If <code>null</code> is input to this parameter, this method
1289      * will use a <i>wildcarded</i>
1290      * template (will match all services) when performing template-matching.
1291      * Note that the effects of modifying contents of this parameter before this
1292      * method returns are unpredictable and undefined.
1293      * @param filter an instance of <code>ServiceItemFilter</code> containing
1294      * matching criteria that should be applied in addition to the
1295      * template-matching employed when searching for desired services. If
1296      * <code>null</code> is input to this parameter, then only template-matching
1297      * will be employed to find the desired services.
1298      *
1299      * @return a single instance of <code>ServiceItem</code> corresponding to a
1300      * reference to a service that matches the criteria represented in the input
1301      * parameters; or <code>null</code> if no matching service can be found.
1302      * Note that if multiple services matching the input criteria exist, it is
1303      * arbitrary as to which reference is returned.
1304      *
1305      * @see net.jini.core.lookup.ServiceRegistrar#lookup
1306      * @see net.jini.core.lookup.ServiceTemplate
1307      * @see net.jini.lookup.ServiceItemFilter
1308      */
1309     public ServiceItem lookup(ServiceTemplate tmpl, ServiceItemFilter filter) {
1310 	checkTerminated();
1311         ServiceRegistrar[] proxys = buildServiceRegistrar();
1312         int len = proxys.length;
1313         if (len == 0) {
1314             return null;
1315         }
1316         int rand = random.nextInt(Integer.MAX_VALUE) % len;
1317         for (int i = 0; i < len; i++) {
1318             ServiceRegistrar proxy = proxys[(i + rand) % len];
1319             ServiceItem sItem = null;
1320             try {
1321                 int maxMatches = ((filter != null) ? Integer.MAX_VALUE : 1);
1322 		Object [] matches;
1323 		if (useInsecureLookup()){
1324 		    ServiceMatches sm = proxy.lookup(tmpl, maxMatches);
1325 		    matches = sm.items;
1326 		} else {
1327 		    matches = ((SafeServiceRegistrar)proxy).lookUp(tmpl, maxMatches);
1328 		}
1329 		if (matches == null) continue;
1330                 sItem = getMatchedServiceItem(matches, filter);
1331             } catch (Exception e) {
1332                 if (logger.isLoggable(Level.INFO))
1333                     log(Level.INFO,
1334                         "Exception occurred during query, discarding proxy",
1335                         e);
1336                 discard(proxy);
1337             }
1338             if (sItem != null) {
1339                 return sItem; //Don't need to clone
1340             }
1341         }//end loop
1342         return null;
1343     }//end lookup
1344     
1345     /**
1346      * Queries each available lookup service in the managed set for a service
1347      * that matches the input criteria. The semantics of this method are similar
1348      * to the semantics of the <code>lookup</code> method provided by the
1349      * <code>ServiceRegistrar</code> interface; employing the same
1350      * template-matching scheme. Additionally, this method allows any entity to
1351      * supply an object referred to as a <i>filter</i>. Such an object is a
1352      * non-remote object that defines additional matching criteria that the
1353      * <code>ServiceDiscoveryManager</code> applies when searching for the
1354      * entity's services of interest. This filtering facility is particularly
1355      * useful to entities that wish to extend the capabilities of standard
1356      * template-matching.
1357      * <p>
1358      * This version of <code>lookup</code> returns a <i>single</i> instance of
1359      * <code>ServiceItem</code> corresponding to one of possibly many service
1360      * references that satisfy the matching criteria. If multiple services
1361      * matching the input criteria happen to exist, it is arbitrary as to which
1362      * reference is actually returned. It is for this reason that entities that
1363      * invoke this method typically care only that <i>a</i>
1364      * service is returned, not <i>which</i> service.
1365      * <p>
1366      * Note that this version of <code>lookup</code> provides a
1367      * <i>blocking</i> feature that is controlled through the
1368      * <code>waitDur</code> parameter. That is, this version will not return
1369      * until either a service that matches the input criteria has been found, or
1370      * the amount of time contained in the <code>waitDur</code> parameter has
1371      * passed. If, while waiting for the service of interest to be found, the
1372      * entity decides that it no longer wishes to wait the entire period for
1373      * this method to return, the entity may interrupt this method by invoking
1374      * the interrupt method from the <code>Thread</code> class. The intent of
1375      * this mechanism is to allow the entity to interrupt this method in the
1376      * same way it would a sleeping thread.
1377      * <p>
1378      * Entities typically employ this method when they need infrequent access to
1379      * services, are willing (or forced) to wait for those services to be found,
1380      * and consider the cost of making remote queries for those services is
1381      * outweighed by the overhead of maintaining a local cache (for example,
1382      * because of resource limitations).
1383      *
1384      * @param tmpl an instance of <code>ServiceTemplate</code> corresponding to
1385      * the object to use for template-matching when searching for desired
1386      * services. If <code>null</code> is input to this parameter, this method
1387      * will use a <i>wildcarded</i>
1388      * template (will match all services) when performing template-matching.
1389      * Note that the effects of modifying contents of this parameter before this
1390      * method returns are unpredictable and undefined.
1391      * @param filter an instance of <code>ServiceItemFilter</code> containing
1392      * matching criteria that should be applied in addition to the
1393      * template-matching employed when searching for desired services. If
1394      * <code>null</code> is input to this parameter, then only template-matching
1395      * will be employed to find the desired services.
1396      * @param waitDur the amount of time (in milliseconds) to wait before ending
1397      * the "search" and returning <code>null</code>. If a non-positive value is
1398      * input to this parameter, then this method will not wait; it will simply
1399      * query the available lookup services and return a matching service
1400      * reference or <code>null</code>.
1401      *
1402      * @return a single instance of <code>ServiceItem</code> corresponding to a
1403      * reference to a service that matches the criteria represented in the input
1404      * parameters; or <code>null</code> if no matching service can be found.
1405      * Note that if multiple services matching the input criteria exist, it is
1406      * arbitrary as to which reference is returned.
1407      *
1408      * @throws java.lang.InterruptedException this exception occurs when the
1409      * entity interrupts this method by invoking the interrupt method from the
1410      * <code>Thread</code> class.
1411      *
1412      * @throws java.rmi.RemoteException typically, this exception occurs when a
1413      * RemoteException occurs either as a result of an attempt to export a
1414      * remote listener, or an attempt to register with the event mechanism of a
1415      * lookup service.
1416      *
1417      * @see net.jini.core.lookup.ServiceRegistrar#lookup
1418      * @see net.jini.core.lookup.ServiceTemplate
1419      * @see net.jini.lookup.ServiceItemFilter
1420      * @see java.lang.Thread
1421      */
1422     public ServiceItem lookup(ServiceTemplate tmpl,
1423             ServiceItemFilter filter,
1424             long waitDur) throws InterruptedException,
1425             RemoteException {
1426         /* First query each lookup for the desired service */
1427         ServiceItem sm = lookup(tmpl, filter);//checkTerminated() is done here
1428         if (sm != null) {
1429             return sm;
1430         }
1431         /* If the desired service is not in any of the lookups, wait for it. */
1432         ServiceDiscoveryListener cacheListener
1433                 = new ServiceDiscoveryListenerImpl();
1434         LookupCacheImpl cache = null;
1435         try {
1436             /* The cache must be created inside the listener sync block,
1437              * otherwise a race condition can occur. This is because the
1438              * creation of a cache results in event registration which
1439              * will ultimately result in the invocation of the serviceAdded()
1440              * method in the cache's listener, and the interruption of any
1441              * objects waiting on the cache's listener. If the notifications
1442              * happen to occur before commencing the wait on the listener
1443              * object (see below), then the wait will never be interrupted
1444              * because the interrupts were sent before the wait() method
1445              * was invoked. Synchronizing on the listener and the listener's
1446              * serviceAdded() method, and creating the cache only after the
1447              * lock has been acquired, together will prevent this situation
1448              * since event registration cannot occur until the cache is
1449              * created, and the lock that allows entry into the serviceAdded()
1450              * method (which is invoked once the events do arrive) is not
1451              * released until the wait() method is invoked .
1452              */
1453             synchronized (cacheListener) {
1454                 cache = createLookupCache(tmpl, filter, cacheListener, waitDur);
1455                 long duration = cache.getLeaseDuration();
1456                 while (duration > 0) {
1457                     cacheListener.wait(duration);
1458                     sm = cache.lookup(null);
1459                     if (sm != null) {
1460                         return sm;
1461                     }
1462                     duration = cache.getLeaseDuration();
1463                 }//end loop
1464             }//end sync(cacheListener)
1465             return null; // Make it clear we're returning null.
1466         } finally {
1467             if (cache != null) {
1468                 terminator.terminate(cache);
1469             }
1470         }
1471     }//end lookup
1472 
1473     /**
1474      * The <code>createLookupCache</code> method allows the client-like entity
1475      * to request that the <code>ServiceDiscoveryManager</code> create a new
1476      * managed set (or cache) and populate it with services, which match
1477      * criteria defined by the entity, and whose references are registered with
1478      * one or more of the lookup services the entity has targeted for discovery.
1479      * <p>
1480      * This method returns an object of type <code>LookupCache</code>. Through
1481      * this return value, the entity can query the cache for services of
1482      * interest, manage the cache's event mechanism for service discoveries, or
1483      * terminate the cache.
1484      * <p>
1485      * An entity typically uses the object returned by this method to provide
1486      * local storage of, and access to, references to services that it is
1487      * interested in using. Entities needing frequent access to numerous
1488      * services will find the object returned by this method quite useful
1489      * because acquisition of those service references is provided through local
1490      * method invocations. Additionally, because the object returned by this
1491      * method provides an event mechanism, it is also useful to entities wishing
1492      * to simply monitor, in an event-driven manner, the state changes that
1493      * occur in the services of interest.
1494      * <p>
1495      * Although not required, a common usage pattern for entities that wish to
1496      * use the <code>LookupCache</code> class to store and manage "discovered"
1497      * services is to create a separate cache for each service type of interest.
1498      *
1499      * @param tmpl template to match. It uses template-matching semantics to
1500      * identify the service(s) to acquire from lookup services in the managed
1501      * set. If this value is <code>null</code>, it is the equivalent of passing
1502      * a <code>ServiceTemplate</code> constructed with all <code>null</code>
1503      * arguments (all wildcards).
1504      * @param filter used to apply additional matching criteria to any
1505      * <code>ServiceItem</code> found through template-matching. If this value
1506      * is <code>null</code>, no additional filtering will be applied beyond the
1507      * template-matching.
1508      * @param listener object that will receive notifications when services
1509      * matching the input criteria are discovered for the first time, or have
1510      * encountered a state change such as removal from all lookup services or
1511      * attribute set changes. If this value is <code>null</code>, the cache
1512      * resulting from that invocation will send no such notifications.
1513      *
1514      * @return LookupCache used to query the cache for services of interest,
1515      * manage the cache's event mechanism for service discoveries, or terminate
1516      * the cache.
1517      *
1518      * @throws java.rmi.RemoteException typically, this exception occurs when a
1519      * RemoteException occurs as a result of an attempt to export the remote
1520      * listener that receives service events from the lookup services in the
1521      * managed set.
1522      *
1523      * @see net.jini.lookup.ServiceItemFilter
1524      */
1525     public LookupCache createLookupCache(ServiceTemplate tmpl,
1526             ServiceItemFilter filter,
1527             ServiceDiscoveryListener listener)
1528             throws RemoteException {
1529         checkTerminated();
1530         return createLookupCache(tmpl, filter, listener, Long.MAX_VALUE);
1531     }//end createLookupCache
1532 
1533     /**
1534      * The <code>getDiscoveryManager</code> method will return an object that
1535      * implements the <code>DiscoveryManagement</code> interface. The object
1536      * returned by this method provides the <code>ServiceDiscoveryManager</code>
1537      * with the ability to set discovery listeners and to discard previously
1538      * discovered lookup services when they are found to be unavailable.
1539      *
1540      * @return DiscoveryManagement implementation
1541      * @see net.jini.discovery.DiscoveryManagement
1542      */
1543     public DiscoveryManagement getDiscoveryManager() {
1544         checkTerminated();
1545         return discMgr;
1546     }//end getDiscoveryManager
1547 
1548     /**
1549      * The <code>getLeaseRenewalManager</code> method will return an instance of
1550      * the <code>LeaseRenewalManager</code> class. The object returned by this
1551      * method manages the leases requested and held by the
1552      * <code>ServiceDiscoveryManager</code>. In general, these leases correspond
1553      * to the registrations made by the <code>ServiceDiscoveryManager</code>
1554      * with the event mechanism of each lookup service in the managed set.
1555      *
1556      * @return LeaseRenewalManager for this instance of the
1557      * <code>ServiceDiscoveryManager</code>.
1558      * @see net.jini.lease.LeaseRenewalManager
1559      */
1560     public LeaseRenewalManager getLeaseRenewalManager() {
1561         checkTerminated();
1562         return leaseRenewalMgr;
1563     }//end getLeaseRenewalManager
1564 
1565     /**
1566      * The <code>terminate</code> method performs cleanup duties related to the
1567      * termination of the event mechanism for lookup service discovery, the
1568      * event mechanism for service discovery, and the cache management duties of
1569      * the <code>ServiceDiscoveryManager</code>.
1570      * <p>
1571      * For each instance of <code>LookupCache</code> created and managed by the
1572      * <code>ServiceDiscoveryManager</code>, the <code>terminate</code> method
1573      * will do the following:
1574      * <ul>
1575      * <li>Either remove all listener objects registered for receipt of
1576      * <code>DiscoveryEvent</code> objects or, if the discovery manager employed
1577      * by the <code>ServiceDiscoveryManager</code> was created by the
1578      * <code>ServiceDiscoveryManager</code> itself, terminate all discovery
1579      * processing being performed by that manager object on behalf of the
1580      * entity.
1581      * <p>
1582      * <li>Cancel all event leases granted by each lookup service in the managed
1583      * set of lookup services.
1584      * <p>
1585      * <li>Un-export all remote listener objects registered with each lookup
1586      * service in the managed set.
1587      * <p>
1588      * <li>Terminate all threads involved in the process of retrieving and
1589      * storing references to discovered services of interest.
1590      * </ul>
1591      * Calling any method after the termination will result in an
1592      * <code>IllegalStateException</code>.
1593      *
1594      * @see net.jini.lookup.LookupCache
1595      * @see net.jini.discovery.DiscoveryEvent
1596      */
1597     public void terminate() {
1598         synchronized (this) {
1599             if (bTerminated) {
1600                 return;//allow for multiple terminations
1601             }
1602             bTerminated = true;
1603             /* Terminate lookup service discovery processing */
1604             discMgr.removeDiscoveryListener(discMgrListener);
1605             if (discMgrInternal) {
1606                 discMgr.terminate();
1607             }
1608         }//end sync
1609         terminatorThread.interrupt();
1610         /* Terminate all caches: cancel event leases, un-export listeners */
1611         List<LookupCache> terminate;
1612         cachesRead.lock();
1613         try{
1614             terminate = new ArrayList<LookupCache>(caches);
1615         } finally {
1616             cachesRead.unlock();
1617         }
1618         Iterator iter = terminate.iterator();
1619         while (iter.hasNext()) {
1620             LookupCacheImpl cache = (LookupCacheImpl) iter.next();
1621             cache.terminate();
1622         }//end loop
1623         leaseRenewalMgr.close();
1624     }//end terminate
1625 
1626     /**
1627      * Queries each available lookup service in the managed set for service(s)
1628      * that match the input criteria. The semantics of this method are similar
1629      * to the semantics of the <code>lookup</code> method provided by the
1630      * <code>ServiceRegistrar</code> interface; employing the same
1631      * template-matching scheme. Additionally, this method allows any entity to
1632      * supply an object referred to as a <i>filter</i>. Such an object is a
1633      * non-remote object that defines additional matching criteria that the
1634      * <code>ServiceDiscoveryManager</code> applies when searching for the
1635      * entity's services of interest. This filtering facility is particularly
1636      * useful to entities that wish to extend the capabilities of standard
1637      * template-matching.
1638      * <p>
1639      * Entities typically employ this method when they need infrequent access to
1640      * multiple instances of services, and when the cost of making remote
1641      * queries is outweighed by the overhead of maintaining a local cache (for
1642      * example, because of resource limitations).
1643      * <p>
1644      * This version of <code>lookup</code> returns an <i>array</i> of instances
1645      * of <code>ServiceItem</code> in which each element corresponds to a
1646      * service reference that satisfies the matching criteria. The number of
1647      * elements in the returned set will be no greater than the value of the
1648      * <code>maxMatches</code> parameter, but may be less.
1649      * <p>
1650      * Note that this version of <code>lookup</code> does not provide a
1651      * <i>blocking</i> feature. That is, this version will return immediately
1652      * with whatever number of service references it can find, up to the number
1653      * indicated in the <code>maxMatches</code> parameter. If no services
1654      * matching the input criteria can be found on the first attempt, an empty
1655      * array is returned.
1656      *
1657      * It is important to understand this characteristic because there is a
1658      * common usage scenario that can cause confusion when this version of
1659      * <code>lookup</code> is used but fails to discover any instances of the
1660      * expected service of interest. Suppose an entity creates a service
1661      * discovery manager and then immediately calls this version of
1662      * <code>lookup</code>, which simply queries the currently discovered lookup
1663      * services for the service of interest. If the discovery manager employed
1664      * by the service discovery manager has not yet discovered any lookup
1665      * services (thus, there are no lookup services to query) the method will
1666      * immediately return an empty array. This can be confusing when one
1667      * verifies that instance(s) of such a service of interest have indeed been
1668      * started and registered with the existing lookup service(s). To address
1669      * this issue, one of the blocking versions of <code>lookup</code> could be
1670      * used instead of this version, or the entity could simply wait until the
1671      * discovery manager has been given enough time to complete its own (lookup)
1672      * discovery processing.
1673      *
1674      * @param tmpl an instance of <code>ServiceTemplate</code> corresponding to
1675      * the object to use for template-matching when searching for desired
1676      * services. If <code>null</code> is input to this parameter, this method
1677      * will use a <i>wildcarded</i> template (will match all services) when
1678      * performing template-matching. Note that the effects of modifying contents
1679      * of this parameter before this method returns are unpredictable and
1680      * undefined.
1681      * @param maxMatches this method will return no more than this number of
1682      * service references
1683      * @param filter an instance of <code>ServiceItemFilter</code> containing
1684      * matching criteria that should be applied in addition to the
1685      * template-matching employed when searching for desired services. If
1686      * <code>null</code> is input to this parameter, then only template-matching
1687      * will be employed to find the desired services.
1688      *
1689      * @return an array of instances of <code>ServiceItem</code> where each
1690      * element corresponds to a reference to a service that matches the criteria
1691      * represented in the input parameters; or an empty array if no matching
1692      * service can be found.
1693      *
1694      * @see net.jini.core.lookup.ServiceRegistrar#lookup
1695      * @see net.jini.core.lookup.ServiceTemplate
1696      * @see net.jini.lookup.ServiceItemFilter
1697      */
1698     public ServiceItem[] lookup(ServiceTemplate tmpl,
1699             int maxMatches,
1700             ServiceItemFilter filter) 
1701     {
1702         checkTerminated();
1703         if (maxMatches < 1) {
1704             throw new IllegalArgumentException("maxMatches must be > 0");
1705         }
1706         /* retrieve the lookup service(s) to query for matching service(s) */
1707         ServiceRegistrar[] proxys = buildServiceRegistrar();
1708 
1709         int len = proxys.length;
1710         List<ServiceItem> sItemSet = new ArrayList<ServiceItem>(len);
1711         if (len > 0) {
1712             /* loop thru the set of lookups, randomly selecting each lookup */
1713             int rand = (random.nextInt(Integer.MAX_VALUE)) % len;
1714             for (int i = 0; i < len; i++) {
1715                 int max = maxMatches;
1716                 ServiceRegistrar proxy = proxys[(i + rand) % len];
1717                 try {
1718                     /* If a filter is to be applied (filter != null), then
1719                      * the value of the maxMatches parameter will not
1720                      * suffice when querying the current lookup service.
1721                      * This is because although services returned from a
1722                      * query of the lookup service will match the template,
1723                      * some of those services may get filtered out. Thus,
1724                      * asking for exactly maxMatches may result in fewer
1725                      * matching services than actually are contained in
1726                      * the lookup. Thus, all matching services are
1727                      * requested by passing in "infinity" for the maximum
1728                      * number of matches (Integer.MAX_VALUE).
1729                      */
1730                     if (filter != null) {
1731                         max = Integer.MAX_VALUE;
1732                     }
1733                     /* Query the current lookup for matching service(s). */
1734 		    Object [] result;
1735 		    if (useInsecureLookup()){
1736 			ServiceMatches matches = proxy.lookup(tmpl, max);
1737 			result = matches.items;
1738 		    } else {
1739 			result = ((SafeServiceRegistrar)proxy).lookUp(tmpl, max);
1740 		    }
1741 		    if (result == null) continue;
1742                     int nItems = result.length;
1743                     if (nItems == 0) {
1744                         continue;//no matches, query next lookup
1745                     }                    
1746 		    /* Loop thru the matching services, randomly selecting
1747                      * each service, applying the filter if appropriate,
1748                      * and making sure the service has not already been
1749                      * selected (it may have been returned from a previously
1750                      * queried lookup).
1751                      */
1752 
1753                     int r = (random.nextInt(Integer.MAX_VALUE)) % nItems;
1754                     for (int j = 0; j < nItems; j++) {
1755                         Object obj = result[(j + r) % nItems];
1756                         if (obj == null) continue;
1757 			ServiceItem sItem;
1758 			if (useInsecureLookup()){
1759 			    sItem = (ServiceItem) obj;
1760 			    if (!filterPassed(sItem, filter)) continue;
1761 			} else {
1762 			    sItem = check(obj, filter, bootstrapProxyPreparer);
1763 			    if (sItem == null) continue;
1764 			}                     
1765                         if (!isArrayContainsServiceItem(sItemSet, sItem)) {
1766                             sItemSet.add(sItem);
1767                         }
1768                         if (sItemSet.size() >= maxMatches) {
1769                             return sItemSet.toArray(new ServiceItem[sItemSet.size()]);
1770                         }
1771                     }                
1772 		} catch (Exception e) {
1773                     if (logger.isLoggable(Level.INFO))
1774                         log(Level.INFO,
1775                             "Exception occurred during query, discarding proxy",
1776                             e);
1777                     discard(proxy);
1778                 }
1779             }//end loop(i)
1780         }//endif(len>0)
1781         /* Will reach this return statement only when less than the number
1782          * of services requested have been found in the loop above.
1783          */
1784         return (ServiceItem[]) (sItemSet.toArray(new ServiceItem[sItemSet.size()]));
1785     }//end lookup
1786 
1787     /**
1788      * Queries each available lookup service in the managed set for service(s)
1789      * that match the input criteria. The semantics of this method are similar
1790      * to the semantics of the <code>lookup</code> method provided by the
1791      * <code>ServiceRegistrar</code> interface; employing the same
1792      * template-matching scheme. Additionally, this method allows any entity to
1793      * supply an object referred to as a <i>filter</i>. Such an object is a
1794      * non-remote object that defines additional matching criteria that the
1795      * <code>ServiceDiscoveryManager</code> applies when searching for the
1796      * entity's services of interest. This filtering facility is particularly
1797      * useful to entities that wish to extend the capabilities of standard
1798      * template-matching.
1799      * <p>
1800      * This version of <code>lookup</code> returns an <i>array</i> of instances
1801      * of <code>ServiceItem</code> in which each element corresponds to a
1802      * service reference that satisfies the matching criteria. The number of
1803      * elements in the returned set will be no greater than the value of the
1804      * <code>maxMatches</code> parameter, but may be less.
1805      * <p>
1806      * Note that this version of <code>lookup</code> provides a
1807      * <i>blocking</i> feature that is controlled through the
1808      * <code>waitDur</code> parameter in conjunction with the
1809      * <code>minMatches</code> and the <code>maxMatches</code> parameters. This
1810      * method will not return until one of the following occurs:
1811      * <p>
1812      * <ul>
1813      * <li> the number of matching services found on the first attempt is
1814      * greater than or equal to the value of the <code>minMatches</code>
1815      * parameter, in which case this method returns each of the services found
1816      * up to the value of the <code>maxMatches</code> parameter
1817      * <li> the number of matching services found <i>after</i> the first attempt
1818      * (that is, after the method enters the "wait state") is at least as great
1819      * as the value of the <code>minMatches</code> parameter in which case this
1820      * method returns each of the services found up to the value of the
1821      * <code>maxMatches</code> parameter
1822      * <li> the amount of time that has passed since this method entered the
1823      * wait state exceeds the value of the <code>waitDur</code> parameter, in
1824      * which case this method returns all of the currently discovered services
1825      * </ul>
1826      * <p>
1827      * The purpose of the <code>minMatches</code> parameter is to allow the
1828      * entity to balance its need for multiple matching service references with
1829      * its need to minimize the time spent in the wait state; time that most
1830      * would consider wasted if an acceptable number of matching service
1831      * references were found, but this method continued to wait until the end of
1832      * the designated time period.
1833      * <p>
1834      * If, while waiting for the minimum number of desired services to be
1835      * discovered, the entity decides that it no longer wishes to wait the
1836      * entire period for this method to return, the entity may interrupt this
1837      * method by invoking the interrupt method from the <code>Thread</code>
1838      * class. The intent of this mechanism is to allow the entity to interrupt
1839      * this method in the same way it would a sleeping thread.
1840      * <p>
1841      * Entities typically employ this method when they need infrequent access to
1842      * multiple instances of services, are willing (or forced) to wait for those
1843      * services to be found, and consider the cost of making remote queries for
1844      * those services is outweighed by the overhead of maintaining a local cache
1845      * (for example, because of resource limitations).
1846      *
1847      * @param tmpl an instance of <code>ServiceTemplate</code> corresponding to
1848      * the object to use for template-matching when searching for desired
1849      * services. If <code>null</code> is input to this parameter, this method
1850      * will use a
1851      * <i>wildcarded</i> template (will match all services) when performing
1852      * template-matching. Note that the effects of modifying contents of this
1853      * parameter before this method returns are unpredictable and undefined.
1854      * @param minMatches this method will immediately exit the wait state and
1855      * return once this number of service references is found
1856      * @param maxMatches this method will return no more than this number of
1857      * service references
1858      * @param filter an instance of <code>ServiceItemFilter</code> containing
1859      * matching criteria that should be applied in addition to the
1860      * template-matching employed when searching for desired services. If
1861      * <code>null</code> is input to this parameter, then only template-matching
1862      * will be employed to find the desired services.
1863      * @param waitDur the amount of time (in milliseconds) to wait before ending
1864      * the "search" and returning an empty array. If a non-positive value is
1865      * input to this parameter, then this method will not wait; it will simply
1866      * query the available lookup services and return whatever matching service
1867      * reference(s) it could find, up to <code>maxMatches</code>.
1868      *
1869      * @return an array of instances of <code>ServiceItem</code> where each
1870      * element corresponds to a reference to a service that matches the criteria
1871      * represented in the input parameters; or an empty array if no matching
1872      * service can be found within the time allowed.
1873      *
1874      * @throws java.lang.InterruptedException this exception occurs when the
1875      * entity interrupts this method by invoking the interrupt method from the
1876      * <code>Thread</code> class.
1877      *
1878      * @throws java.lang.IllegalArgumentException this exception occurs when one
1879      * of the following conditions is satisfied:
1880      * <p>
1881      * <ul> <li>the <code>minMatches</code> parameter is non-positive
1882      * <li>the <code>maxMatches</code> parameter is non-positive
1883      * <li>the value of <code>maxMatches</code> is <i>less than</i>
1884      * the value of <code>minMatches</code>
1885      * </ul>
1886      *
1887      * @throws java.rmi.RemoteException typically, this exception occurs when a
1888      * RemoteException occurs either as a result of an attempt to export a
1889      * remote listener, or an attempt to register with the event mechanism of a
1890      * lookup service.
1891      *
1892      * @see net.jini.core.lookup.ServiceRegistrar#lookup
1893      * @see net.jini.core.lookup.ServiceTemplate
1894      * @see net.jini.lookup.ServiceItemFilter
1895      * @see java.lang.Thread
1896      */
1897     public ServiceItem[] lookup(ServiceTemplate tmpl,
1898             int minMatches,
1899             int maxMatches,
1900             ServiceItemFilter filter,
1901                                 long waitDur )  throws InterruptedException,
1902                                                        RemoteException
1903     {
1904         checkTerminated();
1905         if (minMatches < 1) {
1906             throw new IllegalArgumentException("minMatches must be > 0");
1907         }
1908         if (maxMatches < minMatches) {
1909             throw new IllegalArgumentException("maxMatches must be > minMatches");
1910         }
1911 
1912         long delay = System.currentTimeMillis();
1913         ServiceItem[] sItems = lookup(tmpl, maxMatches, filter);
1914         if (sItems.length >= minMatches) {
1915             return sItems;
1916         }
1917         List<ServiceItem> sItemSet = new LinkedList<ServiceItem>();
1918         for (int i = 0, l = sItems.length; i < l; i++) {
1919 	    //if(!sItemSet.contains(sItems[i])
1920             //sItemSet.add(sItems[i]);
1921             if (!isArrayContainsServiceItem(sItemSet, sItems[i])) {
1922                 sItemSet.add(sItems[i]);
1923             }//endif
1924         }//end loop       
1925         ServiceDiscoveryListenerImpl cacheListener
1926                 = new ServiceDiscoveryListenerImpl();
1927         /* The cache must be created inside the listener sync block,
1928          * otherwise a race condition can occur. This is because the
1929          * creation of a cache results in event registration which
1930          * will ultimately result in the invocation of the serviceAdded()
1931          * method in the cache's listener, and the interruption of any
1932          * objects waiting on the cache's listener. If the notifications
1933          * happen to occur before commencing the wait on the listener
1934          * object (see below), then the wait will never be interrupted
1935          * because the interrupts were sent before the wait() method
1936          * was invoked. Synchronizing on the listener and the listener's
1937          * serviceAdded() method, and creating the cache only after the
1938          * lock has been acquired, together will prevent this situation
1939          * since event registration cannot occur until the cache is
1940          * created, and the lock that allows entry into the serviceAdded()
1941          * method (which is invoked once the events do arrive) is not
1942          * released until the wait() method is invoked.
1943          */
1944         LookupCacheImpl cache;
1945         synchronized (cacheListener) { // uncontended lock.
1946             delay = (System.currentTimeMillis() - delay) + 1; // Calculate initial time delay in ms.
1947             cache = createLookupCache(tmpl, filter, cacheListener, waitDur);
1948             long duration = cache.getLeaseDuration();
1949             while (duration > delay) { // Some milli's to spare to ensure we return in reasonable time.
1950                 cacheListener.wait(duration - delay);
1951                 ServiceItem items[] = cacheListener.getServiceItem();
1952                 for (int i = 0, l = items.length; i < l; i++) {
1953                     if (!isArrayContainsServiceItem(sItemSet, items[i])) {
1954                         sItemSet.add(items[i]);
1955                     }//endif
1956                 }//end loop
1957                 if (sItemSet.size() == minMatches) {
1958                     break;
1959                 }
1960                 duration = cache.getLeaseDuration();
1961             }//end loop
1962         }//end sync(cacheListener)
1963         // Termination is now performed by a dedicated thread to ensure
1964         // Remote method call doesn't take too long.
1965         terminator.terminate(cache);
1966         if (sItemSet.size() > maxMatches) {
1967             // Discard some matches
1968             ServiceItem[] r = new ServiceItem[maxMatches];
1969             // Iterator is faster for LinkedList.
1970             Iterator<ServiceItem> it = sItemSet.iterator();
1971             for (int i = 0; it.hasNext() && i < maxMatches; i++) {
1972                 r[i] = it.next();
1973             }
1974             return r;
1975         }
1976         ServiceItem[] r = new ServiceItem[sItemSet.size()];
1977         sItemSet.toArray(r);
1978         return r;
1979     }//end lookup
1980 
1981     /**
1982      * From the given set of ServiceMatches, randomly selects and returns a
1983      * ServiceItem that matches the given filter (if applicable).
1984      */
1985     private ServiceItem getMatchedServiceItem(Object [] sm,
1986 						ServiceItemFilter filter) 
1987     {
1988         int len = sm.length;
1989         if (len > 0) {
1990             int rand = random.nextInt(Integer.MAX_VALUE) % len;
1991             for (int i = 0; i < len; i++) {
1992                 Object sItem = sm[(i + rand) % len];
1993                 if (sItem == null) continue;
1994 		ServiceItem item = null;
1995 		if (useInsecureLookup()){
1996 		    item = (ServiceItem) sItem;
1997 		    if (filterPassed(item, filter)) return item;
1998 		} else {
1999 		    item = check(sItem, filter, bootstrapProxyPreparer);
2000 		    if (item == null) continue;
2001 		    return item;
2002 		}
2003             }//end loop
2004         }//endif
2005         return null;
2006     }//end getMatchedServiceItem
2007     
2008     /**
2009      * 
2010      * @param bootstrapProxy
2011      * @param filter
2012      * @param bootstrapPreparer
2013      * @param serviceProxyPreparer
2014      * @return a new ServiceItem if preparation and filter are successful, or
2015      * null.
2016      */
2017     private ServiceItem check(Object bootstrapProxy, ServiceItemFilter filter,
2018 	    ProxyPreparer bootstrapPreparer) 
2019     {
2020 	try {
2021 	    if (!(bootstrapProxy instanceof ServiceAttributesAccessor) &&
2022 		    !(bootstrapProxy instanceof ServiceIDAccessor) &&
2023 		    !(bootstrapProxy instanceof ServiceProxyAccessor)) return null;
2024 	    // The bootstrap proxy preparer can authenticate, dynamically
2025 	    // grant permission to download and deserialize the service proxy.
2026 	    // The service proxy can be trusted, however no constraints have
2027 	    // been applied to the service proxy yet.
2028 	    Object preparedProxy = bootstrapPreparer.prepareProxy(bootstrapProxy);
2029 	    // getServiceAttributes may try to download code, the bootstrapPreparer
2030 	    // should have authenticated and authorised any code downloads.
2031 	    Entry[] serviceAttributes =
2032 		    ((ServiceAttributesAccessor) preparedProxy).getServiceAttributes();
2033 	    ServiceID serviceID = ((ServiceIDAccessor) preparedProxy).serviceID();
2034 	    ServiceItem item = new ServiceItem(serviceID, bootstrapProxy, serviceAttributes);
2035 	    try {
2036 		if (filter == null){ // No local filter, retrieve service proxy.
2037 		    item.service = 
2038 			((ServiceProxyAccessor) preparedProxy).getServiceProxy();
2039 		    return item;
2040 		}
2041 		// The ServiceItemFilter should mutate the ServiceItem.service
2042 		// field after preparing the proxy and retrieving the 
2043 		// service proxy using ServiceProxyAccessor.
2044 		if (filter.check(item)) return item;
2045 	    } catch (SecurityException ex) {
2046                 if (logger.isLoggable(Level.FINE))
2047                     log(Level.FINE, 
2048 		    "Exception thrown while filtering ServiceItem containing bootstrap proxy, downloading service proxy and trying again, suggest rewriting your filter", ex);
2049 		// If ClassCastException, then filter has attempted to cast the
2050 		// bootstrap proxy to the service type, it is likely to be 
2051 		// an older filter implementation that doesn't know about
2052 		// ServiceProxyAccessor.
2053 		// If SecurityException, then proxy preparation failed, which
2054 		// is probably due to the filter not expecting a bootstrap
2055 		// proxy and attempting to apply method constraints.
2056 		// SOLUTION: Download service proxy and retry filter.
2057 		item.service = 
2058 			((ServiceProxyAccessor) preparedProxy).getServiceProxy();
2059 		if (filter.check(item)) return item;
2060 	    } catch (ClassCastException ex){
2061 		if (logger.isLoggable(Level.FINE))
2062                     log(Level.FINE, 
2063 		    "Exception thrown while filtering ServiceItem containing bootstrap proxy, downloading service proxy and trying again, suggest rewriting your filter", ex);
2064 		// If ClassCastException, then filter has attempted to cast the
2065 		// bootstrap proxy to the service type, it is likely to be 
2066 		// an older filter implementation that doesn't know about
2067 		// ServiceProxyAccessor.
2068 		// If SecurityException, then proxy preparation failed, which
2069 		// is probably due to the filter not expecting a bootstrap
2070 		// proxy and attempting to apply method constraints.
2071 		// SOLUTION: Download service proxy and retry filter.
2072 		item.service = 
2073 			((ServiceProxyAccessor) preparedProxy).getServiceProxy();
2074 		if (filter.check(item)) return item;
2075 	    }
2076 	    return null;
2077 	} catch (IOException ex) {
2078             if (logger.isLoggable(Level.FINE))
2079                 log(Level.FINE, "IOException thrown while checking bootstrapProxy, filtering and downloading service proxy", ex);
2080 	    return null;
2081 	}
2082     }
2083 
2084     /**
2085      * Creates a LookupCache with specific lease duration.
2086      */
2087     private LookupCacheImpl createLookupCache(ServiceTemplate tmpl,
2088             ServiceItemFilter filter,
2089             ServiceDiscoveryListener listener,
2090             long leaseDuration)
2091             throws RemoteException {
2092         /* Atomic start of terminator */
2093         synchronized (terminatorThread) {
2094             if (!started) {
2095                 terminatorThread.start();
2096             }
2097             started = true;
2098         }
2099         if (tmpl == null) {
2100             tmpl = new ServiceTemplate(null, null, null);
2101         }
2102         LookupCacheImpl cache = new LookupCacheImpl(tmpl, filter, listener, leaseDuration, this, useInsecureLookup());
2103         cache.initCache();
2104         cachesWrite.lock();
2105         try {
2106             caches.add(cache);
2107         } finally {
2108             cachesWrite.unlock();
2109         }
2110         if (logger.isLoggable(Level.FINEST))
2111             log(Level.FINE, "ServiceDiscoveryManager - LookupCache created");
2112         return cache;
2113     }//end createLookupCache
2114     
2115     /**
2116      * LookupCache removes itself once it has been terminated.
2117      * @param cache
2118      * @return true if removed.
2119      */
2120     boolean removeLookupCache(LookupCache cache){
2121         cachesWrite.lock();
2122         try {
2123             return caches.remove(cache);
2124         } finally {
2125             cachesWrite.unlock();
2126         }
2127     }
2128 
2129     /**
2130      * Removes and returns element from proxyRegSet that corresponds to the
2131      * given proxy.
2132      */
2133     ProxyReg removeReg(ServiceRegistrar proxy) {
2134         ProxyReg pReg = new ProxyReg(proxy);
2135         proxyRegSetWrite.lock();
2136         try{
2137             if(proxyRegSet.remove(pReg)) return pReg;
2138             return null;
2139         } finally {
2140             proxyRegSetWrite.unlock();
2141         }
2142     }//end removeReg
2143 
2144     /**
2145      * Convenience method invoked when failure occurs in the cache tasks
2146      * executed in this utility. If the appropriate logging level is enabled,
2147      * this method will log the stack trace of the given <code>Throwable</code>;
2148      * noting the given source class and method, and displaying the given
2149      * message. Additionally, this method will discard the given lookup service
2150      * proxy. Note that if the utility itself has already been terminated, or if
2151      * the cache in which the failure occurred has been terminated, then the
2152      * failure is logged at the HANDLED level, and the lookup service proxy is
2153      * not discarded.
2154      *
2155      * Also, note that if the discovery manager employed by this utility has
2156      * already been terminated, then the attempt to discard the given lookup
2157      * service proxy will result in an <code>IllegalStateException</code>. Since
2158      * this method is called from within the tasks run by this utility, and
2159      * since propagating an <code>IllegalStateException</code> out into the
2160      * ThreadGroup of those tasks is undesirable, this method does not propagate
2161      * <code>IllegalStateException</code>s that occur as a result of an attempt
2162      * to discard a lookup service proxy from the discovery manager.
2163      *
2164      * For more information, refer to Bug 4490358 and 4858211.
2165      */
2166     void fail(Throwable e,
2167             ServiceRegistrar proxy,
2168             String sourceClass,
2169             String sourceMethod,
2170             String msg,
2171             boolean cacheTerminated) {
2172         Level logLevel = Level.INFO;
2173         boolean discardProxy = true;
2174         synchronized (this) {
2175             if (bTerminated || cacheTerminated) {
2176                 logLevel = Levels.HANDLED;
2177                 discardProxy = false;
2178             }//endif
2179         }//end sync(this)
2180         if ((e != null) && (logger.isLoggable(logLevel))) {
2181             logp(logLevel, sourceClass, sourceMethod, msg, e);
2182         }//endif
2183         try {
2184             if (discardProxy) {
2185                 discard(proxy);
2186             }
2187         } catch (IllegalStateException e1) {
2188             if (logger.isLoggable(logLevel)) {
2189                 logp(
2190                     logLevel,
2191                     sourceClass,
2192                     sourceMethod,
2193                     "failure discarding lookup service proxy, "
2194                     + "discovery manager already terminated",
2195                     e1
2196                 );
2197             }//endif
2198         }
2199     }//end fail
2200 
2201     /**
2202      * Discards a ServiceRegistrar through the discovery manager.
2203      */
2204     private void discard(ServiceRegistrar proxy) {
2205         discMgr.discard(proxy);
2206     }//end discard
2207 
2208     /**
2209      * Cancels the given event lease.
2210      */
2211     void cancelLease(Lease lease) {
2212         try {
2213             leaseRenewalMgr.cancel(lease);
2214         } catch (Exception e) {
2215             if (logger.isLoggable(Level.FINER))
2216                 log(Level.FINER,
2217                     "exception occurred while cancelling an event "
2218                     + "registration lease",
2219                     e);
2220         }
2221     }//end cancelLease
2222 
2223     /**
2224      * Registers for events from the lookup service associated with the given
2225      * proxy, and returns both the lease and the event sequence number from the
2226      * event registration wrapped in the locally-defined class,
2227      * <code>EventReg</code>.
2228      *
2229      * This method is called from the <code>RegisterListenerTask</code>. If a
2230      * <code>RemoteException</code> occurs during the event registration
2231      * attempt, this method discards the lookup service and returns
2232      * <code>null</code>.
2233      */
2234     EventReg registerListener(ServiceRegistrar proxy,
2235             ServiceTemplate tmpl,
2236             RemoteEventListener listenerProxy,
2237             long duration) throws RemoteException {
2238         /* Register with the event mechanism of the given lookup service */
2239         EventRegistration e;
2240         int transition = (ServiceRegistrar.TRANSITION_NOMATCH_MATCH
2241                 | ServiceRegistrar.TRANSITION_MATCH_NOMATCH
2242                 | ServiceRegistrar.TRANSITION_MATCH_MATCH);
2243 	if (useInsecureLookup()){
2244 	    e = proxy.notify(tmpl, transition, listenerProxy, null, duration);
2245 	} else {
2246 	    e = ((SafeServiceRegistrar)proxy).notiFy(tmpl, transition, listenerProxy, null, duration);
2247 	}
2248         /* Proxy preparation -
2249          *
2250          * Prepare the proxy to the lease on the event registration just
2251          * returned. Because lease management (renewal and cancellation)
2252          * involves remote calls, lease proxies should be prepared before
2253          * management of the associated leases begins. This allows one to
2254          * verify trust in the lease, and ensures that the appropriate
2255          * constraints are attached to the lease.
2256          */
2257         Lease eventLease = e.getLease();
2258         eventLease = (Lease) eventLeasePreparer.prepareProxy(eventLease);
2259         if (logger.isLoggable(Level.FINEST))
2260             log(Level.FINEST, 
2261                 "ServiceDiscoveryManager - proxy to event registration lease prepared: {0}", 
2262                 new Object []{eventLease}
2263             );
2264         /* Management the lease on the event registration */
2265         leaseRenewalMgr.renewFor(eventLease,
2266                 duration,
2267                 new LeaseListenerImpl(proxy));
2268         /* Wrap source, id, event sequence & lease in EventReg, and return. */
2269         return (new EventReg(e.getSource(),
2270                 e.getID(),
2271                 e.getSequenceNumber(),
2272                 eventLease));
2273     }//end registerListener
2274 
2275     /**
2276      * Throws an IllegalStateException if the current instance of the
2277      * ServiceDiscoveryManager has been terminated.
2278      */
2279     synchronized void checkTerminated() {
2280         if (bTerminated) {
2281             throw new IllegalStateException("service discovery manager was terminated");
2282         }//endif
2283     }//end checkTerminated
2284 
2285     /**
2286      * Determines if the given ServiceItem is an element of the given array.
2287      */
2288     static private boolean isArrayContainsServiceItem(List<ServiceItem> a,
2289             ServiceItem s) {
2290         Iterator<ServiceItem> iter = a.iterator();
2291         while (iter.hasNext()) {
2292             Object o = iter.next();
2293             if (!(o instanceof ServiceItem)) {
2294                 continue;
2295             }
2296             ServiceItem sa = (ServiceItem) o;
2297             if (sa.serviceID.equals(s.serviceID)
2298                     && LookupAttributes.equal(sa.attributeSets, s.attributeSets)
2299                     && (sa.service.equals(s.service))) {
2300                 return true;
2301             }
2302         }//end loop
2303         return false;
2304     }//end isArrayContainsServiceItems
2305 
2306     /**
2307      * Initializer for ServiceDiscoveryManager
2308      */
2309     private static class Initializer {
2310 
2311         Configuration thisConfig;
2312         ProxyPreparer registrarPreparer;
2313         ProxyPreparer eventLeasePreparer;
2314 	ProxyPreparer bootstrapProxyPreparer;
2315         LeaseRenewalManager leaseRenewalMgr;
2316         long discardWait;
2317         DiscoveryManagement discMgr;
2318         boolean discMgrInternal;
2319 	boolean useInsecureLookup;
2320     }
2321 
2322     private static Initializer initial(
2323             DiscoveryManagement discoveryMgr,
2324             LeaseRenewalManager leaseMgr,
2325             Configuration config)
2326             throws IOException {
2327         try {
2328             return init(discoveryMgr, leaseMgr, config);
2329         } catch (ConfigurationException e) {
2330             /* This should never happen */
2331             throw new IOException(e);
2332         }
2333     }
2334 
2335     /* Convenience method that encapsulates the retrieval of the configurable
2336      * items from the given <code>Configuration</code> object.
2337      */
2338     private static Initializer init(DiscoveryManagement discoveryMgr,
2339             LeaseRenewalManager leaseMgr,
2340             Configuration config)
2341             throws IOException, ConfigurationException {
2342         /* Retrieve configuration items if applicable */
2343         if (config == null) {
2344             throw new NullPointerException("config is null");
2345         }
2346         Initializer init = new Initializer();
2347         init.thisConfig = config;
2348         /* Proxy preparers */
2349         init.registrarPreparer = init.thisConfig.getEntry(COMPONENT_NAME,
2350                 "registrarPreparer",
2351                 ProxyPreparer.class,
2352                 new BasicProxyPreparer());
2353         init.eventLeasePreparer = init.thisConfig.getEntry(COMPONENT_NAME,
2354                 "eventLeasePreparer",
2355                 ProxyPreparer.class,
2356                 new BasicProxyPreparer());
2357 	init.bootstrapProxyPreparer = init.thisConfig.getEntry(COMPONENT_NAME,
2358                 "bootstrapPreparer",
2359                 ProxyPreparer.class,
2360                 new BasicProxyPreparer());
2361         /* Lease renewal manager */
2362         init.leaseRenewalMgr = leaseMgr;
2363         if (init.leaseRenewalMgr == null) {
2364             try {
2365                 init.leaseRenewalMgr
2366                         = init.thisConfig.getEntry(COMPONENT_NAME,
2367                                 "leaseManager",
2368                                 LeaseRenewalManager.class);
2369             } catch (NoSuchEntryException e) { /* use default */
2370 
2371                 init.leaseRenewalMgr = new LeaseRenewalManager(init.thisConfig);
2372             }
2373         }//endif
2374         /* Wait value for the "service discard problem". */
2375         init.discardWait = (init.thisConfig.getEntry(COMPONENT_NAME,
2376                 "discardWait",
2377                 long.class,
2378                 600000L));
2379         /* Discovery manager */
2380         init.discMgr = discoveryMgr;
2381         if (init.discMgr == null) {
2382             init.discMgrInternal = true;
2383             try {
2384                 init.discMgr = init.thisConfig.getEntry(COMPONENT_NAME,
2385                         "discoveryManager",
2386                         DiscoveryManagement.class);
2387             } catch (NoSuchEntryException e) { /* use default */
2388 
2389                 init.discMgr = new LookupDiscoveryManager(new String[]{""}, null, null, init.thisConfig);
2390             }
2391         }//endif
2392 	init.useInsecureLookup = (init.thisConfig.getEntry(COMPONENT_NAME,
2393 		"useInsecureLookup",
2394 		Boolean.class,
2395 		Boolean.FALSE));
2396         return init;
2397     }//end init
2398 
2399     /**
2400      * Applies the given <code>filter</code> to the given <code>item</code>, and
2401      * returns <code>true</code> if the <code>filter</code> returns a
2402      * <code>pass</code> value; otherwise, returns <code>false</code>.
2403      * <p>
2404      * Note that as described in the specification of
2405      * <code>ServiceItemFilter</code>, when the <code>item</code> passes the
2406      * <code>filter</code>, the <code>service</code> field of the
2407      * <code>item</code> is replaced with the filtered form of the object
2408      * previously contained in that field. Additionally, if the
2409      * <code>filter</code> returns <code>indefinite</code>, then as specified,
2410      * the <code>service</code> field is replaced with <code>null</code> (in
2411      * which case, this method returns <code>false</code>).
2412      * <p>
2413      * This method is used by the non-blocking version(s) of the
2414      * <code>lookup</code> method of the <code>ServiceDiscoveryManager</code>,
2415      * as well as when second-stage filtering is performed in the
2416      * <code>LookupCache</code>.
2417      */
2418     static boolean filterPassed(ServiceItem item, ServiceItemFilter filter) {
2419         if ((item == null) || (item.service == null)) {
2420             return false;
2421         }
2422         if (filter == null) {
2423             return true;
2424         }
2425         return filter.check(item);
2426     }//end filterPassFail
2427 
2428 }//end class ServiceDiscoveryManager