2 * Copyright (c) 2001-2007 Sun Microsystems, Inc. All rights reserved.
4 * The Sun Project JXTA(TM) Software License
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * 3. The end-user documentation included with the redistribution, if any, must
17 * include the following acknowledgment: "This product includes software
18 * developed by Sun Microsystems, Inc. for JXTA(TM) technology."
19 * Alternately, this acknowledgment may appear in the software itself, if
20 * and wherever such third-party acknowledgments normally appear.
22 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
23 * not be used to endorse or promote products derived from this software
24 * without prior written permission. For written permission, please contact
25 * Project JXTA at http://www.jxta.org.
27 * 5. Products derived from this software may not be called "JXTA", nor may
28 * "JXTA" appear in their name, without prior written permission of Sun.
30 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
32 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN
33 * MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
36 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
39 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 * JXTA is a registered trademark of Sun Microsystems, Inc. in the United
42 * States and other countries.
44 * Please see the license information page at :
45 * <http://www.jxta.org/project/www/license.html> for instructions on use of
46 * the license in source files.
48 * ====================================================================
50 * This software consists of voluntary contributions made by many individuals
51 * on behalf of Project JXTA. For more information on Project JXTA, please see
52 * http://www.jxta.org.
54 * This license is based on the BSD license adopted by the Apache Foundation.
57 package net.jxta.impl.util;
60 import java.util.Enumeration;
61 import java.util.Hashtable;
62 import java.io.IOException;
64 import net.jxta.platform.Module;
65 import net.jxta.platform.ModuleClassID;
66 import net.jxta.platform.ModuleSpecID;
67 import net.jxta.discovery.DiscoveryService;
68 import net.jxta.document.Advertisement;
69 import net.jxta.document.AdvertisementFactory;
70 import net.jxta.document.Element;
71 import net.jxta.document.MimeMediaType;
72 import net.jxta.document.StructuredDocument;
73 import net.jxta.document.StructuredDocumentFactory;
74 import net.jxta.document.StructuredDocumentUtils;
75 import net.jxta.document.TextElement;
76 import net.jxta.protocol.ModuleClassAdvertisement;
77 import net.jxta.protocol.ModuleImplAdvertisement;
78 import net.jxta.protocol.ModuleSpecAdvertisement;
79 import net.jxta.peergroup.PeerGroup;
80 import net.jxta.peergroup.PeerGroupID;
81 import net.jxta.id.IDFactory;
87 * This class allows to manage modules to be loaded, started and stopped
88 * within a PeerGroup. Modules that are loaded using the ModuleManager do not need
89 * to be listed within the PeerGroup advertisement, nor do they have to have
90 * published their ModuleSpec and ModuleImpl advertisements: the ModuleManager
91 * takes care of this task. However, other peers which may want to load the Module
92 * will also have to use its own loader (or the ModuleManager itself, of course):
93 * the ModuleManager only manages Modules on the local peer.
95 * The Module Manager allows, as an option, to use an application provided class loader.
96 * The default class loader is the PeerGroup class loader.
98 * The following example shows how to use the ModuleManager:
102 * // Get the peergroup
103 * PeerGroup group = getMyPeerGroup();
104 * // Get the ModuleManager
105 * ModuleManager moduleManager = ModuleManager.getModuleManager (group);
107 * // Is the Module already loaded ?
108 * Module module = moduleManager.lookupModule ("SampleModule");
109 * if (module == null) {
110 * // SampleModue is not loaded yet. Load it now.
111 * module = moduleManager.loadModule ( "SampleModule", "net.jxta.app.SampleModule.SampleModule");
114 * // Start SampleModule
115 * moduleManager.startModule ("SampleModule", moduleArgs);
119 public class ModuleManager {
121 private static Hashtable<PeerGroupID, ModuleManager> managers = null;
122 private static long LOCAL_ADV_TTL = 5 * 60 * 1000;
123 // 5 minutes is more than sufficient
124 private static long REMOTE_ADV_TTL = 0;
125 // We do not allow remote access of the advertisements.
127 private final Hashtable<String, ModuleDesc> modules = new Hashtable<String, ModuleDesc>();
128 private final PeerGroup group;
131 * Private constructor that allows to create an instance of the Module Manager for each
134 * @param group the PeerGroup for which the ModuleManager needs to allocated a new instance
137 private ModuleManager(PeerGroup group) {
144 * This method is invoked by the application to start a previously loaded
147 * @param moduleName is the symbolic name of the module.
148 * @param args is an array of String containing optional arguments for the module. This
149 * array is passed directly to the startApp (String[] ) method of the Module.
151 public void startModule(String moduleName, String[] args) {
153 ModuleDesc moduleDesc = modules.get(moduleName);
155 if (moduleDesc == null) {
156 // Cannot find such a module
159 moduleDesc.startApp(args);
165 * This method is invoked by the application to stop a running module.
167 * @param moduleName is the symbolic name of the module.
169 public void stopModule(String moduleName) {
171 ModuleDesc moduleDesc = modules.get(moduleName);
173 if (moduleDesc == null) {
174 // Cannot find such a module
177 moduleDesc.stopApp();
183 * This method is used in order to get the instance of the ModuleManager for a given
184 * PeerGroup. getModuleManager will create a new instance automatically if there is no
185 * instance for the given PeerGroup.
187 * @param group the PeerGroup for which the ModuleManager is asked.
188 * @return the ModuleManager instance for the given PeerGroup.
190 public static ModuleManager getModuleManager(PeerGroup group) {
192 if (managers == null) {
193 // This is the first time the ModuleManager is invoked. Create
195 managers = new Hashtable<PeerGroupID, ModuleManager>();
197 ModuleManager manager;
199 manager = managers.get(group.getPeerGroupID());
201 if (manager == null) {
202 manager = new ModuleManager(group);
203 managers.put(group.getPeerGroupID(), manager);
209 * Description of the Method
211 * @param moduleName Description of the Parameter
212 * @param module Description of the Parameter
213 * @return Description of the Return Value
215 private synchronized boolean registerModule(String moduleName, Module module) {
217 ModuleDesc moduleDesc = modules.get(moduleName);
219 if (moduleDesc != null) {
220 // There is already a module registered to that name.
223 moduleDesc = new ModuleDesc(module);
224 modules.put(moduleName, moduleDesc);
231 * Get the Module from its symbolic name.
233 * @param moduleName symbolic name of the Module
234 * @return the Module for the given name. null is returned if there is no module
237 public synchronized Module lookupModule(String moduleName) {
239 ModuleDesc moduleDesc = modules.get(moduleName);
241 if (moduleDesc == null) {
242 // There is not any module registered to that name.
245 return moduleDesc.module;
251 * Loads a Module. A class loaded is provided by the application.
252 * If the module has already been loaded, the existing Module is returned.
254 * @param moduleName symbolic name of the Module
255 * @param loader application provided class loader
256 * @return the Module for the given name. null is returned if the module could not be
260 public synchronized Module loadModule(String moduleName, ModuleManagerLoader loader) {
262 // First check if the module is already loaded and registered
263 Module module = lookupModule(moduleName);
265 if (module != null) {
268 module = loader.loadModule(moduleName);
269 if (module != null) {
270 // Since this module is not started by the standard
271 // JXTA PeerGroup, we need to initialize ourself.
272 // Note that the ID and the ModuleImplAdvertisement is
273 // then set to null, which is fine, since that has been
274 // the decision from the application to actually not use
275 // the standard PeerGroup Module loading scheme.
277 module.init(group, null, null);
278 } catch (Exception e) {
279 // Init failed, the module cannot be initialized
282 registerModule(moduleName, module);
290 * Loads a Module. The default PeerGroup class loader will be used. The class
291 * must be within the CLASSPATH of the platform.
292 * If the module has already been loaded, the existing Module is returned.
294 * @param moduleName symbolic name of the Module
295 * @param moduleCode the name of the class to be loaded.
296 * @return the Module for the given name. null is returned if the module could not be
299 public synchronized Module loadModule(String moduleName, String moduleCode) {
301 // First check if the module is already loaded and registered
302 Module module = lookupModule(moduleName);
304 if (module != null) {
308 if (!createModuleAdvs(moduleName, null, moduleCode, null, LOCAL_ADV_TTL, REMOTE_ADV_TTL)) {
310 // Creation of the module advertisement has failed.
313 // Get the module. This should always work since the advertisements have
314 // just been created.
315 module = loadModule(moduleName);
316 if (module == null) {
317 // There is really nothing more we can do here.
324 * Description of the Method
326 * @param moduleName Description of the Parameter
327 * @return Description of the Return Value
329 private synchronized Module loadModule(String moduleName) {
331 // First check if the module is already loaded and registered
332 Module module = lookupModule(moduleName);
334 if (module != null) {
339 // Recover the ModuleClassAdvertisement
340 Enumeration each = group.getDiscoveryService().getLocalAdvertisements(DiscoveryService.ADV, "Name", moduleName);
342 if (!each.hasMoreElements()) {
347 ModuleClassAdvertisement mcAdv = null;
349 while (each.hasMoreElements()) {
351 mcAdv = (ModuleClassAdvertisement) each.nextElement();
353 } catch (Exception ez1) {// ignored
357 // Revover the Module Specification Advertisement
358 each = group.getDiscoveryService().getLocalAdvertisements(DiscoveryService.ADV, "Name", moduleName);
359 if (!each.hasMoreElements()) {
363 ModuleSpecAdvertisement mSpecAdv = null;
365 while (each.hasMoreElements()) {
367 mSpecAdv = (ModuleSpecAdvertisement) each.nextElement();
369 } catch (Exception ez1) {// ignored
373 module = group.loadModule(mcAdv.getModuleClassID(), mSpecAdv.getModuleSpecID(), PeerGroup.Here);
375 if (module != null) {
376 registerModule(moduleName, module);
379 } catch (Exception ez2) {
385 * Description of the Method
387 * @param moduleName Description of the Parameter
388 * @param moduleSpecURI Description of the Parameter
389 * @param moduleCode Description of the Parameter
390 * @param moduleCodeURI Description of the Parameter
391 * @param localTTL Description of the Parameter
392 * @param remoteTTL Description of the Parameter
393 * @return Description of the Return Value
395 private boolean createModuleAdvs(String moduleName, String moduleSpecURI, String moduleCode, String moduleCodeURI, long localTTL, long remoteTTL) {
397 DiscoveryService disco = group.getDiscoveryService();
400 // First create the Module class advertisement associated with the module
401 // We build the module class advertisement using the advertisement
402 // Factory class by passing it the type of the advertisement we
403 // want to construct. The Module class advertisement is to be used
404 // to simply advertise the existence of the module. This is a
405 // a very small advertisement that only advertise the existence
406 // of module. In order to access the module, a peer will
407 // have to discover the associated module spec advertisement.
409 ModuleClassAdvertisement mcadv = (ModuleClassAdvertisement)
410 AdvertisementFactory.newAdvertisement(ModuleClassAdvertisement.getAdvertisementType());
412 mcadv.setName(moduleName);
413 mcadv.setDescription("Created by ModuleManager: " + moduleName);
415 ModuleClassID mcID = IDFactory.newModuleClassID();
417 mcadv.setModuleClassID(mcID);
419 // Ok the Module Class advertisement was created, just publish
420 // it in my local cache and to my peergroup. This
421 // is the NetPeerGroup
422 disco.publish(mcadv, localTTL, remoteTTL);
424 // Create the Module Spec advertisement associated with the module
425 // We build the module Spec Advertisement using the advertisement
426 // Factory class by passing in the type of the advertisement we
427 // want to construct. The Module Spec advertisement will contain
428 // all the information necessary for a client to contact the module
429 // for instance it will contain a pipe advertisement to
430 // be used to contact the module
432 ModuleSpecAdvertisement mdadv = (ModuleSpecAdvertisement)
433 AdvertisementFactory.newAdvertisement(ModuleSpecAdvertisement.getAdvertisementType());
435 // ModuleManager does not allow to set up any customized
436 // information for the Module.
438 mdadv.setName(moduleName);
439 mdadv.setCreator("jxta.org");
440 mdadv.setModuleSpecID(IDFactory.newModuleSpecID(mcID));
442 if (moduleSpecURI != null) {
443 mdadv.setSpecURI(moduleSpecURI);
446 // Ok the Module advertisement was created, just publish
447 // it in my local cache and into the NetPeerGroup.
448 disco.publish(mdadv, localTTL, remoteTTL);
450 // Create the Module Implementation advertisement
451 ModuleImplAdvertisement miadv = (ModuleImplAdvertisement)
452 AdvertisementFactory.newAdvertisement(ModuleImplAdvertisement.getAdvertisementType());
454 miadv.setModuleSpecID(mdadv.getModuleSpecID());
455 if (moduleCode != null) {
456 miadv.setCode(moduleCode);
459 if (moduleCodeURI != null) {
460 miadv.setUri(moduleCodeURI);
462 miadv.setDescription("Created by ModuleManager: " + moduleName);
464 // Steal the compat, provider, and uri from the
465 // group's own impl adv. We DO want them identical in
467 ModuleImplAdvertisement pgImpl = (ModuleImplAdvertisement) group.getImplAdvertisement();
469 miadv.setCompat(pgImpl.getCompat());
470 miadv.setUri(pgImpl.getUri());
472 // Ok the Module Class advertisement was created, just publish
473 // it in my local cache and to my peergroup. This
474 // is the NetPeerGroup
475 disco.publish(miadv, localTTL, remoteTTL);
476 } catch (Exception ex) {
482 // FIXME this method should be refactored
484 * Creates a Module Class, Spec, and Impl advertisements, and adds the service
485 * Advertisement as part of the Module Impl Advertisement, and publishes the advertisements
489 * @param moduleName module name
490 * @param description module description
491 * @param moduleSpecURI module spec uri
492 * @param moduleCode module code
493 * @param moduleCodeURI module code uri
494 * @param mcID module class id
495 * @param msID module spec id
496 * @param code module code
497 * @param serviceAdv service advertisement
498 * @param localTTL local cache lifetime in ms
499 * @param remoteTTL remote cache lifetime in ms
500 * @exception IOException if an io error occurs
502 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 {
504 DiscoveryService discovery = group.getDiscoveryService();
505 // Create module class advertisement
506 ModuleClassAdvertisement mcadv = (ModuleClassAdvertisement)
507 AdvertisementFactory.newAdvertisement(ModuleClassAdvertisement.getAdvertisementType());
509 mcadv.setModuleClassID(mcID);
510 mcadv.setName(moduleName);
511 mcadv.setDescription(description);
513 // Module spec advertisement
514 ModuleSpecAdvertisement mdspec = (ModuleSpecAdvertisement)
515 AdvertisementFactory.newAdvertisement(ModuleSpecAdvertisement.getAdvertisementType());
517 mdspec.setModuleSpecID(msID);
518 mdspec.setName(moduleName);
519 mdspec.setSpecURI(moduleSpecURI);
521 // Module implementation advertisement
522 ModuleImplAdvertisement miadv = (ModuleImplAdvertisement)
523 AdvertisementFactory.newAdvertisement(ModuleImplAdvertisement.getAdvertisementType());
525 miadv.setModuleSpecID(mdspec.getModuleSpecID());
526 miadv.setDescription(description);
527 if (moduleCodeURI != null) {
528 miadv.setUri(moduleCodeURI);
530 if (moduleCode != null) {
531 miadv.setCode(moduleCode);
533 // Steal the compat, provider, and uri from the
534 // group's own impl adv. We DO want them identical in
536 ModuleImplAdvertisement pgImpl = (ModuleImplAdvertisement) group.getImplAdvertisement();
538 miadv.setCompat(pgImpl.getCompat());
540 Element pEl = (Element) serviceAdv.getDocument(MimeMediaType.XMLUTF8);
541 StructuredDocument svcParm = StructuredDocumentFactory.newStructuredDocument(MimeMediaType.XMLUTF8, "Parm");
543 StructuredDocumentUtils.copyElements(svcParm, svcParm, pEl);
544 miadv.setParam(svcParm);
545 // publish the advertisements
546 discovery.publish(mcadv, localTTL, remoteTTL);
547 discovery.publish(mdspec, localTTL, remoteTTL);
548 discovery.publish(miadv, localTTL, remoteTTL);
552 * Retreives a Service Advertisement from a module impl advertisement
553 * @param group peer group
554 * @param mia ModuleImplAdvertisement
555 * @param advertismentType service advertisment string Type
556 * @return The service Advertisement
557 * @exception IOException if an io error occurs
559 public Advertisement getServiceAdvertisement(PeerGroup group, ModuleImplAdvertisement mia, String advertismentType) throws IOException {
560 Element param = mia.getParam();
564 Enumeration list = param.getChildren(advertismentType);
566 if (list.hasMoreElements()) {
567 pel = (Element) list.nextElement();
570 Advertisement adv = AdvertisementFactory.newAdvertisement((TextElement) pel);
576 * Description of the Class
578 private class ModuleDesc {
581 * Description of the Field
583 protected Module module = null;
584 private boolean started = false;
585 private boolean stopped = true;
588 *Constructor for the ModuleDesc object
590 * @param module Description of the Parameter
592 public ModuleDesc(Module module) {
593 this.module = module;
597 * Description of the Method
599 * @param args Description of the Parameter
601 public void startApp(String[] args) {
602 if (module == null) {
606 // Already started - nothing to do
609 module.startApp(args);
615 * Description of the Method
617 public void stopApp() {
618 if (module == null) {
622 // Already stopped - nothing to do
633 * ModuleManagerLoader interface.
634 * This interface is used by the application in order to provide its own
635 * class loader instead of using the standard PeerGroup loader.
638 public interface ModuleManagerLoader {
641 * This method is invoked by the ModuleManager when it is time to load
642 * the class associated to the module. The name of the module is provided,
643 * which allows the application provided loader to be able to load a variety
644 * of modules, if that is necessary for the application. Note that the ModuleManager
645 * assumes that the module which is loaded by the provided loader is not started:
646 * loading and starting a module are two different operations for the ModuleManager.
648 * @param moduleName is the symbolic name of the Module.
649 * @return Module the object that has been loaded.
651 public Module loadModule(String moduleName);