/* * Created on Jun 15, 2005 * * 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. * * Copyright @2005 the original author or authors. */ package org.springmodules.remoting.xmlrpc.dom; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.springframework.util.xml.SimpleSaxErrorHandler; import org.springmodules.remoting.xmlrpc.XmlRpcInternalException; import org.springmodules.remoting.xmlrpc.XmlRpcInvalidPayloadException; import org.springmodules.remoting.xmlrpc.XmlRpcNotWellFormedException; import org.springmodules.remoting.xmlrpc.XmlRpcParsingException; import org.springmodules.remoting.xmlrpc.XmlRpcServerException; import org.springmodules.remoting.xmlrpc.support.XmlRpcArray; import org.springmodules.remoting.xmlrpc.support.XmlRpcBase64; import org.springmodules.remoting.xmlrpc.support.XmlRpcBoolean; import org.springmodules.remoting.xmlrpc.support.XmlRpcDateTime; import org.springmodules.remoting.xmlrpc.support.XmlRpcDouble; import org.springmodules.remoting.xmlrpc.support.XmlRpcElement; import org.springmodules.remoting.xmlrpc.support.XmlRpcElementNames; import org.springmodules.remoting.xmlrpc.support.XmlRpcInteger; import org.springmodules.remoting.xmlrpc.support.XmlRpcString; import org.springmodules.remoting.xmlrpc.support.XmlRpcStruct; import org.springmodules.remoting.xmlrpc.support.XmlRpcStruct.XmlRpcMember; import org.springmodules.remoting.xmlrpc.util.XmlRpcParsingUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * <p> * Template for XML-RPC request/response parsers that use DOM. * </p> * * @author Alex Ruiz * * @version $Revision$ $Date$ */ public abstract class AbstractDomXmlRpcParser { /** * SAX entity resolver to be used for parsing. By default, * <code>{@link XmlRpcDtdResolver}</code> will be used. */ private EntityResolver entityResolver; /** * <p> * Implementation of <code>org.xml.sax.ErrorHandler</code> for custom * handling of XML parsing errors and warnings. * </p> * <p> * If not set, a default <code>{@link SimpleSaxErrorHandler}</code> is used * that simply logs warnings using the logger instance of the view class, and * rethrows errors to discontinue the XML transformation. * </p> * * @see org.springframework.util.xml.SimpleSaxErrorHandler */ private ErrorHandler errorHandler; protected final Log logger = LogFactory.getLog(getClass()); /** * Flag that indicates if the XML parser should validate the XML-RPC request. * Default is <code>false</code>. */ private boolean validating; public AbstractDomXmlRpcParser() { super(); setEntityResolver(new XmlRpcDtdResolver()); setErrorHandler(new SimpleSaxErrorHandler(logger)); } /** * Creates a new XML document by parsing the given InputStream. * * @param inputStream * the InputStream to parse. * @return the created XML document. * @throws XmlRpcServerException * if there are any internal errors. * @throws XmlRpcParsingException * if there are any errors during the parsing. */ protected final Document loadXmlDocument(InputStream inputStream) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); if (logger.isDebugEnabled()) { logger.debug("Using JAXP implementation [" + factory + "]"); } factory.setValidating(validating); DocumentBuilder docBuilder = factory.newDocumentBuilder(); docBuilder.setErrorHandler(errorHandler); if (entityResolver != null) { docBuilder.setEntityResolver(entityResolver); } return docBuilder.parse(inputStream); } catch (ParserConfigurationException exception) { throw new XmlRpcInternalException("Parser configuration exception", exception); } catch (SAXParseException exception) { throw new XmlRpcNotWellFormedException("Line " + exception.getLineNumber() + " in XML-RPC payload is invalid", exception); } catch (SAXException exception) { throw new XmlRpcNotWellFormedException("XML-RPC payload is invalid", exception); } catch (IOException exception) { throw new XmlRpcInternalException( "IOException when parsing XML-RPC payload", exception); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException exception) { logger.warn("Could not close InputStream", exception); } } } } /** * Parses the given XML element that contains a XML-RPC array. * * @param arrayElement * the XML element to parse. * @return the created XML-RPC array. * @throws XmlRpcInvalidPayloadException * if the element contains an unknown child. Only one "data" element * is allowed inside an "array" element. * @see #parseDataElement(Element) */ protected final XmlRpcArray parseArrayElement(Element arrayElement) { NodeList children = arrayElement.getChildNodes(); int childCount = children.getLength(); for (int i = 0; i < childCount; i++) { Node child = children.item(i); if (child instanceof Element) { String childName = child.getNodeName(); if (XmlRpcElementNames.DATA.equals(childName)) { Element dataElement = (Element) child; return parseDataElement(dataElement); } XmlRpcParsingUtils.handleUnexpectedElementFound(childName); } } // we should not reach this point. return null; } /** * Parses the given XML element that contains the data of a XML-RPC array. * * @param dataElement * the XML element to parse. * @return the created XML-RPC array. * @see #parseValueElement(Element) */ protected final XmlRpcArray parseDataElement(Element dataElement) { XmlRpcArray array = new XmlRpcArray(); NodeList children = dataElement.getChildNodes(); int childCount = children.getLength(); for (int i = 0; i < childCount; i++) { Node child = children.item(i); if (child instanceof Element) { String childName = child.getNodeName(); if (XmlRpcElementNames.VALUE.equals(childName)) { Element valueElement = (Element) child; XmlRpcElement element = parseValueElement(valueElement); array.add(element); } } } return array; } /** * Parses the given XML element that contains a member of a XML-RPC complex * structure. * * @param memberElement * the XML element to parse. * @return the created member of a XML-RPC complex structure. * @throws XmlRpcInvalidPayloadException * if the element contains a child with an unknown name. Only one * element with name "name" and one element with name "value" are * allowed inside an "member" element. * @throws XmlRpcInvalidPayloadException * if the name of the parsed struct member is empty. * @see #parseValueElement(Element) */ protected final XmlRpcMember parseMemberElement(Element memberElement) { String name = null; XmlRpcElement value = null; NodeList children = memberElement.getChildNodes(); int childCount = children.getLength(); for (int i = 0; i < childCount; i++) { Node child = children.item(i); if (child instanceof Element) { String childName = child.getNodeName(); if (XmlRpcElementNames.NAME.equals(childName)) { Element nameElement = (Element) child; name = DomUtils.getTextValue(nameElement); } else if (XmlRpcElementNames.VALUE.equals(childName)) { Element valueElement = (Element) child; value = parseValueElement(valueElement); } else { XmlRpcParsingUtils.handleUnexpectedElementFound(childName); } } } if (!StringUtils.hasText(name)) { throw new XmlRpcInvalidPayloadException( "The struct member should have a name"); } return new XmlRpcMember(name, value); } /** * Parses the given XML element that contains a single parameter of either a * XML-RPC request or a XML-RPC response. * * @param parameterElement * the XML element to parse. * @return the created parameter. * @throws XmlRpcInvalidPayloadException * if the element contains a child with name other than a "value". * @see #parseValueElement(Element) */ protected final XmlRpcElement parseParameterElement(Element parameterElement) { NodeList children = parameterElement.getChildNodes(); int childCount = children.getLength(); for (int i = 0; i < childCount; i++) { Node child = children.item(i); if (child instanceof Element) { String nodeName = child.getNodeName(); if (XmlRpcElementNames.VALUE.equals(nodeName)) { Element valueElement = (Element) child; return parseValueElement(valueElement); } XmlRpcParsingUtils.handleUnexpectedElementFound(nodeName); } } // we should not reach this point. return null; } /** * Parses the given XML element that contains all the parameters or either a * XML-RPC request or a XML-RPC response. * * @param parametersElement * the XML element to parse. * @return the created parameters. * @throws XmlRpcInvalidPayloadException * if there are elements other than "param". * @see #parseParameterElement(Element) */ protected final XmlRpcElement[] parseParametersElement( Element parametersElement) { List parameters = new ArrayList(); NodeList children = parametersElement.getChildNodes(); int childCount = children.getLength(); for (int i = 0; i < childCount; i++) { Node child = children.item(i); if (child instanceof Element) { String childName = child.getNodeName(); if (XmlRpcElementNames.PARAM.equals(childName)) { Element parameterElement = (Element) child; XmlRpcElement parameter = this .parseParameterElement(parameterElement); parameters.add(parameter); } else { XmlRpcParsingUtils.handleUnexpectedElementFound(childName); } } } return (XmlRpcElement[]) parameters.toArray(new XmlRpcElement[parameters .size()]); } /** * Parses the given XML element that contains a XML-RPC complex structure. * * @param structElement * the XML element to parse. * @return the created XML-RPC complex structure. * @see #parseMemberElement(Element) */ protected final XmlRpcStruct parseStructElement(Element structElement) { XmlRpcStruct struct = new XmlRpcStruct(); NodeList children = structElement.getChildNodes(); int childCount = children.getLength(); for (int i = 0; i < childCount; i++) { Node child = children.item(i); if (child instanceof Element) { String childName = child.getNodeName(); if (XmlRpcElementNames.MEMBER.equals(childName)) { Element memberElement = (Element) child; XmlRpcMember member = parseMemberElement(memberElement); struct.add(member); } } } return struct; } /** * Parses the given XML element that contains the value of a parameter, a * struct member or an element of an array. * * @param valueElement * the XML element to parse. * @return the created value. * @throws XmlRpcInvalidPayloadException * if there are invalid XML elements. * @see #parseArrayElement(Element) * @see #parseStructElement(Element) */ protected final XmlRpcElement parseValueElement(Element valueElement) { NodeList children = valueElement.getChildNodes(); int childCount = children.getLength(); for (int i = 0; i < childCount; i++) { Node child = children.item(i); if (child instanceof Element) { String childName = child.getNodeName(); Element xmlElement = (Element) child; if (XmlRpcElementNames.ARRAY.equals(childName)) { return parseArrayElement(xmlElement); } else if (XmlRpcElementNames.BASE_64.equals(childName)) { String source = DomUtils.getTextValue(xmlElement); return new XmlRpcBase64(source); } else if (XmlRpcElementNames.BOOLEAN.equals(childName)) { String source = DomUtils.getTextValue(xmlElement); return new XmlRpcBoolean(source); } else if (XmlRpcElementNames.DATE_TIME.equals(childName)) { String source = DomUtils.getTextValue(xmlElement); return new XmlRpcDateTime(source); } else if (XmlRpcElementNames.DOUBLE.equals(childName)) { String source = DomUtils.getTextValue(xmlElement); return new XmlRpcDouble(source); } else if (XmlRpcElementNames.I4.equals(childName) || XmlRpcElementNames.INT.equals(childName)) { String source = DomUtils.getTextValue(xmlElement); return new XmlRpcInteger(source); } else if (XmlRpcElementNames.STRING.equals(childName)) { String source = DomUtils.getTextValue(xmlElement); return new XmlRpcString(source); } else if (XmlRpcElementNames.STRUCT.equals(childName)) { return parseStructElement(xmlElement); } else { XmlRpcParsingUtils.handleUnexpectedElementFound(childName); } } else if (child instanceof Text) { String source = DomUtils.getTextValue(valueElement); return new XmlRpcString(source); } } // we should not reach this point. return null; } public final void setEntityResolver(EntityResolver newEntityResolver) { entityResolver = newEntityResolver; } public final void setErrorHandler(ErrorHandler newErrorHandler) { errorHandler = newErrorHandler; } public final void setValidating(boolean newValidating) { validating = newValidating; } }