All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xmlrpc.XmlRpc Maven / Gradle / Ivy

/*
 * Copyright 1999,2005 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.apache.xmlrpc;

import org.xml.sax.*;
import uk.co.wilson.xml.MinML;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Hashtable;
import java.util.Stack;
import java.util.TimeZone;
import java.util.Vector;

/**
 * This abstract base class provides basic capabilities for XML-RPC,
 * like parsing of parameters or encoding Java objects into XML-RPC
 * format.  Any XML parser with a SAX interface can be used.
 *
 * 

XmlRpcServer and XmlRpcClient are the classes that actually * implement an XML-RPC server and client. * * @see org.apache.xmlrpc.XmlRpcServer * @see org.apache.xmlrpc.XmlRpcClient * * @author Hannes Wallnoefer * @author Daniel L. Rall * @author Andrew Evers * @version $Id: XmlRpc.java 350048 2005-11-30 21:49:32 +0100 (Mi, 30 Nov 2005) jochen $ */ public abstract class XmlRpc extends HandlerBase { /** * The version string used in HTTP communication. */ // FIXME: Use Ant to pre-process version number into a // class-loaded .properties file at build time. Use here when // available, and otherwise provide no version string. public static final String version = "Apache XML-RPC 2.0"; /** * The default parser to use (MinML). */ private static final String DEFAULT_PARSER = MinML.class.getName(); /** * The maximum number of threads which can be used concurrently. */ private static int maxThreads = 100; String methodName; /** * The class name of SAX parser to use. */ private static Class parserClass; private static Hashtable saxDrivers = new Hashtable (8); static { // A mapping of short identifiers to the fully qualified class // names of common SAX parsers. If more mappings are added // here, increase the size of the saxDrivers Map used to store // them. saxDrivers.put("xerces", "org.apache.xerces.parsers.SAXParser"); saxDrivers.put("xp", "com.jclark.xml.sax.Driver"); saxDrivers.put("ibm1", "com.ibm.xml.parser.SAXDriver"); saxDrivers.put("ibm2", "com.ibm.xml.parsers.SAXParser"); saxDrivers.put("aelfred", "com.microstar.xml.SAXDriver"); saxDrivers.put("oracle1", "oracle.xml.parser.XMLParser"); saxDrivers.put("oracle2", "oracle.xml.parser.v2.SAXParser"); saxDrivers.put("openxml", "org.openxml.parser.XMLSAXParser"); } // the stack we're parsing our values into. Stack values; Value currentValue; /** * Used to collect character data (CDATA) of * parameter values. */ StringBuffer cdata; boolean readCdata; // XML RPC parameter types used for dataMode static final int STRING = 0; static final int INTEGER = 1; static final int BOOLEAN = 2; static final int DOUBLE = 3; static final int DATE = 4; static final int BASE64 = 5; static final int STRUCT = 6; static final int ARRAY = 7; // Error level + message int errorLevel; String errorMsg; static final int NONE = 0; static final int RECOVERABLE = 1; static final int FATAL = 2; /** * Wheter to use HTTP Keep-Alive headers. */ static boolean keepalive = false; /** * Whether to log debugging output. */ public static boolean debug = false; /** * The list of valid XML elements used for RPC. */ final static String types[] = { "String", "Integer", "Boolean", "Double", "Date", "Base64", "Struct", "Array" }; /** * Java's name for the encoding we're using. Defaults to * UTF8 (of which ISO8859_1 is a * subset). */ static String encoding = XmlWriter.UTF8; /** * Java's name for the input encoding we're using. Defaults to * null, signifying the platform default. This may * need to be overridden on platforms where the default encoding * is not compatible with ASCII (eg. EBCDIC) but the network is * still ASCII-like. */ static String defaultInputEncoding = null; private TypeFactory typeFactory; private String inputEncoding; /** * Creates a new instance with the {@link * org.apache.xmlrpc.TypeFactory} set to an instance of the class * named by the org.apache.xmlrpc.TypeFactory System * property. If property not set or class is unavailable, uses * the default of {@link org.apache.xmlrpc.TypeFactory}. */ protected XmlRpc() { String typeFactoryName = null; try { typeFactoryName = System.getProperty(TypeFactory.class.getName()); } catch (SecurityException e) { // An unsigned applet may not access system properties. // No-op means we use the default TypeFactory instead. if (debug) { System.out.println("Unable to determine the value of the " + "system property '" + TypeFactory.class.getName() + "': " + e.getMessage()); } } this.typeFactory = createTypeFactory(typeFactoryName); this.inputEncoding = defaultInputEncoding; } /** * Creates a new instance with the specified {@link * org.apache.xmlrpc.TypeFactory}. * * @param typeFactoryName The fully qualified class name of the * {@link org.apache.xmlrpc.TypeFactory} implementation to use. */ protected XmlRpc(String typeFactoryName) { this.typeFactory = createTypeFactory(typeFactoryName); } /** * Creates a new instance of the specified {@link * org.apache.xmlrpc.TypeFactory}. * * @param className The fully qualified class name of the * implementation to use. * @return The new type mapping. */ private TypeFactory createTypeFactory(String className) { Class c = null; if (className != null && className.length() > 0) { try { c = Class.forName(className); } catch (ClassNotFoundException e) { System.err.println("Error loading TypeFactory '" + "' " + c.getName() + "': Using the default instead: " + e.getMessage()); } } // If we're using the default, provide it immediately. if (c == null || DefaultTypeFactory.class.equals(c)) { return new DefaultTypeFactory(tz); } try { return (TypeFactory) c.newInstance(); } catch (Exception e) { System.err.println("Unable to create configured TypeFactory '" + c.getName() + "': " + e.getMessage() + ": Falling back to default"); if (debug) { e.printStackTrace(); } return new DefaultTypeFactory(tz); } } TimeZone tz; void setServerTimeZone(TimeZone z) { tz = z; } TimeZone getServerTimeZone() { return tz; } /** * Set the SAX Parser to be used. The argument can either be the * full class name or a user friendly shortcut if the parser is * known to this class. The parsers that can currently be set by * shortcut are listed in the main documentation page. If you are * using another parser please send me the name of the SAX driver * and I'll include it in a future release. If setDriver() is * never called then the System property "sax.driver" is * consulted. If that is not defined the driver defaults to * OpenXML. */ public static void setDriver(String driver) throws ClassNotFoundException { String parserClassName = null; try { parserClassName = (String) saxDrivers.get(driver); if (parserClassName == null) { // Identifier lookup failed, assuming we were provided // with the fully qualified class name. parserClassName = driver; } parserClass = Class.forName(parserClassName); } catch (ClassNotFoundException x) { throw new ClassNotFoundException ("SAX driver not found: " + parserClassName); } } /** * Set the SAX Parser to be used by directly passing the Class object. */ public static void setDriver(Class driver) { parserClass = driver; } /** * Set the encoding of the XML. * * @param enc The Java name of the encoding. */ public static void setEncoding(String enc) { encoding = enc; } /** * Return the encoding, transforming to the canonical name if * possible. * * @see org.apache.xmlrpc.XmlWriter#canonicalizeEncoding(String) */ public String getEncoding() { return XmlWriter.canonicalizeEncoding(encoding); } /** Set the default input encoding of the XML. * This is used only if set. * * @param enc The Java name of the encoding. * @see #setInputEncoding(String) */ public static void setDefaultInputEncoding(String enc) { defaultInputEncoding = enc; } /** * Return the default input encoding. This may be null. * This is always a Java encoding name, it is not transformed. * * @return the Java encoding name to use, if set, otherwise null. * @see #getInputEncoding() */ public static String getDefaultInputEncoding() { return defaultInputEncoding; } /** * Set the input encoding for this XmlRpc instance. This can be * used when the XMLRPC response does not contain the proper * encoding information in the XML declaration. * * @param enc The Java name of the encoding. */ public void setInputEncoding(String enc) { inputEncoding = enc; } /** * Get the input encoding for this XmlRpc instance. This is a Java * encoding name. * * @return The Java encoding name to use. null if not set. */ public String getInputEncoding() { return inputEncoding; } /** * Gets the maximum number of threads used at any given moment. */ public static int getMaxThreads() { return maxThreads; } /** * Sets the maximum number of threads used at any given moment. */ public static void setMaxThreads(int maxThreads) { XmlRpc.maxThreads = maxThreads; } /** * Switch debugging output on/off. */ public static void setDebug(boolean val) { debug = val; } /** * Switch HTTP keepalive on/off. */ public static void setKeepAlive(boolean val) { keepalive = val; } /** * get current HTTP keepalive mode. */ public static boolean getKeepAlive() { return keepalive; } /** * Parse the input stream. For each root level object, method * objectParsed is called. */ synchronized void parse(InputStream is) throws Exception { // reset values (XmlRpc objects are reusable) errorLevel = NONE; errorMsg = null; values = new Stack(); if (cdata == null) { cdata = new StringBuffer(128); } else { cdata.setLength(0); } readCdata = false; currentValue = null; long now = System.currentTimeMillis(); if (parserClass == null) { // try to get the name of the SAX driver from the System properties String driver; try { driver = System.getProperty("sax.driver", DEFAULT_PARSER); } catch (SecurityException e) { // An unsigned applet may not access system properties. driver = DEFAULT_PARSER; } setDriver(driver); } Parser parser = null; try { parser = (Parser) parserClass.newInstance(); } catch (NoSuchMethodError nsm) { // This is thrown if no constructor exists for the parser class // and is transformed into a regular exception. throw new Exception("Can't create Parser: " + parserClass); } parser.setDocumentHandler(this); parser.setErrorHandler(this); if (debug) { System.out.println("Beginning parsing XML input stream"); } try { if(inputEncoding == null) { parser.parse(new InputSource(is)); } else { parser.parse( new InputSource( new InputStreamReader(is, inputEncoding))); } } finally { // Clear any huge buffers. if (cdata.length() > 128 * 4) { // Exceeded original capacity by greater than 4x; release // buffer to prevent leakage. cdata = null; } } if (debug) { System.out.println ("Spent " + (System.currentTimeMillis() - now) + " millis parsing"); } } /** * This method is called when a root level object has been parsed. * Sub-classes implement this callback to receive the fully parsed * object. */ protected abstract void objectParsed(Object what); //////////////////////////////////////////////////////////////// // methods called by XML parser /** * Method called by SAX driver. */ public void characters(char ch[], int start, int length) throws SAXException { if (readCdata) { cdata.append(ch, start, length); } } /** * Method called by SAX driver. */ public void endElement(String name) throws SAXException { if (debug) { System.out.println("endElement: " + name); } // finalize character data, if appropriate if (currentValue != null && readCdata) { currentValue.characterData(cdata.toString()); cdata.setLength(0); readCdata = false; } if ("value".equals(name)) { // Only handle top level objects or objects contained in // arrays here. For objects contained in structs, wait // for (see code below). int depth = values.size(); if (depth < 2 || values.elementAt(depth - 2).hashCode() != STRUCT) { Value v = currentValue; values.pop(); if (depth < 2) { // This is a top-level object objectParsed(v.value); currentValue = null; } else { // Add object to sub-array; if current container // is a struct, add later (at ). currentValue = (Value) values.peek(); currentValue.endElement(v); } } } // Handle objects contained in structs. if ("member".equals(name)) { Value v = currentValue; values.pop(); currentValue = (Value) values.peek(); currentValue.endElement(v); } else if ("methodName".equals(name)) { methodName = cdata.toString(); cdata.setLength(0); readCdata = false; } } /** * Method called by SAX driver. */ public void startElement(String name, AttributeList atts) throws SAXException { if (debug) { System.out.println("startElement: " + name); } if ("value".equals(name)) { Value v = new Value(); values.push(v); currentValue = v; // cdata object is reused cdata.setLength(0); readCdata = true; } else if ("methodName".equals(name)) { cdata.setLength(0); readCdata = true; } else if ("name".equals(name)) { cdata.setLength(0); readCdata = true; } else if ("string".equals(name)) { // currentValue.setType (STRING); cdata.setLength(0); readCdata = true; } else if ("i4".equals(name) || "int".equals(name)) { currentValue.setType(INTEGER); cdata.setLength(0); readCdata = true; } else if ("boolean".equals(name)) { currentValue.setType(BOOLEAN); cdata.setLength(0); readCdata = true; } else if ("double".equals(name)) { currentValue.setType(DOUBLE); cdata.setLength(0); readCdata = true; } else if ("dateTime.iso8601".equals(name)) { currentValue.setType(DATE); cdata.setLength(0); readCdata = true; } else if ("base64".equals(name)) { currentValue.setType(BASE64); cdata.setLength(0); readCdata = true; } else if ("struct".equals(name)) { currentValue.setType(STRUCT); } else if ("array".equals(name)) { currentValue.setType(ARRAY); } } /** * * @param e * @throws SAXException */ public void error(SAXParseException e) throws SAXException { System.err.println("Error parsing XML: " + e); errorLevel = RECOVERABLE; errorMsg = e.toString(); } /** * * @param e * @throws SAXException */ public void fatalError(SAXParseException e) throws SAXException { System.err.println("Fatal error parsing XML: " + e); errorLevel = FATAL; errorMsg = e.toString(); } /** * This represents a XML-RPC value parsed from the request. */ class Value { int type; Object value; // the name to use for the next member of struct values String nextMemberName; Hashtable struct; Vector array; /** * Constructor. */ public Value() { this.type = STRING; } /** * Notification that a new child element has been parsed. */ public void endElement(Value child) { switch (type) { case ARRAY: array.addElement(child.value); break; case STRUCT: struct.put(nextMemberName, child.value); } } /** * Set the type of this value. If it's a container, create the * corresponding java container. */ public void setType(int type) { //System.out.println ("setting type to "+types[type]); this.type = type; switch (type) { case ARRAY: value = array = new Vector(); break; case STRUCT: value = struct = new Hashtable(); break; } } /** * Set the character data for the element and interpret it * according to the element type. */ public void characterData(String cdata) { switch (type) { case INTEGER: value = typeFactory.createInteger(cdata); break; case BOOLEAN: value = typeFactory.createBoolean(cdata); break; case DOUBLE: value = typeFactory.createDouble(cdata); break; case DATE: value = typeFactory.createDate(cdata); break; case BASE64: value = typeFactory.createBase64(cdata); break; case STRING: value = typeFactory.createString(cdata); break; case STRUCT: // this is the name to use for the next member of this struct nextMemberName = cdata; break; } } /** * This is a performance hack to get the type of a value * without casting the Object. It breaks the contract of * method hashCode, but it doesn't matter since Value objects * are never used as keys in Hashtables. */ public int hashCode() { return type; } /** * * @return */ public String toString() { return (types[type] + " element " + value); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy