summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml
diff options
context:
space:
mode:
Diffstat (limited to 'projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml')
-rw-r--r--projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCDecoder.java114
-rw-r--r--projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCDecoderHelper.java534
-rw-r--r--projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCEncoder.java526
-rw-r--r--projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCReceiver.java72
-rw-r--r--projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCSelector.java238
-rw-r--r--projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/XMLRPCServlet.java283
-rw-r--r--projects/net.wotonomy.web/src/main/java/net/wotonomy/web/xml/package.html30
7 files changed, 1797 insertions, 0 deletions
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( "<test>" );
+ 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( "</test>" );
+ }
+*/
+
+ 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. <br><br>
+*
+* 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. <br><br>
+*
+* 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(). <br><br>
+*
+* Responses are in the specification's standard response format.<br><br>
+*
+* 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. <br><br>
+*
+* 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 @@
+<body>
+<p>
+The XML object serialization framework, including
+an XML-RPC serializer implementation and an easy-to-use
+XML-RPC servlet and client.
+</p>
+<p>
+The primary serialization interfaces are XMLEncoder and XMLDecoder.
+</p>
+<p>
+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.
+</p>
+<p>
+The XMLRPCServlet utilizes the framework to allow you to turn any
+java object into an XML-RPC server with one line of code.
+</p>
+<p>
+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.
+</p>
+<p>
+This package has dependencies on the foundation and util packages.
+The servlet has a dependency on javax.servlet.HttpServlet.
+</p>
+</body>