/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.jini.lookup;
import com.sun.jini.constants.ThrowableConstants;
import com.sun.jini.lookup.entry.LookupAttributes;
import com.sun.jini.thread.RetryTask;
import com.sun.jini.thread.TaskManager;
import com.sun.jini.thread.WakeupManager;
import com.sun.jini.logging.LogUtil;
import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.config.EmptyConfiguration;
import net.jini.config.NoSuchEntryException;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryManagement;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lease.LeaseListener;
import net.jini.lease.LeaseRenewalEvent;
import net.jini.lease.LeaseRenewalManager;
import net.jini.security.BasicProxyPreparer;
import net.jini.security.ProxyPreparer;
import net.jini.core.entry.Entry;
import net.jini.core.lease.Lease;
import net.jini.core.lease.UnknownLeaseException;
import net.jini.core.lookup.ServiceID;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceRegistration;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A goal of any well-behaved service is to advertise the facilities and
* functions it provides by requesting residency within at least one lookup
* service. Making such a request of a lookup service is known as registering
* with, or <i>joining</i>, a lookup service. To demonstrate this good
* behavior, a service must comply with both the multicast discovery protocol
* and the unicast discovery protocol in order to discover the lookup services
* it is interested in joining. The service must also comply with the join
* protocol to register with the desired lookup services.
* <p>
* In order for a service to maintain its residency in the lookup services
* it has joined, the service must provide for the coordination, systematic
* renewal, and overall management of all leases on that residency. In
* addition to handling all discovery and join duties, as well as managing
* all leases on lookup service residency, the service must also provide
* for the coordination and management of any attribute sets with which
* it may have registered with the lookup services in which it resides.
* <p>
* This class performs all of the functions related to discovery, joining,
* service lease renewal, and attribute management which is required of a
* well-behaved service. Each of these activities is intimately involved
* with the maintenance of a service's residency in one or more lookup
* services (the service's join state), thus the name <code>JoinManager</code>.
* <p>
* This class should be employed by services, not clients. The use of this
* class in a wide variety of services can help minimize the work resulting
* from having to repeatedly implement this required functionality in each
* service. Note that this class is not remote. Services that wish to use
* this class will create an instance of this class in the service's address
* space to manage the entity's join state locally.
*
* @com.sun.jini.impl <!-- Implementation Specifics -->
*
* The following implementation-specific items are discussed below:
* <ul><li> <a href="#jmConfigEntries">Configuring JoinManager</a>
* <li> <a href="#jmLogging">Logging</a>
* </ul>
*
* <a name="jmConfigEntries">
* <p>
* <b><font size="+1">Configuring JoinManager</font></b>
* <p>
* </a>
*
* This implementation of <code>JoinManager</code> supports the following
* configuration entries; where each configuration entry name is associated
* with the component name <code>net.jini.lookup.JoinManager</code>. Note
* that the configuration entries specified here are specific to this
* implementation of <code>JoinManager</code>. Unless otherwise stated, each
* entry is retrieved from the configuration only once per instance of
* this utility, where each such retrieval is performed in the constructor.
*
* <a name="discoveryManager">
* <table summary="Describes the discoveryManager configuration entry"
* border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>discoveryManager</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link net.jini.discovery.DiscoveryManagement}
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code> new
* {@link net.jini.discovery.LookupDiscoveryManager#LookupDiscoveryManager(
* java.lang.String[],
* net.jini.core.discovery.LookupLocator[],
* net.jini.discovery.DiscoveryListener,
* net.jini.config.Configuration) LookupDiscoveryManager}(
* new java.lang.String[] {""},
* new {@link net.jini.core.discovery.LookupLocator}[0],
* null, config)</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> The object used to manage the discovery processing
* performed by this utility. This entry will be retrieved
* from the configuration only if no discovery manager is
* specified in the constructor. Note that this object should
* not be shared with other components in the application that
* employs this utility.
* </table>
*
* <a name="leaseManager">
* <table summary="Describes the leaseManager configuration entry"
* border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>leaseManager</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link net.jini.lease.LeaseRenewalManager}
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code> new
* {@link net.jini.lease.LeaseRenewalManager#LeaseRenewalManager(
* net.jini.config.Configuration) LeaseRenewalManager}(config)</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> The object used to manage each service lease returned
* to this utility when the service is registered with the
* the various discovered lookup services. This entry will
* be retrieved from the configuration only if no lease
* renewal manager is specified in the constructor.
* </table>
*
* <a name="maxLeaseDuration">
* <table summary="Describes the maxLeaseDuration
* configuration entry" border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>maxLeaseDuration</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> <code>long</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>Lease.FOREVER</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> The maximum lease duration (in milliseconds) that is requested
* from each discovered lookup service on behalf of the service;
* both when the lease is initially requested, as well as when
* renewal of that lease is requested. Note that as this value is
* made smaller, renewal requests will be made more frequently
* while the service is up, and lease expiration will occur sooner
* when the service goes down.
* </table>
*
* <a name="registrarPreparer">
* <table summary="Describes the registrarPreparer configuration entry"
* border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>registrarPreparer</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link net.jini.security.ProxyPreparer}
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
* </code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> Preparer for the proxies to the lookup services that are
* discovered and used by this utility.
* <p>
* The following methods of the proxy returned by this preparer are
* invoked by this utility:
* <ul>
* <li>{@link net.jini.core.lookup.ServiceRegistrar#register register}
* </ul>
* </table>
*
* <a name="registrationPreparer">
* <table summary="Describes the registrationPreparer configuration entry"
* border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>registrationPreparer</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link net.jini.security.ProxyPreparer}
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
* </code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> Preparer for the proxies to the registrations returned to
* this utility upon registering the service with each discovered
* lookup service.
* <p>
* The following methods of the proxy returned by this preparer are
* invoked by this utility:
* <ul>
* <li>{@link net.jini.core.lookup.ServiceRegistration#getServiceID
* getServiceID}
* <li>{@link net.jini.core.lookup.ServiceRegistration#getLease
* getLease}
* <li>{@link net.jini.core.lookup.ServiceRegistration#addAttributes
* addAttributes}
* <li>{@link net.jini.core.lookup.ServiceRegistration#modifyAttributes
* modifyAttributes}
* <li>{@link net.jini.core.lookup.ServiceRegistration#setAttributes
* setAttributes}
* </ul>
* </table>
*
* <a name="serviceLeasePreparer">
* <table summary="Describes the serviceLeasePreparer configuration entry"
* border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>serviceLeasePreparer</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link net.jini.security.ProxyPreparer}
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>new {@link net.jini.security.BasicProxyPreparer}()
* </code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> Preparer for the leases returned to this utility through
* the registrations with each discovered lookup service with
* which this utility has registered the service.
* <p>
* Currently, none of the methods on the service lease returned
* by this preparer are invoked by this implementation of the utility.
* </table>
*
* <a name="taskManager">
* <table summary="Describes the taskManager configuration entry"
* border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>taskManager</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link com.sun.jini.thread.TaskManager}
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>new
* {@link com.sun.jini.thread.TaskManager#TaskManager()
* TaskManager}(15, (15*1000), 1.0f)</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> The object that pools and manages the various threads
* executed by this utility. The default manager creates a
* maximum of 15 threads, waits 15 seconds before removing
* idle threads, and uses a load factor of 1.0 when
* determining whether to create a new thread. This object
* should not be shared with other components in the
* application that employs this utility.
* </table>
*
* <a name="wakeupManager">
* <table summary="Describes the wakeupManager configuration entry"
* border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>wakeupManager</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> {@link com.sun.jini.thread.WakeupManager}
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>new
* {@link com.sun.jini.thread.WakeupManager#WakeupManager(
* com.sun.jini.thread.WakeupManager.ThreadDesc)
* WakeupManager}(new
* {@link com.sun.jini.thread.WakeupManager.ThreadDesc}(null,true))</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> Object that pools and manages the various tasks that are
* initially executed by the object corresponding to the
* <a href="#taskManager"><code>taskManager</code></a> entry
* of this component, but which fail during that initial execution.
* This object schedules the re-execution of such a failed task -
* in the <a href="#taskManager"><code>taskManager</code></a>
* object - at various times in the future, until either the
* task succeeds or the task has been executed the maximum
* number of allowable times, corresponding to the
* <a href="#wakeupRetries"><code>wakeupRetries</code></a>
* entry of this component. This object should not be shared
* with other components in the application that employs this
* utility.
* </table>
*
* <a name="wakeupRetries">
* <table summary="Describes the wakeupRetries
* configuration entry" border="0" cellpadding="2">
* <tr valign="top">
* <th scope="col" summary="layout"> <font size="+1">•</font>
* <th scope="col" align="left" colspan="2"> <font size="+1">
* <code>wakeupRetries</code></font>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Type: <td> <code>int</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Default: <td> <code>6</code>
*
* <tr valign="top"> <td>   <th scope="row" align="right">
* Description:
* <td> The maximum number of times a failed task is allowed to
* be executed by the object corresponding to the
* <a href="#wakeupManager"><code>wakeupManager</code></a>
* entry of this component.
* </table>
*
* <a name="jmLogging">
* <p>
* <b><font size="+1">Logging</font></b>
* <p>
* </a>
*
* This implementation of <code>JoinManager</code> uses the
* {@link Logger} named <code>net.jini.lookup.JoinManager</code>
* to log information at the following logging levels: <p>
*
* <table border="1" cellpadding="5"
* summary="Describes the information logged by JoinManager,
* and the levels at which that information is logged">
*
* <caption halign="center" valign="top">
* <b><code>net.jini.lookup.JoinManager</code></b>
* </caption>
*
* <tr> <th scope="col"> Level</th>
* <th scope="col"> Description</th>
* </tr>
*
* <tr>
* <td>{@link java.util.logging.Level#INFO INFO}</td>
* <td>when a task is stopped because of a definite exception</td>
* </tr>
* <tr>
* <td>{@link java.util.logging.Level#INFO INFO}</td>
* <td>
* when a task is stopped because it has exceeded the maximum number of
* times the task is allowed to be tried/re-tried
* </td>
* </tr>
* <tr>
* <td>{@link java.util.logging.Level#INFO INFO}</td>
* <td>when any exception occurs while attempting to prepare a proxy</td>
* </tr>
* <tr>
* <td>{@link java.util.logging.Level#FINER FINER}</td>
* <td>
* when any exception (other than the more serious exceptions logged
* at higher levels) occurs in a task
* </td>
* </tr>
* <tr>
* <td>{@link java.util.logging.Level#FINEST FINEST}</td>
* <td>
* when an <code>IllegalStateException</code> occurs upon attempting to
* discard a lookup service
* </td>
* </tr>
* <tr>
* <td>{@link java.util.logging.Level#FINEST FINEST}</td>
* <td>whenever any task is started</td>
* </tr>
*
* <tr>
* <td>{@link java.util.logging.Level#FINEST FINEST}</td>
* <td>whenever any task completes successfully</td>
* </tr>
*
* <tr>
* <td>{@link java.util.logging.Level#FINEST FINEST}</td>
* <td>whenever a proxy is prepared</td>
* </tr>
* </table>
* <p>
*
* @author Sun Microsystems, Inc.
*
* @see net.jini.discovery.DiscoveryManagement
* @see net.jini.lease.LeaseRenewalManager
* @see java.util.logging.Level
* @see java.util.logging.Logger
*/
public class JoinManager {
/** Implementation Note:
*
* This class executes a number of tasks asynchronously. Each task is
* initially queued in a <code>com.sun.jini.thread.TaskManager</code>
* instance, which executes each task in a separate thread. In this
* way, an upper bound is placed on the number of threads executing
* concurrently at any one time; that is, the number of concurrent
* threads is "throttled".
*
* In addition to throttling the number of concurrent threads, the
* use of a task manager, in conjunction with the task configuration,
* provides a level of resiliency with respect to down/unresponsive
* lookup services.
*
* Recall from the specification that the primary function of a join
* manager is to maintain and update the state (registrations,
* attribute augmentations/replacements/changes, etc.) of the join
* manager's single associated service with all of the desired lookup
* services. Because updating a service's state in a lookup service
* involves remote communication, such update operations are performed
* asynchronously, in separate tasks. If those operations are not
* performed in separate tasks, then a communication problem with
* one of the lookup services while performing one operation could
* prevent (or significantly slow) the execution of all future
* state update operations; causing all processing in the join manager
* to degrade or even hang indefinitely.
*
* Although performing each update operation in a separate task thread
* prevents a down/unresponsive lookup service from blocking other
* processing in the join manager, it does not guarantee consistency
* of the service's state in each "up" lookup service, with the state
* expected by the client that requested the state changes.
*
* For example, suppose the client first requests that the service's
* attributes be replaced with a new set, and then immediately after
* the attribute replacement request, the client requests that the
* service's attributes be augmented by yet another set of attributes.
* Unless control is imposed on the order of execution of the tasks
* that perform the attribute replacement and augmentation, there is
* a possibility that the service's state in one or more of the lookup
* service's will have experienced the attribute augmentation prior to
* the attribute replacement, rather than the replacement followed by
* the augmentation, as the client would have expected. Thus, not only
* can the service's state in one or more of the lookup services be
* inconsistent with what the client expects, but it can also be
* inconsistent with one or more of the other lookup services with
* which the service is registered.
*
* To prevent the possibility of inconsistencies such as those just
* described, the state update operations are grouped according to
* the particular lookup service with which they are associated. Each
* such grouping is implemented as a task, executed by the task
* manager employed by this implementation of <code>JoinManager</code>,
* containing a queue of sub-tasks (the actual state update operations)
* that are executed by the main task in the same order in which they
* were requested by the client.
*
* Each main task executed by this join manager's task manager is a
* sub-class of the abstract class <code>RetryTask</code>, defined in
* the package <code>com.sun.jini.thread</code>, which implements
* the <code>com.sun.jini.thread.TaskManager.Task</code> interface.
* The association of each such task with a particular lookup service,
* and with a unique sequence number is reflected in the fields of this
* class. The unique sequence number associated with each main task
* is exploited by the <code>runAfter</code> method defined in
* <code>RetryTask</code> to guarantee that the service managed by
* this <code>JoinManager</code> is assigned only one service ID.
*
* Because of the relationship between the main tasks and
* <code>RetryTask</code>, those concrete main tasks created in this
* implementation of <code>JoinManager</code> can be processed by either
* a task manager or a wakeup manager; and this fact is exploited to
* provide a failure recovery mechanism that can tolerate task "storms".
* Task storms can occur in systems that contain numerous services,
* where each service employs its own join manager to handle its join
* state with the desired lookup services. A "registration request storm"
* is an example of one type of task storm.
*
* A registration request storm can occur when a system is initially
* started, or when it recovers from a system-wide failure, when each
* service's join manager attempts to register with each discovered
* lookup service; resulting in a flood - or "storm" - of registration
* requests at each lookup service. Because the accept queue under some
* operating systems is - by default - very small, it is possible that,
* in the face of such a large number of concurrent requests, one or
* more of the lookup services will not be able to process the requests
* as fast as they are arriving in the queue. When this happens, the
* associated task in the join manager typically encounters a failure
* such as a <code>java.rmi.ConnectException</code>.
*
* To deal with situations such as that described above, this
* <code>JoinManager</code> implementation employs a wakeup manager
* in addition to a task manager. When a main task is created, it is
* initially executed by a task manager. If a failure is encountered,
* and if the nature of that failure indicates that retrying the task
* will <i>definitely</i> fail again, then the associated lookup service
* is discarded so that the task can be retried when the lookup service
* is rediscovered. But if it is not clear that retrying the task will
* again fail, (that is, the failure is <i>indefinite</i>), then the
* task is queued for re-execution at a later time in a wakeup manager.
* This process is repeated - employing a "backoff" strategy - until one
* of the following events occurs:
*
* <p><ul>
* <li> the task succeeds
* <li> a definite failure is encountered
* <li> the task has been executed the maximum number times allowed
* </ul><p>
*
* The maximum number of times a task is allowed to be retried before
* giving up and discarding the associated lookup service can be changed
* from its default value by setting the <code>wakeupRetries</code>
* configuration entry for this component.
*
* @see com.sun.jini.thread.TaskManager
* @see com.sun.jini.thread.WakeupManager
* @see com.sun.jini.thread.TaskManager.Task
* @see com.sun.jini.thread.RetryTask
* @see com.sun.jini.constants.ThrowableConstants
*/
/** Abstract base class from which all of the task classes are derived. */
private class ProxyRegTask extends RetryTask {
private final long[] sleepTime = { 5*1000, 10*1000, 15*1000,
20*1000, 25*1000, 30*1000 };
protected int tryIndx = 0;
protected int nRetries = 0;
protected ProxyReg proxyReg;
protected int seqN;
/** Basic constructor; simply stores the input parameters */
ProxyRegTask(ProxyReg proxyReg, int seqN) {
super(JoinManager.this.taskMgr,JoinManager.this.wakeupMgr);
this.proxyReg = proxyReg;
this.seqN = seqN;
}//end constructor
/** Executes the current instance of this task once, queuing it
* for retry at a later time and returning <code>false</code>
* upon failure. This method attempts to execute all of the tasks
* associated with the lookup service referenced in this task's
* <code>proxyReg</code> field. Order of execution is important,
* and this method executes the tasks in the <code>proxyReg</code>'s
* <code>taskList</code> in a FIFO order.
*
* Note that tasks may be added to the <code>taskList</code> of
* the <code>proxyReg</code> during the execution of this method.
*
* Upon successfully executing all of the tasks in the
* <code>taskList</code>, this method returns <code>true</code>
* and the current instance of this task is not executed again.
* For each unsuccessful execution of a task in the
* <code>taskList</code>, this method returns <code>false</code>,
* which causes the task to be scheduled by the
* <code>WakeupManager</code> to be executed again at a later
* time, as indicated by the value returned by <code>retryTime</code>.
*/
public boolean tryOnce() {
while(true) {
JoinTask t = null;
synchronized(proxyReg.taskList) {
if(proxyReg.taskList.isEmpty()) {
proxyReg.proxyRegTask = null;
return true;
}//endif
t = (JoinTask)proxyReg.taskList.get(0);
}//end sync
try {
t.run();
synchronized(proxyReg.taskList) {
if( !proxyReg.taskList.isEmpty() ) {
proxyReg.taskList.remove(0);
}//endif
}//end sync
/* reset the retry info for the next task in the list */
tryIndx = 0;
nRetries = 0;
} catch (Exception e) {
return stopTrying(e);
}
}//end loop
}//end tryOnce
/** Returns the next absolute time (in milliseconds) at which another
* execution of this task should be made (after the previous
* attempt has failed).
*/
public long retryTime() {
long nextTryTime = System.currentTimeMillis() + sleepTime[tryIndx];
if(tryIndx < sleepTime.length-1) tryIndx++;//don't go past end
nRetries++;
return nextTryTime;
}//end retryTime
/** Returns true if the current instance of this task must be run
* after any task already in the task manager queue.
*
* It is important that when the join manager is constructed with
* a <code>null</code> service ID (where it is desired that
* a unique service ID be generated on the service's behalf),
* that only the first task in the task manager's queue be run; no
* other tasks in the queue should be run while that first task
* is running. This is because the first sub-task executed by
* the first main task in the task manager's queue will always be
* a <code>RegisterTask</code>. And during the execution of that
* first sub-task (if the service ID has not yet been set), the
* service ID generated by the associated lookup service is retrieved
* and stored for use in all future lookup service registration
* tasks, Once the service ID is set by that first registration
* sub-task, all future main tasks (and their associated registration
* sub-tasks) can be run in parallel; each using the same service ID.
* If this is not done, then the registration sub-tasks would be
* run in parallel, each assigning a different ID to the service.
*
* This method guarantees that until the service's ID is set,
* only one registration sub-task is run; that is, one task
* doesn't start until the currently running task has completed,
* and a non-<code>null</code> service ID is assigned to the service.
*
* Executing the main tasks sequentially until the service ID is
* retrieved and stored must also be guaranteed because the currently
* running registration task may fail to register the service
* (because of a <code>RemoteException</code>), and thus may fail
* to obtain an ID for the service. This method guarantees then
* that each main task (and thus, each registration sub-task) will
* run in sequence until one of those tasks completes successfully;
* and from that point on, this method guarantees that all other
* queued tasks will run in parallel.
*
* @param tasks the tasks with which to compare the current task
* @param size elements with index less than size are considered
*/
public boolean runAfter(List tasks, int size) {
/* If the service's ID has already been set, then it's okay
* to run all ProxyRegTask's in parallel, otherwise, the
* ProxyRegTask with the lowest sequence number should be run.
*/
synchronized(serviceItem) {//accessing serviceItem.serviceID
if(serviceItem.serviceID != null) return false;
/* For task with lowest seq #, run it now; else run it later */
for(int i=0; i<size; i++) {
TaskManager.Task t = (TaskManager.Task)tasks.get(i);
int nextTaskSeqN = ((ProxyRegTask)t).getSeqN();
if( seqN > nextTaskSeqN ) return true;
}//end loop
return false;
}//end sync(serviceItem)
}//end runAfter
/** Accessor method that returns the instance of <code>ProxyReg</code>
* (the lookup service) associated with the task represented by
* the current instance of this class.
*/
public ProxyReg getProxyReg() {
return proxyReg;
}//end getProxy
/** Accessor method that returns the unique sequence number associated
* with the task represented by the current instance of this class.
*/
public int getSeqN() {
return seqN;
}//end getSeqN
/** Convenience method called by the child tasks when they encounter
* an exception. If the given exception indicates that retrying the
* task would definitely fail, or if the maximum allowable number
* of retries of the task has been exceeded, then this method will
* do the following:
* - remove all pending tasks that are to be run after this task
* - cancel this task
* - discard the look service associated with this task
* - return <code>true</code> (which stops the wakeup manager
* from retrying this task
* otherwise, this method returns <code>false</code>, which indicates
* that the wakeup manager should not stop trying to successfully
* execute the task.
*/
protected boolean stopTrying(Exception e) {
int exCat = ThrowableConstants.retryable(e);
if( (exCat != ThrowableConstants.INDEFINITE)
|| (nRetries >= maxNRetries) )
{
synchronized(joinSet) {
removeTasks(proxyReg);//cancel and clear all related tasks
}//end sync(joinSet)
proxyReg.fail(e);
return true;//don't try again
}//endif
logger.log(Level.FINER,
"JoinManager - failure, will retry later", e);
return false;//try this task again later
}//end stopTrying
}//end class ProxyRegTask
/** Abstract base class from which all the sub-task classes are derived. */
private abstract class JoinTask {
/** Data structure referencing the task's associated lookup service */
protected ProxyReg proxyReg;
/** Basic constructor; simply stores the input parameters */
JoinTask(ProxyReg proxyReg) {
this.proxyReg = proxyReg;
}//end constructor
/** Method executed in a separate thread created by the task manager */
public abstract void run() throws Exception;
}//end class JoinTask
/** Task that asynchronously registers the service associated with this
* join manager with the lookup service referenced by the current
* instance of this class.
*/
private class RegisterTask extends JoinTask {
/** Attributes with which to register the service. These attributes
* must not change during the registration process performed in
* this this task.
*/
Entry[] regAttrs;
/** Constructor that associates this task with the lookup service
* referenced in the given <code>ProxyReg</code> parameter.
*
* @param proxyReg data structure that references the lookup service
* with which the service is to be registered
* @param regAttrs array of Entry objects whose elements are the
* attributes with which to register the service.
* The caller of this constructor should take steps
* to guarantee that the contents of this parameter
* do not change during the registration process
* performed in this task.
*/
RegisterTask(ProxyReg proxyReg, Entry[] regAttrs) {
super(proxyReg);
this.regAttrs = regAttrs;
}//end constructor
/** Attempts to register this join manager's service with the lookup
* service referenced in this task's proxyReg field.
*/
public void run() throws Exception {
logger.finest("JoinManager - RegisterTask started");
proxyReg.register(regAttrs);
logger.finest("JoinManager - RegisterTask completed");
}//end run
}//end class RegisterTask
/** Task that asynchronously re-registers the service associated with this
* join manager with the lookup service referenced by the current
* instance of this class.
*
* This task is typically executed when the service's lease with the
* referenced lookup service has expired.
*/
private class LeaseExpireNotifyTask extends JoinTask {
/** Attributes with which to re-register the service. These attributes
* must not change during the registration process performed in
* this this task.
*/
Entry[] regAttrs;
/** Constructor that associates this task with the lookup service
* referenced in the given <code>ProxyReg</code> parameter.
*
* @param proxyReg data structure that references the lookup service
* with which the service is to be re-registered
* @param regAttrs array of Entry objects whose elements are the
* attributes with which to re-register the service.
* The caller of this constructor should take steps
* to guarantee that the contents of this parameter
* do not change during the registration process
* performed in this task.
*/
LeaseExpireNotifyTask(ProxyReg proxyReg, Entry[] regAttrs) {
super(proxyReg);
this.regAttrs = regAttrs;
}//end constructor
/** Attempts to re-register this join manager's service with the
* lookup service referenced by the current instance of this class.
*/
public void run() throws Exception {
logger.finest("JoinManager - LeaseExpireNotifyTask started");
boolean tryIt = false;
synchronized(joinSet) {
tryIt = joinSet.contains(proxyReg);
}//end sync(joinSet)
if(tryIt) proxyReg.register(regAttrs);
logger.finest("JoinManager - LeaseExpireNotifyTask completed");
}//end run
}//end class LeaseExpireNotifyTask
/** Task that asynchronously requests the cancellation of the lease
* on the <code>ServiceRegistration</code> referenced by the given
* <code>ProxyReg</code> object. The lease to be cancelled was granted
* by the lookup service whose proxy is also referenced by the given
* <code>ProxyReg</code> object. This task is executed whenever that
* lookup service is discarded.
*
* Note that although this task is executed upon receipt of any type
* of lookup service discard event, there is one type of discard
* that this task is actually intended to address: the so-called
* "lost-interest" discard. A discard corresponding to a loss of
* interest is an indication that the discarded lookup service is
* still up and available, but the discovery manager employed by
* this join manager (not the join manager or the service itself)
* discards the lookup service because the service is no longer
* interested in being registered with that lookup service. This
* loss of interest is caused by a change in either the lookup
* service's member groups or the service's groups-to-discover.
* Such a change in groups would typically be made administratively,
* through either the lookup service's administrative interface or
* through the service's administrative interface (or both).
*
* When the lookup service is discarded because of a loss of
* interest, there is a time period in which the service is still
* registered with the lookup service, even though the service no
* longer wishes to be registered with that lookup service. To
* address this, the lease is cancelled so that the service is
* removed from the lookup service in a much more timely fashion
* than simply allowing the lease to expire.
*
* As stated above, this task is also executed upon receipt of
* the other types of discard event. But because those other
* discard types occur when the lookup service is no longer
* available, the lease cancellation that is attempted here has
* no significant effect.
*
* @see DiscMgrListener#discarded
*/
private class DiscardProxyTask extends JoinTask {
/** Constructor that provides this task with access (through the given
* <code>ProxyReg</code> parameter) to the lease to be cancelled.
*
* @param proxyReg data structure that references the
* <code>ServiceRegistration</code> and associated
* lease whose cancellation is to be requested
*/
DiscardProxyTask(ProxyReg proxyReg) {
super(proxyReg);
}//end constructor
/** Requests the cancellation of the lease on the
* <code>ServiceRegistration</code> that is referenced in the
* <code>proxyReg</code> data structure.
*/
public void run() {
logger.finest("JoinManager --> DiscardProxyTask started");
if( (proxyReg != null) && (proxyReg.serviceLease != null) ) {
try {
proxyReg.serviceLease.cancel();
} catch (Exception e) { /*ignore*/ }
}//endif
logger.finest("JoinManager - DiscardProxyTask completed");
}//end run
}//end class DiscardProxyTask
/** Task that asynchronously augments the attributes associated with this
* join manager's service in the lookup service referenced by the
* current instance of this class.
*/
private class AddAttributesTask extends JoinTask {
/** The new attribute values with which the service's current
* attributes will be augmented, replaced, or changed.
*/
protected Entry[] attrSets;
/** Constructor that associates this task with the lookup service
* referenced in the given <code>ProxyReg</code> parameter.
*
* @param proxyReg data structure that references the lookup service
* in which the service's attributes should be
* augmented
* @param newAttrs the attributes with which to augment the
* service's current set of attributes
*/
AddAttributesTask(ProxyReg proxyReg, Entry[] newAttrs) {
super(proxyReg);
this.attrSets = (Entry[])newAttrs.clone();
}//end constructor
/** Performs the actual attribute augmentation, replacement, or
* modification work. This method is typically overridden by
* sub-classes of this class.
*/
protected void doAttributes(ProxyReg proxyReg) throws Exception {
logger.finest("JoinManager - AddAttributesTask started");
proxyReg.addAttributes(attrSets);
logger.finest("JoinManager - AddAttributesTask completed");
}//end AddAttributesTask.doAttributes
/** Attempts to either augment, replace, or modify the attributes
* of this join manager's service in the lookup service referenced
* by the current instance of this class. Which action is taken --
* augmentation, replacement, or modification -- is dependent on the
* definition of the <code>doAttributes/code> method.
*/
public void run() throws Exception {
doAttributes(proxyReg);
}//end run
}//end class AddAttributesTask
/** Task that asynchronously replaces the attributes associated with this
* join manager's service in the lookup service referenced by the
* current instance of this class.
*/
private final class SetAttributesTask extends AddAttributesTask {
/** Constructor that associates this task with the lookup service
* referenced in the given <code>ProxyReg</code> parameter.
*
* @param proxyReg data structure that references the lookup
* service in which the service's attributes
* should be replaced
* @param replacementAttrs the attributes with which to replace the
* service's current set of attributes
*/
SetAttributesTask(ProxyReg proxyReg, Entry[] replacementAttrs){
super(proxyReg, replacementAttrs);
}//end constructor
/** Performs the actual attribute replacement work. */
protected void doAttributes(ProxyReg proxyReg) throws Exception {
logger.finest("JoinManager - SetAttributesTask started");
proxyReg.setAttributes(attrSets);
logger.finest("JoinManager - SetAttributesTask completed");
}//end SetAttributesTask.doAttributes
}//end class SetAttributesTask
/** Task that asynchronously modifies the attributes associated with this
* join manager's service in the lookup service referenced by the
* current instance of this class.
*/
private final class ModifyAttributesTask extends AddAttributesTask {
private Entry[] attrSetTemplates;
/** Constructor that associates this task with the lookup service
* referenced in the given <code>ProxyReg</code> parameter.
*
* @param proxyReg data structure that references the lookup
* service in which the service's attributes
* should be changed
* @param attrSetTemplates the attribute templates that are used to
* select (through attribute matching) the
* attributes to change in the service's
* current set of attributes
* @param attrChanges the attributes containing the changes to
* make to the attributes in the service's
* current set that are selected through
* attribute matching with the
* attrSetTemplates parameter
*/
ModifyAttributesTask( ProxyReg proxyReg,
Entry[] attrSetTemplates,
Entry[] attrChanges )
{
super(proxyReg, attrChanges);
this.attrSetTemplates = (Entry[])attrSetTemplates.clone();
}//end constructor
/** Performs the actual attribute modification work. */
protected void doAttributes(ProxyReg proxyReg) throws Exception {
logger.finest("JoinManager - ModifyAttributesTask started");
proxyReg.modifyAttributes(attrSetTemplates, attrSets);
logger.finest("JoinManager - ModifyAttributesTask completed");
}//end ModifyAttributesTask.doAttributes
}//end class ModifyAttributesTask
/** Wrapper class in which each instance corresponds to a lookup
* service to discover, and with which this join manager's service
* should be registered.
*/
private class ProxyReg {
/** Class that is registered as a listener with this join manager's
* lease renewal manager. That lease renewal manager manages the
* lease granted to this join manager's associated service by the
* lookup service referenced in the proxy object associated with
* this class (<code>ProxyReg</code>).
*
* If the lease expires in the lookup service before the lease
* renewal manager requests renewal of the lease, then upon sending
* that renewal request, the lease renewal manager will receive an
* <code>UnknownLeaseException</code> from the lookup service.
* As a result, the lease renewal manager removes the expired lease
* so that no further renewal attempts are made for that lease,
* and then sends to this listener a <code>LeaseRenewalEvent</code>,
* containing an <code>UnknownLeaseException</code>.
*
* Alternatively, suppose that at the time the lease renewal manager
* is about to request the renewal of the lease, the lease renewal
* manager determines that the expiration time of the lease has
* actually passed. In this case, since there is no reason to
* send the renewal request, the lease renewal manager instead
* removes the expired lease (again, so that no further renewal
* attempts are made for that lease), and then sends a
* <code>LeaseRenewalEvent</code> to this listener to indicate
* that the lease has expired. The difference between this case,
* and the case described previously is that in this case the
* <code>LeaseRenewalEvent</code> contains no exception (that is,
* <code>LeaseRenewalEvent.getException</code> returns
* <code>null</code>).
*
* Both situations described above indicate that the lease
* referenced by the event received by this listener has expired.
* Thus, the normal course of action should be to attempt to
* re-register the service with the lookup service that originally
* granted the lease that has expired.
*
* Prior to re-registering the service, the joinSet is examined to
* determine if it contains a ProxyReg object that is equivalent to
* the ProxyReg object referencing the current instance of this
* listener class. That is, using <code>equals</code>, it is
* determined whether the joinSet contains either ProxyReg.this,
* or a new instance of ProxyReg that is equal to ProxyReg.this.
* If the joinSet does not contain such a ProxyReg, then the lookup
* service must have been discarded and not yet re-discovered; in
* which case, there is no need to attempt to re-register with that
* lookup service, since it is unavailable.
*
* If it is determined that the joinSet does contain either
* ProxyReg.this or a new ProxyReg equivalent to ProxyReg.this,
* then re-registration should be attempted, but only if the lease
* associated with the ProxyReg in the joinSet is equal to the
* expired lease referenced in the event received by this listener.
* Equality of those leases is an indication that the lease on the
* service's current registration has expired; thus, an attempt to
* re-register the service should be made.
*
* If the lease associated with the ProxyReg from the joinSet
* does not equal the expired lease from the event, then
* re-registration should not be attempted. This is because
* the inequality of the leases is an indication that the lease
* renewal event received by this listener was the result of an
* <code>UnknownLeaseException</code> that occurs when the
* ProxyReg in the joinSet is a new ProxyReg, different from
* ProxyReg.this, and the lease renewal manager requests the
* renewal of the (now invalid) lease associated with that old
* ProxyReg.this; not the valid lease associated with the new
* ProxyReg.
*
* A scenario such as that just described can occur when the
* lookup service is discarded, rediscovered, and the service is
* re-registered, resulting in a new ProxyReg (with new lease)
* being placed in the joinSet, replacing the previous ProxyReg
* (ProxyReg.this). But before the old, expired lease is removed
* from the lease renewal manager, an attempt to renew the old
* lease is made. Such an attempt can occur because the lease
* renewal manager may be in the process of requesting the renewal
* of that lease (or may have queued such a request) just prior to,
* or at the same time as, when the lease removal request is made
* during discard processing. When the request is made to renew
* the expired lease, an <code>UnknownLeaseException</code> occurs
* and a lease renewal event is sent to this listener.
*
* If, upon receiving an event such as that just described, the
* service were to be re-registered, the current valid service
* registration would be replaced, a new lease would be granted,
* and the corresponding ProxyReg currently contained in the joinSet
* would be replaced with a new ProxyReg. Additionally, the now
* invalid lease corresponding to the ProxyReg that was just
* replaced would remain in the lease renewal manager. This means
* that an attempt to renew that lease will eventually be made and
* will fail, and the cycle just described will repeat indefinitely.
*
* Thus, for the reasons stated above, re-registration is attempted
* only if the lease associated with the ProxyReg contained in the
* joinSet is equal to the expired lease referenced in the lease
* renewal event received by this listener.
*/
private class DiscLeaseListener implements LeaseListener {
public void notify(LeaseRenewalEvent e) {
Throwable ex = e.getException();
if ( (ex == null) || (ex instanceof UnknownLeaseException) ) {
synchronized(joinSet) {
removeTasks(ProxyReg.this);
Lease expiredLease = e.getLease();
// Maybe re-register
int indx = joinSet.indexOf(ProxyReg.this);
if(indx >= 0) {//new proxyReg/old ProxyReg.this in set
ProxyReg curProxyReg = (ProxyReg)joinSet.get(indx);
if(expiredLease.equals(curProxyReg.serviceLease)) {
// Okay to re-register
addTask(new LeaseExpireNotifyTask
(ProxyReg.this,
(Entry[])lookupAttr.clone()));
}//endif
}//endif
}//end sync(joinSet)
} else {
fail(ex);
}//endif
}//end notify
}//end class DiscLeaseListener
/** The <code>ProxyRegTask</code> that instantiated this
* <code>ProxyReg</code>.
*/
public ProxyRegTask proxyRegTask;
/** The <i>prepared</i> proxy to the lookup service referenced by
* this class, and with which this join manager's service will be
* registered.
*/
public ServiceRegistrar proxy;
/** The <i>prepared</i> registration proxy returned by this class'
* associated lookup service when this join manager registers its
* associated service.
*/
public ServiceRegistration srvcRegistration = null;
/* The <i>prepared</i> proxy to the lease on the registration of this
* join manager's service with the this class' associated lookup
* service.
*/
public Lease serviceLease = null;
/** The set of sub-tasks that are to be executed in order for the
* lookup service associated with the current instance of this class.
*/
public List taskList = new ArrayList(1);
/** The instance of <code>DiscLeaseListener</code> that is registered
* with the lease renewal manager that handles the lease of this join
* manger's service.
*/
private DiscLeaseListener dListener = new DiscLeaseListener();
/** Constructor that associates this class with the lookup service
* referenced in the given <code>ProxyReg</code> parameter.
*
* @param proxy data structure that references the lookup service on
* which the sub-tasks referenced in this class will be
* executed in order
*/
public ProxyReg(ServiceRegistrar proxy) {
if(proxy == null) throw new IllegalArgumentException
("proxy can't be null");
this.proxy = proxy;
}//end constructor
/** Convenience method that adds new sub-tasks to this class'
* task queue.
*
* @param task the task to add to the task queue
*/
public void addTask(JoinTask task) {
synchronized(JoinManager.this) {
if(bTerminated) return;
}//end sync
synchronized(taskList) {
taskList.add(task);
if(this.proxyRegTask == null) {
this.proxyRegTask = new ProxyRegTask(this,taskSeqN++);
synchronized (taskMgr) {
taskMgr.add(this.proxyRegTask);
}//end sync(taskMgr)
}//endif
}//end sync(taskList)
}//end addTask
/** Registers the service associated with this join manager with the
* the lookup service corresponding to this class. Additionally,
* this method retrieves the lease granted by the lookup service
* on the service's registration, and passes that lease to the
* lease renewal manager. If a <code>ServiceIDListener</code>
* has been registered with this join manager, this method will
* send to that listener a notification containing the service's ID.
*/
public void register(Entry[] srvcAttrs) throws Exception {
if(proxy == null) throw new RuntimeException("proxy is null");
/* The lookup service proxy was already prepared at discovery */
ServiceItem tmpSrvcItem = null;
synchronized(joinSet) {
srvcRegistration = null;
synchronized(serviceItem) {//accessing serviceItem.serviceID
tmpSrvcItem = new ServiceItem(serviceItem.serviceID,
serviceItem.service,
srvcAttrs);
}//end sync(serviceItem)
}//end sync(joinSet)
/* Retrieve and prepare the proxy to the service registration */
ServiceRegistration tmpSrvcRegistration
= proxy.register(tmpSrvcItem, renewalDuration);
try {
tmpSrvcRegistration =
(ServiceRegistration)registrationPreparer.prepareProxy
( tmpSrvcRegistration );
logger.finest
("JoinManager - ServiceRegistration proxy prepared");
} catch(Exception e) {
LogUtil.logThrow(logger, Level.WARNING, ProxyReg.class,
"register", "JoinManager - failure during " +
"preparation of ServiceRegistration proxy: {0}",
new Object[] { tmpSrvcRegistration }, e);
throw e; //rethrow the exception since proxy may be unusable
}
/* Retrieve and prepare the proxy to the service lease */
serviceLease = tmpSrvcRegistration.getLease();
try {
serviceLease =
(Lease)serviceLeasePreparer.prepareProxy(serviceLease);
logger.finest("JoinManager - service lease proxy prepared");
} catch(Exception e) {
LogUtil.logThrow(logger, Level.WARNING, ProxyReg.class,
"register", "JoinManager - failure during " +
"preparation of service lease proxy: {0}",
new Object[] { serviceLease }, e);
throw e; //rethrow the exception since proxy may be unusable
}
leaseRenewalMgr.renewUntil(serviceLease, Lease.FOREVER,
renewalDuration, dListener);
ServiceID tmpID = null;
synchronized(joinSet) {
srvcRegistration = tmpSrvcRegistration;
synchronized(serviceItem) {//accessing serviceItem.serviceID
if(serviceItem.serviceID == null) {
serviceItem.serviceID
= srvcRegistration.getServiceID();
tmpID = serviceItem.serviceID;
}//endif
}//end sync(serviceItem)
}//end sync(joinSet)
if( (tmpID != null) && (callback != null) ) {
callback.serviceIDNotify(tmpID);
}//endif
}//end ProxyReg.register
/** With respect to the lookup service referenced in this class
* and with which this join manager's service is registered, this
* method associates with that service a new set of attributes -- in
* addition to that service's current set of attributes.
*/
public void addAttributes(Entry[] attSet) throws Exception {
srvcRegistration.addAttributes(attSet);
}//end ProxyReg.addAttributes
/** With respect to the lookup service referenced in this class
* and with which this join manager's service is registered, this
* method changes that service's current attributes by selecting
* the attributes to change using the given first parameter;
* and identifying the desired changes to make through the
* contents of the second parameter.
*/
public void modifyAttributes(Entry[] templ, Entry[] attSet)
throws Exception
{
srvcRegistration.modifyAttributes(templ, attSet);
}//end ProxyReg.modifyAttributes
/** With respect to the lookup service referenced in this class
* and with which this join manager's service is registered, this
* method replaces that service's current attributes with a new
* set of attributes.
*/
public void setAttributes(Entry[] attSet) throws Exception {
srvcRegistration.setAttributes(attSet);
}//end ProxyReg.setAttributes
/** Convenience method that encapsulates appropriate behavior when
* failure is encountered related to the current instance of this
* class. This method discards the lookup service proxy associated
* with this object, and logs the stack trace of the given
* <code>Throwable</code> according to the logging levels specified
* for this utility.
*
* Note that if the discovery manager employed by this join manager
* has been terminated, then the attempt to discard the lookup
* service proxy will result in an <code>IllegalStateException</code>.
* Since this method is called only within the tasks run by
* this join manager, and since propagating an
* <code>IllegalStateException</code> out into the
* <code>ThreadGroup</code> of those tasks is undesirable, this
* method will not propagate the <code>IllegalStateException</code>
* that occurs as a result of an attempt to discard a lookup
* service proxy from the discovery manager employed by this
* join manager.
*
* For more information, refer to Bug 4490355.
*/
public void fail(Throwable e) {
synchronized(this) {
if(bTerminated) {
return;
} else {
LogUtil.logThrow(logger, Level.INFO, ProxyReg.class, "fail",
"JoinManager - failure for lookup service proxy: {0}",
new Object[] { proxy }, e);
try {
discMgr.discard(proxy);
} catch(IllegalStateException e1) {
logger.log(Level.FINEST,
"JoinManager - cannot discard lookup, "
+"discovery manager already terminated",
e1);
}
}//endif
}//end sync(this)
}//end ProxyReg.fail
/** Returns true if the both objects' associated proxies are equal. */
public boolean equals(Object obj) {
if (obj instanceof ProxyReg) {
return proxy.equals( ((ProxyReg)obj).proxy );
} else {
return false;
}//endif
}//end ProxyReg.equals
/** Returns the hash code of the proxy referenced in this class. */
public int hashCode() {
return proxy.hashCode();
}//end hashCode
}//end class ProxyReg
/* Listener class for discovery/discard notification of lookup services. */
private class DiscMgrListener implements DiscoveryListener {
/* Invoked when new or previously discarded lookup is discovered. */
public void discovered(DiscoveryEvent e) {
synchronized(joinSet) {
ServiceRegistrar[] proxys
= (ServiceRegistrar[])e.getRegistrars();
for(int i=0;i<proxys.length;i++) {
/* Prepare the proxy to the discovered lookup service
* before interacting with it.
*/
try {
proxys[i]
= (ServiceRegistrar)registrarPreparer.prepareProxy
(proxys[i]);
logger.log(Level.FINEST, "JoinManager - discovered "
+"lookup service proxy prepared: {0}",
proxys[i]);
} catch(Exception e1) {
LogUtil.logThrow(logger, Level.INFO,
DiscMgrListener.class, "discovered", "failure "
+ "preparing discovered ServiceRegistrar proxy: "
+ "{0}", new Object[] { proxys[i] }, e1);
discMgr.discard(proxys[i]);
continue;
}
/* If the serviceItem is a lookup service, don't need to
* register it with itself since a well-defined lookup
* service will always register with itself.
*/
if( !proxys[i].equals(serviceItem.service) ) {
ProxyReg proxyReg = new ProxyReg(proxys[i]);
if( !joinSet.contains(proxyReg) ) {
joinSet.add(proxyReg);
proxyReg.addTask(new RegisterTask
(proxyReg,
(Entry[])lookupAttr.clone()));
}//endif
}//endif
}//end loop
}//end sync(joinSet)
}//end discovered
/* Invoked when previously discovered lookup is discarded. */
public void discarded(DiscoveryEvent e) {
synchronized(joinSet) {
ServiceRegistrar[] proxys
= (ServiceRegistrar[])e.getRegistrars();
for(int i=0;i<proxys.length;i++) {
ProxyReg proxyReg = findReg(proxys[i]);
if(proxyReg != null) {
removeTasks(proxyReg);
joinSet.remove(proxyReg);
try {
leaseRenewalMgr.remove( proxyReg.serviceLease );
} catch(UnknownLeaseException ex) { /*ignore*/ }
proxyReg.addTask(new DiscardProxyTask(proxyReg));
}//endif
}//end loop
}//end sync(joinSet)
}//end discarded
}//end class DiscMgrListener
/* Name of this component; used in config entry retrieval and the logger.*/
private static final String COMPONENT_NAME = "net.jini.lookup.JoinManager";
/* Logger used by this utility. */
private static final Logger logger = Logger.getLogger(COMPONENT_NAME);
/** Maximum number of concurrent tasks that can be run in any default
* task manager created by this class.
*/
private static final int MAX_N_TASKS = 15;
/** Whenever a task is created in this join manager, it is assigned a
* unique sequence number so that the task is not run prior to the
* execution, and completion of, any other tasks with which that task
* is associated (tasks are grouped by the <code>ProxyReg</code> with
* which each task is associated). This field contains the value of
* the sequence number assigned to the most recently created task.
*/
private int taskSeqN = 0;
/** Task manager for the various tasks executed by this join manager.
* On the first attempt to execute any task is managed by this
* <code>TaskManager</code> so that the number of concurrent threads
* can be bounded. If one or more of those attempts fails, a
* <code>WakeupManager</code> is used (through the use of a
* <code>RetryTask</code>) to schedule - at a later time (employing a
* "backoff strategy") - the re-execution of each failed task in this
* <code>TaskManager</code>.
*/
private TaskManager taskMgr;
/** Maximum number of times a failed task is allowed to be re-executed. */
private int maxNRetries = 6;
/** Wakeup manager for the various tasks executed by this join manager.
* After an initial failure of any task executed by this join manager,
* the failed task is managed by this <code>WakeupManager</code>; which
* schedules the re-execution of the failed task - in the task manager -
* at various times in the future until either the task succeeds or the
* task has been executed the maximum number of allowable times. This
* wakeup manager is supplied to the <code>RetryTask</code>) that
* performs the actual task execution so that when termination of this
* join manager is requested, all tasks scheduled for retry by this
* wakeup manager can be cancelled.
*/
private WakeupManager wakeupMgr;
/** Contains the reference to the service that is to be registered with
* all of the desired lookup services referenced by <code>discMgr</code>.
*/
private ServiceItem serviceItem;
/** Contains the attributes with which to associate the service in each
* of the lookup services with which this join manager registers the
* service.
*/
private Entry[] lookupAttr = null;
/** Contains the listener -- instantiated by the entity that constructs
* this join manager -- that will receive an event containing the
* service ID assigned to this join manager's service by one of the
* lookup services with which that service is registered.
*/
private ServiceIDListener callback;
/** Contains elements of type <code>ProxyReg</code> where each element
* references a proxy to one of the lookup services with which this
* join manager's service is registered.
*/
private final ArrayList joinSet = new ArrayList(1);
/** Contains the discovery manager that discovers the lookup services
* with which this join manager will register its associated service.
*/
private DiscoveryManagement discMgr = null;
/** Contains the discovery listener registered by this join manager with
* the discovery manager so that this join manager is notified whenever
* one of the desired lookup services is discovered or discarded.
*/
private DiscMgrListener discMgrListener = new DiscMgrListener();
/** Flag that indicate whether the discovery manager employed by this
* join manager was created by this join manager itself, or by the
* entity that constructed this join manager.
*/
private boolean bCreateDiscMgr = false;
/** Contains the lease renewal manager that renews all of the leases
* this join manager's service holds with each lookup service with which
* it has been registered.
*/
private LeaseRenewalManager leaseRenewalMgr = null;
/** The value to use as the <code>renewDuration</code> parameter
* when invoking the lease renewal manager's <code>renewUntil</code>
* method to add a service lease to manage. This value represents,
* effectively, the time interval (in milliseconds) over which each
* managed lease must be renewed. As this value is made smaller,
* renewal requests will be made more frequently while the service
* is up, and lease expirations will occur sooner when the service
* goes down.
*/
private long renewalDuration = Lease.FOREVER;
/** Flag that indicates if this join manager has been terminated. */
private boolean bTerminated = false;
/* Preparer for the proxies to the lookup services that are discovered
* and used by this utility.
*/
private ProxyPreparer registrarPreparer;
/* Preparer for the proxies to the registrations returned to this utility
* upon registering the service with each discovered lookup service.
*/
private ProxyPreparer registrationPreparer;
/* Preparer for the proxies to the leases returned to this utility through
* the registrations with each discovered lookup service with which this
* utility has registered the service.
*/
private ProxyPreparer serviceLeasePreparer;
/**
* Constructs an instance of this class that will register the given
* service reference with all discovered lookup services and, through
* an event sent to the given <code>ServiceIDListener</code> object,
* communicate the service ID assigned by the first lookup service
* with which the service is registered. This constructor is typically
* used by services which have not yet been assigned a service ID.
* <p>
* The value input to the <code>serviceProxy</code> parameter represents
* the service reference (proxy) to register with each discovered lookup
* service. If the <code>Object</code> input to that parameter is not
* <code>Serializable</code>, an <code>IllegalArgumentException</code>
* is thrown. If <code>null</code> is input to that parameter, a
* <code>NullPointerException</code> is thrown.
* <p>
* The value input to the <code>attrSets</code> parameter is an array
* of <code>Entry</code> objects, none of whose elements may be
* <code>null</code>, that represents the new set of attributes to
* associate with the new service reference to be registered. Passing
* <code>null</code> as the value of the <code>attrSets</code> parameter
* is equivalent to passing an empty array. If any of the elements
* of the <code>attrSets</code> array are <code>null</code>, a
* <code>NullPointerException</code> is thrown. The set of attributes
* passed in this parameter will be associated with the service in all
* future join processing until those attributes are changed through
* an invocation of a method on this class such as,
* <code>addAttributes</code>, <code>setAttributes</code>,
* <code>modifyAttributes</code>, or <code>replaceRegistration</code>.
* <p>
* When constructing this utility, the service supplies an object through
* which notifications that indicate a lookup service has been discovered
* or discarded will be received. At a minimum, the object supplied
* (through the <code>discoveryMgr</code> parameter) must satisfy the
* contract defined in the <code>DiscoveryManagement</code> interface.
* That is, the object supplied must provide this utility with the ability
* to set discovery listeners and to discard previously discovered
* lookup services when they are found to be unavailable. A value of
* <code>null</code> may be input to the <code>discoveryMgr</code>
* parameter. When <code>null</code> is input to that parameter, an
* instance of <code>LookupDiscoveryManager</code> is used to listen
* for events announcing the discovery of only those lookup services
* that are members of the public group.
* <p>
* The object input to the <code>leaseMgr</code> parameter provides for
* the coordination, systematic renewal, and overall management of all
* leases on the given service reference's residency in the lookup
* services that have been joined. As with the <code>discoveryMgr</code>
* parameter, a value of <code>null</code> may be input to this
* parameter. When <code>null</code> is input to this parameter,
* an instance of <code>LeaseRenewalManager</code>, initially managing
* no <code>Lease</code> objects will be used. This feature allows a
* service to either use a single entity to manage all of its leases,
* or to use separate entities: one to manage the leases unrelated to
* the join process, and one to manage the leases that result from the
* join process, that are accessible only within the current instance
* of the <code>JoinManager</code>.
*
* @param serviceProxy the service reference (proxy) to register with all
* discovered lookup services
* @param attrSets array of <code>Entry</code> consisting of the
* attribute sets with which to register the service
* @param callback reference to the object that should receive the
* event containing the service ID, assigned to the
* service by the first lookup service with which the
* service reference is registered
* @param discoveryMgr reference to the <code>DiscoveryManagement</code>
* object this class should use to manage lookup
* service discovery on behalf of the given service
* @param leaseMgr reference to the <code>LeaseRenewalManager</code>
* object this class should use to manage the leases
* on the given service's residency in the lookup
* services that have been joined
*
* @throws java.lang.IllegalArgumentException if the object input to the
* <code>serviceProxy</code> parameter is not serializable
* @throws java.lang.NullPointerException if either <code>null</code> is
* input to the <code>serviceProxy</code> parameter, or at least
* one of the elements of the <code>attrSets</code> parameter is
* <code>null</code>
* @throws java.io.IOException if initiation of discovery process results
* in <code>IOException</code> when socket allocation occurs
*
* @throws java.lang.IllegalStateException if this method is called on
* a terminated <code>JoinManager</code> instance. Note that this
* exception is implementation-specific.
*
* @see net.jini.lookup.ServiceIDListener
* @see net.jini.discovery.DiscoveryManagement
* @see net.jini.discovery.LookupDiscoveryManager
* @see net.jini.lease.LeaseRenewalManager
*/
public JoinManager(Object serviceProxy,
Entry[] attrSets,
ServiceIDListener callback,
DiscoveryManagement discoveryMgr,
LeaseRenewalManager leaseMgr) throws IOException
{
discMgr = discoveryMgr;
try {
createJoinManager(null, serviceProxy, attrSets, callback, leaseMgr,
EmptyConfiguration.INSTANCE);
} catch(ConfigurationException e) { /* swallow this exception */ }
}//end constructor
/**
* Constructs an instance of this class, configured using the items
* retrieved through the given <code>Configuration</code> object,
* that will register the given service reference with all discovered
* lookup services and, through an event sent to the given
* <code>ServiceIDListener</code> object, communicate the service ID
* assigned by the first lookup service with which the service is
* registered. This constructor is typically used by services which
* have not yet been assigned a service ID, and which wish to allow
* for deployment-time configuration of the service's join processing.
* <p>
* The items used to configure the current instance of this class
* are obtained through the object input to the <code>config</code>
* parameter. If <code>null</code> is input to that parameter, a
* <code>NullPointerException</code> is thrown.
* <p>
* The object this utility will use to manage lookup service discovery on
* behalf of the given service can be supplied through either the
* <code>discoveryMgr</code> parameter or through an entry contained
* in the given <code>Configuration</code>. If <code>null</code> is input
* to the <code>discoveryMgr</code> parameter, an attempt will first be
* made to retrieve from the given <code>Configuration</code>, an entry
* named "discoveryManager" (described above). If such an object is
* successfully retrieved from the given <code>Configuration</code>, that
* object will be used to perform the lookup service discovery management
* required by this utility.
* <p>
* If <code>null</code> is input to the <code>discoveryMgr</code>
* parameter, and no entry named "discoveryManager" is specified in the
* given <code>Configuration</code>, then an instance of the utility class
* <code>LookupDiscoveryManager</code> will be used to listen for events
* announcing the discovery of only those lookup services that are
* members of the public group.
* <p>
* As with the <code>discoveryMgr</code> parameter, the object this
* utility will use to perform lease management on behalf of the given
* service can be supplied through either the <code>leaseMgr</code>
* parameter or through an entry contained in the given
* <code>Configuration</code>. If <code>null</code> is input to the
* <code>leaseMgr</code> parameter, an attempt will first be made to
* retrieve from the given <code>Configuration</code>, an entry named
* "leaseManager" (described above). If such an object is successfully
* retrieved from the given <code>Configuration</code>, that object
* will be used to perform the lease management required by this utility.
* <p>
* If <code>null</code> is input to the <code>leaseMgr</code>
* parameter, and no entry named "leaseManager" is specified in the
* given <code>Configuration</code>, then an instance of the utility
* class <code>LeaseRenewalManager</code> that takes the given
* <code>Configuration</code> will be created (initially managing no
* leases) and used to perform all required lease renewal management
* on behalf of the given service.
* <p>
* Except for the <code>config</code> parameter and the additional
* semantics imposed by that parameter (as noted above), all other
* parameters of this form of the constructor, along with their
* associated semantics, are identical to that of the five-argument
* constructor that takes a <code>ServiceIDListener</code>.
*
* @param serviceProxy the service reference (proxy) to register with all
* discovered lookup services
* @param attrSets array of <code>Entry</code> consisting of the
* attribute sets with which to register the service
* @param callback reference to the <code>ServiceIDListener</code>
* object that should receive the event containing the
* service ID assigned to the service by the first
* lookup service with which the service reference
* is registered
* @param discoveryMgr reference to the <code>DiscoveryManagement</code>
* object this class should use to manage lookup
* service discovery on behalf of the given service
* @param leaseMgr reference to the <code>LeaseRenewalManager</code>
* object this class should use to manage the leases
* on the given service's residency in the lookup
* services that have been joined
* @param config instance of <code>Configuration</code> through
* which the items used to configure the current
* instance of this class are obtained
*
* @throws java.lang.IllegalArgumentException if the object input to the
* <code>serviceProxy</code> parameter is not serializable
* @throws java.lang.NullPointerException if <code>null</code> is input
* to the <code>serviceProxy</code> parameter or the
* <code>config</code> parameter, or if at least one of the
* elements of the <code>attrSets</code> parameter is
* <code>null</code>
* @throws java.io.IOException if initiation of discovery process results
* in <code>IOException</code> when socket allocation occurs
* @throws net.jini.config.ConfigurationException if an exception
* occurs while retrieving an item from the given
* <code>Configuration</code> object
*
* @throws java.lang.IllegalStateException if this method is called on
* a terminated <code>JoinManager</code> instance. Note that this
* exception is implementation-specific.
*
* @see net.jini.lookup.ServiceIDListener
* @see net.jini.discovery.DiscoveryManagement
* @see net.jini.discovery.LookupDiscoveryManager
* @see net.jini.lease.LeaseRenewalManager
* @see net.jini.config.Configuration
* @see net.jini.config.ConfigurationException
*/
public JoinManager(Object serviceProxy,
Entry[] attrSets,
ServiceIDListener callback,
DiscoveryManagement discoveryMgr,
LeaseRenewalManager leaseMgr,
Configuration config)
throws IOException, ConfigurationException
{
discMgr = discoveryMgr;
createJoinManager(null, serviceProxy, attrSets,
callback, leaseMgr, config);
}//end constructor
/**
* Constructs an instance of this class that will register the
* service with all discovered lookup services, using the supplied
* <code>ServiceID</code>. This constructor is typically used by
* services which have already been assigned a service ID (possibly
* by the service provider itself or as a result of a prior registration
* with some lookup service), and which do not wish to allow for
* deployment-time configuration of the service's join processing.
* <p>
* Except that the desired <code>ServiceID</code> is supplied through the
* <code>serviceID</code> parameter rather than through a notification
* sent to a <code>ServiceIDListener</code>, all other parameters
* of this form of the constructor, along with their associated semantics,
* are identical to that of the five-argument constructor that takes
* a <code>ServiceIDListener</code>.
*
* @param serviceProxy a reference to the service requesting the services
* of this class
* @param attrSets array of <code>Entry</code> consisting of the
* attribute sets with which to register the service
* @param serviceID an instance of <code>ServiceID</code> with which to
* register the service with all desired lookup
* services
* @param discoveryMgr reference to the <code>DiscoveryManagement</code>
* object this class should use to manage the given
* service's lookup service discovery duties
* @param leaseMgr reference to the <code>LeaseRenewalManager</code>
* object this class should use to manage the leases
* on the given service's residency in the lookup
* services that have been joined
*
* @throws java.lang.IllegalArgumentException if the object input to the
* <code>serviceProxy</code> parameter is not serializable
* @throws java.lang.NullPointerException if either <code>null</code> is
* input to the <code>serviceProxy</code> parameter, or at least
* one of the elements of the <code>attrSets</code> parameter is
* <code>null</code>
* @throws java.io.IOException if initiation of discovery process results
* in <code>IOException</code> when socket allocation occurs
*
* @throws java.lang.IllegalStateException if this method is called on
* a terminated <code>JoinManager</code> instance. Note that this
* exception is implementation-specific.
*
* @see net.jini.core.lookup.ServiceID
* @see net.jini.discovery.DiscoveryManagement
* @see net.jini.discovery.LookupDiscoveryManager
* @see net.jini.lease.LeaseRenewalManager
*/
public JoinManager(Object serviceProxy,
Entry[] attrSets,
ServiceID serviceID,
DiscoveryManagement discoveryMgr,
LeaseRenewalManager leaseMgr) throws IOException
{
discMgr = discoveryMgr;
try {
createJoinManager(serviceID, serviceProxy, attrSets,
(ServiceIDListener)null, leaseMgr,
EmptyConfiguration.INSTANCE);
} catch(ConfigurationException e) { /* swallow this exception */ }
}//end constructor
/**
* Constructs an instance of this class, configured using the items
* retrieved through the given <code>Configuration</code>, that will
* register the service with all discovered lookup services, using the
* supplied <code>ServiceID</code>. This constructor is typically used by
* services which have already been assigned a service ID (possibly
* by the service provider itself or as a result of a prior registration
* with some lookup service), and which wish to allow for deployment-time
* configuration of the service's join processing.
* <p>
* The items used to configure the current instance of this class
* are obtained through the object input to the <code>config</code>
* parameter. If <code>null</code> is input to that parameter, a
* <code>NullPointerException</code> is thrown.
* <p>
* Except that the desired <code>ServiceID</code> is supplied through the
* <code>serviceID</code> parameter rather than through a notification
* sent to a <code>ServiceIDListener</code>, all other parameters
* of this form of the constructor, along with their associated semantics,
* are identical to that of the six-argument constructor that takes
* a <code>ServiceIDListener</code>.
*
* @param serviceProxy a reference to the service requesting the services
* of this class
* @param attrSets array of <code>Entry</code> consisting of the
* attribute sets with which to register the service.
* @param serviceID an instance of <code>ServiceID</code> with which to
* register the service with all desired lookup
* services
* @param discoveryMgr reference to the <code>DiscoveryManagement</code>
* object this class should use to manage lookup
* service discovery on behalf of the given service
* @param leaseMgr reference to the <code>LeaseRenewalManager</code>
* object this class should use to manage the leases
* on the given service's residency in the lookup
* services that have been joined
* @param config instance of <code>Configuration</code> through
* which the items used to configure the current
* instance of this class are obtained
*
* @throws java.lang.IllegalArgumentException if the object input to the
* <code>serviceProxy</code> parameter is not serializable
* @throws java.lang.NullPointerException if <code>null</code> is input
* to the <code>serviceProxy</code> parameter or the
* <code>config</code> parameter, or if at least one of the
* elements of the <code>attrSets</code> parameter is
* <code>null</code>
* @throws java.io.IOException if initiation of discovery process results
* in <code>IOException</code> when socket allocation occurs
* @throws net.jini.config.ConfigurationException if an exception
* occurs while retrieving an item from the given
* <code>Configuration</code> object
*
* @throws java.lang.IllegalStateException if this method is called on
* a terminated <code>JoinManager</code> instance. Note that this
* exception is implementation-specific.
*
* @see net.jini.core.lookup.ServiceID
* @see net.jini.discovery.DiscoveryManagement
* @see net.jini.discovery.LookupDiscoveryManager
* @see net.jini.lease.LeaseRenewalManager
* @see net.jini.config.Configuration
* @see net.jini.config.ConfigurationException
*/
public JoinManager(Object serviceProxy,
Entry[] attrSets,
ServiceID serviceID,
DiscoveryManagement discoveryMgr,
LeaseRenewalManager leaseMgr,
Configuration config)
throws IOException, ConfigurationException
{
discMgr = discoveryMgr;
createJoinManager(serviceID, serviceProxy, attrSets,
(ServiceIDListener)null, leaseMgr, config);
}//end constructor
/**
* Returns the instance of <code>DiscoveryManagement</code> that was
* either passed into the constructor, or that was created as a result
* of <code>null</code> being input to that parameter.
* <p>
* The object returned by this method encapsulates the mechanism by which
* either the <code>JoinManager</code> or the entity itself can set
* discovery listeners and discard previously discovered lookup services
* when they are found to be unavailable.
*
* @return the instance of the <code>DiscoveryManagement</code> interface
* that was either passed into the constructor, or that was
* created as a result of <code>null</code> being input to that
* parameter.
*
* @see net.jini.discovery.DiscoveryManagement
* @see net.jini.discovery.LookupDiscoveryManager
*/
public DiscoveryManagement getDiscoveryManager(){
synchronized(this) {
if(bTerminated) {
throw new IllegalStateException("join manager was terminated");
}//endif
}//end sync
return discMgr;
}//end getDiscoveryManager
/**
* Returns the instance of the <code>LeaseRenewalManager</code> class
* that was either passed into the constructor, or that was created
* as a result of <code>null</code> being input to that parameter.
* <p>
* The object returned by this method manages the leases requested and
* held by the <code>JoinManager</code>. Although it may also manage
* leases unrelated to the join process that are requested and held by
* the service itself, the leases with which the <code>JoinManager</code>
* is concerned are the leases that correspond to the service registration
* requests made with each lookup service the service wishes to join.
*
* @return the instance of the <code>LeaseRenewalManager</code> class
* that was either passed into the constructor, or that was
* created as a result of <code>null</code> being input to that
* parameter.
*
* @see net.jini.discovery.DiscoveryManagement
* @see net.jini.lease.LeaseRenewalManager
*/
public LeaseRenewalManager getLeaseRenewalManager(){
synchronized(this) {
if(bTerminated) {
throw new IllegalStateException("join manager was terminated");
}//endif
}//end sync
return leaseRenewalMgr;
}//end getLeaseRenewalManager
/**
* Returns an array of <code>ServiceRegistrar</code> objects, each
* corresponding to a lookup service with which the service is currently
* registered (joined). If there are no lookup services with which the
* service is currently registered, this method returns the empty array.
* This method returns a new array upon each invocation.
*
* @return array of instances of <code>ServiceRegistrar</code>, each
* corresponding to a lookup service with which the service is
* currently registered
*
* @see net.jini.core.lookup.ServiceRegistrar
*/
public ServiceRegistrar[] getJoinSet() {
synchronized(this) {
if(bTerminated) {
throw new IllegalStateException("join manager was terminated");
}//endif
}//end sync
synchronized(joinSet) {
ArrayList retList = new ArrayList(joinSet.size());
int k = 0;
for (Iterator iter = joinSet.iterator(); iter.hasNext(); ) {
ProxyReg proxyReg = (ProxyReg)iter.next();
if(proxyReg.srvcRegistration != null) {//test registration flag
retList.add(proxyReg.proxy);
}//endif
}//end loop
return ( (ServiceRegistrar[])(retList.toArray
(new ServiceRegistrar[retList.size()]) ) );
}//end sync(joinSet)
}//end getJoinSet
/**
* Returns an array containing the set of attributes currently associated
* with the service. If the service is not currently associated with an
* attribute set, this method returns the empty array. This method returns
* a new array upon each invocation.
*
* @return array of instances of <code>Entry</code> consisting of the
* set of attributes with which the service is registered in
* each lookup service that it has joined
*
* @see net.jini.core.entry.Entry
* @see #setAttributes
*/
public Entry[] getAttributes() {
synchronized(this) {
if(bTerminated) {
throw new IllegalStateException("join manager was terminated");
}//endif
}//end sync
synchronized(joinSet) {
return (Entry[])lookupAttr.clone();
}//end sync(joinSet)
}//end getAttributes
/**
* Associates a new set of attributes with the service, in addition to
* the service's current set of attributes. The association of this new
* set of attributes with the service will be propagated to each lookup
* service with which the service is registered. Note that this
* propagation is performed asynchronously, thus there is no guarantee
* that the propagation of the attributes to all lookup services with
* which the service is registered will have completed upon return from
* this method.
* <p>
* An invocation of this method with duplicate elements in the
* <code>attrSets</code> parameter (where duplication means attribute
* equality as defined by calling the <code>MarshalledObject.equals</code>
* method on field values) is equivalent to performing the invocation
* with the duplicates removed from that parameter.
* <p>
* Note that because there is no guarantee that attribute propagation
* will have completed upon return from this method, services that
* invoke this method must take care not to modify the contents of the
* <code>attrSets</code> parameter. Doing so could cause the service's
* attribute state to be corrupted or inconsistent on a subset of the
* lookup services with which the service is registered as compared with
* the state reflected on the remaining lookup services. It is for this
* reason that the effects of modifying the contents of the
* <code>attrSets</code> parameter, after this method is invoked, are
* undefined.
*
* @param attrSets array of <code>Entry</code> consisting of the
* attribute sets with which to augment the service's
* current set of attributes
*
* @throws java.lang.NullPointerException if either <code>null</code> is
* input to the <code>attrSets</code> parameter, or one or more
* of the elements of the <code>attrSets</code> parameter is
* <code>null</code>
*
* @see net.jini.core.entry.Entry
*/
public void addAttributes(Entry[] attrSets) {
addAttributes(attrSets, false);
}//end addAttributes
/**
* Associates a new set of attributes with the service, in addition to
* the service's current set of attributes. The association of this new
* set of attributes with the service will be propagated to each lookup
* service with which the service is registered. Note that this
* propagation is performed asynchronously, thus there is no guarantee
* that the propagation of the attributes to all lookup services with
* which the service is registered will have completed upon return from
* this method.
* <p>
* An invocation of this method with duplicate elements in the
* <code>attrSets</code> parameter (where duplication means attribute
* equality as defined by calling the <code>MarshalledObject.equals</code>
* method on field values) is equivalent to performing the invocation
* with the duplicates removed from that parameter.
* <p>
* Note that because there is no guarantee that attribute propagation
* will have completed upon return from this method, services that
* invoke this method must take care not to modify the contents of the
* <code>attrSets</code> parameter. Doing so could cause the service's
* attribute state to be corrupted or inconsistent on a subset of the
* lookup services with which the service is registered as compared with
* the state reflected on the remaining lookup services. It is for this
* reason that the effects of modifying the contents of the
* <code>attrSets</code> parameter, after this method is invoked, are
* undefined.
* <p>
* A service typically employs this version of <code>addAttributes</code>
* to prevent clients or other services from attempting to add what are
* referred to as "service controlled attributes" to the service's set.
* A service controlled attribute is an attribute that implements the
* <code>ServiceControlled</code> marker interface.
* <p>
* Consider a printer service. With printers, there are often times error
* conditions, that only the printer can detect (for example, a paper
* jam or a toner low condition). To report conditions such as these to
* interested parties, the printer typically adds an attribute to its
* attribute set, resulting in an event being sent that notifies clients
* that have registered interest in such events. When the condition is
* corrected, the printer would then remove the attribute from its set
* by invoking the <code>modifyAttributes</code> method in the appropriate
* manner.
* <p>
* Attributes representing conditions that only the service can know about
* or control are good candidates for being defined as service controlled
* attributes. That is, the service provider (the developer of the printer
* service for example) would define the attributes that represent
* conditions such as those just described to implement the
* <code>ServiceControlled</code> marker interface. Thus, when other
* entities attempt to add new attributes, services that wish to employ
* such service controlled attributes should ultimately invoke only this
* version of <code>addAttributes</code> (with the <code>checkSC</code>
* parameter set to <code>true</code>), resulting in a
* <code>SecurityException</code> if any of the attributes being added
* happen to be service controlled attributes. In this way, only the
* printer itself would be able to set a "paper jammed" or "toner low"
* attribute, not some arbitrary client.
*
* @param attrSets array of <code>Entry</code> consisting of the
* attribute sets with which to augment the service's
* current set of attributes
* @param checkSC <code>boolean</code> flag indicating whether the
* elements of the set of attributes to add should be
* checked to determine if they are service controlled
* attributes
*
* @throws java.lang.NullPointerException if either <code>null</code> is
* input to the <code>attrSets</code> parameter, or one or more
* of the elements of the <code>attrSets</code> parameter is
* <code>null</code>
*
* @throws java.lang.SecurityException if the <code>checkSC</code>
* parameter is <code>true</code>, and at least one of the
* attributes to be added is an instance of the
* <code>ServiceControlled</code> marker interface
*
* @see net.jini.core.entry.Entry
* @see net.jini.lookup.entry.ServiceControlled
*/
public void addAttributes(Entry[] attrSets, boolean checkSC) {
synchronized(this) {
if(bTerminated) {
throw new IllegalStateException("join manager was terminated");
}//endif
}//end sync
synchronized(joinSet) {
lookupAttr = LookupAttributes.add(lookupAttr, attrSets, checkSC);
serviceItem.attributeSets = lookupAttr;
for(int i=0;i<joinSet.size();i++) {
ProxyReg proxyReg = (ProxyReg)joinSet.get(i);
proxyReg.addTask(new AddAttributesTask(proxyReg,attrSets));
}//end loop
}//end sync(joinSet)
}//end addAttributes
/**
* Replaces the service's current set of attributes with a new set of
* attributes. The association of this new set of attributes with the
* service will be propagated to each lookup service with which the
* service is registered. Note that this propagation is performed
* asynchronously, thus there is no guarantee that the propagation of
* the attributes to all lookup services with which the service is
* registered will have completed upon return from this method.
* <p>
* An invocation of this method with duplicate elements in the
* <code>attrSets</code> parameter (where duplication means attribute
* equality as defined by calling the <code>MarshalledObject.equals</code>
* method on field values) is equivalent to performing the invocation
* with the duplicates removed from that parameter.
* <p>
* Note that because there is no guarantee that attribute propagation
* will have completed upon return from this method, services that
* invoke this method must take care not to modify the contents of the
* <code>attrSets</code> parameter. Doing so could cause the service's
* attribute state to be corrupted or inconsistent on a subset of the
* lookup services with which the service is registered as compared with
* the state reflected on the remaining lookup services. It is for this
* reason that the effects of modifying the contents of the
* <code>attrSets</code> parameter, after this method is invoked, are
* undefined.
*
* @param attrSets array of <code>Entry</code> consisting of the
* attribute sets with which to replace the service's
* current set of attributes
*
* @throws java.lang.NullPointerException if either <code>null</code> is
* input to the <code>attrSets</code> parameter, or one or more
* of the elements of the <code>attrSets</code> parameter is
* <code>null</code>.
*
* @see net.jini.core.entry.Entry
* @see #getAttributes
*/
public void setAttributes(Entry[] attrSets) {
synchronized(this) {
if(bTerminated) {
throw new IllegalStateException("join manager was terminated");
}//endif
}//end sync
testForNullElement(attrSets);
synchronized(joinSet) {
lookupAttr = (Entry[]) attrSets.clone();
serviceItem.attributeSets = lookupAttr;
for(int i=0;i<joinSet.size();i++) {
ProxyReg proxyReg = (ProxyReg)joinSet.get(i);
proxyReg.addTask(new SetAttributesTask(proxyReg,attrSets));
}//end loop
}//end sync(joinSet)
}//end setAttributes
/**
* Changes the service's current set of attributes using the same
* semantics as the <code>modifyAttributes</code> method of the
* <code>ServiceRegistration</code> class.
* <p>
* The association of the new set of attributes with the service will
* be propagated to each lookup service with which the service is
* registered. Note that this propagation is performed asynchronously,
* thus there is no guarantee that the propagation of the attributes to
* all lookup services with which the service is registered will have
* completed upon return from this method.
* <p>
* Note that if the length of the array containing the templates does
* not equal the length of the array containing the modifications, an
* <code>IllegalArgumentException</code> will be thrown and propagated
* through this method.
* <p>
* Note also that because there is no guarantee that attribute propagation
* will have completed upon return from this method, services that
* invoke this method must take care not to modify the contents of the
* <code>attrSets</code> parameter. Doing so could cause the service's
* attribute state to be corrupted or inconsistent on a subset of the
* lookup services with which the service is registered as compared with
* the state reflected on the remaining lookup services. It is for this
* reason that the effects of modifying the contents of the
* <code>attrSets</code> parameter, after this method is invoked, are
* undefined.
*
* @param attrSetTemplates array of <code>Entry</code> used to identify
* which elements to modify from the service's
* current set of attributes
* @param attrSets array of <code>Entry</code> containing the
* actual modifications to make in the matching
* sets found using the
* <code>attrSetTemplates</code> parameter
*
* @throws java.lang.IllegalArgumentException if the array containing the
* templates does not equal the length of the array containing the
* modifications
*
* @see net.jini.core.entry.Entry
* @see net.jini.core.lookup.ServiceRegistration#modifyAttributes
*/
public void modifyAttributes(Entry[] attrSetTemplates, Entry[] attrSets) {
modifyAttributes(attrSetTemplates, attrSets, false);
}//end modifyAttributes
/**
* Changes the service's current set of attributes using the same
* semantics as the <code>modifyAttributes</code> method of the
* <code>ServiceRegistration</code> class.
* <p>
* The association of the new set of attributes with the service will
* be propagated to each lookup service with which the service is
* registered. Note that this propagation is performed asynchronously,
* thus there is no guarantee that the propagation of the attributes to
* all lookup services with which the service is registered will have
* completed upon return from this method.
* <p>
* Note that if the length of the array containing the templates does
* not equal the length of the array containing the modifications, an
* <code>IllegalArgumentException</code> will be thrown and propagated
* through this method.
* <p>
* Note also that because there is no guarantee that attribute propagation
* will have completed upon return from this method, services that
* invoke this method must take care not to modify the contents of the
* <code>attrSets</code> parameter. Doing so could cause the service's
* attribute state to be corrupted or inconsistent on a subset of the
* lookup services with which the service is registered as compared with
* the state reflected on the remaining lookup services. It is for this
* reason that the effects of modifying the contents of the
* <code>attrSets</code> parameter, after this method is invoked, are
* undefined.
* <p>
* A service typically employs this version of
* <code>modifyAttributes</code> to prevent clients or other services
* from attempting to modify what are referred to as "service controlled
* attributes" in the service's set. A service controlled attribute is an
* attribute that implements the <code>ServiceControlled</code> marker
* interface.
* <p>
* Attributes representing conditions that only the service can know about
* or control are good candidates for being defined as service controlled
* attributes. When other entities attempt to modify a service's
* attributes, if the service wishes to employ such service controlled
* attributes, the service should ultimately invoke only this version
* of <code>modifyAttributes</code> (with the <code>checkSC</code>
* parameter set to <code>true</code>), resulting in a
* <code>SecurityException</code> if any of the attributes being modified
* happen to be service controlled attributes.
*
* @param attrSetTemplates array of <code>Entry</code> used to identify
* which elements to modify from the service's
* current set of attributes
* @param attrSets array of <code>Entry</code> containing the
* actual modifications to make in the matching
* sets found using the
* <code>attrSetTemplates</code> parameter
* @param checkSC <code>boolean</code> flag indicating whether the
* elements of the set of attributes to modify
* should be checked to determine if they are
* service controlled attributes
*
* @throws java.lang.IllegalArgumentException if the array containing the
* templates does not equal the length of the array containing
* the modifications
*
* @throws java.lang.SecurityException if the <code>checkSC</code>
* parameter is <code>true</code>, and at least one of the
* attributes to be modified is an instance of the
* <code>ServiceControlled</code> marker interface
*
* @see net.jini.core.entry.Entry
* @see net.jini.core.lookup.ServiceRegistration#modifyAttributes
* @see net.jini.lookup.entry.ServiceControlled
*/
public void modifyAttributes(Entry[] attrSetTemplates,
Entry[] attrSets,
boolean checkSC)
{
synchronized(this) {
if(bTerminated) {
throw new IllegalStateException("join manager was terminated");
}//endif
}//end sync
synchronized(joinSet) {
lookupAttr = LookupAttributes.modify(lookupAttr, attrSetTemplates,
attrSets, checkSC);
serviceItem.attributeSets = lookupAttr;
for(int i=0;i<joinSet.size();i++) {
ProxyReg proxyReg = (ProxyReg)joinSet.get(i);
proxyReg.addTask(new ModifyAttributesTask(proxyReg,
attrSetTemplates,
attrSets));
}//end loop
}//end sync(joinSet)
}//end modifyAttributes
/**
* Performs cleanup duties related to the termination of the lookup
* service discovery event mechanism, as well as the lease and
* thread management performed by the <code>JoinManager</code>. This
* method will cancel all of the service's managed leases that were
* granted by the lookup services with which the service is registered,
* and will terminate all threads that have been created.
* <p>
* Note that if the discovery manager employed by the instance of this
* class that is being terminated was created by the instance itself,
* this method will terminate all discovery processing being performed by
* that manager object on behalf of the service; otherwise, the discovery
* manager supplied by the service is still valid.
* <p>
* Whether an instance of the <code>LeaseRenewalManager</code> class was
* supplied by the service or created by the <code>JoinManager</code>
* itself, any reference to that object obtained by the service prior to
* termination will still be valid after termination.
* Note also this class makes certain concurrency guarantees with respect
* to an invocation of the terminate method while other method invocations
* are in progress. The termination process will not begin until
* completion of all invocations of the methods defined in the public
* interface of this class. Furthermore, once the termination process has
* begun, no further remote method invocations will be made by this class,
* and all other method invocations made on this class will not return
* until the termination process has completed.
* <p>
* Upon completion of the termination process, the semantics of all
* current and future method invocations on the instance of this class
* that was just terminated are undefined; although the reference to the
* <code>LeaseRenewalManager</code> object employed by that instance
* of <code>JoinManager</code> is still valid.
*/
public void terminate() {
synchronized(this) {
if(bTerminated) return;//allow for multiple terminations
bTerminated = true;
/* Terminate discovery and task management */
discMgr.removeDiscoveryListener(discMgrListener);
if(bCreateDiscMgr) discMgr.terminate();
}//end sync(this)
terminateTaskMgr();
/* Clear the joinSet and cancel all leases held by the service */
ArrayList srvcLeases = null;//store leases for use outside of sync blk
synchronized(joinSet) {
srvcLeases = new ArrayList(joinSet.size());
for (Iterator iter = joinSet.iterator(); iter.hasNext(); ) {
srvcLeases.add
( (((ProxyReg)iter.next()).serviceLease) );
}//end loop
joinSet.clear();
}//end sync(joinSet)
/* Must cancel leases outside of sync block because of remote call */
if(srvcLeases == null) return;
for(int i=0;i<srvcLeases.size();i++) {
try {
leaseRenewalMgr.cancel((Lease)srvcLeases.get(i));
} catch (Exception e) { }
}//end loop
}//end terminate
/**
* Registers a new reference to the service with all current and future
* discovered lookup services. The new service reference will replace
* the reference that was previously registered as a result of either
* constructing this utility, or a prior invocation of one of the forms
* of this method. The new service reference will be registered using
* the same <code>ServiceID</code> with which previous registrations
* were made through this utility.
* <p>
* The value input to the <code>serviceProxy</code> parameter represents
* the new service reference (proxy) to register with each discovered
* lookup service. If the <code>Object</code> input to that parameter is
* not <code>Serializable</code>, an <code>IllegalArgumentException</code>
* is thrown. If <code>null</code> is input to that parameter, a
* <code>NullPointerException</code> is thrown.
* <p>
* The attribute sets that this method associates with the new service
* reference are the same attribute sets as those associated with the
* old registration.
*
* @param serviceProxy the new service reference (proxy) to register with
* all current and future discovered lookup services
*
* @throws java.lang.IllegalArgumentException if the object input to the
* <code>serviceProxy</code> parameter is not serializable
* @throws java.lang.NullPointerException if <code>null</code> is input
* to the <code>serviceProxy</code> parameter
*
* @throws java.lang.IllegalStateException if this method is called on
* a terminated <code>JoinManager</code> instance. Note that this
* exception is implementation-specific.
*/
public void replaceRegistration(Object serviceProxy) {
replaceRegistrationDo(serviceProxy, null, false);
}//end replaceRegistration
/**
* Registers a new reference to the service with all current and future
* discovered lookup services, applying semantics identical to the
* one-argument form of this method, except with respect to the
* registration of the given attribute sets.
* <p>
* This form of <code>replaceRegistration</code> takes as its
* second parameter, an array of <code>Entry</code> objects
* (<code>attrSets</code>), none of whose elements may be
* <code>null</code>, that represents the new set of attributes to
* associate with the new service reference to be registered. As with
* the constructor to this utility, passing <code>null</code> as the
* value of the <code>attrSets</code> parameter is equivalent to passing
* an empty array. If any of the elements of <code>attrSets</code> are
* <code>null</code>, a <code>NullPointerException</code> is thrown.
* This new set of attributes will be associated with the service in
* all future join processing.
*
* @param serviceProxy the new service reference (proxy) to register with
* all current and future discovered lookup services
* @param attrSets array of <code>Entry</code> consisting of the
* attribute sets with which to register the new
* service reference. Passing <code>null</code> as
* the value of this parameter is equivalent to
* passing an empty <code>Entry</code> array
*
* @throws java.lang.IllegalArgumentException if the object input to the
* <code>serviceProxy</code> parameter is not serializable
* @throws java.lang.NullPointerException if either <code>null</code> is
* input to the <code>serviceProxy</code> parameter, or at least
* one of the elements of the <code>attrSets</code> parameter is
* <code>null</code>
*
* @throws java.lang.IllegalStateException if this method is called on
* a terminated <code>JoinManager</code> instance. Note that this
* exception is implementation-specific.
*/
public void replaceRegistration(Object serviceProxy, Entry[] attrSets) {
replaceRegistrationDo(serviceProxy, attrSets, true);
}//end replaceRegistration
/** Convenience method invoked by the constructors of this class that
* uses the given <code>Configuration</code> to initialize the current
* instance of this utility, and initiates all join processing for
* the given parameters. This method handles the various configurations
* allowed by the different constructors.
*/
private void createJoinManager(ServiceID serviceID,
Object serviceProxy,
Entry[] attrSets,
ServiceIDListener callback,
LeaseRenewalManager leaseMgr,
Configuration config)
throws IOException, ConfigurationException
{
if(!(serviceProxy instanceof java.io.Serializable)) {
throw new IllegalArgumentException
("serviceProxy must be Serializable");
}//endif
/* Retrieve configuration items if applicable */
if(config == null) throw new NullPointerException("config is null");
/* Proxy preparers */
registrarPreparer = (ProxyPreparer)config.getEntry
(COMPONENT_NAME,
"registrarPreparer",
ProxyPreparer.class,
new BasicProxyPreparer());
registrationPreparer = (ProxyPreparer)config.getEntry
(COMPONENT_NAME,
"registrationPreparer",
ProxyPreparer.class,
new BasicProxyPreparer());
serviceLeasePreparer = (ProxyPreparer)config.getEntry
(COMPONENT_NAME,
"serviceLeasePreparer",
ProxyPreparer.class,
new BasicProxyPreparer());
/* Task manager */
try {
taskMgr = (TaskManager)config.getEntry(COMPONENT_NAME,
"taskManager",
TaskManager.class);
} catch(NoSuchEntryException e) { /* use default */
taskMgr = new TaskManager(MAX_N_TASKS,(15*1000),1.0f);
}
/* Wakeup manager */
try {
wakeupMgr = (WakeupManager)config.getEntry(COMPONENT_NAME,
"wakeupManager",
WakeupManager.class);
} catch(NoSuchEntryException e) { /* use default */
wakeupMgr = new WakeupManager
(new WakeupManager.ThreadDesc(null,true));
}
/* Max number of times to re-schedule tasks in thru wakeup manager */
maxNRetries = ((Integer)config.getEntry
(COMPONENT_NAME,
"wakeupRetries",
int.class,
new Integer(maxNRetries))).intValue();
if(attrSets == null) {
lookupAttr = new Entry[0];
} else {
attrSets = (Entry[])attrSets.clone();
LookupAttributes.check(attrSets,false);//null elements NOT ok
lookupAttr = attrSets;
}//endif
serviceItem = new ServiceItem(serviceID, serviceProxy, lookupAttr);
/* Lease renewal manager */
leaseRenewalMgr = leaseMgr;
if(leaseRenewalMgr == null) {
try {
leaseRenewalMgr = (LeaseRenewalManager)config.getEntry
(COMPONENT_NAME,
"leaseManager",
LeaseRenewalManager.class);
} catch(NoSuchEntryException e) { /* use default */
leaseRenewalMgr = new LeaseRenewalManager(config);
}
}//endif
renewalDuration = ((Long)config.getEntry
(COMPONENT_NAME,
"maxLeaseDuration",
long.class,
new Long(renewalDuration))).longValue();
if( (renewalDuration == 0) || (renewalDuration < Lease.ANY) ) {
throw new ConfigurationException("invalid configuration entry: "
+"renewalDuration ("
+renewalDuration+") must be "
+"positive or Lease.ANY");
}//endif
this.callback = callback;
/* Discovery manager */
if(discMgr == null) {
bCreateDiscMgr = true;
try {
discMgr = (DiscoveryManagement)config.getEntry
(COMPONENT_NAME,
"discoveryManager",
DiscoveryManagement.class);
} catch(NoSuchEntryException e) { /* use default */
discMgr = new LookupDiscoveryManager
(new String[] {""}, null, null, config);
}
}//endif
discMgr.addDiscoveryListener(discMgrListener);
}//end createJoinManager
/** For the given lookup service proxy, searches the <code>joinSet</code>
* for the corresponding <code>ProxyReg</code> element, and upon finding
* such an element, returns that element; otherwise returns
* <code>null</code>.
*/
private ProxyReg findReg(ServiceRegistrar proxy) {
for (Iterator iter = joinSet.iterator(); iter.hasNext(); ) {
ProxyReg reg =(ProxyReg)iter.next();
if(reg.proxy.equals(proxy)) return reg;
}//end loop
return null;
}//end findReg
/** Removes (from the task manager) and cancels (in the wakeup manager)
* all tasks associated with the given instance of <code>ProxyReg</code>.
*/
private void removeTasks(ProxyReg proxyReg) {
if(proxyReg == null) return;
if(taskMgr == null) return;
synchronized(proxyReg.taskList) {
if(proxyReg.proxyRegTask != null) {
synchronized(taskMgr) {
taskMgr.remove(proxyReg.proxyRegTask);
}//end sync(taskMgr)
proxyReg.proxyRegTask.cancel();//cancel retry in WakeupMgr
proxyReg.proxyRegTask = null; //don't reuse because of seq#
}//endif
proxyReg.taskList.clear();
}//end sync(proxyReg.taskList)
}//end removeTasks
/** Removes from the task manager, all pending tasks regardless of the
* the instance of <code>ProxyReg</code> with which the task is
* associated, and then terminates the task manager, and makes it
* a candidate for garbage collection.
*/
private void terminateTaskMgr() {
synchronized(wakeupMgr) {
/* Cancel all tasks scheduled for future retry by the wakeup mgr */
wakeupMgr.cancelAll();//cancel all tickets
wakeupMgr.stop();//stop execution of the wakeup manager
synchronized(taskMgr) {
/* Remove all pending tasks */
ArrayList pendingTasks = taskMgr.getPending();
for(int i=0;i<pendingTasks.size();i++) {
RetryTask pendingTask = (RetryTask)pendingTasks.get(i);
pendingTask.cancel();//cancel wakeup ticket
taskMgr.remove(pendingTask);//remove from task mgr
}//end loop
/* Interrupt all active tasks, prepare taskMgr for GC. */
taskMgr.terminate();
taskMgr = null;
}//end sync(taskMgr)
wakeupMgr = null;
}//end sync(wakeupMgr)
}//end terminateTaskMgr
/** Examines the elements of the input set and, upon finding at least one
* <code>null</code> element, throws a <code>NullPointerException</code>.
*/
private void testForNullElement(Object[] a) {
if(a == null) return;
for(int i=0;i<a.length;i++) {
if(a[i] == null) {
throw new NullPointerException
("input array contains at least one null element");
}//endif
}//end loop
}//end testForNullElement
/** Convenience method invoked by either form of the method
* <code>replaceRegistration</code>. This method registers the
* given <code>serviceProxy</code> with all discovered lookup
* services, replacing all current registrations. If the value
* of the <code>doAttrs</code> parameter is <code>true</code>,
* this method will associate the given <code>attrSets</code>
* with the new service registration; otherwise, it will use
* the attribute sets currently associated with the old registration.
*/
private void replaceRegistrationDo(Object serviceProxy,
Entry[] attrSets,
boolean doAttrs)
{
synchronized(this) {
if(bTerminated) {
throw new IllegalStateException("join manager was terminated");
}//endif
}//end sync
if(!(serviceProxy instanceof java.io.Serializable)) {
throw new IllegalArgumentException
("serviceProxy must be Serializable");
}//endif
synchronized(joinSet) {
if(doAttrs) {
if(attrSets == null) {
lookupAttr = new Entry[0];
} else {
attrSets = (Entry[])attrSets.clone();
LookupAttributes.check(attrSets,false);//no null elements
lookupAttr = attrSets;
}//endif
}//endif
serviceItem.service = serviceProxy;
serviceItem.attributeSets = lookupAttr;
for(int i=0;i<joinSet.size();i++) {
ProxyReg proxyReg = (ProxyReg)(joinSet.get(i));
removeTasks(proxyReg);
try {
leaseRenewalMgr.remove( proxyReg.serviceLease );
} catch (Exception e) { }
proxyReg.addTask(new RegisterTask(proxyReg,
(Entry[])lookupAttr.clone()));
}//end loop
}//end sync(joinSet)
}//end replaceRegistrationDo
}//end class JoinManager