| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.ode.axis2.httpbinding; |
| |
| import org.apache.commons.httpclient.Header; |
| import org.apache.commons.httpclient.HttpMethod; |
| import org.apache.commons.httpclient.methods.DeleteMethod; |
| import org.apache.commons.httpclient.methods.EntityEnclosingMethod; |
| import org.apache.commons.httpclient.methods.GetMethod; |
| import org.apache.commons.httpclient.methods.PostMethod; |
| import org.apache.commons.httpclient.methods.PutMethod; |
| import org.apache.commons.httpclient.methods.RequestEntity; |
| import org.apache.commons.httpclient.methods.StringRequestEntity; |
| import org.apache.commons.httpclient.params.HostParams; |
| import org.apache.commons.httpclient.params.HttpParams; |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.ode.utils.Properties; |
| import org.apache.ode.axis2.util.URLEncodedTransformer; |
| import org.apache.ode.axis2.util.UrlReplacementTransformer; |
| import org.apache.ode.bpel.epr.MutableEndpoint; |
| import org.apache.ode.bpel.iapi.PartnerRoleMessageExchange; |
| import org.apache.ode.utils.DOMUtils; |
| import org.apache.ode.utils.Namespaces; |
| import org.apache.ode.utils.http.HttpUtils; |
| import static org.apache.ode.utils.http.HttpUtils.bodyAllowed; |
| import static org.apache.ode.utils.http.StatusCode._202_ACCEPTED; |
| import org.apache.ode.utils.wsdl.Messages; |
| import org.apache.ode.utils.wsdl.WsdlUtils; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.xml.sax.SAXException; |
| |
| import javax.wsdl.Binding; |
| import javax.wsdl.BindingInput; |
| import javax.wsdl.BindingOperation; |
| import javax.wsdl.BindingOutput; |
| import javax.wsdl.Definition; |
| import javax.wsdl.Fault; |
| import javax.wsdl.Message; |
| import javax.wsdl.Operation; |
| import javax.wsdl.Part; |
| import javax.wsdl.extensions.UnknownExtensibilityElement; |
| import javax.wsdl.extensions.http.HTTPOperation; |
| import javax.wsdl.extensions.mime.MIMEContent; |
| import javax.xml.namespace.QName; |
| import java.io.IOException; |
| import java.io.UnsupportedEncodingException; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.HashSet; |
| |
| public class HttpMethodConverter { |
| |
| private static final Log log = LogFactory.getLog(HttpMethodConverter.class); |
| |
| protected static final Messages msgs = Messages.getMessages(Messages.class); |
| protected Definition definition; |
| protected Binding binding; |
| protected QName serviceName; |
| protected String portName; |
| |
| public HttpMethodConverter(Definition definition, QName serviceName, String portName) { |
| this.definition = definition; |
| this.binding = definition.getService(serviceName).getPort(portName).getBinding(); |
| this.serviceName = serviceName; |
| this.portName = portName; |
| } |
| |
| |
| public HttpMethod createHttpRequest(PartnerRoleMessageExchange odeMex, HttpParams params) throws UnsupportedEncodingException { |
| return createHttpRequest(odeMex, params, ((MutableEndpoint) odeMex.getEndpointReference()).getUrl()); |
| } |
| |
| public HttpMethod createHttpRequest(PartnerRoleMessageExchange odeMex, HttpParams params, String baseUrl) throws UnsupportedEncodingException { |
| Operation operation = odeMex.getOperation(); |
| BindingOperation bindingOperation = binding.getBindingOperation(operation.getName(), operation.getInput().getName(), operation.getOutput().getName()); |
| |
| // message to be sent |
| Element message = odeMex.getRequest().getMessage(); |
| Message msgDef = operation.getInput().getMessage(); |
| |
| // extract part values into a map and check that all parts are assigned a value |
| Map<String, Element> partElements = extractPartElements(msgDef, message); |
| |
| // http method type |
| // the operation may override the verb, this is an extension for RESTful BPEL |
| String verb = WsdlUtils.resolveVerb(binding, bindingOperation); |
| |
| // build the http method itself |
| HttpMethod method = prepareHttpMethod(bindingOperation, verb, partElements, odeMex.getRequest().getHeaderParts(), baseUrl, params); |
| |
| return method; |
| } |
| |
| |
| /** |
| * create and initialize the http method. |
| * Http Headers that may been passed in the params are not set in this method. |
| * Headers will be automatically set by HttpClient. |
| * See usages of HostParams.DEFAULT_HEADERS |
| * See org.apache.commons.httpclient.HttpMethodDirector#executeMethod(org.apache.commons.httpclient.HttpMethod) |
| */ |
| protected HttpMethod prepareHttpMethod(BindingOperation opBinding, String verb, Map<String, Element> partValues, Map<String, Node> headers, |
| final String rootUri, HttpParams params) throws UnsupportedEncodingException { |
| if (log.isDebugEnabled()) log.debug("Preparing http request..."); |
| // convenience variables... |
| BindingInput bindingInput = opBinding.getBindingInput(); |
| HTTPOperation httpOperation = (HTTPOperation) WsdlUtils.getOperationExtension(opBinding); |
| MIMEContent content = WsdlUtils.getMimeContent(bindingInput.getExtensibilityElements()); |
| String contentType = content == null ? null : content.getType(); |
| boolean useUrlEncoded = WsdlUtils.useUrlEncoded(bindingInput) || PostMethod.FORM_URL_ENCODED_CONTENT_TYPE.equalsIgnoreCase(contentType); |
| boolean useUrlReplacement = WsdlUtils.useUrlReplacement(bindingInput); |
| |
| // the http method to be built and returned |
| HttpMethod method = null; |
| |
| // the 4 elements the http method may be made of |
| String relativeUri = httpOperation.getLocationURI(); |
| String queryPath = null; |
| RequestEntity requestEntity; |
| String encodedParams = null; |
| |
| // ODE supports uri template in both port and operation location. |
| // so assemble the final url *before* replacement |
| String completeUri = rootUri; |
| if (StringUtils.isNotEmpty(relativeUri)) { |
| completeUri = completeUri + (completeUri.endsWith("/") || relativeUri.startsWith("/") ? "" : "/") + relativeUri; |
| } |
| |
| if (useUrlReplacement) { |
| // insert part values in the url |
| completeUri = new UrlReplacementTransformer().transform(completeUri, partValues); |
| } else if (useUrlEncoded) { |
| // encode part values |
| encodedParams = new URLEncodedTransformer().transform(partValues); |
| } |
| |
| // http-client api is not really neat |
| // something similar to the following would save some if/else manipulations. |
| // But we have to deal with it as-is. |
| // |
| // method = new Method(verb); |
| // method.setRequestEnity(..) |
| // etc... |
| if ("GET".equalsIgnoreCase(verb) || "DELETE".equalsIgnoreCase(verb)) { |
| if ("GET".equalsIgnoreCase(verb)) { |
| method = new GetMethod(); |
| } else if ("DELETE".equalsIgnoreCase(verb)) { |
| method = new DeleteMethod(); |
| } |
| method.getParams().setDefaults(params); |
| if (useUrlEncoded) { |
| queryPath = encodedParams; |
| } |
| |
| // Let http-client manage the redirection |
| // see org.apache.commons.httpclient.params.HttpClientParams.MAX_REDIRECTS |
| // default is 100 |
| method.setFollowRedirects(true); |
| } else if ("POST".equalsIgnoreCase(verb) || "PUT".equalsIgnoreCase(verb)) { |
| |
| if ("POST".equalsIgnoreCase(verb)) { |
| method = new PostMethod(); |
| } else if ("PUT".equalsIgnoreCase(verb)) { |
| method = new PutMethod(); |
| } |
| method.getParams().setDefaults(params); |
| // some body-building... |
| final String contentCharset = method.getParams().getContentCharset(); |
| if (log.isDebugEnabled()) log.debug("Content-Type [" + contentType + "] Charset [" + contentCharset + "]"); |
| if (useUrlEncoded) { |
| requestEntity = new StringRequestEntity(encodedParams, PostMethod.FORM_URL_ENCODED_CONTENT_TYPE, contentCharset); |
| } else { |
| // get the part to be put in the body |
| Part part = opBinding.getOperation().getInput().getMessage().getPart(content.getPart()); |
| Element partValue = partValues.get(part.getName()); |
| |
| if (part.getElementName() == null) { |
| String errMsg = "XML Types are not supported. Parts must use elements."; |
| if (log.isErrorEnabled()) log.error(errMsg); |
| throw new RuntimeException(errMsg); |
| } else if (HttpUtils.isXml(contentType)) { |
| if (log.isDebugEnabled()) log.debug("Content-Type [" + contentType + "] equivalent to 'text/xml'"); |
| // stringify the first element |
| String xmlString = DOMUtils.domToString(DOMUtils.getFirstChildElement(partValue)); |
| requestEntity = new StringRequestEntity(xmlString, contentType, contentCharset); |
| } else { |
| if (log.isDebugEnabled()) |
| log.debug("Content-Type [" + contentType + "] NOT equivalent to 'text/xml'. The text content of part value will be sent as text"); |
| // encoding conversion is managed by StringRequestEntity if necessary |
| requestEntity = new StringRequestEntity(DOMUtils.getTextContent(partValue), contentType, contentCharset); |
| } |
| } |
| |
| // cast safely, PUT and POST are subclasses of EntityEnclosingMethod |
| final EntityEnclosingMethod enclosingMethod = (EntityEnclosingMethod) method; |
| enclosingMethod.setRequestEntity(requestEntity); |
| enclosingMethod.setContentChunked(params.getBooleanParameter(Properties.PROP_HTTP_REQUEST_CHUNK, false)); |
| |
| } else { |
| // should not happen because of HttpBindingValidator, but never say never |
| throw new IllegalArgumentException("Unsupported HTTP method: " + verb); |
| } |
| |
| method.setPath(completeUri); // assumes that the path is properly encoded (URL safe). |
| method.setQueryString(queryPath); |
| |
| // set headers |
| setHttpRequestHeaders(method, opBinding, partValues, headers, params); |
| return method; |
| } |
| |
| /** |
| * First go through the list of default headers set in the method params. This param is then remove to avoid interference with HttpClient. |
| * Actually the default headers should be overriden by any headers set from the process. |
| * Not to mention that, for a given header, HttpClient do not overwrite any previous values but simply append the default value.<br/> |
| * See {@link see org.apache.commons.httpclient.params.HostParams.DEFAULT_HEADERS} |
| * <p/> |
| * Then go through the list of message headers and set them if empty. |
| * <p/> |
| * Finally go through the list of {@linkplain Namespaces.ODE_HTTP_EXTENSION_NS}{@code :header} elements included in the input binding. |
| * For each of them, set the HTTP Request Header with the static value defined by the attribute {@linkplain Namespaces.ODE_HTTP_EXTENSION_NS}{@code :value}, |
| * or the part value mentioned in the attribute {@linkplain Namespaces.ODE_HTTP_EXTENSION_NS}{@code :part}. |
| * <p/> |
| * Finally, set the 'Accept' header if the output content type of the operation exists. |
| * <p/> |
| * Notice that the last header value overrides any values set previoulsy. Meaning that message headers might get overriden by parts bound to headers. |
| * |
| */ |
| public void setHttpRequestHeaders(HttpMethod method, BindingOperation opBinding, Map<String, Element> partValues, Map<String, Node> headers, HttpParams params) { |
| BindingInput inputBinding = opBinding.getBindingInput(); |
| Message inputMessage = opBinding.getOperation().getInput().getMessage(); |
| |
| // Do not let HttpClient manage the default headers |
| // Actually the default headers should be overriden by any headers set from the process. |
| // (Not to mention that, for a given header, HttpClient do not overwrite any previous values but simply append the default value) |
| Collection defaultHeaders = (Collection) params.getParameter(HostParams.DEFAULT_HEADERS); |
| if (defaultHeaders != null) { |
| Iterator i = defaultHeaders.iterator(); |
| while (i.hasNext()) { |
| method.setRequestHeader((Header) i.next()); |
| } |
| } |
| |
| // process message headers |
| for (Iterator<Map.Entry<String, Node>> iterator = headers.entrySet().iterator(); iterator.hasNext();) { |
| Map.Entry<String, Node> e = iterator.next(); |
| String headerName = e.getKey(); |
| Node headerNode = e.getValue(); |
| String headerValue = DOMUtils.domToString(headerNode); |
| method.setRequestHeader(headerName, HttpHelper.replaceCRLFwithLWS(headerValue)); |
| } |
| |
| // process parts that are bound to message parts |
| Collection<UnknownExtensibilityElement> headerBindings = WsdlUtils.getHttpHeaders(inputBinding.getExtensibilityElements()); |
| for (Iterator<UnknownExtensibilityElement> iterator = headerBindings.iterator(); iterator.hasNext();) { |
| Element binding = iterator.next().getElement(); |
| String headerName = binding.getAttribute("name"); |
| String partName = binding.getAttribute("part"); |
| String value = binding.getAttribute("value"); |
| |
| /* Header binding may use a part or a static value */ |
| String headerValue; |
| if (StringUtils.isNotEmpty(partName)) { |
| // 'part' attribute is used |
| // get the part to be put in the header |
| Part part = inputMessage.getPart(partName); |
| Element partWrapper = partValues.get(part.getName()); |
| if (DOMUtils.isEmptyElement(partWrapper)) { |
| headerValue = ""; |
| } else { |
| /* |
| The expected part value could be a simple type |
| or an element of a simple type. |
| So if a element is there, take its text content |
| else take the text content of the part element itself |
| */ |
| Element childElement = DOMUtils.getFirstChildElement(partWrapper); |
| if (childElement != null) { |
| if (DOMUtils.getFirstChildElement(childElement) != null) { |
| String errMsg = "Complex types are not supported. Header Parts must be simple types or elements of a simple type."; |
| if (log.isErrorEnabled()) log.error(errMsg); |
| throw new RuntimeException(errMsg); |
| } else { |
| headerValue = DOMUtils.getTextContent(childElement); |
| } |
| } else { |
| headerValue = DOMUtils.getTextContent(partWrapper); |
| } |
| } |
| } else if (StringUtils.isNotEmpty(value)) { |
| // 'value' attribute is used, this header is a static value |
| headerValue = value; |
| } else { |
| String errMsg = "Invalid binding: missing attribute! Expecting " + new QName(Namespaces.ODE_HTTP_EXTENSION_NS, "part") + " or " + new QName(Namespaces.ODE_HTTP_EXTENSION_NS, "value"); |
| if (log.isErrorEnabled()) log.error(errMsg); |
| throw new RuntimeException(errMsg); |
| } |
| // do not set the header isf the value is empty |
| if (StringUtils.isNotEmpty(headerValue)) |
| method.setRequestHeader(headerName, HttpHelper.replaceCRLFwithLWS(headerValue)); |
| } |
| |
| MIMEContent outputContent = WsdlUtils.getMimeContent(opBinding.getBindingOutput().getExtensibilityElements()); |
| // set Accept header if output content type is set |
| if (outputContent != null) { |
| method.setRequestHeader("Accept", outputContent.getType()); |
| } |
| |
| } |
| |
| |
| protected Map<String, Element> extractPartElements(Message msgDef, Element message) { |
| if (msgDef.getParts().size() != 0 && message == null) { |
| throw new IllegalArgumentException(msgs.msgOdeMessageExpected()); |
| } |
| Map<String, Element> partValues = new HashMap<String, Element>(); |
| for (Iterator iterator = msgDef.getParts().values().iterator(); iterator.hasNext();) { |
| Part part = (Part) iterator.next(); |
| Element partEl = DOMUtils.findChildByName(message, new QName(null, part.getName())); |
| if (partEl == null) |
| throw new IllegalArgumentException(msgs.msgOdeMessageMissingRequiredPart(part.getName())); |
| partValues.put(part.getName(), partEl); |
| } |
| return partValues; |
| } |
| |
| |
| /** |
| * Create the element to be associated with this part into the {@link org.apache.ode.bpel.iapi.Message}. |
| * <br/>An element named with the part name will be returned. the content of this element depends on the part. |
| * <p/>If the part has a non-null element name, a new element will be created and named accordingly then the text value is inserted in this new element. |
| * <br/>else the given text content is simply set on the part element. |
| * |
| * @param part |
| * @param textContent |
| * @return an element named with the part name will be returned |
| */ |
| public Element createPartElement(Part part, String textContent) { |
| Document doc = DOMUtils.newDocument(); |
| Element partElement = doc.createElementNS(null, part.getName()); |
| if (part.getElementName() != null) { |
| Element element = doc.createElementNS(part.getElementName().getNamespaceURI(), part.getElementName().getLocalPart()); |
| element.setTextContent(textContent); |
| partElement.appendChild(element); |
| } else { |
| partElement.setTextContent(textContent); |
| } |
| return partElement; |
| } |
| |
| |
| /** |
| * Create the element to be associated with this part into the {@link org.apache.ode.bpel.iapi.Message}. |
| * <p/>If the part has a non-null element name, the bodyElement is simply appended. |
| * Else if the bodyElement has a text content, the value is set to the message. |
| * Else append all nodes of bodyElement to the returned element. Attributes are ignored. |
| * <p/> |
| * The name of the returned element is the part name. |
| * |
| * @param part |
| * @param receivedElement |
| * @return the element to insert "as is" to ODE message |
| */ |
| public Element createPartElement(Part part, Element receivedElement) { |
| Document doc = DOMUtils.newDocument(); |
| Element partElement = doc.createElementNS(null, part.getName()); |
| if (part.getElementName() != null) { |
| partElement.appendChild(doc.importNode(receivedElement, true)); |
| } else { |
| if (DOMUtils.isEmptyElement(receivedElement)) { |
| // Append an empty text node. |
| // Warning! setting an empty string with setTextContent has not effect. See javadoc. |
| partElement.appendChild(doc.createTextNode("")); |
| } else { |
| // No need to make the distinction between simple and complex types, importNode will handle it |
| // !!! Attributes are ignored |
| for (int m = 0; m < receivedElement.getChildNodes().getLength(); m++) { |
| Node child = receivedElement.getChildNodes().item(m); |
| partElement.appendChild(doc.importNode(child, true)); |
| } |
| } |
| } |
| return partElement; |
| } |
| |
| /** |
| * Process the HTTP Response Headers. |
| * <p/> |
| * First go through the list of {@linkplain Namespaces.ODE_HTTP_EXTENSION_NS}{@code :header} elements included in the output binding. |
| * For each of them, set the header value as the value of the message part. |
| * <p/> |
| * Then add all HTTP headers as header part in the message. The name of the header would be the part name. |
| * <p/> |
| * Finally, insert a header names 'Status-Line'. This header contains an element as returned by {@link HttpHelper#statusLineToElement(String)} . |
| * |
| * @param odeMessage |
| * @param method |
| * @param operationDef |
| */ |
| public void extractHttpResponseHeaders(org.apache.ode.bpel.iapi.Message odeMessage, HttpMethod method, Operation operationDef) { |
| Message messageDef = operationDef.getOutput().getMessage(); |
| |
| BindingOutput outputBinding = binding.getBindingOperation(operationDef.getName(), operationDef.getInput().getName(), operationDef.getOutput().getName()).getBindingOutput(); |
| Collection<UnknownExtensibilityElement> headerBindings = WsdlUtils.getHttpHeaders(outputBinding.getExtensibilityElements()); |
| |
| // iterate through the list of header bindings |
| // and set the message parts accordingly |
| for (Iterator<UnknownExtensibilityElement> iterator = headerBindings.iterator(); iterator.hasNext();) { |
| Element binding = iterator.next().getElement(); |
| String partName = binding.getAttribute("part"); |
| String headerName = binding.getAttribute("name"); |
| |
| Part part = messageDef.getPart(partName); |
| if (StringUtils.isNotEmpty(partName)) { |
| Header responseHeader = method.getResponseHeader(headerName); |
| // if the response header is not set, just skip it. no need to fail. |
| if (responseHeader != null) { |
| odeMessage.setPart(partName, createPartElement(part, responseHeader.getValue())); |
| } |
| } else { |
| String errMsg = "Invalid binding: missing required attribute! Part name: " + new QName(Namespaces.ODE_HTTP_EXTENSION_NS, "part"); |
| if (log.isErrorEnabled()) log.error(errMsg); |
| throw new RuntimeException(errMsg); |
| } |
| } |
| |
| // add all HTTP response headers (in their condensed form) into the message as header parts |
| Set<String> headerNames = new HashSet<String>(); |
| for (Header header : method.getResponseHeaders()) headerNames.add(header.getName()); |
| for(String hname:headerNames) odeMessage.setHeaderPart(hname, method.getResponseHeader(hname).getValue()); |
| |
| // make the status line information available as a single element |
| odeMessage.setHeaderPart("Status-Line", HttpHelper.statusLineToElement(method.getStatusLine())); |
| } |
| |
| public void parseHttpResponse(org.apache.ode.bpel.iapi.Message odeResponse, HttpMethod method, Operation opDef) throws SAXException, IOException { |
| BindingOperation opBinding = binding.getBindingOperation(opDef.getName(), opDef.getInput().getName(), opDef.getOutput().getName()); |
| /* process headers */ |
| extractHttpResponseHeaders(odeResponse, method, opDef); |
| |
| /* process the body if any */ |
| |
| // assumption is made that a response may have at most one body. HttpBindingValidator checks this. |
| MIMEContent outputContent = WsdlUtils.getMimeContent(opBinding.getBindingOutput().getExtensibilityElements()); |
| int status = method.getStatusCode(); |
| |
| boolean xmlExpected = outputContent != null && HttpUtils.isXml(outputContent.getType()); |
| // '202/Accepted' and '204/No Content' status codes explicitly state that there is no body, so we should not fail even if a part is bound to the body response |
| boolean isBodyExpected = outputContent != null; |
| boolean isBodyMandatory = isBodyExpected && bodyAllowed(status) && status != _202_ACCEPTED; |
| final String body; |
| try { |
| body = method.getResponseBodyAsString(); |
| } catch (IOException e) { |
| throw new RuntimeException("Unable to get the request body : " + e.getMessage()); |
| } |
| |
| final boolean emptyBody = StringUtils.isEmpty(body); |
| if (emptyBody) { |
| if (isBodyMandatory) { |
| throw new RuntimeException("Response body is mandatory but missing!"); |
| } |
| } else { |
| if (isBodyExpected) { |
| Part partDef = opDef.getOutput().getMessage().getPart(outputContent.getPart()); |
| Element partElement; |
| |
| if (xmlExpected) { |
| |
| Header h = method.getResponseHeader("Content-Type"); |
| String receivedType = h != null ? h.getValue() : null; |
| boolean contentTypeSet = receivedType != null; |
| boolean xmlReceived = contentTypeSet && HttpUtils.isXml(receivedType); |
| |
| // a few checks |
| if (!contentTypeSet) { |
| if (log.isDebugEnabled()) |
| log.debug("Received Response with a body but no 'Content-Type' header!"); |
| } else if (!xmlReceived) { |
| if (log.isDebugEnabled()) |
| log.debug("Xml type was expected but non-xml type received! Expected Content-Type=" + outputContent.getType() + " Received Content-Type=" + receivedType); |
| } |
| |
| // parse the body and create the message part |
| Element bodyElement = DOMUtils.stringToDOM(body); |
| partElement = createPartElement(partDef, bodyElement); |
| } else { |
| // if not xml, process it as text |
| partElement = createPartElement(partDef, body); |
| } |
| |
| // set the part |
| odeResponse.setPart(partDef.getName(), partElement); |
| |
| } else { |
| // the body was not expected but we don't know how to deal with it |
| if (log.isDebugEnabled()) log.debug("Body received but not mapped to any part! Body=\n" + body); |
| } |
| } |
| } |
| |
| public Object[] parseFault(PartnerRoleMessageExchange odeMex, HttpMethod method) { |
| Operation opDef = odeMex.getOperation(); |
| BindingOperation opBinding = binding.getBindingOperation(opDef.getName(), opDef.getInput().getName(), opDef.getOutput().getName()); |
| |
| final String body; |
| try { |
| body = method.getResponseBodyAsString(); |
| } catch (IOException e) { |
| throw new RuntimeException("Unable to get the request body : " + e.getMessage(), e); |
| } |
| Header h = method.getResponseHeader("Content-Type"); |
| String receivedType = h != null ? h.getValue() : null; |
| if (opDef.getFaults().isEmpty()) { |
| throw new RuntimeException("Operation [" + opDef.getName() + "] has no fault. This " + method.getStatusCode() + " error will be considered as a failure."); |
| } else if (opBinding.getBindingFaults().isEmpty()) { |
| throw new RuntimeException("No fault binding. This " + method.getStatusCode() + " error will be considered as a failure."); |
| } else if (StringUtils.isEmpty(body)) { |
| throw new RuntimeException("No body in the response. This " + method.getStatusCode() + " error will be considered as a failure."); |
| } else if (receivedType != null && !HttpUtils.isXml(receivedType)) { |
| throw new RuntimeException("Response Content-Type [" + receivedType + "] does not describe XML entities. Faults must be XML. This " + method.getStatusCode() + " error will be considered as a failure."); |
| } else { |
| |
| if (receivedType == null) { |
| if (log.isWarnEnabled()) |
| log.warn("[Service: " + serviceName + ", Port: " + portName + ", Operation: " + opDef.getName() + "] Received Response with a body but no 'Content-Type' header! Will try to parse nevertheless."); |
| } |
| |
| // try to parse body |
| final Element bodyElement; |
| try { |
| bodyElement = DOMUtils.stringToDOM(body); |
| } catch (Exception e) { |
| throw new RuntimeException("Unable to parse the response body as xml. This " + method.getStatusCode() + " error will be considered as a failure.", e); |
| } |
| |
| // Guess which fault it is |
| QName bodyName = new QName(bodyElement.getNamespaceURI(), bodyElement.getNodeName()); |
| Fault faultDef = WsdlUtils.inferFault(opDef, bodyName); |
| |
| if (faultDef == null) { |
| throw new RuntimeException("Unknown Fault Type [" + bodyName + "] This " + method.getStatusCode() + " error will be considered as a failure."); |
| } else if (!WsdlUtils.isOdeFault(opBinding.getBindingFault(faultDef.getName()))) { |
| // is this fault bound with ODE extension? |
| throw new RuntimeException("Fault [" + bodyName + "] is not bound with " + new QName(Namespaces.ODE_HTTP_EXTENSION_NS, "fault") + ". This " + method.getStatusCode() + " error will be considered as a failure."); |
| } else { |
| // a fault has only one part |
| Part partDef = (Part) faultDef.getMessage().getParts().values().iterator().next(); |
| |
| QName faultName = new QName(definition.getTargetNamespace(), faultDef.getName()); |
| QName faultType = faultDef.getMessage().getQName(); |
| |
| // create the ODE Message now that we know the fault |
| org.apache.ode.bpel.iapi.Message response = odeMex.createMessage(faultType); |
| |
| // build the element to be sent back |
| Element partElement = createPartElement(partDef, bodyElement); |
| response.setPart(partDef.getName(), partElement); |
| |
| // extract and set headers |
| extractHttpResponseHeaders(response, method, opDef); |
| return new Object[]{faultName, response}; |
| } |
| } |
| } |
| |
| } |
| |