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) { 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