Invoking HTTP Services from Oracle BPEL PM – SOA enabling PHP, Servlets, RSS,…

We live in a service oriented world. Or we will do so in the very near future. Services are all around us. And mind you: services not just as in WebServices or BPEL Processes. Services as in well defined interfaces that provide reusable functionality with clearly specified input and output parameters. And that definition covers a lot of existing ‘legacy’ functionality, implemented in a plethora of technologies. Much of which, grantedly, is not already exposed with a well-defined interface. But than again: according to that definition, many services do exist.

A vast domain of services lurks behind the HTTP protocol. With straightforward GET and POST request messages can we invoke services within our intranets and across the internet. The response message can be generated by Java based applications – such as Servlets or JSPs – and just as easy by VB.NET, C#, Perl, (MOD_)PLSQL, PHP -powered applications. Publishing a service as a simple HTTP ‘end-point’ is easy. We have been doing that for a long time. Frequently these services return markup language – HTML – but some already reply in XML messages. Let’s agree that implementing services and exposing existing functionality as services is within our grasp if it can be as simple as deploying a PHP page or a Servlet.

One of the major service consuming forces to be reckoned with is of course the body of of BPEL processes that we have implemented or will develop in the near future. So it is important to know how HTTP services can be invoked from within BPEL processes. That is what this article is about. And fortunately, it turns out to be quite simple. The essence really is not BPEL specific – it is creating a WSDL (Web Service Definition Language) document, based on the WSIF extensions to WSDL (specifically the HTTPBinding), for our HTTP service.

The steps for BPEL-enabling HTTP services are

  • create or locate the HTTP service to leverage from the BPEL Process – that is: the Servlet, JSP, PHP or Perl program we deploy on our web server
  • specify the input and the output parameters for the service – for a GET service that is for example the list of parameters appended to the URL with ?param=value&param2=value
  • create the WSDL document with the HTTP binding and possibly the XSD for the type definitions for complex types in request or – more likely – response

Using this WSDL document, we can create a PartnerLink in our BPEL process and Invoke this PartnerLink.

The HTTP Service –  The Hotel Booking Method Service

Let’s look at an example service. Our Service is a very simple one: it takes the name of an hotel as input parameter and returns the method to contact this hotel as well as the contact-details like email-address. The Service is published through a Servlet:

package nl.amis.coa.rules;

import java.io.IOException;
import java.io.PrintWriter;

import java.util.Enumeration;

import javax.servlet.*;
import javax.servlet.http.*;

public class HotelBookingMethodServiceServlet extends HttpServlet {
    private static final String CONTENT_TYPE = "text/xml; charset=windows-1252";

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }

    public void doGet(HttpServletRequest request, 
                      HttpServletResponse response) throws ServletException, IOException {response.setContentType(CONTENT_TYPE);
        PrintWriter out = response.getWriter();
        String hotelName = (String)request.getParameter("hotelName");
        
        HotelBookingMethod bookingMethod = HotelRulesService.getHotelBookingMethod(hotelName);
        out.println("<coa:hotelBooking xmlns:coa=\"http://coa.amis.nl/HotelRules\"><coa:bookingMethod>"+bookingMethod.bookingMethod+"</coa:bookingMethod><coa:hotelContact>"+bookingMethod.getContactDetails()+"</coa:hotelContact></coa:hotelBooking>");
        out.close();
    }

    public void doPost(HttpServletRequest request, 
                       HttpServletResponse response) throws ServletException, IOException {response.setContentType(CONTENT_TYPE);
      doGet(request, response);
    }
}

The Servlet expects a parameter called hotelName. It writes its response as a piece of XML that looks like this:

<coa:hotelBooking xmlns:coa="http://coa.amis.nl/HotelRules">
  <coa:bookingMethod>manual</coa:bookingMethod>
  <coa:hotelContact>053-302150</coa:hotelContact>
</coa:hotelBooking>

 

The servlet uses additional backoffice services – the HotelRulesService class – that presumably access enterprise resources for retrieving the required information. Of course we as Service Consumers do not need to know about such internals.

Creating the WSDL document

Now we need to express our HTTP Service through a WSDL document. This document has a standard structure that specifies the service, the operations, the input and output parameters, the fauls or exceptions that can be raised and the physical protocol and end point location required for the actual access of the service. The  WSDL for our Servlet based service looks as follows:

<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:tns="http://coa.amis.nl/HotelRules"
             xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
             xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/"
             targetNamespace="http://coa.amis.nl/HotelRules"
             xmlns="http://schemas.xmlsoap.org/wsdl/">
  <types>
    <xsd:schema elementFormDefault="qualified"
                targetNamespace="http://coa.amis.nl/HotelRules">
      <xsd:element name="hotelBooking">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="bookingMethod" type="xsd:string"/>
            <xsd:element name="hotelContact" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="hotel" type="xsd:string"/>
    </xsd:schema>
  </types>
  <message name="GetStockQuoteHttpGetIn">
  <part name="hotelName" element="tns:hotel" />
  </message>
  <message name="GetStockQuoteHttpGetOut">
    <part name="Body" element="tns:hotelBooking"/>
  </message>
  <portType name="HotelRulesHttpGet">
    <operation name="GetHotelBookingMethod">
      <input message="tns:GetStockQuoteHttpGetIn"/>
      <output message="tns:GetStockQuoteHttpGetOut"/>
    </operation>
  </portType>
  <binding name="HotelRulesHttpGet" type="tns:HotelRulesHttpGet">
    <http:binding verb="GET"/>
    <operation name="GetHotelBookingMethod">
      <http:operation location="/COA_WEB-COARules-context-root/hotelbookingmethodserviceservlet"/>
      <input>
        <http:urlEncoded/>
      </input>
      <outp
ut>
        <mime
:mimeXml part="Body"/>
      </output>
    </operation>
  </binding>
  <service name="HotelRules">
    <port name="HotelRulesHttpGet" binding="tns:HotelRulesHttpGet">
      <http:address location="http://localhost:8988/"/>
    </port>
  </service>
  <plnk:partnerLinkType name="HotelRulesService">
    <plnk:role name="HotelRulesServiceProvider">
      <plnk:portType name="tns:HotelRulesHttpGet"/>
    </plnk:role>
  </plnk:partnerLinkType>
</definitions>

Here we see a number of sections. We can start with service element. This specifies the overall service (HotelRules) and ties it to a specific port. The port is a combination of a physical location (the base url where the service is deployed) and a binding. The binding joins a functional service description (the portType) with a protocol specific binding (http:binding). The binding defines operations and for each contains the second part of the url that directs us to the specific servlet/page/module that implements the specific operation. It also specifies the ‘physical’ characteristics of the input and output of the service. The logical definition of the input and the output is defined withing the portType element.

It is important to realize that the Oracle BPEL PM knows how to invoke services that have an http:binding, instead of the more common soap:binding. Interpreting the logical part of the WSDL is the same for SOAP WebServices and HTTP based services but actually making the service request requires the BPEL engine to interact over the HTTP protocol. The details of the actual service call are completely shielded from us: based on the WSDL, the BPEL engine makes the call.

  <portType name="HotelRulesHttpGet">
    <operation name="GetHotelBookingMethod">
      <input message="tns:GetStockQuoteHttpGetIn"/>
      <output message="tns:GetStockQuoteHttpGetOut"/>
    </operation>
  </portType>

The portType element specifies the logical operation GetHotelBookingMethod. It indicates that the input should be of message type tns:GetStockQuoteHttpGetIn and the output is of type tns:GetStockQuoteHttpGetOut. The definitions of these messages is also in the WSDL:

  <message name="GetStockQuoteHttpGetIn">
  <part name="hotelName" element="tns:hotel" />
  </message>
  <message name="GetStockQuoteHttpGetOut">
    <part name="Body" element="tns:hotelBooking"/>
  </message>

and the messages are built on types that too are in the WSDL (could also have been in an external XSD):

  <types>
    <xsd:schema elementFormDefault="qualified"
                targetNamespace="http://coa.amis.nl/HotelRules">
      <xsd:element name="hotelBooking">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="bookingMethod" type="xsd:string"/>
            <xsd:element name="hotelContact" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="hotel" type="xsd:string"/>
    </xsd:schema>
  </types>

 

The hotel type is a very simple one, just a single string that is called hotel. The hotelBooking is a slightly more complex one, that contains child elements bookingMethod and hotelContact. Note that this definition is the
one that describes the servlet response that we saw earlier:

<coa:hotelBooking xmlns:coa="http://coa.amis.nl/HotelRules">
  <coa:bookingMethod>manual</coa:bookingMethod>
  <coa:hotelContact>053-302150</coa:hotelContact>
</coa:hotelBooking>

With the WSDL in hand, we can include this Service in our BPEL Process.

Invoking the HTTP Service from the BPEL Process

Our BPEL Process deals with the Registration of an Attendee for a Conference. As part of the registration, our BPEL process can make a Hotel reservation if so requested by the registree. The Conference visitor can select a hotel from a list of several options. Our BPEL process then will either automatically invoke the hotel’s reservation service – if there is one – or start a Humanual Task (a bit of workflow) that requires one of the back office staff to call or fax the hotel to make the reservation. We need the HotelRulesService to tell us what the method is to contact a specific hotel and also to learn the contact details for that particular hotel.

Invoking HTTP Services from Oracle BPEL PM - SOA enabling PHP, Servlets, RSS,... bpelHTTP

 Creating the PartnerLink for the HotelBookingRulesService is no different from creating any PartnerLink based on a WSDL document. We simply select the WSDL and the BPEL Designer interprets the WSDL and helps us to set up the invoke and the input and output variables.

Invoking HTTP Services from Oracle BPEL PM - SOA enabling PHP, Servlets, RSS,... bpelHTTP2

The definition of the (structure of the) Input and Output variable are taken from the Messages and Types defined in the WSDL. We can now easily set up the input variable and retrieve the values we need from the output:

<sequence name="Sequence_8">
  <scope name="ZoekHotelReserveringsRegels">
  <variables>
    <variable name="Invoke_HotelBoekingsRulesService_GetHotelBookingMethod_InputVariable" messageType="ns15:GetStockQuoteHttpGetIn"/>
    <variable name="Invoke_HotelBoekingsRulesService_GetHotelBookingMethod_OutputVariable" messageType="ns15:GetStockQuoteHttpGetOut"/>
  </variables>
  <sequence name="Sequence_10">
    <assign name="PrepareHotelBookingsRulesService_Input">
      <copy>
        <from expression="'manual'"/>
        <to variable="TypeReservering"/>
      </copy>
      <copy>
        <from variable="COARegistration" part="COAWebConferenceRegistration" query="/COAWebConferenceRegistration/hotelName"/>
        <to variable="Invoke_HotelBoekingsRulesService_GetHotelBookingMethod_InputVariable" part="hotelName" query="/ns15:hotel"/>
      </copy>
    </assign>
    <invoke name="Invoke_HotelBookingsRulesService" partnerLink="HotelBoekingRulesService" portType="ns15:HotelRulesHttpGet" operation="GetHotelBookingMethod" inputVariable="Invoke_HotelBoekingsRulesService_GetHotelBookingMethod_InputVariable" outputVariable="Invoke_HotelBoekingsRulesService_GetHotelBookingMethod_OutputVariable"/>
    <assign name="ProcessBookingMethod">
      <copy>
        <from variable="Invoke_HotelBoekingsRulesService_GetHotelBookingMethod_OutputVariable" part="Body" query="/ns15:hotelBooking/ns15:bookingMethod"/>
        <to variable="TypeReservering"/>
      </copy>
      <copy>
        <from variable="Invoke_HotelBoekingsRulesService_GetHotelBookingMethod_OutputVariable" part="Body" query="/ns15:hotelBooking/ns15:hotelContact"/>
        <to variable="HotelContact"/>
      </copy>
    </assign>
  </sequence>
  ..

More visually pleasing:

Invoking HTTP Services from Oracle BPEL PM - SOA enabling PHP, Servlets, RSS,... bpelHTTP3

 

Note that nothing in the BPEL process is special because we invoke an HTTP Binding, a Service that is implemented and deployed as a Servlet. The WSDL provides the logical definition and that is all that matters – because the BPEL Engine knows how to deal with the http:binding and the URL that are specified in the WSDL. If we were to change the deployment method for this service, into for example a SOAP WebService, all we need to do is change the PORT and BINDING sections of the WSDL, redeploy the changed WSDL (and nothing else) and we are in business with the newly deployed – yet logically identical – service.

Running the BPEL Process and Accessing the HTTP Service

Running this BPEL Process is obviously not different from running any BPEL Process. The fact that now a HTTP Servlet is invoked matters not in the least.
So we normally deploy the BPEL Process to the Oracle BPEL PM, have it invoked in whatever way it is invoked – in this case by the reception of an email – and see if it calls the servlet. Of course for this to happen, we must make sure that the Servlet is available at the url where we specified in the WSDL it would be available. So the WebServer at http://localhost:8988/ must be up and running.

If we look in the BPEL PM Console, it is easy to find out what happened when the process had to call out to our HotelBookingMethodRulesService:

Invoking HTTP Services from Oracle BPEL PM - SOA enabling PHP, Servlets, RSS,... bpelHTTP4

Conclusion

Many service are already available for simple HTTP invocation. They can be implemented in Java, PHP, Perl, C# or any technology that can be exposed by a webserver and can handle HTTP Requests. Publishing these services in a way that makes them accessible to the Oracle BPEL PM is straightforward: we need to create a WSDL document for the service, specify the http:binding, provide the physical location of the service and from there on we can treat this service like any other service we create partnerlinks for.

16 Comments

  1. Jarek December 15, 2009
  2. Anil October 14, 2009
  3. Mishit August 4, 2009
  4. Jar March 18, 2009
  5. Sunil March 18, 2009
  6. Purushottaman February 24, 2009
  7. Jack February 20, 2009
  8. Francis July 18, 2008
  9. Aiman May 15, 2008
  10. Lucas Jellema October 12, 2007
  11. Jean Lim October 12, 2007
  12. Yael February 21, 2007
  13. Nathan Tsang November 17, 2006
  14. Lucas Jellema July 7, 2006
  15. Lucas Jellema July 6, 2006
  16. Dimitri Gielis July 6, 2006