/*
* Copyright (c) 2001-2007 Sun Microsystems, Inc. All rights reserved.
*
* The Sun Project JXTA(TM) Software License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The end-user documentation included with the redistribution, if any, must
* include the following acknowledgment: "This product includes software
* developed by Sun Microsystems, Inc. for JXTA(TM) technology."
* Alternately, this acknowledgment may appear in the software itself, if
* and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
* not be used to endorse or promote products derived from this software
* without prior written permission. For written permission, please contact
* Project JXTA at http://www.jxta.org.
*
* 5. Products derived from this software may not be called "JXTA", nor may
* "JXTA" appear in their name, without prior written permission of Sun.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN
* MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* JXTA is a registered trademark of Sun Microsystems, Inc. in the United
* States and other countries.
*
* Please see the license information page at :
* <http://www.jxta.org/project/www/license.html> for instructions on use of
* the license in source files.
*
* ====================================================================
*
* This software consists of voluntary contributions made by many individuals
* on behalf of Project JXTA. For more information on Project JXTA, please see
* http://www.jxta.org.
*
* This license is based on the BSD license adopted by the Apache Foundation.
*/
package net.jxta.impl.peergroup;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.URI;
import java.net.URL;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.jxta.access.AccessService;
import net.jxta.content.ContentService;
import net.jxta.discovery.DiscoveryService;
import net.jxta.document.Advertisement;
import net.jxta.document.AdvertisementFactory;
import net.jxta.document.Element;
import net.jxta.document.StructuredDocument;
import net.jxta.document.XMLElement;
import net.jxta.endpoint.EndpointService;
import net.jxta.exception.PeerGroupException;
import net.jxta.exception.ProtocolNotSupportedException;
import net.jxta.exception.ServiceNotFoundException;
import net.jxta.id.ID;
import net.jxta.id.IDFactory;
import net.jxta.impl.loader.RefJxtaLoader;
import net.jxta.impl.protocol.PSEConfigAdv;
import net.jxta.impl.protocol.PeerGroupConfigAdv;
import net.jxta.impl.protocol.PeerGroupConfigFlag;
import net.jxta.impl.protocol.PlatformConfig;
import net.jxta.impl.util.TimeUtils;
import net.jxta.impl.util.threads.TaskManager;
import net.jxta.logging.Logging;
import net.jxta.membership.MembershipService;
import net.jxta.peer.PeerID;
import net.jxta.peer.PeerInfoService;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.pipe.PipeService;
import net.jxta.platform.JxtaLoader;
import net.jxta.platform.Module;
import net.jxta.platform.ModuleClassID;
import net.jxta.platform.ModuleSpecID;
import net.jxta.protocol.ConfigParams;
import net.jxta.protocol.ModuleImplAdvertisement;
import net.jxta.protocol.PeerAdvertisement;
import net.jxta.protocol.PeerGroupAdvertisement;
import net.jxta.rendezvous.RendezVousService;
import net.jxta.resolver.ResolverService;
import net.jxta.service.Service;
/**
* Provides common services for most peer group implementations.
*/
public abstract class GenericPeerGroup implements PeerGroup {
/**
* Logger
*/
private final static transient Logger LOG = Logger.getLogger(GenericPeerGroup.class.getName());
/**
* Holder for configuration parameters for groups in the process of being created.
*/
private final static Map<ID, ConfigParams> group_configs = Collections.synchronizedMap(new HashMap<ID, ConfigParams>());
/**
* Default compatibility equater instance.
*/
private static final CompatibilityEquater COMP_EQ =
new CompatibilityEquater() {
public boolean compatible(Element test) {
return CompatibilityUtils.isCompatible(test);
}
};
/**
* Statically scoped JxtaLoader which is used as the root of the
* JXTA class loader hierarchy. The world peer group defers to the
* static loader as it's parent class loader.
* <p/>
* This class loader is a concession. Use the group-scoped loader
* instead.
* <p/>
* XXX 20080817 mcumings - I'd like this to go away entirely, now that
* each group has it's own JxtaLoader instance. If the root loader was
* simply the JxtaLoader used by the WPG things would make more sense.
*/
private final static JxtaLoader staticLoader =
new RefJxtaLoader(new URL[0], COMP_EQ);
/**
* The PeerGroup-specific JxtaLoader instance.
*/
private JxtaLoader loader;
/*
* Shortcuts to well known services.
*/
private EndpointService endpoint;
private ResolverService resolver;
private DiscoveryService discovery;
private PipeService pipe;
private MembershipService membership;
private RendezVousService rendezvous;
private PeerInfoService peerinfo;
private AccessService access;
private ContentService content;
/**
* This peer's advertisement in this group.
*/
private final PeerAdvertisement peerAdvertisement;
/**
* This group's advertisement.
*/
private PeerGroupAdvertisement peerGroupAdvertisement = null;
/**
* This group's implAdvertisement.
*/
protected ModuleImplAdvertisement implAdvertisement = null;
/**
* This peer's config advertisement.
*/
protected ConfigParams configAdvertisement = null;
/**
* This service implements a group but, being a Service, it runs inside of
* some group. That's its home group.
* <p/>
* Exception: The platform peer group does not have a parent group. It
* has to be entirely self sufficient.
*/
protected PeerGroup parentGroup = null;
/**
* The location of our store
*/
protected URI jxtaHome = null;
/**
* The services that do the real work of the Peer Group.
*/
private final Map<ModuleClassID, Service> services = new HashMap<ModuleClassID, Service>();
/**
* {@code true} when we have decided to stop this group.
*/
private volatile boolean stopping = false;
/**
* {@code true} when the PG adv has been published.
*/
private boolean published = false; // assume it hasn't
/**
* Counts the number of times an interface to this group has been given out.
* This is decremented every time an interface object is GCed or its owner
* calls {@link unref()}.
* <p/
* >When it reaches zero, if it is time to tear-down the group instance;
* nomatter what the GC thinks. There are threads that need to be stopped
* before the group instance object ever becomes un-reachable.
*/
private AtomicInteger masterRefCount = new AtomicInteger(0);
/**
* Is {@code true} when at least one interface object has been generated
* AFTER initComplete has become true. If true, the group stops when its ref
* count reaches zero.
*/
private boolean stopWhenUnreferenced = false;
/**
* Is set to {@code true} when {@code init()} is completed enough that it
* makes sense to perform ref-counting.
*/
protected volatile boolean initComplete = false;
/**
* The thread group in which threads created by this group or services of
* this group should live. The thread group is used primarily for debugging
* and classification purposes--we don't try to use any of the fancy (and
* mostly useless) ThreadGroup features.
*/
private ThreadGroup threadGroup = null;
/**
* The minimum number of Threads our Executor will reserve. Once started
* these Threads will remain.
*
* todo convert these hardcoded settings into group config params.
*/
private final int COREPOOLSIZE = 5;
/**
* The number of seconds that Threads above {@code COREPOOLSIZE} will
* remain idle before terminating.
*
* todo convert these hardcoded settings into group config params.
*/
private final long KEEPALIVETIME = 15;
/**
* The intended upper bound on the number of threads we will allow our
* Executor to create. We will allow the pool to grow to twice this size if
* we run out of threads.
*
* todo convert these hardcoded settings into group config params.
*/
private final int MAXPOOLSIZE = 100;
/**
* Queue for tasks waiting to be run by our {@code Executor}.
*/
private BlockingQueue<Runnable> taskQueue;
/**
* The PeerGroup ThreadPool
*/
private ExecutorService threadPool;
/**
* The PeerGroup ScheduledExecutor
*/
private ScheduledExecutorService scheduledExecutor;
/**
* {@inheritDoc}
* <p/>
* We do not want to count on the invoker to properly unreference the group
* object that we return; this call is often used in a loop and it is silly
* to increment and decrement ref-counts for references that are sure to
* live shorter than the referee.
* <p/>
* On the other hand it is dangerous for us to share our reference object to
* the parent group. That's where weak interface objects come in handy. We
* can safely make one and give it away.
*/
public PeerGroup getParentGroup() {
if (parentGroup == null) {
return null;
}
return parentGroup.getWeakInterface();
}
/**
* {@inheritDoc}
*/
public URI getStoreHome() {
return jxtaHome;
}
/**
* Sets the root location for the store to be used by this peergroup.
* <p/>
* This should be set early in the peer group's life and then never changed.
*
* @param newHome The new store location.
*/
protected void setStoreHome(URI newHome) {
jxtaHome = newHome;
}
/**
* Get a JxtaLoader instance which can be used to load modules
* irrespective of the PeerGroup.
*
* @return JxtaLoader instance
* @deprecated this statically scoped JxtaLoader instance should be phased
* out of use in favor of the group-scoped JxtaLoaders available via the
* {@code getLoader()} method.
*/
@Deprecated
public static JxtaLoader getJxtaLoader() {
return staticLoader;
}
public GenericPeerGroup() {
// Start building our peer adv.
peerAdvertisement = (PeerAdvertisement) AdvertisementFactory.newAdvertisement(PeerAdvertisement.getAdvertisementType());
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object target) {
if (!(target instanceof PeerGroup)) {
return false;
}
PeerGroup targetAsPeerGroup = (PeerGroup) target;
// both null or both non-null.
if ((null == parentGroup) && (null != targetAsPeerGroup.getParentGroup())) {
return false;
}
if ((null != parentGroup) && (null == targetAsPeerGroup.getParentGroup())) {
return false;
}
if ((null != parentGroup) && !parentGroup.equals(targetAsPeerGroup.getParentGroup())) {
return false;
}
// and same peer ids.
return getPeerGroupID().equals(targetAsPeerGroup.getPeerGroupID());
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
// before init we must fail.
if ((null == peerAdvertisement) || (null == getPeerGroupID())) {
throw new IllegalStateException("PeerGroup not sufficiently initialized");
}
// XXX 20050907 bondolo including parentGroup would improve the hash.
return getPeerGroupID().hashCode();
}
/**
* {@inheritDoc}
* <p/>
* An implementation suitable for debugging. <b>Don't try to parse this
* string!</b> All of the information is available from other sources.
*/
@Override
public String toString() {
if (null == getPeerGroupID()) {
return super.toString();
}
StringBuilder result = new StringBuilder();
result.append(getPeerGroupID().toString());
String peerGroupName = peerGroupAdvertisement.getName();
if (null != peerGroupName) {
result.append(" \"");
result.append(peerGroupName);
result.append('\"');
}
result.append('[');
result.append(masterRefCount.get());
result.append(",");
result.append(loader.hashCode());
result.append(']');
if (parentGroup == null) {
result.append(" / [");
result.append(staticLoader.hashCode());
result.append("]");
} else {
result.append(" / ");
result.append(parentGroup.toString());
}
return result.toString();
}
/**
* {@inheritDoc}
*/
public ThreadGroup getHomeThreadGroup() {
return threadGroup;
}
/**
* Discover advertisements.
*
* @param discovery The discovery service to use.
* @param type the Discovery advertisement type.
* @param attr The attribute to search for or {@code null}.
* @param value The attribute value to match or {@code null}.
* @param seconds The number of seconds to search.
* @param thisClass The Advertisement class which the advertisement must
* match.
* @return a Collection of advertisements
*/
private Collection<Advertisement> discoverSome(DiscoveryService discovery, int type, String attr, String value, int seconds, Class thisClass) {
long discoverUntil = TimeUtils.toAbsoluteTimeMillis(seconds * TimeUtils.ASECOND);
long lastRemoteAt = 0; // no previous remote discovery made.
List<Advertisement> results = new ArrayList<Advertisement>();
try {
do {
Enumeration<Advertisement> res = discovery.getLocalAdvertisements(type, attr, value);
while (res.hasMoreElements()) {
Advertisement a = res.nextElement();
if (thisClass.isInstance(a)) {
results.add(a);
}
}
if (!results.isEmpty()) {
break;
}
if (TimeUtils.toRelativeTimeMillis(TimeUtils.timeNow(), lastRemoteAt) > (30 * TimeUtils.ASECOND)) {
discovery.getRemoteAdvertisements(null, type, attr, value, 20);
lastRemoteAt = TimeUtils.timeNow();
}
// snooze waiting for responses to come in.
Thread.sleep(1000);
} while (TimeUtils.timeNow() < discoverUntil);
} catch (Exception whatever) {
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Failure during discovery", whatever);
}
}
return results;
}
/**
* Discover an advertisement within the local peer group.
*
* @param type the Discovery advertisement type.
* @param attr The attribute to search for or {@code null}.
* @param value The attribute value to match or {@code null}.
* @param seconds The number of seconds to search.
* @param thisClass The Advertisement class which the advertisement must match.
* @return a Collection of advertisements
*/
private Advertisement discoverOne(int type, String attr, String value, int seconds, Class thisClass) {
Iterator<Advertisement> res = discoverSome(discovery, type, attr, value, seconds, thisClass).iterator();
if (!res.hasNext()) {
return null;
}
return res.next();
}
/**
* Shortcuts to the standard basic services.
*
* @param mcid The Module Class ID of the service.
* @param service The service instance to set as the shortcut or
* {@code null} to clear the shortcut.
*/
private void setShortCut(ModuleClassID mcid, Service service) {
if (endpointClassID.equals(mcid)) {
endpoint = (EndpointService) service;
return;
}
if (resolverClassID.equals(mcid)) {
resolver = (ResolverService) service;
return;
}
if (discoveryClassID.equals(mcid)) {
discovery = (DiscoveryService) service;
return;
}
if (pipeClassID.equals(mcid)) {
pipe = (PipeService) service;
return;
}
if (membershipClassID.equals(mcid)) {
membership = (MembershipService) service;
return;
}
if (peerinfoClassID.equals(mcid)) {
peerinfo = (PeerInfoService) service;
return;
}
if (rendezvousClassID.equals(mcid)) {
rendezvous = (RendezVousService) service;
return;
}
if (accessClassID.equals(mcid)) {
access = (AccessService) service;
}
if (contentClassID.equals(mcid)) {
content = (ContentService) service;
}
}
/**
* Add a service to the collection of known services.
*
* @param mcid The Module Class ID of the service.
* @param service The service instance to set as the shortcut or
*/
protected synchronized void addService(ModuleClassID mcid, Service service) {
if (stopping) {
return;
}
if (services.containsKey(mcid)) {
throw new IllegalStateException("Service" + mcid + " already registered.");
}
services.put(mcid, service);
setShortCut(mcid, service);
}
/**
* {@inheritDoc}
*/
synchronized public Service lookupService(ID mcid) throws ServiceNotFoundException {
Service p = services.get(mcid);
if (p == null) {
throw new ServiceNotFoundException("Not found: " + mcid.toString());
}
return p.getInterface();
}
/**
* {@inheritDoc}
* <p/>
* Group implementations do not have to support mapping.
* it would be nice to separate better Interfaces, so that
* Interface Objects can do things that the real service does
* not have to implement.
*/
public Service lookupService(ID mcid, int roleIndex) throws ServiceNotFoundException {
// If the role number is != 0, it can't be honored: we
// do not have an explicit map.
if (roleIndex != 0) {
throw new ServiceNotFoundException("Not found: " + mcid + "[" + roleIndex + "]");
}
return lookupService(mcid);
}
/**
* {@inheritDoc}
*/
public Iterator getRoleMap(ID name) {
// No translation; use the given name in a singleton.
return Collections.singletonList(name).iterator();
}
/**
* check that all required core services are registered
*
* @throws ServiceNotFoundException If a required service was not found.
*/
protected void checkServices() throws ServiceNotFoundException {
Service ignored;
ignored = lookupService(endpointClassID);
ignored = lookupService(resolverClassID);
ignored = lookupService(membershipClassID);
ignored = lookupService(accessClassID);
/**
* ignored = lookupService(discoveryClassID);
* ignored = lookupService(pipeClassID);
* ignored = lookupService(rendezvousClassID);
* ignored = lookupService(peerinfoClassID);
*/
}
/**
* Ask a group to unregister and unload a service
*
* @param mcid The service to be removed.
* @throws ServiceNotFoundException if service is not found
*/
protected synchronized void removeService(ModuleClassID mcid) throws ServiceNotFoundException {
setShortCut(mcid, null);
Service p = services.remove(mcid);
if (p == null) {
throw new ServiceNotFoundException("Not found: " + mcid.toString());
}
p.stopApp();
// service.terminate(); FIXME. We probably need a terminate() method.
//
// FIXME 20011013 jice To make sure the service is no-longer referenced
// we should always return interfaces, and have a way to cut the
// reference to the real service in the interfaces. One way of doing
// that would be to have to levels of indirection: we should keep one
// and return references to it.
// When we want to cut the service loose, we should clear the reference
// from the interface that we own before letting it go. We need to study
// the consequences of doing that before implementing it.
}
/**
* {@inheritDoc}
*/
public Module loadModule(ID assigned, Advertisement impl) throws ProtocolNotSupportedException, PeerGroupException {
return loadModule(assigned, (ModuleImplAdvertisement) impl, false);
}
/**
* Load a Module from a ModuleImplAdv.
* <p/>
* Compatibility is checked and load is attempted. If compatible and
* loaded successfully, the resulting Module is initialized and returned.
* In most cases the other loadModule() method should be preferred, since
* unlike this one, it will seek many compatible implementation
* advertisements and try them all until one works. The home group of the new
* module (its' parent group if the new Module is a group) will be this group.
*
* @param assigned Id to be assigned to that module (usually its ClassID).
* @param implAdv An implementation advertisement for that module.
* @param privileged If {@code true} then the module is provided the true
* group obj instead of just an interface to the group object. This is
* normally used only for the group's defined services and applications.
* @return Module the module loaded and initialized.
* @throws ProtocolNotSupportedException The module is incompatible.
* @throws PeerGroupException The module could not be loaded or initialized
*/
protected Module loadModule(ID assigned, ModuleImplAdvertisement implAdv, boolean privileged) throws ProtocolNotSupportedException, PeerGroupException {
Element compat = implAdv.getCompat();
if (null == compat) {
throw new IllegalArgumentException("No compatibility statement for : " + assigned);
}
if (!compatible(compat)) {
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.warning("Incompatible Module : " + assigned);
}
throw new ProtocolNotSupportedException("Incompatible Module : " + assigned);
}
Module newMod = null;
if ((null != implAdv.getCode()) && (null != implAdv.getUri())) {
try {
// Good one. Try it.
Class<? extends Module> clazz;
try {
clazz = loader.loadClass(implAdv.getModuleSpecID());
} catch (ClassNotFoundException notLoaded) {
clazz = loader.defineClass(implAdv);
}
if (null == clazz) {
throw new ClassNotFoundException("Cannot load class (" + implAdv.getCode() + ") : " + assigned);
}
newMod = clazz.newInstance();
newMod.init(privileged ? this : getInterface(), assigned, implAdv);
if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
LOG.info("Loaded" + (privileged ? " privileged" : "") +
" module : " + implAdv.getDescription() + " (" + implAdv.getCode() + ")");
}
} catch (Exception ex) {
try {
newMod.stopApp();
} catch (Throwable ignored) {
// If this does not work, nothing needs to be done.
}
throw new PeerGroupException("Could not load module for : " + assigned + " (" + implAdv.getDescription() + ")", ex);
}
} else {
String error;
if (null == implAdv.getCode()) {
error = "ModuleImpAdvertisement missing Code element";
} else if (null == implAdv.getUri()) {
error = "ModuleImpAdvertisement missing URI element";
} else {
error = "ModuleImpAdvertisement missing both Code and URI elements";
}
throw new PeerGroupException("Can not load module : " + error + " for" + assigned);
}
// Publish or renew the lease of this adv since we're using it.
try {
if (discovery != null) {
discovery.publish(implAdv, DEFAULT_LIFETIME, DEFAULT_EXPIRATION);
}
} catch (Exception ignored) {
// ignored
}
// If we reached this point we're done.
return newMod;
}
/**
* {@inheritDoc}
*/
public Module loadModule(ID assigned, ModuleSpecID specID, int where) {
return loadModule(assigned, specID, where, false);
}
/**
* Load a module from a ModuleSpecID
* <p/>
* Advertisement is sought, compatibility is checked on all candidates and
* load is attempted. The first one that is compatible and loads
* successfully is initialized and returned.
*
* @param assignedID Id to be assigned to that module (usually its ClassID).
* @param specID The specID of this module.
* @param where May be one of: {@code Here}, {@code FromParent}, or
* {@code Both}, meaning that the implementation advertisement will be
* searched in this group, its parent or both. As a general guideline, the
* implementation advertisements of a group should be searched in its
* prospective parent (that is Here), the implementation advertisements of a
* group standard service should be searched in the same group than where
* this group's advertisement was found (that is, FromParent), while
* applications may be sought more freely (Both).
* @param privileged If {@code true} then the module is provided the true
* group obj instead of just an interface to the group object. This is
* normally used only for the group's defined services and applications.
* @return Module the new module, or {@code null} if no usable implementation was found.
*/
protected Module loadModule(ID assignedID, ModuleSpecID specID, int where, boolean privileged) {
List<Advertisement> allModuleImplAdvs = new ArrayList<Advertisement>();
ModuleImplAdvertisement loadedImplAdv = loader.findModuleImplAdvertisement(specID);
// We already have a module defined for this spec id.
// We test the spec id before deciding to use it
if (null != loadedImplAdv && specID.equals(loadedImplAdv.getModuleSpecID())) {
allModuleImplAdvs.add(loadedImplAdv);
}
// A module implementation may have been found, but was not valid and not registered.
// If so, we need to broaden the search
if (allModuleImplAdvs.isEmpty()){
boolean fromHere = (where == Here || where == Both);
boolean fromParent = (where == FromParent || where == Both);
if (fromHere && (null != discovery)) {
Collection<Advertisement> here = discoverSome(discovery, DiscoveryService.ADV,
"MSID", specID.toString(), 120, ModuleImplAdvertisement.class);
allModuleImplAdvs.addAll(here);
}
if (fromParent && (null != getParentGroup()) && (null != parentGroup.getDiscoveryService())) {
Collection<Advertisement> parent = discoverSome(parentGroup.getDiscoveryService(), DiscoveryService.ADV,
"MSID", specID.toString(), 120, ModuleImplAdvertisement.class);
allModuleImplAdvs.addAll(parent);
}
}
Throwable recentFailure = null;
for (Advertisement eachAdv : allModuleImplAdvs) {
if (!(eachAdv instanceof ModuleImplAdvertisement)) {
continue;
}
ModuleImplAdvertisement foundImpl = (ModuleImplAdvertisement) eachAdv;
try {
// First check that the MSID is really the one we're looking for.
// It could have appeared somewhere else in the adv than where
// we're looking, and discovery doesn't know the difference.
if (!specID.equals(foundImpl.getModuleSpecID())) {
continue;
}
Module newMod = loadModule(assignedID, foundImpl, privileged);
// If we reach that point, the module is good.
return newMod;
} catch (ProtocolNotSupportedException failed) {
// Incompatible implementation.
if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Incompatbile impl adv");
}
} catch (PeerGroupException failed) {
// Initialization failure.
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Initialization failed", failed);
}
} catch (Throwable e) {
recentFailure = e;
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Not a usable impl adv: ", e);
}
}
}
// Throw an exception if there was a recent failure.
if (null != recentFailure) {
if (recentFailure instanceof Error) {
throw (Error) recentFailure;
} else if (recentFailure instanceof RuntimeException) {
throw (RuntimeException) recentFailure;
} else {
throw new UndeclaredThrowableException(recentFailure);
}
}
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.warning("Could not find a loadable implementation for SpecID: " + specID);
}
return null;
}
/**
* {@inheritDoc}
*/
public ConfigParams getConfigAdvertisement() {
return configAdvertisement;
}
/**
* Sets the configuration advertisement for this peer group.
*
* @param config The configuration advertisement which will be used for
* this peer group or {@code null} if no configuration advertisement is to
* be used.
*/
protected void setConfigAdvertisement(ConfigParams config) {
configAdvertisement = config;
}
/**
* Adds configuration parameters for the specified group. The configuration
* parameters remain cached until either the specified group is started or
* the parameters are replaced.
*
* @param groupid The group for who's params are being provided.
* @param params The parameters to be provided to the peer group when it is
* created.
*/
public static void setGroupConfigAdvertisement(ID groupid, ConfigParams params) {
if (null != params) {
group_configs.put(groupid, params);
} else {
group_configs.remove(groupid);
}
}
/*
* Now comes the implementation of the public API, including the
* API mandated by the Service interface.
*/
/**
* {@inheritDoc}
* <p/>
* It is not recommended to overload this method. Instead, subclassers
* should overload either or both of
* {@link #initFirst(PeerGroup,ID,Advertisement)} and {@link #initLast()}.
* If this method is to be overloaded, the overloading method must
* invoke <code>super.init</code>.
* <p/>
* This method invokes <code>initFirst</code>
* with identical parameters. <code>initLast</initLast> does not take
* parameters since the relevant information can be obtained from the
* group following completion of the <code>initFirst</code> phase.
* The resulting values may be different from the parameters to
* <code>initFirst</code> since <code>initFirst</code> may
* be overLoaded and the overloading method may modify these parameters
* when calling <code>super.initFirst</code>. (See
* {@link net.jxta.impl.peergroup.Platform} for an example of such a case).
* <p/>
* Upon completion, the group object is marked as completely initialized
* in all cases. Once a group object is completely initialized, it becomes
* sensitive to reference counting.
* <p/>
* In the future this method may become final.
*/
public void init(PeerGroup homeGroup, ID assignedID, Advertisement impl) throws PeerGroupException {
try {
initFirst(homeGroup, assignedID, impl);
initLast();
} finally {
// This must be done in all cases.
initComplete = true;
}
}
/**
* Performs all initialization steps that need to be performed
* before any subclass initialization is performed.
* <p/>
* Classes that override this method should always call
* <code>super.initFirst()</code> <strong>before</strong> doing
* any of their own work.
*
* @param homeGroup The group that serves as a parent to this group.
* @param assignedID The unique ID assigned to this module. For
* group this is the group ID or <code>null</code> if a group ID
* has not yet been assigned. If null is passed, GenericPeerGroup
* will generate a new group ID.
* @param impl The ModuleImplAdvertisement which defines this
* group's implementation.
* @throws PeerGroupException if a group initialization error occurs
*/
protected void initFirst(PeerGroup homeGroup, ID assignedID, Advertisement impl) throws PeerGroupException {
this.implAdvertisement = (ModuleImplAdvertisement) impl;
this.parentGroup = homeGroup;
if (null != parentGroup) {
jxtaHome = parentGroup.getStoreHome();
}
// Set the peer configuration before we start.
if ((null != assignedID) && (null == getConfigAdvertisement())) {
setConfigAdvertisement(group_configs.remove(assignedID));
}
try {
// FIXME 20030919 bondolo@jxta.org This setup doesnt give us any
// capability to use seed material or parent group.
if (null == assignedID) {
if ("cbid".equals(IDFactory.getDefaultIDFormat())) {
throw new IllegalStateException("Cannot generate group id for cbid group");
} else {
assignedID = IDFactory.newPeerGroupID();
}
} else {
if (parentGroup != null) {
DiscoveryService disco = parentGroup.getDiscoveryService();
if (null != disco) {
Enumeration found = disco.getLocalAdvertisements(DiscoveryService.GROUP, "GID", assignedID.toString());
if (found.hasMoreElements()) {
peerGroupAdvertisement = (PeerGroupAdvertisement) found.nextElement();
}
}
}
}
if (!(assignedID instanceof PeerGroupID)) {
throw new PeerGroupException("assignedID must be a peer group ID");
}
peerAdvertisement.setPeerGroupID((PeerGroupID) assignedID);
// // make sure the parent group is the required group
// if (null != peerAdvertisement.getPeerGroupID().getParentPeerGroupID()) {
// if (null == parentGroup) {
// throw new PeerGroupException("Group requires parent group : " + peerAdvertisement.getPeerGroupID().getParentPeerGroupID());
// } else if (!parentGroup.getPeerGroupID().equals(peerAdvertisement.getPeerGroupID().getParentPeerGroupID())) {
// throw new PeerGroupException("Group requires parent group : " + peerAdvertisement.getPeerGroupID().getParentPeerGroupID() + ". Provided parent was : " + parentGroup.getPeerGroupID());
// }
// }
// Do our part of the PeerAdv construction.
if ((configAdvertisement != null) && (configAdvertisement instanceof PlatformConfig)) {
PlatformConfig platformConfig = (PlatformConfig) configAdvertisement;
// Normally there will be a peer ID and a peer name in the config.
PeerID configPID = platformConfig.getPeerID();
if ((null == configPID) || (ID.nullID == configPID)) {
if ("cbid".equals(IDFactory.getDefaultIDFormat())) {
// Get our peer-defined parameters in the configAdv
XMLElement param = (XMLElement) platformConfig.getServiceParam(PeerGroup.membershipClassID);
if (null == param) {
throw new IllegalArgumentException(PSEConfigAdv.getAdvertisementType() + " could not be located");
}
Advertisement paramsAdv = null;
try {
paramsAdv = AdvertisementFactory.newAdvertisement(param);
} catch (NoSuchElementException noadv) {// ignored
}
if (!(paramsAdv instanceof PSEConfigAdv)) {
throw new IllegalArgumentException("Provided Advertisement was not a " + PSEConfigAdv.getAdvertisementType());
}
PSEConfigAdv config = (PSEConfigAdv) paramsAdv;
Certificate clientRoot = config.getCertificate();
byte[] pub_der = clientRoot.getPublicKey().getEncoded();
platformConfig.setPeerID(IDFactory.newPeerID((PeerGroupID) assignedID, pub_der));
} else {
platformConfig.setPeerID(IDFactory.newPeerID((PeerGroupID) assignedID));
}
}
peerAdvertisement.setPeerID(platformConfig.getPeerID());
peerAdvertisement.setName(platformConfig.getName());
peerAdvertisement.setDesc(platformConfig.getDesc());
} else {
if (null == parentGroup) {
// If we did not get a valid peer id, we'll initialize it here.
peerAdvertisement.setPeerID(IDFactory.newPeerID((PeerGroupID) assignedID));
} else {
// We're not the world peer group, which is the authoritative source of these values.
peerAdvertisement.setPeerID(parentGroup.getPeerAdvertisement().getPeerID());
peerAdvertisement.setName(parentGroup.getPeerAdvertisement().getName());
peerAdvertisement.setDesc(parentGroup.getPeerAdvertisement().getDesc());
}
}
if (peerGroupAdvertisement == null) {
// No existing gadv. OK then we're creating the group or we're
// the platform, it seems. Start a grp adv with the essentials
// that we know.
peerGroupAdvertisement = (PeerGroupAdvertisement) AdvertisementFactory.newAdvertisement(PeerGroupAdvertisement.getAdvertisementType());
peerGroupAdvertisement.setPeerGroupID((PeerGroupID) assignedID);
peerGroupAdvertisement.setModuleSpecID(implAdvertisement.getModuleSpecID());
} else {
published = true;
}
// Now that we have our PeerGroupAdvertisement, we can pull out
// the config to see if we have any PeerGroupConfigAdv params
if (null == parentGroup) {
if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
LOG.fine("Setting up group loader -> static loader");
}
loader = new RefJxtaLoader(new URL[0], staticLoader, COMP_EQ, this);
} else {
ClassLoader upLoader = parentGroup.getLoader();
StructuredDocument cfgDoc =
peerGroupAdvertisement.getServiceParam(
PeerGroup.peerGroupClassID);
PeerGroupConfigAdv pgca;
if (cfgDoc != null) {
pgca = (PeerGroupConfigAdv)
AdvertisementFactory.newAdvertisement((XMLElement)
peerGroupAdvertisement.getServiceParam(PeerGroup.peerGroupClassID));
if (pgca.isFlagSet(PeerGroupConfigFlag.SHUNT_PARENT_CLASSLOADER)) {
// We'll shunt to the same class loader that loaded this
// class, but not the JXTA form (to prevent Module
// definitions).
upLoader = getClass().getClassLoader();
}
}
if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
LOG.fine("Setting up group loader -> " + upLoader);
}
loader = new RefJxtaLoader(new URL[0], upLoader, COMP_EQ, this);
}
// If we still do not have a config adv, make one with the minimal info in it.
// All groups but the Platform and the netPG are in that case.
// In theory a plain ConfigParams would be enough for subgroups. But for now
// GenericPeerGroup always has a full Platformconfig and there is no other concrete
// ConfigParams subclass.
if (configAdvertisement == null) {
PlatformConfig conf = (PlatformConfig) AdvertisementFactory.newAdvertisement(PlatformConfig.getAdvertisementType());
conf.setPeerID(peerAdvertisement.getPeerID());
conf.setName(peerAdvertisement.getName());
conf.setDesc(peerAdvertisement.getDesc());
configAdvertisement = conf;
}
// Merge service params with those specified by the group (if any). The only
// policy, right now, is to give peer params the precedence over group params.
Hashtable grpParams = peerGroupAdvertisement.getServiceParams();
Enumeration keys = grpParams.keys();
while (keys.hasMoreElements()) {
ID key = (ID) keys.nextElement();
Element e = (Element) grpParams.get(key);
if (configAdvertisement.getServiceParam(key) == null) {
configAdvertisement.putServiceParam(key, e);
}
}
/*
* Now seems like the right time to attempt to register the group.
* The only trouble is that it could cause the group to
* be used before all the services are initialized, but on the
* other hand, we do not want to let a redundant group go through
* it's service initialization because that would cause irreparable
* damage to the legitimate instance. There should be a synchro on
* on the get<service>() and lookupService() routines.
*/
if (!globalRegistry.registerInstance((PeerGroupID) assignedID, this)) {
throw new PeerGroupException("Group already instantiated");
}
} catch (Throwable any) {
if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
LOG.log(Level.SEVERE, "Group init failed", any);
}
if (any instanceof Error) {
throw (Error) any;
} else if (any instanceof RuntimeException) {
throw (RuntimeException) any;
} else if (any instanceof PeerGroupException) {
throw (PeerGroupException) any;
}
throw new PeerGroupException("Group init failed", any);
}
ThreadGroup parentThreadGroup = (null != this.parentGroup)
? parentGroup.getHomeThreadGroup()
: Thread.currentThread().getThreadGroup();
threadGroup = new ThreadGroup(parentThreadGroup, "Group " + peerGroupAdvertisement.getPeerGroupID());
threadPool = TaskManager.getTaskManager().getExecutorService();
scheduledExecutor = TaskManager.getTaskManager().getScheduledExecutorService();
// This was the older form, before TaskManager...
// taskQueue = new ArrayBlockingQueue<Runnable>(COREPOOLSIZE * 2);
// threadPool = new ThreadPoolExecutor(COREPOOLSIZE, MAXPOOLSIZE,
// KEEPALIVETIME, TimeUnit.SECONDS,
// taskQueue,
// new PeerGroupThreadFactory("Executor", getHomeThreadGroup()),
// new CallerBlocksPolicy());
// scheduledExecutor = new ScheduledThreadPoolExecutor(1,
// new PeerGroupThreadFactory("Scheduled Executor", getHomeThreadGroup()));
// Try to allow core threads to idle out. (Requires a 1.6 method)
try {
Method allowCoreThreadTimeOut = threadPool.getClass().getMethod("allowCoreThreadTimeOut", boolean.class);
allowCoreThreadTimeOut.invoke(threadPool, Boolean.TRUE);
} catch (Throwable ohWell) {
// Our attempt failed.
if (Logging.SHOW_FINEST && LOG.isLoggable(Level.FINEST)) {
LOG.log(Level.FINEST, "Failed to enable 'allowCoreThreadTimeOut'", ohWell);
}
}
/*
* The rest of construction and initialization are left to the
* group subclass, between here and the begining for initLast.
* That should include instanciating and setting the endpoint, and
* finally supplying it with endpoint protocols.
* That also includes instanciating the appropriate services
* and registering them.
* For an example, see the StdPeerGroup class.
*/
}
/**
* Perform all initialization steps that need to be performed
* after any subclass initialization is performed.
* <p/>
* Classes that override this method should always call super.initLast
* <strong>after</strong> doing any of their own work.
* @throws PeerGroupException if a group initialization error occurs
*/
protected void initLast() throws PeerGroupException {
if (Logging.SHOW_CONFIG && LOG.isLoggable(Level.CONFIG)) {
StringBuilder configInfo = new StringBuilder("Configuring Group : " + getPeerGroupID());
if (implAdvertisement != null) {
configInfo.append("\n\tImplementation :");
configInfo.append("\n\t\tModule Spec ID: ").append(implAdvertisement.getModuleSpecID());
configInfo.append("\n\t\tImpl Description : ").append(implAdvertisement.getDescription());
configInfo.append("\n\t\tImpl URI : ").append(implAdvertisement.getUri());
configInfo.append("\n\t\tImpl Code : ").append(implAdvertisement.getCode());
}
configInfo.append("\n\tGroup Params :");
configInfo.append("\n\t\tModule Spec ID : ").append(implAdvertisement.getModuleSpecID());
configInfo.append("\n\t\tPeer Group ID : ").append(getPeerGroupID());
configInfo.append("\n\t\tGroup Name : ").append(getPeerGroupName());
configInfo.append("\n\t\tPeer ID in Group : ").append(getPeerID());
configInfo.append("\n\tConfiguration :");
if (null == parentGroup) {
configInfo.append("\n\t\tHome Group : (none)");
} else {
configInfo.append("\n\t\tHome Group : \"").append(parentGroup.getPeerGroupName()).append("\" / ").append(parentGroup.getPeerGroupID());
}
configInfo.append("\n\t\tServices :");
for (Map.Entry<ModuleClassID, Service> anEntry : services.entrySet()) {
ModuleClassID aMCID = anEntry.getKey();
ModuleImplAdvertisement anImplAdv = (ModuleImplAdvertisement) anEntry.getValue().getImplAdvertisement();
configInfo.append("\n\t\t\t").append(aMCID).append("\t").append(anImplAdv.getDescription());
}
LOG.config(configInfo.toString());
}
}
/**
* {@inheritDoc}
*/
public int startApp(String[] arg) {
return Module.START_OK;
}
/**
* {@inheritDoc}
* <p/>
* PeerGroupInterface's stopApp() does nothing. Only a real reference to the
* group object permits to stop it without going through ref counting.
*/
public void stopApp() {
stopping = true;
Collection<ModuleClassID> allServices = new ArrayList<ModuleClassID>(services.keySet());
// Stop and remove all remaining services.
for (ModuleClassID aService : allServices) {
try {
removeService(aService);
} catch (Exception failure) {
LOG.log(Level.WARNING, "Failure shutting down service : " + aService, failure);
}
}
if (!services.isEmpty()) {
LOG.warning(services.size() + " services could not be shut down during peer group stop.");
}
// remove everything (just in case);
services.clear();
globalRegistry.unRegisterInstance(peerGroupAdvertisement.getPeerGroupID(), this);
// Explicitly unreference our parent group in order to allow it
// to terminate if this group object was itself the last reference
// to it.
if (parentGroup != null) {
parentGroup.unref();
parentGroup = null;
}
// executors from TaskManager are now shutdown by the NetworkManager
// No longer initialized.
initComplete = false;
}
/**
* {@inheritDoc}
* <p/>
* May be called by a module which has a direct reference to the group
* object and wants to notify its abandoning it. Has no effect on the real
* group object.
*/
public boolean unref() {
return true;
}
/**
* Called every time an interface object that refers to this group
* goes away, either by being finalized or by its unref() method being
* invoked explicitly.
*/
protected void decRefCount() {
int new_count = masterRefCount.decrementAndGet();
if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
Throwable trace = new Throwable("Stack Trace");
StackTraceElement elements[] = trace.getStackTrace();
LOG.info("[" + getPeerGroupID() + "] GROUP REF COUNT DECCREMENTED TO: " + new_count + " by\n\t" + elements[2]);
}
if (new_count < 0) {
// Shutdown happens at zero. We must not go lower.
throw new IllegalStateException("Peer Group reference count has gone negative!");
}
if (new_count > 0) {
// If there are other references then we don't quit.
return;
}
if (!stopWhenUnreferenced) {
return;
}
if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
LOG.info("[" + getPeerGroupID() + "] STOPPING UNREFERENCED GROUP");
}
stopApp();
}
/*
* Implement the Service API so that we can make groups services when we
* decide to.
*/
/**
* {@inheritDoc}
*/
public PeerGroup getInterface() {
if (stopping) {
throw new IllegalStateException("Group has been shutdown. getInterface() is not available");
}
if (initComplete) {
// If init is complete the group can become sensitive to its ref
// count reaching zero. Before there could be transient references
// before there is a chance to give a permanent reference to the
// invoker of newGroup.
stopWhenUnreferenced = true;
}
int new_count = masterRefCount.incrementAndGet();
PeerGroupInterface pgInterface = new RefCountPeerGroupInterface(this);
if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) {
Throwable trace = new Throwable("Stack Trace");
StackTraceElement elements[] = trace.getStackTrace();
LOG.info("[" + pgInterface + "] GROUP REF COUNT INCREMENTED TO: " + new_count + " by\n\t" + elements[2]);
}
return pgInterface;
}
/**
* {@inheritDoc}
*/
public PeerGroup getWeakInterface() {
return new PeerGroupInterface(this);
}
/**
* {@inheritDoc}
*/
public ModuleImplAdvertisement getImplAdvertisement() {
return implAdvertisement.clone();
}
/**
* {@inheritDoc}
*/
public void publishGroup(String name, String description) throws IOException {
if (published) {
return;
}
peerGroupAdvertisement.setName(name);
peerGroupAdvertisement.setDescription(description);
if (parentGroup == null) {
return;
}
DiscoveryService parentDiscovery = parentGroup.getDiscoveryService();
if (null == parentDiscovery) {
return;
}
parentDiscovery.publish(peerGroupAdvertisement, DEFAULT_LIFETIME, DEFAULT_EXPIRATION);
published = true;
}
/**
* {@inheritDoc}
*/
public PeerGroup newGroup(Advertisement pgAdv) throws PeerGroupException {
PeerGroupAdvertisement adv = (PeerGroupAdvertisement) pgAdv;
PeerGroupID gid = adv.getPeerGroupID();
if ((gid == null) || ID.nullID.equals(gid)) {
throw new IllegalArgumentException("Advertisement did not contain a peer group ID");
}
PeerGroup theNewGroup = globalRegistry.lookupInstance(gid);
if (theNewGroup != null) {
return theNewGroup;
}
// We do not know if the grp adv had been previously published or not... Since it may contain information essential to
// the configuration of services, we need to make sure it is published localy, rather than letting the group publish
// itself after the fact.
// FIXME 20040713 jice The downside is that we're publishing the adv even before making sure that this group
// can really be instantiated. We're basically using the cm as a means to pass parameters to the module because it is a
// group. We have the same parameter issue with the config adv. Eventually we need to find a clean way of passing
// parameters specific to a certain types of module.
try {
discovery.publish(adv, DEFAULT_LIFETIME, DEFAULT_EXPIRATION);
} catch (Exception any) {
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Could not publish the group advertisement: ", any);
}
}
theNewGroup = (PeerGroup) loadModule(adv.getPeerGroupID(), adv.getModuleSpecID(), Here, false);
if (theNewGroup == null) {
throw new PeerGroupException("Could not find group implementation with " + adv.getModuleSpecID());
}
return theNewGroup.getInterface();
}
/**
* {@inheritDoc}
*/
public PeerGroup newGroup(PeerGroupID gid, Advertisement impl, String name, String description) throws PeerGroupException {
PeerGroup theNewGroup = null;
if (null != gid) {
theNewGroup = globalRegistry.lookupInstance(gid);
}
if (theNewGroup != null) {
return theNewGroup;
}
try {
theNewGroup = (PeerGroup) loadModule(gid, (ModuleImplAdvertisement) impl, false);
} catch (Throwable any) {
if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) {
LOG.log(Level.SEVERE, "Could not load group implementation", any);
}
throw new PeerGroupException("Could not load group implementation", any);
}
try {
// The group adv definitely needs to be published.
theNewGroup.publishGroup(name, description);
} catch (Exception any) {
if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Could not publish group or implementation:", any);
}
}
return theNewGroup.getInterface();
}
/**
* {@inheritDoc}
*/
public PeerGroup newGroup(PeerGroupID gid) throws PeerGroupException {
if ((gid == null) || ID.nullID.equals(gid)) {
throw new IllegalArgumentException("Invalid peer group ID");
}
PeerGroup result = globalRegistry.lookupInstance(gid);
if (result != null) {
return result;
}
PeerGroupAdvertisement adv;
try {
adv = (PeerGroupAdvertisement) discoverOne(DiscoveryService.GROUP, "GID", gid.toString(), 120, PeerGroupAdvertisement.class);
} catch (Throwable any) {
throw new PeerGroupException("Failed finding group advertisement for " + gid, any);
}
if (adv == null) {
throw new PeerGroupException("Could not find group advertisement for group " + gid);
}
return newGroup(adv);
}
/**
* {@inheritDoc}
*/
public JxtaLoader getLoader() {
return loader;
}
/**
* {@inheritDoc}
*/
public String getPeerName() {
// before init we must fail.
if (null == peerAdvertisement) {
throw new IllegalStateException("PeerGroup not sufficiently initialized");
}
return peerAdvertisement.getName();
}
/**
* {@inheritDoc}
*/
public String getPeerGroupName() {
// before init we must fail.
if (null == peerGroupAdvertisement) {
throw new IllegalStateException("PeerGroup not sufficiently initialized");
}
return peerGroupAdvertisement.getName();
}
/**
* {@inheritDoc}
*/
public PeerGroupID getPeerGroupID() {
// before init we must fail.
if (null == peerGroupAdvertisement) {
throw new IllegalStateException("PeerGroup not sufficiently initialized");
}
return peerGroupAdvertisement.getPeerGroupID();
}
/**
* {@inheritDoc}
*/
public PeerID getPeerID() {
// before init we must fail.
if (null == peerAdvertisement) {
throw new IllegalStateException("PeerGroup not sufficiently initialized");
}
return peerAdvertisement.getPeerID();
}
/**
* {@inheritDoc}
*/
public PeerAdvertisement getPeerAdvertisement() {
return peerAdvertisement;
}
/**
* {@inheritDoc}
*/
public PeerGroupAdvertisement getPeerGroupAdvertisement() {
return peerGroupAdvertisement;
}
/**
* {@inheritDoc}
*/
public boolean isRendezvous() {
if (rendezvous == null) {
if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
LOG.fine("Rendezvous service null");
}
}
return (rendezvous != null) && rendezvous.isRendezVous();
}
/*
* shortcuts to the well-known services, in order to avoid calls to lookup.
*/
/**
* {@inheritDoc}
*/
public EndpointService getEndpointService() {
if (endpoint == null) {
return null;
}
return endpoint;
}
/**
* {@inheritDoc}
*/
public ResolverService getResolverService() {
if (resolver == null) {
return null;
}
return (ResolverService) resolver.getInterface();
}
/**
* {@inheritDoc}
*/
public DiscoveryService getDiscoveryService() {
if (discovery == null) {
return null;
}
return (DiscoveryService) discovery.getInterface();
}
/**
* {@inheritDoc}
*/
public PeerInfoService getPeerInfoService() {
if (peerinfo == null) {
return null;
}
return (PeerInfoService) peerinfo.getInterface();
}
/**
* {@inheritDoc}
*/
public MembershipService getMembershipService() {
if (membership == null) {
return null;
}
return (MembershipService) membership.getInterface();
}
/**
* {@inheritDoc}
*/
public PipeService getPipeService() {
if (pipe == null) {
return null;
}
return (PipeService) pipe.getInterface();
}
/**
* {@inheritDoc}
*/
public RendezVousService getRendezVousService() {
if (rendezvous == null) {
return null;
}
return (RendezVousService) rendezvous.getInterface();
}
/**
* {@inheritDoc}
*/
public AccessService getAccessService() {
if (access == null) {
return null;
}
return (AccessService) access.getInterface();
}
/**
* {@inheritDoc}
*/
public ContentService getContentService() {
if (content == null) {
return null;
}
return (ContentService) content.getInterface();
}
/**
* Returns the executor pool
*
* @return the executor pool
*/
public Executor getExecutor() {
return threadPool;
}
/**
* Returns the scheduled executor. The
*
* @return the scheduled executor
*/
public ScheduledExecutorService getScheduledExecutor() {
// FIXME 20070815 bondolo We should return a proxy object to disable shutdown()
return scheduledExecutor;
}
/**
* Our rejected execution handler which has the effect of pausing the
* caller until the task can be queued.
*/
private static class CallerBlocksPolicy implements RejectedExecutionHandler {
private CallerBlocksPolicy() {
}
/**
* {@inheritDoc}
*/
public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) {
BlockingQueue<Runnable> queue = executor.getQueue();
while (!executor.isShutdown()) {
executor.purge();
try {
boolean pushed = queue.offer(runnable, 500, TimeUnit.MILLISECONDS);
if (pushed) {
break;
}
} catch (InterruptedException woken) {
throw new RejectedExecutionException("Interrupted while attempting to enqueue", woken);
}
}
}
}
/**
* Our thread factory that adds the threads to our thread group and names
* the thread to something recognizable.
*/
static class PeerGroupThreadFactory implements ThreadFactory {
final AtomicInteger threadNumber = new AtomicInteger(1);
final String name;
final ThreadGroup threadgroup;
PeerGroupThreadFactory(String name, ThreadGroup threadgroup) {
this.name = name;
this.threadgroup = threadgroup;
}
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(threadgroup, runnable, name + " - " + threadNumber.getAndIncrement(), 0);
if (thread.isDaemon()) {
thread.setDaemon(false);
}
if (thread.getPriority() != Thread.NORM_PRIORITY) {
thread.setPriority(Thread.NORM_PRIORITY);
}
return thread;
}
}
}