Retrieving uploaded attachments -UCM-
As stated in my previous blog entry, Oracle BPM 11g 11.1.1.5.1 (aka PS4FP) introduced a new cool feature whereby you can use Oracle WebCenter Content (previously known as Oracle UCM) as the repository for the human task attached documents. For more information about how to use or enable this feature, have a look here.
The attachment scope (either TASK or PROCESS) also applies to UCM-attachments. But even with this other feature, one question might arise when using UCM attachments. How can I get them from within the process?
The first answer would be to use the same getTaskAttachmentContents() XPath function already explained in my previous blog entry. In fact, that's the way it should be. But in Oracle BPM 11g 11.1.1.5.1 (PS4FP) and 11.1.1.6.0 (PS5) there's a bug that prevents you to do that. If you invoke such function against a UCM-attachment, you'll get a null content response (bug#13907552). Even if the attachment was correctly uploaded.
While this bug gets fixed, next I will show a workaround that lets me to retrieve the UCM-attached documents from within a BPM process. Besides, the sample will show how to interact with WCC API from within a BPM process.Aside note: I suggest you to read my previous blog entry about Human Task attachments where I briefly describe some concepts that are used next, such as the execData/attachment[] structure.
Sample Process
I will be using the following sample process:
A dummy UserTask using "HumanTask2" Human Task, followed by an Embedded Subprocess that will retrieve the attachments payload. In this case, and here's the key point of the sample, we will retrieve such payload using WebCenter Content WebService API (IDC):
and once retrieved, we will write each of them back to a file in the server using a File Adapter service:
In detail:
We will use the same attachmentCollection XSD structure and same BusinessObject definition as in the previous blog entry. However we create a separate variable, named attachmentUCM, based on such BusinessObject.
We will still need to keep a copy of the HumanTask output's execData structure. Therefore we need to create a new variable of type TaskExecutionData (different one than the other used for non-UCM attachments):
As in the non-UCM attachments flow, in the output tab of the UserTask mapping, we'll keep a copy of the execData structure:
Now we get into the embedded subprocess that will retrieve the
attachments' payload. First, and using an XSLT transformation, we feed
the attachmentUCM variable with the following information:
The name of each attachment (from execData/attachment/name element)
The WebCenter Content ID of the uploaded attachment. This info is stored in execData/attachment/URI element with the format ecm://<id>. As we just want the numeric <id>, we need to get rid of the protocol prefix ("ecm://"). We do so with some XPath functions as detailed below:
with these two functions being invoked, respectively:
We, again, set the target payload element with an empty string, to get the <payload></payload> tag created.
The complete XSLT transformation is shown below. Remember that we're using the XSLT for-each node to create as many target structures as necessary.
Once we have fed the attachmentsUCM structure and so it now contains the name
of each of the attachments along with each WCC unique id (dID), it is time to iterate through it and get
the payload. Therefore we will use a new embedded subprocess of type MultiInstance, that will iterate over the attachmentsUCM/attachment[] element:
In each iteration we will use a Service activity that invokes WCC API through a WebService. Follow these steps to create and configure the Partner Link needed:
Login to WCC console with an administrator user (i.e. weblogic). Go to Administration menu and click on "Soap Wsdls" link. We will use the GetFile service to retrieve a file based on its dID. Thus we'll need such service WSDL definition that can be downloaded by clicking the GetFile link. Save the WSDL file in your JDev project folder.
In the BPM project's composite view, drag & drop a WebService adapter to create a new External Reference, based on the just added GetFile.wsdl. Name it UCM_GetFile.
WCC services are secured through basic HTTP authentication. Therefore we need to enable the just created reference for that:
Right-click the reference and click on Configure WS Policies.
Under the Security section, click "+" to add the "oracle/wss_username_token_client_policy" policy
The last step is to set the credentials for the security policy. For the sample we will use the admin user for WCC (weblogic/welcome1). Open the composite.xml file and select the Source view.
Search for the UCM_GetFile entry and add the following highlighted elements into it:
<reference name="UCM_GetFile" ui:wsdlLocation="GetFile.wsdl">
<interface.wsdl interface="http://www.stellent.com/GetFile/#wsdl.interface(GetFileSoap)"/>
<binding.ws port="http://www.stellent.com/GetFile/#wsdl.endpoint(GetFile/GetFileSoap)"
location="GetFile.wsdl" soapVersion="1.1">
<wsp:PolicyReference URI="oracle/wss_username_token_client_policy"
orawsp:category="security" orawsp:status="enabled"/>
<property name="weblogic.wsee.wsat.transaction.flowOption"
type="xs:string" many="false">WSDLDriven</property>
<property name="oracle.webservices.auth.username"
type="xs:string">weblogic</property>
<property name="oracle.webservices.auth.password"
type="xs:string">welcome1</property>
</binding.ws>
</reference>
Now the new external reference is ready:
Once the reference has just been created, we should be able now to use it from our BPM process. However we find here a problem. The WCC GetFile service operation that we will use, GetFileByID, accepts as input a structure similar to this one, where all element tags are optional:
<get:GetFileByID xmlns:get="http://www.stellent.com/GetFile/">
<get:dID>?</get:dID> <get:rendition>?</get:rendition> <get:extraProps> <get:property> <get:name>?</get:name> <get:value>?</get:value> </get:property> </get:extraProps></get:GetFileByID>
and we need to fill up just the <get:dID> tag element. Due to some kind of restriction or bug on WCC, the rest of the tag elements must NOT be sent, not even empty (i.e.: <get:rendition></get:rendition> or <get:rendition/>). A sample request that performs the query just by the dID, must be in the following format:
<get:GetFileByID xmlns:get="http://www.stellent.com/GetFile/"> <get:dID>12345</get:dID></get:GetFileByID>
The issue here is that the simple mapping in BPM does create empty tags being a sample result as follows:
<get:GetFileByID xmlns:get="http://www.stellent.com/GetFile/">
<get:dID>12345</get:dID>
<get:rendition/>
<get:extraProps/>
</get:GetFileByID>
Although the above structure is perfectly valid, it is not accepted by WCC. Therefore, we need to bypass the problem. The workaround we use (many others are available) is to add a Mediator component between the BPM process and the Service that simply copies the input structure from BPM but getting rid of the empty tags. Follow these steps to configure the Mediator:
Drag & drop a new Mediator component into the composite.
Uncheck the creation of the SOAP bindings and use the Interface Definition from WSDL template and select the existing GetFile.wsdl
Double click in the mediator to edit it. Add a static routing rule to the GetFileByID operation, of type Service and select References/UCM_GetFile/GetFileByID target service:
Create the request and reply XSLT mappers:
Make sure you map only the dID element in the request:
And do an Auto-mapper for the whole response:
Finally, we can now add and configure the Service activity in the BPM process. Drag & drop it to the embedded subprocess and select the NormalizedGetFile service and getFileByID operation:
Map both the input:
...and the output:
Once this embedded subprocess ends, we will have all attachments (name + payload) in the attachmentsUCM
variable, which is the main goal of this sample. But in order to test
everything runs fine, we finish the sample writing each attachment to a
file. To that end we include a final embedded subprocess to concurrently
iterate through each attachmentsUCM/attachment[] element:
On each iteration we will use a Service activity that invokes a
File Adapter write service. In here we have two important parameters to
set. First, the payload itself. The file adapter awaits binary data in
base64 format (string). We have to map it using XPath (Simple mapping doesn't recognize a String as a base64-binary valid target):
Second, we must set the target filename using the Service Properties dialog box:
Again, note how we're making use of the loopCounter index variable to get the right element within the embedded subprocess iteration.
Final blog entry about attachments will handle how to inject documents to Human Tasks from the BPM process and how to share attachments between different User Tasks. Will come soon.
Again, once I finish will all posts on this matter, I will upload the whole sample project to java.net.