But what if you don't know the filenames at run-time? Well then you can use the Adapter Header variable. But it turns out that the FTP Header does not provide means to give in the host, port, username and password at run-time. At default BPEL PM or ESB assumes that an adapter listens or writes to a fixed location.
Luckily it is possible to provide these at runtime. Doing so, you can create a generic FTP process. For Get-purposes you'll have to use the Synchronous get. The regular get is only usefull for polling adapters or when using Correlation-Sets.
I'll explain the configuration of the Synchronous Get service first. Then I'll explain the Put-service..
Synchronous Get
Create a partnerlink for a FTP Adapter. Accept the default JNDI name. Give it a proper service name, for instance: “FTPSynchGet”.
For the file-type choose “binary”, for the operation type choose “Synchronous Get File”.
For the directory name give a dummy directory, for instance “/tmp”. Uncheck the checkbox for deleting the file afterwards. (Probably we'll have to check it in the end solution, but for now I don't want it. It might be a good idea to have a deleting adapter and a non-deleting one if it is necessary to choose between these options).
Give the filename a dummy value, for instance “tmp.txt”.
Check the box “Native formatting is not required (Schema is Opaque)”.
Since it is a synchronous get, you have to invoke it, so use an invoke activity, and not a receive activity. The adapter wizard should have created a ftpAdapterOutboundHeader.wsdl. This is because the synchronous get is to be invoked having a header variable that specifies which file to get. By default the schema type that is created in it only contains a “fileName” element:
Copy the fileName element and name it “directory” to have it a directory entry also:
This seams to be a bug, the wizard won't create this element.
At the adapter-tab of the invoke activity properties define a header-variable based on the message-type in this wsdl. This variable can be used to define the input file and directory.
The FTPSynchGet.wsdl has a service with a jca:address that is based on a JNDI-location:
Replace this one to have it based on a ManagedConnectionFactory with mcf properties:
This way you can specify the the hosname, username and password on runtime with an assign statement.
To do this add an assign activity before the invoke. Add copy-rules to it for the host-name, username and password, where these values are fetch from a variable with an expression. This expression is based with on a bpws:getVariableData() statement within a string() function. The bpws:getVariableData() statement returns a XML-Node, but it needs to be serialized to a string using the string() function. The target should be the FTPSynchGet partnerlink with a bpelx:property. This bpelx:property should be one of:
jca.mcf.Host
- jca.mcf.Port
- jca.mcf.Username
- jca.mcf.Password
Then add an assign activity to copy the values for the filename and directory to the header variable.
FTP Put
The configuration of the FTPPut partnerlink is mainly the same as the configuration of the FTPSynchGet. Create a partnerlink, accept the JNDI location, choose File Type “binary”, Operation “Put”, give in a default directory and filenaming convention. It is then important to uncheck the “Number of messages equals” box. If you don't, the wsdl wil contain the attribute 'NumberMessages="1"' in the jca:operation element. This will lead into the error:
ORABPEL-11061
Batching is not allowed if dynamic directories are specified.
Batching is not allowed if dynamic directories are specified.
Please ensure that parameters such as NumberMessages, ElapsedTime or FileSize are not defined in the case of dynamic directories.
Removing the attribute will solve this.
Finish the wizard, with choosing an opaque schema.
Also in the FTPPut.wsdl the jndi-location in the jca:address should be replaced with the ManagedConnectionFactory like in the FTPSynchGet.
Add an invoke activity for the FTPPut partnerlink, and define a header variable on the adapter-tab, based on the message type in the ftpAdapterOutboundHeader.wsdl.
Then you can add comparable assign statements for the Hostname, username, password, filename and directory as described with the FTPSynchGet.
Don't forget to assign the Base64-encoded value to the input variable of the invoke of the FTP Put. For example the value of the outputvariable of the invoke of the FTPSynchGet.
Excuse me for the small screendumps. The Blog has some difficulties with XML-fragments.
Hi,
ReplyDeleteNice blog you have here. Have you ever tried same thing with Oracle ESB? How would you give dynamically username, password, hostname and port in ESB?
Hi Samik,
ReplyDeleteNo I have not tried it using ESB. That was my intention though, but I have not found the time. In my case I had to do some logging and scheduling as well. So I needed BPEL anyway.
The big side affect of this solution is that the file/ftp adapter reads the complete file in memory (base64 encoded). So it cannot be too large.
For large files I created a simple java webservice that gets the file using FTP and puts it to the other server. I think I write a blog about that one too. Although it is fairly simple using jFTP.
Regards,
Martien
Hi,
ReplyDeletegreat blog, but can you tell me how could i read 2 files from different directory in same Process using FTP Adapter.!
and creating 2 different instances.
it might happen that one directory may not cotain a file.. but due to other directory may contain it.!!
Hi,
ReplyDeleteSorry I did not respond earlier, I've seen your comment only now.
The problem is that you cannot use the synchronous get for polling. So In your case you should us the process to read the file and schedule it with some scheduling tool or process. For example see my blog about a schedule process in BPEL.
See: http://darwin-it.blogspot.com/2008/01/how-to-create-bpel-job-scheduler.html
ReplyDeleteVery nice work. Thanks. I had a question. I dont want to expose all details of my ftp adapter like pwd. However I still want to make the adapter dynamic. To do this, I want to pass the jndi name as an input, just like I would have passed filename, password, host, port etc
ReplyDeleteHow can I do that.
Hi Amit,
ReplyDeleteI think you cannot do that. The JNDI is fixed, there are no header variables for that.
Because I had to transfer files dynamically for which the BPEL proces had nothing to do with the content, I myself created a Webservice for that purpose. In your case you could somewhere store your passwords encrypted and decrypt the passwords in such a webservice. I used the jFTP java-ftp library from sourceforge. It was really simple to create a java ftp service with that and deploy it as a webservice.
Regards,
Martien