Thursday, August 21, 2008

WSIF Revisited: Bottom up development using JDeveloper

Oversight....
In the previous post, I had elucidated how to call Java using WSIF. But, an oversight on my part caused the things to look a trifle more complicated than they actually were. Here is an attempt restore some sanity.


Create the Class
First, you need to create the Java class. Launch JDeveloper and create a new Java class. For ease of writing, I shall use the same class as that in the previous post. The class's task is to concatenate one string with another.



Here is the Java snippet:

package com.nebulasky.blogspot;
public class ConcatString {
public ConcatString() { }
public String getConcatenation(String input){
return "Hello " + input;
}
}

Generate the contract
A peek into the earlier post and you will realize that we had then created the contract manually, when in fact we could have done it with a JDeveloper wizard (the oversight I was blabbering about). Right click the Java source from the context menu of the project and click 'Create J2EE Web Service'



In the first step of the wizard check the WSIF Binding uncheck the SOAP 1.1 Binding (this is checked by default).



Once these are done, finish the wizard. Open the WSDL thus generated and have a look. Scroll down to the service tag and you should see something like this:

<service name="WSIFConcatService">
<port name="WSIFConcatServiceWSIFPort" binding="tns:WSIFConcatServiceWSIF">
<java:address className="com.nebulasky.blogspot.ConcatString"/>
</port>
</service>

Note java:address tag. It specifies a class name instead of a SOAP address which is how it would have been if you had chosen SOAP binding. Additionally, will also find some new tags like format:typeMap, format:typeMapping, java:binding and java:operation. For more information on these, visit my previous post.

Deploying the classes
Next, you load the com.nebulasky.blogspot.ConcatString class onto the application server. For this simply copy the class and put it &lt;BPEL_HOME>/system/classes directory. Restart the server.

Create the BPEL process
Create a synchronous BPEL process and create a new partnerlink. Import the WSDL from the local file system into the BPEL project directory. Assign the appropriate fields like operation, partnerR, myRole etc. Now drag an invoke activity and bind it with the service. Here is how the BPEL process will look like:




Deploy and test
Deploy the process and test it. Provided everything is in place, it should return the expected response.


Wednesday, August 20, 2008

WSIF: Calling native Java code from BPEL

What's the deal?
The alternatives to calling Java from BPEL aren't legion. Infact, there are only three.
  1. Using Java embedding to call the code directly
  2. Wrapping the code in a SOAP service
  3. Calling the code using Web Service Invocation Framework
Thus, it becomes all the more important to make a judicious choice amogst the three. The subject matter of this post is to acquaint the reader with calling native Java code using WSIF. But before moving on, it makes perfect sense to look at the pros and cons of all the three.

The pros and cons
Java Embedding: This is an out of box feature provided by BPEL to aid the developer to write and use Java snippets inside the BPEL environment. The greatest advantage is it lets the developer have full access to the entire BPEL environment. Consequently, direct manipulation of data inside the process is fairly easy. Add to it the ease of use, since you don't need to create auxiliary artifacts that you need with the other approaches, you are spared a lot of effort. On the flip side, it makes the BPEL code a little unreadable as you have mixed up Java with the BPEL tags. Use this to do seemingly simple tasks with short Java snippets.

SOAP Service: If you already have the Java code, wrapping it up as a SOAP service with JDeveloper is a peice of cake. Advantage is it lends a lot of reusability to the code and keeps your BPEL process clean. The disadvantage is that the reusability comes at a cost. You have to bear with the SOAP overhead, which can be tremendous especially if you are running a BPEL process that is swamped with SOAP calls. Use this when the service needs to be accessible from multiple machines and different BPEL processes or other applications.

WSIF: Without compromising much on readabiliy and resusability, WSIF provides a cleaner approach to use native code. By keeping the code separate from BPEL it makes sure that readability isn't sacrificed. Also, it calls the code directly, thereby eliminating the SOAP overhead altogether. Finally the code can easily be re-used by other BPEL processes by deploying the jar file containing the java classes and the WSDL document as part of a BPEL suitcase.

Prerequisites
Now that we have looked into the inherent positives and negatives of the three approaches, lets get ahead with the subject matter of this post - using WSIF to call java code. For the sake of demonstration, I shall use a Java class that takes a string as a parameter and concatenates it with another string. Here is code:

package com.nebulasky.blogspot;
public class ConcatString {

public ConcatString() { }
public String getConcatenation(String input)
{
return "Hello " + input;
}
}

Now, use JDeveloper and publish this as a J2EE service. Deploy and test it. Create a BPEL process and invoke this service. Fairly simple. Once these are done, we are all set to bring WSIF into the picture.

The WSIF file
To reiterate our purpose, we are going to tweak the BPEL to use WSIF to call the Java class instead of using SOAP. It makes sense to look at the WSDL file for the J2EE service. Of special interest is the binding section of the WSDL, since this tells our BPEL process how to call the service. I have omitted the rest of the file for simplicity, as they simply specify the data format for the messages. Here is an excerpt from the WSDL:

<binding name="ConcatenationSoapHttp" type="tns:Concatenation">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="getConcatenation">
<soap:operation soapAction="http://blogspot.nebulasky.com/getConcatenation"/>
<input> <soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/> </output> </operation>
</binding>

Notice that SOAP binding is being used (soap:binding, soap:operation , soap:body tags). All we need to do to persuade the BPEL to use WSIF is to change the binding information without changing the structure of the contract. As long as the contract is the same and references the same operations that the Java class exposes, and the messages in the contract are in conformance with the data types and parameters of the class, changing the binding will only change the way the BPEL communicates with the class, nothing else.

Definitions tag
Open the WSDL for the J2EE service in JDeveloper. Click on the source tab because you are going to edit it. Start by defining the two namespaces used by WSIF providers in the root element of the WSDL document, the tag. The format namespace is used to define the type mappings and the java namespace to define the operation mappings and the full name of the Java class:

<definitions name="Concatenation" targetNamespace="http://blogspot.nebulasky.com/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://blogspot.nebulasky.com/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns0="http://blogspot.nebulasky.com/types/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:format="http://schemas.xmlsoap.org/wsdl/formatbinding/" xmlns:java="http://schemas.xmlsoap.org/wsdl/java/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">

Map Java types to XML Schema definitions
To generate Java to XML mapping we need to create XML facades. These façades are Oracle BPEL Process Manager's original Java-to-XML binding for WSIF. XML façades are a set of Java interfaces and classes through which you can access and modify XML data stored in BPEL variables in a relatively easy way using get/set methods. Here we need the mappings for the WSDL file for the J2EE service. For this, you need to use the schema compiler utility called schemac as:

C:\com\nebulasky\blogspot\>schemac Concatenation.wsdl

First, you indicate to the BPEL process that it is bound to native Java code and not to a SOAP service. This is how you do it:

<binding name="JavaBinding" type="tns:WSIFTestProcess">
<java:binding/>

Next, you specify that XSD types will be mapped onto Java types. For this use the format:typeMapping tag. Then you define what Java types shall be used for what xsd types by using the format:typeMap tag. Here is the snippet that does these two tasks for you:

<format:typeMapping encoding="Java" style="Java">
<format:typeMap typeName="xsd:string" formatType="String"/> </format:typeMapping>

Mapping Java methods to WSDL operations
The final step is now to map the Java method calls onto the WSDL operations. This is done using the java:operation tag to identify which Java method should be used to support a given operation:

<operation name="getConcatenation">
<java:operation methodName="getConcatenation"/>
<input/>
<output/>
</operation>
</binding>

Define the Service
We are through with most of it now. All that remains is defining the service. Here you will provide a Java address unlike a SOAP address which is how it was untill recently:

<service name="Concatenation"> <port name="JavaPort" binding="tns:JavaBinding">
<java:address className="com.nebulasky.blogspot.ConcatString"/>
</port>

Deploy the class
For it to work at runtime then the Process Manager must be able to find the Java classes referenced in the WSIF. When using WSIF the classpath for the invoked WSIF service is different to the classpath for the BPEL process. It is necessary to move the classes to the <BPEL_HOME>/system/classes directory. Here the classes are the the ones generated by the schemac command and the class responsible for the concatenation.

Test the process
If it is not already thus, ensure that the BPEL process takes the Concatenation.wsdl file from the current directory and not from the Web service itself. The bpel.xml file should look like this:

<partnerLinkBinding name="Concatenation"> <property name="wsdlLocation">Concatenation.wsdl</property>
</partnerLinkBinding>

Test the process. It should give you the expected result.

Tuesday, August 19, 2008

Security: Enabling SSL in OC4J

Securing the channel
Of the myriad ways to lend security to your enterprise, one that you certainly cannot afford to miss is securing the channel over which your partners communicate with you. An important step in this direction is configuring HTTPS on the OC4J. This is the subject matter of this post. Readers would do well to realize that this is not the final security enforcement point, but merely one among the plethora of policies that one must have in place.

Create a keystore
Your first step is to create a keystore (nothing but a repository of security certificates). Open command prompt and navigate to <JDEV_HOME>\jdk\bin directory. Now, use SUN's keytool to generate the keystore:

keytool -genkey -dname "CN=Sankash Thakuria, OU=Oracle, O=Fujitsu Consulting, L=Bangalore, S=Karnataka, C=IN" -keyalg RSA -sigalg Sha1WithRSA -keypass sankash -storepass sankash -keystore sankashkeystore.jks -alias nebulasky

Copy sankashkeystore.jks to <ORACLE_HOME>/j2ee/home/config.

Configure SSL in OC4J
The default behavior of the OC4J is to expose all resources (services) over HTTP, which is in turn is because of certain settings that are already in place in <ORACLE_HOME>/j2ee/home/config/default-web-site.xml file. We shall override this file to achieve SSL over HTTP. Create a copy of this file under the config directory and rename it as secure-web-site.xml. Open secure-web-site.xml in your favourite text editor and do the following:


  • Inside the <web-site> tag, change the port to 4443 and add the element secure="true".
  • Add <ssl-config> element and and point this to the newly created keystore.

Here is how the file will look like once these are done:

<web-site xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://xmlns.oracle.com/oracleas/schema/web-site-10_0.xsd" port="4443" secure="true" protocol="ajp13" display-name="OC4J 10g (10.1.3) Default Web Site" schema-major-version="10" schema-minor-version="0" >

...

<ssl-config keystore="sankashkeystore.jks" keystore-password="sankash" />

...

<web-site>

Now, you need to make the OC4J aware of these changes. To do that, go ahead and open the server.xml file. Add the following to the file:

<web-site default="true" path="./default-web-site.xml" />

<web-site path="./secure-web-site.xml" />

In essence the secure-website.xml is the same as default-web-site.xml. Thus, all resources that were avaialble over HTTP will now become available over HTTPS. If you want some applications to be available only over HTTPS, you need to remove those applications from the default-web-site.xml. All applications are wrapped under the <web-app> tag.

Bounce OC4J and test

Restart the container and test. For instance, if the BPEL console was available over http://172.28.10.60:7777/BPELConsole it should now be also available over https://172.28.10.60:4443/BPELConsole provided the entry for the same exists in the secure-website.xml file.



Wednesday, August 6, 2008

Calling BPEL from PL/SQL

The UTL_HTTP package
Calling PL/SQL code from BPEL is a walk in the park. But what if you want to do the opposite? Is there a way? Well, fortunately, there is. Oracle 9i/10g comes intact with the UTL_HTTP package that can be used to access data on the Internet over the HTTP protocol. With a little tweaking you can leverage its functionality to call BPEL processes. And I shall show you exactly how to do it.

Declare variables
We declare the following PL/SQL variables

  • request_envelope VARCHAR2(30000): This is the SOAP request that will be sent to the BPEL process
  • response_envelope VARCHAR2(30000): The response message relayed back by the BPEL process after the request has been successfully served
  • http_request utl_http.req: The PL/SQL abstraction of the HTTP request sent to the web server
  • http_response utl_http.resp: The PL/SQL abstraction of the HTTP respone delegated to the caller
The SOAP message
Since SOAP is widely recognised as an industry standard to communicate with services avaialble over the internet, your first task is to create the message. For the sake of convinience, I shall assume that there is a synchronous BPEL process in place that accepts a string as input and concatenates the string with 'Hello' - a typical HelloWorld BPEL process. Initialize the request_envelope variable with the SOAP message as shown below

request_envelope :=
'<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header/>
<soap:Body xmlns:ns1="http://xmlns.oracle.com/HelloWorld">
<ns1:HelloWorldProcessRequest>
<ns1:input>Sankash</ns1:input>
</ns1:HelloWorldProcessRequest>
</soap:Body>
</soap:Envelope>';

BEGIN_REQUEST
BEGIN_REQUEST begins a new HTTP request. When the function returns, the UTL_HTTP package has established the network connection to the target Web server, and has sent the HTTP request line. This function takes three parameters which are

  • url : The end-point for the service you wish to invoke. This will typically be the URL of the BPEL process
  • method : POST or GET.

The "GET" method is suitable for non-parameterized URLs or for URLs with a manageable volume of parameter name-value pairs. The maximum length of the URL string is limited by the capacity of the PL/SQL VARCHAR2 variable used to pass it.

The "POST" method is suitable for parameterizing the request with an arbitrarily large volume of data, especially for example as might be the case when the request is expressed as an XML document.

  • http_version : the version of HTTP like 1.0 or 1.1 etc
In our case this function will look like

http_request :=
utl_http.begin_request(
url => 'http://172.28.0.54:7777/orabpel/default/HelloWorld/1.0', method => 'POST', http_version => 'HTTP/1.1');

Header information
The next step is to set the header information. For this we shall use SET_HEADER function. This function sets a HTTP request header. The request header is sent to the Web server as soon as it is set. The function takes three parameters which are

  • r : The http request object
  • name : The header name
  • value : The header value
In our case we need to set the values for Content-Type, Content-Length and SOAPAction to complete the header. This is done as follows

utl_http.set_header(
r => http_request,
name => 'Content-Type',
VALUE => 'text/xml');

utl_http.set_header(
r => http_request,
name => 'Content-Length',
VALUE => LENGTH(request_envelope));

utl_http.set_header(
r => http_request,
name => 'SOAPAction',
VALUE => 'process');

WRITE_TEXT
This function writes text data in the HTTP request body. As soon as some data is sent as the HTTP request body, the HTTP request headers section is completed. Text data is automatically converted from the database character set to the request body character set. This function takes two parameters

  • r : The http request object
  • data : The text data that forms the request. In our case this is the SOAP message

This is how we will call this function

utl_http.write_text(r => http_request, data => request_envelope);

GET_RESPONSE
This procedure reads the HTTP response. When this procedure returns, the status line and the HTTP response headers have been read and processed. The status code, reason phrase and the HTTP protocol version are stored in the response record. We shall call this in the following way

http_response := utl_http.get_response(r => http_request);

READ_TEXT
This reads the HTTP response body in text form and returns the output in the caller-supplied buffer. The end_of_body exception will be raised if the end of the HTTP response body is reached. Text data is automatically converted from the response body character set to the database character set. It takes two parameters

  • r : the HTTP response object
  • data : the text data of the response. In our case this is the SOAP response
We shall use this function in the following way

utl_http.read_line(r => http_response, data => response_envelope);

END_RESPONSE
This ends the HTTP response. This completes the HTTP request and response cycle. The function takes only one parameter which is the HTTP response object.
Use it like this

utl_http.end_response(http_response);

Handle Exceptions
To take care of any inadvertent exceptions that may arise we embed the following exception handling block in our code

EXCEPTION
WHEN utl_http.end_of_body
THEN utl_http.end_response(http_response);
WHEN utl_http.request_failed THEN
DBMS_OUTPUT.PUT_LINE('Request Failed: ' utl_http.get_detailed_sqlerrm);
WHEN utl_http.http_server_error THEN
DBMS_OUTPUT.PUT_LINE('Server Error: ' utl_http.get_detailed_sqlerrm);
WHEN utl_http.http_client_error THEN
DBMS_OUTPUT.PUT_LINE('Client Error: ' utl_http.get_detailed_sqlerrm);
WHEN others THEN
DBMS_OUTPUT.PUT_LINE(sqlerrm);

Gotchas
So far so good. But when you try to read the output you in your PL/SQL code you are almost certain to get this error, because in most of the cases the output from the BPEL process will be verbose.

ORA-20000: ORU-10028: line length overflow, limit of 255 chars per line

This is because DBMS_OUTPUT.PUT_LINE can write a maximum of 255 characters in one line as the error says. Thus you will have to break the output into multiple lines. To get round this problem I shall use the following piece of code. This restricts the number of characters per line to 255. Extra characters are passed onto the next line.

FOR i IN 1 .. MOD(LENGTH(response_envelope), 255)
LOOP
DBMS_OUTPUT.PUT_LINE(SUBSTR(response_envelope, j, 255));
j := j + 255;
END LOOP;

That is all. Check the BPEL console to ensure that the process was successfully initiated.