The Resource-Oriented Architecture defines four concepts:
and four properties:
HTTP binding as defined in WSDL 1.1 is not well suitable to describe services implementing these concepts and properties, mainly because a port type may access 4 different locations/resources but with only one HTTP method.
To better describe RESTful services, and turn a port type into a "resource type", ODE brings a set of 4 extensions:
Further details below.
In this page, we use an imaginary blog service as a use case to illustrate and make things more palpable. We will focus on the resources defined by the following URI template: http://blog.org/post/{id}
Let's assume that such a resource accept four operations:
According to the WSDL 1.1 specification, the verb describing the HTTP method has to be at the binding level. Which implies that the same HTTP method is used by all operations of a given port type. But RESTful web services leverage HTTP methods as a uniform interface to describe operation on resources. So for instance, if you want to use the following HTTP operations -- GET, POST, PUT, DELETE -- for a given resource, four different bindings would be required. And consequently four port types and four ports. Quite verbose and unusable, isn't it?
So, this extension is to push down the HTTP verb at the operation level. And if an operation does not have its own verb, then the verb defined at the binding level will be used.
This extension is declared in the namespace: http://www.apache.org/ode/type/extension/http
Please note that ODE supports GET, POST, PUT, DELETE only.
<definitions ... xmlns:odex="http://www.apache.org/ode/type/extension/http"/> <!-- many wsdl elements are ommited to highlight the interesting part --> <binding name="blogBinding" type="blogPortType"> <operation name="GET"> <odex:binding verb="GET" /> </operation> <operation name="DELETE"> <odex:binding verb="DELETE"/> </operation> </binding> </definitions>
A RESTful service exposed a set of resources, each of them being accessible through a uniform interface: HTTP methods for a web service. So we need a way to define four operations (at most) for a single resource.
Moreover it's very likely that the resource URI actually describes a set of resources. For instance, the set of posts contained in our imaginary blog: http://blog.org/post/{post_id}
.
HTTP binding offers the http:operation element to set the path of an operation. While the service address is set in the http:address of the wsdl:port element. So one could imagine splitting the URL this way:
<definitions ... xmlns:odex="http://www.apache.org/ode/type/extension/http"/> <service name="blogService"> <port name="blogPort" binding="blogPortType"> <http:address location="http://blog.org"/> </port> </service> <binding name="blogBinding" type="blogPortType"> <operation name="PUT"> <odex:binding verb="PUT" /> <http:operation location="post/(post_id)"/> <input> <http:urlReplacement/> </input> <output/> </operation> </binding> </definitions>
However, here 3 issues show up:
To solve this, ODE allows http:operation elements to be omitted or empty, and the full resource URI to be set in a single place, the http:address element.
In addition, the http:urlReplacement is relaxed: all parts are not required in the URI template anymore. One part could go in the URI, another in the request body.
<definitions ... xmlns:odex="http://www.apache.org/ode/type/extension/http"/> <service name="blogService"> <port name="blogPort" binding="blogPortType"> <!-- here is the full URI template, using curly brackets --> <http:address location="http://blog.org/post/{post_id}"/> </port> </service> <binding name="blogBinding" type="blogPortType"> <operation name="PUT"> <odex:binding verb="PUT" /> <!-- location attribute intentionally blank --> <http:operation location=""/> <input> <http:urlReplacement/> <!-- an additional part can be mapped to the request body even if urlReplacement is used--> <mime:content type="text/xml" part="post_content"/> </input> <output/> </operation> </binding> </definitions>
HTTP protocal convey a lot of information in Request/Response Headers. Caching information, Content description for instance. All this data is completely left out by WSDL 1.1 HTTP Binding. To fix this, ODE provides a header element. This element can be used to insert a part or a static value into a given HTTP request header (standard or custom). And the way around, a HTTP request header into a part. Also note that all HTTP response headers are inserted into the message headers, and thus are available from the BPEL process.
<definitions ... xmlns:odex="http://www.apache.org/ode/type/extension/http"/> <binding name="blogBinding" type="blogPortType"> <operation name="PUT"> <odex:binding verb="PUT" /> <http:operation location=""/> <input> <http:urlReplacement/> <mime:content type="text/xml" part="post_content"/> <!-- set a standard request header from a part --> <odex:header name="Authorization" part="credentials_part"/> <!-- set a custom request header with a static value --> <odex:header name="MyCustomHeader" value="ode@apache.org" /> </input> <output> <mime:content type="text/xml" part="post_content"/> <!-- set 1 response header to a part --> <odex:header name="Age" part="age_part"/> </output> </operation> </binding> </definitions>
For every HTTP response, in addition to HTTP response headers, the Status-Line is passed as a message header. To save some painful XPath string manipulations, the Status-line is already parsed into the following structure:
<Status-Line> <HTTP-Version>HTTP/1.1</HTTP-Version> <Status-Code>200</Status-Code> <Reason-Phrase>OK</Reason-Phrase> <!-- the original unaltered Status-Line --> <original>HTTP/1.1 200 OK</original> </Status-Line>
So that you can write the following BPEL lines:
<assign> <copy> <from variable="postMsg" header="Status-Line"/> <to>$statusLine</to> </copy> </assign> <if> <condition>number($statusLine/Status-Code) > 200 and number($statusLine/Status-Code) < 300</condition> <assign> <copy> <from>'Request successful!!!'</from> <to>$outputVar.TestPart</to> </copy> </assign> </if>
Another domain completely neglected by WSDL 1.1 HTTP Binding is Fault management. The word is not even mentioned in the HTTP Binding section. ODE allows you to bind a fault with HTTP Binding. If a 4xx or a 5xx is returned, the following logic is applied:
A failure is thrown if the code is one of these:
Status-Codes triggering a Failure |
---|
3xx Redirections |
401_UNAUTHORIZED |
408_REQUEST_TIMEOUT |
503_SERVICE_UNAVAILABLE |
504_GATEWAY_TIMEOUT |
http.protocol.max-redirects
in the enpoint-configuration.properties of your process.Here what ODE does, if the status code is one of those listed in the next table (500, 501, etc):
If so far everything is fine, the HTTP response body is parsed into an xml document. Then the fault to be thrown is inferred from the qname of the response root element, i.e the fault having a message part matching the root element. This matching process is exactly the same as for a SOAP service. If one of these steps fails, a failure is thrown.
Status-Codes that may trigger a Fault | if the body element matches a fault declaration |
---|---|
500_INTERNAL_SERVER_ERROR | 407_PROXY_AUTHENTICATION_REQUIRED |
501_NOT_IMPLEMENTED | 409_CONFLICT |
502_BAD_GATEWAY | 410_GONE |
505_HTTP_VERSION_NOT_SUPPORTED | 412_PRECONDITION_FAILED |
400_BAD_REQUEST | 413_REQUEST_TOO_LONG |
402_PAYMENT_REQUIRED | 414_REQUEST_URI_TOO_LONG |
403_FORBIDDEN | 415_UNSUPPORTED_MEDIA_TYPE |
404_NOT_FOUND | 411_LENGTH_REQUIRED |
405_METHOD_NOT_ALLOWED | 416_REQUESTED_RANGE_NOT_SATISFIABLE |
406_NOT_ACCEPTABLE | 417_EXPECTATION_FAILED |
Note that you can't bind a given fault to a specific status code.
<definitions ... xmlns:odex="http://www.apache.org/ode/type/extension/http"/> <portType name="BlogPortType"> <operation name="PUT"> <input message="..."/> <output message="..."/> <fault name="UpdateFault" message="tns:UpdateFault"/> </operation> </portType> <binding name="blogBinding" type="blogPortType"> <operation name="PUT"> <odex:binding verb="PUT" /> <http:operation location=""/> <input> ... </input> <output> ... </output> <!-- fault binding --> <fault name="UpdateException"> <!-- name attribute is optional if there is only one fault for this operation --> <!-- <odex:fault name="UpdateFault"/> --> <odex:fault/> </fault> </operation> </binding> </definitions>