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.util.ArrayList; 23 import java.util.Iterator; 24 import java.util.LinkedList; 25 import java.util.List; 26 import java.util.concurrent.Callable; 27 import java.util.concurrent.ConcurrentLinkedQueue; 28 import java.util.concurrent.CopyOnWriteArrayList; 29 import java.util.concurrent.ExecutorService; 30 import java.util.concurrent.Future; 31 import java.util.concurrent.LinkedBlockingQueue; 32 import java.util.concurrent.RunnableFuture; 33 import java.util.concurrent.ThreadPoolExecutor; 34 import java.util.concurrent.TimeUnit; 35 import java.util.logging.Level; 36 import java.util.logging.Logger; 37 import net.jini.config.Configuration; 38 import net.jini.config.ConfigurationException; 39 import net.jini.config.EmptyConfiguration; 40 import net.jini.config.NoSuchEntryException; 41 import net.jini.core.entry.CloneableEntry; 42 import net.jini.core.entry.Entry; 43 import net.jini.core.lease.Lease; 44 import net.jini.core.lease.UnknownLeaseException; 45 import net.jini.core.lookup.ServiceID; 46 import net.jini.core.lookup.ServiceItem; 47 import net.jini.core.lookup.ServiceRegistrar; 48 import net.jini.core.lookup.ServiceRegistration; 49 import net.jini.discovery.DiscoveryEvent; 50 import net.jini.discovery.DiscoveryListener; 51 import net.jini.discovery.DiscoveryManagement; 52 import net.jini.discovery.LookupDiscoveryManager; 53 import net.jini.lease.LeaseListener; 54 import net.jini.lease.LeaseRenewalEvent; 55 import net.jini.lease.LeaseRenewalManager; 56 import net.jini.security.BasicProxyPreparer; 57 import net.jini.security.ProxyPreparer; 58 import org.apache.river.constants.ThrowableConstants; 59 import org.apache.river.logging.LogUtil; 60 import org.apache.river.lookup.entry.LookupAttributes; 61 import org.apache.river.thread.DependencyLinker; 62 import org.apache.river.thread.ExtensibleExecutorService; 63 import org.apache.river.thread.ExtensibleExecutorService.RunnableFutureFactory; 64 import org.apache.river.thread.FutureObserver; 65 import org.apache.river.thread.NamedThreadFactory; 66 import org.apache.river.thread.wakeup.RetryTask; 67 import org.apache.river.thread.wakeup.WakeupManager; 68 69 /** 70 * A goal of any well-behaved service is to advertise the facilities and 71 * functions it provides by requesting residency within at least one lookup 72 * service. Making such a request of a lookup service is known as registering 73 * with, or <i>joining</i>, a lookup service. To demonstrate this good 74 * behavior, a service must comply with both the multicast discovery protocol 75 * and the unicast discovery protocol in order to discover the lookup services 76 * it is interested in joining. The service must also comply with the join 77 * protocol to register with the desired lookup services. 78 * <p> 79 * In order for a service to maintain its residency in the lookup services 80 * it has joined, the service must provide for the coordination, systematic 81 * renewal, and overall management of all leases on that residency. In 82 * addition to handling all discovery and join duties, as well as managing 83 * all leases on lookup service residency, the service must also provide 84 * for the coordination and management of any attribute sets with which 85 * it may have registered with the lookup services in which it resides. 86 * <p> 87 * This class performs all of the functions related to discovery, joining, 88 * service lease renewal, and attribute management which is required of a 89 * well-behaved service. Each of these activities is intimately involved 90 * with the maintenance of a service's residency in one or more lookup 91 * services (the service's join state), thus the name <code>JoinManager</code>. 92 * <p> 93 * This class should be employed by services, not clients. The use of this 94 * class in a wide variety of services can help minimize the work resulting 95 * from having to repeatedly implement this required functionality in each 96 * service. Note that this class is not remote. Services that wish to use 97 * this class will create an instance of this class in the service's address 98 * space to manage the entity's join state locally. 99 * 100 * <!-- Implementation Specifics --> 101 * 102 * The following implementation-specific items are discussed below: 103 * <ul><li> <a href="#jmConfigEntries">Configuring JoinManager</a> 104 * <li> <a href="#jmLogging">Logging</a> 105 * </ul> 106 * 107 * <a name="jmConfigEntries"> 108 * <b><font size="+1">Configuring JoinManager</font></b> 109 * </a> 110 * 111 * This implementation of <code>JoinManager</code> supports the following 112 * configuration entries; where each configuration entry name is associated 113 * with the component name <code>net.jini.lookup.JoinManager</code>. Note 114 * that the configuration entries specified here are specific to this 115 * implementation of <code>JoinManager</code>. Unless otherwise stated, each 116 * entry is retrieved from the configuration only once per instance of 117 * this utility, where each such retrieval is performed in the constructor. 118 * 119 * <a name="discoveryManager"></a> 120 * <table summary="Describes the discoveryManager configuration entry" 121 * border="0" cellpadding="2"> 122 * <tr valign="top"> 123 * <th scope="col"> <font size="+1">•</font> 124 * <th scope="col" align="left" colspan="2"> <font size="+1"> 125 * <code>discoveryManager</code></font> 126 * 127 * <tr valign="top"> <td> <th scope="row" align="right"> 128 * Type: <td> {@link net.jini.discovery.DiscoveryManagement} 129 * 130 * <tr valign="top"> <td> <th scope="row" align="right"> 131 * Default: <td> <code> new 132 * {@link net.jini.discovery.LookupDiscoveryManager#LookupDiscoveryManager( 133 * java.lang.String[], 134 * net.jini.core.discovery.LookupLocator[], 135 * net.jini.discovery.DiscoveryListener, 136 * net.jini.config.Configuration) LookupDiscoveryManager}( 137 * new java.lang.String[] {""}, 138 * new {@link net.jini.core.discovery.LookupLocator}[0], 139 * null, config)</code> 140 * 141 * <tr valign="top"> <td> <th scope="row" align="right"> 142 * Description: 143 * <td> The object used to manage the discovery processing 144 * performed by this utility. This entry will be retrieved 145 * from the configuration only if no discovery manager is 146 * specified in the constructor. Note that this object should 147 * not be shared with other components in the application that 148 * employs this utility. 149 * </table> 150 * 151 * <a name="leaseManager"></a> 152 * <table summary="Describes the leaseManager configuration entry" 153 * border="0" cellpadding="2"> 154 * <tr valign="top"> 155 * <th scope="col" > <font size="+1">•</font> 156 * <th scope="col" align="left" colspan="2"> <font size="+1"> 157 * <code>leaseManager</code></font> 158 * 159 * <tr valign="top"> <td> <th scope="row" align="right"> 160 * Type: <td> {@link net.jini.lease.LeaseRenewalManager} 161 * 162 * <tr valign="top"> <td> <th scope="row" align="right"> 163 * Default: <td> <code> new 164 * {@link net.jini.lease.LeaseRenewalManager#LeaseRenewalManager( 165 * net.jini.config.Configuration) LeaseRenewalManager}(config)</code> 166 * 167 * <tr valign="top"> <td> <th scope="row" align="right"> 168 * Description: 169 * <td> The object used to manage each service lease returned 170 * to this utility when the service is registered with the 171 * the various discovered lookup services. This entry will 172 * be retrieved from the configuration only if no lease 173 * renewal manager is specified in the constructor. 174 * </table> 175 * 176 * <a name="maxLeaseDuration"></a> 177 * <table summary="Describes the maxLeaseDuration 178 * configuration entry" border="0" cellpadding="2"> 179 * <tr valign="top"> 180 * <th scope="col" > <font size="+1">•</font> 181 * <th scope="col" align="left" colspan="2"> <font size="+1"> 182 * <code>maxLeaseDuration</code></font> 183 * 184 * <tr valign="top"> <td> <th scope="row" align="right"> 185 * Type: <td> <code>long</code> 186 * 187 * <tr valign="top"> <td> <th scope="row" align="right"> 188 * Default: <td> <code>Lease.FOREVER</code> 189 * 190 * <tr valign="top"> <td> <th scope="row" align="right"> 191 * Description: 192 * <td> The maximum lease duration (in milliseconds) that is requested 193 * from each discovered lookup service on behalf of the service; 194 * both when the lease is initially requested, as well as when 195 * renewal of that lease is requested. Note that as this value is 196 * made smaller, renewal requests will be made more frequently 197 * while the service is up, and lease expiration will occur sooner 198 * when the service goes down. 199 * </table> 200 * 201 * <a name="registrarPreparer"></a> 202 * <table summary="Describes the registrarPreparer configuration entry" 203 * border="0" cellpadding="2"> 204 * <tr valign="top"> 205 * <th scope="col" > <font size="+1">•</font> 206 * <th scope="col" align="left" colspan="2"> <font size="+1"> 207 * <code>registrarPreparer</code></font> 208 * 209 * <tr valign="top"> <td> <th scope="row" align="right"> 210 * Type: <td> {@link net.jini.security.ProxyPreparer} 211 * 212 * <tr valign="top"> <td> <th scope="row" align="right"> 213 * Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}() 214 * </code> 215 * 216 * <tr valign="top"> <td> <th scope="row" align="right"> 217 * Description: 218 * <td> Preparer for the proxies to the lookup services that are 219 * discovered and used by this utility. 220 * <p> 221 * The following methods of the proxy returned by this preparer are 222 * invoked by this utility: 223 * <ul> 224 * <li>{@link net.jini.core.lookup.ServiceRegistrar#register register} 225 * </ul> 226 * </table> 227 * 228 * <a name="registrationPreparer"></a> 229 * <table summary="Describes the registrationPreparer configuration entry" 230 * border="0" cellpadding="2"> 231 * <tr valign="top"> 232 * <th scope="col" > <font size="+1">•</font> 233 * <th scope="col" align="left" colspan="2"> <font size="+1"> 234 * <code>registrationPreparer</code></font> 235 * 236 * <tr valign="top"> <td> <th scope="row" align="right"> 237 * Type: <td> {@link net.jini.security.ProxyPreparer} 238 * 239 * <tr valign="top"> <td> <th scope="row" align="right"> 240 * Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}() 241 * </code> 242 * 243 * <tr valign="top"> <td> <th scope="row" align="right"> 244 * Description: 245 * <td> Preparer for the proxies to the registrations returned to 246 * this utility upon registering the service with each discovered 247 * lookup service. 248 * <p> 249 * The following methods of the proxy returned by this preparer are 250 * invoked by this utility: 251 * <ul> 252 * <li>{@link net.jini.core.lookup.ServiceRegistration#getServiceID 253 * getServiceID} 254 * <li>{@link net.jini.core.lookup.ServiceRegistration#getLease 255 * getLease} 256 * <li>{@link net.jini.core.lookup.ServiceRegistration#addAttributes 257 * addAttributes} 258 * <li>{@link net.jini.core.lookup.ServiceRegistration#modifyAttributes 259 * modifyAttributes} 260 * <li>{@link net.jini.core.lookup.ServiceRegistration#setAttributes 261 * setAttributes} 262 * </ul> 263 * </table> 264 * 265 * <a name="serviceLeasePreparer"></a> 266 * <table summary="Describes the serviceLeasePreparer configuration entry" 267 * border="0" cellpadding="2"> 268 * <tr valign="top"> 269 * <th scope="col" > <font size="+1">•</font> 270 * <th scope="col" align="left" colspan="2"> <font size="+1"> 271 * <code>serviceLeasePreparer</code></font> 272 * 273 * <tr valign="top"> <td> <th scope="row" align="right"> 274 * Type: <td> {@link net.jini.security.ProxyPreparer} 275 * 276 * <tr valign="top"> <td> <th scope="row" align="right"> 277 * Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}() 278 * </code> 279 * 280 * <tr valign="top"> <td> <th scope="row" align="right"> 281 * Description: 282 * <td> Preparer for the leases returned to this utility through 283 * the registrations with each discovered lookup service with 284 * which this utility has registered the service. 285 * <p> 286 * Currently, none of the methods on the service lease returned 287 * by this preparer are invoked by this implementation of the utility. 288 * </table> 289 * 290 * <a name="executorService"></a> 291 * <table summary="Describes the executorService configuration entry" 292 * border="0" cellpadding="2"> 293 * <tr valign="top"> 294 * <th scope="col" > <font size="+1">•</font> 295 * <th scope="col" align="left" colspan="2"> <font size="+1"> 296 * <code>executorService</code></font> 297 * 298 * <tr valign="top"> <td> <th scope="row" align="right"> 299 * Type: <td> {@link java.util.concurrent.ExecutorService ExecutorService} 300 * 301 * <tr valign="top"> <td> <th scope="row" align="right"> 302 * Default: <td> <code>new 303 * {@link java.util.concurrent.ThreadPoolExecutor ThreadPoolExecutor}( 304 * 15, 305 * 15, 306 * 15, 307 * TimeUnit.SECONDS, 308 * new {@link java.util.concurrent.LinkedBlockingQueue LinkedBlockingQueue}(), 309 * new {@link NamedThreadFactory NamedThreadFactory}("JoinManager executor thread", false))</code> 310 * 311 * <tr valign="top"> <td> <th scope="row" align="right"> 312 * Description: 313 * <td> The object that pools and manages the various threads 314 * executed by this utility. This object 315 * should not be shared with other components in the 316 * application that employs this utility. 317 * </table> 318 * 319 * <a name="wakeupManager"></a> 320 * <table summary="Describes the wakeupManager configuration entry" 321 * border="0" cellpadding="2"> 322 * <tr valign="top"> 323 * <th scope="col" > <font size="+1">•</font> 324 * <th scope="col" align="left" colspan="2"> <font size="+1"> 325 * <code>wakeupManager</code></font> 326 * 327 * <tr valign="top"> <td> <th scope="row" align="right"> 328 * Type: <td> {@link org.apache.river.thread.wakeup.WakeupManager} 329 * 330 * <tr valign="top"> <td> <th scope="row" align="right"> 331 * Default: <td> <code>new 332 * {@link org.apache.river.thread.wakeup.WakeupManager#WakeupManager( 333 * org.apache.river.thread.wakeup.WakeupManager.ThreadDesc) 334 * WakeupManager}(new 335 * {@link org.apache.river.thread.wakeup.WakeupManager.ThreadDesc}(null,true))</code> 336 * 337 * <tr valign="top"> <td> <th scope="row" align="right"> 338 * Description: 339 * <td> Object that pools and manages the various tasks that are 340 * initially executed by the object corresponding to the 341 * <a href="#executorService"><code>executorService</code></a> entry 342 * of this component, but which fail during that initial execution. 343 * This object schedules the re-execution of such a failed task - 344 * in the <a href="#executorService"><code>executorService</code></a> 345 * object - at various times in the future, until either the 346 * task succeeds or the task has been executed the maximum 347 * number of allowable times, corresponding to the 348 * <a href="#wakeupRetries"><code>wakeupRetries</code></a> 349 * entry of this component. This object should not be shared 350 * with other components in the application that employs this 351 * utility. 352 * </table> 353 * 354 * <a name="wakeupRetries"></a> 355 * <table summary="Describes the wakeupRetries 356 * configuration entry" border="0" cellpadding="2"> 357 * <tr valign="top"> 358 * <th scope="col" > <font size="+1">•</font> 359 * <th scope="col" align="left" colspan="2"> <font size="+1"> 360 * <code>wakeupRetries</code></font> 361 * 362 * <tr valign="top"> <td> <th scope="row" align="right"> 363 * Type: <td> <code>int</code> 364 * 365 * <tr valign="top"> <td> <th scope="row" align="right"> 366 * Default: <td> <code>6</code> 367 * 368 * <tr valign="top"> <td> <th scope="row" align="right"> 369 * Description: 370 * <td> The maximum number of times a failed task is allowed to 371 * be executed by the object corresponding to the 372 * <a href="#wakeupManager"><code>wakeupManager</code></a> 373 * entry of this component. 374 * </table> 375 * 376 * <a name="jmLogging"> 377 * 378 * <b><font size="+1">Logging</font></b> 379 * 380 * </a> 381 * 382 * This implementation of <code>JoinManager</code> uses the 383 * {@link Logger} named <code>net.jini.lookup.JoinManager</code> 384 * to log information at the following logging levels: <p> 385 * 386 * <table border="1" cellpadding="5" 387 * summary="Describes the information logged by JoinManager, 388 * and the levels at which that information is logged"> 389 * 390 * <caption> 391 * <b><code>net.jini.lookup.JoinManager</code></b> 392 * </caption> 393 * 394 * <tr> <th scope="col"> Level</th> 395 * <th scope="col"> Description</th> 396 * </tr> 397 * 398 * <tr> 399 * <td>{@link java.util.logging.Level#INFO INFO}</td> 400 * <td>when a task is stopped because of a definite exception</td> 401 * </tr> 402 * <tr> 403 * <td>{@link java.util.logging.Level#INFO INFO}</td> 404 * <td> 405 * when a task is stopped because it has exceeded the maximum number of 406 * times the task is allowed to be tried/re-tried 407 * </td> 408 * </tr> 409 * <tr> 410 * <td>{@link java.util.logging.Level#INFO INFO}</td> 411 * <td>when any exception occurs while attempting to prepare a proxy</td> 412 * </tr> 413 * <tr> 414 * <td>{@link java.util.logging.Level#FINER FINER}</td> 415 * <td> 416 * when any exception (other than the more serious exceptions logged 417 * at higher levels) occurs in a task 418 * </td> 419 * </tr> 420 * <tr> 421 * <td>{@link java.util.logging.Level#FINEST FINEST}</td> 422 * <td> 423 * when an <code>IllegalStateException</code> occurs upon attempting to 424 * discard a lookup service 425 * </td> 426 * </tr> 427 * <tr> 428 * <td>{@link java.util.logging.Level#FINEST FINEST}</td> 429 * <td>whenever any task is started</td> 430 * </tr> 431 * 432 * <tr> 433 * <td>{@link java.util.logging.Level#FINEST FINEST}</td> 434 * <td>whenever any task completes successfully</td> 435 * </tr> 436 * 437 * <tr> 438 * <td>{@link java.util.logging.Level#FINEST FINEST}</td> 439 * <td>whenever a proxy is prepared</td> 440 * </tr> 441 * </table> 442 * <p> 443 * 444 * @author Sun Microsystems, Inc. 445 * 446 * @see net.jini.discovery.DiscoveryManagement 447 * @see net.jini.lease.LeaseRenewalManager 448 * @see java.util.logging.Level 449 * @see java.util.logging.Logger 450 */ 451 public class JoinManager { 452 453 /** Implementation Note: 454 * 455 * This class executes a number of tasks asynchronously. Each task is 456 * initially queued in a <code>org.apache.river.thread.TaskManager</code> 457 * instance, which executes each task in a separate thread. In this 458 * way, an upper bound is placed on the number of threads executing 459 * concurrently at any one time; that is, the number of concurrent 460 * threads is "throttled". 461 * 462 * In addition to throttling the number of concurrent threads, the 463 * use of a task manager, in conjunction with the task configuration, 464 * provides a level of resiliency with respect to down/unresponsive 465 * lookup services. 466 * 467 * Recall from the specification that the primary function of a join 468 * manager is to maintain and update the state (registrations, 469 * attribute augmentations/replacements/changes, etc.) of the join 470 * manager's single associated service with all of the desired lookup 471 * services. Because updating a service's state in a lookup service 472 * involves remote communication, such update operations are performed 473 * asynchronously, in separate tasks. If those operations are not 474 * performed in separate tasks, then a communication problem with 475 * one of the lookup services while performing one operation could 476 * prevent (or significantly slow) the execution of all future 477 * state update operations; causing all processing in the join manager 478 * to degrade or even hang indefinitely. 479 * 480 * Although performing each update operation in a separate task thread 481 * prevents a down/unresponsive lookup service from blocking other 482 * processing in the join manager, it does not guarantee consistency 483 * of the service's state in each "up" lookup service, with the state 484 * expected by the client that requested the state changes. 485 * 486 * For example, suppose the client first requests that the service's 487 * attributes be replaced with a new set, and then immediately after 488 * the attribute replacement request, the client requests that the 489 * service's attributes be augmented by yet another set of attributes. 490 * Unless control is imposed on the order of execution of the tasks 491 * that perform the attribute replacement and augmentation, there is 492 * a possibility that the service's state in one or more of the lookup 493 * service's will have experienced the attribute augmentation prior to 494 * the attribute replacement, rather than the replacement followed by 495 * the augmentation, as the client would have expected. Thus, not only 496 * can the service's state in one or more of the lookup services be 497 * inconsistent with what the client expects, but it can also be 498 * inconsistent with one or more of the other lookup services with 499 * which the service is registered. 500 * 501 * To prevent the possibility of inconsistencies such as those just 502 * described, the state update operations are grouped according to 503 * the particular lookup service with which they are associated. Each 504 * such grouping is implemented as a task, executed by the task 505 * manager employed by this implementation of <code>JoinManager</code>, 506 * containing a queue of sub-tasks (the actual state update operations) 507 * that are executed by the main task in the same order in which they 508 * were requested by the client. 509 * 510 * Each main task executed by this join manager's task manager is a 511 * sub-class of the abstract class <code>RetryTask</code>, defined in 512 * the package <code>org.apache.river.thread</code>, which implements 513 * the <code>org.apache.river.thread.TaskManager.Task</code> interface. 514 * The association of each such task with a particular lookup service, 515 * and with a unique sequence number is reflected in the fields of this 516 * class. The unique sequence number associated with each main task 517 * is exploited by the <code>runAfter</code> method defined in 518 * <code>RetryTask</code> to guarantee that the service managed by 519 * this <code>JoinManager</code> is assigned only one service ID. 520 * 521 * Because of the relationship between the main tasks and 522 * <code>RetryTask</code>, those concrete main tasks created in this 523 * implementation of <code>JoinManager</code> can be processed by either 524 * a task manager or a wakeup manager; and this fact is exploited to 525 * provide a failure recovery mechanism that can tolerate task "storms". 526 * Task storms can occur in systems that contain numerous services, 527 * where each service employs its own join manager to handle its join 528 * state with the desired lookup services. A "registration request storm" 529 * is an example of one type of task storm. 530 * 531 * A registration request storm can occur when a system is initially 532 * started, or when it recovers from a system-wide failure, when each 533 * service's join manager attempts to register with each discovered 534 * lookup service; resulting in a flood - or "storm" - of registration 535 * requests at each lookup service. Because the accept queue under some 536 * operating systems is - by default - very small, it is possible that, 537 * in the face of such a large number of concurrent requests, one or 538 * more of the lookup services will not be able to process the requests 539 * as fast as they are arriving in the queue. When this happens, the 540 * associated task in the join manager typically encounters a failure 541 * such as a <code>java.rmi.ConnectException</code>. 542 * 543 * To deal with situations such as that described above, this 544 * <code>JoinManager</code> implementation employs a wakeup manager 545 * in addition to a task manager. When a main task is created, it is 546 * initially executed by a task manager. If a failure is encountered, 547 * and if the nature of that failure indicates that retrying the task 548 * will <i>definitely</i> fail again, then the associated lookup service 549 * is discarded so that the task can be retried when the lookup service 550 * is rediscovered. But if it is not clear that retrying the task will 551 * again fail, (that is, the failure is <i>indefinite</i>), then the 552 * task is queued for re-execution at a later time in a wakeup manager. 553 * This process is repeated - employing a "backoff" strategy - until one 554 * of the following events occurs: 555 * 556 * <p><ul> 557 * <li> the task succeeds 558 * <li> a definite failure is encountered 559 * <li> the task has been executed the maximum number times allowed 560 * </ul><p> 561 * 562 * The maximum number of times a task is allowed to be retried before 563 * giving up and discarding the associated lookup service can be changed 564 * from its default value by setting the <code>wakeupRetries</code> 565 * configuration entry for this component. 566 * 567 * @see org.apache.river.thread.TaskManager 568 * @see org.apache.river.thread.wakeup.WakeupManager 569 * @see org.apache.river.thread.TaskManager.Task 570 * @see org.apache.river.thread.RetryTask 571 * @see org.apache.river.constants.ThrowableConstants 572 */ 573 574 /** Abstract base class from which all of the task classes are derived. */ 575 private class ProxyRegTask extends RetryTask implements Comparable<ProxyRegTask> { 576 private final long[] sleepTime = { 5*1000, 10*1000, 15*1000, 577 20*1000, 25*1000, 30*1000 }; 578 // volatile fields only mutated while synchronized on proxyReg.taskList 579 private volatile int tryIndx = 0; 580 private volatile int nRetries = 0; 581 private final ProxyReg proxyReg; 582 private final int seqN; 583 584 /** Basic constructor; simply stores the input parameters */ 585 ProxyRegTask(ProxyReg proxyReg, int seqN) { 586 super(JoinManager.this.executor,JoinManager.this.wakeupMgr); 587 this.proxyReg = proxyReg; 588 this.seqN = seqN; 589 }//end constructor 590 591 /** Executes the current instance of this task once, queuing it 592 * for retry at a later time and returning <code>false</code> 593 * upon failure. This method attempts to execute all of the tasks 594 * associated with the lookup service referenced in this task's 595 * <code>proxyReg</code> field. Order of execution is important, 596 * and this method executes the tasks in the <code>proxyReg</code>'s 597 * <code>taskList</code> in a FIFO order. 598 * 599 * Note that tasks may be added to the <code>taskList</code> of 600 * the <code>proxyReg</code> during the execution of this method. 601 * 602 * Upon successfully executing all of the tasks in the 603 * <code>taskList</code>, this method returns <code>true</code> 604 * and the current instance of this task is not executed again. 605 * For each unsuccessful execution of a task in the 606 * <code>taskList</code>, this method returns <code>false</code>, 607 * which causes the task to be scheduled by the 608 * <code>WakeupManager</code> to be executed again at a later 609 * time, as indicated by the value returned by <code>retryTime</code>. 610 */ 611 @Override 612 public boolean tryOnce() { 613 while(true) { 614 JoinTask t; 615 synchronized(proxyReg.taskList) { 616 if(proxyReg.taskList.isEmpty()) { 617 proxyReg.proxyRegTask = null; 618 return true; 619 }//endif 620 t = proxyReg.taskList.get(0); 621 }//end sync 622 try { 623 t.run(); 624 synchronized(proxyReg.taskList) { 625 if( !proxyReg.taskList.isEmpty() ) { 626 JoinTask task = proxyReg.taskList.get(0); 627 if (task == t) proxyReg.taskList.remove(0); 628 }//endif 629 /* reset the retry info for the next task in the list */ 630 tryIndx = 0; 631 nRetries = 0; 632 }//end sync 633 634 } catch (Exception e) { 635 return stopTrying(e); 636 } 637 }//end loop 638 }//end tryOnce 639 640 /** Returns the next absolute time (in milliseconds) at which another 641 * execution of this task should be made (after the previous 642 * attempt has failed). 643 */ 644 @Override 645 public long retryTime() { 646 long nextTryTime = System.currentTimeMillis(); 647 synchronized (proxyReg.taskList){ 648 nextTryTime += sleepTime[tryIndx]; 649 if(tryIndx < sleepTime.length-1) tryIndx++;//don't go past end 650 nRetries++; 651 } 652 return nextTryTime; 653 }//end retryTime 654 655 /** Returns true if the current instance of this task must be run 656 * after any task already in the task manager queue. 657 * 658 * It is important that when the join manager is constructed with 659 * a <code>null</code> service ID (where it is desired that 660 * a unique service ID be generated on the service's behalf), 661 * that only the first task in the task manager's queue be run; no 662 * other tasks in the queue should be run while that first task 663 * is running. This is because the first sub-task executed by 664 * the first main task in the task manager's queue will always be 665 * a <code>RegisterTask</code>. And during the execution of that 666 * first sub-task (if the service ID has not yet been set), the 667 * service ID generated by the associated lookup service is retrieved 668 * and stored for use in all future lookup service registration 669 * tasks, Once the service ID is set by that first registration 670 * sub-task, all future main tasks (and their associated registration 671 * sub-tasks) can be run in parallel; each using the same service ID. 672 * If this is not done, then the registration sub-tasks would be 673 * run in parallel, each assigning a different ID to the service. 674 * 675 * This method guarantees that until the service's ID is set, 676 * only one registration sub-task is run; that is, one task 677 * doesn't start until the currently running task has completed, 678 * and a non-<code>null</code> service ID is assigned to the service. 679 * 680 * Executing the main tasks sequentially until the service ID is 681 * retrieved and stored must also be guaranteed because the currently 682 * running registration task may fail to register the service 683 * (because of a <code>RemoteException</code>), and thus may fail 684 * to obtain an ID for the service. This method guarantees then 685 * that each main task (and thus, each registration sub-task) will 686 * run in sequence until one of those tasks completes successfully; 687 * and from that point on, this method guarantees that all other 688 * queued tasks will run in parallel. 689 * 690 * @param tasks the tasks with which to compare the current task 691 * @param size elements with index less than size are considered 692 */ 693 public boolean dependsOn(ProxyRegTask t) { 694 return seqN > t.getSeqN(); 695 } 696 697 /* If the service's ID has already been set, then it's okay 698 * to run all ProxyRegTask's in parallel, otherwise, the 699 * ProxyRegTask with the lowest sequence number should be run. 700 */ 701 public boolean hasDeps(){ 702 return serviceItem.serviceID == null; 703 } 704 705 /** Accessor method that returns the instance of <code>ProxyReg</code> 706 * (the lookup service) associated with the task represented by 707 * the current instance of this class. 708 */ 709 public ProxyReg getProxyReg() { 710 return proxyReg; 711 }//end getProxy 712 713 /** Accessor method that returns the unique sequence number associated 714 * with the task represented by the current instance of this class. 715 */ 716 public int getSeqN() { 717 return seqN; 718 }//end getSeqN 719 720 /** Convenience method called by the child tasks when they encounter 721 * an exception. If the given exception indicates that retrying the 722 * task would definitely fail, or if the maximum allowable number 723 * of retries of the task has been exceeded, then this method will 724 * do the following: 725 * - remove all pending tasks that are to be run after this task 726 * - cancel this task 727 * - discard the look service associated with this task 728 * - return <code>true</code> (which stops the wakeup manager 729 * from retrying this task 730 * otherwise, this method returns <code>false</code>, which indicates 731 * that the wakeup manager should not stop trying to successfully 732 * execute the task. 733 */ 734 protected boolean stopTrying(Exception e) { 735 int exCat = ThrowableConstants.retryable(e); 736 if( (exCat != ThrowableConstants.INDEFINITE) 737 || (nRetries >= maxNRetries) ) 738 { 739 removeTasks(proxyReg);//cancel and clear all related tasks 740 proxyReg.fail(e); 741 return true;//don't try again 742 }//endif 743 logger.log(Level.FINER, 744 "JoinManager - failure, will retry later", e); 745 return false;//try this task again later 746 }//end stopTrying 747 748 @Override 749 public int compareTo(ProxyRegTask o) { 750 if (seqN < o.seqN) return -1; 751 if (seqN > o.seqN) return 1; 752 return 0; 753 } 754 755 @Override 756 public boolean equals(Object o){ 757 if (!(o instanceof ProxyRegTask)) return false; 758 ProxyRegTask that = (ProxyRegTask) o; 759 if (this.seqN != that.seqN) return false; 760 return this.proxyReg.equals(that.proxyReg); 761 } 762 763 @Override 764 public int hashCode() { 765 int hash = 5; 766 hash = 79 * hash + (this.proxyReg != null ? this.proxyReg.hashCode() : 0); 767 hash = 79 * hash + this.seqN; 768 return hash; 769 } 770 }//end class ProxyRegTask 771 772 private static final class ProxyRegTaskQueue implements FutureObserver { 773 // CacheTasks pending completion. 774 private final ConcurrentLinkedQueue<ProxyRegTask> pending; 775 private final ExecutorService executor; 776 777 private ProxyRegTaskQueue(ExecutorService e){ 778 this.pending = new ConcurrentLinkedQueue<ProxyRegTask>(); 779 executor = e; 780 } 781 782 private Future submit(ProxyRegTask t){ 783 pending.offer(t); 784 t.addObserver(this); 785 if (t.hasDeps()) { 786 List<ObservableFuture> deps = new LinkedList<ObservableFuture>(); 787 Iterator<ProxyRegTask> it = pending.iterator(); 788 while (it.hasNext()){ 789 ProxyRegTask c = it.next(); 790 if (t.dependsOn(c)) { 791 deps.add(c); 792 } 793 } 794 if (deps.isEmpty()){ 795 executor.submit(t); 796 } else { 797 DependencyLinker linker = new DependencyLinker(executor, deps, t); 798 linker.register(); 799 } 800 } else { 801 executor.submit(t); 802 } 803 return t; 804 } 805 806 @Override 807 public void futureCompleted(Future e) { 808 pending.remove(e); 809 } 810 } 811 812 /** Abstract base class from which all the sub-task classes are derived. */ 813 private static abstract class JoinTask { 814 815 /** Data structure referencing the task's associated lookup service */ 816 protected final ProxyReg proxyReg; 817 818 /** Basic constructor; simply stores the input parameters */ 819 JoinTask(ProxyReg proxyReg) { 820 this.proxyReg = proxyReg; 821 }//end constructor 822 823 /** Method executed in a separate thread created by the task manager */ 824 public abstract void run() throws Exception; 825 826 }//end class JoinTask 827 828 /** Task that asynchronously registers the service associated with this 829 * join manager with the lookup service referenced by the current 830 * instance of this class. 831 */ 832 private static class RegisterTask extends JoinTask { 833 /** Attributes with which to register the service. These attributes 834 * must not change during the registration process performed in 835 * this this task. 836 */ 837 final Entry[] regAttrs; 838 839 /** Constructor that associates this task with the lookup service 840 * referenced in the given <code>ProxyReg</code> parameter. 841 * 842 * @param proxyReg data structure that references the lookup service 843 * with which the service is to be registered 844 * @param regAttrs array of Entry objects whose elements are the 845 * attributes with which to register the service. 846 * The caller of this constructor should take steps 847 * to guarantee that the contents of this parameter 848 * do not change during the registration process 849 * performed in this task. 850 */ 851 RegisterTask(ProxyReg proxyReg, Entry[] regAttrs) { 852 super(proxyReg); 853 this.regAttrs = regAttrs; 854 }//end constructor 855 856 /** Attempts to register this join manager's service with the lookup 857 * service referenced in this task's proxyReg field. 858 */ 859 @Override 860 public void run() throws Exception { 861 logger.finest("JoinManager - RegisterTask started"); 862 proxyReg.register(regAttrs); 863 logger.finest("JoinManager - RegisterTask completed"); 864 }//end run 865 866 }//end class RegisterTask 867 868 /** Task that asynchronously re-registers the service associated with this 869 * join manager with the lookup service referenced by the current 870 * instance of this class. 871 * 872 * This task is typically executed when the service's lease with the 873 * referenced lookup service has expired. 874 */ 875 private class LeaseExpireNotifyTask extends JoinTask { 876 /** Attributes with which to re-register the service. These attributes 877 * must not change during the registration process performed in 878 * this this task. 879 */ 880 Entry[] regAttrs; 881 /** Constructor that associates this task with the lookup service 882 * referenced in the given <code>ProxyReg</code> parameter. 883 * 884 * @param proxyReg data structure that references the lookup service 885 * with which the service is to be re-registered 886 * @param regAttrs array of Entry objects whose elements are the 887 * attributes with which to re-register the service. 888 * The caller of this constructor should take steps 889 * to guarantee that the contents of this parameter 890 * do not change during the registration process 891 * performed in this task. 892 */ 893 LeaseExpireNotifyTask(ProxyReg proxyReg, Entry[] regAttrs) { 894 super(proxyReg); 895 this.regAttrs = regAttrs; 896 }//end constructor 897 898 /** Attempts to re-register this join manager's service with the 899 * lookup service referenced by the current instance of this class. 900 */ 901 @Override 902 public void run() throws Exception { 903 logger.finest("JoinManager - LeaseExpireNotifyTask started"); 904 if(joinSet.contains(proxyReg)) proxyReg.register(regAttrs); 905 logger.finest("JoinManager - LeaseExpireNotifyTask completed"); 906 }//end run 907 908 }//end class LeaseExpireNotifyTask 909 910 /** Task that asynchronously requests the cancellation of the lease 911 * on the <code>ServiceRegistration</code> referenced by the given 912 * <code>ProxyReg</code> object. The lease to be cancelled was granted 913 * by the lookup service whose proxy is also referenced by the given 914 * <code>ProxyReg</code> object. This task is executed whenever that 915 * lookup service is discarded. 916 * 917 * Note that although this task is executed upon receipt of any type 918 * of lookup service discard event, there is one type of discard 919 * that this task is actually intended to address: the so-called 920 * "lost-interest" discard. A discard corresponding to a loss of 921 * interest is an indication that the discarded lookup service is 922 * still up and available, but the discovery manager employed by 923 * this join manager (not the join manager or the service itself) 924 * discards the lookup service because the service is no longer 925 * interested in being registered with that lookup service. This 926 * loss of interest is caused by a change in either the lookup 927 * service's member groups or the service's groups-to-discover. 928 * Such a change in groups would typically be made administratively, 929 * through either the lookup service's administrative interface or 930 * through the service's administrative interface (or both). 931 * 932 * When the lookup service is discarded because of a loss of 933 * interest, there is a time period in which the service is still 934 * registered with the lookup service, even though the service no 935 * longer wishes to be registered with that lookup service. To 936 * address this, the lease is cancelled so that the service is 937 * removed from the lookup service in a much more timely fashion 938 * than simply allowing the lease to expire. 939 * 940 * As stated above, this task is also executed upon receipt of 941 * the other types of discard event. But because those other 942 * discard types occur when the lookup service is no longer 943 * available, the lease cancellation that is attempted here has 944 * no significant effect. 945 * 946 * @see DiscMgrListener#discarded 947 */ 948 private class DiscardProxyTask extends JoinTask { 949 /** Constructor that provides this task with access (through the given 950 * <code>ProxyReg</code> parameter) to the lease to be cancelled. 951 * 952 * @param proxyReg data structure that references the 953 * <code>ServiceRegistration</code> and associated 954 * lease whose cancellation is to be requested 955 */ 956 DiscardProxyTask(ProxyReg proxyReg) { 957 super(proxyReg); 958 }//end constructor 959 960 /** Requests the cancellation of the lease on the 961 * <code>ServiceRegistration</code> that is referenced in the 962 * <code>proxyReg</code> data structure. 963 */ 964 @Override 965 public void run() { 966 logger.finest("JoinManager --> DiscardProxyTask started"); 967 Lease svcLease = proxyReg != null ? proxyReg.serviceLease : null; 968 if( svcLease != null ) { 969 try { 970 svcLease.cancel(); 971 } catch (Exception e) { /*ignore*/ } 972 }//endif 973 logger.finest("JoinManager - DiscardProxyTask completed"); 974 }//end run 975 976 }//end class DiscardProxyTask 977 978 /** Task that asynchronously augments the attributes associated with this 979 * join manager's service in the lookup service referenced by the 980 * current instance of this class. 981 */ 982 private static class AddAttributesTask extends JoinTask { 983 /** The new attribute values with which the service's current 984 * attributes will be augmented, replaced, or changed. 985 */ 986 protected final Entry[] attrSets; 987 988 /** Constructor that associates this task with the lookup service 989 * referenced in the given <code>ProxyReg</code> parameter. 990 * 991 * @param proxyReg data structure that references the lookup service 992 * in which the service's attributes should be 993 * augmented 994 * @param newAttrs the attributes with which to augment the 995 * service's current set of attributes 996 */ 997 AddAttributesTask(ProxyReg proxyReg, Entry[] newAttrs) { 998 super(proxyReg); 999 this.attrSets = (Entry[])newAttrs.clone(); 1000 }//end constructor 1001 1002 /** Performs the actual attribute augmentation, replacement, or 1003 * modification work. This method is typically overridden by 1004 * sub-classes of this class. 1005 */ 1006 protected void doAttributes(ProxyReg proxyReg) throws Exception { 1007 logger.finest("JoinManager - AddAttributesTask started"); 1008 proxyReg.addAttributes(attrSets); 1009 logger.finest("JoinManager - AddAttributesTask completed"); 1010 }//end AddAttributesTask.doAttributes 1011 1012 /** Attempts to either augment, replace, or modify the attributes 1013 * of this join manager's service in the lookup service referenced 1014 * by the current instance of this class. Which action is taken -- 1015 * augmentation, replacement, or modification -- is dependent on the 1016 * definition of the <code>doAttributes/code> method. 1017 */ 1018 @Override 1019 public void run() throws Exception { 1020 doAttributes(proxyReg); 1021 }//end run 1022 1023 }//end class AddAttributesTask 1024 1025 /** Task that asynchronously replaces the attributes associated with this 1026 * join manager's service in the lookup service referenced by the 1027 * current instance of this class. 1028 */ 1029 private static final class SetAttributesTask extends AddAttributesTask { 1030 /** Constructor that associates this task with the lookup service 1031 * referenced in the given <code>ProxyReg</code> parameter. 1032 * 1033 * @param proxyReg data structure that references the lookup 1034 * service in which the service's attributes 1035 * should be replaced 1036 * @param replacementAttrs the attributes with which to replace the 1037 * service's current set of attributes 1038 */ 1039 SetAttributesTask(ProxyReg proxyReg, Entry[] replacementAttrs){ 1040 super(proxyReg, replacementAttrs); 1041 }//end constructor 1042 1043 /** Performs the actual attribute replacement work. */ 1044 @Override 1045 protected void doAttributes(ProxyReg proxyReg) throws Exception { 1046 logger.finest("JoinManager - SetAttributesTask started"); 1047 proxyReg.setAttributes(attrSets); 1048 logger.finest("JoinManager - SetAttributesTask completed"); 1049 }//end SetAttributesTask.doAttributes 1050 1051 }//end class SetAttributesTask 1052 1053 /** Task that asynchronously modifies the attributes associated with this 1054 * join manager's service in the lookup service referenced by the 1055 * current instance of this class. 1056 */ 1057 private static final class ModifyAttributesTask extends AddAttributesTask { 1058 private final Entry[] attrSetTemplates; 1059 /** Constructor that associates this task with the lookup service 1060 * referenced in the given <code>ProxyReg</code> parameter. 1061 * 1062 * @param proxyReg data structure that references the lookup 1063 * service in which the service's attributes 1064 * should be changed 1065 * @param attrSetTemplates the attribute templates that are used to 1066 * select (through attribute matching) the 1067 * attributes to change in the service's 1068 * current set of attributes 1069 * @param attrChanges the attributes containing the changes to 1070 * make to the attributes in the service's 1071 * current set that are selected through 1072 * attribute matching with the 1073 * attrSetTemplates parameter 1074 */ 1075 ModifyAttributesTask( ProxyReg proxyReg, 1076 Entry[] attrSetTemplates, 1077 Entry[] attrChanges ) 1078 { 1079 super(proxyReg, attrChanges); 1080 this.attrSetTemplates = (Entry[])attrSetTemplates.clone(); 1081 }//end constructor 1082 1083 /** Performs the actual attribute modification work. */ 1084 @Override 1085 protected void doAttributes(ProxyReg proxyReg) throws Exception { 1086 logger.finest("JoinManager - ModifyAttributesTask started"); 1087 proxyReg.modifyAttributes(attrSetTemplates, attrSets); 1088 logger.finest("JoinManager - ModifyAttributesTask completed"); 1089 }//end ModifyAttributesTask.doAttributes 1090 1091 }//end class ModifyAttributesTask 1092 1093 /** Wrapper class in which each instance corresponds to a lookup 1094 * service to discover, and with which this join manager's service 1095 * should be registered. 1096 */ 1097 private class ProxyReg implements FutureObserver{ 1098 1099 /** Class that is registered as a listener with this join manager's 1100 * lease renewal manager. That lease renewal manager manages the 1101 * lease granted to this join manager's associated service by the 1102 * lookup service referenced in the proxy object associated with 1103 * this class (<code>ProxyReg</code>). 1104 * 1105 * If the lease expires in the lookup service before the lease 1106 * renewal manager requests renewal of the lease, then upon sending 1107 * that renewal request, the lease renewal manager will receive an 1108 * <code>UnknownLeaseException</code> from the lookup service. 1109 * As a result, the lease renewal manager removes the expired lease 1110 * so that no further renewal attempts are made for that lease, 1111 * and then sends to this listener a <code>LeaseRenewalEvent</code>, 1112 * containing an <code>UnknownLeaseException</code>. 1113 * 1114 * Alternatively, suppose that at the time the lease renewal manager 1115 * is about to request the renewal of the lease, the lease renewal 1116 * manager determines that the expiration time of the lease has 1117 * actually passed. In this case, since there is no reason to 1118 * send the renewal request, the lease renewal manager instead 1119 * removes the expired lease (again, so that no further renewal 1120 * attempts are made for that lease), and then sends a 1121 * <code>LeaseRenewalEvent</code> to this listener to indicate 1122 * that the lease has expired. The difference between this case, 1123 * and the case described previously is that in this case the 1124 * <code>LeaseRenewalEvent</code> contains no exception (that is, 1125 * <code>LeaseRenewalEvent.getException</code> returns 1126 * <code>null</code>). 1127 * 1128 * Both situations described above indicate that the lease 1129 * referenced by the event received by this listener has expired. 1130 * Thus, the normal course of action should be to attempt to 1131 * re-register the service with the lookup service that originally 1132 * granted the lease that has expired. 1133 * 1134 * Prior to re-registering the service, the joinSet is examined to 1135 * determine if it contains a ProxyReg object that is equivalent to 1136 * the ProxyReg object referencing the current instance of this 1137 * listener class. That is, using <code>equals</code>, it is 1138 * determined whether the joinSet contains either ProxyReg.this, 1139 * or a new instance of ProxyReg that is equal to ProxyReg.this. 1140 * If the joinSet does not contain such a ProxyReg, then the lookup 1141 * service must have been discarded and not yet re-discovered; in 1142 * which case, there is no need to attempt to re-register with that 1143 * lookup service, since it is unavailable. 1144 * 1145 * If it is determined that the joinSet does contain either 1146 * ProxyReg.this or a new ProxyReg equivalent to ProxyReg.this, 1147 * then re-registration should be attempted, but only if the lease 1148 * associated with the ProxyReg in the joinSet is equal to the 1149 * expired lease referenced in the event received by this listener. 1150 * Equality of those leases is an indication that the lease on the 1151 * service's current registration has expired; thus, an attempt to 1152 * re-register the service should be made. 1153 * 1154 * If the lease associated with the ProxyReg from the joinSet 1155 * does not equal the expired lease from the event, then 1156 * re-registration should not be attempted. This is because 1157 * the inequality of the leases is an indication that the lease 1158 * renewal event received by this listener was the result of an 1159 * <code>UnknownLeaseException</code> that occurs when the 1160 * ProxyReg in the joinSet is a new ProxyReg, different from 1161 * ProxyReg.this, and the lease renewal manager requests the 1162 * renewal of the (now invalid) lease associated with that old 1163 * ProxyReg.this; not the valid lease associated with the new 1164 * ProxyReg. 1165 * 1166 * A scenario such as that just described can occur when the 1167 * lookup service is discarded, rediscovered, and the service is 1168 * re-registered, resulting in a new ProxyReg (with new lease) 1169 * being placed in the joinSet, replacing the previous ProxyReg 1170 * (ProxyReg.this). But before the old, expired lease is removed 1171 * from the lease renewal manager, an attempt to renew the old 1172 * lease is made. Such an attempt can occur because the lease 1173 * renewal manager may be in the process of requesting the renewal 1174 * of that lease (or may have queued such a request) just prior to, 1175 * or at the same time as, when the lease removal request is made 1176 * during discard processing. When the request is made to renew 1177 * the expired lease, an <code>UnknownLeaseException</code> occurs 1178 * and a lease renewal event is sent to this listener. 1179 * 1180 * If, upon receiving an event such as that just described, the 1181 * service were to be re-registered, the current valid service 1182 * registration would be replaced, a new lease would be granted, 1183 * and the corresponding ProxyReg currently contained in the joinSet 1184 * would be replaced with a new ProxyReg. Additionally, the now 1185 * invalid lease corresponding to the ProxyReg that was just 1186 * replaced would remain in the lease renewal manager. This means 1187 * that an attempt to renew that lease will eventually be made and 1188 * will fail, and the cycle just described will repeat indefinitely. 1189 * 1190 * Thus, for the reasons stated above, re-registration is attempted 1191 * only if the lease associated with the ProxyReg contained in the 1192 * joinSet is equal to the expired lease referenced in the lease 1193 * renewal event received by this listener. 1194 */ 1195 private class DiscLeaseListener implements LeaseListener { 1196 @Override 1197 public void notify(LeaseRenewalEvent e) { 1198 Throwable ex = e.getException(); 1199 if ( (ex == null) || (ex instanceof UnknownLeaseException) ) { 1200 removeTasks(ProxyReg.this); 1201 Lease expiredLease = e.getLease(); 1202 // Maybe re-register 1203 Iterator<ProxyReg> it = joinSet.iterator(); 1204 while (it.hasNext()){ 1205 ProxyReg next = it.next(); 1206 if (!ProxyReg.this.equals(next)) continue; 1207 if(expiredLease.equals(next.serviceLease)) { 1208 // Okay to re-register 1209 addTask( 1210 new LeaseExpireNotifyTask (ProxyReg.this, 1211 (Entry[])lookupAttr.clone())); 1212 }//endif 1213 } 1214 } else { 1215 fail(ex); 1216 }//endif 1217 }//end notify 1218 }//end class DiscLeaseListener 1219 1220 /** The <code>ProxyRegTask</code> that instantiated this 1221 * <code>ProxyReg</code>. 1222 */ 1223 volatile ProxyRegTask proxyRegTask;// writes sync on taskList 1224 /** The <i>prepared</i> proxy to the lookup service referenced by 1225 * this class, and with which this join manager's service will be 1226 * registered. 1227 */ 1228 final ServiceRegistrar proxy; 1229 /** The <i>prepared</i> registration proxy returned by this class' 1230 * associated lookup service when this join manager registers its 1231 * associated service. 1232 * 1233 * Writes to reference synchronized on JoinManager.this, but not referent 1234 * as it has foreign remote methods. 1235 */ 1236 volatile ServiceRegistration srvcRegistration = null; 1237 /* The <i>prepared</i> proxy to the lease on the registration of this 1238 * join manager's service with the this class' associated lookup 1239 * service. 1240 */ 1241 volatile Lease serviceLease = null; 1242 /** The set of sub-tasks that are to be executed in order for the 1243 * lookup service associated with the current instance of this class. 1244 */ 1245 final List<JoinTask> taskList = new ArrayList<JoinTask>(); 1246 /** The instance of <code>DiscLeaseListener</code> that is registered 1247 * with the lease renewal manager that handles the lease of this join 1248 * manger's service. 1249 */ 1250 final List<Future> runningTasks = new ArrayList<Future>(); 1251 1252 private final DiscLeaseListener dListener = new DiscLeaseListener(); 1253 1254 /** Constructor that associates this class with the lookup service 1255 * referenced in the given <code>ProxyReg</code> parameter. 1256 * 1257 * @param proxy data structure that references the lookup service on 1258 * which the sub-tasks referenced in this class will be 1259 * executed in order 1260 */ 1261 public ProxyReg(ServiceRegistrar proxy) { 1262 if(proxy == null) throw new IllegalArgumentException 1263 ("proxy can't be null"); 1264 this.proxy = proxy; 1265 }//end constructor 1266 1267 @Override 1268 public void futureCompleted(Future e) { 1269 synchronized (runningTasks){ 1270 runningTasks.remove(e); 1271 } 1272 } 1273 1274 public void terminate(){ 1275 synchronized (runningTasks){ 1276 Iterator<Future> it = runningTasks.iterator(); 1277 while (it.hasNext()){ 1278 it.next().cancel(false); 1279 } 1280 runningTasks.clear(); 1281 } 1282 } 1283 1284 /** Convenience method that adds new sub-tasks to this class' 1285 * task queue. 1286 * 1287 * @param task the task to add to the task queue 1288 */ 1289 public void addTask(JoinTask task) { 1290 if(bTerminated) return; 1291 Future future = null; 1292 synchronized(taskList) { 1293 taskList.add(task); 1294 if(this.proxyRegTask == null) { 1295 this.proxyRegTask = new ProxyRegTask(this,taskSeqN++); 1296 this.proxyRegTask.addObserver(this); 1297 future = proxyRegTaskQueue.submit(this.proxyRegTask); 1298 }//endif 1299 }//end sync(taskList) 1300 synchronized (runningTasks){ 1301 runningTasks.add(future); 1302 } 1303 }//end addTask 1304 1305 /** Registers the service associated with this join manager with the 1306 * the lookup service corresponding to this class. Additionally, 1307 * this method retrieves the lease granted by the lookup service 1308 * on the service's registration, and passes that lease to the 1309 * lease renewal manager. If a <code>ServiceIDListener</code> 1310 * has been registered with this join manager, this method will 1311 * send to that listener a notification containing the service's ID. 1312 */ 1313 public void register(Entry[] srvcAttrs) throws Exception { 1314 if(proxy == null) throw new RuntimeException("proxy is null"); 1315 /* The lookup service proxy was already prepared at discovery */ 1316 ServiceItem tmpSrvcItem; 1317 ServiceItem item; 1318 srvcRegistration = null; 1319 //accessing serviceItem.serviceID 1320 item = serviceItem; 1321 tmpSrvcItem = new ServiceItem(item.serviceID, 1322 item.service, 1323 srvcAttrs); 1324 /* Retrieve and prepare the proxy to the service registration */ 1325 ServiceRegistration tmpSrvcRegistration 1326 = proxy.register(tmpSrvcItem, renewalDuration); 1327 try { 1328 tmpSrvcRegistration = 1329 (ServiceRegistration)registrationPreparer.prepareProxy 1330 ( tmpSrvcRegistration ); 1331 logger.finest 1332 ("JoinManager - ServiceRegistration proxy prepared"); 1333 } catch(Exception e) { 1334 LogUtil.logThrow(logger, Level.WARNING, ProxyReg.class, 1335 "register", "JoinManager - failure during " + 1336 "preparation of ServiceRegistration proxy: {0}", 1337 new Object[] { tmpSrvcRegistration }, e); 1338 throw e; //rethrow the exception since proxy may be unusable 1339 } 1340 /* Retrieve and prepare the proxy to the service lease */ 1341 Lease svcLease = tmpSrvcRegistration.getLease(); 1342 try { 1343 this.serviceLease = 1344 (Lease)serviceLeasePreparer.prepareProxy(svcLease); 1345 logger.finest("JoinManager - service lease proxy prepared"); 1346 } catch(Exception e) { 1347 LogUtil.logThrow(logger, Level.WARNING, ProxyReg.class, 1348 "register", "JoinManager - failure during " + 1349 "preparation of service lease proxy: {0}", 1350 new Object[] { svcLease }, e); 1351 throw e; //rethrow the exception since proxy may be unusable 1352 } 1353 leaseRenewalMgr.renewUntil(svcLease, Lease.FOREVER, 1354 renewalDuration, dListener); 1355 ServiceID tmpID = null; 1356 srvcRegistration = tmpSrvcRegistration; 1357 ServiceID id = srvcRegistration.getServiceID(); 1358 synchronized (JoinManager.this){ 1359 item = serviceItem; 1360 if(item.serviceID == null) { 1361 serviceItem = new ServiceItem(id, item.service, item.attributeSets); 1362 tmpID = id; 1363 }//endif 1364 } 1365 if( (tmpID != null) && (callback != null) ) { 1366 callback.serviceIDNotify(tmpID); 1367 }//endif 1368 }//end ProxyReg.register 1369 1370 /** With respect to the lookup service referenced in this class 1371 * and with which this join manager's service is registered, this 1372 * method associates with that service a new set of attributes -- in 1373 * addition to that service's current set of attributes. 1374 */ 1375 public void addAttributes(Entry[] attSet) throws Exception { 1376 ServiceRegistration sr = srvcRegistration; 1377 if (sr != null) sr.addAttributes(attSet); 1378 }//end ProxyReg.addAttributes 1379 1380 /** With respect to the lookup service referenced in this class 1381 * and with which this join manager's service is registered, this 1382 * method changes that service's current attributes by selecting 1383 * the attributes to change using the given first parameter; 1384 * and identifying the desired changes to make through the 1385 * contents of the second parameter. 1386 */ 1387 public void modifyAttributes(Entry[] templ, Entry[] attSet) 1388 throws Exception 1389 { 1390 ServiceRegistration sr = srvcRegistration; 1391 if (sr != null) sr.modifyAttributes(templ, attSet); 1392 }//end ProxyReg.modifyAttributes 1393 1394 /** With respect to the lookup service referenced in this class 1395 * and with which this join manager's service is registered, this 1396 * method replaces that service's current attributes with a new 1397 * set of attributes. 1398 */ 1399 public void setAttributes(Entry[] attSet) throws Exception { 1400 ServiceRegistration sr = srvcRegistration; 1401 if (sr != null) sr.setAttributes(attSet); 1402 }//end ProxyReg.setAttributes 1403 1404 /** Convenience method that encapsulates appropriate behavior when 1405 * failure is encountered related to the current instance of this 1406 * class. This method discards the lookup service proxy associated 1407 * with this object, and logs the stack trace of the given 1408 * <code>Throwable</code> according to the logging levels specified 1409 * for this utility. 1410 * 1411 * Note that if the discovery manager employed by this join manager 1412 * has been terminated, then the attempt to discard the lookup 1413 * service proxy will result in an <code>IllegalStateException</code>. 1414 * Since this method is called only within the tasks run by 1415 * this join manager, and since propagating an 1416 * <code>IllegalStateException</code> out into the 1417 * <code>ThreadGroup</code> of those tasks is undesirable, this 1418 * method will not propagate the <code>IllegalStateException</code> 1419 * that occurs as a result of an attempt to discard a lookup 1420 * service proxy from the discovery manager employed by this 1421 * join manager. 1422 * 1423 * For more information, refer to Bug 4490355. 1424 */ 1425 public void fail(Throwable e) { 1426 if(bTerminated) return; 1427 LogUtil.logThrow(logger, Level.INFO, ProxyReg.class, "fail", 1428 "JoinManager - failure for lookup service proxy: {0}", 1429 new Object[] { proxy }, e); 1430 try { 1431 discMgr.discard(proxy); 1432 } catch(IllegalStateException e1) { 1433 logger.log(Level.FINEST, 1434 "JoinManager - cannot discard lookup, " 1435 +"discovery manager already terminated", 1436 e1); 1437 } 1438 }//end ProxyReg.fail 1439 1440 /** Returns true if the both objects' associated proxies are equal. */ 1441 @Override 1442 public boolean equals(Object obj) { 1443 if (obj instanceof ProxyReg) { 1444 return proxy.equals( ((ProxyReg)obj).proxy ); 1445 } else { 1446 return false; 1447 }//endif 1448 }//end ProxyReg.equals 1449 1450 /** Returns the hash code of the proxy referenced in this class. */ 1451 @Override 1452 public int hashCode() { 1453 return proxy.hashCode(); 1454 }//end hashCode 1455 1456 }//end class ProxyReg 1457 1458 /* Listener class for discovery/discard notification of lookup services. */ 1459 private class DiscMgrListener implements DiscoveryListener { 1460 /* Invoked when new or previously discarded lookup is discovered. */ 1461 @Override 1462 public void discovered(DiscoveryEvent e) { 1463 ServiceRegistrar[] proxys 1464 = (ServiceRegistrar[])e.getRegistrars(); 1465 int l = proxys.length; 1466 for(int i=0;i<l;i++) { 1467 /* Prepare the proxy to the discovered lookup service 1468 * before interacting with it. 1469 */ 1470 try { 1471 proxys[i] 1472 = (ServiceRegistrar)registrarPreparer.prepareProxy 1473 (proxys[i]); 1474 logger.log(Level.FINEST, "JoinManager - discovered " 1475 +"lookup service proxy prepared: {0}", 1476 proxys[i]); 1477 } catch(Exception e1) { 1478 LogUtil.logThrow(logger, Level.INFO, 1479 DiscMgrListener.class, "discovered", "failure " 1480 + "preparing discovered ServiceRegistrar proxy: " 1481 + "{0}", new Object[] { proxys[i] }, e1); 1482 discMgr.discard(proxys[i]); 1483 continue; 1484 } 1485 /* If the serviceItem is a lookup service, don't need to 1486 * register it with itself since a well-defined lookup 1487 * service will always register with itself. 1488 */ 1489 if( !proxys[i].equals(serviceItem.service) ) { 1490 ProxyReg proxyReg = new ProxyReg(proxys[i]); 1491 if( !joinSet.contains(proxyReg) ) { 1492 joinSet.add(proxyReg); 1493 proxyReg.addTask(new RegisterTask 1494 (proxyReg, 1495 (Entry[])lookupAttr.clone())); 1496 }//endif 1497 }//endif 1498 }//end loop 1499 }//end discovered 1500 1501 /* Invoked when previously discovered lookup is discarded. */ 1502 @Override 1503 public void discarded(DiscoveryEvent e) { 1504 ServiceRegistrar[] proxys 1505 = (ServiceRegistrar[])e.getRegistrars(); 1506 int l = proxys.length; 1507 for(int i=0;i<l;i++) { 1508 ProxyReg proxyReg = findReg(proxys[i]); 1509 if(proxyReg != null) { 1510 removeTasks(proxyReg); 1511 joinSet.remove(proxyReg); 1512 try { 1513 leaseRenewalMgr.remove( proxyReg.serviceLease ); 1514 } catch(UnknownLeaseException ex) { /*ignore*/ } 1515 proxyReg.addTask(new DiscardProxyTask(proxyReg)); 1516 }//endif 1517 }//end loop 1518 }//end discarded 1519 }//end class DiscMgrListener 1520 1521 /* Name of this component; used in config entry retrieval and the logger.*/ 1522 private static final String COMPONENT_NAME = "net.jini.lookup.JoinManager"; 1523 /* Logger used by this utility. */ 1524 private static final Logger logger = Logger.getLogger(COMPONENT_NAME); 1525 /** Maximum number of concurrent tasks that can be run in any default 1526 * ExecutorService created by this class. 1527 */ 1528 private static final int MAX_N_TASKS = 15; 1529 /** Whenever a task is created in this join manager, it is assigned a 1530 * unique sequence number so that the task is not run prior to the 1531 * execution, and completion of, any other tasks with which that task 1532 * is associated (tasks are grouped by the <code>ProxyReg</code> with 1533 * which each task is associated). This field contains the value of 1534 * the sequence number assigned to the most recently created task. 1535 */ 1536 private int taskSeqN = 0; // access sync on taskList 1537 /** Task manager for the various tasks executed by this join manager. 1538 * On the first attempt to execute any task is managed by this 1539 * <code>ExecutorService</code> so that the number of concurrent threads 1540 * can be bounded. If one or more of those attempts fails, a 1541 * <code>WakeupManager</code> is used (through the use of a 1542 * <code>RetryTask</code>) to schedule - at a later time (employing a 1543 * "backoff strategy") - the re-execution of each failed task in this 1544 * <code>ExecutorService</code>. 1545 */ 1546 private final ExecutorService executor; 1547 private final ProxyRegTaskQueue proxyRegTaskQueue; 1548 /** Maximum number of times a failed task is allowed to be re-executed. */ 1549 private final int maxNRetries; 1550 /** Wakeup manager for the various tasks executed by this join manager. 1551 * After an initial failure of any task executed by this join manager, 1552 * the failed task is managed by this <code>WakeupManager</code>; which 1553 * schedules the re-execution of the failed task - in the task manager - 1554 * at various times in the future until either the task succeeds or the 1555 * task has been executed the maximum number of allowable times. This 1556 * wakeup manager is supplied to the <code>RetryTask</code>) that 1557 * performs the actual task execution so that when termination of this 1558 * join manager is requested, all tasks scheduled for retry by this 1559 * wakeup manager can be cancelled. 1560 */ 1561 private final WakeupManager wakeupMgr; 1562 /** Contains the reference to the service that is to be registered with 1563 * all of the desired lookup services referenced by <code>discMgr</code>. 1564 */ 1565 private volatile ServiceItem serviceItem = null; // writes sync on JoinManager.this 1566 /** Contains the attributes with which to associate the service in each 1567 * of the lookup services with which this join manager registers the 1568 * service. 1569 */ 1570 private volatile Entry[] lookupAttr = null; // writes sync on JoinManager.this 1571 /** Contains the listener -- instantiated by the entity that constructs 1572 * this join manager -- that will receive an event containing the 1573 * service ID assigned to this join manager's service by one of the 1574 * lookup services with which that service is registered. 1575 */ 1576 private final ServiceIDListener callback; 1577 /** Contains elements of type <code>ProxyReg</code> where each element 1578 * references a proxy to one of the lookup services with which this 1579 * join manager's service is registered. 1580 */ 1581 private final List<ProxyReg> joinSet = new CopyOnWriteArrayList<ProxyReg>(); 1582 /** Contains the discovery manager that discovers the lookup services 1583 * with which this join manager will register its associated service. 1584 */ 1585 private final DiscoveryManagement discMgr; 1586 /** Contains the discovery listener registered by this join manager with 1587 * the discovery manager so that this join manager is notified whenever 1588 * one of the desired lookup services is discovered or discarded. 1589 */ 1590 private final DiscMgrListener discMgrListener ; 1591 /** Flag that indicate whether the discovery manager employed by this 1592 * join manager was created by this join manager itself, or by the 1593 * entity that constructed this join manager. 1594 */ 1595 private final boolean bCreateDiscMgr; 1596 /** Contains the lease renewal manager that renews all of the leases 1597 * this join manager's service holds with each lookup service with which 1598 * it has been registered. 1599 */ 1600 private final LeaseRenewalManager leaseRenewalMgr; 1601 /** The value to use as the <code>renewDuration</code> parameter 1602 * when invoking the lease renewal manager's <code>renewUntil</code> 1603 * method to add a service lease to manage. This value represents, 1604 * effectively, the time interval (in milliseconds) over which each 1605 * managed lease must be renewed. As this value is made smaller, 1606 * renewal requests will be made more frequently while the service 1607 * is up, and lease expirations will occur sooner when the service 1608 * goes down. 1609 */ 1610 private final long renewalDuration; 1611 /** Flag that indicates if this join manager has been terminated. */ 1612 private volatile boolean bTerminated = false; // write access sync on this. 1613 /* Preparer for the proxies to the lookup services that are discovered 1614 * and used by this utility. 1615 */ 1616 private final ProxyPreparer registrarPreparer; 1617 /* Preparer for the proxies to the registrations returned to this utility 1618 * upon registering the service with each discovered lookup service. 1619 */ 1620 private final ProxyPreparer registrationPreparer; 1621 /* Preparer for the proxies to the leases returned to this utility through 1622 * the registrations with each discovered lookup service with which this 1623 * utility has registered the service. 1624 */ 1625 private final ProxyPreparer serviceLeasePreparer; 1626 1627 /** 1628 * Constructs an instance of this class that will register the given 1629 * service reference with all discovered lookup services and, through 1630 * an event sent to the given <code>ServiceIDListener</code> object, 1631 * communicate the service ID assigned by the first lookup service 1632 * with which the service is registered. This constructor is typically 1633 * used by services which have not yet been assigned a service ID. 1634 * <p> 1635 * The value input to the <code>serviceProxy</code> parameter represents 1636 * the service reference (proxy) to register with each discovered lookup 1637 * service. If the <code>Object</code> input to that parameter is not 1638 * <code>Serializable</code>, an <code>IllegalArgumentException</code> 1639 * is thrown. If <code>null</code> is input to that parameter, a 1640 * <code>NullPointerException</code> is thrown. 1641 * <p> 1642 * The value input to the <code>attrSets</code> parameter is an array 1643 * of <code>Entry</code> objects, none of whose elements may be 1644 * <code>null</code>, that represents the new set of attributes to 1645 * associate with the new service reference to be registered. Passing 1646 * <code>null</code> as the value of the <code>attrSets</code> parameter 1647 * is equivalent to passing an empty array. If any of the elements 1648 * of the <code>attrSets</code> array are <code>null</code>, a 1649 * <code>NullPointerException</code> is thrown. The set of attributes 1650 * passed in this parameter will be associated with the service in all 1651 * future join processing until those attributes are changed through 1652 * an invocation of a method on this class such as, 1653 * <code>addAttributes</code>, <code>setAttributes</code>, 1654 * <code>modifyAttributes</code>, or <code>replaceRegistration</code>. 1655 * <p> 1656 * When constructing this utility, the service supplies an object through 1657 * which notifications that indicate a lookup service has been discovered 1658 * or discarded will be received. At a minimum, the object supplied 1659 * (through the <code>discoveryMgr</code> parameter) must satisfy the 1660 * contract defined in the <code>DiscoveryManagement</code> interface. 1661 * That is, the object supplied must provide this utility with the ability 1662 * to set discovery listeners and to discard previously discovered 1663 * lookup services when they are found to be unavailable. A value of 1664 * <code>null</code> may be input to the <code>discoveryMgr</code> 1665 * parameter. When <code>null</code> is input to that parameter, an 1666 * instance of <code>LookupDiscoveryManager</code> is used to listen 1667 * for events announcing the discovery of only those lookup services 1668 * that are members of the public group. 1669 * <p> 1670 * The object input to the <code>leaseMgr</code> parameter provides for 1671 * the coordination, systematic renewal, and overall management of all 1672 * leases on the given service reference's residency in the lookup 1673 * services that have been joined. As with the <code>discoveryMgr</code> 1674 * parameter, a value of <code>null</code> may be input to this 1675 * parameter. When <code>null</code> is input to this parameter, 1676 * an instance of <code>LeaseRenewalManager</code>, initially managing 1677 * no <code>Lease</code> objects will be used. This feature allows a 1678 * service to either use a single entity to manage all of its leases, 1679 * or to use separate entities: one to manage the leases unrelated to 1680 * the join process, and one to manage the leases that result from the 1681 * join process, that are accessible only within the current instance 1682 * of the <code>JoinManager</code>. 1683 * 1684 * @param serviceProxy the service reference (proxy) to register with all 1685 * discovered lookup services 1686 * @param attrSets array of <code>Entry</code> consisting of the 1687 * attribute sets with which to register the service 1688 * @param callback reference to the object that should receive the 1689 * event containing the service ID, assigned to the 1690 * service by the first lookup service with which the 1691 * service reference is registered 1692 * @param discoveryMgr reference to the <code>DiscoveryManagement</code> 1693 * object this class should use to manage lookup 1694 * service discovery on behalf of the given service 1695 * @param leaseMgr reference to the <code>LeaseRenewalManager</code> 1696 * object this class should use to manage the leases 1697 * on the given service's residency in the lookup 1698 * services that have been joined 1699 * 1700 * @throws java.lang.IllegalArgumentException if the object input to the 1701 * <code>serviceProxy</code> parameter is not serializable 1702 * @throws java.lang.NullPointerException if either <code>null</code> is 1703 * input to the <code>serviceProxy</code> parameter, or at least 1704 * one of the elements of the <code>attrSets</code> parameter is 1705 * <code>null</code> 1706 * @throws java.io.IOException if initiation of discovery process results 1707 * in <code>IOException</code> when socket allocation occurs 1708 * 1709 * @throws java.lang.IllegalStateException if this method is called on 1710 * a terminated <code>JoinManager</code> instance. Note that this 1711 * exception is implementation-specific. 1712 * 1713 * @see net.jini.lookup.ServiceIDListener 1714 * @see net.jini.discovery.DiscoveryManagement 1715 * @see net.jini.discovery.LookupDiscoveryManager 1716 * @see net.jini.lease.LeaseRenewalManager 1717 */ 1718 public JoinManager(Object serviceProxy, 1719 Entry[] attrSets, 1720 ServiceIDListener callback, 1721 DiscoveryManagement discoveryMgr, 1722 LeaseRenewalManager leaseMgr) throws IOException 1723 { 1724 this(serviceProxy, attrSets, null, callback, 1725 getConf(EmptyConfiguration.INSTANCE, leaseMgr, discoveryMgr, serviceProxy)); 1726 }//end constructor 1727 1728 /** 1729 * Constructs an instance of this class, configured using the items 1730 * retrieved through the given <code>Configuration</code> object, 1731 * that will register the given service reference with all discovered 1732 * lookup services and, through an event sent to the given 1733 * <code>ServiceIDListener</code> object, communicate the service ID 1734 * assigned by the first lookup service with which the service is 1735 * registered. This constructor is typically used by services which 1736 * have not yet been assigned a service ID, and which wish to allow 1737 * for deployment-time configuration of the service's join processing. 1738 * <p> 1739 * The items used to configure the current instance of this class 1740 * are obtained through the object input to the <code>config</code> 1741 * parameter. If <code>null</code> is input to that parameter, a 1742 * <code>NullPointerException</code> is thrown. 1743 * <p> 1744 * The object this utility will use to manage lookup service discovery on 1745 * behalf of the given service can be supplied through either the 1746 * <code>discoveryMgr</code> parameter or through an entry contained 1747 * in the given <code>Configuration</code>. If <code>null</code> is input 1748 * to the <code>discoveryMgr</code> parameter, an attempt will first be 1749 * made to retrieve from the given <code>Configuration</code>, an entry 1750 * named "discoveryManager" (described above). If such an object is 1751 * successfully retrieved from the given <code>Configuration</code>, that 1752 * object will be used to perform the lookup service discovery management 1753 * required by this utility. 1754 * <p> 1755 * If <code>null</code> is input to the <code>discoveryMgr</code> 1756 * parameter, and no entry named "discoveryManager" is specified in the 1757 * given <code>Configuration</code>, then an instance of the utility class 1758 * <code>LookupDiscoveryManager</code> will be used to listen for events 1759 * announcing the discovery of only those lookup services that are 1760 * members of the public group. 1761 * <p> 1762 * As with the <code>discoveryMgr</code> parameter, the object this 1763 * utility will use to perform lease management on behalf of the given 1764 * service can be supplied through either the <code>leaseMgr</code> 1765 * parameter or through an entry contained in the given 1766 * <code>Configuration</code>. If <code>null</code> is input to the 1767 * <code>leaseMgr</code> parameter, an attempt will first be made to 1768 * retrieve from the given <code>Configuration</code>, an entry named 1769 * "leaseManager" (described above). If such an object is successfully 1770 * retrieved from the given <code>Configuration</code>, that object 1771 * will be used to perform the lease management required by this utility. 1772 * <p> 1773 * If <code>null</code> is input to the <code>leaseMgr</code> 1774 * parameter, and no entry named "leaseManager" is specified in the 1775 * given <code>Configuration</code>, then an instance of the utility 1776 * class <code>LeaseRenewalManager</code> that takes the given 1777 * <code>Configuration</code> will be created (initially managing no 1778 * leases) and used to perform all required lease renewal management 1779 * on behalf of the given service. 1780 * <p> 1781 * Except for the <code>config</code> parameter and the additional 1782 * semantics imposed by that parameter (as noted above), all other 1783 * parameters of this form of the constructor, along with their 1784 * associated semantics, are identical to that of the five-argument 1785 * constructor that takes a <code>ServiceIDListener</code>. 1786 * 1787 * @param serviceProxy the service reference (proxy) to register with all 1788 * discovered lookup services 1789 * @param attrSets array of <code>Entry</code> consisting of the 1790 * attribute sets with which to register the service 1791 * @param callback reference to the <code>ServiceIDListener</code> 1792 * object that should receive the event containing the 1793 * service ID assigned to the service by the first 1794 * lookup service with which the service reference 1795 * is registered 1796 * @param discoveryMgr reference to the <code>DiscoveryManagement</code> 1797 * object this class should use to manage lookup 1798 * service discovery on behalf of the given service 1799 * @param leaseMgr reference to the <code>LeaseRenewalManager</code> 1800 * object this class should use to manage the leases 1801 * on the given service's residency in the lookup 1802 * services that have been joined 1803 * @param config instance of <code>Configuration</code> through 1804 * which the items used to configure the current 1805 * instance of this class are obtained 1806 * 1807 * @throws java.lang.IllegalArgumentException if the object input to the 1808 * <code>serviceProxy</code> parameter is not serializable 1809 * @throws java.lang.NullPointerException if <code>null</code> is input 1810 * to the <code>serviceProxy</code> parameter or the 1811 * <code>config</code> parameter, or if at least one of the 1812 * elements of the <code>attrSets</code> parameter is 1813 * <code>null</code> 1814 * @throws java.io.IOException if initiation of discovery process results 1815 * in <code>IOException</code> when socket allocation occurs 1816 * @throws net.jini.config.ConfigurationException if an exception 1817 * occurs while retrieving an item from the given 1818 * <code>Configuration</code> object 1819 * 1820 * @throws java.lang.IllegalStateException if this method is called on 1821 * a terminated <code>JoinManager</code> instance. Note that this 1822 * exception is implementation-specific. 1823 * 1824 * @see net.jini.lookup.ServiceIDListener 1825 * @see net.jini.discovery.DiscoveryManagement 1826 * @see net.jini.discovery.LookupDiscoveryManager 1827 * @see net.jini.lease.LeaseRenewalManager 1828 * @see net.jini.config.Configuration 1829 * @see net.jini.config.ConfigurationException 1830 */ 1831 public JoinManager(Object serviceProxy, 1832 Entry[] attrSets, 1833 ServiceIDListener callback, 1834 DiscoveryManagement discoveryMgr, 1835 LeaseRenewalManager leaseMgr, 1836 Configuration config) 1837 throws IOException, ConfigurationException 1838 { 1839 1840 this(serviceProxy, attrSets, null, callback, 1841 getConfig(config, leaseMgr, discoveryMgr, serviceProxy) 1842 ); 1843 }//end constructor 1844 1845 /** 1846 * Constructs an instance of this class that will register the 1847 * service with all discovered lookup services, using the supplied 1848 * <code>ServiceID</code>. This constructor is typically used by 1849 * services which have already been assigned a service ID (possibly 1850 * by the service provider itself or as a result of a prior registration 1851 * with some lookup service), and which do not wish to allow for 1852 * deployment-time configuration of the service's join processing. 1853 * <p> 1854 * Except that the desired <code>ServiceID</code> is supplied through the 1855 * <code>serviceID</code> parameter rather than through a notification 1856 * sent to a <code>ServiceIDListener</code>, all other parameters 1857 * of this form of the constructor, along with their associated semantics, 1858 * are identical to that of the five-argument constructor that takes 1859 * a <code>ServiceIDListener</code>. 1860 * 1861 * @param serviceProxy a reference to the service requesting the services 1862 * of this class 1863 * @param attrSets array of <code>Entry</code> consisting of the 1864 * attribute sets with which to register the service 1865 * @param serviceID an instance of <code>ServiceID</code> with which to 1866 * register the service with all desired lookup 1867 * services 1868 * @param discoveryMgr reference to the <code>DiscoveryManagement</code> 1869 * object this class should use to manage the given 1870 * service's lookup service discovery duties 1871 * @param leaseMgr reference to the <code>LeaseRenewalManager</code> 1872 * object this class should use to manage the leases 1873 * on the given service's residency in the lookup 1874 * services that have been joined 1875 * 1876 * @throws java.lang.IllegalArgumentException if the object input to the 1877 * <code>serviceProxy</code> parameter is not serializable 1878 * @throws java.lang.NullPointerException if either <code>null</code> is 1879 * input to the <code>serviceProxy</code> parameter, or at least 1880 * one of the elements of the <code>attrSets</code> parameter is 1881 * <code>null</code> 1882 * @throws java.io.IOException if initiation of discovery process results 1883 * in <code>IOException</code> when socket allocation occurs 1884 * 1885 * @throws java.lang.IllegalStateException if this method is called on 1886 * a terminated <code>JoinManager</code> instance. Note that this 1887 * exception is implementation-specific. 1888 * 1889 * @see net.jini.core.lookup.ServiceID 1890 * @see net.jini.discovery.DiscoveryManagement 1891 * @see net.jini.discovery.LookupDiscoveryManager 1892 * @see net.jini.lease.LeaseRenewalManager 1893 */ 1894 public JoinManager(Object serviceProxy, 1895 Entry[] attrSets, 1896 ServiceID serviceID, 1897 DiscoveryManagement discoveryMgr, 1898 LeaseRenewalManager leaseMgr) throws IOException 1899 { 1900 this(serviceProxy, attrSets, serviceID, null, 1901 getConf(EmptyConfiguration.INSTANCE, leaseMgr, discoveryMgr, serviceProxy) 1902 ); 1903 }//end constructor 1904 1905 /** 1906 * Constructs an instance of this class, configured using the items 1907 * retrieved through the given <code>Configuration</code>, that will 1908 * register the service with all discovered lookup services, using the 1909 * supplied <code>ServiceID</code>. This constructor is typically used by 1910 * services which have already been assigned a service ID (possibly 1911 * by the service provider itself or as a result of a prior registration 1912 * with some lookup service), and which wish to allow for deployment-time 1913 * configuration of the service's join processing. 1914 * <p> 1915 * The items used to configure the current instance of this class 1916 * are obtained through the object input to the <code>config</code> 1917 * parameter. If <code>null</code> is input to that parameter, a 1918 * <code>NullPointerException</code> is thrown. 1919 * <p> 1920 * Except that the desired <code>ServiceID</code> is supplied through the 1921 * <code>serviceID</code> parameter rather than through a notification 1922 * sent to a <code>ServiceIDListener</code>, all other parameters 1923 * of this form of the constructor, along with their associated semantics, 1924 * are identical to that of the six-argument constructor that takes 1925 * a <code>ServiceIDListener</code>. 1926 * 1927 * @param serviceProxy a reference to the service requesting the services 1928 * of this class 1929 * @param attrSets array of <code>Entry</code> consisting of the 1930 * attribute sets with which to register the service. 1931 * @param serviceID an instance of <code>ServiceID</code> with which to 1932 * register the service with all desired lookup 1933 * services 1934 * @param discoveryMgr reference to the <code>DiscoveryManagement</code> 1935 * object this class should use to manage lookup 1936 * service discovery on behalf of the given service 1937 * @param leaseMgr reference to the <code>LeaseRenewalManager</code> 1938 * object this class should use to manage the leases 1939 * on the given service's residency in the lookup 1940 * services that have been joined 1941 * @param config instance of <code>Configuration</code> through 1942 * which the items used to configure the current 1943 * instance of this class are obtained 1944 * 1945 * @throws java.lang.IllegalArgumentException if the object input to the 1946 * <code>serviceProxy</code> parameter is not serializable 1947 * @throws java.lang.NullPointerException if <code>null</code> is input 1948 * to the <code>serviceProxy</code> parameter or the 1949 * <code>config</code> parameter, or if at least one of the 1950 * elements of the <code>attrSets</code> parameter is 1951 * <code>null</code> 1952 * @throws java.io.IOException if initiation of discovery process results 1953 * in <code>IOException</code> when socket allocation occurs 1954 * @throws net.jini.config.ConfigurationException if an exception 1955 * occurs while retrieving an item from the given 1956 * <code>Configuration</code> object 1957 * 1958 * @throws java.lang.IllegalStateException if this method is called on 1959 * a terminated <code>JoinManager</code> instance. Note that this 1960 * exception is implementation-specific. 1961 * 1962 * @see net.jini.core.lookup.ServiceID 1963 * @see net.jini.discovery.DiscoveryManagement 1964 * @see net.jini.discovery.LookupDiscoveryManager 1965 * @see net.jini.lease.LeaseRenewalManager 1966 * @see net.jini.config.Configuration 1967 * @see net.jini.config.ConfigurationException 1968 */ 1969 public JoinManager(Object serviceProxy, 1970 Entry[] attrSets, 1971 ServiceID serviceID, 1972 DiscoveryManagement discoveryMgr, 1973 LeaseRenewalManager leaseMgr, 1974 Configuration config) 1975 throws IOException, ConfigurationException 1976 { 1977 this(serviceProxy, attrSets, serviceID, null, 1978 getConfig(config, leaseMgr, discoveryMgr, serviceProxy) 1979 ); 1980 }//end constructor 1981 1982 /** 1983 * Returns the instance of <code>DiscoveryManagement</code> that was 1984 * either passed into the constructor, or that was created as a result 1985 * of <code>null</code> being input to that parameter. 1986 * <p> 1987 * The object returned by this method encapsulates the mechanism by which 1988 * either the <code>JoinManager</code> or the entity itself can set 1989 * discovery listeners and discard previously discovered lookup services 1990 * when they are found to be unavailable. 1991 * 1992 * @return the instance of the <code>DiscoveryManagement</code> interface 1993 * that was either passed into the constructor, or that was 1994 * created as a result of <code>null</code> being input to that 1995 * parameter. 1996 * 1997 * @see net.jini.discovery.DiscoveryManagement 1998 * @see net.jini.discovery.LookupDiscoveryManager 1999 */ 2000 public DiscoveryManagement getDiscoveryManager(){ 2001 if(bTerminated) 2002 throw new IllegalStateException("join manager was terminated"); 2003 return discMgr; 2004 }//end getDiscoveryManager 2005 2006 /** 2007 * Returns the instance of the <code>LeaseRenewalManager</code> class 2008 * that was either passed into the constructor, or that was created 2009 * as a result of <code>null</code> being input to that parameter. 2010 * <p> 2011 * The object returned by this method manages the leases requested and 2012 * held by the <code>JoinManager</code>. Although it may also manage 2013 * leases unrelated to the join process that are requested and held by 2014 * the service itself, the leases with which the <code>JoinManager</code> 2015 * is concerned are the leases that correspond to the service registration 2016 * requests made with each lookup service the service wishes to join. 2017 * 2018 * @return the instance of the <code>LeaseRenewalManager</code> class 2019 * that was either passed into the constructor, or that was 2020 * created as a result of <code>null</code> being input to that 2021 * parameter. 2022 * 2023 * @see net.jini.discovery.DiscoveryManagement 2024 * @see net.jini.lease.LeaseRenewalManager 2025 */ 2026 public LeaseRenewalManager getLeaseRenewalManager(){ 2027 if(bTerminated) 2028 throw new IllegalStateException("join manager was terminated"); 2029 return leaseRenewalMgr; 2030 }//end getLeaseRenewalManager 2031 2032 /** 2033 * Returns an array of <code>ServiceRegistrar</code> objects, each 2034 * corresponding to a lookup service with which the service is currently 2035 * registered (joined). If there are no lookup services with which the 2036 * service is currently registered, this method returns the empty array. 2037 * This method returns a new array upon each invocation. 2038 * 2039 * @return array of instances of <code>ServiceRegistrar</code>, each 2040 * corresponding to a lookup service with which the service is 2041 * currently registered 2042 * 2043 * @see net.jini.core.lookup.ServiceRegistrar 2044 */ 2045 public ServiceRegistrar[] getJoinSet() { 2046 if(bTerminated) throw new IllegalStateException("join manager was terminated"); 2047 List<ServiceRegistrar> retList = new LinkedList<ServiceRegistrar>(); 2048 for (Iterator<ProxyReg> iter = joinSet.iterator(); iter.hasNext(); ) { 2049 ProxyReg proxyReg = iter.next(); 2050 if(proxyReg.srvcRegistration != null) {//test registration flag 2051 retList.add(proxyReg.proxy); 2052 }//endif 2053 }//end loop 2054 return ( (retList.toArray(new ServiceRegistrar[retList.size()]) ) ); 2055 }//end getJoinSet 2056 2057 /** 2058 * Returns an array containing the set of attributes currently associated 2059 * with the service. If the service is not currently associated with an 2060 * attribute set, this method returns the empty array. This method returns 2061 * a new array upon each invocation. 2062 * 2063 * @return array of instances of <code>Entry</code> consisting of the 2064 * set of attributes with which the service is registered in 2065 * each lookup service that it has joined 2066 * 2067 * @see net.jini.core.entry.Entry 2068 * @see #setAttributes 2069 */ 2070 public Entry[] getAttributes() { 2071 if(bTerminated) throw new IllegalStateException("join manager was terminated"); 2072 Entry[] result = lookupAttr.clone(); 2073 for (int i = 0, l = result.length; i < l; i++){ 2074 if (result[i] instanceof CloneableEntry){ 2075 result[i] = ((CloneableEntry)result[i]).clone(); 2076 } 2077 } 2078 return result; 2079 }//end getAttributes 2080 2081 /** 2082 * Associates a new set of attributes with the service, in addition to 2083 * the service's current set of attributes. The association of this new 2084 * set of attributes with the service will be propagated to each lookup 2085 * service with which the service is registered. Note that this 2086 * propagation is performed asynchronously, thus there is no guarantee 2087 * that the propagation of the attributes to all lookup services with 2088 * which the service is registered will have completed upon return from 2089 * this method. 2090 * <p> 2091 * An invocation of this method with duplicate elements in the 2092 * <code>attrSets</code> parameter (where duplication means attribute 2093 * equality as defined by calling the <code>MarshalledObject.equals</code> 2094 * method on field values) is equivalent to performing the invocation 2095 * with the duplicates removed from that parameter. 2096 * <p> 2097 * Note that because there is no guarantee that attribute propagation 2098 * will have completed upon return from this method, services that 2099 * invoke this method must take care not to modify the contents of the 2100 * <code>attrSets</code> parameter. Doing so could cause the service's 2101 * attribute state to be corrupted or inconsistent on a subset of the 2102 * lookup services with which the service is registered as compared with 2103 * the state reflected on the remaining lookup services. It is for this 2104 * reason that the effects of modifying the contents of the 2105 * <code>attrSets</code> parameter, after this method is invoked, are 2106 * undefined. 2107 * 2108 * @param attrSets array of <code>Entry</code> consisting of the 2109 * attribute sets with which to augment the service's 2110 * current set of attributes 2111 * 2112 * @throws java.lang.NullPointerException if either <code>null</code> is 2113 * input to the <code>attrSets</code> parameter, or one or more 2114 * of the elements of the <code>attrSets</code> parameter is 2115 * <code>null</code> 2116 * 2117 * @see net.jini.core.entry.Entry 2118 */ 2119 public void addAttributes(Entry[] attrSets) { 2120 addAttributes(attrSets, false); 2121 }//end addAttributes 2122 2123 /** 2124 * Associates a new set of attributes with the service, in addition to 2125 * the service's current set of attributes. The association of this new 2126 * set of attributes with the service will be propagated to each lookup 2127 * service with which the service is registered. Note that this 2128 * propagation is performed asynchronously, thus there is no guarantee 2129 * that the propagation of the attributes to all lookup services with 2130 * which the service is registered will have completed upon return from 2131 * this method. 2132 * <p> 2133 * An invocation of this method with duplicate elements in the 2134 * <code>attrSets</code> parameter (where duplication means attribute 2135 * equality as defined by calling the <code>MarshalledObject.equals</code> 2136 * method on field values) is equivalent to performing the invocation 2137 * with the duplicates removed from that parameter. 2138 * <p> 2139 * Note that because there is no guarantee that attribute propagation 2140 * will have completed upon return from this method, services that 2141 * invoke this method must take care not to modify the contents of the 2142 * <code>attrSets</code> parameter. Doing so could cause the service's 2143 * attribute state to be corrupted or inconsistent on a subset of the 2144 * lookup services with which the service is registered as compared with 2145 * the state reflected on the remaining lookup services. It is for this 2146 * reason that the effects of modifying the contents of the 2147 * <code>attrSets</code> parameter, after this method is invoked, are 2148 * undefined. 2149 * <p> 2150 * A service typically employs this version of <code>addAttributes</code> 2151 * to prevent clients or other services from attempting to add what are 2152 * referred to as "service controlled attributes" to the service's set. 2153 * A service controlled attribute is an attribute that implements the 2154 * <code>ServiceControlled</code> marker interface. 2155 * <p> 2156 * Consider a printer service. With printers, there are often times error 2157 * conditions, that only the printer can detect (for example, a paper 2158 * jam or a toner low condition). To report conditions such as these to 2159 * interested parties, the printer typically adds an attribute to its 2160 * attribute set, resulting in an event being sent that notifies clients 2161 * that have registered interest in such events. When the condition is 2162 * corrected, the printer would then remove the attribute from its set 2163 * by invoking the <code>modifyAttributes</code> method in the appropriate 2164 * manner. 2165 * <p> 2166 * Attributes representing conditions that only the service can know about 2167 * or control are good candidates for being defined as service controlled 2168 * attributes. That is, the service provider (the developer of the printer 2169 * service for example) would define the attributes that represent 2170 * conditions such as those just described to implement the 2171 * <code>ServiceControlled</code> marker interface. Thus, when other 2172 * entities attempt to add new attributes, services that wish to employ 2173 * such service controlled attributes should ultimately invoke only this 2174 * version of <code>addAttributes</code> (with the <code>checkSC</code> 2175 * parameter set to <code>true</code>), resulting in a 2176 * <code>SecurityException</code> if any of the attributes being added 2177 * happen to be service controlled attributes. In this way, only the 2178 * printer itself would be able to set a "paper jammed" or "toner low" 2179 * attribute, not some arbitrary client. 2180 * 2181 * @param attrSets array of <code>Entry</code> consisting of the 2182 * attribute sets with which to augment the service's 2183 * current set of attributes 2184 * @param checkSC <code>boolean</code> flag indicating whether the 2185 * elements of the set of attributes to add should be 2186 * checked to determine if they are service controlled 2187 * attributes 2188 * 2189 * @throws java.lang.NullPointerException if either <code>null</code> is 2190 * input to the <code>attrSets</code> parameter, or one or more 2191 * of the elements of the <code>attrSets</code> parameter is 2192 * <code>null</code> 2193 * 2194 * @throws java.lang.SecurityException if the <code>checkSC</code> 2195 * parameter is <code>true</code>, and at least one of the 2196 * attributes to be added is an instance of the 2197 * <code>ServiceControlled</code> marker interface 2198 * 2199 * @see net.jini.core.entry.Entry 2200 * @see net.jini.lookup.entry.ServiceControlled 2201 */ 2202 public void addAttributes(Entry[] attrSets, boolean checkSC) { 2203 if(bTerminated) throw new IllegalStateException("join manager was terminated"); 2204 synchronized(this) { 2205 lookupAttr = LookupAttributes.add(lookupAttr, attrSets, checkSC); 2206 serviceItem = new ServiceItem(serviceItem.serviceID, serviceItem.service, lookupAttr); 2207 } 2208 Iterator<ProxyReg> it = joinSet.iterator(); 2209 while (it.hasNext()){ 2210 ProxyReg proxyReg = it.next(); 2211 proxyReg.addTask(new AddAttributesTask(proxyReg,attrSets)); 2212 }//end loop 2213 }//end addAttributes 2214 2215 /** 2216 * Replaces the service's current set of attributes with a new set of 2217 * attributes. The association of this new set of attributes with the 2218 * service will be propagated to each lookup service with which the 2219 * service is registered. Note that this propagation is performed 2220 * asynchronously, thus there is no guarantee that the propagation of 2221 * the attributes to all lookup services with which the service is 2222 * registered will have completed upon return from this method. 2223 * <p> 2224 * An invocation of this method with duplicate elements in the 2225 * <code>attrSets</code> parameter (where duplication means attribute 2226 * equality as defined by calling the <code>MarshalledObject.equals</code> 2227 * method on field values) is equivalent to performing the invocation 2228 * with the duplicates removed from that parameter. 2229 * <p> 2230 * Note that because there is no guarantee that attribute propagation 2231 * will have completed upon return from this method, services that 2232 * invoke this method must take care not to modify the contents of the 2233 * <code>attrSets</code> parameter. Doing so could cause the service's 2234 * attribute state to be corrupted or inconsistent on a subset of the 2235 * lookup services with which the service is registered as compared with 2236 * the state reflected on the remaining lookup services. It is for this 2237 * reason that the effects of modifying the contents of the 2238 * <code>attrSets</code> parameter, after this method is invoked, are 2239 * undefined. 2240 * 2241 * @param attrSets array of <code>Entry</code> consisting of the 2242 * attribute sets with which to replace the service's 2243 * current set of attributes 2244 * 2245 * @throws java.lang.NullPointerException if either <code>null</code> is 2246 * input to the <code>attrSets</code> parameter, or one or more 2247 * of the elements of the <code>attrSets</code> parameter is 2248 * <code>null</code>. 2249 * 2250 * @see net.jini.core.entry.Entry 2251 * @see #getAttributes 2252 */ 2253 public void setAttributes(Entry[] attrSets) { 2254 if(bTerminated) 2255 throw new IllegalStateException("join manager was terminated"); 2256 testForNullElement(attrSets); 2257 synchronized(this) { 2258 lookupAttr = (Entry[]) attrSets.clone(); 2259 serviceItem = new ServiceItem(serviceItem.serviceID, 2260 serviceItem.service, lookupAttr); 2261 } 2262 Iterator<ProxyReg> it = joinSet.iterator(); 2263 while (it.hasNext()){ 2264 ProxyReg proxyReg = it.next(); 2265 proxyReg.addTask(new SetAttributesTask(proxyReg,attrSets)); 2266 } 2267 }//end setAttributes 2268 2269 /** 2270 * Changes the service's current set of attributes using the same 2271 * semantics as the <code>modifyAttributes</code> method of the 2272 * <code>ServiceRegistration</code> class. 2273 * <p> 2274 * The association of the new set of attributes with the service will 2275 * be propagated to each lookup service with which the service is 2276 * registered. Note that this propagation is performed asynchronously, 2277 * thus there is no guarantee that the propagation of the attributes to 2278 * all lookup services with which the service is registered will have 2279 * completed upon return from this method. 2280 * <p> 2281 * Note that if the length of the array containing the templates does 2282 * not equal the length of the array containing the modifications, an 2283 * <code>IllegalArgumentException</code> will be thrown and propagated 2284 * through this method. 2285 * <p> 2286 * Note also that because there is no guarantee that attribute propagation 2287 * will have completed upon return from this method, services that 2288 * invoke this method must take care not to modify the contents of the 2289 * <code>attrSets</code> parameter. Doing so could cause the service's 2290 * attribute state to be corrupted or inconsistent on a subset of the 2291 * lookup services with which the service is registered as compared with 2292 * the state reflected on the remaining lookup services. It is for this 2293 * reason that the effects of modifying the contents of the 2294 * <code>attrSets</code> parameter, after this method is invoked, are 2295 * undefined. 2296 * 2297 * @param attrSetTemplates array of <code>Entry</code> used to identify 2298 * which elements to modify from the service's 2299 * current set of attributes 2300 * @param attrSets array of <code>Entry</code> containing the 2301 * actual modifications to make in the matching 2302 * sets found using the 2303 * <code>attrSetTemplates</code> parameter 2304 * 2305 * @throws java.lang.IllegalArgumentException if the array containing the 2306 * templates does not equal the length of the array containing the 2307 * modifications 2308 * 2309 * @see net.jini.core.entry.Entry 2310 * @see net.jini.core.lookup.ServiceRegistration#modifyAttributes 2311 */ 2312 public void modifyAttributes(Entry[] attrSetTemplates, Entry[] attrSets) { 2313 modifyAttributes(attrSetTemplates, attrSets, false); 2314 }//end modifyAttributes 2315 2316 /** 2317 * Changes the service's current set of attributes using the same 2318 * semantics as the <code>modifyAttributes</code> method of the 2319 * <code>ServiceRegistration</code> class. 2320 * <p> 2321 * The association of the new set of attributes with the service will 2322 * be propagated to each lookup service with which the service is 2323 * registered. Note that this propagation is performed asynchronously, 2324 * thus there is no guarantee that the propagation of the attributes to 2325 * all lookup services with which the service is registered will have 2326 * completed upon return from this method. 2327 * <p> 2328 * Note that if the length of the array containing the templates does 2329 * not equal the length of the array containing the modifications, an 2330 * <code>IllegalArgumentException</code> will be thrown and propagated 2331 * through this method. 2332 * <p> 2333 * Note also that because there is no guarantee that attribute propagation 2334 * will have completed upon return from this method, services that 2335 * invoke this method must take care not to modify the contents of the 2336 * <code>attrSets</code> parameter. Doing so could cause the service's 2337 * attribute state to be corrupted or inconsistent on a subset of the 2338 * lookup services with which the service is registered as compared with 2339 * the state reflected on the remaining lookup services. It is for this 2340 * reason that the effects of modifying the contents of the 2341 * <code>attrSets</code> parameter, after this method is invoked, are 2342 * undefined. 2343 * <p> 2344 * A service typically employs this version of 2345 * <code>modifyAttributes</code> to prevent clients or other services 2346 * from attempting to modify what are referred to as "service controlled 2347 * attributes" in the service's set. A service controlled attribute is an 2348 * attribute that implements the <code>ServiceControlled</code> marker 2349 * interface. 2350 * <p> 2351 * Attributes representing conditions that only the service can know about 2352 * or control are good candidates for being defined as service controlled 2353 * attributes. When other entities attempt to modify a service's 2354 * attributes, if the service wishes to employ such service controlled 2355 * attributes, the service should ultimately invoke only this version 2356 * of <code>modifyAttributes</code> (with the <code>checkSC</code> 2357 * parameter set to <code>true</code>), resulting in a 2358 * <code>SecurityException</code> if any of the attributes being modified 2359 * happen to be service controlled attributes. 2360 * 2361 * @param attrSetTemplates array of <code>Entry</code> used to identify 2362 * which elements to modify from the service's 2363 * current set of attributes 2364 * @param attrSets array of <code>Entry</code> containing the 2365 * actual modifications to make in the matching 2366 * sets found using the 2367 * <code>attrSetTemplates</code> parameter 2368 * @param checkSC <code>boolean</code> flag indicating whether the 2369 * elements of the set of attributes to modify 2370 * should be checked to determine if they are 2371 * service controlled attributes 2372 * 2373 * @throws java.lang.IllegalArgumentException if the array containing the 2374 * templates does not equal the length of the array containing 2375 * the modifications 2376 * 2377 * @throws java.lang.SecurityException if the <code>checkSC</code> 2378 * parameter is <code>true</code>, and at least one of the 2379 * attributes to be modified is an instance of the 2380 * <code>ServiceControlled</code> marker interface 2381 * 2382 * @see net.jini.core.entry.Entry 2383 * @see net.jini.core.lookup.ServiceRegistration#modifyAttributes 2384 * @see net.jini.lookup.entry.ServiceControlled 2385 */ 2386 public void modifyAttributes(Entry[] attrSetTemplates, 2387 Entry[] attrSets, 2388 boolean checkSC) 2389 { 2390 if(bTerminated) 2391 throw new IllegalStateException("join manager was terminated"); 2392 synchronized(this) { 2393 lookupAttr = LookupAttributes.modify(lookupAttr, attrSetTemplates, 2394 attrSets, checkSC); 2395 serviceItem = new ServiceItem(serviceItem.serviceID, 2396 serviceItem.service, lookupAttr); 2397 }//end sync 2398 Iterator<ProxyReg> it = joinSet.iterator(); 2399 while (it.hasNext()){ 2400 ProxyReg proxyReg = it.next(); 2401 proxyReg.addTask(new ModifyAttributesTask(proxyReg, 2402 attrSetTemplates, 2403 attrSets)); 2404 }//end loop 2405 }//end modifyAttributes 2406 2407 /** 2408 * Performs cleanup duties related to the termination of the lookup 2409 * service discovery event mechanism, as well as the lease and 2410 * thread management performed by the <code>JoinManager</code>. This 2411 * method will cancel all of the service's managed leases that were 2412 * granted by the lookup services with which the service is registered, 2413 * and will terminate all threads that have been created. 2414 * <p> 2415 * Note that if the discovery manager employed by the instance of this 2416 * class that is being terminated was created by the instance itself, 2417 * this method will terminate all discovery processing being performed by 2418 * that manager object on behalf of the service; otherwise, the discovery 2419 * manager supplied by the service is still valid. 2420 * <p> 2421 * Whether an instance of the <code>LeaseRenewalManager</code> class was 2422 * supplied by the service or created by the <code>JoinManager</code> 2423 * itself, any reference to that object obtained by the service prior to 2424 * termination will still be valid after termination. 2425 * Note also this class makes certain concurrency guarantees with respect 2426 * to an invocation of the terminate method while other method invocations 2427 * are in progress. The termination process will not begin until 2428 * completion of all invocations of the methods defined in the public 2429 * interface of this class. Furthermore, once the termination process has 2430 * begun, no further remote method invocations will be made by this class, 2431 * and all other method invocations made on this class will not return 2432 * until the termination process has completed. 2433 * <p> 2434 * Upon completion of the termination process, the semantics of all 2435 * current and future method invocations on the instance of this class 2436 * that was just terminated are undefined; although the reference to the 2437 * <code>LeaseRenewalManager</code> object employed by that instance 2438 * of <code>JoinManager</code> is still valid. 2439 */ 2440 public void terminate() { 2441 synchronized(this) { 2442 if(bTerminated) return;//allow for multiple terminations 2443 bTerminated = true; 2444 }//end sync(this) 2445 /* Terminate discovery and task management */ 2446 discMgr.removeDiscoveryListener(discMgrListener); 2447 if(bCreateDiscMgr) discMgr.terminate(); 2448 terminateTaskMgr(); 2449 /* Clear the joinSet and cancel all leases held by the service */ 2450 Iterator<ProxyReg> iter = joinSet.iterator(); 2451 while (iter.hasNext()) { 2452 try { 2453 leaseRenewalMgr.cancel(iter.next().serviceLease ); 2454 } catch (UnknownLeaseException e){ 2455 } catch (RemoteException e) {} 2456 }//end loop 2457 leaseRenewalMgr.close(); 2458 joinSet.clear(); 2459 }//end terminate 2460 2461 /** 2462 * Registers a new reference to the service with all current and future 2463 * discovered lookup services. The new service reference will replace 2464 * the reference that was previously registered as a result of either 2465 * constructing this utility, or a prior invocation of one of the forms 2466 * of this method. The new service reference will be registered using 2467 * the same <code>ServiceID</code> with which previous registrations 2468 * were made through this utility. 2469 * <p> 2470 * The value input to the <code>serviceProxy</code> parameter represents 2471 * the new service reference (proxy) to register with each discovered 2472 * lookup service. If the <code>Object</code> input to that parameter is 2473 * not <code>Serializable</code>, an <code>IllegalArgumentException</code> 2474 * is thrown. If <code>null</code> is input to that parameter, a 2475 * <code>NullPointerException</code> is thrown. 2476 * <p> 2477 * The attribute sets that this method associates with the new service 2478 * reference are the same attribute sets as those associated with the 2479 * old registration. 2480 * 2481 * @param serviceProxy the new service reference (proxy) to register with 2482 * all current and future discovered lookup services 2483 * 2484 * @throws java.lang.IllegalArgumentException if the object input to the 2485 * <code>serviceProxy</code> parameter is not serializable 2486 * @throws java.lang.NullPointerException if <code>null</code> is input 2487 * to the <code>serviceProxy</code> parameter 2488 * 2489 * @throws java.lang.IllegalStateException if this method is called on 2490 * a terminated <code>JoinManager</code> instance. Note that this 2491 * exception is implementation-specific. 2492 */ 2493 public void replaceRegistration(Object serviceProxy) { 2494 replaceRegistrationDo(serviceProxy, null, false); 2495 }//end replaceRegistration 2496 2497 /** 2498 * Registers a new reference to the service with all current and future 2499 * discovered lookup services, applying semantics identical to the 2500 * one-argument form of this method, except with respect to the 2501 * registration of the given attribute sets. 2502 * <p> 2503 * This form of <code>replaceRegistration</code> takes as its 2504 * second parameter, an array of <code>Entry</code> objects 2505 * (<code>attrSets</code>), none of whose elements may be 2506 * <code>null</code>, that represents the new set of attributes to 2507 * associate with the new service reference to be registered. As with 2508 * the constructor to this utility, passing <code>null</code> as the 2509 * value of the <code>attrSets</code> parameter is equivalent to passing 2510 * an empty array. If any of the elements of <code>attrSets</code> are 2511 * <code>null</code>, a <code>NullPointerException</code> is thrown. 2512 * This new set of attributes will be associated with the service in 2513 * all future join processing. 2514 * 2515 * @param serviceProxy the new service reference (proxy) to register with 2516 * all current and future discovered lookup services 2517 * @param attrSets array of <code>Entry</code> consisting of the 2518 * attribute sets with which to register the new 2519 * service reference. Passing <code>null</code> as 2520 * the value of this parameter is equivalent to 2521 * passing an empty <code>Entry</code> array 2522 * 2523 * @throws java.lang.IllegalArgumentException if the object input to the 2524 * <code>serviceProxy</code> parameter is not serializable 2525 * @throws java.lang.NullPointerException if either <code>null</code> is 2526 * input to the <code>serviceProxy</code> parameter, or at least 2527 * one of the elements of the <code>attrSets</code> parameter is 2528 * <code>null</code> 2529 * 2530 * @throws java.lang.IllegalStateException if this method is called on 2531 * a terminated <code>JoinManager</code> instance. Note that this 2532 * exception is implementation-specific. 2533 */ 2534 public void replaceRegistration(Object serviceProxy, Entry[] attrSets) { 2535 replaceRegistrationDo(serviceProxy, attrSets, true); 2536 }//end replaceRegistration 2537 2538 private static class Conf{ 2539 ProxyPreparer registrarPreparer; 2540 ProxyPreparer registrationPreparer; 2541 ProxyPreparer serviceLeasePreparer; 2542 ExecutorService executorService; 2543 WakeupManager wakeupManager; 2544 Integer maxNretrys; 2545 LeaseRenewalManager leaseRenewalManager; 2546 Long renewalDuration; 2547 DiscoveryManagement discoveryMgr; 2548 boolean bcreateDisco; 2549 2550 Conf ( ProxyPreparer registrarPreparer, 2551 ProxyPreparer registrationPreparer, 2552 ProxyPreparer serviceLeasePreparer, 2553 ExecutorService taskManager, 2554 WakeupManager wakeupManager, 2555 Integer maxNretrys, 2556 LeaseRenewalManager leaseRenewalManager, 2557 Long renewalDuration, 2558 DiscoveryManagement discoveryMgr, 2559 boolean bcreateDisco) 2560 { 2561 this.registrarPreparer = registrarPreparer; 2562 this.registrationPreparer = registrationPreparer; 2563 this.serviceLeasePreparer = serviceLeasePreparer; 2564 this.executorService = taskManager; 2565 this.wakeupManager = wakeupManager; 2566 this.maxNretrys = maxNretrys; 2567 this.leaseRenewalManager = leaseRenewalManager; 2568 this.renewalDuration = renewalDuration; 2569 this.discoveryMgr = discoveryMgr; 2570 this.bcreateDisco = bcreateDisco; 2571 } 2572 } 2573 2574 /** 2575 * This method is for constructors that use an empty configuration. 2576 * 2577 * @param config 2578 * @param leaseMgr 2579 * @param discoveryMgr 2580 * @param serviceProxy 2581 * @return Conf 2582 * @throws IOException 2583 * @throws NullPointerException 2584 * @throws IllegalArgumentException 2585 */ 2586 private static Conf getConf( Configuration config, 2587 LeaseRenewalManager leaseMgr, 2588 DiscoveryManagement discoveryMgr, 2589 Object serviceProxy) 2590 throws IOException, NullPointerException, IllegalArgumentException { 2591 try { 2592 return getConfig(config, leaseMgr, discoveryMgr, serviceProxy); 2593 } catch (ConfigurationException e){ 2594 throw new IOException("Configuration problem during construction", e); 2595 } 2596 } 2597 2598 /** 2599 * Gets the configuration and throws any exceptions. 2600 * 2601 * This static method guards against finalizer attacks and allows fields 2602 * to be final. 2603 * 2604 * @param config 2605 * @param leaseMgr 2606 * @param discoveryMgr 2607 * @param serviceProxy 2608 * @return Conf 2609 * @throws IOException 2610 * @throws ConfigurationException 2611 * @throws NullPointerException 2612 * @throws IllegalArgumentException 2613 */ 2614 private static Conf getConfig( Configuration config, 2615 LeaseRenewalManager leaseMgr, 2616 DiscoveryManagement discoveryMgr, 2617 Object serviceProxy) 2618 throws IOException, ConfigurationException, NullPointerException, 2619 IllegalArgumentException 2620 { 2621 if(!(serviceProxy instanceof java.io.Serializable)) { 2622 throw new IllegalArgumentException 2623 ("serviceProxy must be Serializable"); 2624 }//endif 2625 /* Retrieve configuration items if applicable */ 2626 if(config == null) throw new NullPointerException("config is null"); 2627 /* Proxy preparers */ 2628 ProxyPreparer registrarPreparer = config.getEntry 2629 (COMPONENT_NAME, 2630 "registrarPreparer", 2631 ProxyPreparer.class, 2632 new BasicProxyPreparer()); 2633 ProxyPreparer registrationPreparer = config.getEntry 2634 (COMPONENT_NAME, 2635 "registrationPreparer", 2636 ProxyPreparer.class, 2637 new BasicProxyPreparer()); 2638 ProxyPreparer serviceLeasePreparer = config.getEntry 2639 (COMPONENT_NAME, 2640 "serviceLeasePreparer", 2641 ProxyPreparer.class, 2642 new BasicProxyPreparer()); 2643 /* Task manager */ 2644 ExecutorService taskMgr; 2645 try { 2646 taskMgr = config.getEntry(COMPONENT_NAME, 2647 "executorService", 2648 ExecutorService.class); 2649 } catch(NoSuchEntryException e) { /* use default */ 2650 taskMgr = new ThreadPoolExecutor( 2651 MAX_N_TASKS, 2652 MAX_N_TASKS, /* Ignored */ 2653 15, 2654 TimeUnit.SECONDS, 2655 new LinkedBlockingQueue(), /* Unbounded Queue */ 2656 new NamedThreadFactory("JoinManager executor thread", false) 2657 ); 2658 } 2659 /* Wakeup manager */ 2660 WakeupManager wakeupMgr; 2661 try { 2662 wakeupMgr = config.getEntry(COMPONENT_NAME, 2663 "wakeupManager", 2664 WakeupManager.class); 2665 } catch(NoSuchEntryException e) { /* use default */ 2666 wakeupMgr = new WakeupManager 2667 (new WakeupManager.ThreadDesc(null,true)); 2668 } 2669 /* Max number of times to re-schedule tasks in thru wakeup manager */ 2670 int maxNRetries = (config.getEntry 2671 (COMPONENT_NAME, 2672 "wakeupRetries", 2673 int.class, 2674 Integer.valueOf(6))).intValue(); 2675 /* Lease renewal manager */ 2676 if(leaseMgr == null) { 2677 try { 2678 leaseMgr = config.getEntry 2679 (COMPONENT_NAME, 2680 "leaseManager", 2681 LeaseRenewalManager.class); 2682 } catch(NoSuchEntryException e) { /* use default */ 2683 leaseMgr = new LeaseRenewalManager(config); 2684 } 2685 }//endif 2686 long renewalDuration = (config.getEntry 2687 (COMPONENT_NAME, 2688 "maxLeaseDuration", 2689 long.class, 2690 Long.valueOf(Lease.FOREVER))).longValue(); 2691 if( (renewalDuration == 0) || (renewalDuration < Lease.ANY) ) { 2692 throw new ConfigurationException("invalid configuration entry: " 2693 +"renewalDuration (" 2694 +renewalDuration+") must be " 2695 +"positive or Lease.ANY"); 2696 }//endif 2697 /* Discovery manager */ 2698 boolean bCreateDiscMgr = false; 2699 if(discoveryMgr == null) { 2700 bCreateDiscMgr = true; 2701 try { 2702 discoveryMgr = config.getEntry 2703 (COMPONENT_NAME, 2704 "discoveryManager", 2705 DiscoveryManagement.class); 2706 } catch(NoSuchEntryException e) { /* use default */ 2707 discoveryMgr = new LookupDiscoveryManager 2708 (new String[] {""}, null, null, config); 2709 } 2710 }//endif 2711 return new Conf(registrarPreparer, registrationPreparer, serviceLeasePreparer, 2712 taskMgr, wakeupMgr, maxNRetries, leaseMgr, renewalDuration, 2713 discoveryMgr, bCreateDiscMgr); 2714 } 2715 2716 /** Convenience method invoked by the constructors of this class that 2717 * uses the given <code>Configuration</code> to initialize the current 2718 * instance of this utility, and initiates all join processing for 2719 * the given parameters. This method handles the various configurations 2720 * allowed by the different constructors. 2721 */ 2722 private JoinManager(Object serviceProxy, 2723 Entry[] attrSets, ServiceID serviceID, 2724 ServiceIDListener callback, Conf conf) 2725 { 2726 registrarPreparer = conf.registrarPreparer; 2727 registrationPreparer = conf.registrationPreparer; 2728 serviceLeasePreparer = conf.serviceLeasePreparer; 2729 executor = new ExtensibleExecutorService(conf.executorService, 2730 new RunnableFutureFactory(){ 2731 2732 @Override 2733 public <T> RunnableFuture<T> newTaskFor(Runnable r, T value) { 2734 if (r instanceof ProxyRegTask) return (RunnableFuture<T>) r; 2735 throw new IllegalStateException("Runnable not instance of ProxyRegTask"); 2736 } 2737 2738 @Override 2739 public <T> RunnableFuture<T> newTaskFor(Callable<T> c) { 2740 if (c instanceof ProxyRegTask) return (RunnableFuture<T>) c; 2741 throw new IllegalStateException("Callable not instance of ProxyRegTask"); 2742 } 2743 2744 }); 2745 proxyRegTaskQueue = new ProxyRegTaskQueue(executor); 2746 wakeupMgr = conf.wakeupManager; 2747 maxNRetries = conf.maxNretrys; 2748 leaseRenewalMgr = conf.leaseRenewalManager; 2749 renewalDuration = conf.renewalDuration; 2750 bCreateDiscMgr = conf.bcreateDisco; 2751 DiscMgrListener discMgrListen = new DiscMgrListener(); 2752 if(attrSets == null) { 2753 lookupAttr = new Entry[0]; 2754 } else { 2755 attrSets = attrSets.clone(); 2756 LookupAttributes.check(attrSets,false);//null elements NOT ok 2757 lookupAttr = attrSets; 2758 }//endif 2759 serviceItem = new ServiceItem(serviceID, serviceProxy, lookupAttr); 2760 this.callback = callback; 2761 conf.discoveryMgr.addDiscoveryListener(discMgrListen); 2762 discMgr = conf.discoveryMgr; 2763 discMgrListener = discMgrListen; 2764 }//end createJoinManager 2765 2766 /** For the given lookup service proxy, searches the <code>joinSet</code> 2767 * for the corresponding <code>ProxyReg</code> element, and upon finding 2768 * such an element, returns that element; otherwise returns 2769 * <code>null</code>. 2770 */ 2771 private ProxyReg findReg(ServiceRegistrar proxy) { 2772 for (Iterator iter = joinSet.iterator(); iter.hasNext(); ) { 2773 ProxyReg reg =(ProxyReg)iter.next(); 2774 if(reg.proxy.equals(proxy)) return reg; 2775 }//end loop 2776 return null; 2777 }//end findReg 2778 2779 /** Removes (from the task manager) and cancels (in the wakeup manager) 2780 * all tasks associated with the given instance of <code>ProxyReg</code>. 2781 */ 2782 private void removeTasks(ProxyReg proxyReg) { 2783 if(proxyReg == null) return; 2784 if(executor == null) return; 2785 synchronized(proxyReg.taskList) { 2786 if(proxyReg.proxyRegTask != null) { 2787 proxyReg.proxyRegTask.cancel(false); 2788 proxyReg.proxyRegTask = null; //don't reuse because of seq# 2789 }//endif 2790 proxyReg.taskList.clear(); 2791 }//end sync(proxyReg.taskList) 2792 proxyReg.terminate(); 2793 }//end removeTasks 2794 2795 /** Removes from the task manager, all pending tasks regardless of the 2796 * the instance of <code>ProxyReg</code> with which the task is 2797 * associated, and then terminates the task manager, and makes it 2798 * a candidate for garbage collection. 2799 */ 2800 private void terminateTaskMgr() { 2801 synchronized(wakeupMgr) { 2802 /* Cancel all tasks scheduled for future retry by the wakeup mgr */ 2803 wakeupMgr.cancelAll();//cancel all tickets 2804 wakeupMgr.stop();//stop execution of the wakeup manager 2805 } 2806 /* Interrupt all active tasks, prepare taskMgr for GC. */ 2807 executor.shutdownNow(); 2808 }//end terminateTaskMgr 2809 2810 /** Examines the elements of the input set and, upon finding at least one 2811 * <code>null</code> element, throws a <code>NullPointerException</code>. 2812 */ 2813 private void testForNullElement(Object[] a) { 2814 if(a == null) return; 2815 int l = a.length; 2816 for(int i=0;i<l;i++) { 2817 if(a[i] == null) { 2818 throw new NullPointerException 2819 ("input array contains at least one null element"); 2820 }//endif 2821 }//end loop 2822 }//end testForNullElement 2823 2824 /** Convenience method invoked by either form of the method 2825 * <code>replaceRegistration</code>. This method registers the 2826 * given <code>serviceProxy</code> with all discovered lookup 2827 * services, replacing all current registrations. If the value 2828 * of the <code>doAttrs</code> parameter is <code>true</code>, 2829 * this method will associate the given <code>attrSets</code> 2830 * with the new service registration; otherwise, it will use 2831 * the attribute sets currently associated with the old registration. 2832 */ 2833 private void replaceRegistrationDo(Object serviceProxy, 2834 Entry[] attrSets, 2835 boolean doAttrs) 2836 { 2837 if(bTerminated) 2838 throw new IllegalStateException("join manager was terminated"); 2839 if(!(serviceProxy instanceof java.io.Serializable)) { 2840 throw new IllegalArgumentException 2841 ("serviceProxy must be Serializable"); 2842 }//endif 2843 synchronized(this) { 2844 if(doAttrs) { 2845 if(attrSets == null) { 2846 lookupAttr = new Entry[0]; 2847 } else { 2848 attrSets = (Entry[])attrSets.clone(); 2849 LookupAttributes.check(attrSets,false);//no null elements 2850 lookupAttr = attrSets; 2851 }//endif 2852 }//endif 2853 serviceItem = new ServiceItem(serviceItem.serviceID, serviceProxy, lookupAttr); 2854 } 2855 Iterator<ProxyReg> it = joinSet.iterator(); 2856 while (it.hasNext()){ 2857 ProxyReg proxyReg = it.next(); 2858 removeTasks(proxyReg); 2859 try { 2860 leaseRenewalMgr.remove( proxyReg.serviceLease ); 2861 } catch (UnknownLeaseException e) { } 2862 proxyReg.addTask(new RegisterTask(proxyReg, 2863 (Entry[])lookupAttr.clone())); 2864 }//end loop 2865 }//end replaceRegistrationDo 2866 2867 }//end class JoinManager 2868