Talking to Perl from BPEL
or WSIF HTTP Bindings
Earlier this week I was asked how to call Perl from BPEL. My first inclination was to use a <bpelx:exec> to embed some Java and then in Java use Runtime.getRuntime().exec() to invoke a Perl script. A little more thought came up with a better solution. Perl is usually used as part of a cgi solution so why not us WSIF to create an HTTP binding. Lets look at this solution.The Perl Script
Not being a Perl guru I created a tremendously unsophisticated Perl script#!/usr/bin/perl
use strict;
use CGI;
my $cgi = CGI::new();
print $cgi->header(-type => "text/xml; charset=utf-8");
print <<XML;
<?xml version= '1.0' encoding= 'UTF-8' ?>
<response xmlns="http://perl.antony.blog">
XML
print " <name>Hello " . $cgi->param('name') . "</name>";
print <<XML
</response>
XML
This just takes the parameter "name" passed into the script and creates an XML document response that conforms to the following schemause strict;
use CGI;
my $cgi = CGI::new();
print $cgi->header(-type => "text/xml; charset=utf-8");
print <<XML;
<?xml version= '1.0' encoding= 'UTF-8' ?>
<response xmlns="http://perl.antony.blog">
XML
print " <name>Hello " . $cgi->param('name') . "</name>";
print <<XML
</response>
XML
<?xml version="1.0" encoding="windows-1252" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://perl.antony.blog"
targetNamespace="http://perl.antony.blog"
elementFormDefault="qualified">
<xsd:element name="name" nillable="true" type="xsd:string" />
<xsd:element name="response">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="name"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
To deploy the Perl script I dropped it into the $ORACLE_HOME/Apache/Apache/cgi-bin directory of Oracle App server which by default will run Perl scripts in this directory.<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://perl.antony.blog"
targetNamespace="http://perl.antony.blog"
elementFormDefault="qualified">
<xsd:element name="name" nillable="true" type="xsd:string" />
<xsd:element name="response">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="name"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
A sample query may be "http://localhost/perl/helloParamXML.pl?name=Jackson%20Franklin" This produces the following XML document
<?xml version= '1.0' encoding= 'UTF-8' ?>
<response xmlns="http://perl.antony.blog">
<name>Hello Jackson Franklin</name>
</response>
So now I had a sample Perl script. The next step was to create a WSIF wrapper for the Perl service.<response xmlns="http://perl.antony.blog">
<name>Hello Jackson Franklin</name>
</response>
Wrapping Up an HTTP GET in WSIF
WSIF enables us to create a WSDL description with non-SOAP bindings. In this case I wanted to create a HTTP GET binding. There just happens to be an example of this in the BPEL Process manager samples at $ORACLE_HOME/bpel/samples/tutorials/702.Bindings/HTTPBinding/HTTPBindingSample/bpel/QuoteService.wsdl.I used this a a template to create my own WSIF. Here is the WSIF I created.
<?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://perl.antony.blog"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/"
targetNamespace="http://perl.antony.blog"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:perl="http://perl.antony.blog">
I didn't check all the definitions to see if they were all needed. The changes I made from the sample are highlighted.<definitions
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://perl.antony.blog"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/"
targetNamespace="http://perl.antony.blog"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:perl="http://perl.antony.blog">
<types>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<xsd:import schemaLocation="PerlSchema.xsd"
namespace="http://perl.antony.blog"/>
</schema>
</types>
Message types are imported from a seperate XSD, I find this makes it cleaner and easier to modify the schema because the same schema can be used in multiple places.<schema xmlns="http://www.w3.org/2001/XMLSchema">
<xsd:import schemaLocation="PerlSchema.xsd"
namespace="http://perl.antony.blog"/>
</schema>
</types>
<message name="CallPerlRequest">
<part name="name" type="xsd:string" />
</message>
This is the input message and will later be bound onto a HTTP GET request. It consists of a single string. Multiple parameters would be entered as multiple parts. If a complex XML type or document is the input then it would be better to map onto a POST request and enter the XML document as the value of a an input parameter.<part name="name" type="xsd:string" />
</message>
<message name="CallPerlResponse">
<part name="Body" element="perl:response" />
</message>
This is the response message and is the XML document returned by the Perl service.<part name="Body" element="perl:response" />
</message>
<portType name="PerlHttpGet">
<operation name="CallPerl">
<input message="tns:CallPerlRequest" />
<output message="tns:CallPerlResponse" />
</operation>
</portType>
Having set up the Port Type now need to bind the service onto an HTTP GET Request<operation name="CallPerl">
<input message="tns:CallPerlRequest" />
<output message="tns:CallPerlResponse" />
</operation>
</portType>
<binding name="PerlHttpGet" type="tns:PerlHttpGet">
<http:binding verb="GET" />
<operation name="CallPerl">
<http:operation location="/perl/helloParamXML.pl" />
<input>
<http:urlEncoded />
</input>
<output>
<mime:mimeXml part="Body" />
</output>
</operation>
</binding>
The binding maps the operation onto a URL path and species that the input message is mapped onto HTTP URL encoded parameters and the response is an XML document.<http:binding verb="GET" />
<operation name="CallPerl">
<http:operation location="/perl/helloParamXML.pl" />
<input>
<http:urlEncoded />
</input>
<output>
<mime:mimeXml part="Body" />
</output>
</operation>
</binding>
<service name="HelloPerl">
<port name="PerlHttpGet" binding="tns:PerlHttpGet">
<http:address location="http://localhost" />
</port>
</service>
The service binding identifies the server providing this service.<port name="PerlHttpGet" binding="tns:PerlHttpGet">
<http:address location="http://localhost" />
</port>
</service>
<plnk:partnerLinkType name="HelloPerlService">
<plnk:role name="HelloPerlServiceProvider">
<plnk:portType name="tns:PerlHttpGet"/>
</plnk:role>
</plnk:partnerLinkType>
</definitions>
The WSIF descriptor is finished off by adding a partner link for use by BPEL.<plnk:role name="HelloPerlServiceProvider">
<plnk:portType name="tns:PerlHttpGet"/>
</plnk:role>
</plnk:partnerLinkType>
</definitions>
Comments (3)
Hi Antony, nice post and example of WSIF with HTTP binding. Personally though, my first inclination for invoking perl in a SOA/Web Services context would be to simply implement a SOAP interface usign SOAP::Lite. Great information is available in the book Programming Web Services With Perl
Posted by Paul | June 6, 2007 8:23 PM
Posted on June 6, 2007 20:23
hi Antony, fyi to expand the discussion I got a little derivative ;) and just posted Diving for SOAP Perls which describes the approach of wrapping your perl with a SOAP interface.
Paul Gallagher
Posted by Antony Reynolds | June 14, 2007 5:49 PM
Posted on June 14, 2007 17:49
Hello Antony,
I am writeing my degree with name WSIF. I found your examples very useful.
But i have problem with example JMS (message bean) with WSIF.
In my message bean is method methodName(BigDecimal x, BigDecimal y).
I don't know how to call this method with WSIF.
If you can help me i will be verrrrrry thankful.
Posted by Primoz | June 19, 2007 4:38 AM
Posted on June 19, 2007 04:38