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