/*
* 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.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.TextElement;
import net.jxta.document.XMLDocument;
import net.jxta.id.IDFactory;
import net.jxta.impl.peergroup.StdPeerGroup;
import net.jxta.impl.peergroup.StdPeerGroupParamAdv;
import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
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 net.jxta.protocol.PipeAdvertisement;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Advertisements and ID's "cooked" according to recipes lifted
* from J-C and Frog. Static methods meant for convenience in developing
* experimental propagation modules (pipe or rendezvous services,
* rendezvous managers) but maybe generally useful.
*
* @author vasha
*
* @since 2.6 This class is not used anywhere in the code and has been deprecated.
* It will be removed in the next release.
*
**/
@Deprecated
public class AdvCooker {
/**
* Reconstructs a ModuleClassID from its String representation
* as printed by the foregoing recipes.
*
* @param url -- the module class id in String form, "urn:jxta:uuid-[the big hex string]"
* @throws URISyntaxException -- if url is messed up
* @return -- module class id reconstructed from String
*/
private static ModuleClassID buildModuleClassID(String uri) throws URISyntaxException {
return (ModuleClassID) IDFactory.fromURI(new URI(uri));
}
/** Reconstructs a ModuleSpecID from its String representation
* as printed by the foregoing recipes.
*
* @param url -- the module spec id in String form, "urn:jxta:uuid-[the big hex string]"
* @throws URISyntaxException -- if url is messed up
* @return -- module spec id reconstructed from String
*/
private static ModuleSpecID buildModuleSpecID(String uri) throws URISyntaxException {
return (ModuleSpecID) IDFactory.fromURI(new URI(uri));
}
/**
* The module class advertisement is to simply advertise the
* existence of a module.
*
* @param mcid -- the module class id
* @param serviceName -- something like "JXTAMOD:JXTA-WIRE-MyNewThing"
* @param serviceDescription -- something like "JXTA-WIRE MyNewThing Module"
* @return an appropriate ModuleClassAdvertisement
*/
public static ModuleClassAdvertisement buildModuleClassAdvertisement(ModuleClassID mcid, String serviceName, String serviceDescription) {
ModuleClassAdvertisement mcadv = (ModuleClassAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleClassAdvertisement.getAdvertisementType());
mcadv.setName(serviceName);
mcadv.setDescription(serviceDescription);
mcadv.setModuleClassID(mcid);
return mcadv;
}
/**
* The ModuleSpecAdvertisement has two purposes, to publish
* the uri of its formal specs for developers and to publish the
* means of remote access to the module's services if that
* is appropriate. (See {@link ModuleSpecAdvertisement} )
* Use this form for a minimal advertisement, suitable
* for development.
*
* @param msid -- the module spec id, "urn:jxta:uuid-[the big hex string]"
* @param moduleSpecName -- something like "JXTASPEC:JXTA-WIRE-MyNewThing-SPEC"
* @param moduleSpecDescription -- something like "JXTA-WIRE MyNewThing Specification"
* @return -- a boilerplate suitable for development.
*/
public static ModuleSpecAdvertisement buildModuleSpecAdvertisement(ModuleSpecID msid, String moduleSpecName, String moduleSpecDescription) {
return buildModuleSpecAdvertisement(msid, moduleSpecName, moduleSpecDescription, null, null, null, null, null, null, null);
}
/**
* Use this form for production provided remote access is not required.
*
* @param msid -- the module spec id, "urn:jxta:uuid-[the big hex string]"
* @param moduleSpecName -- something like "JXTASPEC:JXTA-WIRE-MyNewThing-SPEC"
* @param moduleSpecDescription -- something like "JXTA-WIRE MyNewThing Specification"
* @param creator -- something like "jxta.org"
* @param version -- something like "Version 1.0"
* @param specURI -- where to locate the formal specs, e.g. "http://www.jxta.org/MyNewThing"
* @return -- a fully populated advert suitable if remote access is not required.
*/
public static ModuleSpecAdvertisement buildModuleSpecAdvertisement(ModuleSpecID msid, String moduleSpecName, String moduleSpecDescription, String creator, String version, String specURI) {
return buildModuleSpecAdvertisement(msid, moduleSpecName, moduleSpecDescription, creator, version, specURI, null, null
,
null, null);
}
/**
* Use this form for a fully populated advert.
*
* @param msid -- the module spec id, "urn:jxta:uuid-[the big hex string]"
* @param moduleSpecName -- something like "JXTASPEC:JXTA-WIRE-MyNewThing-SPEC"
* @param moduleSpecDescription -- something like "JXTA-WIRE MyNewThing Specification"
* @param creator -- something like "jxta.org"
* @param version -- something like "Version 2.0"
* @param specURI -- where to locate the formal specs, e.g. "http://www.jxta.org/MyNewThing"
* @param pipeAdv -- to make the module useable remotely (see {@link ModuleSpecAdvertisement})
* @param proxySpecID -- sometimes required for remote use (see {@link ModuleSpecAdvertisement})
* @param authorizationSpecID -- sometimes required for remote use (see {@link ModuleSpecAdvertisement})
* @param param -- anything else
* @return -- a fully populated advert specifying remote access to module services.
*/
public static ModuleSpecAdvertisement buildModuleSpecAdvertisement(ModuleSpecID msid, String moduleSpecName, String moduleSpecDescription, String creator, String version, String specURI, PipeAdvertisement pipeAdv, ModuleSpecID proxySpecID, ModuleSpecID authorizationSpecID, StructuredDocument param) {
ModuleSpecAdvertisement msadv = (ModuleSpecAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleSpecAdvertisement.getAdvertisementType());
msadv.setModuleSpecID(msid);
msadv.setName(moduleSpecName);
msadv.setDescription(moduleSpecDescription);
msadv.setCreator(creator == null ? "jxta.org" : creator);
msadv.setVersion(version == null ? "Version 2.0" : version);
msadv.setSpecURI(specURI == null ? "http://www.jxta.org/" + moduleSpecName : specURI);
if (pipeAdv != null) {
msadv.setPipeAdvertisement(pipeAdv);
}
if (proxySpecID != null) {
msadv.setProxySpecID(proxySpecID);
}
if (authorizationSpecID != null) {
msadv.setAuthSpecID(authorizationSpecID);
}
if (param != null) {
msadv.setParam(param);
}
return msadv;
}
/**
* Compat's (compatibility statements) serve to narrow the search
* for a ModuleImplAdvertisement. Basically you want something
* compatible with your group's implementation. Use this form for
* compatibilty with the current StdPeerGroup.
*
* @return -- boilerplate compat for StdPeerGroup
*/
public static XMLDocument buildCompat() {
try {
PeerGroup wpg = PeerGroup.globalRegistry.lookupInstance(PeerGroupID.worldPeerGroupID);
ModuleImplAdvertisement implAdv = wpg.getAllPurposePeerGroupImplAdvertisement();
wpg.unref();
XMLDocument compat = (XMLDocument) implAdv.getCompat();
return compat;
} catch (Exception e) {
// but if it doesn't work default to what was current on Feb 22 2006.
return buildCompat("JDK1.4.1", "V2.0 Ref Impl");
}
}
/**
* Use this form for customized compatibility statements.
* Alternatively a group's compat is accessible via
* group.getCompat()
*
* @param efmt -- something like "JDK1.4"
* @param bind -- something like "V1.0 Ref Impl"
* @return -- custom compatibility tag
*/
public static XMLDocument buildCompat(String efmt, String bind) {
XMLDocument doc = (XMLDocument)
StructuredDocumentFactory.newStructuredDocument(MimeMediaType.XMLUTF8, "Comp");
Element e = doc.createElement("Efmt", efmt);
doc.appendChild(e);
e = doc.createElement("Bind", bind);
doc.appendChild(e);
return doc;
}
/**
* A ModuleImplAdvertisement represents one of any number of
* published implementations of a given specification. Use this form
* with for a development boilerplate. Use buildCompat() for a compat
* boilerplate.
* (See {@link ModuleImplAdvertisement}.)
*
* @param msid -- the module spec id
* @param code -- the module's fully qualified classname, "net.jxta.impl.wire.MyNewThing"
* @param compat -- a compatibility statement. Use buildCompat() for a boilerplate.
* @return -- a development boilerplate with custom compatibility.
*/
public static ModuleImplAdvertisement buildModuleImplAdvertisement(ModuleSpecID msid, String code, Element compat) {
ModuleImplAdvertisement miadv = (ModuleImplAdvertisement)
AdvertisementFactory.newAdvertisement(ModuleImplAdvertisement.getAdvertisementType());
miadv.setCompat(compat);
miadv.setModuleSpecID(msid);
miadv.setCode(code);
miadv.setDescription(code + " Module, J2SE Implementation");
miadv.setProvider("jxta.org");
miadv.setUri("http://download.jxta.org");
return miadv;
}
/** Use this form to fully populate a ModuleImplAdvertisement.
* A ModuleImplAdvertisement has an optional field, "param" which is
* neglected here. If needed it should be set with advert's setParam method.
* (See {@link ModuleImplAdvertisement}.)
*
* @param msid -- the module spec id
* @param code -- the module's fully qualified classname, "net.jxta.impl.wire.MyNewThing"
* @param compat -- a compatibility statement
* @param description -- something like "MyNewThing Module, J2SE Implementation"
* @param provider -- something like "jxta.org"
* @param uri -- currently ornamental, eventually where to find binaries.
* @return -- a custom advert, fully populated except for "param" field.
*/
public static ModuleImplAdvertisement buildModuleImplAdvertisement(ModuleSpecID msid, String code, Element compat, String description, String provider, String uri) {
ModuleImplAdvertisement miadv = buildModuleImplAdvertisement(msid, code, compat);
miadv.setDescription(description);
miadv.setProvider(provider);
miadv.setUri(uri);
return miadv;
}
/**
* Modifies a copy of the parent's implementation
* advertisement to reflect the addition or replacement of
* services. The newServices Hashtable must have ModuleClassID
* keys and ModuleImplAdvertisement values. I've deferred adding
* applications or protocols for the moment --vasha@jxta.org Dec 3 2001.
*
* @return -- advert for the new peergroup which the StdPeerGroup module will implement.
* @param parent -- a running instance of the new group's parent
* @param newGroupModuleSpecID -- since new or replacement services are involved
* @param newDescription -- the new group's reason to be
* @param newServices -- advert's for the new services
* @throws IllegalArgumentException -- for a bad key or value type
* @throws Exception --- if the parent can't produce a copy of its own impl advert
*/
public static ModuleImplAdvertisement buildPeerGroupImplAdvertisement(StdPeerGroup parent, ModuleSpecID newGroupModuleSpecID, String newDescription, Map newServices) throws Exception {
Map newApps = null, newProtos = null;
// illegal types will cause an IllegalArgumentException
typeCheckKeys(newServices);
typeCheckValues(newServices);
// get a copy of parent's general purpose advert as a template
ModuleImplAdvertisement implAdv = parent.getAllPurposePeerGroupImplAdvertisement();
implAdv.setDescription(newDescription);
implAdv.setModuleSpecID(newGroupModuleSpecID);
// extract embedded ad for standard modules
TextElement paramElement = (TextElement) implAdv.getParam();
StdPeerGroupParamAdv paramAdv = new StdPeerGroupParamAdv(paramElement);
// alter services
Map services = paramAdv.getServices();
typeCheckKeys(services);
// mergeTables will override old services with new if base classes are the same
services = mergeTables(services, newServices);
paramAdv.setServices(services);
paramElement = (TextElement) paramAdv.getDocument(MimeMediaType.XMLUTF8);
implAdv.setParam(paramElement);
return implAdv;
}
public static ModuleImplAdvertisement buildPeerGroupImplAdvertisement(PeerGroup parent, ModuleSpecID newGroupModuleSpecID, String newDescription, Map newServices, Map newApps) throws Exception {
Map newProtos = null;
// illegal types will cause an IllegalArgumentException
typeCheckKeys(newServices);
typeCheckValues(newServices);
typeCheckKeys(newApps);
typeCheckValues(newApps);
// get a copy of parent's general purpose advert as a template
ModuleImplAdvertisement implAdv = parent.getAllPurposePeerGroupImplAdvertisement();
implAdv.setDescription(newDescription);
implAdv.setModuleSpecID(newGroupModuleSpecID);
// extract embedded ad for standard modules
TextElement paramElement = (TextElement) implAdv.getParam();
StdPeerGroupParamAdv paramAdv = new StdPeerGroupParamAdv(paramElement);
// alter services
Map services = paramAdv.getServices();
typeCheckKeys(services);
// mergeTables will override old services with new if base classes are the same
services = mergeTables(services, newServices);
paramAdv.setServices(services);
// alter apps
Map apps = paramAdv.getApps();
typeCheckKeys(apps);
apps = mergeTables(apps, newApps);
paramAdv.setApps(apps);
paramElement = (TextElement) paramAdv.getDocument(MimeMediaType.XMLUTF8);
implAdv.setParam(paramElement);
return implAdv;
}
public static ModuleImplAdvertisement buildPeerGroupImplAdvertisement(PeerGroup parent, ModuleSpecID newGroupModuleSpecID, String newDescription, Map newServices, Map newApps, Map newProtos) throws Exception {
// illegal types will cause an IllegalArgumentException
typeCheckKeys(newServices);
typeCheckValues(newServices);
typeCheckKeys(newApps);
typeCheckValues(newApps);
typeCheckKeys(newProtos);
typeCheckValues(newProtos);
// get a copy of parent's general purpose advert as a template
ModuleImplAdvertisement implAdv = parent.getAllPurposePeerGroupImplAdvertisement();
implAdv.setDescription(newDescription);
implAdv.setModuleSpecID(newGroupModuleSpecID);
// extract embedded ad for standard modules
TextElement paramElement = (TextElement) implAdv.getParam();
StdPeerGroupParamAdv paramAdv = new StdPeerGroupParamAdv(paramElement);
// alter services
Map services = paramAdv.getServices();
typeCheckKeys(services);
// mergeTables will override old services with new if base classes are the same
services = mergeTables(services, newServices);
paramAdv.setServices(services);
// alter apps
Map apps = paramAdv.getApps();
typeCheckKeys(apps);
apps = mergeTables(apps, newApps);
paramAdv.setApps(newApps);
// alter protos
Map protos = paramAdv.getProtos();
typeCheckKeys(protos);
apps = mergeTables(protos, newProtos);
paramAdv.setProtos(newProtos);
paramElement = (TextElement) paramAdv.getDocument(MimeMediaType.XMLUTF8);
implAdv.setParam(paramElement);
return implAdv;
}
/**
* Module table vaules must be ModuleImplAdvertisements here.
* Though StdPeerGroup allows for values of type ModuleSpecID,
* the context in which they seem to apply is not our context of adding
* or replacing modules, so I've prohibited them. --vasha@jxta.org dec 3 2001.
*
* @param moduleTable -- a Hashtable of services, applications or protocols.
* @throws IllegalArgumentException -- for an invalid key or value type
*/
public static void typeCheckValues(Map moduleTable) {
String badVal = "Module table value not a ModuleImplAdvertisement ";
Iterator keys = moduleTable.keySet().iterator();
while (keys.hasNext()) {
// Tables allow for ModuleSpecID values when, as I understand it,
// they can load the module from the parent. I'm insisting that
// NEW or ALTERNATIVE modules supply a ModuleImplAdvertisement.
Object value = moduleTable.get(keys.next());
boolean legalValue = value instanceof ModuleImplAdvertisement;
if (!legalValue) {
throw(new IllegalArgumentException(badVal + value));
}
}
}
/**
* Module table keys must be ModuleClassID's.
*
* @param moduleTable -- a Hashtable of services, applications or protocols.
* @throws IllegalArgumentException -- for an invalid key or value type
*/
public static void typeCheckKeys(Map moduleTable) {
String badKey = "Module table key not a ModuleClassID ";
Iterator keys = moduleTable.keySet().iterator();
while (keys.hasNext()) {
Object key = keys.next();
boolean legalKey = key instanceof ModuleClassID;
if (!legalKey) {
throw(new IllegalArgumentException(badKey + key));
}
}
}
/**
* Merge two hashtables of servcices, overwriting old with new if
* they have the same base class id.
*
* @oldServices --service table of a parent group
* @newServices --services to be added or substituted
* @return --merged table
*/
private static Map mergeTables(Map oldServices, Map newServices) {
// just use brute force; we won't be doing it that often
Map mergedServices = new HashMap(oldServices);
Iterator newKeys = newServices.keySet().iterator();
while (newKeys.hasNext()) {
ModuleClassID key = (ModuleClassID) newKeys.next();
Iterator oldKeys = oldServices.keySet().iterator();
while (oldKeys.hasNext()) {
ModuleClassID oldkey = (ModuleClassID) oldKeys.next();
if (oldkey.isOfSameBaseClass(key)) {
mergedServices.remove(oldkey);
}
}
mergedServices.put(key, newServices.get(key));
}
return mergedServices;
}
}