Tuesday, May 1, 2012

Changing the Archive File Name in Inbound File/FTP Adapter


Requirement:

To customize the archive file name, remove the junk characters from the archived file name. The default format is as under.

SOA 10g - filename_yyyymmdd_hh24mmss
SOA 11g - filename_encryptedToken_yyyymmdd_hh24mmss (new format introduced with 11g)

The possible customization is given in the solution below.

Solution:

SOA 10g

You have a JCA property for FIle/FTP adapter, UseLongArchiveFileName, which can be used to prefix the process name to the archive filename to make the format filename_processname_timestamp.

SOA 11g (11.1.1.3/+) 

UseLongArchiveFileName is not available in 11g. Apply Patch 10155914 and it will become available.    

SOA 11g (11.1.1.5/+) 

Apply Patch 13249896.
A new property  UseDigest can be used now in addition to  UseLongArchiveFileName (11g format as filename_processname_digest_timestamp) with File/FTP adapter.
By default UseDigest is set to true which leads to the digest (encrypted token) being sandwiched in the filename and timestamp as filename_digest_timestamp.
Setting this property to false will change the format to filename_timetamp. To use this property edit the .jca file to include this property as given below.

SOA 11g (11.1.1.7/+)

UseDigest property will be available by default without the need to apply the above patch.

There is, however, no further customization possible (presently) in terms of adding instance id to the file name or specifying custom file name. Hope the same gets available in the 11.1.1.7(+) or 12C version.

Tuesday, April 10, 2012

Polling files on-demand : File Based Triggers

Many a times there is a need to trigger the polling of File/FTP Adapter on the basis of some event e.g. after process A has finished writing the file F1, process B should poll for the file F1 OR just process the files only between 10.00 AM to 11.00 AM. While the above could be accomplished to some extent using Sync Read operation, but it requires the knowledge of the File Name in advance (wild cards can't be used).

Well, the solution is File-Based Triggers. File-based triggers can be used to control the File/FTP adapter activation. File Triggers are nothing but the files with .trg extension. The content of trigger file doesn't matter. When File-based triggers are used, File/FTP adapter checks for the existence of a Trigger file (e.g. ReadFile.trg) before reading the actual file. If the trigger file is found, it reads and processes the actual file else it does nothing. You can configure how frequently the adapter checks for the trigger file and hence can control the processing of the actual files.
To use a File-Based trigger, follow the below steps.
1.) Define a File/FTP adapter with Read Operation.
2.) On the File Polling page, Check the option Use trigger file and provide the Trigger File details as shown below.

3.) Complete the adapter wizard.
4.) Follow the normal steps to create a bpel process to poll the file. Deploy this process.

As soon as the process is deployed, File/FTP adapter looks for the trigger file in the C:/TriggerFileDir location. If it finds the triger file, it will poll and process the file. How frequently it looks for the trigger file depends on the property TriggerFileStrategy (we didn't specify this property anywhere because the property is set already by default to value EndpointActivation).

The various possible values for TriggerFileStrategy are:
OnceOnly: The adapter will looks for the trigger file only once in its lifetime. Once it finds the trigger file, it remembers that across restarts and redeployments. This could be very risky as the adapter would never look for a trigger file again in future.

EveryTime: The adapter looks for the trigger file everytime it goes for polling the file. At every polling interval, it checks if the trigger file is there or not. If the trigger file is present, it picks the actual file else it will do nothing and will check for the existence of trigger file at next polling cycle.

EndpointActivation (Default): The adapter looks for the trigger file every time the composite is activated that means everytime you Restart/Activate/Redeploy your composite.

Add the property in .jca file of your File Adapter as shown below and it's done.

Note: You may want to delete the Trigger file after your file has been successfully processed. For this, there is no Delete operation. But you can use Sync Read operation to read the trigger file and check Delete Files after successful retrieval option.

Happy to hear back if any suggestions or comments!

Sunday, January 22, 2012

Sending attachment using Email Activity in BPEL 11g

Sending an attachment using Email Activity (in SOA 11.1.1.5) is an easy task. All you have to do is drag and drop an email activity and double click on it. Assign the following parameters and the attachment will be sent along with the email.
Name- Name of the attachment as it should appear
MimeType- This MimeType is for attachment. Choose appropriate MimeType from here.
Value - Write what you would like to see in the attachment. Simply hard coding.

Well, sometimes the requirement may be to send some custom data, may be you fetched some data at runtime from the DB and would lke to send it as attachment. This isn't complex either, all you have to do is modify the ContentBody parameter as follows. You can do this successfully from the EmailParamsAssign Activity but don't forget to verify the same in the bpel source too.


Extending this a bit further, if you'd like to send some files (image,pdf,doc,xls,csv) that are already stored at some server location or you may have written them from your process, follow these  steps.
1.) Set the appropriate MimeType for the file.
2.) Add the following line in the .bpel source under appropriate BodyPart.

3.) Add another copy operation in the bpel source to assign string('base64') to ContentEncoding in BodyPart[2].

4.) Modify the  ContentBody  parameter to read the file at runtime using ora:readFile() function as follows.


Key Points to Note:

  • The file location and name in ora:readFile() above are case-sensitive.
  • Specifying the correct MimeType is very important. If the MimeType is incorrect, either the mail won't go or you may get encrypted characters in the attached file.
  • file:/// (three slashes mean absolute path and two slashes mean relative path)

The above solution works with all the files. Should anyone need the working project, I can mail the same. 

Inserting a New Line character in message using BPEL XSLT

In a requirement, we were trying to format the data fetched from DB (in XML format in BPEL) into a String variable with a new line inserted after each record. So that the data below would appear in the String variable like Following is the simple transformation to achieve the same.

<xsl:for-each select="/ns0:SelectFromLookupOutputCollection/ns0:SelectFromLookupOutput">
<xsl:value-of select='concat(ns0:property_name, ",", ns0:property_value,"&#10;")'/>
</xsl:for-each>

Key Points to Note:

  • The magic lies in the word &#10; which serves as a new line character in XSLT (11g). In above case, it gets appended at the end of each record resulting in new line at the end of each record.
  • The function somehow doesn't work in SOA Suite 11.1.1.3 (BUG-13602524, thanks to my colleague Nivea for identifying this) but works in 11.1.1.4 and older versions.


Tuesday, January 17, 2012

How to pass JNDI name dynamically to an Adapter

Recently, I've been trying configure an MQ adapter that could enqueue data of any structure into specified queue based on the specified JNDI, all passed dynamically at runtime. This post decsribes how you can pass the JNDI Name dynamically to an Adapter.

All you have to do is define a
String variable (say myJndiName) that stores your JNDI name (eis/MQ/someThing...already configured on Weblogic) and pass this variable into Invoke Activity properties directly in the .bpel source.   

Note that you'll not be able to find this property in the
Invoke Activity > Properties tab.


And that's it. You can assign different values on the fly to the variable myJndiName and same adapter can enqueue in different JNDI locations. It also works for FTP and JMS adapters too. 

Friday, October 7, 2011

How to pass the queue name dynamically to MQ Adapter

Requirement: To pass the queue name dynamically to MQ Adapter from a variable


Use-case: I have a process that is required to enqueue the data in one of the two MQs at runtime based on certain condition using single MQ Adapter. So if the condition is true the data is to be enqueued in queue A else in queue B. However, the payload schema remains the same in both the queues. (using SOA Suite 11.1.1.3)


Solution: Use the jca.mq.ISpec.EnqueueMsgToQ property appearing in the Properties tab of Invoke Activity. Assign the value of the property to the variable name which contains the MQ name.
Passing Queue Name from a variable


Setting the value to this variable will allow you to set the queue name dynamically in the process or from outside the process.


Advantage: MQ Adapter can be loosely coupled and kept in a separate composite which can be called by any caller composite. Also a single MQ Adapter can be used for en-queuing to multiple queues. 

Wednesday, July 13, 2011

Pass multiple parameters, dynamic query in Pure SQL in Database Adapter

I came across a case in which I was required to pass the bind parameters values on the fly. The values of parameters were not fixed and hence I couldn't define all of them as the bind parameters. Hence in the search of the solution I wasted considerable time until I was directed to see this great solution. The solution was fantastic and I customized it further to make it work with SOA Suite11g. This can be further extended with Joins to do wonders as required. 


Use Case : I want the salaries of all the employees whose first names I'd pass at run time. Hence it can be single name or n names coming as parameters. The very simple query, as it comes to our mind, would be


SELECT SALARY FROM EMPLOYEES WHERE F_NAME IN (?) 
Where ? is the argument that is passed dynamically. 


However, the same query doesn't work in DB adapter for any combination passed dynamically such as ('NEERAJ', 'JACOB') or "('NEERAJ', 'JACOB')" or 'NEERAJ', 'JACOB' for the adapter tries to interpret the incoming argument as a bind parameter and not SQL string.


Solution :  If the parameters can be passed as a result of an inner query the problem can be solved. The following query uses CONNECT BY LEVEL clause to separate the individual parameter values separated by a and represents them as individual result row.


SELECT SALARY FROM EMPLOYEES WHERE F_NAME 
IN (WITH BIND_PARAM_LIST AS
(SELECT #val FROM dual)
SELECT SUBSTR(#val1, (decode(LEVEL, 1, 0, instr(#val2, ':', 1, LEVEL -1)) + 1), (decode(instr(#val3, ':', 1, LEVEL) -1, -1, LENGTH(#val4), instr(#val5, ':', 1, LEVEL) -1)) -(decode(LEVEL, 1, 0, instr(#val6, ':', 1, LEVEL -1)) + 1) + 1) a
FROM 
BIND_PARAM_LIST CONNECT BY LEVEL <=
(SELECT(LENGTH(#val7) -LENGTH(REPLACE(#val8, ':', NULL)))
FROM 
BIND_PARAM_LIST) + 1)


To understand the query one needs to be familiar with the CONNECT BY clause which is used for hierarchical queries. If you simply do 


SELECT LEVEL FROM DUAL CONNECT BY LEVEL <=5, it prints 1 to 5 in each row like

LEVEL
=====
1
2
3
4
5


The same logic is being used in the above query to separate the various F_Names. It just sub-strings the incoming string NEERAJ:JACOB:ROBERT, wise for each LEVEL to feed the result in the IN clause as 


NEERAJ
JACOB
ROBERT


Usage : Just add this query in the DB Adapter. Be careful to use # instead of ? as in 11g the bind variable name can be defined as #bind_variable, in our case #val1, #val2 ... #val8. Also you'll have to assign the same input string to these multiple vals. The val variables get exposed as input parameters while assigning and can be assigned as 'NEERAJ:JACOB:ROBERT'(your parameter list). You can use any other separator too e.g. ; instead of : by customizing the query a bit. 

Note:The above solution requires to assign the same value to bind parameter #val 9 times. This limitation is addressed by a more-refined Query as suggested here.

Just deploy the project and it is done. The same thing can also be achieved by using query-database() function as described here.