From aedc34d55462a75e329bbf342251ff6504cd117e Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Sun, 19 May 2024 17:56:33 -0400 Subject: Initial import from SVN --- .../java/net/wotonomy/web/xml/XMLRPCDecoder.java | 114 +++++ .../net/wotonomy/web/xml/XMLRPCDecoderHelper.java | 534 +++++++++++++++++++++ .../java/net/wotonomy/web/xml/XMLRPCEncoder.java | 526 ++++++++++++++++++++ .../java/net/wotonomy/web/xml/XMLRPCReceiver.java | 72 +++ .../java/net/wotonomy/web/xml/XMLRPCSelector.java | 238 +++++++++ .../java/net/wotonomy/web/xml/XMLRPCServlet.java | 283 +++++++++++ .../main/java/net/wotonomy/web/xml/package.html | 30 ++ 7 files changed, 1797 insertions(+) create mode 100644 projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCDecoder.java create mode 100644 projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCDecoderHelper.java create mode 100644 projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCEncoder.java create mode 100644 projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCReceiver.java create mode 100644 projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCSelector.java create mode 100644 projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCServlet.java create mode 100644 projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/package.html (limited to 'projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml') diff --git a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCDecoder.java b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCDecoder.java new file mode 100644 index 0000000..8da71fe --- /dev/null +++ b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCDecoder.java @@ -0,0 +1,114 @@ +/* +Wotonomy: OpenStep design patterns for pure Java applications. +Copyright (C) 2000 Intersect Software Corporation + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, see http://www.gnu.org +*/ + +package net.wotonomy.web.xml; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import net.wotonomy.foundation.internal.WotonomyException; +import net.wotonomy.foundation.xml.XMLDecoder; + +import org.xml.sax.SAXException; + +/** +* An implementation of XMLDecoder that reads objects from +* XMLRPC format. +* This implementation is not thread-safe, so a new instances +* should be created to accomodate multiple threads. +*/ +public class XMLRPCDecoder implements XMLDecoder +{ + XMLRPCDecoderHelper helper = new XMLRPCDecoderHelper(); + + /** + * Decodes an object in XML-RPC format from the specified input stream. + * @param anInputStream The input stream from which to read. + * The stream will be read fully. + * @param aDescription A description to accompany error messages + * for the stream, typically a file name. + * @param aURL A URL against which relative references within the + * XML will be resolved. + * @return The object that was constructed from the XML content, + * or null if no object could be constructed. + */ + public Object decode( + InputStream anInputStream, String aDescription, URL aURL ) + { + Object result = null; + + try { + SAXParser sparser = SAXParserFactory.newInstance().newSAXParser(); + sparser.parse(anInputStream,helper,aURL.toExternalForm()); + result = helper.getResult(); + } catch (ParserConfigurationException e) { + throw new WotonomyException("Problem in parser configuration", e ); + } catch (IOException e) { + throw new WotonomyException("IOException thrown while parsing", e ); + } catch (SAXException e) { + throw new WotonomyException("SAXException thrown while parsing", e ); + } + helper.reset(); + return result; + } + + /** + * Decodes an XML-RPC message from the specified input stream. + * Stand-alone values not wrapped in "methodCall" or "param" + * tags will be treated as a response. + * @param anInputStream The input stream from which to read. + * The stream will be read fully. + * @param aReceiver an XMLRPCReceiver that will be invoked with + * the appropriate method: request, response, or fault. + */ + public void decode( + InputStream anInputStream, XMLRPCReceiver aReceiver ) + { + try + { + SAXParser sparser = SAXParserFactory.newInstance().newSAXParser(); + sparser.parse(anInputStream,helper); + + if ( helper.isRequest() ) + { + aReceiver.request( helper.getMethodName(), helper.getParameters() ); + } + else + if ( helper.isFault() ) + { + aReceiver.fault( helper.getFaultCode(), helper.getFaultString() ); + } + else // all else is considered a response + { + aReceiver.response( helper.getResult() ); + } + } catch (ParserConfigurationException e) { + throw new WotonomyException("Problem in parser configuration", e ); + } catch (IOException e) { + throw new WotonomyException("IOException thrown while parsing", e ); + } catch (SAXException e) { + throw new WotonomyException("SAXException thrown while parsing", e ); + } + helper.reset(); + } +} diff --git a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCDecoderHelper.java b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCDecoderHelper.java new file mode 100644 index 0000000..2368672 --- /dev/null +++ b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCDecoderHelper.java @@ -0,0 +1,534 @@ +/* +Wotonomy: OpenStep design patterns for pure Java applications. +Copyright (C) 2000 Michael Powers + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, see http://www.gnu.org +*/ + +package net.wotonomy.web.xml; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import net.wotonomy.foundation.internal.Introspector; +import net.wotonomy.foundation.internal.ValueConverter; +import net.wotonomy.foundation.internal.WotonomyException; + +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +/** +* Used by XMLDecoder to implement the necessary interfaces +* required by the jclark xp parser. +* This class is not thread safe. +*/ +class XMLRPCDecoderHelper extends DefaultHandler +{ + protected final Object nilMarker = new Object(); + protected Stack valueStack; + protected String methodName; + protected int faultCode; + protected String faultString; + protected List parameters; + protected StringBuffer cdataBuffer; + + + + public XMLRPCDecoderHelper() + { + valueStack = new Stack(); + parameters = new LinkedList(); + cdataBuffer = new StringBuffer(); + reset(); + } + + public void reset() + { + valueStack.clear(); + parameters.clear(); + cdataBuffer.setLength( 0 ); + methodName = null; + faultCode = 0; + faultString = null; + } + + public boolean isRequest() + { + return ( methodName != null ); + } + + public boolean isResponse() + { + return ( methodName == null ); + } + + public boolean isFault() + { + // faults are responses + return ( isResponse() ) && ( faultString != null ); + } + + public int getFaultCode() + { + return faultCode; + } + + public String getFaultString() + { + return faultString; + } + + public String getMethodName() + { + return methodName; + } + + public Object[] getParameters() + { + return parameters.toArray(); + } + + public void endDocument() throws SAXException { + // TODO Auto-generated method stub + super.endDocument(); + } + + public void startDocument() throws SAXException { + // TODO Auto-generated method stub + super.startDocument(); + reset(); + } + + public Object getResult() + { + if ( valueStack.empty() ) return null; + Object result = valueStack.peek(); + if ( result == nilMarker ) result = null; + return result; + } + + + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + super.startElement(uri, localName, qName, attributes); + if ( XMLRPCEncoder.VALUE.equals( localName ) ) + { + ValueMarker marker = new ValueMarker(); + String classname = attributes.getValue( uri, XMLRPCEncoder.CLASS ); + if ( classname != null ) + { + try + { + Class c = Class.forName( classname ); + if ( c != null ) + { + marker.setMarkerClass( c ); + } + } + catch ( Exception exc ) + { + System.out.println( "XMLRPCDecoderHelper.startElement: " + + "Can't find class: " + classname ); + } + } + valueStack.push( marker ); + } + } + + public void characters(char[] ch, int start, int length) throws SAXException { + super.characters(ch, start, length); + char[] someChars = new char[((start+length)<=ch.length) ? length : ch.length-start]; + for (int i = 0; i < someChars.length; i++ ) { + someChars[i] = ch[start+i]; + } + cdataBuffer.append(someChars); + } + + public void endElement(String uri, String localName, String qName) throws SAXException { + super.endElement(uri, localName, qName); + + // if any cdata is buffered or if string value + if ( ( XMLRPCEncoder.STRING.equals( localName ) ) + || ( cdataBuffer.length() > 0 ) ) + { + // push value on the stack + valueStack.push( cdataBuffer.toString() ); + cdataBuffer.setLength( 0 ); + } + + if ( XMLRPCEncoder.VALUE.equals( localName ) ) + { + Object value = valueStack.pop(); + try + { +// ValueMarker marker = (ValueMarker) valueStack.pop(); + ValueMarker marker = null; + Object markerValue = valueStack.pop(); + if ( markerValue instanceof ValueMarker ) + { + marker = (ValueMarker) markerValue; + } + else + { + throw new WotonomyException( "Expected value marker, found" + + markerValue.getClass() + " : " + markerValue ); + } + +//System.out.println( "getMarkerClass: " + marker.getMarkerClass() + " : " + value ); +//System.out.println( valueStack ); + if ( marker.getMarkerClass() != null ) + { + // apply introspection + if ( value instanceof Map ) + { + Map map = (Map)value; + Map.Entry entry; + value = marker.getMarkerClass().newInstance(); + Iterator it = map.entrySet().iterator(); + Object entryValue; + while( it.hasNext() ) + { + entry = (Map.Entry) it.next(); + entryValue = entry.getValue(); + if ( entryValue == nilMarker ) entryValue = null; + Introspector.set( + value, entry.getKey().toString(), entryValue ); + } + } + if ( ! ( value.getClass().equals( marker.getMarkerClass() ) ) ) + { + Object converted = + ValueConverter.convertObjectToClass( + value, marker.getMarkerClass() ); + if ( converted != null ) + { + value = converted; + } + } + } + } + catch ( Exception exc ) + { + // fall back on unconverted value + } + + valueStack.push( value ); +// System.out.println( "convertedValue: " + value + "("+ value.getClass() +")" ); + } + else + if ( XMLRPCEncoder.MEMBER.equals( localName ) ) // Map.Entry + { + // leave key and value to be handled by struct + } + else + if ( XMLRPCEncoder.STRUCT.equals( localName ) ) // write Entries to map or object + { + // write values to array (reverse the order) + Object value; + Map map = new HashMap(); + while ( ( ! valueStack.empty() ) + && ( ! ( valueStack.peek() instanceof ValueMarker ) ) ) + { + value = valueStack.pop(); + map.put( valueStack.pop(), value ); + } + // push the list on the stack + valueStack.push( map ); + } + else + if ( XMLRPCEncoder.ARRAY.equals( localName ) ) + { +//System.out.println( "ended ARRAY: " + valueStack.size() ); + // write values to array (reverse the order) + Object value; + LinkedList list = new LinkedList(); + while ( ( ! valueStack.empty() ) + && ( ! ( valueStack.peek() instanceof ValueMarker ) ) ) + { + value = valueStack.pop(); + if ( value == nilMarker ) value = null; + list.addFirst( value ); + } + // push the list on the stack + valueStack.push( list ); + } + else + if ( XMLRPCEncoder.INT.equals( localName ) ) + { + Object value = valueStack.pop(); + try + { + valueStack.push( + new Integer( value.toString() ) ); + } + catch ( NumberFormatException exc ) + { + throw new WotonomyException( + "Invalid double format: " + value.toString() ); + } + } + else + if ( XMLRPCEncoder.I4.equals( localName ) ) + { + Object value = valueStack.pop(); + try + { + valueStack.push( + new Integer( value.toString() ) ); + } + catch ( NumberFormatException exc ) + { + throw new WotonomyException( + "Invalid double format: " + value.toString() ); + } + } + else + if ( XMLRPCEncoder.NIL.equals( localName ) ) + { + valueStack.push( nilMarker ); + } + else + if ( XMLRPCEncoder.DOUBLE.equals( localName ) ) + { + Object value = valueStack.pop(); + try + { + valueStack.push( + new Double( value.toString() ) ); + } + catch ( NumberFormatException exc ) + { + throw new WotonomyException( + "Invalid double format: " + value.toString() ); + } + } + else + if ( XMLRPCEncoder.DATE.equals( localName ) ) + { + Object value = valueStack.pop(); + try + { + valueStack.push( + XMLRPCEncoder.DATEFORMAT8601.parseObject( + value.toString() ) ); + } + catch ( Exception exc ) + { + throw new WotonomyException( + "Invalid date format: " + value ); + } + } + else + if ( XMLRPCEncoder.BOOLEAN.equals( localName ) ) + { + Object value = valueStack.pop(); + if ( XMLRPCEncoder.TRUE.equals( value ) ) + { + valueStack.push( Boolean.TRUE ); + } + else + if ( XMLRPCEncoder.FALSE.equals( value ) ) + { + valueStack.push( Boolean.FALSE ); + } + else + { + throw new WotonomyException( + "Invalid boolean format: " + value ); + } + } + else + if ( XMLRPCEncoder.BASE64.equals( localName ) ) + { + throw new WotonomyException( "Not implemented yet." ); + } + else + if ( XMLRPCEncoder.FAULT.equals( localName ) ) + { + Map faultMap = (Map) valueStack.pop(); + try + { + faultCode = ((Integer) + faultMap.get( XMLRPCEncoder.FAULTCODE )).intValue(); + faultString = (String) faultMap.get( XMLRPCEncoder.FAULTSTRING ); + } + catch ( Exception exc ) + { + throw new WotonomyException( + "Invalid fault format: " + faultMap ); + } + } + else + if ( XMLRPCEncoder.METHODNAME.equals( localName ) ) + { + methodName = (String) valueStack.pop(); + } + else + if ( XMLRPCEncoder.PARAM.equals( localName ) ) + { + //NOTE: this leaves the parameter on the stack + parameters.add( getResult() ); + } + } + + + + public void endPrefixMapping(String prefix) throws SAXException { + // TODO Auto-generated method stub + super.endPrefixMapping(prefix); + } + + public void error(SAXParseException e) throws SAXException { + // TODO Auto-generated method stub + super.error(e); + } + + public void fatalError(SAXParseException e) throws SAXException { + // TODO Auto-generated method stub + super.fatalError(e); + } + + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { + // TODO Auto-generated method stub + super.ignorableWhitespace(ch, start, length); + } + + public void notationDecl(String name, String publicId, String systemId) throws SAXException { + // TODO Auto-generated method stub + super.notationDecl(name, publicId, systemId); + } + + public void processingInstruction(String target, String data) throws SAXException { + // TODO Auto-generated method stub + super.processingInstruction(target, data); + } + + public InputSource resolveEntity(String publicId, String systemId) throws SAXException { + // NOTE: Sun accepted an incompatible api difference. The (false) should be tossed by hotspot + try { + if (false) throw new IOException("Fake exception to make it compile in both 1.4 and 1.5"); + return super.resolveEntity(publicId, systemId); + } catch (IOException e) { + throw new SAXException(e.getClass().getName() + " thrown while resolving entity.",e); + } + } + + public void setDocumentLocator(Locator locator) { + // TODO Auto-generated method stub + super.setDocumentLocator(locator); + } + + public void skippedEntity(String name) throws SAXException { + // TODO Auto-generated method stub + super.skippedEntity(name); + } + + + + public void startPrefixMapping(String prefix, String uri) throws SAXException { + // TODO Auto-generated method stub + super.startPrefixMapping(prefix, uri); + } + + public void unparsedEntityDecl(String name, String publicId, String systemId, String notationName) throws SAXException { + // TODO Auto-generated method stub + super.unparsedEntityDecl(name, publicId, systemId, notationName); + } + + public void warning(SAXParseException e) throws SAXException { + // TODO Auto-generated method stub + super.warning(e); + } + + + // marker class + + private class ValueMarker + { + private Class theClass; + + public ValueMarker() + { + theClass = null; + } + + public void setMarkerClass( Class aClass ) + { + theClass = aClass; + } + + public Class getMarkerClass() + { + return theClass; + } + + public String toString() + { + return "[ValueMarker: " + theClass + "]"; + } + } + +} + +/* + * $Log$ + * Revision 1.1 2006/02/19 01:44:03 cgruber + * Add xmlrpc files + * Remove jclark and replace with dom4j and javax.xml.sax stuff + * Re-work dependencies and imports so it all compiles. + * + * Revision 1.1 2006/02/16 13:22:22 cgruber + * Check in all sources in eclipse-friendly maven-enabled packages. + * + * Revision 1.7 2003/08/06 23:07:53 chochos + * general code cleanup (mostly, removing unused imports) + * + * Revision 1.6 2001/03/03 15:16:35 mpowers + * Fixed bug in decoding empty strings: string handler did nothing and + * empty cdatas were ignored, so no value was placed on the stack. + * + * Revision 1.5 2001/02/17 16:52:06 mpowers + * Changes in imports to support building with jdk1.1 collections. + * + * Revision 1.4 2001/02/09 15:51:39 mpowers + * Fixed a pernicious bug: I was using the character event entirely + * incorrectly, but the problem only exhibited itself with large data + * and even then only randomly. Now using a string buffer. + * + * Revision 1.3 2001/02/07 19:24:28 mpowers + * Moved XML classes to separate package. + * + * Revision 1.2 2001/02/06 14:34:23 mpowers + * Forgot to rename the package declarations. + * + * Revision 1.1 2001/02/06 14:31:19 mpowers + * Moving XML utilities from util to xml package. + * + * Revision 1.1.1.1 2000/12/21 15:52:39 mpowers + * Contributing wotonomy. + * + * Revision 1.2 2000/12/20 16:25:48 michael + * Added log to all files. + * + * + */ + diff --git a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCEncoder.java b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCEncoder.java new file mode 100644 index 0000000..3a63d45 --- /dev/null +++ b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCEncoder.java @@ -0,0 +1,526 @@ +/* +Wotonomy: OpenStep design patterns for pure Java applications. +Copyright (C) 2000 Intersect Software Corporation + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, see http://www.gnu.org +*/ + +package net.wotonomy.web.xml; + +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.text.Format; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import net.wotonomy.foundation.internal.Introspector; +import net.wotonomy.foundation.internal.WotonomyException; +import net.wotonomy.foundation.xml.XMLEncoder; + +import org.dom4j.Element; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.XMLWriter; +import org.dom4j.util.NonLazyElement; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +/** +* An implementation of XMLEncoder that serializes objects +* into XMLRPC format, which is found at http://xmlrpc.com/spec. +* We extend that standard only in that we add a "class" +* attribute to the "value" tag, so that a java-based decoder +* can more closely reconstruct the original java data structure. +* The class attribute can be safely ignored by clients. +* This implementation is not thread-safe, so a new instances +* should be created to accomodate multiple threads. +*/ +public class XMLRPCEncoder implements XMLEncoder +{ + public static final String METHODCALL = "methodCall"; + public static final String METHODNAME = "methodName"; + public static final String METHODRESPONSE = "methodResponse"; + public static final String PARAMS = "params"; + public static final String PARAM = "param"; + public static final String FAULT = "fault"; + public static final String FAULTCODE = "faultCode"; + public static final String FAULTSTRING = "faultString"; + + public static final String VALUE = "value"; + public static final String CLASS = "class"; + + public static final String STRUCT = "struct"; + public static final String MEMBER = "member"; + public static final String NAME = "name"; + + public static final String ARRAY = "array"; + public static final String DATA = "data"; + + public static final String NIL = "nil"; + public static final String INT = "int"; + public static final String I4 = "i4"; + public static final String BOOLEAN = "boolean"; + public static final String STRING = "string"; + public static final String DOUBLE = "double"; + public static final String DATE = "dateTime.iso8601"; + public static final String BASE64 = "base64"; + + public static final String TRUE = "1"; + public static final String FALSE = "0"; + + public static final Format DATEFORMAT8601 = + new SimpleDateFormat( "yyyyMMdd'T'HHmmss" ); + + /** + * Encodes an object to the specified output stream as XML. + * @param anObject The object to be serialized to XML format. + * @param anOutputStream The output stream to which the object + * will be written. + */ + public void encode( Object anObject, OutputStream anOutputStream ) + { + try + { + + //XMLWriter writer = new UTF8XMLWriter( anOutputStream ); + RPCXMLWriter writer = new RPCXMLWriter(anOutputStream,OutputFormat.createCompactFormat()); + writeValueToXMLWriter( anObject, writer ); + writer.flush(); + } + catch ( Exception exc ) + { + throw new WotonomyException( exc ); + } + } + + /** + * Encodes a method request in XML-RPC format in a "methodCall" tag, + * and writes the XML to the specified output stream. + * This method only writes XML: the caller is responsible for + * generating the appropriate header, if any, which should set + * the content type as "text/xml" and the content length as appropriate. + * The caller is also responsible for writing the xml version tag. + * @param aMethodName The method name to appear in the "methodName" tag. + * @param aParameterArray An array of objects, each of which will be + * encoded as values enclosed in a "param" tag, all of which will be + * enclosed in a "params" tag. + * @param anOutputStream The stream to which the XML will be written. + */ + public void encodeRequest( + String aMethodName, Object[] aParameterArray, + OutputStream anOutputStream ) + { + try + { + RPCXMLWriter writer = new RPCXMLWriter( anOutputStream, OutputFormat.createCompactFormat()); + writer.processingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ); + writer.startElement( METHODCALL ); + writer.startElement( METHODNAME ); + writer.write( aMethodName ); + writer.endElement( METHODNAME ); + writer.startElement( PARAMS ); + for ( int i = 0; i < aParameterArray.length; i++ ) + { + writer.startElement( PARAM ); + writeValueToXMLWriter( aParameterArray[i], writer ); + writer.endElement( PARAM ); + } + writer.endElement( PARAMS ); + writer.endElement( METHODCALL ); + writer.flush(); + } + catch ( Exception exc ) + { + throw new WotonomyException( exc ); + } + + //TODO: should this return the content-length? + } + + /** + * Encodes a method response in XML-RPC format in a "methodResponse" tag, + * and writes the XML to the specified output stream. + * This method only writes XML: the caller is responsible for + * generating the appropriate header, if any, which should set + * the content type as "text/xml" and the content length as appropriate. + * The caller is also responsible for writing the xml version tag. + * @param aResult A object which will be + * encoded as values enclosed in a "param" tag, all of which will be + * enclosed in a "params" tag. + * @param anOutputStream The stream to which the XML will be written. + */ + public void encodeResponse( + Object aResult, OutputStream anOutputStream ) + { + try + { + RPCXMLWriter writer = new RPCXMLWriter( anOutputStream, OutputFormat.createCompactFormat() ); + writer.processingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" ); + writer.startElement( METHODRESPONSE ); + writer.startElement( PARAMS ); + writer.startElement( PARAM ); + writeValueToXMLWriter( aResult, writer ); + writer.endElement( PARAM ); + writer.endElement( PARAMS ); + writer.endElement( METHODRESPONSE ); + writer.flush(); + } + catch ( Exception exc ) + { + throw new WotonomyException( exc ); + } + + //TODO: should this return the content-length? + } + + /** + * Encodes a fault response in XML-RPC format in a "methodResponse" tag, + * and writes the XML to the specified output stream. + * This method only writes XML: the caller is responsible for first + * generating the appropriate header, if any, which should set + * the content type as "text/xml" and the content length as appropriate. + * The caller is also responsible for writing the xml version tag. + * @param aFaultCode An application-defined error code. + * @param aFaultString A human-readable error description. + * @param anOutputStream The stream to which the XML will be written. + */ + public void encodeFault( + int aFaultCode, String aFaultString, OutputStream anOutputStream ) + { + try + { + RPCXMLWriter writer = new RPCXMLWriter( anOutputStream, OutputFormat.createCompactFormat() ); + writer.processingInstruction( "xml", "version=\"1.0\"" ); + writer.startElement( METHODRESPONSE ); + writer.startElement( FAULT ); + writer.startElement( VALUE ); + writer.startElement( STRUCT ); + + writer.startElement( MEMBER ); + writer.startElement( NAME ); + writer.write( FAULTCODE ); + writer.endElement( NAME ); + writer.startElement( VALUE ); + writer.startElement( INT ); + writer.write( new Integer( aFaultCode ).toString() ); + writer.endElement( INT ); + writer.endElement( VALUE ); + writer.endElement( MEMBER ); + + writer.startElement( MEMBER ); + writer.startElement( NAME ); + writer.write( FAULTSTRING ); + writer.endElement( NAME ); + writer.startElement( VALUE ); + writer.startElement( STRING ); + writer.write( aFaultString ); + writer.endElement( STRING ); + writer.endElement( VALUE ); + writer.endElement( MEMBER ); + + writer.endElement( STRUCT ); + writer.endElement( VALUE ); + writer.endElement( FAULT ); + writer.endElement( METHODRESPONSE ); + writer.flush(); + } + catch ( Exception exc ) + { + throw new WotonomyException( exc ); + } + + //TODO: should this return the content-length? + } + + /** + * Performs the actual writing of the file to XML. + */ + private void writeValueToXMLWriter( + Object anObject, RPCXMLWriter writer ) + { + try + { + + + if ( anObject == null ) + { + writer.startElement( VALUE ); + // write nil for null + Element nill = new NonLazyElement(NIL); + writer.write(nill); + } + else + if ( anObject instanceof Collection ) + { + // write class so we can restore if possible + AttributesImpl a = new AttributesImpl(); + a.addAttribute(null, CLASS, null, null, anObject.getClass().getName() ); + + writer.startElement( VALUE, a ); + + // write items in the order we get them from the iterator + + writer.startElement( ARRAY ); + writer.startElement( DATA ); + Iterator it = ((Collection)anObject).iterator(); + while ( it.hasNext() ) + { + writeValueToXMLWriter( it.next(), writer ); + } + writer.endElement( DATA ); + writer.endElement( ARRAY ); + + } + else + if ( anObject instanceof Map ) + { + AttributesImpl a = new AttributesImpl(); + a.addAttribute(null, CLASS, null, null, anObject.getClass().getName() ); + + writer.startElement( VALUE, a ); + + // write items in the order we get them from the iterator + //FIXME: The method-based properties are being ignored! + + Map.Entry entry; + writer.startElement( STRUCT ); + writer.startElement( MEMBER ); + Iterator it = ((Map)anObject).entrySet().iterator(); + while ( it.hasNext() ) + { + entry = (Map.Entry) it.next(); + writer.startElement( NAME ); + writeValueToXMLWriter( entry.getKey(), writer ); + writer.endElement( NAME ); + writeValueToXMLWriter( entry.getValue(), writer ); + } + writer.endElement( MEMBER ); + writer.endElement( STRUCT ); + } + else // not a collection + { + // check for primitive types + if ( anObject instanceof String ) + { + writer.startElement( VALUE ); + + writer.startElement( STRING ); + writer.write( anObject.toString() ); + writer.endElement( STRING ); + } + else + if ( anObject instanceof StringBuffer ) + { + // write class so we can restore if possible + AttributesImpl a = new AttributesImpl(); + a.addAttribute(null, CLASS, null, null, anObject.getClass().getName() ); + + writer.startElement( VALUE, a ); + + writer.startElement( STRING ); + writer.write( anObject.toString() ); + writer.endElement( STRING ); + } + else + if ( anObject instanceof Number ) + { + // write class so we can restore if possible + AttributesImpl a = new AttributesImpl(); + a.addAttribute(null, CLASS, null, null, anObject.getClass().getName() ); + + writer.startElement( VALUE, a ); + + if ( ( anObject instanceof Double ) + || ( anObject instanceof Float ) ) + { + writer.startElement( DOUBLE ); + writer.write( anObject.toString() ); + writer.endElement( DOUBLE ); + } + else + { + writer.startElement( INT ); + writer.write( anObject.toString() ); + writer.endElement( INT ); + } + } + else + if ( anObject instanceof Date ) + { + // write class so we can restore if possible + AttributesImpl a = new AttributesImpl(); + a.addAttribute(null, CLASS, null, null, anObject.getClass().getName() ); + + writer.startElement( VALUE, a ); + + writer.startElement( DATE ); + writer.write( DATEFORMAT8601.format( anObject ) ); + writer.endElement( DATE ); + } + else + if ( anObject instanceof Boolean ) + { + writer.startElement( BOOLEAN ); + if ( ((Boolean)anObject).booleanValue() ) + { + writer.write( "1" ); + } + else + { + writer.write( "0" ); + } + writer.endElement( BOOLEAN ); + } + else + if ( anObject.getClass().isArray() ) + { + // write class so we can restore if possible + AttributesImpl a = new AttributesImpl(); + a.addAttribute(null, CLASS, null, null, anObject.getClass().getName() ); + + writer.startElement( VALUE, a ); + + writer.startElement( ARRAY ); + writer.startElement( DATA ); + + int length = Array.getLength( anObject ); + for ( int i = 0; i < length; i++ ) + { + writeValueToXMLWriter( Array.get( anObject, i ), writer ); + } + + writer.endElement( DATA ); + writer.endElement( ARRAY ); + } + else // not primitive or collection, treat as struct + { + // write class so we can restore if possible + AttributesImpl a = new AttributesImpl(); + a.addAttribute(null, CLASS, null, null, anObject.getClass().getName() ); + + writer.startElement( VALUE, a ); + + List readProperties = new ArrayList(); + String[] read = Introspector.getReadPropertiesForObject( anObject ); + for ( int i = 0; i < read.length; i++ ) + { + readProperties.add( read[i] ); + } + + List properties = new ArrayList(); + String[] write = Introspector.getWritePropertiesForObject( anObject ); + for ( int i = 0; i < write.length; i++ ) + { + properties.add( write[i] ); + } + + // only use readable properties + properties.retainAll( readProperties ); + +// if ( properties.size() > 0 ) +// { + String key; + Object value; + Iterator it = properties.iterator(); + writer.startElement( STRUCT ); + while ( it.hasNext() ) + { + key = (String) it.next(); + value = Introspector.get( anObject, key ); + + writer.startElement( MEMBER ); + writer.startElement( NAME ); + writer.write( key ); + writer.endElement( NAME ); + writeValueToXMLWriter( value, writer ); + writer.endElement( MEMBER ); + } + writer.endElement( STRUCT ); +/* + } + else // no properties - write a converted string + { + writer.startElement( STRING ); + Object converted = + ValueConverter.convertObjectToClass( anObject, String.class ); + if ( converted != null ) + { + writer.write( converted.toString() ); + } + else + { + writer.write( anObject.toString() ); + } + writer.endElement( STRING ); + } +*/ + } + } + + writer.endElement( VALUE ); + } + catch ( Exception exc ) + { + System.err.println( "XMLFileSoup.writeValueToXMLWriter: " + exc ); + exc.printStackTrace(); + } + + } +/* + public static void main( String[] argv ) + { + System.out.println( "" ); + XMLRPCEncoder encoder = new XMLRPCEncoder(); + encoder.encodeRequest( "systemObject.test", new Object[] { + new net.wotonomy.test.TestObject(), + new net.wotonomy.test.TestObject(), + new net.wotonomy.test.TestObject() }, System.out ); + System.out.println(); + System.out.println(); + encoder.encodeResponse( new net.wotonomy.test.TestObject(), System.out ); + System.out.println(); + System.out.println(); + encoder.encodeFault( -1, "This is a fault.", System.out ); + System.out.println(); + System.out.println(); + System.out.println( "" ); + } +*/ + + private class RPCXMLWriter extends XMLWriter { + + public RPCXMLWriter(OutputStream arg0, OutputFormat arg1) throws UnsupportedEncodingException { + super(arg0, arg1); + } + + public void endElement(String localname) throws SAXException { + super.endElement(null, localname, null); + } + + public void startElement(String localname) throws SAXException { + this.startElement(localname, null); + } + public void startElement(String localname, Attributes attributes) throws SAXException { + this.startElement(null, localname, null, attributes); + } + + } + +} diff --git a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCReceiver.java b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCReceiver.java new file mode 100644 index 0000000..48c9e41 --- /dev/null +++ b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCReceiver.java @@ -0,0 +1,72 @@ +/* +Wotonomy: OpenStep design patterns for pure Java applications. +Copyright (C) 2000 Intersect Software Corporation + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, see http://www.gnu.org +*/ + +package net.wotonomy.web.xml; + +/** +* A call-back interface that receives an XML-RPC transaction message. +* Used by XMLRPCDecoder to return values from a message. +*/ +public interface XMLRPCReceiver +{ + /** + * Receives an XML-RPC request. + * @param aMethodName The method name of the request. + * @param aParameterArray The objects contained in the request, in order. + */ + void request( String aMethodName, Object[] aParameterArray ); + + /** + * Receives an XML-RPC response. + * @param aResult The object contained in the response. + */ + void response( Object aResult ); + + /** + * Receives an XML-RPC fault response. + * @param aFaultCode The fault code contained in the response. + * @param aFaultString The fault string contained in the response. + */ + void fault( int aFaultCode, String aFaultString ); +} + +/* + * $Log$ + * Revision 1.1 2006/02/19 01:44:02 cgruber + * Add xmlrpc files + * Remove jclark and replace with dom4j and javax.xml.sax stuff + * Re-work dependencies and imports so it all compiles. + * + * Revision 1.1 2006/02/16 13:22:22 cgruber + * Check in all sources in eclipse-friendly maven-enabled packages. + * + * Revision 1.2 2001/02/06 14:34:23 mpowers + * Forgot to rename the package declarations. + * + * Revision 1.1 2001/02/06 14:31:19 mpowers + * Moving XML utilities from util to xml package. + * + * Revision 1.1.1.1 2000/12/21 15:52:44 mpowers + * Contributing wotonomy. + * + * Revision 1.2 2000/12/20 16:25:49 michael + * Added log to all files. + * + * + */ + diff --git a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCSelector.java b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCSelector.java new file mode 100644 index 0000000..7c1f104 --- /dev/null +++ b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCSelector.java @@ -0,0 +1,238 @@ +/* +Wotonomy: OpenStep design patterns for pure Java applications. +Copyright (C) 2001 Intersect Software Corporation + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, see http://www.gnu.org +*/ + +package net.wotonomy.web.xml; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.net.URLConnection; + +import net.wotonomy.foundation.NSSelector; +import net.wotonomy.foundation.internal.WotonomyException; + +/** +* An NSSelector customized to invoke methods with XMLRPC +* when a URL is passed in as the object to the invoke() method. +* The method name and parameters will be marshalled and sent +* as an XMLRPC request to the host specified by the URL.

+* +* To use this class simply as an XMLRPC client, just call +* invoke() with a URL referencing the XMLRPC server and an +* optional array of parameters. +* +* @author michael@mpowers.net +* @author $Author: cgruber $ +* @version $Revision: 905 $ +*/ +public class XMLRPCSelector extends NSSelector +{ + private boolean copyStream = false; + + /** + * Constructor specifying a method name. + */ + public XMLRPCSelector (String aMethodName) + { + super( aMethodName, EMPTY_CLASS_ARRAY ); + } + + /** + * Constructor specifying a method name and an array of parameter types. + * When accessing XMLRPC servers, invoke() does require that the + * specified objects match the types in the parameter type array. + */ + public XMLRPCSelector (String aMethodName, Class[] aParameterTypeArray) + { + super( aMethodName, aParameterTypeArray ); + } + + /** + * Invokes this selector's method on the specified object + * using the specified parameters. + */ + public Object invoke (Object anObject, Object[] parameters) + throws IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException + { + if ( anObject instanceof URL ) + { + Receiver receiver = new Receiver(); + byte[] copyOfResponse = null; + try + { + URLConnection cn = ((URL)anObject).openConnection(); + + // set properties + cn.setDoOutput(true); + cn.setDoInput(true); + cn.setRequestProperty( + "content-type","text/xml"); + + // send parameters + OutputStream out = cn.getOutputStream(); + new XMLRPCEncoder().encodeRequest( + name(), parameters, out ); + out.flush(); + out.close(); + + // get response: getInputStream initiates the request + InputStream input = + new BufferedInputStream( cn.getInputStream() ); + if ( copyStream ) + { + ByteArrayOutputStream byteArray = + new ByteArrayOutputStream(); + int b; + while ( ( b = input.read() ) != -1 ) + { + byteArray.write( b ); + } + copyOfResponse = byteArray.toByteArray(); + input = new ByteArrayInputStream( copyOfResponse ); + } + new XMLRPCDecoder().decode( input, receiver ); + } + catch ( FileNotFoundException exc ) + { + throw new WotonomyException( "Server did not return a response." ); + } + catch ( Exception exc ) + { + if ( copyOfResponse != null ) + { + System.out.println( new String( copyOfResponse ) ); + exc.printStackTrace(); + } + throw new InvocationTargetException( exc ); + } + + if ( receiver.faultString == null ) + { + return receiver.result; + } + else + { + throw new InvocationTargetException( + new WotonomyException( + receiver.faultCode + ": " + receiver.faultString ) ); + } + } + + // else: not a URL + return super.invoke( anObject, parameters ); + } + + public static Object invoke + (String methodName, Class[] parameterTypes, Object anObject, Object[] parameters) + throws IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException + { + return new XMLRPCSelector( methodName, parameterTypes ).invoke( anObject, parameters ); + } + + public static Object invoke + (String methodName, Object anObject) + throws IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException + { + return XMLRPCSelector.invoke( + methodName, EMPTY_CLASS_ARRAY, anObject, EMPTY_OBJECT_ARRAY ); + } + + public static Object invoke + (String methodName, Class[] parameterTypes, + Object anObject, Object aParameter) + throws IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException + { + return XMLRPCSelector.invoke( + methodName, parameterTypes, anObject, new Object[] { aParameter } ); + } + + public static Object invoke + (String methodName, Class[] parameterTypes, + Object anObject, Object p1, Object p2) + throws IllegalAccessException, IllegalArgumentException, + InvocationTargetException, NoSuchMethodException + { + return XMLRPCSelector.invoke( + methodName, parameterTypes, anObject, new Object[] { p1, p2 } ); + } + + private class Receiver implements XMLRPCReceiver + { + public Object result; + public int faultCode; + public String faultString; + + public Receiver() + { + result = null; + faultCode = -1; + faultString = null; + } + + public void request( + String aMethodName, Object[] aParameterArray ) + { + throw new WotonomyException( + "Invalid response: Expected response but received request." ); + } + + public void response( + Object aResult ) + { + result = aResult; + faultCode = -1; + faultString = null; + } + + public void fault( + int aFaultCode, String aFaultString) + { + result = null; + faultCode = aFaultCode; + faultString = aFaultString; + } + } + + +} + +/* + * $Log$ + * Revision 1.1 2006/02/19 01:44:03 cgruber + * Add xmlrpc files + * Remove jclark and replace with dom4j and javax.xml.sax stuff + * Re-work dependencies and imports so it all compiles. + * + * Revision 1.1 2006/02/16 13:22:22 cgruber + * Check in all sources in eclipse-friendly maven-enabled packages. + * + * Revision 1.1 2001/02/07 19:24:28 mpowers + * Moved XML classes to separate package. + * + * + */ + diff --git a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCServlet.java b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCServlet.java new file mode 100644 index 0000000..a9981a4 --- /dev/null +++ b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCServlet.java @@ -0,0 +1,283 @@ +/* +Wotonomy: OpenStep design patterns for pure Java applications. +Copyright (C) 2001 Intersect Software Corporation + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, see http://www.gnu.org +*/ + +package net.wotonomy.web.xml; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Hashtable; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import net.wotonomy.foundation.NSSelector; +import net.wotonomy.foundation.internal.WotonomyException; + +/** +* A servlet that can make any java object into an XML-RPC server. +* Simply pass in the object to the constructor and XML-RPC requests +* to this servlet will call the appropriate methods and convert +* the results to an XML-RPC response.

+* +* Depending on your servlet container, it may be necessary to +* create a simple subclass that creates your handler object +* in its constructor and then calls setHandler().

+* +* Responses are in the specification's standard response format.

+* +* Faults are returned if any exception is thrown in the method, +* or if the specified method is not found on the object. +* The fault string is the toString value of the exception, +* and the fault code is the hasCode value of the exception.

+* +* Remember that this servlet only responds to POSTs, +* per the XML-RPC spec. +*/ +public class XMLRPCServlet extends HttpServlet +{ + protected Object handler; + protected boolean synchronizing; + private Hashtable selectorCache = new Hashtable(); // thread safe + private boolean copyStream = false; + + /** + * Default constructor initializes internal state. + */ + public XMLRPCServlet() + { + handler = null; + synchronizing = false; + } + + /** + * Constructor takes any java object and allows its methods + * to be invoked via XMLRPC requests to this servlet. + * Simply calls setHandler(). + */ + public XMLRPCServlet( Object aHandler ) + { + this(); + setHandler( aHandler ); + } + + /** + * Gets the object whose methods will be invoked to + * handle incoming requests. + */ + public Object getHandler() + { + return handler; + } + + /** + * Sets the object whose methods will be invoked to + * handle incoming requests. + */ + public void setHandler( Object aHandler ) + { + handler = aHandler; + } + + /** + * Gets whether the servlet should synchonize on the + * object before invoking methods on it. + * Defaults to false. + */ + public boolean isSynchronizing() + { + return synchronizing; + } + + /** + * Sets whether the servlet should synchonize on the + * object before invoking methods on it. + * Defaults to false. + */ + public void setSynchronizing( boolean willSynchronize ) + { + synchronizing = willSynchronize; + } + + /** + * Overridden to service the request. + */ + protected void doPost( + HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException + { + if ( getHandler() != null ) + { + InputStream input = req.getInputStream(); + byte[] copyOfRequest = null; + + if ( copyStream ) + { + ByteArrayOutputStream byteArray = + new ByteArrayOutputStream(); + int b; + while ( ( b = input.read() ) != -1 ) + { + byteArray.write( b ); + } + copyOfRequest = byteArray.toByteArray(); + input = new ByteArrayInputStream( copyOfRequest ); + } + + try + { + new XMLRPCDecoder().decode( input, + new Receiver( this, resp ) ); + } + catch ( WotonomyException exc ) + { + if ( copyOfRequest != null ) + { + System.out.println( new String( copyOfRequest ) ); + exc.printStackTrace(); + } + // catches io exceptions thrown in handleRequest. + Throwable t = exc.getWrappedThrowable(); + if ( t instanceof IOException ) + { + throw (IOException)t; + } + throw exc; + } + } + } + + /** + * Called by doPost after parsing an incoming request, + * and is responsible for invoking the specified method + * with the specified parameters on the handler object. + * (This implementation calls getOutputStream() on the response.) + * Override to customize the handling of the request. + */ + protected void handleRequest( String aMethodName, + Object[] aParameterArray, HttpServletResponse aResponse ) + { + OutputStream output = null; + + try + { + output = aResponse.getOutputStream(); + aResponse.setStatus( HttpServletResponse.SC_OK ); // always 200 + aResponse.setContentType( "text/xml" ); + } + catch ( IOException exc ) + { + // caught in doPost + throw new WotonomyException( exc ); + } + + // get the array of types + XMLRPCEncoder encoder = new XMLRPCEncoder(); + Class[] types = new Class[ aParameterArray.length ]; + for ( int i = 0; i < aParameterArray.length; i++ ) + { + types[i] = aParameterArray[i].getClass(); + } + + //TODO: selectors should be cached if possible + + Object handler = getHandler(); + if ( isSynchronizing() ) + { + synchronized ( handler ) + { + execute( encoder, handler, output, + new NSSelector( aMethodName, types ), aParameterArray ); + } + } + else + { + execute( encoder, handler, output, + new NSSelector( aMethodName, types ), aParameterArray ); + } + } + + private void execute( XMLRPCEncoder anEncoder, Object aHandler, + OutputStream output, NSSelector aSelector, Object[] aParameterArray ) + { + try + { + Object result = + aSelector.invoke( aHandler, aParameterArray ); + anEncoder.encodeResponse( result, output ); + } + catch ( Exception exc ) + { + anEncoder.encodeFault( + exc.hashCode(), exc.toString(), output ); + } + } + + private class Receiver implements XMLRPCReceiver + { + XMLRPCServlet controller; + HttpServletResponse response; + + public Receiver( + XMLRPCServlet aController, + HttpServletResponse aResponse ) + { + controller = aController; + response = aResponse; + } + + public void request( + String aMethodName, Object[] aParameterArray ) + { + controller.handleRequest( + aMethodName, aParameterArray, response ); + } + + public void response( + Object aResult ) + { + // does nothing + } + + public void fault( + int aFaultCode, String aFaultString) + { + // does nothing + } + } +} + +/* + * $Log$ + * Revision 1.1 2006/02/19 01:44:02 cgruber + * Add xmlrpc files + * Remove jclark and replace with dom4j and javax.xml.sax stuff + * Re-work dependencies and imports so it all compiles. + * + * Revision 1.1 2006/02/16 13:22:22 cgruber + * Check in all sources in eclipse-friendly maven-enabled packages. + * + * Revision 1.1 2001/02/07 19:24:28 mpowers + * Moved XML classes to separate package. + * + */ + diff --git a/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/package.html b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/package.html new file mode 100644 index 0000000..4303e07 --- /dev/null +++ b/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/package.html @@ -0,0 +1,30 @@ + +

+The XML object serialization framework, including +an XML-RPC serializer implementation and an easy-to-use +XML-RPC servlet and client. +

+

+The primary serialization interfaces are XMLEncoder and XMLDecoder. +

+

+The implementation of those interfaces is in XMLRPCEncoder and XMLRPCDecoder. +In addition to serializing java objects to an XML-RPC struct format, these +classes also define methods for generating and parsing XML-RPC requests, +responses, and faults. Other implementations (like SOAP) may follow. +

+

+The XMLRPCServlet utilizes the framework to allow you to turn any +java object into an XML-RPC server with one line of code. +

+

+XMLRPCSelector turns NSSelector into a XML-RPC client. If you +pass in a URL as the target object, the selector will invoke its +method on the specified XML-RPC server and return the result as +a java object. The selector otherwise works normally. +

+

+This package has dependencies on the foundation and util packages. +The servlet has a dependency on javax.servlet.HttpServlet. +

+ -- cgit v1.2.3