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