package lava.net.psyc; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import lava.net.common.UNA; import lava.net.common.UNL; import lava.net.common.Value; import lava.net.common.VariableModifier; import lava.net.common.VariableNormalizer; import lava.net.common.ModifierParser; import lava.net.mmp.MMPCenter; import lava.net.mmp.MMPMessageCenter; import lava.net.mmp.MMPException; import lava.net.mmp.MMPDeliveryException; import lava.net.mmp.MMPReceiverException; import lava.net.mmp.MMPPacket; import lava.net.mmp.MMPPacketManager; /** * **/ public class PSYCMessageCenter { /** * **/ public final static String ProtocolName = "PSYC"; /** * **/ public final static int ProtocolMajorVersion = 0; /** * **/ public final static int ProtocolMinorVersion = 7; /** * **/ public final static String defaultScheme = "psyc"; /** * **/ public final static int defaultPort = 4404; /** * **/ public final static String defaultProtocol = "tcp"; /** * **/ public final static String defaultResource = "/"; /** * The maximum number of permanent variables per object/context. * This is to prevent denial of service. * Numbers smaller than 1 are considered infinite. **/ public static int maxVariables = 50; /** * **/ public final static char METHOD_HIERARCHY_DELIMITER = '_'; /** * **/ public final static char VERSION_DELIMITER = '/'; /** * **/ public final static char MINOR_VERSION_DELIMITER = '.'; /** * **/ public final static String usingProtocolsTag = // "_using_protocols"; /** * **/ private final static String[] usingProtocolsAliases = // {"using_protocols"}; /** * **/ public final static String understandProtocolsTag = // "_understand_protocols"; /** * **/ private final static String[] understandProtocolsAliases = // {"understand_protocols"}; /** * **/ public final static String usingPackagesTag = "_using_packages"; /** * **/ private final static String[] usingPackagesAliases = // {"using_packages"}; /** * **/ public final static String understandPackagesTag = // "_understand_packages"; /** * **/ private final static String[] understandPackagesAliases = // {"understand_packages"}; /** * **/ public final static String contextTag = "_context"; /** * **/ private final static String[] contextAliases = {"context", "c"}; /** * **/ public final static String nameTag = "_name"; /** * **/ private final static String[] nameAliases = {}; /** * **/ public final static String identificationTag = "_identification"; /** * **/ private final static String[] identificationAliases = // {"identification", "i"}; /** * **/ public final static String locationTag = "_location"; /** * **/ private final static String[] locationAliases = {}; /** * **/ public final static String addressesTag = "_addresses"; /** * **/ private final static String[] addressesAliases = {}; /** * **/ public final static String nameObjectTag = "_name_object"; /** * **/ private final static String[] nameObjectAliases = {}; /** * **/ public final static String descriptionTag = "_description"; /** * **/ private final static String[] descriptionAliases = {}; /** * **/ public final static String implementationTag = "_implementation"; /** * **/ private final static String[] implementationAliases = {}; /** * **/ public final static String nicknameTag = "_nick"; /** * **/ private final static String[] nicknameAliases = { "_nick_alias" }; /** * **/ public final static String contentTag = "_type_content"; /** * **/ private final static String[] contentAliases = {}; /** * **/ public final static String actionTag = "_action"; /** * **/ private final static String[] actionAliases = {}; /** * **/ public final static String pageDescriptionTag = "_page_description"; /** * **/ private final static String[] pageDescriptionAliases = {}; /** * **/ public final static String humanInformationTag = "_human_information"; /** * **/ private final static String[] humanInformationAliases = {}; /** * **/ public final static String setPrefix = "_set"; /** * **/ public final static String assignPrefix = "_assign"; /** * **/ public final static String augmentPrefix = "_augment"; /** * **/ public final static String diminishPrefix = "_diminish"; public class DummyManager implements PSYCPacketManager { /** * **/ public void manage(PSYCPacket packet) { } /** * **/ public void error(PSYCException e, UNL remote) { } /** * **/ public void reset(UNL remote) { } } /** * Hash to find out if we know the variable name **/ private Hashtable knownVars = new Hashtable(); /** * **/ private PSYCPacketManager manager = null; /** * **/ private MMPCenter mmpCenter = null; /** * **/ private Normalizer normalizer = new Normalizer(); /** * **/ private MMPPacketManager peer = new PacketManager(); /** * mapping UNA -> UNL without context **/ private Hashtable objects = new Hashtable(); /** * mapping UNL without context -> Vector of UNA **/ private Hashtable addresses = new Hashtable(); /** * mapping UNL without context -> Hashtable of ASSIGNed contexts **/ private Hashtable incomingContexts = new Hashtable(); /** * mapping UNL -> Hashtable of global variables **/ private Hashtable globVars = new Hashtable(); /** * mapping UNL -> Hashtable of temporarily variables **/ private Hashtable tempVars = new Hashtable(); /** * mapping UNL -> Vector of outgoing variables **/ private Hashtable outgoings = new Hashtable(); /** * mapping context name -> Hashtable of context variables **/ private Hashtable outgoingContexts = new Hashtable(); /** * mapping context name -> Hashtable of peers (UNL without context, UNL) **/ private Hashtable outgoingContextInvolved = new Hashtable(); ///** // * mark locations as dead // **/ //private Hashtable deadLocations = new Hashtable(); /** * mapping variable alias -> normalized variable **/ private Hashtable variableAlias = new Hashtable(); /** * mapping method alias -> normalized method **/ private Hashtable methodAlias = new Hashtable(); /** * **/ private PSYCDeliveryException gotException = null; /** * for packages **/ private Hashtable packages = new Hashtable(); /** * **/ private Hashtable callPackagesVariable = new Hashtable(); /** * **/ private Hashtable callPackagesMethod = new Hashtable(); /** * **/ private Hashtable callPackagesError = new Hashtable(); /** * **/ private Hashtable method = new Hashtable(); /** * For greeting... a list of protocols this message center understands. **/ public Value understandProtocols = null; /** * **/ private Value usingPackages = null; /** * **/ private Value understandPackages = null; /** * For greeting... a message method which is sent to new connected * remote objects. **/ private String greetingMethod = null; /** * For greeting... the body of the message which is sent to new connected * remote objects. **/ private String greetingBody = null; /** * This creates a new instance of a message center for the PSYC * protocol. * This instance does not listen at the network and therefor it * cannot be reached from the outside except it establishes * connections itself (for example by sending messages to some * remote object - then it can be reached by this object). **/ public PSYCMessageCenter() { this (null); } /** * This creates a new instance of a message center for the PSYC * protocol that sends every information it gets to the specified * packet manager. * If the manager is null, it will be ignored. * This instance does not listen at the network and therefor it * cannot be reached from the outside except it establishes * connections itself (for example by sending messages to some * remote object - then it can be reached by this object). **/ public PSYCMessageCenter(PSYCPacketManager manager) { this (manager,(MMPCenter)null); } /** * This creates a new instance of a message center for the PSYC * protocol that sends every information it gets to the specified * packet manager. * If the manager is null, it will be ignored. * The flag listen specifies, if the message center is listening * at the network for incoming messages. * If it should listen, it tries listenig at the PSYC well known * port, if this is not possible, it will listen at random ports. **/ public PSYCMessageCenter(PSYCPacketManager manager, boolean listen) { setDefaults(); setManager(manager); setMMPCenter(new MMPMessageCenter(peer,listen)); initializeAliases(); } /** * This creates a new instance of a message center for the PSYC * protocol that sends every information it gets to the specified * packet manager. * If the manager is null, it will be ignored. * The array listenLocations specifies a set of locations, the * center should listen on. * If it cannot listen at some of the locations, they'll be ignored. **/ public PSYCMessageCenter(PSYCPacketManager manager, UNA[] listenLocations) { setDefaults(); setManager(manager); setMMPCenter(new MMPMessageCenter(peer,listenLocations)); initializeAliases(); } /** * This creates a new instance of a message center for the PSYC * protocol that sends every information it gets to the specified * packet manager and the specified underlying transport manager. * If the manager is null, it will be ignored. * If the transport manager is null, a default one will be created. **/ public PSYCMessageCenter(PSYCPacketManager manager, // MMPCenter center) { setDefaults(); setManager(manager); setMMPCenter(center == null ? new MMPMessageCenter(peer) : center); initializeAliases(); } /** * Sets the default values for UNA/UNL/UNI. * This is called by the PSYCMessageCenter constructors normally. * It does also call the setDefaults of the MMPMessageCenter. * If it is called manually, this must be done before some * UNA/UNL/UNI is instantiated. **/ public static void setDefaults() { UNL.setDefaults(defaultScheme,MMPMessageCenter.getLocalHost(),// defaultPort,defaultProtocol,defaultResource); MMPMessageCenter.setDefaults(); } /** * Sets the underlying MMPCenter which is called by this * PSYCMessageCenter. **/ public void setMMPCenter(MMPCenter mmpCenter) { if(mmpCenter == null) return; this.mmpCenter = mmpCenter; } /** * Returns the underlying MMPCenter which is called by this * PSYCMessageCenter. **/ public MMPCenter getMMPCenter() { return mmpCenter; } /** * Sets the PSYCPacketManager which is called by this * PSYCMessageCenter if it delivers some PSYC event. * * @see lava.net.psyc.PSYCMessageCenter#addPackage(lava.net.psyc.PSYCPackage) **/ public PSYCPacketManager setManager(PSYCPacketManager manager) { if(manager == null) manager = new DummyManager(); PSYCPacketManager old = this.manager; this.manager = manager; return old; } /** * Closes this message center. **/ public synchronized void close() { mmpCenter.close(); } /** * For greeting ... the method and the body which is sent to new connected * remote objects. This is not required by the PSYC protocol, so it can * be defined as liked. **/ public void setGreeting(String method, String body) { greetingMethod = method; greetingBody = body; } /** * Adds a package to this message center. * * @see lava.net.psyc.PSYCMessageCenter#setManager(lava.net.psyc.PSYCPacketManager) **/ public boolean addPackage(PSYCPackage pack) { if(pack == null) return false; if(packages.get(pack.getClass()) != null) return false; packages.put(pack.getClass(),pack); String name = pack.getPackageName(); if(name != null && name.length() > 0) { //Vector vars = new Vector(); if(pack.uses()) if(usingPackages == null) { usingPackages = new Value(name); assignVariable((UNL)null,usingPackagesTag,usingPackages,// true); } else { usingPackages.augment(name); augmentVariable((UNL)null,usingPackagesTag,new Value(name),// true); } if(pack.understands()) if(understandPackages == null) { understandPackages = new Value(name); assignVariable((UNL)null,understandPackagesTag,// understandPackages,true); } else { understandPackages.augment(name); augmentVariable((UNL)null,understandPackagesTag,// new Value(name),true); } } if(pack.wantsErrors()) callPackagesError.put(pack,pack); String[] variables = pack.getVariables(); if(variables != null && variables.length > 0) for(int i = 0;i < variables.length;++i) { Hashtable packages = // (Hashtable)callPackagesVariable.get(variables[i]); if(packages == null) callPackagesVariable.put(variables[i],// packages = new Hashtable()); packages.put(pack,pack); } String[] methods = pack.getMethods(); if(methods != null && methods.length > 0) for(int i = 0;i < methods.length;++i) { Hashtable packages = // (Hashtable)callPackagesMethod.get(methods[i]); if(packages == null) callPackagesMethod.put(methods[i],packages = new Hashtable()); packages.put(pack,pack); } pack.registerCenter(this); return true; } /** * Guess addresses of the given object and initialize the object-address * mapping with them. **/ private void initMapping(UNL object) { String scheme = object.getScheme(); String host = object.getHostName(); int port = object.getPort(); String protocol = object.getProtocol(); String resource = object.getResource(); Vector addresses = new Vector(); UNA address; for(int i = 0;i < MMPMessageCenter.protocols.length;++i) { address = new UNA(scheme,host,port,// MMPMessageCenter.protocols[i],resource); if(address.getProtocol().equals(object.getProtocol())) addresses.insertElementAt(address,0); else addresses.addElement(address); } initMapping(object,addresses); } /** * Initialize the object-address mapping. **/ private void initMapping(UNL object, Vector addresses) { boolean hasDefault = false; int i; // for now, remove every other stuff if we have tcp for(i = 0;!hasDefault && i < addresses.size();++i) if(((UNA)addresses.elementAt(i)).getProtocol().equals(// MMPMessageCenter.defaultProtocol)) hasDefault = true; if(hasDefault) for(i = 0;i < addresses.size();++i) if(!((UNA)addresses.elementAt(i)).getProtocol().equals(// MMPMessageCenter.defaultProtocol)) addresses.removeElementAt(i--); this.addresses.put(object,addresses); for(Enumeration e = addresses.elements();e.hasMoreElements();) objects.put(e.nextElement(),object); } /** * Remove the specified address from its objects address mapping. **/ private void removeAddress(UNA address) { if(address == null) return; UNL object = (UNL)objects.get(address); if(object == null) return; Vector addresses = (Vector)this.addresses.get(object.withoutContext()); if(addresses == null) return; for(int i = 0;i < addresses.size();++i) if(addresses.elementAt(i).equals(address)) addresses.removeElementAt(i--); this.addresses.remove(address); } /** * Remove the object-address mapping for the specified object. **/ private void removeObject(UNL object) { if(object == null) return; Vector addresses = (Vector)this.addresses.remove(// object.withoutContext()); if(addresses == null) return; for(Enumeration e = addresses.elements();e.hasMoreElements();) objects.remove(e.nextElement()); } /** * Returns the actual address for a given object. **/ public UNA getAddress(UNL object) { if(object == null) return null; Vector addresses = (Vector)this.addresses.get(object.withoutContext()); if(addresses == null) { initMapping(object.withoutContext()); addresses = (Vector)this.addresses.get(object.withoutContext()); } UNA address = addresses.size() > 0 ? // (UNA)addresses.firstElement() : null; return address; } /** * Returns the object for a given address. **/ public UNL getObject(UNA address) { if(address == null) return null; UNL object = (UNL)objects.get(address); if(object == null) { Vector addresses = new Vector(); addresses.addElement(address); initMapping(new UNL(address),addresses); object = (UNL)objects.get(address); } return object; } /** * Adds a variable alias to the message centers variable expand buffer. **/ public void addVariableAlias(String alias, String longVar) { if(alias == null || longVar == null) return; variableAlias.put(alias,longVar); } /** * Removes a variable alias from the message centers variable expand buffer. **/ public String removeVariableAlias(String alias) { if(alias == null) return null; return (String)variableAlias.remove(alias); } /** * Adds a method alias to the message centers method expand buffer. **/ public void addMethodAlias(String alias, String longName) { if(alias == null || longName == null) return; methodAlias.put(alias,longName); } /** * Removes a method alias from the message centers method expand buffer. **/ public String removeMethodAlias(String alias) { if(alias == null) return null; return (String)methodAlias.remove(alias); } private class Normalizer implements VariableNormalizer { /** * **/ public String normalizeVariableName(String name) { if(name == null) return null; String expanded = (String)variableAlias.get(name); if(expanded == null) return name; return expanded; } /** * **/ public String normalizeMethodName(String name) { if(name == null) return null; String expanded = (String)methodAlias.get(name); if(expanded == null) return name; return expanded; } } /** * **/ private void initializeAliases() { Value v = new Value(); int i; for(i = 0;i < usingProtocolsAliases.length;++i) addVariableAlias(usingProtocolsAliases[i],usingProtocolsTag); for(i = 0;i < understandProtocolsAliases.length;++i) addVariableAlias(understandProtocolsAliases[i],// understandProtocolsTag); for(i = 0;i < usingPackagesAliases.length;++i) addVariableAlias(usingPackagesAliases[i],usingPackagesTag); for(i = 0;i < understandPackagesAliases.length;++i) addVariableAlias(understandPackagesAliases[i],// understandPackagesTag); for(i = 0;i < contextAliases.length;++i) addVariableAlias(contextAliases[i],contextTag); for(i = 0;i < nameAliases.length;++i) addVariableAlias(nameAliases[i],nameTag); for(i = 0;i < identificationAliases.length;++i) addVariableAlias(identificationAliases[i],identificationTag); for(i = 0;i < locationAliases.length;++i) addVariableAlias(locationAliases[i],locationTag); for(i = 0;i < addressesAliases.length;++i) addVariableAlias(addressesAliases[i],addressesTag); for(i = 0;i < nameObjectAliases.length;++i) addVariableAlias(nameObjectAliases[i],nameObjectTag); for(i = 0;i < descriptionAliases.length;++i) addVariableAlias(descriptionAliases[i],descriptionTag); for(i = 0;i < implementationAliases.length;++i) addVariableAlias(implementationAliases[i],implementationTag); for(i = 0;i < nicknameAliases.length;++i) addVariableAlias(nicknameAliases[i],nicknameTag); for(i = 0;i < contentAliases.length;++i) addVariableAlias(contentAliases[i],contentTag); for(i = 0;i < actionAliases.length;++i) addVariableAlias(actionAliases[i],actionTag); for(i = 0;i < pageDescriptionAliases.length;++i) addVariableAlias(pageDescriptionAliases[i],pageDescriptionTag); for(i = 0;i < humanInformationAliases.length;++i) addVariableAlias(humanInformationAliases[i],// humanInformationTag); Value prot = new Value(ProtocolName + "/" + // ProtocolMajorVersion + "." + ProtocolMinorVersion); Value sub = mmpCenter.usingProtocols(); if(sub != null) { String s = sub.toString(); if(s != null && s.length() > 0) prot.augment(s); } assignVariable((UNL)null,usingProtocolsTag,prot,false); UNA[] addresses = mmpCenter.getListenLocations(); if(addresses != null && addresses.length > 0) { Value addr = new Value(); String name_object = null; int bestMatch = Integer.MAX_VALUE; for(i = 0;i < addresses.length;++i) { String addresses_ = addresses[i].toString(); addr.augment(addresses_); int actual = addresses_.length(); if(actual < bestMatch || name_object == null) { bestMatch = actual; name_object = addresses_; } } addr.diminish(name_object); Value addrs = new Value(name_object); addrs.augment(addr.toString()); assignVariable((UNL)null,addressesTag,addrs,false); assignVariable((UNL)null,nameObjectTag,new Value(name_object),// false); } Value implementation = new Value(); implementation.augment(ProtocolName + VERSION_DELIMITER + // ProtocolMajorVersion + MINOR_VERSION_DELIMITER + // ProtocolMinorVersion); String runtime = "java"; String os_name = "Applet"; String os_arch = "Unknown"; if(!UNL.isApplet()) { os_name = "Application"; String tmp = System.getProperty("java.version"); if(tmp != null) runtime = runtime + VERSION_DELIMITER + tmp; tmp = System.getProperty("os.name"); if(tmp != null) os_name = tmp; tmp = System.getProperty("os.version"); if(tmp != null) os_name = os_name + VERSION_DELIMITER + tmp; tmp = System.getProperty("os.arch"); if(tmp != null) os_arch = tmp; } implementation.augment(runtime); implementation.augment(os_name); implementation.augment(os_arch); assignVariable((UNL)null,implementationTag,implementation,false); } /** * Returns a list of the names of all variables currently set * by the specified remote object. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public String[] getVars(UNL remote) { String[] names; if(remote == null) { synchronized(knownVars) { int size = knownVars.size(); int i = 0; names = new String[size]; for(Enumeration e = knownVars.keys();e.hasMoreElements();) names[i++] = (String)e.nextElement(); } } else { Hashtable globVars = (Hashtable)this.globVars.get(remote); Hashtable tempVars = (Hashtable)this.tempVars.get(remote); int size, i = 0; Enumeration e; synchronized(globVars) { synchronized(tempVars) { if(globVars != null) { size = globVars.size(); if(tempVars != null) for(e = tempVars.keys();e.hasMoreElements();) if(globVars.get(e.nextElement()) == null) ++size; } else size = tempVars == null ? 0 : tempVars.size(); names = new String[size]; String tmp; if(globVars != null) for(e = globVars.keys();e.hasMoreElements();) names[i++] = (String)e.nextElement(); if(tempVars != null) for(e = tempVars.keys();e.hasMoreElements();) { tmp = (String)e.nextElement(); if(globVars == null || globVars.get(tmp) == null) names[i++] = tmp; } } } } return names; } /** * Returns the Value currently set for the specified variable * by the specified remote object. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public Value getValue(UNL remote, String name) { if(name == null) return null; Value val; Hashtable vars = knownVars; if(remote != null) { vars = (Hashtable)tempVars.get(remote); if(vars == null) vars = (Hashtable)globVars.get(remote); else if((val = (Value)vars.get(name)) != null) return val; else vars = (Hashtable)globVars.get(remote); if(vars != null && (val = (Value)vars.get(name)) != null) return val; if(remote.getContext() != null) return getValue(remote.withoutContext(),name); else return null; } return (Value)vars.get(name); } /** * Removes the specified variable currently set by the specified remote * object. Returns the removed value. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public Value removeVariable(UNL remote, String name) { if(name == null) return null; Value old = getValue(remote,name); if(remote != null) { Hashtable vars = (Hashtable)tempVars.get(remote); if(vars != null) vars.remove(name); else { vars = (Hashtable)globVars.get(remote); if(vars != null) vars.remove(name); } } else knownVars.remove(name); return old; } /** * Checks the specified (list-)variable of the specified remote object, if * it contains the specified element. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ private boolean checkElement(UNL remote, String name, String element) { if(element == null || element.length() <= 0) return false; Value v = getValue(remote,name); if(v == null) return true; String value = v.toString(); int index = value.indexOf(element); if(index < 0) return false; if(index > 0) { char prev = value.charAt(index - 1); boolean found = false; for(int i = 0;i < Value.listSeparator.length;++i) if(prev == Value.listSeparator[i]) { found = true; break; } if(!found) return false; } int nextpos = element.length() + index; if(nextpos >= value.length()) return true; char next = value.charAt(nextpos); if(next == VERSION_DELIMITER) return true; for(int i = 0;i < Value.listSeparator.length;++i) if(next == Value.listSeparator[i]) return true; return false; } /** * Returns a list of protocols currently used by the specified remote * object. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public String[] getUsingProtocols(UNL remote) { Value v = getValue(remote,usingProtocolsTag); if(v != null) return v.toList(); return null; } /** * Checks, if the specified remote object uses the specified protocol. **/ public boolean usesProtocol(UNL remote, String name) { return checkElement(remote,usingProtocolsTag,name); } /** * Returns a list of protocols the specified remote object understands. **/ public String[] getUnderstandProtocols(UNL remote) { Value v = getValue(remote,understandProtocolsTag); if(v != null) return v.toList(); return null; } /** * Checks, if the specified remote object understands the specified * protocol. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public boolean understandsProtocol(UNL remote, String name) { return checkElement(remote,understandProtocolsTag,name); } /** * **/ public void assignUnderstandProtocol(UNL remote, String protocol) { assignVariable(remote,understandProtocolsTag,new Value(protocol),// true); } /** * **/ public void augmentUnderstandProtocol(UNL remote, String protocol) { augmentVariable(remote,understandProtocolsTag,new Value(protocol),// true); } /** * **/ public void diminishUnderstandProtocol(UNL remote, String protocol) { diminishVariable(remote,understandProtocolsTag,new Value(protocol),// true); } /** * Returns a list of packages currently used by the specified remote * object. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public String[] getUsingPackages(UNL remote) { Value v = getValue(remote,usingPackagesTag); if(v != null) return v.toList(); return null; } /** * Checks, if the specified remote object uses the specified * package. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public boolean usesPackage(UNL remote, String name) { return checkElement(remote,usingPackagesTag,name); } /** * Returns a list of packages the specified remote object understands. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public String[] getUnderstandPackages(UNL remote) { Value v = getValue(remote,understandPackagesTag); if(v != null) return v.toList(); return null; } /** * Checks, if the specified remote object understands the specified * package. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public boolean understandsPackage(UNL remote, String name) { return checkElement(remote,understandPackagesTag,name); } /** * Returns the full name of the specified remote object. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public String getName(UNL remote) { Value v = getValue(remote,nameTag); if(v != null) return v.toString(); return null; } /** * **/ public void setName(UNL remote, String name) { if(name != null) assignVariable(remote,nameTag,new Value(name),true); else diminishVariable(remote,nameTag,null,true); } /** * Returns the Uniform Network Identification * of the specified remote object. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public UNI getIdentification(UNL remote) { Value v = getValue(remote,identificationTag); if(v != null) return new UNI(v.toString()); return null; } /** * **/ public void setIdentification(UNL remote, UNI identification) { if(identification != null) assignVariable(remote,identificationTag,// new Value(identification.toString()),true); else diminishVariable(remote,identificationTag,null,true); } /** * Returns a list of all Uniform Network Locations * of the specified remote object. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public UNL[] getLocations(UNL remote) { Value v = getValue(remote,locationTag); if(v != null) { String[] list = v.toList(); UNL[] locations = new UNL[list.length]; for(int i = 0;i < list.length;++i) locations[i] = new UNL(list[i]); return locations; } return null; } /** * **/ public void assignLocation(UNL remote, UNL location) { if(location != null) assignVariable(remote,locationTag,new Value(location.toString()),// true); } /** * **/ public void augmentLocation(UNL remote, UNL location) { if(location != null) augmentVariable(remote,locationTag,new Value(location.toString()),// true); } /** * **/ public void diminishLocation(UNL remote, UNL location) { diminishVariable(remote,locationTag,// location == null ? null : new Value(location.toString()),// true); } /** * **/ public UNL getObjectName(UNL remote) { Value v = getValue(remote,nameObjectTag); if(v != null) return new UNL(v.toString()); return null; } /** * Returns a Virtual Environment Description Language (VEDL) * object description of the specified remote object. * VEDL object descriptions are not supported yet. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public Object getDescription(UNL remote) { return getValue(remote,descriptionTag); } /** * **/ public void setDescription(UNL remote, Object description) { if(description != null) assignVariable(remote,descriptionTag,// new Value(description.toString()),true); else diminishVariable(remote,descriptionTag,null,true); } /** * Returns a list of platforms the specified remote object is * implemented on. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public String[] getImplementation(UNL remote) { Value v = getValue(remote,implementationTag); if(v != null) return v.toList(); return null; } /** * Returns the Nickname of the specified remote object * (Conferencing specific stuff). * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public String getNickname(UNL remote) { Value v = getValue(remote,nicknameTag); if(v != null) return v.toString(); return null; } /** * **/ public void setNickname(UNL remote, String nickname) { if(nickname != null) assignVariable(remote,nicknameTag,new Value(nickname),true); else diminishVariable(remote,nicknameTag,null,true); } /** * Returns the actual content type, the specified remote object * is using for its bodies. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public String getContentType(UNL remote) { Value v = getValue(remote,contentTag); if(v != null) return v.toString(); return null; } /** * **/ public void setContentType(UNL remote, String contentType) { if(contentType != null) assignVariable(remote,contentTag,new Value(contentType),true); else diminishVariable(remote,contentTag,null,true); } /** * Returns a currently set action of the specified remote object * (Conferencing specific stuff). * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public String getAction(UNL remote) { Value v = getValue(remote,actionTag); if(v != null) return v.toString(); return null; } /** * **/ public void setAction(UNL remote, String action) { if(action != null) assignVariable(remote,actionTag,new Value(action),true); else diminishVariable(remote,actionTag,null,true); } /** * Returns the URL where the specified remote object is described on * (Conferencing specific stuff - for example a home page). * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public URL getURLDescription(UNL remote) { Value v = getValue(remote,pageDescriptionTag); if(v != null) try { return new URL(v.toString()); } catch(MalformedURLException e) { } return null; } /** * **/ public void setURLDescription(UNL remote, URL description) { if(description != null) assignVariable(remote,pageDescriptionTag,// new Value(description.toString()),true); else diminishVariable(remote,pageDescriptionTag,null,true); } /** * Returns a human readable description of the specified remote object. * If remote is null, it returns the same for itself. * This means the Value which is sent to every new connected remote. **/ public String getHumanInformation(UNL remote) { Value v = getValue(remote,humanInformationTag); if(v != null) return v.toString(); return null; } /** * **/ public void setHumanInformation(UNL remote, String information) { if(information != null) assignVariable(remote,humanInformationTag,new Value(information),// true); else diminishVariable(remote,humanInformationTag,null,true); } /** * Returns the actual method the specified remote object has sent. * Only defined while PSYCPackage.received() is called. * If remote is null, it returns null at all. **/ public String getActualMethod(UNL remote) { if(remote == null) return null; return (String)method.get(remote); } /** * **/ public MMPPacketManager getMMPPacketManager() { return peer; } private class PacketManager implements MMPPacketManager { /** * **/ public String getGreetingBody(UNA remote, UNA local) { Vector greetingVars = new Vector(); for(Enumeration e = knownVars.keys();e.hasMoreElements();) { String name = (String)e.nextElement(); greetingVars.addElement(new VariableModifier(// VariableModifier.GLYPH_ASSIGN,name,// (Value)knownVars.get(name))); } return preparePacket(greetingMethod,greetingBody,// null,greetingVars); } /** * **/ public void error(MMPException e, UNA remote, UNA local, // MMPPacket packet) { if(e == null) return; int code = e.getErrorCode(); boolean cleanObject = false; try { if(e instanceof MMPReceiverException) { if(code == MMPReceiverException.CLOSED && remote != null) { UNL r = getObject(remote); removeAddress(remote); if(getAddress(r) != null) { // everything's fine, we can still communicate // with the object // send a null message for testing try { send(r,null,null,null); } catch(PSYCDeliveryException ex) { } return; } // whoups, we cannot communicate any more with the object cleanObject = true; } // for now, ignore every exception in case of received packets // TODO: make that better } else { UNL r = getObject(remote); // save the error for method calls gotException = new PSYCDeliveryException(// (MMPDeliveryException)e); // for now, ignore every unspecified error if(code == MMPDeliveryException.NO_ERROR_CODE_GIVEN) return; removeAddress(remote); UNA nextTry = getAddress(r); // check next address for that UNL if(nextTry != null) mmpCenter.send(nextTry,packet); else { // if there are no more UNAs, call packet manager manager.error(gotException,r); cleanObject = true; } } } finally { if(cleanObject) { UNL r = getObject(remote); for(Enumeration en = callPackagesError.keys();// en.hasMoreElements();) ((PSYCPackage)en.nextElement()).reset(r); manager.reset(r); // step through all contexts and remove if its involved for(Enumeration en = outgoingContextInvolved.elements();// en.hasMoreElements();) ((Hashtable)en.nextElement()).remove(r); globVars.remove(r); tempVars.remove(r); removeObject(r); } } } /** * **/ public void manage(MMPPacket packet) { String mmpBody = packet.getStringBody(); int mmpBodyLength = mmpBody == null ? 0 : mmpBody.length(); int i = 0; boolean gotEOL = true; String variables = null; String method = null; String body = null; char ch; // get variables, method and body try { while(i < mmpBodyLength) { ch = mmpBody.charAt(i); if(gotEOL && !VariableModifier.isGlyph(ch) && // ch != VariableModifier.VALUE_SEPARATOR) break; gotEOL = ch == VariableModifier.EOL; ++i; } variables = mmpBody.substring(0,i); int beginMethod = i; while(i < mmpBodyLength) { ch = mmpBody.charAt(i); if(ch == ' ' || ch == VariableModifier.VALUE_SEPARATOR || // ch == VariableModifier.EOL) break; ++i; } method = normalizer.normalizeMethodName(// mmpBody.substring(beginMethod,i)); body = mmpBody.substring(i + 1); } catch(StringIndexOutOfBoundsException e) { } ; if(variables != null && variables.length() <= 0) variables = null; if(method != null && method.length() <= 0) method = null; if(body != null && body.length() <= 0) body = null; UNA remote = packet.getRemote(); if(remote == null) return; Vector header = ModifierParser.parseHeader(normalizer,variables); VariableModifier modifier; char glyph; String name; Value value; boolean removeContext = false; boolean staticContext = false; String context = null; Value addresses = null; UNL source = (UNL)objects.get(remote); // first step: run through the variables and get them we need for us if(header != null) for(i = 0;i < header.size();++i) { modifier = (VariableModifier)header.elementAt(i); name = modifier.getName(); if(contextTag.equals(name)) { glyph = modifier.getGlyph(); value = modifier.getValue(); if(value == null || value.toString().length() <= 0) { if(glyph == VariableModifier.GLYPH_DIMINISH) { // handle remove on complete variable explicitely removeContext = true; context = null; } } else if(glyph == VariableModifier.GLYPH_SET || // glyph == VariableModifier.GLYPH_ASSIGN) { context = value.toString(); if(VariableModifier.isTemporary(glyph)) staticContext = false; else staticContext = true; } //header.removeElementAt(i--); continue; } if(addressesTag.equals(name)) { if(addresses == null) { // address change - load all addresses without possible // initialization of the object name addresses = new Value(); if(source != null) { // ah, we know this object already Vector addrs = (Vector)PSYCMessageCenter.this.addresses.remove(// source.withoutContext()); if(addrs != null) for(Enumeration e = addrs.elements();// e.hasMoreElements();) { UNA addr = (UNA)e.nextElement(); PSYCMessageCenter.this.objects.remove(addr); addresses.augment(addr.toString()); } } } glyph = modifier.getGlyph(); value = modifier.getValue(); if(glyph == VariableModifier.GLYPH_DIMINISH) { if(value == null || value.toString().length() <= 0) { // handle remove on complete variable explicitely addresses = new Value(); } else { // we diminish one or more addresses addresses.diminish(value); } } else if(glyph == VariableModifier.GLYPH_ASSIGN) { addresses = value; } else if(glyph == VariableModifier.GLYPH_AUGMENT) { addresses.augment(value.toString()); } // ignore temporarily set addresses //header.removeElementAt(i--); continue; } } if(addresses != null) { // we got an addresses tag String[] list = addresses.toList(); Vector addrs = new Vector(); if(list.length > 0) { if(source == null) source = new UNL(list[0]); for(i = 0;i < list.length;++i) addrs.addElement(new UNA(list[i])); } // (re)initialize the mapping addrs.insertElementAt(remote,0); if(source == null) source = new UNL(remote); initMapping(source,addrs); } source = getObject(remote); if(removeContext) incomingContexts.remove(source); if(context == null) context = (String)incomingContexts.get(source); else if(staticContext) incomingContexts.put(source,context); if(context != null) source = source.addContext(context); Hashtable packages; Hashtable globVars = // (Hashtable)PSYCMessageCenter.this.globVars.get(source); Hashtable tempVars = // (Hashtable)PSYCMessageCenter.this.tempVars.get(source); if(globVars == null) PSYCMessageCenter.this.globVars.put(// source,globVars = new Hashtable()); if(tempVars == null) PSYCMessageCenter.this.tempVars.put(// source,tempVars = new Hashtable()); synchronized(globVars) { synchronized(tempVars) { tempVars.clear(); if(header != null && header.size() > 0) { for(Enumeration e = header.elements();// e.hasMoreElements();) { modifier = (VariableModifier)e.nextElement(); glyph = modifier.getGlyph(); name = modifier.getName(); value = modifier.getValue(); do { if(value == null || value.toString().length() <= 0) if(glyph == VariableModifier.GLYPH_DIMINISH) { // handle remove on complete variable explicitely tempVars.remove(name); globVars.remove(name); continue; } else if(value == null) value = new Value(); if(glyph == VariableModifier.GLYPH_SET) tempVars.put(name,value); else if(glyph == VariableModifier.GLYPH_ASSIGN) { if(globVars.size() < maxVariables || // maxVariables < 1) { globVars.put(name,value); } else { //TODO: generate some error } } else if(glyph == VariableModifier.GLYPH_AUGMENT || // glyph == VariableModifier.GLYPH_DIMINISH) { Value oldVal = getValue(source,name); if(oldVal == null) { if(globVars.size() < maxVariables || // maxVariables < 1) { globVars.put(name,oldVal = new Value()); } else { //TODO: generate some error } } oldVal.handle(glyph,value.toString()); } else if(glyph == VariableModifier.GLYPH_QUERY) { Value ourValue = getValue(null,name); if(ourValue != null) assignVariable(source,name,ourValue,true); } } while(false); if((packages = // (Hashtable)callPackagesVariable.get(name)) != null) for(Enumeration ee = packages.keys();// ee.hasMoreElements();) ((PSYCPackage)ee.nextElement()).// variableChanged(source,modifier); } } PSYCPacket p = new PSYCPacket(source,header,method,body); if(p.isEmpty()) return; // call packages if(method != null) { PSYCMessageCenter.this.method.put(source,method); for(Enumeration e = callPackagesMethod.keys();// e.hasMoreElements();) { name = (String)e.nextElement(); // check if method is equal or submethod // of the registered method if(method.regionMatches(0,name,0,name.length()) && (// method.length() == name.length() || // method.charAt(name.length()) == // METHOD_HIERARCHY_DELIMITER)) { packages = (Hashtable)callPackagesMethod.get(name); for(Enumeration ee = packages.keys();// ee.hasMoreElements();) // send them the registered method name not the real ((PSYCPackage)ee.nextElement()).received(source,// name,body); } } PSYCMessageCenter.this.method.remove(source); } // call packet manager manager.manage(p); } } } } /** * Creates the specified outgoing context. **/ public void createContext(String context) { if(context == null) return; if(!isContext(context)) { outgoingContexts.put(context,new Hashtable()); outgoingContextInvolved.put(context,new Hashtable()); } } /** * Checks, if the specified String describes an existing * outgoing context. **/ public boolean isContext(String context) { if(context == null) return false; return outgoingContexts.get(context) != null; } /** * Removes the specified outgoing context. **/ public void removeContext(String context) { outgoingContexts.remove(context); outgoingContextInvolved.remove(context); } /** * Adds the specified remote object to the specified outgoing context. **/ public UNL addToContext(String context, UNL remote) { if(context == null || remote == null) return null; remote = remote.addContext(context); addToContext(remote); return remote; } /** * Adds the specified remote object to an implicite given context.
* Note: UNLs may contain contexts. **/ public void addToContext(UNL remote) { if(remote == null) return; String context = remote.getContext(); if(context == null) return; Hashtable hash = (Hashtable)outgoingContextInvolved.get(context); if(hash == null) { createContext(context); hash = (Hashtable)outgoingContextInvolved.get(context); } if(hash.get(remote.withoutContext()) == null) { // a new involved peer hash.put(remote.withoutContext(),remote); // send em the complete context hash = (Hashtable)outgoingContexts.get(context); String name; for(Enumeration e = hash.keys();e.hasMoreElements();) { name = (String)e.nextElement(); assignVariable(remote,name,(Value)hash.get(name),false); } } return; } /** * Removes the specified remote object from the specified outgoing context. **/ public void removeFromContext(String context, UNL remote) { if(context == null || remote == null) return; remote = remote.addContext(context); removeFromContext(remote); } /** * Removes the specified remote object from the implicite given context.
* Note: UNLs may contain contexts. **/ public void removeFromContext(UNL remote) { if(remote == null) return; String context = remote.getContext(); if(context == null) return; Hashtable hash = (Hashtable)outgoingContextInvolved.get(context); if(hash == null) return; hash.remove(remote.withoutContext()); } /** * Assigns the given Value to the specified variable for the * specified outgoing context. **/ public void assignVariable(String context, String name, Value value, // boolean immediately) { if(context == null || name == null) return; // get all already connected remotes, assign that there Hashtable hash = (Hashtable)outgoingContextInvolved.get(context); if(hash == null) { createContext(context); hash = (Hashtable)outgoingContextInvolved.get(context); } for(Enumeration e = hash.elements();e.hasMoreElements();) assignVariable((UNL)e.nextElement(),name,value,immediately); // assign it to our context-store too for further connected remotes hash = (Hashtable)outgoingContexts.get(context); hash.put(name,value); } /** * Augments the given Value to the specified variable for the * specified outgoing context. **/ public void augmentVariable(String context, String name, Value value, // boolean immediately) { if(context == null || name == null) return; // get all already connected remotes, augment that there Hashtable hash = (Hashtable)outgoingContextInvolved.get(context); if(hash == null) { createContext(context); hash = (Hashtable)outgoingContextInvolved.get(context); } for(Enumeration e = hash.elements();e.hasMoreElements();) augmentVariable((UNL)e.nextElement(),name,value,immediately); // augment it to our context-store too for further connected remotes hash = (Hashtable)outgoingContexts.get(context); Value v = (Value)hash.get(name); if(v == null) v = new Value(); v.augment(value.toString()); } /** * Diminishes the given Value from the specified variable for the * specified outgoing context. **/ public void diminishVariable(String context, String name, Value value, // boolean immediately) { if(context == null || name == null) return; // get all already connected remotes, diminish that there Hashtable hash = (Hashtable)outgoingContextInvolved.get(context); if(hash == null) { createContext(context); hash = (Hashtable)outgoingContextInvolved.get(context); } for(Enumeration e = hash.elements();e.hasMoreElements();) diminishVariable((UNL)e.nextElement(),name,value,immediately); // diminish it to our context-store too for further connected remotes hash = (Hashtable)outgoingContexts.get(context); Value v = (Value)hash.get(name); if(v == null) return; v.diminish(value); if(v.toString().length() <= 0) hash.remove(name); } /** * Removes the specified variable from the variable buffer for * the specified outgoing context. It does not tell any peer about that. * That means, that new peers don't get this variable any more. **/ public void removeVariable(String context, String name) { if(context == null || name == null) return; Hashtable hash = (Hashtable)outgoingContexts.get(context); if(hash == null) return; hash.remove(name); } /** * **/ private void setVariable(UNL remote, VariableModifier modifier, // boolean immediately) { if(modifier == null) return; if(remote == null) { // broadcast // set it to our known variables Value value = (Value)knownVars.get(modifier.getName()); Value v = modifier.getValue(); if(value == null) { value = new Value(); knownVars.put(modifier.getName(),value); } if(v == null) v = new Value(); value.handle(modifier.getGlyph(),v.toString()); if(value.toString().length() <= 0) { knownVars.remove(modifier.getName()); modifier = new VariableModifier(modifier.getGlyph(),// modifier.getName(),null); } // and apply it to all open connections for(Enumeration e = addresses.keys();e.hasMoreElements();) setVariable((UNL)e.nextElement(),modifier,immediately); } else { // unicast Vector modifiers = (Vector)outgoings.get(remote); if(modifiers == null) { modifiers = new Vector(); outgoings.put(remote,modifiers); } modifiers.addElement(modifier); if(immediately) { String prefix = null; switch(modifier.getGlyph()) { case VariableModifier.GLYPH_SET: prefix = setPrefix; break; case VariableModifier.GLYPH_ASSIGN: prefix = assignPrefix; break; case VariableModifier.GLYPH_AUGMENT: prefix = augmentPrefix; break; case VariableModifier.GLYPH_DIMINISH: prefix = diminishPrefix; break; } if(prefix != null) try { send(remote,prefix + modifier.getName(),null,null); } catch(PSYCDeliveryException e) { } } } } /** * Assigns the specified variable for the specified remote object with * the specified Value. * If the remote is null, it does the same for itself. * This means, it will be sent to every new connected remote object. **/ public void assignVariable(UNL remote, String name, Value value, // boolean immediately) { setVariable(remote,// new VariableModifier(VariableModifier.GLYPH_ASSIGN,name,value),// immediately); } /** * Augments the specified variable for the specified remote object with * the specified Value. * If the remote is null, it does the same for itself. * This means, it will be sent to every new connected remote object. **/ public void augmentVariable(UNL remote, String name, Value value, // boolean immediately) { setVariable(remote,// new VariableModifier(VariableModifier.GLYPH_AUGMENT,name,value),// immediately); } /** * Diminishes the specified variable for the specified remote object with * the specified Value. * If the remote is null, it does the same for itself. **/ public void diminishVariable(UNL remote, String name, Value value, // boolean immediately) { setVariable(remote,// new VariableModifier(VariableModifier.GLYPH_DIMINISH,name,value),// immediately); } /** * Queries the specified variable for the specified remote object. * If the remote is null, it does the same for itself. * This means, it will be sent to every new connected remote object. **/ public void queryVariable(UNL remote, String name) // throws PSYCDeliveryException { setVariable(remote,// new VariableModifier(VariableModifier.GLYPH_QUERY,name,null),true); } /** * **/ private String preparePacket(String method, String body, // Hashtable tempVars, Vector globVars) { StringBuffer packet = new StringBuffer(); packet.append(ModifierParser.writeHeader(globVars)); packet.append(ModifierParser.writeHeader(tempVars)); if(globVars != null) globVars.removeAllElements(); if(method != null) packet.append(method); if(body != null) { packet.append(VariableModifier.EOL); packet.append(body); } else if(packet.length() > 0 && // packet.charAt(packet.length() - 1) == VariableModifier.EOL) packet.setLength(packet.length() - 1); return packet.toString(); } /** * Sends the specified message (method and body) to the specified * remote object and sets temporarily for this message the * specified variables. tempVars contains a Map of String variableName * to Value variableValue. **/ public void send(UNL remote, String method, String body, // Hashtable tempVars) throws PSYCDeliveryException { if(remote == null) throw new PSYCDeliveryException(// PSYCDeliveryException.NO_TARGET); if(remote.getContext() != null) { if(tempVars == null) tempVars = new Hashtable(); tempVars.put(contextTag,remote.getContext()); } String packet = preparePacket(method,body,// tempVars,(Vector)outgoings.get(remote)); synchronized(this) { gotException = null; mmpCenter.send(getAddress(remote),new MMPPacket(null,packet)); if(gotException != null) throw gotException; } } /** * Sends the specified message (method and body) to the specified * context and sets temporarily for this message the * specified variables. tempVars contains a Map of String variableName * to Value variableValue.
* It ignores errors if they occure in sending to single context members. **/ public void send(String context, String method, String body, // Hashtable tempVars) { if(context == null) return; Hashtable hash = (Hashtable)outgoingContextInvolved.get(context); if(hash == null) return; for(Enumeration e = hash.elements();e.hasMoreElements();) try { send((UNL)e.nextElement(),method,body,tempVars); } catch(PSYCDeliveryException ex) { } } /** * Sends the specified message (method and body) to the specified * remote object. **/ public void send(UNL remote, String method, String body) // throws PSYCDeliveryException { send(remote,method,body,null); } /** * Sends the specified message (method and body) to the specified * context.
* It ignores errors if they occure in sending to single context members. **/ public void send(String context, String method, String body) { if(context == null) return; Hashtable hash = (Hashtable)outgoingContextInvolved.get(context); if(hash == null) return; for(Enumeration e = hash.elements();e.hasMoreElements();) try { send((UNL)e.nextElement(),method,body); } catch(PSYCDeliveryException ex) { } } /** * Sends the specified message (method, body and temporarily set variables) * to the specified remote object but only, if the object supports the * specified package. **/ public void sendChecked(UNL remote, String packageName, // String method, String body, Hashtable tempVars) // throws PSYCDeliveryException { if(!understandsPackage(remote,packageName)) throw new PSYCDeliveryException(PSYCDeliveryException.NOT_SUPPORTED); send(remote,method,body,tempVars); } /** * Sends the specified message (method and body) to the specified remote * object but only, if the object supports the specified package. **/ public void sendChecked(UNL remote, String packageName, // String method, String body) throws PSYCDeliveryException { sendChecked(remote,packageName,method,body,null); } }