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.

Tuesday, August 5, 2008

Accessing BPEL variables from within XSLT

What's new?
XSLT is a remarkable technology, and I almost always prefer using it over other alternatives, most notably JAVA(of course there are times when nothing else will work and JAVA enticingly fits the bill). However, very often one is tempted to write JAVA snippets, and expose them as webservices to accomplish a task, when the same could be achived using XSLT with minimal or no coding.

Most of us are aware that BPEL PM provides an out of box function ora:processXSLT to execute an XSL template. The signature of the function is pretty well known.

ora:processXSLT('template','input','properties'?)
template : The XSL File Name
input : The variable to be transformed

Very often we use only the first two parameters without giving much of a thought about the third. This post is intended unravel the mystery sorrounding this parameter.

The third parameter
Interestingly, the third parameter comes in handy when you need access to BPEL variables from within an XSL file. Withing the XSLT engine this parameter translates to XSL parameters that can be accessed within the XSL Map using the construct
<xsl:param name="<paramName>"/>

All that remains is retrieving data from this parameter within XSLT. This is relatively straight forward and is done as follows
<Name><xsl:value-of select="$param1"/></Name>

More about "properties"
For the "properties" argument to be available to the XSLT engine, it must be of type "message" that conforms to the following schema.
...
<?xml version="1.0" encoding="windows-1252" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.oracle.com/service/bpel/common" targetNamespace="http://schemas.oracle.com/service/bpel/common" elementFormDefault="qualified">
<xsd:element name="parameters">
<xsd:annotation>
<xsd:documentation> A sample element </xsd:documentation> </xsd:annotation>
<xsd:complexType>
<xsd:sequence>
<xsd:element name="item" maxOccurs="unbounded"> <xsd:complexType>
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="value" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
...

Create and initialize the variable
Your first step is to import this schema into the project wsdl file. Once this is done your wsdl file should look like this
...
<types>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://schemas.oracle.com/service/bpel/common" schemaLocation="Props.xsd" />
</schema>
</types>
...

Add the namespace under the definitions tag of the wsdl file like this

xmlns:ns1="http://schemas.oracle.com/service/bpel/common"

Now, go ahead and create the appropriate message types. For the sake of convinience, I have modelled my request message on top of this schema(since my intention is just to drive home the concept; readers are welcome to improvise) so that I don't need to create another variable and populate it with the contents of input variable again within the BPEL process. Instead, I shall directly use the input variable as the third argument. If you are using JDeveloper, there isn't any need to do all these manually. All you need to do is specify this schema for the input/request message when defining the process at the beginning. The designer automatically generates these fragments for you without you even knowing it. Anyways, here is my message type
...
<message name="ReadConfig1RequestMessage">
<part name="payload" element="ns1:parameters" />
</message>
...

NS:If you are not directly using the input variable, you must first create a variable that conforms to this message and populate the same with the contents of that bpel variable whose values you wish to make avaiable within the XSLT.

Anyways, here is how the third argument will look like after it has been initialized
...
<parameters xmlns:ns2="http://schemas.oracle.com/service/bpel/common" xmlns="http://schemas.oracle.com/service/bpel/common"/>
<ns2:item>
<ns2:name>Name</ns2:name>
<ns2:value>Sankash</ns2:value>
</ns2:item>
<ns2:item>
<ns2:name>Occupation</ns2:name>
<ns2:value>Software Engineer</ns2:value>
</ns2:item>
</parameters>
...

Call ora:processXSLT with the third argument
Unfortunately, the JDeveloper GUI does not support using this parameter. So, you have to do it after clicking the source tab in the designer. Just add this snippet within the tranformation
...
<copy>
<from expression="ora:processXSLT('Transformation_1.xsl',bpws:getVariableData('Invoke_1_SynchRead_OutputVariable','Configurations'),bpws:getVariableData('inputVariable','payload'))"/>
<to variable="outputVariable" part="payload"/>
</copy>
...

The XSLT snippet
Here is the xsl file. It will concat the values of the parameters which are essentially the contents of the BPEL variable (the input variable in this case). ...
<xsl:stylesheet version="1.0" ....>
<xsl:param name="Name"/>

<xsl:param name="Occupation"/>
<xsl:template match="/">
<ns1:ReadConfig1ResponseMessage>
<ns1:result>
<xsl:value-of select="concat('Name : ', $Name, ' Occupation : ',$Occupation)"/> </ns1:result>
</ns1:ReadConfig1ResponseMessage>
</xsl:template>
</xsl:stylesheet>
...

Testing the process
Save the project and deploy. Ideally the response message should containt the concatenated value of the parameters. In this case it shoudl look like this

<ns1:result>Name : Sankash Occupation : Software Engineer</ns1:result>

Monday, August 4, 2008

Creating and using custom xpath functions in BPEL


The motivation..
Recently, while tinkering with the email activity, I stumbled upon a strange but interesting discovery. I had tried to use the ora:fileread xpath function to read a text file (and subsequently send the same as an attachment) and I found that the function returned some strange text. After some more playing around, it dawned on me that the function had actually returned the Base64 encoded equivalent of the original text. So, in order to recover the original text I had to decode the encoded text. Since BPEL does not provide an out of box function to do the same, my first impulse was to write some java snippet and subsequently wrap it as web service. Then I got a better idea - using a custom xpath function that would do the decoding for me. Obviously, this was a better design inasmuch as I would no longer need to use an additional partnerlink in my BPEL process. In fact, this is the subject that I am going to cover in this post.

For ease of writing, I shall use the same code I wrote to decode Base64 encoded text. This is intended to serve as an example. Readers can improvise on it to develop their own functions.

The business logic
The first step is to write the code to decode the message, which is relatively simple.

Here is the snippet
...
private String decodeString(String str)
{
String decoded = null;
try
{
decoded = Base64Decoder.decode(str);
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
return decoded;
}
...

Wrapping the code
In order that this snippet be available as XPath function we need to first implement the IXPathFunction interface. This has a single method that it requires us to implement:

public Object call(IXPathContext iXPathContext, List list) throws XPathFunctionException

Here is the implementation of the above method

private static final int NO_OF_ARGS = 1;
public Object call(IXPathContext context,List args) throws XPathFunctionException {
// test if we have the right argument number

if (args.size() != NO_OF_ARGS)
{
throw new XPathFunctionException("This function requires one argument.");
}
// extract the String of the argument from the BPEL process

Object o = args.get(0);
String str = getValue(o);
// call the business method

return decodeString(str);
}

To get the value of the object, we shall use the getValue method. The implementation of the same is shown below.

private String getValue(Object o) throws XPathFunctionException {
if (o instanceof String)
{
return ((String)o);
}
else if (o instanceof Node)
{
return ((Node)o).getNodeValue();
}
else
{
throw new XPathFunctionException("Unknown argument type.");
}
}

Adding the class to the Server
Now that we are done with the coding, we need to compile it and drop the class into $ORACLE_HOME/bpel/system/classes directory of the BPEL PM server, so that the application server has access to this class.

Registering the xpath funtion with the server
The BPEL PM needs to be aware of this new function so that you can use it. So you need to register it with the server. This is done by adding a new entry in the xpath-functions.xml file located under the $BPEL_HOME/domains/default/config directory.

Namespace prefix for this function<?xml version = '1.0' encoding = 'UTF-8'?>
<bpel-xpath-functions version="2.0.2">
<function id="decode">
<classname>callbpelfromjava.CustomXpathExtension</classname>
<comment><![CDATA[decode the string]]></comment>
<property id="namespace-uri">
<value>http://nebulasky.blogspot.com</value>
<comment>Namespace URI for this function</comment>
</property>
<property id="namespace-prefix"><value>nebulasky</value>
<comment>Namespace prefix for this function</comment>
</property>
</function>
</bpel-xpath-functions>
Setting the BPEL classpath
Edit the shared library of your application server. Navigate to $ORACLE_HOME/j2ee/oc4j_soa/config. Open the server.xml file and locate the shared library called oracle.bpel.common. Add a tag code-source with your classpath as the others code-source tags.
...
<shared-library name="oracle.bpel.common" version="10.1.3">
...
<code-source path="/u01/apps/orasoa/product/10.1.3/bpel/system/classes"/>
...
</shared-library>
...
Now, open the domain.xml and scroll down to property id = "bpelcClasspath". Ensure that the classpath is already present. If not, add it. Once these tasks are done, bounce the server.
...
<property id="bpelcClasspath">
<name>BPEL process compiler classpath</name>
<value>/u01/apps/orasoa/product/10.1.3/bpel/system/classes:/u01/apps/orasoa/product/10.1.3/bpel/lib/j2ee_1.3.01.jar:/u01/apps/orasoa/product/10.1.3/bpel/lib/xmlparserv2.jar
</value>
</property>
...

Testing the Process
Create a BPEL process in JDeveloper. Under the process tag of the bpel file add the namespace that qualifies the function. This is the same as the values of the namespace-uri/namespace-prefix you had specified in the xpath-functions.xml. Here, the namespace is http://nebulasky.blogspot.com and the prefix is nebulasky (see xpath-functions.xml).
Here is how it will look like
xmlns:nebulasky="http://nebulasky.blogspot.com"

Create a copy operation as follows.
...
<copy>
<from expression="nebulasky:decode(bpws:getVariableData('encoded'))"/>
<to variable="outputVariable" part="payload" query="/client:CustomXpathProcessResponse/client:result"/>
</copy>
...
Deploy the project and initiate it. Ideally, your BPEL process should be able to decode the encoded text back to ASCII text.