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

org.xins.server.SOAPCallingConvention Maven / Gradle / Ivy

The newest version!
/*
 * $Id: SOAPCallingConvention.java,v 1.81 2012/03/15 21:07:39 agoubard Exp $
 *
 * See the COPYRIGHT file for redistribution and use restrictions.
 */
package org.xins.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.w3c.dom.Attr;

import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.Utils;
import org.xins.common.spec.DataSectionElementSpec;
import org.xins.common.spec.EntityNotFoundException;
import org.xins.common.spec.FunctionSpec;
import org.xins.common.spec.InvalidSpecificationException;
import org.xins.common.spec.ParameterSpec;
import org.xins.common.text.ParseException;
import org.xins.common.types.Type;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.xins.common.xml.ElementList;

import org.znerd.xmlenc.XMLOutputter;

/**
 * The SOAP calling convention.
 * The SOAP message parsed by this calling convention is expected to match
 * the WSDL generated by the _WSDL meta-function.
 *
 * @version $Revision: 1.81 $ $Date: 2012/03/15 21:07:39 $
 * @author Anthony Goubard
 * @author Ernst de Haan
 */
public class SOAPCallingConvention extends CallingConvention {

   /**
    * The response encoding format.
    */
   protected static final String RESPONSE_ENCODING = "UTF-8";

   /**
    * The content type of the HTTP response.
    */
   protected static final String RESPONSE_CONTENT_TYPE = "text/xml; charset=" + RESPONSE_ENCODING;

   /**
    * The key used to store the name of the namespace in the request attributes.
    *
    * @since XINS 2.1.
    */
   protected static final String REQUEST_NAMESPACE = "_namespace";

   /**
    * The formatter for XINS Date type.
    */
   private static final SimpleDateFormat XINS_DATE_FORMATTER = new SimpleDateFormat("yyyyMMdd");

   /**
    * The formatter for SOAP Date type.
    */
   private static final SimpleDateFormat SOAP_DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");

   /**
    * The formatter for XINS Timestamp type.
    */
   private static final SimpleDateFormat XINS_TIMESTAMP_FORMATTER = new SimpleDateFormat("yyyyMMddHHmmss");

   /**
    * The formatter for SOAP dateType type.
    */
   private static final SimpleDateFormat SOAP_TIMESTAMP_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

   /**
    * The API. Never null.
    */
   private final API _api;

   /**
    * Creates a new SOAPCallingConvention instance.
    *
    * @param api
    *    the API, needed for the SOAP messages, cannot be null.
    *
    * @throws IllegalArgumentException
    *    if api == null.
    */
   public SOAPCallingConvention(API api) throws IllegalArgumentException {

      // Check arguments
      MandatoryArgumentChecker.check("api", api);

      // Store the API
      _api = api;
   }

   protected String[] getSupportedMethods() {
      return new String[] { "POST" };
   }

   /**
    * Checks if the specified request can be handled by this calling
    * convention.
    *
    * 

This method will not throw any exception. * * @param httpRequest * the HTTP request to investigate, cannot be null. * * @return * true if this calling convention is possibly * able to handle this request, or false if it * definitely not able to handle this request. * * @throws Exception * if analysis of the request causes an exception; * false will be assumed. */ protected boolean matches(HttpServletRequest httpRequest) throws Exception { // Parse the XML in the request (if any) Element element = parseXMLRequest(httpRequest); String elementName = element.getLocalName(); // The root element must be if (elementName.equals("Envelope")) { // There must be a element within the Element bodyElement = new ElementList(element, "Body").get(0); // There must be one child element ElementList bodyChildren = new ElementList(bodyElement); if (bodyChildren.size() == 1) { Element functionElement = (Element) bodyChildren.get(0); String functionElementName = functionElement.getLocalName(); // The name of the child element must match 'Request' return functionElementName.endsWith("Request") && functionElementName.length() > 7; } } return false; } protected FunctionRequest convertRequestImpl(HttpServletRequest httpRequest) throws InvalidRequestException, FunctionNotSpecifiedException { Element envelopeElem = parseXMLRequest(httpRequest); String envelopeName = envelopeElem.getLocalName(); if (! envelopeName.equals("Envelope")) { throw new InvalidRequestException("Root element is not a SOAP envelope but \"" + envelopeName + "\"."); } Element functionElem; try { Element bodyElem = new ElementList(envelopeElem, "Body").getUniqueChildElement(); functionElem = new ElementList(bodyElem).getUniqueChildElement(); } catch (ParseException pex) { throw new InvalidRequestException("Incorrect SOAP message.", pex); } String requestName = functionElem.getLocalName(); if (!requestName.endsWith("Request")) { throw new InvalidRequestException("Function names should always end " + "\"Request\" for the SOAP calling convention."); } String functionName = requestName.substring(0, requestName.lastIndexOf("Request")); Map backpack = new HashMap(); backpack.put(BackpackConstants.FUNCTION_NAME, functionName); backpack.put(REQUEST_NAMESPACE, functionElem.getNamespaceURI()); Element parametersElem; ElementList parametersList = new ElementList(functionElem, "parameters"); if (parametersList.isEmpty()) { parametersElem = functionElem; } else { parametersElem = parametersList.get(0); } // Parse the input parameters Map parameters = readInputParameters(parametersElem, functionName); // Parse the input data section Element transformedDataSection = readDataSection(parametersElem, functionName); return new FunctionRequest(functionName, parameters, transformedDataSection, backpack); } protected void convertResultImpl(FunctionResult xinsResult, HttpServletResponse httpResponse, Map backpack) throws IOException { // Send the XML output to the stream and flush httpResponse.setContentType(RESPONSE_CONTENT_TYPE); PrintWriter out = httpResponse.getWriter(); if (xinsResult.getErrorCode() != null) { httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } else { httpResponse.setStatus(HttpServletResponse.SC_OK); } // Store the result in a StringWriter before sending it. Writer buffer = new StringWriter(1024); // Create an XMLOutputter XMLOutputter xmlout = new XMLOutputter(buffer, RESPONSE_ENCODING); // Output the declaration // XXX: Make it configurable whether the declaration is output or not? xmlout.declaration(); // Write the envelope start tag xmlout.startTag("soap:Envelope"); xmlout.attribute("xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/"); // Write the body start tag xmlout.startTag("soap:Body"); String functionName = (String) backpack.get(BackpackConstants.FUNCTION_NAME); String namespaceURI = (String) backpack.get(REQUEST_NAMESPACE); if (xinsResult.getErrorCode() != null) { writeFaultSection(functionName, namespaceURI, xinsResult, xmlout); } else { // Write the response start tag xmlout.startTag("ns0:" + functionName + "Response"); xmlout.attribute("xmlns:ns0", namespaceURI); writeOutputParameters(functionName, xinsResult, xmlout); writeOutputDataSection(functionName, xinsResult, xmlout); xmlout.endTag(); // response } xmlout.endTag(); // body xmlout.endTag(); // envelope // Write the result to the servlet response out.write(buffer.toString()); out.close(); } /** * Reads the input parameters. * * @param parametersElem * the XML element which contains the parameters, cannot be null * * @param functionName * the name of the function called, cannot be null. * * @return * the parameters for the function, never null. */ protected Map readInputParameters(Element parametersElem, String functionName) { Map parameters = new HashMap(); for (Element parameterElem : new ElementList(parametersElem)) { String parameterName = parameterElem.getLocalName(); String parameterValue = parameterElem.getTextContent(); try { FunctionSpec functionSpec = _api.getAPISpecification().getFunction(functionName); Type parameterType = functionSpec.getInputParameter(parameterName).getType(); parameterValue = soapInputValueTransformation(parameterType, parameterValue); } catch (InvalidSpecificationException ise) { // keep the old value } catch (EntityNotFoundException enfe) { // keep the old value } parameters.put(parameterName, parameterValue); } return parameters; } /** * Reads the input parameters. * * @param parametersElem * the XML element which contains the parameters and data section, * cannot be null * * @param functionName * the name of the function called, cannot be null. * * @return * the data section for the function, can be null. * * @throws InvalidRequestException * if the SOAP request is invalid. */ protected Element readDataSection(Element parametersElem, String functionName) throws InvalidRequestException { Element transformedDataSection = null; ElementList dataSectionList = new ElementList(parametersElem, "data"); if (dataSectionList.size() == 1) { Element dataSection = (Element) dataSectionList.get(0); try { FunctionSpec functionSpec = _api.getAPISpecification().getFunction(functionName); Map dataSectionSpec = functionSpec.getInputDataSectionElements(); transformedDataSection = soapElementTransformation(dataSectionSpec, true, dataSection, true); } catch (InvalidSpecificationException ise) { // keep the old value transformedDataSection = dataSection; } catch (EntityNotFoundException enfe) { // keep the old value transformedDataSection = dataSection; } } else if (dataSectionList.size() > 1) { throw new InvalidRequestException("Only one data section is allowed."); } return transformedDataSection; } /** * Writes the fault section to the SOAP XML when an error code is returned * from the function call. * * @param functionName * the name of the function called. * * @param namespaceURI * the namespace URI to use for the parameters. * * @param xinsResult * the result of the call to the function. * * @param xmlout * the XML outputter to write the parameters in. * * @throws IOException * if the data cannot be written to the XML outputter for any reason. */ protected void writeFaultSection(String functionName, String namespaceURI, FunctionResult xinsResult, XMLOutputter xmlout) throws IOException { // Write the fault start tag xmlout.startTag("soap:Fault"); xmlout.startTag("faultcode"); if (xinsResult.getErrorCode().equals("_InvalidRequest")) { xmlout.pcdata("soap:Client"); } else { xmlout.pcdata("soap:Server"); } xmlout.endTag(); // faultcode xmlout.startTag("faultstring"); xmlout.pcdata(xinsResult.getErrorCode()); xmlout.endTag(); // faultstring if (!xinsResult.getParameters().isEmpty() || xinsResult.getDataElement() != null) { xmlout.startTag("detail"); xmlout.startTag("ns0:" + xinsResult.getErrorCode() + "Fault"); xmlout.attribute("xmlns:ns0", namespaceURI); writeOutputParameters(functionName, xinsResult, xmlout); writeOutputDataSection(functionName, xinsResult, xmlout); xmlout.endTag(); // ns0:Fault xmlout.endTag(); // detail } xmlout.endTag(); // fault } /** * Writes the output parameters to the SOAP XML. * * @param functionName * the name of the function called. * * @param xinsResult * the result of the call to the function. * * @param xmlout * the XML outputter to write the parameters in. * * @throws IOException * if the data cannot be written to the XML outputter for any reason. */ protected void writeOutputParameters(String functionName, FunctionResult xinsResult, XMLOutputter xmlout) throws IOException { Map parameters = xinsResult.getParameters(); for (Map.Entry outputParameter : parameters.entrySet()) { String parameterName = outputParameter.getKey(); String parameterValue = outputParameter.getValue(); if (xinsResult.getErrorCode() == null) { try { FunctionSpec functionSpec = _api.getAPISpecification().getFunction(functionName); Type parameterType = functionSpec.getOutputParameter(parameterName).getType(); parameterValue = soapOutputValueTransformation(parameterType, parameterValue); } catch (InvalidSpecificationException ise) { // keep the old value } catch (EntityNotFoundException enfe) { // keep the old value } } xmlout.startTag(parameterName); xmlout.pcdata(parameterValue); xmlout.endTag(); } } /** * Writes the output data section to the SOAP XML. * * @param functionName * the name of the function called. * * @param xinsResult * the result of the call to the function. * * @param xmlout * the XML outputter to write the data section in. * * @throws IOException * if the data cannot be written to the XML outputter for any reason. */ protected void writeOutputDataSection(String functionName, FunctionResult xinsResult, XMLOutputter xmlout) throws IOException { Element dataElement = xinsResult.getDataElement(); if (dataElement != null) { Element transformedDataElement = null; if (xinsResult.getErrorCode() == null) { try { FunctionSpec functionSpec = _api.getAPISpecification().getFunction(functionName); Map dataSectionSpec = functionSpec.getOutputDataSectionElements(); transformedDataElement = soapElementTransformation(dataSectionSpec, false, dataElement, true); } catch (InvalidSpecificationException ise) { // keep the old value } catch (EntityNotFoundException enfe) { // keep the old value } } /*ElementSerializer serializer = new ElementSerializer(); if (transformedDataElement == null) { serializer.output(xmlout, dataElement); } else { serializer.output(xmlout, transformedDataElement); }*/ } } /** * Transforms the value of a input SOAP parameter to the XINS equivalent. * * @param parameterType * the type of the parameter, cannot be null. * * @param value * the value of the SOAP parameter, cannot be null. * * @return * the XINS value, never null. * * @throws InvalidSpecificationException * if the specification is incorrect. */ protected String soapInputValueTransformation(Type parameterType, String value) throws InvalidSpecificationException { if (parameterType instanceof org.xins.common.types.standard.Boolean) { if (value.equals("1")) { return "true"; } else if (value.equals("0")) { return "false"; } } if (parameterType instanceof org.xins.common.types.standard.Date) { try { synchronized (SOAP_DATE_FORMATTER) { Date date = SOAP_DATE_FORMATTER.parse(value); return XINS_DATE_FORMATTER.format(date); } } catch (java.text.ParseException pe) { Utils.logProgrammingError(pe); } } if (parameterType instanceof org.xins.common.types.standard.Timestamp) { try { synchronized (SOAP_TIMESTAMP_FORMATTER) { Date date = SOAP_TIMESTAMP_FORMATTER.parse(value); return XINS_TIMESTAMP_FORMATTER.format(date); } } catch (java.text.ParseException pe) { Utils.logProgrammingError(pe); } } return value; } /** * Transforms the value of a output XINS parameter to the SOAP equivalent. * * @param parameterType * the type of the parameter, cannot be null. * * @param value * the value returned by the XINS function, cannot be null. * * @return * the SOAP value, never null. * * @throws InvalidSpecificationException * if the specification is incorrect. */ protected String soapOutputValueTransformation(Type parameterType, String value) throws InvalidSpecificationException { if (parameterType instanceof org.xins.common.types.standard.Date) { try { synchronized (SOAP_DATE_FORMATTER) { Date date = XINS_DATE_FORMATTER.parse(value); return SOAP_DATE_FORMATTER.format(date); } } catch (java.text.ParseException pe) { Utils.logProgrammingError(pe); } } if (parameterType instanceof org.xins.common.types.standard.Timestamp) { try { synchronized (SOAP_TIMESTAMP_FORMATTER) { Date date = XINS_TIMESTAMP_FORMATTER.parse(value); return SOAP_TIMESTAMP_FORMATTER.format(date); } } catch (java.text.ParseException pe) { Utils.logProgrammingError(pe); } } if (parameterType instanceof org.xins.common.types.standard.Hex) { return value.toUpperCase(); } return value; } /** * Convert the values of element to the required format. * * @param dataSection * the specification of the elements, cannot be null. * * @param input * true if it's the input parameter that should be transform, * false if it's the output parameter. * * @param element * the element node to process, cannot be null. * * @param top * true if it's the top element, false otherwise. * * @return * the converted value, never null. */ protected Element soapElementTransformation(Map dataSection, boolean input, Element element, boolean top) { String elementName = element.getLocalName(); if (elementName == null) { elementName = element.getTagName(); } String elementNameSpacePrefix = element.getPrefix(); String elementNameSpaceURI = element.getNamespaceURI(); NamedNodeMap elementAttributes = element.getAttributes(); String elementText = element.getTextContent(); ElementList elementChildren = new ElementList(element); Map childrenSpec = dataSection; String elementQualifiedName = (elementNameSpacePrefix == null) ? elementName : elementNameSpacePrefix + ":" + elementName; Element builder = element.getOwnerDocument().createElementNS(elementNameSpaceURI, elementQualifiedName); if (!top) { builder.setTextContent(elementText); // Find the DataSectionElement for this element. DataSectionElementSpec elementSpec = (DataSectionElementSpec) dataSection.get(elementName); childrenSpec = elementSpec.getSubElements(); // Go through the attributes for (int i = 0; i < elementAttributes.getLength(); i++) { Attr attribute = (Attr) elementAttributes.item(i); String attributeName = attribute.getName(); String attributeValue = attribute.getValue(); try { // Convert the value if needed ParameterSpec attributeSpec = elementSpec.getAttribute(attributeName); Type attributeType = attributeSpec.getType(); if (input) { attributeValue = soapInputValueTransformation(attributeType, attributeValue); } else { attributeValue = soapOutputValueTransformation(attributeType, attributeValue); } } catch (InvalidSpecificationException ise) { // Keep the old value } catch (EntityNotFoundException enfe) { // Keep the old value } setDataElementAttribute(builder, attributeName, attributeValue, elementNameSpacePrefix); } } // Add the children of this element for (Element nextChild : elementChildren) { Element transformedChild = soapElementTransformation(childrenSpec, input, nextChild, false); builder.appendChild(transformedChild); } return builder; } /** * Writes the attribute a output data element for the returned SOAP element. * * @param builder * the builder used to create the SOAP Element, cannot be null. * * @param attributeName * the name of the attribute, cannot be null. * * @param attributeValue * the value of the attribute, cannot be null. * * @param elementNameSpacePrefix * the namespace prefix of the parent element, can be null. * * @since XINS 2.1. */ protected void setDataElementAttribute(Element builder, String attributeName, String attributeValue, String elementNameSpacePrefix) { builder.setAttribute(attributeName, attributeValue); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy