Friday, July 15, 2011

Using ws-adressing to callback to another process.

Scenario :
1. Service-A.
2. Service-B.
3. Service-C ("Asynchronous request-response" service )

Service C is a legacy service that takes an input from a client , process it and then response back to the same client (by default ). However your business is in such a way that your response should not go to the initial caller(Service-A) but to another process(Service-B). Depicted below is the same:



For this , you may need to tweak the SOAP Headers / WS-Adressing properties by sending the WSA ReplyTo to the Service-C's URI in the call from Service-A to Service-C.


ie., The Service-C's input looks like this:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<span style="font-weight:bold;"> <soap:Header xmlns:ns1="http://schemas.xmlsoap.org/ws/2003/03/addressing">
<ns1:MessageID>ws:uniqueAddress</ns1:MessageID>
<ns1:ReplyTo>
<ns1:Address>http://localhost:8001/soa-infra/services/default/SOATestProject/ServiceB?WSDL</ns1:Address>
</ns1:ReplyTo>
</soap:Header>
</span> <soap:Body xmlns:ns1="http://xmlns.oracle.com/TestApplication_jws/SOATestProject/MyAsynchronousService">
<ns1:process>
<ns1:input>Order123</ns1:input>
</ns1:process>
</soap:Body>
</soap:Envelope>

As you see we give the Service-B's WSDL(http://localhost:8001/soa-infra/services/default/SOATestProject/ServiceB?WSDL) in the Reply To tag.

However care has to be taken while creating Service B that you use the same port type and message types that Service C uses to callback. Otherwise you may end up with exception : javax.xml.ws.soap.SOAPFaultException: oracle.fabric.common.FabricException: Unable to find operation: null

Conclusion:


Not changing your legacy Asynchronous request/response service,you can route the call back response to another service apart from the caller using WS-Addressing.

Monday, July 11, 2011

FTP Adapter - Reading File Name that changes for each instance

We often end up in some business requirements where we need to read a file or write to a file where file name is decided based on the message of the instance.

Consider an example where we have one order process which calls another application asynchronously to validate the order. If the validation is successful , it places a file in a FTPshare with name _Success.xml else _Failed.xml. Now the Order process instance will need to read the file from that FTP share. So here the requirement is to read a file whose name would change for every Order ( ie., for every instance).
So here we need to dynamically change the "file name" of the FTPAdapter configuration.

Providing FileName Dynamically :

1. Create a OutboundHeaderVar(lets say outHeaderVar) in the scope. This is of messageType {http://xmlns.oracle.com/pcbpel/adapter/ftp/}OutboundHeader_msg. (Or) you can select the message Type -> click on browse -> Type Explorer -> Project WSDL files -> Select ftpAdapterOutboundHeader.wsdl -> MessageTypes->OutboundHeader_msg.

2. Drag an assign activity and select the from-spec query like for eg : concat('bpws:getVariableData("orderNumber")','_Success.xml').In the To-Spec Query select
outHeaderVar/outboundHeader Query = "/ns2:OutboundFTPHeaderType/ns2:fileName"

This will assign the file name as required at the design time dynamically.

Tuesday, July 5, 2011

MQ Adapters with Opaque message type - Different ways to send a message.

MQ Adapters define two kinds of message payloads:

1. Opaque Schema
2. Schema file ( xsd) that contains a specified schema element.

Chosing opaque schema do not need to specify a message schema.By default the file/message is passed through in base-64 encoding.However

If the message is already base-64 encoded, the MQ Adapter will decode the message before placing in the Queue.


This is very useful certain times i) when the destination service who is listening/dequeuing to/from the MQ is expecting the message in the already decoded format.ii) (i) and when the destination is not confirmed to one message type and hence sender forced to use Opaque format.iii)when destination is listening to a message schema which has targetnamespace as null/empty.

For Case iii) situations , sender could have used the message xsd(destination is expecting) while sending instead of using an opaque schema , if the schema has a proper namespace(not null or empty) defined. As an empty target namespace xsd restricts the wizard of MQ adapter to define it , we are forced to use opaque schema.
Although it's not a right design for the destination app to have bad namespaces defined,we often end up in having this kind of services in the real scnarios.

So in these cases sender can encode the message to binary-64 before sending it to MQ so that the destination gets a decoded non binary64 message.

Given below is a sample code that encodes a message using a java embedding activity in BPEL process :

try
{

String input = (String)getVariableData("invServiceRequestStr");
String encoded;
com.collaxa.common.util.Base64Encoder Encoder = new com.collaxa.common.util.Base64Encoder();
encoded = Encoder.encode(input);
setVariableData("encodedMessageVar", encoded);
}
catch(Exception e)
{
e.printStackTrace();
}

Caveats:
1. If you are using 11g then you must use "oracle.soa.common.util.Base64Encoder" instead of com.collaxa.common.util.Base64Encoder.
In your .bpel you may need this :

FYI - This is present at - $JDEVHOME\soa\modules\oracle.soa.fabric_11.1.1\fabric-runtime.jar.

2. If your input to be converted is not plain string but element then use the below
String base64 = ((oracle.xml.parser.v2.XMLElement)getVariableData("MyElement")).getFirstChild().getNodeValue();

Summary :
Sender of the MQ message can encode the message which will be decoded by the MQ Adapater framework internally before placing into the queue ,if the schema used is a Opaque.