diff --git a/src/baseutil/com/silverwrist/util/xml/XMLLoader.java b/src/baseutil/com/silverwrist/util/xml/XMLLoader.java index 9886ba6..8b1a81a 100644 --- a/src/baseutil/com/silverwrist/util/xml/XMLLoader.java +++ b/src/baseutil/com/silverwrist/util/xml/XMLLoader.java @@ -341,6 +341,33 @@ public class XMLLoader } // end getSubElementNS + /** + * Returns the sole sub-element of the specified element. Throws an error if the element has more than one + * sub-element. + * + * @param sect The Element to extract the sub-element from. + * @return The sub-element. + * @exception com.silverwrist.util.XMLLoadException If there is more than one sub-element. + */ + public final Element getSoleSubElement(Element sect) throws XMLLoadException + { + Element rc = null; + NodeList nl = sect.getChildNodes(); + for (int i=0; i found"); + rc = (Element)n; + + } // end for + + return rc; + + } // end getSoleSubElement + /** * Returns all sub-elements of an Element element that have a specific name. * diff --git a/src/dynamo-framework/com/silverwrist/dynamo/app/ApplicationContainer.java b/src/dynamo-framework/com/silverwrist/dynamo/app/ApplicationContainer.java index a2c0f61..34ce94e 100644 --- a/src/dynamo-framework/com/silverwrist/dynamo/app/ApplicationContainer.java +++ b/src/dynamo-framework/com/silverwrist/dynamo/app/ApplicationContainer.java @@ -1146,6 +1146,12 @@ public class ApplicationContainer } // end removeSessionLink + public String getServerIdentity() + { + return m_identity; + + } // end getServerIdentity + public void setServerHeader(HttpServletResponse resp) { resp.setHeader("Server",m_identity); diff --git a/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcCaller.java b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcCaller.java new file mode 100644 index 0000000..aef88d5 --- /dev/null +++ b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcCaller.java @@ -0,0 +1,32 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2003 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.dynamo.xmlrpc; + +import java.io.IOException; +import java.net.URL; +import java.util.List; + +public interface XmlRpcCaller +{ + public URL getURL(); + + public Object call(String method, Object[] params) throws IOException, FaultCode; + + public Object call(String method, List params) throws IOException, FaultCode; + +} // end interface XmlRpcCaller diff --git a/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcCallerFactory.java b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcCallerFactory.java new file mode 100644 index 0000000..293d165 --- /dev/null +++ b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcCallerFactory.java @@ -0,0 +1,31 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2003 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.dynamo.xmlrpc; + +import java.net.MalformedURLException; +import java.net.URL; + +public interface XmlRpcCallerFactory +{ + public XmlRpcCaller newCaller(URL url); + + public XmlRpcCaller newCaller(String url) throws MalformedURLException; + + public XmlRpcCaller newCaller(String hostname, int port) throws MalformedURLException; + +} // end interface XmlRpcCallerFactory diff --git a/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcCallerImpl.java b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcCallerImpl.java new file mode 100644 index 0000000..7ff5b62 --- /dev/null +++ b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcCallerImpl.java @@ -0,0 +1,247 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2003 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.dynamo.xmlrpc; + +import java.io.*; +import java.net.*; +import java.util.*; +import org.w3c.dom.*; +import com.silverwrist.util.*; +import com.silverwrist.util.xml.*; + +class XmlRpcCallerImpl implements XmlRpcCaller +{ + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private URL m_url; + private String m_identifier; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + XmlRpcCallerImpl(URL url, String identifier) + { + m_url = url; + m_identifier = identifier; + + } // end constructor + + /*-------------------------------------------------------------------------------- + * Internal operations + *-------------------------------------------------------------------------------- + */ + + private final byte[] buildCall(String method, List params) throws IOException + { + ByteArrayOutputStream stm = new ByteArrayOutputStream(); + BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(stm,"UTF-8")); + wr.write("\r\n\r\n"); + wr.write(method); + wr.write("\r\n"); + if (params.size()>0) + { // serialize the parameters + XmlRpcSerializer serializer = XmlRpcSerializer.get(); + wr.write("\r\n"); + Iterator it = params.iterator(); + while (it.hasNext()) + { // serialize the parameters + Object p = it.next(); + wr.write(""); + serializer.serialize(wr,p); + wr.write("\r\n"); + + } // end while + + wr.write("\r\n"); + + } // end if + + wr.write("\r\n"); + wr.flush(); + return stm.toByteArray(); + + } // end buildCall + + private final Object deserialize(Element val) throws IOException + { + XmlRpcSerializer serializer = XmlRpcSerializer.get(); + try + { // just call the serializer and translate its FaultCode if any + return serializer.deserialize(val); + + } // end try + catch (FaultCode fc) + { // Build the IOException and return it. + IOException ioe = new IOException("Value parsing error: " + fc.getMessage()); + ioe.initCause(fc); + throw ioe; + + } // end catch + + } // end deserialize + + private final Object parseResult(InputStream stm) throws IOException, FaultCode + { + XMLLoader loader = XMLLoader.get(); + try + { // parse the output from the server + Document result_doc = loader.load(stm,false); + + // make sure it's a proper method response + Element method_response = loader.getRootElement(result_doc,"methodResponse"); + + // get the sub-element of the methodResponse (there can be only one!) + Element elt = loader.getSoleSubElement(method_response); + if (elt.getTagName().equals("params")) + { // must contain , which must contain + Element param1 = loader.getSoleSubElement(elt); + loader.verifyNodeName(param1,"param",elt); + Element val1 = loader.getSoleSubElement(param1); + loader.verifyNodeName(val1,"value",param1); + + // deserialize the value + return deserialize(val1); + + } // end if + else if (elt.getTagName().equals("fault")) + { // must contain + Element val1 = loader.getSoleSubElement(elt); + loader.verifyNodeName(val1,"value",elt); + + // get the fault value + Map fcdata = null; + try + { // parse the value... + fcdata = (Map)deserialize(val1); + + } // end try + catch (ClassCastException e) + { // turn this into an IOException + throw new IOException("Fault parse failure: fault data must be struct"); + + } // end catch + + // get the fault code + int fault_code = 0; + try + { // retrieve struct member, convert to integer + Integer foo = (Integer)(fcdata.get("faultCode")); + if (foo==null) + throw new IOException("Fault parse failure: fault code member must be present"); + fault_code = foo.intValue(); + + } // end try + catch (ClassCastException e) + { // no go + throw new IOException("Fault parse failure: fault code member must be integer"); + + } // end catch + + // get the fault string + String fault_string = null; + try + { // retrieve struct member + fault_string = (String)(fcdata.get("faultString")); + if (fault_string==null) + throw new IOException("Fault parse failure: fault string member must be present"); + + } // end try + catch (ClassCastException e) + { // no go + throw new IOException("Fault parse failure: fault string member must be string"); + + } // end catch + + // throw the resulting FaultCode object + if (FaultCode.isSystemFaultCode(fault_code)) + throw new SystemFaultCode(fault_code,fault_string); + else + throw new FaultCode(fault_code,fault_string); + + } // end else if + else + throw new XMLLoadException(" must contain either or ",method_response); + + } // end try + catch (XMLLoadException e) + { // translate all XML loading exceptions to I/O exceptions + IOException ioe = new IOException("XML parsing failure: " + e.getMessage()); + ioe.initCause(e); + throw ioe; + + } // end catch + + } // end parseResult + + /*-------------------------------------------------------------------------------- + * Implementations from interface XmlRpcCaller + *-------------------------------------------------------------------------------- + */ + + public URL getURL() + { + return m_url; + + } // end URL + + public Object call(String method, Object[] params) throws IOException, FaultCode + { + if (params==null) + return this.call(method,Collections.EMPTY_LIST); + else + return this.call(method,Arrays.asList(params)); + + } // end call + + public Object call(String method, List params) throws IOException, FaultCode + { + if (params==null) + params = Collections.EMPTY_LIST; + + // Build the XML-RPC call body. + byte[] request_data = buildCall(method,params); + + // Set up a URLConnection to post it. + URLConnection conn = m_url.openConnection(); + conn.setDoInput(true); + conn.setDoOutput(true); + conn.setAllowUserInteraction(false); + conn.setUseCaches(false); + conn.setRequestProperty("User-Agent",m_identifier); + conn.setRequestProperty("Host",m_url.getHost()); + conn.setRequestProperty("Content-Type","text/xml; charset=UTF-8"); + conn.setRequestProperty("Content-Length",String.valueOf(request_data.length)); + + // Post the request data, and get the response data back. + OutputStream ostm = conn.getOutputStream(); + ostm.write(request_data); + ostm.flush(); + ostm.close(); + InputStream istm = conn.getInputStream(); + + // Parse the XML-RPC response and return the return value, or throw the fault code. + return parseResult(istm); + + } // end call + +} // end class XmlRpcCallerImpl diff --git a/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcSubSystem.java b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcSubSystem.java index 31b2554..f30f549 100644 --- a/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcSubSystem.java +++ b/src/dynamo-framework/com/silverwrist/dynamo/xmlrpc/XmlRpcSubSystem.java @@ -17,6 +17,8 @@ */ package com.silverwrist.dynamo.xmlrpc; +import java.net.MalformedURLException; +import java.net.URL; import java.util.*; import org.apache.commons.lang.CharSet; import org.apache.commons.lang.CharSetUtils; @@ -30,7 +32,8 @@ import com.silverwrist.dynamo.iface.*; import com.silverwrist.dynamo.util.*; public class XmlRpcSubSystem - implements NamedObject, ComponentInitialize, ComponentShutdown, XmlRpcCreateSession, XmlRpcConfiguration + implements NamedObject, ComponentInitialize, ComponentShutdown, XmlRpcCreateSession, XmlRpcConfiguration, + XmlRpcCallerFactory { /*-------------------------------------------------------------------------------- * Internal class implementing background sweep @@ -119,6 +122,7 @@ public class XmlRpcSubSystem private Vector m_installed_fmap = new Vector(); // fault mappers installed via the API private ArrayList m_config_fmap = new ArrayList(); // fault mappers configured via the config data private ComponentShutdown m_init_hook; // initialization services hook + private ComponentShutdown m_script; // script object registration hook private ComponentShutdown m_sweep_task; // sweep task /*-------------------------------------------------------------------------------- @@ -571,6 +575,10 @@ public class XmlRpcSubSystem (XmlRpcConfiguration)this); m_init_hook = hooker.hookInitServiceProvider(ssp); + // Add the XML-RPC caller factory to the script engine under the name "xmlrpc." + ScriptEngineConfig seconf = (ScriptEngineConfig)(services.queryService(ScriptEngineConfig.class)); + m_script = seconf.addDeclaredObject("xmlrpc",this,XmlRpcCallerFactory.class); + // Set up the sweeper to run at a fixed rate. BackgroundScheduler sched = (BackgroundScheduler)(services.queryService(BackgroundScheduler.class)); m_sweep_task = sched.runTaskFixedRate(new SessionSweeper(),SWEEP_INTERVAL,SWEEP_INTERVAL); @@ -590,6 +598,9 @@ public class XmlRpcSubSystem m_sweep_task.shutdown(); // shut down the sweep task m_sweep_task = null; + m_script.shutdown(); // shut down the script object + m_script = null; + m_init_hook.shutdown(); // shut down the initialization hook m_init_hook = null; @@ -661,6 +672,29 @@ public class XmlRpcSubSystem } // end addCapability + /*-------------------------------------------------------------------------------- + * Implementations from interface XmlRpcCallerFactory + *-------------------------------------------------------------------------------- + */ + + public XmlRpcCaller newCaller(URL url) + { + return new XmlRpcCallerImpl(url,m_appcon.getServerIdentity()); + + } // end newCaller + + public XmlRpcCaller newCaller(String url) throws MalformedURLException + { + return this.newCaller(new URL(url)); + + } // end newCaller + + public XmlRpcCaller newCaller(String hostname, int port) throws MalformedURLException + { + return this.newCaller(new URL("http://" + hostname + ":" + port + "/RPC2")); + + } // end newCaller + /*-------------------------------------------------------------------------------- * External operations *--------------------------------------------------------------------------------