All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jini.lookup.ServiceDiscoveryManager Maven / Gradle / Ivy

The newest version!
/*
 * 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 java.io.IOException;
import java.rmi.RemoteException;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.config.EmptyConfiguration;
import net.jini.config.NoSuchEntryException;
import net.jini.core.entry.Entry;
import net.jini.core.event.EventRegistration;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.lease.Lease;
import net.jini.core.lookup.ServiceID;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceMatches;
import net.jini.core.lookup.ServiceRegistrar;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.discovery.DiscoveryEvent;
import net.jini.discovery.DiscoveryListener;
import net.jini.discovery.DiscoveryManagement;
import net.jini.discovery.LookupDiscoveryManager;
import net.jini.lookup.ServiceAttributesAccessor;
import net.jini.lookup.ServiceIDAccessor;
import net.jini.lookup.ServiceProxyAccessor;
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 org.apache.river.action.GetBooleanAction;
import org.apache.river.action.GetLongAction;
import org.apache.river.logging.Levels;
import org.apache.river.lookup.entry.LookupAttributes;
import org.apache.river.thread.NamedThreadFactory;

/**
 * The ServiceDiscoveryManager class is a helper utility class that
 * any client-like entity can use to "discover" services registered with any
 * number of lookup services of interest. On behalf of such entities, this class
 * maintains - as much as possible - up-to-date state information about both the
 * lookup services the entity wishes to query, and the services the entity
 * wishes to acquire and use. By maintaining current service state information,
 * the entity can implement efficient mechanisms for service access and usage.
 * 

* There are three basic usage patterns for this class. In order of importance * and typical usage, those patterns are: *

*

    *
  • The entity requests that the ServiceDiscoveryManager create * a cache (an instance of {@link net.jini.lookup.LookupCache LookupCache}) * which will asynchronously "discover", and locally store, references to * services that match criteria defined by the entity; services which are * registered with one or more lookup services managed by the * ServiceDiscoveryManager on behalf of the entity. The cache can * be viewed as a set of service references that the entity can access locally * as needed through one of the public, non-remote methods provided in the * cache's interface. Thus, rather than making costly remote queries of multiple * lookup services at the point in time when the entity needs the service, the * entity can simply make local queries on the cache for the services that the * cache acquired and stored at a prior time. An entity should employ this * pattern when the entity must make frequent * queries for multiple services. By populating the cache with multiple * instances of the desired services, redundancy in the availability of those * services can be provided. Thus, if an instance of a service is found to be * unavailable when needed, the entity can execute a local query on the cache * rather than one or more remote queries on the lookup services to acquire an * instance that is available. To employ this pattern, the entity invokes the * method {@link net.jini.lookup.ServiceDiscoveryManager#createLookupCache * createLookupCache}. *
  • The entity can register with the event mechanism provided by the * ServiceDiscoveryManager. This event mechanism allows the entity * to request that it be notified when a service of interest is discovered for * the first time, or has encountered a state change such as removal from all * lookup services, or attribute set changes. Although interacting with a local * cache of services in the way described in the first pattern can be very * useful to entities that need frequent access to multiple services, some * client-like entities may wish to interact with the cache in a reactive * manner. For example, an entity such as a service browser typically wishes to * be notified of the arrival of new services of interest as well as any changes * in the state of the current services in the cache. In these situations, * polling for such changes is usually viewed as undesirable. If the cache were * to also provide an event mechanism with notification semantics, the needs of * entities that employ either pattern can be satisfied. To employ this pattern, * the entity must create a cache and supply it with an instance of the {@link net.jini.lookup.ServiceDiscoveryListener * ServiceDiscoveryListener} interface that will receive instances of * {@link net.jini.lookup.ServiceDiscoveryEvent ServiceDiscoveryEvent} when * events of interest, related to the services in the cache, occur. *
  • The entity, through the public API of the * ServiceDiscoveryManager, can directly query the lookup services * managed by the ServiceDiscoveryManager for services of interest; * employing semantics similar to the semantics employed in a typical lookup * service query made through the * {@link net.jini.core.lookup.ServiceRegistrar ServiceRegistrar} interface. * Such queries will result in a remote call being made at the same time the * service is needed (unlike the first pattern, in which remote calls typically * occur prior to the time the service is needed). This pattern may be useful to * entities needing to find services on an infrequent basis, or when the cost of * making a remote call is outweighed by the overhead of maintaining a local * cache (for example, due to limited resources). Although an entity that needs * to query lookup service(s) can certainly make such queries through the * {@link net.jini.core.lookup.ServiceRegistrar ServiceRegistrar} interface, the * ServiceDiscoveryManager provides a broad API with semantics that * are richer than the semantics of the * {@link net.jini.core.lookup.ServiceRegistrar#lookup lookup} methods provided * by the {@link net.jini.core.lookup.ServiceRegistrar * ServiceRegistrar}. This API encapsulates functionality that many client-like * entities may find more useful when managing both the set of desired lookup * services, and the service queries made on those lookup services. To employ * this pattern, the entity simply instantiates this class with the desired * parameters, and then invokes the appropriate version of the * {@link net.jini.lookup.ServiceDiscoveryManager#lookup lookup} method when the * entity wishes to acquire a service that matches desired criteria. *
*

* All three mechanisms just described - local queries on the cache, service * discovery notification, and remote lookups - employ the same * template-matching scheme as that employed in the * {@link net.jini.core.lookup.ServiceRegistrar ServiceRegistrar} interface. * Additionally, each mechanism allows the entity to supply an object referred * to as a filter; an instance of * {@link net.jini.lookup.ServiceItemFilter ServiceItemFilter}. A filter is a * non-remote object that defines additional matching criteria that the * ServiceDiscoveryManager applies when searching for the entity's * services of interest. Employing a filter is particularly useful to entities * that wish to extend the capabilities of the standard template-matching * scheme. *

* In addition to (or instead of) employing a filter to apply additional * matching criteria to candidate service proxies initially found through * template matching, filters can also be used to extend the selection process * so that only proxies that are safe to use are returned to the entity. * To do this, the entity would use the * {@link net.jini.lookup.ServiceItemFilter ServiceItemFilter} interface to * supply the ServiceDiscoveryManager or * {@link net.jini.lookup.LookupCache LookupCache} with a filter that, when * applied to a candidate proxy, performs a set of operations that is referred * to as proxy preparation. As described in the documentation for * {@link net.jini.security.ProxyPreparer}, proxy preparation typically includes * operations such as, verifying trust in the proxy, specifying client * constraints, and dynamically granting necessary permissions to the proxy. *

* Note that this utility class is not remote. Clients and services that wish to * use this class will create an instance of this class in their own address * space to manage the state of discovered services and their associated lookup * services locally. * * * * The following implementation-specific items are discussed below: *

* * * * Configuring ServiceDiscoveryManager * * * * This implementation of ServiceDiscoveryManager supports the * following configuration entries; where each configuration entry name is * associated with the component name * net.jini.lookup.ServiceDiscoveryManager. Note that the * configuration entries specified here are specific to this implementation of * ServiceDiscoveryManager. 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. *

* It is important to note that in addition to allowing a client of this utility * to request - through the public API - the creation of a cache that is used * externally by the client, this utility also creates instances of the cache * that are used internally by the utility itself. As such, in addition to the * configuration entries that are used only in this utility (and not in any * cache), and the configuration entries that are retrieved during the * construction of each new cache (and used by only that cache), there are * configuration entries specified below that are retrieved once during the * construction of this utility, but which are shared with, and used by, the * caches that are created. * * * *

* *
* * cacheExecutorService * *
  * Type: {@link java.util.concurrent.ExecutorService ExecutorService} * *
  * Default: new * {@link java.util.concurrent.ThreadPoolExecutor * ThreadPoolExecutor}( 6, 6, 15, TimeUnit.SECONDS, new LinkedBlockingQueue(), * new NamedThreadFactory( "SDM lookup cache", false )) * *
  * Description: * The object that pools and manages the various threads executed by each * of the lookup caches created by this utility. There is one such * ExecutorService created for each cache. For each cache that is created in * this utility, a single, separate instance of this ExecutorService will be * retrieved and employed by that cache. This object should not be shared with * other components in the application that employs this utility. *
* * * * *
* * discardExecutorService * *
  * Type: {@link java.util.concurrent.ScheduledExecutorService ScheduledExecutorService} * *
  * Default: new * {@link java.util.concurrent.ScheduledThreadPoolExecutor * ScheduledThreadPoolExecutor}( 4, * new NamedThreadFactory( "SDM discard timer", false )) * *
  * Description: * The object that pools and manages the threads, executed by a cache, that * wait on verification events after a previousy discovered service has been * discarded. For each cache that is created in this utility, a single, separate * instance of this ExecutorService will be retrieved and employed by that * cache. This object should not be shared with other components in the * application that employs this utility. *
* * * * * * *
* * ServiceEventExecutorService * *
  * Type: {@link java.util.concurrent.ExecutorService ExecutorService} * *
  * Default: new * {@link java.util.concurrent.ThreadPoolExecutor * ThreadPoolExecutor}( 2, 2, 15, TimeUnit.SECONDS, new PriorityBlockingQueue(256), * new NamedThreadFactory( "SDM ServiceEvent: " +toString(), false )) * *
  * Description: * The ExecutorService object that processes incoming ServiceEvent's. This * executor is wrapped by an {@link org.apache.river.thread.ExtensibleExecutorService} * that implements a {@link java.lang.Comparable} {@link java.util.concurrent.FutureTask}, * which orders the ServiceEvent's in the {@link java.util.concurrent.PriorityBlockingQueue}. *

* A single threaded executor is usually sufficient for most purposes, and is * usually seldom found in a running state, testing has shown a single threaded * executor is very unlikely to perform unnecessary lookup's, while a * ThreadPoolExecutor with 4 threads will perform lookup for about 1% of cases. * This can be measured by setting the SDM logger to fine. *

* There is however an issue that does occur when running a single threaded * executor, the lookup events that occur, seem to occur because of a problem * when registering *

* For the purpose of testing, it is beneficial to use a ThreadPoolExecutor with * numerous threads, as this will test the alternate execution paths that * include lookup. *

* * * * * *
* * discardWait * *
  * Type: long * *
  * Default: 2*(5*60*1000) * *
  * Description: * The value used to affect the behavior of the mechanism that handles the * service discard problem described in this utility's specification. * This item allows each entity that uses this utility to define how long (in * milliseconds) to wait for verification from the lookup service(s) that a * discarded service is actually down before committing or un-committing a * requested service discard. The current implementation of this utility * defaults to waiting 10 minutes (twice the maximum lease duration granted by * the Reggie implementation of the lookup service). Note that this item is used * only by the caches (both internal and external) that are created by this * utility, and not by the utility itself. *
* * * * *
* * discoveryManager * *
  * Type: {@link net.jini.discovery.DiscoveryManagement} * *
  * Default: 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) * *
  * Description: * 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. This item is used only by the service discovery manager, and * not by any cache that is created. *
* * * * *
* * eventLeasePreparer * *
  * Type: {@link net.jini.security.ProxyPreparer} * *
  * Default: new {@link net.jini.security.BasicProxyPreparer}() * * *
  * Description: * Preparer for the leases returned when a cache registers with the event * mechanism of any of the discovered lookup services. This item is used only by * the caches (both internal and external) that are created by this utility, and * not by the utility itself. *

* Currently, no methods of the returned proxy are invoked by this utility. *

* * * * *
* * eventListenerExporter * *
  * Type: {@link net.jini.export.Exporter} * *
  * Default: new * {@link net.jini.jeri.BasicJeriExporter#BasicJeriExporter( * net.jini.jeri.ServerEndpoint, * net.jini.jeri.InvocationLayerFactory, * boolean, * boolean) BasicJeriExporter}( * {@link net.jini.jeri.tcp.TcpServerEndpoint#getInstance * TcpServerEndpoint.getInstance}(0),
*                         new * {@link net.jini.jeri.BasicILFactory}(),
*                         * false, false)
* *
  * Description: * Exporter for the remote event listener that each cache supplies to the * lookup services whose event mechanisms those caches register with. Note that * for each cache that is created in this utility, a single, separate instance * of this exporter will be retrieved and employed by that cache. Note also that * the default exporter defined here will disable distributed garbage collection * (DGC) for the server endpoint associated with the exported listener, and the * listener backend (the "impl") will be strongly referenced. This means that * the listener will not "go away" unintentionally. Additionally, that exporter * also sets the keepAlive flag to false to allow the * VM in which this utility runs to "go away" when desired; and not be kept * alive simply because the listener is still exported. *
* * * * *
* * leaseManager * *
  * Type: {@link net.jini.lease.LeaseRenewalManager} * *
  * Default: new * {@link net.jini.lease.LeaseRenewalManager#LeaseRenewalManager( * net.jini.config.Configuration) LeaseRenewalManager}(config) * *
  * Description: * The object used to manage any event leases returned to a cache that has * registered with the event mechanism of the various discovered lookup * services. This entry will be retrieved from the configuration only if no * lease renewal manager is specified in the constructor. This item is used only * by the caches (both internal and external) that are created by this utility, * and not by the utility itself. *
* * * * *
* * registrarPreparer * *
  * Type: {@link net.jini.security.ProxyPreparer} * *
  * Default: new {@link net.jini.security.BasicProxyPreparer}() * * *
  * Description: * Preparer for the proxies to the lookup services that are discovered and * used by this utility. This item is used only by the service discovery * manager, and not by any cache that is created. *

* The following methods of the proxy returned by this preparer are invoked by * this utility: *

    *
  • {@link net.jini.core.lookup.ServiceRegistrar#lookup lookup} *
  • {@link net.jini.core.lookup.ServiceRegistrar#notify notify} *
* *
* * * * * *
* * bootstrapPreparer * *
  * Type: {@link net.jini.security.ProxyPreparer} * *
  * Default: new {@link net.jini.security.BasicProxyPreparer}() * * *
  * Description: * Preparer for bootstrap proxy results returned by lookup services * that are discovered and used by this utility. *

* The following methods of the proxy returned by this preparer are invoked by * this utility: *

    *
  • {@link SafeServiceRegistrar#lookUp(net.jini.core.lookup.ServiceTemplate, int) lookup} *
  • {@link SafeServiceRegistrar#notiFy notiFy} *
* *
* * * * * * *
* * useInsecureLookup * *
  * Type: {@link java.lang.Boolean} * *
  * Default: new {@link java.lang.Boolean}("FALSE") * * *
  * Description: * When true, ServiceDiscoveryManager and LookupCache use * {@link net.jini.core.lookup.ServiceRegistrar#lookup(net.jini.core.lookup.ServiceTemplate, int)} * instead of {@link SafeServiceRegistrar#lookUp(net.jini.core.lookup.ServiceTemplate, int)} * to perform service discovery. *
* * * * * Logging * * * * This implementation of ServiceDiscoveryManager uses the * {@link Logger} named net.jini.lookup.ServiceDiscoveryManager to * log information at the following logging levels: *

* *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* net.jini.lookup.ServiceDiscoveryManager *
Level Description
{@link java.util.logging.Level#INFO INFO} * when any exception occurs while querying a lookup service, or upon applying a * filter to the results of such a query *
{@link java.util.logging.Level#INFO INFO} * when any exception occurs while attempting to register with the event * mechanism of a lookup service, or while attempting to prepare the lease on * the registration with that event mechanism *
{@link java.util.logging.Level#INFO INFO}when any exception occurs while attempting to prepare a proxy
{@link java.util.logging.Level#INFO INFO} * when an IllegalStateException occurs while discarding a lookup * service proxy after logging a failure that has occurred in one of the tasks * executed by this utility *
{@link java.util.logging.Level#INFO INFO}upon failure of the lease renewal process
{@link org.apache.river.logging.Levels#HANDLED HANDLED} * when an exception occurs because a remote call to a lookup service has been * interrupted as a result of the termination of a cache *
{@link org.apache.river.logging.Levels#HANDLED HANDLED} * when a "gap" is encountered in an event sequence from a lookup service *
{@link java.util.logging.Level#FINER FINER}upon failure of the lease cancellation process
{@link java.util.logging.Level#FINEST FINEST}whenever any task is started
{@link java.util.logging.Level#FINEST FINEST}whenever any task completes successfully
{@link java.util.logging.Level#FINEST FINEST}whenever a lookup cache is created
{@link java.util.logging.Level#FINEST FINEST}whenever a lookup cache is terminated
{@link java.util.logging.Level#FINEST FINEST}whenever a proxy is prepared
{@link java.util.logging.Level#FINEST FINEST} * when an exception (that is, IllegalStateException) occurs while * unexporting a cache's remote event listener while the cache is being * terminated *
*

* See the {@link org.apache.river.logging.LogManager} class for one way to use the * logging level {@link org.apache.river.logging.Levels#HANDLED HANDLED} in standard * logging configuration files. *

* * @author Sun Microsystems, Inc. * * @see net.jini.discovery.DiscoveryManagement * @see net.jini.lookup.LookupCache * @see net.jini.lookup.ServiceDiscoveryListener * @see net.jini.lookup.ServiceDiscoveryEvent * @see net.jini.core.lookup.ServiceRegistrar */ public class ServiceDiscoveryManager { final ProxyPreparer bootstrapProxyPreparer; final boolean useInsecureLookup; private final static String DISCARD_PROPERTY = "org.apache.river.sdm.discardWait"; private final static String INSECURE_LOOKUP_PROPERTY = "org.apache.river.sdm.insecureLookup"; private static final ExecutorService logExec = Executors.newSingleThreadExecutor(new NamedThreadFactory("SDM logger", false)); static void log(Level level, String message){ log(level, message, null, null); } static void log(Level level, String message, Throwable thrown){ log(level, message, null, thrown); } static void log(Level level, String message, Object[] parameters){ log(level, message, parameters, null); } static void log(Level level, String message, Object[] parameters, Throwable thrown){ final LogRecord record = new LogRecord(level, message); record.setParameters(parameters); record.setThrown(thrown); logExec.submit(new Runnable(){ public void run() { logger.log(record); } }); } static void logp(Level logLevel, String sourceClass, String sourceMethod, String message, Throwable thrown) { final LogRecord record = new LogRecord(logLevel, message); record.setSourceClassName(sourceClass); record.setSourceMethodName(sourceMethod); record.setThrown(thrown); logExec.submit(new Runnable(){ public void run() { logger.log(record); } }); } /** * @return the discardWait */ long getDiscardWait() { long disWait = AccessController.doPrivileged(new GetLongAction(DISCARD_PROPERTY, discardWait)); if (logger.isLoggable(Level.FINEST)) log(Level.FINEST, "discard wait = {0}", new Object[]{ disWait}); return disWait; } /** * @return the useInsecureLookup */ boolean useInsecureLookup() { if (AccessController.doPrivileged(new GetBooleanAction(INSECURE_LOOKUP_PROPERTY))) { if (logger.isLoggable(Level.CONFIG)){ log(Level.CONFIG, "useInsecureLookup = {0}", new Object[]{true}); } return true; } return useInsecureLookup; } /** * Class that defines the listener that will receive local events from the * internal LookupCache used in the blocking versions of lookup(). */ private final static class ServiceDiscoveryListenerImpl implements ServiceDiscoveryListener { private final List items = new LinkedList(); @Override public synchronized void serviceAdded(ServiceDiscoveryEvent event) { items.add(event.getPostEventServiceItem()); this.notifyAll(); } @Override public void serviceRemoved(ServiceDiscoveryEvent event) { } @Override public void serviceChanged(ServiceDiscoveryEvent event) { } public synchronized ServiceItem[] getServiceItem() { ServiceItem[] r = new ServiceItem[items.size()]; items.toArray(r); items.clear(); return r; } }//end class ServiceDiscoveryManager.ServiceDiscoveryListenerImpl /** * The Listener class for the LeaseRenewalManager. */ private final class LeaseListenerImpl implements LeaseListener { private final ServiceRegistrar proxy; public LeaseListenerImpl(ServiceRegistrar proxy) { this.proxy = proxy; } /* When lease renewal fails, we discard the proxy */ @Override public void notify(LeaseRenewalEvent e) { fail(e.getException(), proxy, this.getClass().getName(), "notify", "failure occurred while renewing an event lease", false); } }//end class ServiceDiscoveryManager.LeaseListenerImpl /** * Allows termination of LookupCacheImpl so blocking lookup can return * quickly */ private static final class LookupCacheTerminator implements Runnable { private final BlockingQueue cacheList = new LinkedBlockingQueue(20); @Override public void run() { while (!cacheList.isEmpty() || !Thread.currentThread().isInterrupted()) { try { LookupCacheImpl cache = cacheList.take(); synchronized (cache) { cache.terminate(); } } catch (InterruptedException ex) { if (logger.isLoggable(Level.FINEST)) log(Level.FINEST, "SDM lookup cache terminator interrupted", ex); Thread.currentThread().interrupt(); } } } void terminate(LookupCacheImpl cache) { boolean added = cacheList.offer(cache); if (!added) { // happens if cacheList is full. // Do it yourself you lazy caller thread! Can't you see I'm busy? synchronized (cache) { cache.terminate(); } } } } /* Name of this component; used in config entry retrieval and the logger.*/ static final String COMPONENT_NAME = "net.jini.lookup.ServiceDiscoveryManager"; /* Logger used by this utility. */ static final Logger logger = Logger.getLogger(COMPONENT_NAME); /* The discovery manager to use (passed in, or create one). */ final DiscoveryManagement discMgr; /* Indicates whether the discovery manager was created internally or not */ final boolean discMgrInternal; /* The listener added to discMgr that receives DiscoveryEvents */ final DiscMgrListener discMgrListener; /* The LeaseRenewalManager to use (passed in, or create one). */ final LeaseRenewalManager leaseRenewalMgr; /* Contains all of the discovered lookup services (ServiceRegistrar). */ final WriteLock proxyRegSetWrite; final ReadLock proxyRegSetRead; final Set proxyRegSet; /* Random number generator for use in lookup. */ final Random random = new Random(); /* Contains all of the instances of LookupCache that are requested. */ final WriteLock cachesWrite; final ReadLock cachesRead; private final List caches; /* Flag to indicate if the ServiceDiscoveryManager has been terminated. */ private boolean bTerminated = false; //sync on this private final Thread terminatorThread; private final LookupCacheTerminator terminator; /* Flag to indicate LookupCacheTerminator has been started */ private boolean started = false; // sync on terminatorThread /* Object used to obtain the configuration items for this utility. */ final Configuration thisConfig; /* Preparer for the proxies to the lookup services that are discovered * and used by this utility. */ private final ProxyPreparer registrarPreparer; /* Preparer for the proxies to the leases returned to this utility when * it registers with the event mechanism of any of the discovered lookup * services. */ private final ProxyPreparer eventLeasePreparer; /* Wait value used when handling the "service discard problem". */ final long discardWait; /* Listener class for lookup service discovery notification. */ private class DiscMgrListener implements DiscoveryListener { /* New or previously discarded proxy has been discovered. */ @Override public void discovered(DiscoveryEvent e) { ServiceRegistrar[] proxys = e.getRegistrars(); for (int i = 0, l = proxys.length; i < l; i++) { /* Prepare each lookup service proxy before using it. */ if ( !useInsecureLookup() && !(proxys[i] instanceof SafeServiceRegistrar)) continue; try { proxys[i] = (ServiceRegistrar) registrarPreparer.prepareProxy(proxys[i]); if (logger.isLoggable(Level.FINEST)){ log(Level.FINEST, "ServiceDiscoveryManager - " + "discovered lookup service proxy prepared: {0}", new Object []{proxys[i]} ); } } catch (Exception e1) { if (logger.isLoggable(Level.INFO)) log(Level.INFO, "failure preparing discovered ServiceRegistrar " + "proxy, discarding the proxy", e1); discard(proxys[i]); continue; } ProxyReg reg = new ProxyReg(proxys[i]); boolean added; // Changed to only add to newProxys if actually new 7th Jan 2014 proxyRegSetWrite.lock(); try{ added = proxyRegSet.add(reg); } finally { proxyRegSetWrite.unlock(); } if (added) cacheAddProxy(reg); }//end loop }//end DiscMgrListener.discovered /* Previously discovered proxy has been discarded. */ @Override public void discarded(DiscoveryEvent e) { ServiceRegistrar[] proxys = e.getRegistrars(); List drops = new LinkedList(); for (int i = 0, l = proxys.length; i < l; i++) { ProxyReg reg = removeReg(proxys[i]); if (reg != null) { // this check can be removed. drops.add(reg); } else { //River-337 if (logger.isLoggable(Level.SEVERE)) log(Level.SEVERE, "discard error, proxy was null"); //throw new RuntimeException("discard error"); }//endif }//end loop Iterator iter = drops.iterator(); while (iter.hasNext()) { dropProxy(iter.next()); }//end loop }//end DiscMgrListener.discarded /** * Discards a ServiceRegistrar through the discovery manager. */ private void discard(ServiceRegistrar proxy) { discMgr.discard(proxy); }//end discard }//end class ServiceDiscoveryManager.DiscMgrListener /** * Adds the given proxy to all the caches maintained by the SDM. */ private void cacheAddProxy(ProxyReg reg) { cachesRead.lock(); try{ Iterator iter = caches.iterator(); while (iter.hasNext()) { LookupCacheImpl cache = (LookupCacheImpl) iter.next(); cache.addProxyReg(reg); }//end loop } finally { cachesRead.unlock(); } }//end cacheAddProxy /** * Removes the given proxy from all the caches maintained by the SDM. */ private void dropProxy(ProxyReg reg) { cachesRead.lock(); try { Iterator iter = caches.iterator(); while (iter.hasNext()) { LookupCacheImpl cache = (LookupCacheImpl) iter.next(); cache.removeProxyReg(reg); }//end loop } finally { cachesRead.unlock(); } }//end dropProxy /** * Constructs an instance of ServiceDiscoveryManager which * will, on behalf of the entity that constructs this class, discover and * manage a set of lookup services, as well as discover and manage sets of * services registered with those lookup services. The entity indicates * which lookup services to discover and manage through the parameters input * to this constructor. *

* As stated in the class description, this class has three usage patterns: *

*

    *
  • the entity uses a {@link net.jini.lookup.LookupCache * LookupCache} to locally store and manage discovered services so that * those services can be accessed quickly *
  • the entity registers with the event mechanism provided by a * {@link net.jini.lookup.LookupCache LookupCache} to be notified when * services of interest are discovered *
  • the entity uses the ServiceDiscoveryManager to perform * remote queries of the lookup services, employing richer semantics than * that provided through the standard * {@link net.jini.core.lookup.ServiceRegistrar ServiceRegistrar} interface *
*

* Although the first two usage patterns emphasize the use of a cache * object, that cache is acquired only through an instance of the * ServiceDiscoveryManager class. *

* It is important to note that some of the methods of this class ({@link net.jini.lookup.ServiceDiscoveryManager#createLookupCache * createLookupCache} and the blocking versions of * {@link net.jini.lookup.ServiceDiscoveryManager#lookup lookup} to be * exact) can throw a {@link java.rmi.RemoteException} when invoked. This is * because each of these methods may attempt to register with the event * mechanism of at least one lookup service, a process that requires a * remote object (a listener) to be exported to the lookup service(s). Both * the process of registering with a lookup service's event mechanism and * the process of exporting a remote object are processes that can result in * a {@link java.rmi.RemoteException}. *

* In order to facilitate the exportation of the remote listener just * described, the ServiceDiscoveryManager class instantiates an * inner class that implements the * {@link net.jini.core.event.RemoteEventListener RemoteEventListener} * interface. Although this class defines, instantiates, and exports this * remote listener, it is the entity's responsibility to provide a * mechanism for any lookup service to acquire the proxy to the exported * listener. One way to do this is to configure this utility to export the * listener using the Jini(TM) Extensible Remote Invocation (Jini ERI) * communication framework. When the listener is exported to use Jini ERI, * and no proxy customizations (such as a custom invocation handler or * transport endpoint) are used, no other action is necessary to make the * proxy to the listener available to the lookup service(s) with which that * listener is registered. *

* The default exporter for this * utility will export the remote event listener under Jini ERI, specifying * that the port and object ID with which the listener is to be exported * should be chosen by the Jini ERI framework, not the deployer. *

* If it is required that the remote event listener be exported under JRMP * instead of Jini ERI, then the entity that employs this utility must * specify this in its configuration. For example, the entity's * configuration would need to contain something like the following: *

*

*
     * import net.jini.jrmp.JrmpExporter;
     *
     * application.configuration.component.name {
     *    .......
     *    .......
     *    // configuration items specific to the application
     *    .......
     *    .......
     * }//end application.configuration.component.name
     *
     * net.jini.lookup.ServiceDiscoveryManager {
     *
     *    serverExporter = new JrmpExporter();
     *
     * }//end net.jini.lookup.ServiceDiscoveryManager
     * 
*
*

* It is important to note that when the remote event listener is exported * under JRMP, unlike Jini ERI, the JRMP remote communication framework does * not provide a mechanism that automatically makes the * listener proxy available to the lookup service(s) with which the listener * is registered; the deployer of the entity, or the entity itself, must * provide such a mechanism. *

* When exported under JRMP, one of the more common mechanisms for making * the listener proxy available to the lookup service(s) with which the * listener is registered consists of the following: *

*

  • store the necessary class files in a JAR file *
  • make the class files in the JAR file preferred * (see package net.jini.loader.pref for details) *
  • run an HTTP server to serve up the JAR file to any requesting lookup * service *
  • advertise the location of that JAR file by setting the * java.rmi.server.codebase property of the entity to "point" * at the JAR file *
*

* For example, suppose an application consists of an entity that intends to * use the ServiceDiscoveryManager will run on a host named * myHost. And suppose that the down-loadable JAR file * named sdm-dl.jar that is provided in the distribution is * located in the directory /files/jini/lib, and will be * served by an HTTP server listening on port * 8082. If the application is run with its codebase property * set to * -Djava.rmi.server.codebase="http://myHost:8082/sdm-dl.jar", * the lookup service(s) should then be able to access the remote listener * exported under JRMP by the ServiceDiscoveryManager on behalf * of the entity. *

* If a mechanism for lookup services to access the remote listener exported * by the ServiceDiscoveryManager is not provided (either by * the remote communication framework itself, or by some other means), the * remote methods of the ServiceDiscoveryManager - the methods * involved in the two most important usage patterns of that utility - will * be of no use. *

* This constructor takes two arguments: an object that implements the * DiscoveryManagement interface and a reference to a * LeaseRenewalManager object. The constructor throws an * IOException because construction of a * ServiceDiscoveryManager may initiate the multicast discovery * process, a process that can throw an IOException. * * @param discoveryMgr the DiscoveryManagement implementation * through which notifications that indicate a lookup service has been * discovered or discarded will be received. If the value of the argument is * null, then an instance of the * LookupDiscoveryManager utility class will be constructed to * listen for events announcing the discovery of only those lookup services * that are members of the public group. * * @param leaseMgr the LeaseRenewalManager to use. A value of * null may be passed as the LeaseRenewalManager * argument. If the value of the argument is null, an instance * of the LeaseRenewalManager class will be created, initially * managing no Lease objects. * * @throws IOException because construction of a * ServiceDiscoveryManager may initiate the multicast discovery * process which can throw an IOException. * * @see net.jini.discovery.DiscoveryManagement * @see net.jini.core.event.RemoteEventListener * @see net.jini.core.lookup.ServiceRegistrar */ public ServiceDiscoveryManager(DiscoveryManagement discoveryMgr, LeaseRenewalManager leaseMgr) throws IOException { this(initial(discoveryMgr, leaseMgr, EmptyConfiguration.INSTANCE)); }//end constructor /** * Constructs an instance of this class, which is configured using the items * retrieved through the given Configuration, that will, on * behalf of the entity that constructs this class, discover and manage a * set of lookup services, as well as discover and manage sets of services * registered with those lookup services. Through the parameters input to * this constructor, the client of this utility indicates which lookup * services to discover and manage, and how it wants the utility * additionally configured. *

* For a more details, refer to the description of the alternate constructor * of this class. *

* This constructor takes three arguments: an object that implements the * DiscoveryManagement interface, a reference to an instance of * the LeaseRenewalManager class, and a * Configuration object. The constructor throws an * IOException because construction of a * ServiceDiscoveryManager may initiate the multicast discovery * process, a process that can throw an IOException. The * constructor also throws a ConfigurationException when an * exception occurs while retrieving an item from the given * Configuration * * @param discoveryMgr the DiscoveryManagement implementation * through which notifications that indicate a lookup service has been * discovered or discarded will be received. If the value of the argument is * null, then an instance of the * LookupDiscoveryManager utility class will be constructed to * listen for events announcing the discovery of only those lookup services * that are members of the public group. * * @param leaseMgr the LeaseRenewalManager to use. A value of * null may be passed as the LeaseRenewalManager * argument. If the value of the argument is null, an instance * of the LeaseRenewalManager class will be created, initially * managing no Lease objects. * @param config the Configuration * * @throws IOException because construction of a * ServiceDiscoveryManager may initiate the multicast discovery * process which can throw an IOException. * * @throws net.jini.config.ConfigurationException indicates an exception * occurred while retrieving an item from the given * Configuration * * @throws java.lang.NullPointerException if null is input for * the configuration * * @see net.jini.discovery.DiscoveryManagement * @see net.jini.core.event.RemoteEventListener * @see net.jini.core.lookup.ServiceRegistrar * @see net.jini.config.Configuration * @see net.jini.config.ConfigurationException */ public ServiceDiscoveryManager(DiscoveryManagement discoveryMgr, LeaseRenewalManager leaseMgr, Configuration config) throws IOException, ConfigurationException { this(init(discoveryMgr, leaseMgr, config)); }//end constructor private ServiceDiscoveryManager(Initializer init) { // Key's added only if absent. this.proxyRegSet = new HashSet(); ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); proxyRegSetRead = rwl.readLock(); proxyRegSetWrite = rwl.writeLock(); this.caches = new ArrayList(32); ReentrantReadWriteLock rwl2 = new ReentrantReadWriteLock(); cachesWrite = rwl2.writeLock(); cachesRead = rwl2.readLock(); thisConfig = init.thisConfig; registrarPreparer = init.registrarPreparer; eventLeasePreparer = init.eventLeasePreparer; bootstrapProxyPreparer = init.bootstrapProxyPreparer; useInsecureLookup = init.useInsecureLookup; leaseRenewalMgr = init.leaseRenewalMgr; discardWait = init.discardWait; discMgr = init.discMgr; discMgrInternal = init.discMgrInternal; discMgrListener = new DiscMgrListener(); discMgr.addDiscoveryListener(discMgrListener); terminator = new LookupCacheTerminator(); terminatorThread = new Thread(terminator, "SDM lookup cache terminator"); terminatorThread.setDaemon(false); } /** * Returns array of ServiceRegistrar created from the proxyRegSet */ private ServiceRegistrar[] buildServiceRegistrar() { List proxys = new LinkedList(); proxyRegSetRead.lock(); try { Iterator iter = proxyRegSet.iterator(); while (iter.hasNext()) { ProxyReg reg = iter.next(); proxys.add(reg.getProxy()); }//end loop } finally { proxyRegSetRead.unlock(); } return proxys.toArray(new ServiceRegistrar[proxys.size()]); }//end buildServiceRegistrar /** * Queries each available lookup service in the set of lookup services * managed by the ServiceDiscoveryManager (the managed * set) for a service reference that matches criteria defined by the * entity that invokes this method. The semantics of this method are similar * to the semantics of the lookup method provided by the * ServiceRegistrar interface; employing the same * template-matching scheme. Additionally, this method allows any entity to * supply an object referred to as a filter. Such an object is a * non-remote object that defines additional matching criteria that the * ServiceDiscoveryManager applies when searching for the * entity's services of interest. This filtering facility is particularly * useful to entities that wish to extend the capabilities of standard * template-matching. *

* Entities typically employ this method when they need infrequent access to * services, and when the cost of making remote queries is outweighed by the * overhead of maintaining a local cache (for example, because of resource * limitations). *

* This version of lookup returns a single instance of * ServiceItem corresponding to one of possibly many service * references that satisfy the matching criteria. If multiple services * matching the input criteria happen to exist, it is arbitrary as to which * reference is actually returned. It is for this reason that entities that * invoke this method typically care only that a * service is returned, not which service. *

* Note that, unlike other versions of lookup provided by the * ServiceDiscoveryManager, this version does not * block. That is, this version will return immediately upon failure * (or success) to find a service matching the input criteria. * * It is important to understand this characteristic because there is a * common usage scenario that can cause confusion when this version of * lookup is used but fails to discover the expected service of * interest. Suppose an entity creates a service discovery manager and then * immediately calls this version of lookup, which simply * queries the currently discovered lookup services for the service of * interest. If the discovery manager employed by the service discovery * manager has not yet disovered any lookup services (thus, there are no * lookup services to query) the method will immediately return a value of * null. This can be confusing when one verifies that such a * service of interest has indeed been started and registered with the * existing lookup service(s). To address this issue, one of the blocking * versions of lookup could be used instead of this version, or * the entity could simply wait until the discovery manager has been given * enough time to complete its own (lookup) discovery processing. * * @param tmpl an instance of ServiceTemplate corresponding to * the object to use for template-matching when searching for desired * services. If null is input to this parameter, this method * will use a wildcarded * template (will match all services) when performing template-matching. * Note that the effects of modifying contents of this parameter before this * method returns are unpredictable and undefined. * @param filter an instance of ServiceItemFilter containing * matching criteria that should be applied in addition to the * template-matching employed when searching for desired services. If * null is input to this parameter, then only template-matching * will be employed to find the desired services. * * @return a single instance of ServiceItem corresponding to a * reference to a service that matches the criteria represented in the input * parameters; or null if no matching service can be found. * Note that if multiple services matching the input criteria exist, it is * arbitrary as to which reference is returned. * * @see net.jini.core.lookup.ServiceRegistrar#lookup * @see net.jini.core.lookup.ServiceTemplate * @see net.jini.lookup.ServiceItemFilter */ public ServiceItem lookup(ServiceTemplate tmpl, ServiceItemFilter filter) { checkTerminated(); ServiceRegistrar[] proxys = buildServiceRegistrar(); int len = proxys.length; if (len == 0) { return null; } int rand = random.nextInt(Integer.MAX_VALUE) % len; for (int i = 0; i < len; i++) { ServiceRegistrar proxy = proxys[(i + rand) % len]; ServiceItem sItem = null; try { int maxMatches = ((filter != null) ? Integer.MAX_VALUE : 1); Object [] matches; if (useInsecureLookup()){ ServiceMatches sm = proxy.lookup(tmpl, maxMatches); matches = sm.items; } else { matches = ((SafeServiceRegistrar)proxy).lookUp(tmpl, maxMatches); } if (matches == null) continue; sItem = getMatchedServiceItem(matches, filter); } catch (Exception e) { if (logger.isLoggable(Level.INFO)) log(Level.INFO, "Exception occurred during query, discarding proxy", e); discard(proxy); } if (sItem != null) { return sItem; //Don't need to clone } }//end loop return null; }//end lookup /** * Queries each available lookup service in the managed set for a service * that matches the input criteria. The semantics of this method are similar * to the semantics of the lookup method provided by the * ServiceRegistrar interface; employing the same * template-matching scheme. Additionally, this method allows any entity to * supply an object referred to as a filter. Such an object is a * non-remote object that defines additional matching criteria that the * ServiceDiscoveryManager applies when searching for the * entity's services of interest. This filtering facility is particularly * useful to entities that wish to extend the capabilities of standard * template-matching. *

* This version of lookup returns a single instance of * ServiceItem corresponding to one of possibly many service * references that satisfy the matching criteria. If multiple services * matching the input criteria happen to exist, it is arbitrary as to which * reference is actually returned. It is for this reason that entities that * invoke this method typically care only that a * service is returned, not which service. *

* Note that this version of lookup provides a * blocking feature that is controlled through the * waitDur parameter. That is, this version will not return * until either a service that matches the input criteria has been found, or * the amount of time contained in the waitDur parameter has * passed. If, while waiting for the service of interest to be found, the * entity decides that it no longer wishes to wait the entire period for * this method to return, the entity may interrupt this method by invoking * the interrupt method from the Thread class. The intent of * this mechanism is to allow the entity to interrupt this method in the * same way it would a sleeping thread. *

* Entities typically employ this method when they need infrequent access to * services, are willing (or forced) to wait for those services to be found, * and consider the cost of making remote queries for those services is * outweighed by the overhead of maintaining a local cache (for example, * because of resource limitations). * * @param tmpl an instance of ServiceTemplate corresponding to * the object to use for template-matching when searching for desired * services. If null is input to this parameter, this method * will use a wildcarded * template (will match all services) when performing template-matching. * Note that the effects of modifying contents of this parameter before this * method returns are unpredictable and undefined. * @param filter an instance of ServiceItemFilter containing * matching criteria that should be applied in addition to the * template-matching employed when searching for desired services. If * null is input to this parameter, then only template-matching * will be employed to find the desired services. * @param waitDur the amount of time (in milliseconds) to wait before ending * the "search" and returning null. If a non-positive value is * input to this parameter, then this method will not wait; it will simply * query the available lookup services and return a matching service * reference or null. * * @return a single instance of ServiceItem corresponding to a * reference to a service that matches the criteria represented in the input * parameters; or null if no matching service can be found. * Note that if multiple services matching the input criteria exist, it is * arbitrary as to which reference is returned. * * @throws java.lang.InterruptedException this exception occurs when the * entity interrupts this method by invoking the interrupt method from the * Thread class. * * @throws java.rmi.RemoteException typically, this exception occurs when a * RemoteException occurs either as a result of an attempt to export a * remote listener, or an attempt to register with the event mechanism of a * lookup service. * * @see net.jini.core.lookup.ServiceRegistrar#lookup * @see net.jini.core.lookup.ServiceTemplate * @see net.jini.lookup.ServiceItemFilter * @see java.lang.Thread */ public ServiceItem lookup(ServiceTemplate tmpl, ServiceItemFilter filter, long waitDur) throws InterruptedException, RemoteException { /* First query each lookup for the desired service */ ServiceItem sm = lookup(tmpl, filter);//checkTerminated() is done here if (sm != null) { return sm; } /* If the desired service is not in any of the lookups, wait for it. */ ServiceDiscoveryListener cacheListener = new ServiceDiscoveryListenerImpl(); LookupCacheImpl cache = null; try { /* The cache must be created inside the listener sync block, * otherwise a race condition can occur. This is because the * creation of a cache results in event registration which * will ultimately result in the invocation of the serviceAdded() * method in the cache's listener, and the interruption of any * objects waiting on the cache's listener. If the notifications * happen to occur before commencing the wait on the listener * object (see below), then the wait will never be interrupted * because the interrupts were sent before the wait() method * was invoked. Synchronizing on the listener and the listener's * serviceAdded() method, and creating the cache only after the * lock has been acquired, together will prevent this situation * since event registration cannot occur until the cache is * created, and the lock that allows entry into the serviceAdded() * method (which is invoked once the events do arrive) is not * released until the wait() method is invoked . */ synchronized (cacheListener) { cache = createLookupCache(tmpl, filter, cacheListener, waitDur); long duration = cache.getLeaseDuration(); while (duration > 0) { cacheListener.wait(duration); sm = cache.lookup(null); if (sm != null) { return sm; } duration = cache.getLeaseDuration(); }//end loop }//end sync(cacheListener) return null; // Make it clear we're returning null. } finally { if (cache != null) { terminator.terminate(cache); } } }//end lookup /** * The createLookupCache method allows the client-like entity * to request that the ServiceDiscoveryManager create a new * managed set (or cache) and populate it with services, which match * criteria defined by the entity, and whose references are registered with * one or more of the lookup services the entity has targeted for discovery. *

* This method returns an object of type LookupCache. Through * this return value, the entity can query the cache for services of * interest, manage the cache's event mechanism for service discoveries, or * terminate the cache. *

* An entity typically uses the object returned by this method to provide * local storage of, and access to, references to services that it is * interested in using. Entities needing frequent access to numerous * services will find the object returned by this method quite useful * because acquisition of those service references is provided through local * method invocations. Additionally, because the object returned by this * method provides an event mechanism, it is also useful to entities wishing * to simply monitor, in an event-driven manner, the state changes that * occur in the services of interest. *

* Although not required, a common usage pattern for entities that wish to * use the LookupCache class to store and manage "discovered" * services is to create a separate cache for each service type of interest. * * @param tmpl template to match. It uses template-matching semantics to * identify the service(s) to acquire from lookup services in the managed * set. If this value is null, it is the equivalent of passing * a ServiceTemplate constructed with all null * arguments (all wildcards). * @param filter used to apply additional matching criteria to any * ServiceItem found through template-matching. If this value * is null, no additional filtering will be applied beyond the * template-matching. * @param listener object that will receive notifications when services * matching the input criteria are discovered for the first time, or have * encountered a state change such as removal from all lookup services or * attribute set changes. If this value is null, the cache * resulting from that invocation will send no such notifications. * * @return LookupCache used to query the cache for services of interest, * manage the cache's event mechanism for service discoveries, or terminate * the cache. * * @throws java.rmi.RemoteException typically, this exception occurs when a * RemoteException occurs as a result of an attempt to export the remote * listener that receives service events from the lookup services in the * managed set. * * @see net.jini.lookup.ServiceItemFilter */ public LookupCache createLookupCache(ServiceTemplate tmpl, ServiceItemFilter filter, ServiceDiscoveryListener listener) throws RemoteException { checkTerminated(); return createLookupCache(tmpl, filter, listener, Long.MAX_VALUE); }//end createLookupCache /** * The getDiscoveryManager method will return an object that * implements the DiscoveryManagement interface. The object * returned by this method provides the ServiceDiscoveryManager * with the ability to set discovery listeners and to discard previously * discovered lookup services when they are found to be unavailable. * * @return DiscoveryManagement implementation * @see net.jini.discovery.DiscoveryManagement */ public DiscoveryManagement getDiscoveryManager() { checkTerminated(); return discMgr; }//end getDiscoveryManager /** * The getLeaseRenewalManager method will return an instance of * the LeaseRenewalManager class. The object returned by this * method manages the leases requested and held by the * ServiceDiscoveryManager. In general, these leases correspond * to the registrations made by the ServiceDiscoveryManager * with the event mechanism of each lookup service in the managed set. * * @return LeaseRenewalManager for this instance of the * ServiceDiscoveryManager. * @see net.jini.lease.LeaseRenewalManager */ public LeaseRenewalManager getLeaseRenewalManager() { checkTerminated(); return leaseRenewalMgr; }//end getLeaseRenewalManager /** * The terminate method performs cleanup duties related to the * termination of the event mechanism for lookup service discovery, the * event mechanism for service discovery, and the cache management duties of * the ServiceDiscoveryManager. *

* For each instance of LookupCache created and managed by the * ServiceDiscoveryManager, the terminate method * will do the following: *

    *
  • Either remove all listener objects registered for receipt of * DiscoveryEvent objects or, if the discovery manager employed * by the ServiceDiscoveryManager was created by the * ServiceDiscoveryManager itself, terminate all discovery * processing being performed by that manager object on behalf of the * entity. *

    *

  • Cancel all event leases granted by each lookup service in the managed * set of lookup services. *

    *

  • Un-export all remote listener objects registered with each lookup * service in the managed set. *

    *

  • Terminate all threads involved in the process of retrieving and * storing references to discovered services of interest. *
* Calling any method after the termination will result in an * IllegalStateException. * * @see net.jini.lookup.LookupCache * @see net.jini.discovery.DiscoveryEvent */ public void terminate() { synchronized (this) { if (bTerminated) { return;//allow for multiple terminations } bTerminated = true; /* Terminate lookup service discovery processing */ discMgr.removeDiscoveryListener(discMgrListener); if (discMgrInternal) { discMgr.terminate(); } }//end sync terminatorThread.interrupt(); /* Terminate all caches: cancel event leases, un-export listeners */ List terminate; cachesRead.lock(); try{ terminate = new ArrayList(caches); } finally { cachesRead.unlock(); } Iterator iter = terminate.iterator(); while (iter.hasNext()) { LookupCacheImpl cache = (LookupCacheImpl) iter.next(); cache.terminate(); }//end loop leaseRenewalMgr.close(); }//end terminate /** * Queries each available lookup service in the managed set for service(s) * that match the input criteria. The semantics of this method are similar * to the semantics of the lookup method provided by the * ServiceRegistrar interface; employing the same * template-matching scheme. Additionally, this method allows any entity to * supply an object referred to as a filter. Such an object is a * non-remote object that defines additional matching criteria that the * ServiceDiscoveryManager applies when searching for the * entity's services of interest. This filtering facility is particularly * useful to entities that wish to extend the capabilities of standard * template-matching. *

* Entities typically employ this method when they need infrequent access to * multiple instances of services, and when the cost of making remote * queries is outweighed by the overhead of maintaining a local cache (for * example, because of resource limitations). *

* This version of lookup returns an array of instances * of ServiceItem in which each element corresponds to a * service reference that satisfies the matching criteria. The number of * elements in the returned set will be no greater than the value of the * maxMatches parameter, but may be less. *

* Note that this version of lookup does not provide a * blocking feature. That is, this version will return immediately * with whatever number of service references it can find, up to the number * indicated in the maxMatches parameter. If no services * matching the input criteria can be found on the first attempt, an empty * array is returned. * * It is important to understand this characteristic because there is a * common usage scenario that can cause confusion when this version of * lookup is used but fails to discover any instances of the * expected service of interest. Suppose an entity creates a service * discovery manager and then immediately calls this version of * lookup, which simply queries the currently discovered lookup * services for the service of interest. If the discovery manager employed * by the service discovery manager has not yet discovered any lookup * services (thus, there are no lookup services to query) the method will * immediately return an empty array. This can be confusing when one * verifies that instance(s) of such a service of interest have indeed been * started and registered with the existing lookup service(s). To address * this issue, one of the blocking versions of lookup could be * used instead of this version, or the entity could simply wait until the * discovery manager has been given enough time to complete its own (lookup) * discovery processing. * * @param tmpl an instance of ServiceTemplate corresponding to * the object to use for template-matching when searching for desired * services. If null is input to this parameter, this method * will use a wildcarded template (will match all services) when * performing template-matching. Note that the effects of modifying contents * of this parameter before this method returns are unpredictable and * undefined. * @param maxMatches this method will return no more than this number of * service references * @param filter an instance of ServiceItemFilter containing * matching criteria that should be applied in addition to the * template-matching employed when searching for desired services. If * null is input to this parameter, then only template-matching * will be employed to find the desired services. * * @return an array of instances of ServiceItem where each * element corresponds to a reference to a service that matches the criteria * represented in the input parameters; or an empty array if no matching * service can be found. * * @see net.jini.core.lookup.ServiceRegistrar#lookup * @see net.jini.core.lookup.ServiceTemplate * @see net.jini.lookup.ServiceItemFilter */ public ServiceItem[] lookup(ServiceTemplate tmpl, int maxMatches, ServiceItemFilter filter) { checkTerminated(); if (maxMatches < 1) { throw new IllegalArgumentException("maxMatches must be > 0"); } /* retrieve the lookup service(s) to query for matching service(s) */ ServiceRegistrar[] proxys = buildServiceRegistrar(); int len = proxys.length; List sItemSet = new ArrayList(len); if (len > 0) { /* loop thru the set of lookups, randomly selecting each lookup */ int rand = (random.nextInt(Integer.MAX_VALUE)) % len; for (int i = 0; i < len; i++) { int max = maxMatches; ServiceRegistrar proxy = proxys[(i + rand) % len]; try { /* If a filter is to be applied (filter != null), then * the value of the maxMatches parameter will not * suffice when querying the current lookup service. * This is because although services returned from a * query of the lookup service will match the template, * some of those services may get filtered out. Thus, * asking for exactly maxMatches may result in fewer * matching services than actually are contained in * the lookup. Thus, all matching services are * requested by passing in "infinity" for the maximum * number of matches (Integer.MAX_VALUE). */ if (filter != null) { max = Integer.MAX_VALUE; } /* Query the current lookup for matching service(s). */ Object [] result; if (useInsecureLookup()){ ServiceMatches matches = proxy.lookup(tmpl, max); result = matches.items; } else { result = ((SafeServiceRegistrar)proxy).lookUp(tmpl, max); } if (result == null) continue; int nItems = result.length; if (nItems == 0) { continue;//no matches, query next lookup } /* Loop thru the matching services, randomly selecting * each service, applying the filter if appropriate, * and making sure the service has not already been * selected (it may have been returned from a previously * queried lookup). */ int r = (random.nextInt(Integer.MAX_VALUE)) % nItems; for (int j = 0; j < nItems; j++) { Object obj = result[(j + r) % nItems]; if (obj == null) continue; ServiceItem sItem; if (useInsecureLookup()){ sItem = (ServiceItem) obj; if (!filterPassed(sItem, filter)) continue; } else { sItem = check(obj, filter, bootstrapProxyPreparer); if (sItem == null) continue; } if (!isArrayContainsServiceItem(sItemSet, sItem)) { sItemSet.add(sItem); } if (sItemSet.size() >= maxMatches) { return sItemSet.toArray(new ServiceItem[sItemSet.size()]); } } } catch (Exception e) { if (logger.isLoggable(Level.INFO)) log(Level.INFO, "Exception occurred during query, discarding proxy", e); discard(proxy); } }//end loop(i) }//endif(len>0) /* Will reach this return statement only when less than the number * of services requested have been found in the loop above. */ return (ServiceItem[]) (sItemSet.toArray(new ServiceItem[sItemSet.size()])); }//end lookup /** * Queries each available lookup service in the managed set for service(s) * that match the input criteria. The semantics of this method are similar * to the semantics of the lookup method provided by the * ServiceRegistrar interface; employing the same * template-matching scheme. Additionally, this method allows any entity to * supply an object referred to as a filter. Such an object is a * non-remote object that defines additional matching criteria that the * ServiceDiscoveryManager applies when searching for the * entity's services of interest. This filtering facility is particularly * useful to entities that wish to extend the capabilities of standard * template-matching. *

* This version of lookup returns an array of instances * of ServiceItem in which each element corresponds to a * service reference that satisfies the matching criteria. The number of * elements in the returned set will be no greater than the value of the * maxMatches parameter, but may be less. *

* Note that this version of lookup provides a * blocking feature that is controlled through the * waitDur parameter in conjunction with the * minMatches and the maxMatches parameters. This * method will not return until one of the following occurs: *

*

    *
  • the number of matching services found on the first attempt is * greater than or equal to the value of the minMatches * parameter, in which case this method returns each of the services found * up to the value of the maxMatches parameter *
  • the number of matching services found after the first attempt * (that is, after the method enters the "wait state") is at least as great * as the value of the minMatches parameter in which case this * method returns each of the services found up to the value of the * maxMatches parameter *
  • the amount of time that has passed since this method entered the * wait state exceeds the value of the waitDur parameter, in * which case this method returns all of the currently discovered services *
*

* The purpose of the minMatches parameter is to allow the * entity to balance its need for multiple matching service references with * its need to minimize the time spent in the wait state; time that most * would consider wasted if an acceptable number of matching service * references were found, but this method continued to wait until the end of * the designated time period. *

* If, while waiting for the minimum number of desired services to be * discovered, the entity decides that it no longer wishes to wait the * entire period for this method to return, the entity may interrupt this * method by invoking the interrupt method from the Thread * class. The intent of this mechanism is to allow the entity to interrupt * this method in the same way it would a sleeping thread. *

* Entities typically employ this method when they need infrequent access to * multiple instances of services, are willing (or forced) to wait for those * services to be found, and consider the cost of making remote queries for * those services is outweighed by the overhead of maintaining a local cache * (for example, because of resource limitations). * * @param tmpl an instance of ServiceTemplate corresponding to * the object to use for template-matching when searching for desired * services. If null is input to this parameter, this method * will use a * wildcarded template (will match all services) when performing * template-matching. Note that the effects of modifying contents of this * parameter before this method returns are unpredictable and undefined. * @param minMatches this method will immediately exit the wait state and * return once this number of service references is found * @param maxMatches this method will return no more than this number of * service references * @param filter an instance of ServiceItemFilter containing * matching criteria that should be applied in addition to the * template-matching employed when searching for desired services. If * null is input to this parameter, then only template-matching * will be employed to find the desired services. * @param waitDur the amount of time (in milliseconds) to wait before ending * the "search" and returning an empty array. If a non-positive value is * input to this parameter, then this method will not wait; it will simply * query the available lookup services and return whatever matching service * reference(s) it could find, up to maxMatches. * * @return an array of instances of ServiceItem where each * element corresponds to a reference to a service that matches the criteria * represented in the input parameters; or an empty array if no matching * service can be found within the time allowed. * * @throws java.lang.InterruptedException this exception occurs when the * entity interrupts this method by invoking the interrupt method from the * Thread class. * * @throws java.lang.IllegalArgumentException this exception occurs when one * of the following conditions is satisfied: *

*

  • the minMatches parameter is non-positive *
  • the maxMatches parameter is non-positive *
  • the value of maxMatches is less than * the value of minMatches *
* * @throws java.rmi.RemoteException typically, this exception occurs when a * RemoteException occurs either as a result of an attempt to export a * remote listener, or an attempt to register with the event mechanism of a * lookup service. * * @see net.jini.core.lookup.ServiceRegistrar#lookup * @see net.jini.core.lookup.ServiceTemplate * @see net.jini.lookup.ServiceItemFilter * @see java.lang.Thread */ public ServiceItem[] lookup(ServiceTemplate tmpl, int minMatches, int maxMatches, ServiceItemFilter filter, long waitDur ) throws InterruptedException, RemoteException { checkTerminated(); if (minMatches < 1) { throw new IllegalArgumentException("minMatches must be > 0"); } if (maxMatches < minMatches) { throw new IllegalArgumentException("maxMatches must be > minMatches"); } long delay = System.currentTimeMillis(); ServiceItem[] sItems = lookup(tmpl, maxMatches, filter); if (sItems.length >= minMatches) { return sItems; } List sItemSet = new LinkedList(); for (int i = 0, l = sItems.length; i < l; i++) { //if(!sItemSet.contains(sItems[i]) //sItemSet.add(sItems[i]); if (!isArrayContainsServiceItem(sItemSet, sItems[i])) { sItemSet.add(sItems[i]); }//endif }//end loop ServiceDiscoveryListenerImpl cacheListener = new ServiceDiscoveryListenerImpl(); /* The cache must be created inside the listener sync block, * otherwise a race condition can occur. This is because the * creation of a cache results in event registration which * will ultimately result in the invocation of the serviceAdded() * method in the cache's listener, and the interruption of any * objects waiting on the cache's listener. If the notifications * happen to occur before commencing the wait on the listener * object (see below), then the wait will never be interrupted * because the interrupts were sent before the wait() method * was invoked. Synchronizing on the listener and the listener's * serviceAdded() method, and creating the cache only after the * lock has been acquired, together will prevent this situation * since event registration cannot occur until the cache is * created, and the lock that allows entry into the serviceAdded() * method (which is invoked once the events do arrive) is not * released until the wait() method is invoked. */ LookupCacheImpl cache; synchronized (cacheListener) { // uncontended lock. delay = (System.currentTimeMillis() - delay) + 1; // Calculate initial time delay in ms. cache = createLookupCache(tmpl, filter, cacheListener, waitDur); long duration = cache.getLeaseDuration(); while (duration > delay) { // Some milli's to spare to ensure we return in reasonable time. cacheListener.wait(duration - delay); ServiceItem items[] = cacheListener.getServiceItem(); for (int i = 0, l = items.length; i < l; i++) { if (!isArrayContainsServiceItem(sItemSet, items[i])) { sItemSet.add(items[i]); }//endif }//end loop if (sItemSet.size() >= minMatches) {//River-466 break; } duration = cache.getLeaseDuration(); }//end loop }//end sync(cacheListener) // Termination is now performed by a dedicated thread to ensure // Remote method call doesn't take too long. terminator.terminate(cache); if (sItemSet.size() > maxMatches) { // Discard some matches ServiceItem[] r = new ServiceItem[maxMatches]; // Iterator is faster for LinkedList. Iterator it = sItemSet.iterator(); for (int i = 0; it.hasNext() && i < maxMatches; i++) { r[i] = it.next(); } return r; } ServiceItem[] r = new ServiceItem[sItemSet.size()]; sItemSet.toArray(r); return r; }//end lookup /** * From the given set of ServiceMatches, randomly selects and returns a * ServiceItem that matches the given filter (if applicable). */ private ServiceItem getMatchedServiceItem(Object [] sm, ServiceItemFilter filter) { int len = sm.length; if (len > 0) { int rand = random.nextInt(Integer.MAX_VALUE) % len; for (int i = 0; i < len; i++) { Object sItem = sm[(i + rand) % len]; if (sItem == null) continue; ServiceItem item = null; if (useInsecureLookup()){ item = (ServiceItem) sItem; if (filterPassed(item, filter)) return item; } else { item = check(sItem, filter, bootstrapProxyPreparer); if (item == null) continue; return item; } }//end loop }//endif return null; }//end getMatchedServiceItem /** * * @param bootstrapProxy * @param filter * @param bootstrapPreparer * @param serviceProxyPreparer * @return a new ServiceItem if preparation and filter are successful, or * null. */ private ServiceItem check(Object bootstrapProxy, ServiceItemFilter filter, ProxyPreparer bootstrapPreparer) { try { if (!(bootstrapProxy instanceof ServiceAttributesAccessor) && !(bootstrapProxy instanceof ServiceIDAccessor) && !(bootstrapProxy instanceof ServiceProxyAccessor)) return null; // The bootstrap proxy preparer can authenticate, dynamically // grant permission to download and deserialize the service proxy. // The service proxy can be trusted, however no constraints have // been applied to the service proxy yet. Object preparedProxy = bootstrapPreparer.prepareProxy(bootstrapProxy); // getServiceAttributes may try to download code, the bootstrapPreparer // should have authenticated and authorised any code downloads. Entry[] serviceAttributes = ((ServiceAttributesAccessor) preparedProxy).getServiceAttributes(); ServiceID serviceID = ((ServiceIDAccessor) preparedProxy).serviceID(); ServiceItem item = new ServiceItem(serviceID, bootstrapProxy, serviceAttributes); try { if (filter == null){ // No local filter, retrieve service proxy. item.service = ((ServiceProxyAccessor) preparedProxy).getServiceProxy(); return item; } // The ServiceItemFilter should mutate the ServiceItem.service // field after preparing the proxy and retrieving the // service proxy using ServiceProxyAccessor. if (filter.check(item)) return item; } catch (SecurityException ex) { if (logger.isLoggable(Level.FINE)) log(Level.FINE, "Exception thrown while filtering ServiceItem containing bootstrap proxy, downloading service proxy and trying again, suggest rewriting your filter", ex); // If ClassCastException, then filter has attempted to cast the // bootstrap proxy to the service type, it is likely to be // an older filter implementation that doesn't know about // ServiceProxyAccessor. // If SecurityException, then proxy preparation failed, which // is probably due to the filter not expecting a bootstrap // proxy and attempting to apply method constraints. // SOLUTION: Download service proxy and retry filter. item.service = ((ServiceProxyAccessor) preparedProxy).getServiceProxy(); if (filter.check(item)) return item; } catch (ClassCastException ex){ if (logger.isLoggable(Level.FINE)) log(Level.FINE, "Exception thrown while filtering ServiceItem containing bootstrap proxy, downloading service proxy and trying again, suggest rewriting your filter", ex); // If ClassCastException, then filter has attempted to cast the // bootstrap proxy to the service type, it is likely to be // an older filter implementation that doesn't know about // ServiceProxyAccessor. // If SecurityException, then proxy preparation failed, which // is probably due to the filter not expecting a bootstrap // proxy and attempting to apply method constraints. // SOLUTION: Download service proxy and retry filter. item.service = ((ServiceProxyAccessor) preparedProxy).getServiceProxy(); if (filter.check(item)) return item; } return null; } catch (IOException ex) { if (logger.isLoggable(Level.FINE)) log(Level.FINE, "IOException thrown while checking bootstrapProxy, filtering and downloading service proxy", ex); return null; } } /** * Creates a LookupCache with specific lease duration. */ private LookupCacheImpl createLookupCache(ServiceTemplate tmpl, ServiceItemFilter filter, ServiceDiscoveryListener listener, long leaseDuration) throws RemoteException { /* Atomic start of terminator */ synchronized (terminatorThread) { if (!started) { terminatorThread.start(); } started = true; } if (tmpl == null) { tmpl = new ServiceTemplate(null, null, null); } LookupCacheImpl cache = new LookupCacheImpl(tmpl, filter, listener, leaseDuration, this, useInsecureLookup()); cache.initCache(); cachesWrite.lock(); try { caches.add(cache); } finally { cachesWrite.unlock(); } if (logger.isLoggable(Level.FINEST)) log(Level.FINE, "ServiceDiscoveryManager - LookupCache created"); return cache; }//end createLookupCache /** * LookupCache removes itself once it has been terminated. * @param cache * @return true if removed. */ boolean removeLookupCache(LookupCache cache){ cachesWrite.lock(); try { return caches.remove(cache); } finally { cachesWrite.unlock(); } } /** * Removes and returns element from proxyRegSet that corresponds to the * given proxy. */ ProxyReg removeReg(ServiceRegistrar proxy) { ProxyReg pReg = new ProxyReg(proxy); proxyRegSetWrite.lock(); try{ if(proxyRegSet.remove(pReg)) return pReg; return null; } finally { proxyRegSetWrite.unlock(); } }//end removeReg /** * Convenience method invoked when failure occurs in the cache tasks * executed in this utility. If the appropriate logging level is enabled, * this method will log the stack trace of the given Throwable; * noting the given source class and method, and displaying the given * message. Additionally, this method will discard the given lookup service * proxy. Note that if the utility itself has already been terminated, or if * the cache in which the failure occurred has been terminated, then the * failure is logged at the HANDLED level, and the lookup service proxy is * not discarded. * * Also, note that if the discovery manager employed by this utility has * already been terminated, then the attempt to discard the given lookup * service proxy will result in an IllegalStateException. Since * this method is called from within the tasks run by this utility, and * since propagating an IllegalStateException out into the * ThreadGroup of those tasks is undesirable, this method does not propagate * IllegalStateExceptions that occur as a result of an attempt * to discard a lookup service proxy from the discovery manager. * * For more information, refer to Bug 4490358 and 4858211. */ void fail(Throwable e, ServiceRegistrar proxy, String sourceClass, String sourceMethod, String msg, boolean cacheTerminated) { Level logLevel = Level.INFO; boolean discardProxy = true; synchronized (this) { if (bTerminated || cacheTerminated) { logLevel = Levels.HANDLED; discardProxy = false; }//endif }//end sync(this) if ((e != null) && (logger.isLoggable(logLevel))) { logp(logLevel, sourceClass, sourceMethod, msg, e); }//endif try { if (discardProxy) { discard(proxy); } } catch (IllegalStateException e1) { if (logger.isLoggable(logLevel)) { logp( logLevel, sourceClass, sourceMethod, "failure discarding lookup service proxy, " + "discovery manager already terminated", e1 ); }//endif } }//end fail /** * Discards a ServiceRegistrar through the discovery manager. */ private void discard(ServiceRegistrar proxy) { discMgr.discard(proxy); }//end discard /** * Cancels the given event lease. */ void cancelLease(Lease lease) { try { leaseRenewalMgr.cancel(lease); } catch (Exception e) { if (logger.isLoggable(Level.FINER)) log(Level.FINER, "exception occurred while cancelling an event " + "registration lease", e); } }//end cancelLease /** * Registers for events from the lookup service associated with the given * proxy, and returns both the lease and the event sequence number from the * event registration wrapped in the locally-defined class, * EventReg. * * This method is called from the RegisterListenerTask. If a * RemoteException occurs during the event registration * attempt, this method discards the lookup service and returns * null. */ EventReg registerListener(ServiceRegistrar proxy, ServiceTemplate tmpl, RemoteEventListener listenerProxy, long duration) throws RemoteException { /* Register with the event mechanism of the given lookup service */ EventRegistration e; int transition = (ServiceRegistrar.TRANSITION_NOMATCH_MATCH | ServiceRegistrar.TRANSITION_MATCH_NOMATCH | ServiceRegistrar.TRANSITION_MATCH_MATCH); if (useInsecureLookup()){ e = proxy.notify(tmpl, transition, listenerProxy, null, duration); } else { e = ((SafeServiceRegistrar)proxy).notiFy(tmpl, transition, listenerProxy, null, duration); } /* Proxy preparation - * * Prepare the proxy to the lease on the event registration just * returned. Because lease management (renewal and cancellation) * involves remote calls, lease proxies should be prepared before * management of the associated leases begins. This allows one to * verify trust in the lease, and ensures that the appropriate * constraints are attached to the lease. */ Lease eventLease = e.getLease(); eventLease = (Lease) eventLeasePreparer.prepareProxy(eventLease); if (logger.isLoggable(Level.FINEST)) log(Level.FINEST, "ServiceDiscoveryManager - proxy to event registration lease prepared: {0}", new Object []{eventLease} ); /* Management the lease on the event registration */ leaseRenewalMgr.renewFor(eventLease, duration, new LeaseListenerImpl(proxy)); /* Wrap source, id, event sequence & lease in EventReg, and return. */ return (new EventReg(e.getSource(), e.getID(), e.getSequenceNumber(), eventLease)); }//end registerListener /** * Throws an IllegalStateException if the current instance of the * ServiceDiscoveryManager has been terminated. */ synchronized void checkTerminated() { if (bTerminated) { throw new IllegalStateException("service discovery manager was terminated"); }//endif }//end checkTerminated /** * Determines if the given ServiceItem is an element of the given array. */ static private boolean isArrayContainsServiceItem(List a, ServiceItem s) { Iterator iter = a.iterator(); while (iter.hasNext()) { Object o = iter.next(); if (!(o instanceof ServiceItem)) { continue; } ServiceItem sa = (ServiceItem) o; if (sa.serviceID.equals(s.serviceID) && LookupAttributes.equal(sa.attributeSets, s.attributeSets) && (sa.service.equals(s.service))) { return true; } }//end loop return false; }//end isArrayContainsServiceItems /** * Initializer for ServiceDiscoveryManager */ private static class Initializer { Configuration thisConfig; ProxyPreparer registrarPreparer; ProxyPreparer eventLeasePreparer; ProxyPreparer bootstrapProxyPreparer; LeaseRenewalManager leaseRenewalMgr; long discardWait; DiscoveryManagement discMgr; boolean discMgrInternal; boolean useInsecureLookup; } private static Initializer initial( DiscoveryManagement discoveryMgr, LeaseRenewalManager leaseMgr, Configuration config) throws IOException { try { return init(discoveryMgr, leaseMgr, config); } catch (ConfigurationException e) { /* This should never happen */ throw new IOException(e); } } /* Convenience method that encapsulates the retrieval of the configurable * items from the given Configuration object. */ private static Initializer init(DiscoveryManagement discoveryMgr, LeaseRenewalManager leaseMgr, Configuration config) throws IOException, ConfigurationException { /* Retrieve configuration items if applicable */ if (config == null) { throw new NullPointerException("config is null"); } Initializer init = new Initializer(); init.thisConfig = config; /* Proxy preparers */ init.registrarPreparer = init.thisConfig.getEntry(COMPONENT_NAME, "registrarPreparer", ProxyPreparer.class, new BasicProxyPreparer()); init.eventLeasePreparer = init.thisConfig.getEntry(COMPONENT_NAME, "eventLeasePreparer", ProxyPreparer.class, new BasicProxyPreparer()); init.bootstrapProxyPreparer = init.thisConfig.getEntry(COMPONENT_NAME, "bootstrapPreparer", ProxyPreparer.class, new BasicProxyPreparer()); /* Lease renewal manager */ init.leaseRenewalMgr = leaseMgr; if (init.leaseRenewalMgr == null) { try { init.leaseRenewalMgr = init.thisConfig.getEntry(COMPONENT_NAME, "leaseManager", LeaseRenewalManager.class); } catch (NoSuchEntryException e) { /* use default */ init.leaseRenewalMgr = new LeaseRenewalManager(init.thisConfig); } }//endif /* Wait value for the "service discard problem". */ init.discardWait = (init.thisConfig.getEntry(COMPONENT_NAME, "discardWait", long.class, 600000L)); /* Discovery manager */ init.discMgr = discoveryMgr; if (init.discMgr == null) { init.discMgrInternal = true; try { init.discMgr = init.thisConfig.getEntry(COMPONENT_NAME, "discoveryManager", DiscoveryManagement.class); } catch (NoSuchEntryException e) { /* use default */ init.discMgr = new LookupDiscoveryManager(new String[]{""}, null, null, init.thisConfig); } }//endif init.useInsecureLookup = (init.thisConfig.getEntry(COMPONENT_NAME, "useInsecureLookup", Boolean.class, Boolean.FALSE)); return init; }//end init /** * Applies the given filter to the given item, and * returns true if the filter returns a * pass value; otherwise, returns false. *

* Note that as described in the specification of * ServiceItemFilter, when the item passes the * filter, the service field of the * item is replaced with the filtered form of the object * previously contained in that field. Additionally, if the * filter returns indefinite, then as specified, * the service field is replaced with null (in * which case, this method returns false). *

* This method is used by the non-blocking version(s) of the * lookup method of the ServiceDiscoveryManager, * as well as when second-stage filtering is performed in the * LookupCache. */ static boolean filterPassed(ServiceItem item, ServiceItemFilter filter) { if ((item == null) || (item.service == null)) { return false; } if (filter == null) { return true; } return filter.check(item); }//end filterPassFail }//end class ServiceDiscoveryManager





© 2015 - 2025 Weber Informatics LLC | Privacy Policy