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">•</font>
221 * <th scope="col" align="left" colspan="2"> <font size="+1">
222 * <code>cacheExecutorService</code></font>
223 *
224 * <tr valign="top"> <td> <th scope="row" align="right">
225 * Type: <td> {@link java.util.concurrent.ExecutorService ExecutorService}
226 *
227 * <tr valign="top"> <td> <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> <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">•</font>
248 * <th scope="col" align="left" colspan="2"> <font size="+1">
249 * <code>discardExecutorService</code></font>
250 *
251 * <tr valign="top"> <td> <th scope="row" align="right">
252 * Type: <td> {@link java.util.concurrent.ScheduledExecutorService ScheduledExecutorService}
253 *
254 * <tr valign="top"> <td> <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> <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">•</font>
276 * <th scope="col" align="left" colspan="2"> <font size="+1">
277 * <code>ServiceEventExecutorService</code></font>
278 *
279 * <tr valign="top"> <td> <th scope="row" align="right">
280 * Type: <td> {@link java.util.concurrent.ExecutorService ExecutorService}
281 *
282 * <tr valign="top"> <td> <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> <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">•</font>
316 * <th scope="col" align="left" colspan="2"> <font size="+1">
317 * <code>discardWait</code></font>
318 *
319 * <tr valign="top"> <td> <th scope="row" align="right">
320 * Type: <td> <code>long</code>
321 *
322 * <tr valign="top"> <td> <th scope="row" align="right">
323 * Default: <td> <code>2*(5*60*1000)</code>
324 *
325 * <tr valign="top"> <td> <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">•</font>
344 * <th scope="col" align="left" colspan="2"> <font size="+1">
345 * <code>discoveryManager</code></font>
346 *
347 * <tr valign="top"> <td> <th scope="row" align="right">
348 * Type: <td> {@link net.jini.discovery.DiscoveryManagement}
349 *
350 * <tr valign="top"> <td> <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> <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">•</font>
375 * <th scope="col" align="left" colspan="2"> <font size="+1">
376 * <code>eventLeasePreparer</code></font>
377 *
378 * <tr valign="top"> <td> <th scope="row" align="right">
379 * Type: <td> {@link net.jini.security.ProxyPreparer}
380 *
381 * <tr valign="top"> <td> <th scope="row" align="right">
382 * Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
383 * </code>
384 *
385 * <tr valign="top"> <td> <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">•</font>
400 * <th scope="col" align="left" colspan="2"> <font size="+1">
401 * <code>eventListenerExporter</code></font>
402 *
403 * <tr valign="top"> <td> <th scope="row" align="right">
404 * Type: <td> {@link net.jini.export.Exporter}
405 *
406 * <tr valign="top"> <td> <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 * new
416 * {@link net.jini.jeri.BasicILFactory}(),<br>
417 *
418 * false, false)</code>
419 *
420 * <tr valign="top"> <td> <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">•</font>
440 * <th scope="col" align="left" colspan="2"> <font size="+1">
441 * <code>leaseManager</code></font>
442 *
443 * <tr valign="top"> <td> <th scope="row" align="right">
444 * Type: <td> {@link net.jini.lease.LeaseRenewalManager}
445 *
446 * <tr valign="top"> <td> <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> <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">•</font>
466 * <th scope="col" align="left" colspan="2"> <font size="+1">
467 * <code>registrarPreparer</code></font>
468 *
469 * <tr valign="top"> <td> <th scope="row" align="right">
470 * Type: <td> {@link net.jini.security.ProxyPreparer}
471 *
472 * <tr valign="top"> <td> <th scope="row" align="right">
473 * Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
474 * </code>
475 *
476 * <tr valign="top"> <td> <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">•</font>
497 * <th scope="col" align="left" colspan="2"> <font size="+1">
498 * <code>bootstrapPreparer</code></font>
499 *
500 * <tr valign="top"> <td> <th scope="row" align="right">
501 * Type: <td> {@link net.jini.security.ProxyPreparer}
502 *
503 * <tr valign="top"> <td> <th scope="row" align="right">
504 * Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
505 * </code>
506 *
507 * <tr valign="top"> <td> <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">•</font>
528 * <th scope="col" align="left" colspan="2"> <font size="+1">
529 * <code>useInsecureLookup</code></font>
530 *
531 * <tr valign="top"> <td> <th scope="row" align="right">
532 * Type: <td> {@link java.lang.Boolean}
533 *
534 * <tr valign="top"> <td> <th scope="row" align="right">
535 * Default: <td> <code>new {@link java.lang.Boolean}("FALSE")
536 * </code>
537 *
538 * <tr valign="top"> <td> <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) {//River-466
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