/*
* 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.util;
import net.jxta.discovery.DiscoveryService;
import net.jxta.document.Advertisement;
import net.jxta.document.AdvertisementFactory;
import net.jxta.document.Element;
import net.jxta.document.MimeMediaType;
import net.jxta.document.StructuredDocument;
import net.jxta.document.StructuredDocumentFactory;
import net.jxta.document.StructuredDocumentUtils;
import net.jxta.document.XMLElement;
import net.jxta.id.IDFactory;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.platform.Module;
import net.jxta.platform.ModuleClassID;
import net.jxta.platform.ModuleSpecID;
import net.jxta.protocol.ModuleClassAdvertisement;
import net.jxta.protocol.ModuleImplAdvertisement;
import net.jxta.protocol.ModuleSpecAdvertisement;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
/**
* Module Manager.
*
* This class allows to manage modules to be loaded, started and stopped
* within a PeerGroup. Modules that are loaded using the ModuleManager do not need
* to be listed within the PeerGroup advertisement, nor do they have to have
* published their ModuleSpec and ModuleImpl advertisements: the ModuleManager
* takes care of this task. However, other peers which may want to load the Module
* will also have to use its own loader (or the ModuleManager itself, of course):
* the ModuleManager only manages Modules on the local peer.
*
* The Module Manager allows, as an option, to use an application provided class loader.
* The default class loader is the PeerGroup class loader.
*
* The following example shows how to use the ModuleManager:
*
*
* <pre>
* // Get the peergroup
* PeerGroup group = getMyPeerGroup();
* // Get the ModuleManager
* ModuleManager moduleManager = ModuleManager.getModuleManager (group);
*
* // Is the Module already loaded ?
* Module module = moduleManager.lookupModule ("SampleModule");
* if (module == null) {
* // SampleModue is not loaded yet. Load it now.
* module = moduleManager.loadModule ( "SampleModule", "net.jxta.app.SampleModule.SampleModule");
* }
*
* // Start SampleModule
* moduleManager.startModule ("SampleModule", moduleArgs);
* </pre>
*
* @since 2.6 Peergroups offer loadmodule() methods, making this class obsolete. It has been
* deprecated and will be removed in the next version.
*
*/
@Deprecated
public class ModuleManager {
private static Hashtable<PeerGroupID, ModuleManager> managers = null;
private static long LOCAL_ADV_TTL = 5 * 60 * 1000;
// 5 minutes is more than sufficient
private static long REMOTE_ADV_TTL = 0;
// We do not allow remote access of the advertisements.
private final Hashtable<String, ModuleDesc> modules = new Hashtable<String, ModuleDesc>();
private final PeerGroup group;
/**
* Private constructor that allows to create an instance of the Module Manager for each
* PeerGroup.
*
* @param group the PeerGroup for which the ModuleManager needs to allocated a new instance
* of itself.
*/
private ModuleManager(PeerGroup group) {
this.group = group;
}
/**
* startModule
*
* This method is invoked by the application to start a previously loaded
* module.
*
* @param moduleName is the symbolic name of the module.
* @param args is an array of String containing optional arguments for the module. This
* array is passed directly to the startApp (String[] ) method of the Module.
*/
public void startModule(String moduleName, String[] args) {
ModuleDesc moduleDesc = modules.get(moduleName);
if (moduleDesc == null) {
// Cannot find such a module
return;
}
moduleDesc.startApp(args);
}
/**
* stopModule
*
* This method is invoked by the application to stop a running module.
*
* @param moduleName is the symbolic name of the module.
*/
public void stopModule(String moduleName) {
ModuleDesc moduleDesc = modules.get(moduleName);
if (moduleDesc == null) {
// Cannot find such a module
return;
}
moduleDesc.stopApp();
}
/**
* getModuleManager
*
* This method is used in order to get the instance of the ModuleManager for a given
* PeerGroup. getModuleManager will create a new instance automatically if there is no
* instance for the given PeerGroup.
*
* @param group the PeerGroup for which the ModuleManager is asked.
* @return the ModuleManager instance for the given PeerGroup.
*/
public static ModuleManager getModuleManager(PeerGroup group) {
if (managers == null) {
// This is the first time the ModuleManager is invoked. Create
// the Hashtable
managers = new Hashtable<PeerGroupID, ModuleManager>();
}
ModuleManager manager;
manager = managers.get(group.getPeerGroupID());
if (manager == null) {
manager = new ModuleManager(group);
managers.put(group.getPeerGroupID(), manager);
}
return manager;
}
/**
* Description of the Method
*
* @param moduleName Description of the Parameter
* @param module Description of the Parameter
* @return Description of the Return Value
*/
private synchronized boolean registerModule(String moduleName, Module module) {
ModuleDesc moduleDesc = modules.get(moduleName);
if (moduleDesc != null) {
// There is already a module registered to that name.
return false;
}
moduleDesc = new ModuleDesc(module);
modules.put(moduleName, moduleDesc);
return true;
}
/**
* lookupModule
*
* Get the Module from its symbolic name.
*
* @param moduleName symbolic name of the Module
* @return the Module for the given name. null is returned if there is no module
* of the given name.
*/
public synchronized Module lookupModule(String moduleName) {
ModuleDesc moduleDesc = modules.get(moduleName);
if (moduleDesc == null) {
// There is not any module registered to that name.
return null;
}
return moduleDesc.module;
}
/**
* loadModule
*
* Loads a Module. A class loaded is provided by the application.
* If the module has already been loaded, the existing Module is returned.
*
* @param moduleName symbolic name of the Module
* @param loader application provided class loader
* @return the Module for the given name. null is returned if the module could not be
* loaded
*/
public synchronized Module loadModule(String moduleName, ModuleManagerLoader loader) {
// First check if the module is already loaded and registered
Module module = lookupModule(moduleName);
if (module != null) {
return module;
}
module = loader.loadModule(moduleName);
if (module != null) {
// Since this module is not started by the standard
// JXTA PeerGroup, we need to initialize ourself.
// Note that the ID and the ModuleImplAdvertisement is
// then set to null, which is fine, since that has been
// the decision from the application to actually not use
// the standard PeerGroup Module loading scheme.
try {
module.init(group, null, null);
} catch (Exception e) {
// Init failed, the module cannot be initialized
return null;
}
registerModule(moduleName, module);
}
return module;
}
/**
* loadModule
*
* Loads a Module. The default PeerGroup class loader will be used. The class
* must be within the CLASSPATH of the platform.
* If the module has already been loaded, the existing Module is returned.
*
* @param moduleName symbolic name of the Module
* @param moduleCode the name of the class to be loaded.
* @return the Module for the given name. null is returned if the module could not be
* loaded
*/
public synchronized Module loadModule(String moduleName, String moduleCode) {
// First check if the module is already loaded and registered
Module module = lookupModule(moduleName);
if (module != null) {
return module;
}
if (!createModuleAdvs(moduleName, null, moduleCode, null, LOCAL_ADV_TTL, REMOTE_ADV_TTL)) {
// Creation of the module advertisement has failed.
return null;
}
// Get the module. This should always work since the advertisements have
// just been created.
module = loadModule(moduleName);
if (module == null) {
// There is really nothing more we can do here.
return null;
}
return module;
}
/**
* Description of the Method
*
* @param moduleName Description of the Parameter
* @return Description of the Return Value
*/
private synchronized Module loadModule(String moduleName) {
// First check if the module is already loaded and registered
Module module = lookupModule(moduleName);
if (module != null) {
return module;
}
try {
// Recover the ModuleClassAdvertisement
Enumeration each = group.getDiscoveryService().getLocalAdvertisements(DiscoveryService.ADV, "Name", moduleName);
if (!each.hasMoreElements()) {
// No advertisement.
return null;
}
ModuleClassAdvertisement mcAdv = null;
while (each.hasMoreElements()) {
try {
mcAdv = (ModuleClassAdvertisement) each.nextElement();
break;
} catch (Exception ez1) {// ignored
}
}
// Revover the Module Specification Advertisement
each = group.getDiscoveryService().getLocalAdvertisements(DiscoveryService.ADV, "Name", moduleName);
if (!each.hasMoreElements()) {
return null;
}
ModuleSpecAdvertisement mSpecAdv = null;
while (each.hasMoreElements()) {
try {
mSpecAdv = (ModuleSpecAdvertisement) each.nextElement();
break;
} catch (Exception ez1) {// ignored
}
}
module = group.loadModule(mcAdv.getModuleClassID(), mSpecAdv.getModuleSpecID(), PeerGroup.Here);
if (module != null) {
registerModule(moduleName, module);
}
return module;
} catch (Exception ez2) {
return null;
}
}
/**
* Description of the Method
*
* @param moduleName Description of the Parameter
* @param moduleSpecURI Description of the Parameter
* @param moduleCode Description of the Parameter
* @param moduleCodeURI Description of the Parameter
* @param localTTL Description of the Parameter
* @param remoteTTL Description of the Parameter
* @return Description of the Return Value
*/
private boolean createModuleAdvs(String moduleName, String moduleSpecURI, String moduleCode, String moduleCodeURI, long localTTL, long remoteTTL) {
DiscoveryService disco = group.getDiscoveryService();
try {
// First create the Module class advertisement associated with the module
// We build the module class advertisement using the advertisement
// Factory class by passing it the type of the advertisement we
// want to construct. The Module class advertisement is to be used
// to simply advertise the existence of the module. This is a
// a very small advertisement that only advertise the existence
// of module. In order to access the module, a peer will
// have to discover the associated module spec advertisement.
ModuleClassAdvertisement mcadv = (ModuleClassAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleClassAdvertisement.getAdvertisementType());
mcadv.setName(moduleName);
mcadv.setDescription("Created by ModuleManager: " + moduleName);
ModuleClassID mcID = IDFactory.newModuleClassID();
mcadv.setModuleClassID(mcID);
// Ok the Module Class advertisement was created, just publish
// it in my local cache and to my peergroup. This
// is the NetPeerGroup
disco.publish(mcadv, localTTL, remoteTTL);
// Create the Module Spec advertisement associated with the module
// We build the module Spec Advertisement using the advertisement
// Factory class by passing in the type of the advertisement we
// want to construct. The Module Spec advertisement will contain
// all the information necessary for a client to contact the module
// for instance it will contain a pipe advertisement to
// be used to contact the module
ModuleSpecAdvertisement mdadv = (ModuleSpecAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleSpecAdvertisement.getAdvertisementType());
// ModuleManager does not allow to set up any customized
// information for the Module.
mdadv.setName(moduleName);
mdadv.setCreator("jxta.org");
mdadv.setModuleSpecID(IDFactory.newModuleSpecID(mcID));
if (moduleSpecURI != null) {
mdadv.setSpecURI(moduleSpecURI);
}
// Ok the Module advertisement was created, just publish
// it in my local cache and into the NetPeerGroup.
disco.publish(mdadv, localTTL, remoteTTL);
// Create the Module Implementation advertisement
ModuleImplAdvertisement miadv = (ModuleImplAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleImplAdvertisement.getAdvertisementType());
miadv.setModuleSpecID(mdadv.getModuleSpecID());
if (moduleCode != null) {
miadv.setCode(moduleCode);
}
if (moduleCodeURI != null) {
miadv.setUri(moduleCodeURI);
}
miadv.setDescription("Created by ModuleManager: " + moduleName);
// Steal the compat, provider, and uri from the
// group's own impl adv. We DO want them identical in
// this case.
ModuleImplAdvertisement pgImpl = (ModuleImplAdvertisement) group.getImplAdvertisement();
miadv.setCompat(pgImpl.getCompat());
miadv.setUri(pgImpl.getUri());
// Ok the Module Class advertisement was created, just publish
// it in my local cache and to my peergroup. This
// is the NetPeerGroup
disco.publish(miadv, localTTL, remoteTTL);
} catch (Exception ex) {
return false;
}
return true;
}
// FIXME this method should be refactored
/**
* Creates a Module Class, Spec, and Impl advertisements, and adds the service
* Advertisement as part of the Module Impl Advertisement, and publishes the advertisements
* in local cache
*
* @param group group
* @param moduleName module name
* @param description module description
* @param moduleSpecURI module spec uri
* @param moduleCode module code
* @param moduleCodeURI module code uri
* @param mcID module class id
* @param msID module spec id
* @param code module code
* @param serviceAdv service advertisement
* @param localTTL local cache lifetime in ms
* @param remoteTTL remote cache lifetime in ms
* @exception IOException if an io error occurs
*/
public void createServiceAdvertisement(PeerGroup group, String moduleName, String description, String moduleSpecURI, String moduleCode, String moduleCodeURI, ModuleClassID mcID, ModuleSpecID msID, String code, Advertisement serviceAdv, long localTTL, long remoteTTL) throws IOException {
DiscoveryService discovery = group.getDiscoveryService();
// Create module class advertisement
ModuleClassAdvertisement mcadv = (ModuleClassAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleClassAdvertisement.getAdvertisementType());
mcadv.setModuleClassID(mcID);
mcadv.setName(moduleName);
mcadv.setDescription(description);
// Module spec advertisement
ModuleSpecAdvertisement mdspec = (ModuleSpecAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleSpecAdvertisement.getAdvertisementType());
mdspec.setModuleSpecID(msID);
mdspec.setName(moduleName);
mdspec.setSpecURI(moduleSpecURI);
// Module implementation advertisement
ModuleImplAdvertisement miadv = (ModuleImplAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleImplAdvertisement.getAdvertisementType());
miadv.setModuleSpecID(mdspec.getModuleSpecID());
miadv.setDescription(description);
if (moduleCodeURI != null) {
miadv.setUri(moduleCodeURI);
}
if (moduleCode != null) {
miadv.setCode(moduleCode);
}
// Steal the compat, provider, and uri from the
// group's own impl adv. We DO want them identical in
// this case.
ModuleImplAdvertisement pgImpl = (ModuleImplAdvertisement) group.getImplAdvertisement();
miadv.setCompat(pgImpl.getCompat());
miadv.setCode(code);
Element pEl = (Element) serviceAdv.getDocument(MimeMediaType.XMLUTF8);
StructuredDocument svcParm = StructuredDocumentFactory.newStructuredDocument(MimeMediaType.XMLUTF8, "Parm");
StructuredDocumentUtils.copyElements(svcParm, svcParm, pEl);
miadv.setParam(svcParm);
// publish the advertisements
discovery.publish(mcadv, localTTL, remoteTTL);
discovery.publish(mdspec, localTTL, remoteTTL);
discovery.publish(miadv, localTTL, remoteTTL);
}
/**
* Retreives a Service Advertisement from a module impl advertisement
* @param group peer group
* @param mia ModuleImplAdvertisement
* @param advertismentType service advertisment string Type
* @return The service Advertisement
* @exception IOException if an io error occurs
*/
public Advertisement getServiceAdvertisement(PeerGroup group, ModuleImplAdvertisement mia, String advertismentType) throws IOException {
Element param = mia.getParam();
Element pel = null;
if (param != null) {
Enumeration list = param.getChildren(advertismentType);
if (list.hasMoreElements()) {
pel = (Element) list.nextElement();
}
}
Advertisement adv = AdvertisementFactory.newAdvertisement((XMLElement) pel);
return adv;
}
/**
* Description of the Class
*/
private class ModuleDesc {
/**
* Description of the Field
*/
protected Module module = null;
private boolean started = false;
private boolean stopped = true;
/**
*Constructor for the ModuleDesc object
*
* @param module Description of the Parameter
*/
public ModuleDesc(Module module) {
this.module = module;
}
/**
* Description of the Method
*
* @param args Description of the Parameter
*/
public void startApp(String[] args) {
if (module == null) {
return;
}
if (started) {
// Already started - nothing to do
return;
}
module.startApp(args);
started = true;
stopped = false;
}
/**
* Description of the Method
*/
public void stopApp() {
if (module == null) {
return;
}
if (stopped) {
// Already stopped - nothing to do
return;
}
module.stopApp();
stopped = true;
started = false;
}
}
/**
* ModuleManagerLoader interface.
* This interface is used by the application in order to provide its own
* class loader instead of using the standard PeerGroup loader.
*/
public interface ModuleManagerLoader {
/**
* This method is invoked by the ModuleManager when it is time to load
* the class associated to the module. The name of the module is provided,
* which allows the application provided loader to be able to load a variety
* of modules, if that is necessary for the application. Note that the ModuleManager
* assumes that the module which is loaded by the provided loader is not started:
* loading and starting a module are two different operations for the ModuleManager.
*
* @param moduleName is the symbolic name of the Module.
* @return Module the object that has been loaded.
*/
public Module loadModule(String moduleName);
}
}