From fad892c95f6e0e811f8ee356346cfbcc9f6a0fe8 Mon Sep 17 00:00:00 2001 From: Jacques Le Roux Date: Thu, 13 Sep 2012 09:57:46 +0000 Subject: [PATCH] =?UTF-8?q?"Applied=20fix=20from=20trunk=20for=20revision:?= =?UTF-8?q?=201243026"=20-------------------------------------------------?= =?UTF-8?q?-----------------------=20r1243026=20|=20jleroux=20|=202012-02-?= =?UTF-8?q?11=2011:50:56=20+0100=20(sam.,=2011=20f=C3=A9vr.=202012)=20|=20?= =?UTF-8?q?12=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OFBiz SOAP implementation was not handling * null values in Map * BigDecimal * correctly TimeStamp format (we use xsd:dateTime in ModelService.getTypes()) So CXF was not able to unmarshall a List of Maps with those types in them. It does not introduce any regressions: * A new specific SOAP null type is introduced using ("nillable","true") and ("xsi:nil","true) attributes. I had also to set the http://www.w3.org/2001/XMLSchema-instance schema on the null node. Because it was impossible to add it in the envelope header, not a big deal anyway. * I introduced a 'T' in the TimeStamp format. It's is OK, the deserialisation handles it well (we use xsd:dateTime, see http://www.w3.org/TR/xmlschema-2/#dateTime)/. We don't handle TimeZone... * BigDecimal was missing and is now correctly handled. I used 10 decimals and half up rounding (ROUND_HALF_UP) Also improves the SOAPEventHandler.sendError() method by passing the serviceName in the message ------------------------------------------------------------------------  git-svn-id: https://svn.apache.org/repos/asf/ofbiz/branches/release11.04@1384258 13f79535-47bb-0310-9956-ffa450edef68 --- .../ofbiz/entity/serialize/XmlSerializer.java | 19 ++++++-- .../src/org/ofbiz/service/ModelService.java | 47 +++++++++++++++++++ .../ofbiz/webapp/event/SOAPEventHandler.java | 43 ++++++++++------- 3 files changed, 89 insertions(+), 20 deletions(-) diff --git a/framework/entity/src/org/ofbiz/entity/serialize/XmlSerializer.java b/framework/entity/src/org/ofbiz/entity/serialize/XmlSerializer.java index c515ad4cd21..3f26f7c1c9c 100644 --- a/framework/entity/src/org/ofbiz/entity/serialize/XmlSerializer.java +++ b/framework/entity/src/org/ofbiz/entity/serialize/XmlSerializer.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.Serializable; import java.lang.ref.WeakReference; +import java.math.BigDecimal; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -130,7 +131,7 @@ public static Object deserialize(Document document, Delegator delegator) throws public static Element serializeSingle(Object object, Document document) throws SerializeException { if (document == null) return null; - if (object == null) return document.createElement("null"); + if (object == null) return makeElement("null", object, document); // - Standard Objects - if (object instanceof String) { @@ -147,9 +148,13 @@ public static Element serializeSingle(Object object, Document document) throws S return makeElement("std-Boolean", object, document); } else if (object instanceof Locale) { return makeElement("std-Locale", object, document); + } else if (object instanceof BigDecimal) { + String stringValue = ((BigDecimal) object).setScale(10, BigDecimal.ROUND_HALF_UP).toString(); + return makeElement("std-BigDecimal", stringValue, document); // - SQL Objects - } else if (object instanceof java.sql.Timestamp) { - return makeElement("sql-Timestamp", object, document); + String stringValue = object.toString().replace(' ', 'T'); + return makeElement("sql-Timestamp", stringValue, document); } else if (object instanceof java.sql.Date) { return makeElement("sql-Date", object, document); } else if (object instanceof java.sql.Time) { @@ -269,7 +274,15 @@ public static Element serializeCustom(Object object, Document document) throws S } public static Element makeElement(String elementName, Object value, Document document) { - if (value == null) return document.createElement("null"); + if (value == null) { + Element element = document.createElement("null"); + element.setAttribute("xsi:nil", "true"); + // I tried to put the schema in the envelope header (in createAndSendSOAPResponse) + // resEnv.declareNamespace("http://www.w3.org/2001/XMLSchema-instance", null); + // But it gets prefixed and that does not work. So adding in each instance + element.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); + return element; + } Element element = document.createElement(elementName); element.setAttribute("value", value.toString()); diff --git a/framework/service/src/org/ofbiz/service/ModelService.java b/framework/service/src/org/ofbiz/service/ModelService.java index 8027758e345..5dad54a452b 100644 --- a/framework/service/src/org/ofbiz/service/ModelService.java +++ b/framework/service/src/org/ofbiz/service/ModelService.java @@ -1358,6 +1358,18 @@ public Types getTypes(Document document, Definition def) { /*--------- Standard Objects --------*/ /*-----------------------------------*/ + /* null Element */ + Element stdNullElement = document.createElement("xsd:element"); + stdNullElement.setAttribute("name", "null"); + Element stdNullElement0 = document.createElement("xsd:complexType"); + stdNullElement.appendChild(stdNullElement0); + Element stdNullElement1 = document.createElement("xsd:attribute"); + stdNullElement0.appendChild(stdNullElement1); + stdNullElement1.setAttribute("name", "value"); + stdNullElement1.setAttribute("type", "xsd:string"); + stdNullElement1.setAttribute("use", "required"); + stdNullElement1.setAttribute("nillable", "true"); + schema.appendChild(stdNullElement); /* std-String Element */ Element stdStringElement = document.createElement("xsd:element"); stdStringElement.setAttribute("name", "std-String"); @@ -1435,6 +1447,17 @@ public Types getTypes(Document document, Definition def) { stdLocaleElement1.setAttribute("type", "xsd:string"); stdLocaleElement1.setAttribute("use", "required"); schema.appendChild(stdLocaleElement); + /* std-BigDecimal Element */ + Element stdBigDecimalElement = document.createElement("xsd:element"); + stdBigDecimalElement.setAttribute("name", "std-BigDecimal"); + Element stdBigDecimalElement0 = document.createElement("xsd:complexType"); + stdBigDecimalElement.appendChild(stdBigDecimalElement0); + Element stdBigDecimalElement1 = document.createElement("xsd:attribute"); + stdBigDecimalElement0.appendChild(stdBigDecimalElement1); + stdBigDecimalElement1.setAttribute("name", "value"); + stdBigDecimalElement1.setAttribute("type", "xsd:decimal"); + stdBigDecimalElement1.setAttribute("use", "required"); + schema.appendChild(stdBigDecimalElement); /*-----------------------------------*/ /*----------- SQL Objects -----------*/ @@ -1645,6 +1668,12 @@ public Types getTypes(Document document, Definition def) { mapValueComplexType.setAttribute("name", "map-Value"); Element mapValueComplexType0 = document.createElement("xsd:choice"); mapValueComplexType.appendChild(mapValueComplexType0); + Element mapValueComplexTypeNull = document.createElement("xsd:element"); + mapValueComplexTypeNull.setAttribute("ref", "tns:null"); + mapValueComplexTypeNull.setAttribute("minOccurs", "1"); + mapValueComplexTypeNull.setAttribute("maxOccurs", "1"); + mapValueComplexTypeNull.setAttribute("nillable", "true"); + mapValueComplexType0.appendChild(mapValueComplexTypeNull); Element mapValueComplexType1 = document.createElement("xsd:element"); mapValueComplexType1.setAttribute("ref", "tns:std-String"); mapValueComplexType1.setAttribute("minOccurs", "1"); @@ -1771,12 +1800,24 @@ public Types getTypes(Document document, Definition def) { mapValueComplexType25.setAttribute("maxOccurs", "1"); mapValueComplexType0.appendChild(mapValueComplexType25); schema.appendChild(mapValueComplexType); + Element mapValueComplexType26 = document.createElement("xsd:element"); + mapValueComplexType26.setAttribute("ref", "tns:std-BigDecimal"); + mapValueComplexType26.setAttribute("minOccurs", "1"); + mapValueComplexType26.setAttribute("maxOccurs", "1"); + mapValueComplexType0.appendChild(mapValueComplexType26); + schema.appendChild(mapValueComplexType); /* col-Collection Complex Type */ Element colCollectionComplexType = document.createElement("xsd:complexType"); colCollectionComplexType.setAttribute("name", "col-Collection"); Element colCollectionComplexType0 = document.createElement("xsd:choice"); colCollectionComplexType.appendChild(colCollectionComplexType0); + Element colCollectionComplexTypeNull = document.createElement("xsd:element"); + colCollectionComplexTypeNull.setAttribute("ref", "tns:null"); + colCollectionComplexTypeNull.setAttribute("minOccurs", "0"); + colCollectionComplexTypeNull.setAttribute("maxOccurs", "unbounded"); + colCollectionComplexTypeNull.setAttribute("nillable", "true"); + colCollectionComplexType0.appendChild(colCollectionComplexTypeNull); Element colCollectionComplexType1 = document.createElement("xsd:element"); colCollectionComplexType1.setAttribute("ref", "tns:std-String"); colCollectionComplexType1.setAttribute("minOccurs", "0"); @@ -1903,6 +1944,12 @@ public Types getTypes(Document document, Definition def) { colCollectionComplexType25.setAttribute("maxOccurs", "unbounded"); colCollectionComplexType0.appendChild(colCollectionComplexType25); schema.appendChild(colCollectionComplexType); + Element colCollectionComplexType26 = document.createElement("xsd:element"); + colCollectionComplexType26.setAttribute("ref", "tns:std-BigDecimal"); + colCollectionComplexType26.setAttribute("minOccurs", "0"); + colCollectionComplexType26.setAttribute("maxOccurs", "unbounded"); + colCollectionComplexType0.appendChild(colCollectionComplexType26); + schema.appendChild(colCollectionComplexType); types.setDocumentationElement(schema); return types; diff --git a/framework/webapp/src/org/ofbiz/webapp/event/SOAPEventHandler.java b/framework/webapp/src/org/ofbiz/webapp/event/SOAPEventHandler.java index ee3b889fa85..150a76817ea 100644 --- a/framework/webapp/src/org/ofbiz/webapp/event/SOAPEventHandler.java +++ b/framework/webapp/src/org/ofbiz/webapp/event/SOAPEventHandler.java @@ -96,7 +96,7 @@ public String invoke(Event event, RequestMap requestMap, HttpServletRequest requ } catch (GenericServiceException e) { serviceName = null; } catch (WSDLException e) { - sendError(response, "Unable to obtain WSDL"); + sendError(response, "Unable to obtain WSDL", serviceName); throw new EventHandlerException("Unable to obtain WSDL", e); } @@ -111,7 +111,7 @@ public String invoke(Event event, RequestMap requestMap, HttpServletRequest requ } return null; } else { - sendError(response, "Unable to obtain WSDL"); + sendError(response, "Unable to obtain WSDL", serviceName); throw new EventHandlerException("Unable to obtain WSDL"); } } @@ -136,7 +136,7 @@ public String invoke(Event event, RequestMap requestMap, HttpServletRequest requ writer.flush(); return null; } catch (Exception e) { - sendError(response, "Unable to obtain WSDL"); + sendError(response, "Unable to obtain WSDL", null); throw new EventHandlerException("Unable to obtain WSDL"); } } @@ -162,30 +162,31 @@ public String invoke(Event event, RequestMap requestMap, HttpServletRequest requ } } } catch (Exception e) { - sendError(response, "Problem processing the service"); + sendError(response, "Problem processing the service", null); throw new EventHandlerException("Cannot get the envelope", e); } Debug.logVerbose("[Processing]: SOAP Event", module); + String serviceName = null; try { SOAPBody reqBody = reqEnv.getBody(); validateSOAPBody(reqBody); OMElement serviceElement = reqBody.getFirstElement(); - String serviceName = serviceElement.getLocalName(); + serviceName = serviceElement.getLocalName(); Map parameters = UtilGenerics.cast(SoapSerializer.deserialize(serviceElement.toString(), delegator)); try { // verify the service is exported for remote execution and invoke it ModelService model = dispatcher.getDispatchContext().getModelService(serviceName); if (model == null) { - sendError(response, "Problem processing the service"); + sendError(response, "Problem processing the service", serviceName); Debug.logError("Could not find Service [" + serviceName + "].", module); return null; } if (!model.export) { - sendError(response, "Problem processing the service"); + sendError(response, "Problem processing the service", serviceName); Debug.logError("Trying to call Service [" + serviceName + "] that is not exported.", module); return null; } @@ -197,19 +198,19 @@ public String invoke(Event event, RequestMap requestMap, HttpServletRequest requ } catch (GenericServiceException e) { if (UtilProperties.getPropertyAsBoolean("service", "secureSoapAnswer", true)) { - sendError(response, "Problem processing the service, check your parameters."); + sendError(response, "Problem processing the service, check your parameters.", serviceName); } else { if(e.getMessageList() == null) { - sendError(response, e.getMessage()); + sendError(response, e.getMessage(), serviceName); } else { - sendError(response, e.getMessageList()); + sendError(response, e.getMessageList(), serviceName); } Debug.logError(e, module); return null; } } } catch (Exception e) { - sendError(response, e.getMessage()); + sendError(response, e.getMessage(), serviceName); Debug.logError(e, module); return null; } @@ -235,6 +236,7 @@ private void createAndSendSOAPResponse(Map serviceResults, Strin // setup the response Debug.logVerbose("[EventHandler] : Setting up response message", module); String xmlResults = SoapSerializer.serialize(serviceResults); + //Debug.log("xmlResults ==================" + xmlResults, module); XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(xmlResults)); StAXOMBuilder resultsBuilder = new StAXOMBuilder(reader); OMElement resultSer = resultsBuilder.getDocumentElement(); @@ -271,15 +273,15 @@ private void createAndSendSOAPResponse(Map serviceResults, Strin } } - private void sendError(HttpServletResponse res, String errorMessage) throws EventHandlerException { + private void sendError(HttpServletResponse res, String errorMessage, String serviceName) throws EventHandlerException { // setup the response - sendError(res, ServiceUtil.returnError(errorMessage)); + sendError(res, ServiceUtil.returnError(errorMessage), serviceName); } - private void sendError(HttpServletResponse res, List errorMessages) throws EventHandlerException { - sendError(res, ServiceUtil.returnError(errorMessages)); + private void sendError(HttpServletResponse res, List errorMessages, String serviceName) throws EventHandlerException { + sendError(res, ServiceUtil.returnError(errorMessages.toString()), serviceName); } - private void sendError(HttpServletResponse res, Object object) throws EventHandlerException { + private void sendError(HttpServletResponse res, Object object, String serviceName) throws EventHandlerException { try { // setup the response res.setContentType("text/xml"); @@ -292,11 +294,18 @@ private void sendError(HttpServletResponse res, Object object) throws EventHandl SOAPFactory factory = OMAbstractFactory.getSOAP11Factory(); SOAPEnvelope resEnv = factory.createSOAPEnvelope(); SOAPBody resBody = factory.createSOAPBody(); - OMElement errMsg = factory.createOMElement(new QName("Response")); + OMElement errMsg = factory.createOMElement(new QName((serviceName != null ? serviceName : "") + "Response")); errMsg.addChild(resultSer.getFirstElement()); resBody.addChild(errMsg); resEnv.addChild(resBody); + // The declareDefaultNamespace method doesn't work see (https://issues.apache.org/jira/browse/AXIS2-3156) + // so the following doesn't work: + // resService.declareDefaultNamespace(ModelService.TNS); + // instead, create the xmlns attribute directly: + OMAttribute defaultNS = factory.createOMAttribute("xmlns", null, ModelService.TNS); + errMsg.addAttribute(defaultNS); + // log the response message if (Debug.verboseOn()) { try {