Best Practices from Oracle Development's A‑Team

Calling Oracle Documents Cloud Service REST APIs from WebCenter Content Custom Components

Mark Foster

Files from Oracle Documents Cloud Service can be migrated to and from WebCenter Content using the SkySync desktop application, which makes for simple transferring between WCC and ODCS. However, other use cases with ODCS may be desired from within WCC itself. To interact with ODCS from WCC, a custom component can be created to call Documents Cloud REST APIs. This allows for creating hybrid ECM solutions specific to business needs. This post illustrates one method for integrating WCC with ODCS using the AppLink REST API.

The method of creating folders in Documents Cloud and displaying AppLinks in on-premise WCC has many potential applications. Moreover, the use of the AppLink feature of Documents Cloud has many integration possibilities outside of WCC and the same approach may be re-used in other Oracle applications, and even third party applications. The use case here is just an illustration of how to use the AppLink feature in on-premise WebCenter Content to achieve additional hybrid ECM functionality.

The use case in the sample is to create Documents Cloud attachments tied to an "anchor" document in WebcenterContent. The demo uses a “JobApplications” profile as the sample use case. The WCC content item is a Job Posting that provides the anchor for attachments in the cloud. When a Job Posting content item is created in WCC, a cloud folder is created at the same time and the GUID for that cloud folder is stored in a WCC metadata field. The AppLink feature allows temporary access for non-provisioned Documents Cloud users. This is accomplished through a REST call that grants tokens for displaying a cloud folder.
When the anchor content item’s DOC_INFO page is displayed, an AppLink is generated to display the embedded cloud folder. This provides an attachments user interface in on-premise WCC.

The embedded AppLink on WebCenter Content for this example displays the iframe on the top of the DOC_INFO page. This enables drag and drop uploads to Documents Cloud and secures the cloud content so that it is only visible to users that have access to that content item in WCC.

Topics covered in this post are:

  • Webcenter Content Filters
  • Reading Credentials from Enterprise Manager in WCC Component
  • Using Jersey to call Documents Cloud Rest Services
  • Parsing JSON responses into POJOs
  • Overriding the display of a metadata field to display the AppLink iframe



Documents Cloud folder AppLink embedded on DOC_INFO page in WebCenter Content



WebCenter Content Filters Review

Using WebCenter Content filters, functionality to call Documents Cloud Service REST services can be added to a custom component. Filter events execute on service calls and allow for modified functionality of content server actions such as checkins, metadata updates, or retrieving content information. In this example, filter events in the CHECKIN and DOC_INFO services are used to trigger REST calls to Documents Cloud. A custom component's hda file, also known as a "glue file" which describes the component, has a section for filter events that define Java methods for execution. The Filters result set in the hda file define the event, such as postComputeDocName or onEndServiceRequestActions, and the Java class that extends the intradoc.shared.FilterImplementor class. The FilterImplementor class has a doFilter method that can be overridden. This is where custom code can be placed to run.

postComputeDocName: Upon checkin of a new content item, a cloud folder is created and the GUID for that folder is stored in a metadata field.
The filter class demo.filter.DOCSRestFilterSample accesses a CSF key for the Documents Cloud username and password. A call is made to the ODCS REST API to create a folder. The returned JSON response is stored in a POJO called FolderItems.java. This class allows easy access to the JSON response in a Java object. For details on the REST call to create a folder, see the documentation on the Folder Resource.

onEndServiceRequestActions: When a DOC_INFO page is accessed, this filter is used to read the CSF key for the Documents Cloud username and password. A REST call is then made to create an AppLink in ODCS. The JSON response is parsed into a class called AppLink.java. This allows simple access to the AppLink details. For details on the REST call to create a folder, see the documentation on the AppLink Resource. The AppLink  response contains variables that must be set into the databinder’s local data section so that the values can be accessed from idocscript. An applink response contains several key pieces: a url, an access token, a refresh token, and the user's role for access rights to the Cloud Folder. At a minimum these values are needed to render an iframe properly with an AppLink url.


@ResultSet Filters 4 type location parameter loadOrder postComputeDocName demo.filter.DOCSRestFilterSample null 1 onEndServiceRequestActions demo.filter.DOCSAppLinkRestFilterSample null 1 @end


Note: Discovering which filter to use for customizations is an important step in creating WebCenter Content custom components that use filters. To determine which filter to use, verbose "services" tracing on the System Audit Information page can be enabled. Be aware that this tracing is quite verbose, so clear the output and then run the service for which you are seeking a filter to use. Viewing only the output for the service call you are investigating will make it much easier to determine what filter hooks are available for your customization.


Since only two filters are used in this demo, only two Java classes are needed in the component to create the integration. Both classes are similar in that they require:

1. Access to a secured username and password, which can be accessed using JPS packages. The credentials are secured in Enterprise Manager and obtained only at runtime.

2. Ability to call external REST services. This can be done using Jersey or Apache CXF libraries (which must be added to the classpath in WCC). This example uses the Jersey classes.

3. Interaction with the service call's localdata in the databinder. This is needed to modify the behavior of the checkin and document information service calls.



Create a credential in Enterprise Manager

A credential can be created through Enterprise Manger (EM) or through WebLogic Scripting Tool (WLST). This is needed to secure the REST calls to the Documents Cloud Service. The filters will need to access the map and key programatically.

To create a credential using WLST, execute the following command:

createdCred(map="oracle.documents.cloud.service", key="DocumentsCloudUser", user="peter.flies@oracle.com", password="mysecretpassword")


To create a credential using EM, do the following:
1. Log in to Enterprise Manager.
2. Click WebLogic Domain.
3. Click Security and then Credentials.
4. Create the map and key:
a. Select Create Map.
b. Enter oracle.documents.cloud.service in the map field and click OK. Note that this can be something else if preferred, just remember to update the component’s environment setting DocumentsCloudUserCredMap.
c. Click Create Key. The key is now available for selection.
5. Enter a key name of DocumentsCloudUser. This is the credential alias used in the component’s environment file DocumentsCloudUserCredKey. This name can be anything, but the key must match the component’s environment file to locate the key.
6. Select password as the type.
7. Enter a user name and password. (e.g peter.flies@oracle.com, welcome1)
8. Optionally, enter a description for the credential. (e.g. “Documents Cloud user for Job Posting attachments”)
9. Click OK.


Once this map and key are created, the JpsContectFactory class can be used to get the credentials at runtime. A code snippet shows a method of interaction with the factory.

            JpsContextFactory ctxFactory;             CredentialStore store;             ctxFactory = JpsContextFactory.getContextFactory();             JpsContext ctx = ctxFactory.getContext();             store = ctx.getServiceInstance(CredentialStore.class);             PasswordCredential pc = null;             pc = (PasswordCredential) store.getCredential(map, key);             if (pc == null) {                 Report.trace("restfilter", "Credential not found for map " + map + " and key " + key + ".", null);                 throw new DataException("Credential not found for map " + map + " and key " + key + ".");             }             String authString = pc.getName() + ":" + new String(pc.getPassword());             try {                 encodedAuth = DatatypeConverter.printBase64Binary(authString.getBytes("UTF-8"));             } catch (UnsupportedEncodingException e) {                 e.printStackTrace();             }



Creating the Folder and Storing the Folder GUID in Metadata

During a new checkin event a folder is created in Documents Cloud Service using standard Jersey libraries to make the REST call. The HTTP request is sent, and the JSON response will contain the newly created folder, identified by a GUID. This GUID must be kept with the content item in WCC. Metadata fields in WCC are ideal for storing this GUID. Worth noting is that this should only be executed on a new checkin and not a revision. Likewise, this should not occur on a metadata update. The cloud folder creation in this example is a one-time event, since the GUID will be used throughout all revisions of the WCC content item. The metadata field should also be "info only" for all users, locked down to block updates to the field.

The call to Documents Cloud REST APIs in Jersey can be done using the usual Jersey methods. One item to note here is that the client configuration can use FEATURE_POJO_MAPPING to map the JSON response into a Java object. This must be enabled when creating the client configuration.


        ClientConfig cc = new DefaultClientConfig();         Client client = null;         cc.getClasses().add(MultiPartWriter.class);         cc.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);

Once the client is prepared, the folder creation can be executed. Creating a Documents Cloud folder takes a JSON payload with a name and description. The POST message is sent and the response is mapped to a FolderItems object, which is a POJO that represents the JSON response.

        String jsonPayload = "{\"name\":\"" + folderName + "\",\"description\":\"" + folderDescription + "\"}";         WebResource webResource = client.resource(restRequestUrl);         Report.trace("restfilter", "Calling POST on " + restRequestUrl + " with payload " + jsonPayload, null);         ClientResponse response =             webResource.header("Authorization", "Basic " + encodedAuthString)             .header("Content-Type", "application/json")             .post(ClientResponse.class, jsonPayload);         //Check for success, otherwise exit. Can be 200 or 201 on folder create         if (response.getStatus() != 200 && response.getStatus() != 201) {             Report.trace("restfilter", "Rest call failed with " + response.getStatus() + "\nStatus Info: " + response.getStatusInfo(), null);             throw new RuntimeException("Failed : HTTP error code: " + response.getStatus() + "\nStatus Info: " +                                        response.getStatusInfo());         }         fi = response.getEntity(FolderItems.class);


Once the POJO is ready, the metadata field can be assigned the folder GUID using the standard WCC databinder method putLocal.

            //Persist new folder into the metadata field             dataBinder.putLocal(folderGUIDField, fi.getId());

At this point, the cloud folder is saved to the content item's metadata in WCC.


Creating the AppLink

In the other filter, a similar process is used to create the AppLink so that the logged in user can see the embedded Documents Cloud folder. The saved GUID in the metadata field is used to create the AppLink. The AppLink REST API is called in the same manner as the create folder API was called. First, the credential for the Documents Cloud user must be obtained. The AppLink service can be called using Jersey, and the JSON response can be parsed into a POJO as well to obtain the token information.

The service call to create a folder AppLink requires a JSON payload with "assignedUser" and "role". The user that is currently logged into WCC should be the "assignedUser". This demonstration assigns the "contributor" role to all AppLinks created, but the role could be determined if the user's access rights to the content item were interrogated. For simplicity of the demo, the same role is used for all WCC users that have access to the content item's DOC_INFO page.

        AppLink al = null;         String jsonPayload = "{\"assignedUser\":\"" + assignedUser + "\",\"role\":\"" + role + "\"}";         WebResource webResource = client.resource(restRequestUrl);         Report.trace("restfilter", "Calling POST on " + restRequestUrl + " with payload " + jsonPayload, null);         ClientResponse response =             webResource.header("Authorization", "Basic " + encodedAuthString)             .header("Content-Type", "application/json")             .post(ClientResponse.class, jsonPayload);         //Check for success, otherwise exit. Can be 200 or 201 on folder create         if (response.getStatus() != 200 && response.getStatus() != 201) {             Report.trace("restfilter", "Rest call failed with " + response.getStatus() + "\nStatus Info: " + response.getStatusInfo(), null);             throw new RuntimeException("Failed : HTTP error code: " + response.getStatus() + "\nStatus Info: " +                                        response.getStatusInfo());         }         al = response.getEntity(AppLink.class);


Once the response is obtained and parsed into a POJO created for the AppLink, the databinder can have the required tokens and details added using the putLocal method.

            dataBinder.putLocal("appLinkRefreshToken", al.getRefreshToken());             dataBinder.putLocal("appLinkAccessToken", al.getAccessToken());             dataBinder.putLocal("appLinkUrl", al.getAppLinkUrl());             dataBinder.putLocal("appLinkRole", al.getRole());



Displaying the AppLink in an iframe on the DOC_INFO page

With the AppLink URL prepared and ready for display, the remaining step is to enable it on the desired page. This example overrides the metadata field that is storing the GUID and replaces it with an iFrame. With dynamichtml includes, the display of WebCenter Content pages is customizable and the placement of the iframe could be done in different ways. A simple way to do this is to use a custom include for the metadata field (e.g. xDocumentCloudFolder in the sample).

The appLinkUrl that was already set in the databinder is accessible in server side idocscript. This allows the iframe source URL to be set during page creation. The include that displays the field should be limited only to execute when the page is a DOC_INFO page (IsInfo) and when the field actually contains a value. Custom include for metadata field that holds the cloud folder GUID

Using Profiles and Rules, the display of the cloud folder metadata field can be altered as needed. Profiles and Rules are a feature of WebCenter Content and allow admin users to change behavior and display of metadata fields.

As was stated before, the display of the iframe can be done in different ways and this is only one example of how to accomplish the UI end of this demonstration.

<@dynamichtml DOCSRestFilterSample_display_field_xDocumentCloudFolder@>  [[% Used as a custom include in the rule DocumentsCloudFolder. Displays iframe for applink to load.%]]  <$if isInfo AND fieldValue$>   <$trace("DOCSRestFilterSample display_field_xDocumentCloudFolder", "#console", "restfilter")$>   <iframe  id="content_frame" src="<$appLinkUrl$>" style="width: 100%; height: 520px; overflow: scroll;" ></iframe>  <$endif$> <@end@>




Handling the appLinkReady messaging between the parent and the iframe

When using the AppLink resource of Documents Cloud, placing the returned URL into an iframe's src attribute is not the last step. A message must be handled from the Documents Cloud service when the AppLink is "ready". This can be done using the HTML5 postMessage method. A message containing the access tokens and the user role must be passed to the iframe in order for the AppLink to appear.

One dynamichtml include that can handle the appLinkReady event from Documents Cloud Service is custom_schema_finalize_display. This include is used because it is one of the last to load during page creation of WebCenter Content pages, meaning the DOM is ready.  JavaScript handling of the appLinkReady event is added to pass the appropriate message to the iframe. Again, the contributor role is assumed in this sample but could be assigned based on WCC rights for the logged in user.

<@dynamichtml custom_schema_finalize_display@>  [[% Outputs trace to the console on the "system" subject. Also, browser console outputs added for illustration purposes. %]]  <$trace("DOCSRestFilterSample include", "#console", "restfilter")$>   console.log("DOCSRestFilterSample: applink in ready function");    function OnMessage (evt) {     console.log("DOCSRestFilterSample: onMessage function " + evt);    if (evt.data.message === 'appLinkReady') {     console.log("OnMessage invoked for appLinkReady event...");     var dAppLinkUrl = "<$appLinkUrl$>";     var dAppLinkRefreshToken = "<$appLinkRefreshToken$>";     var dAppLinkAccessToken = "<$appLinkAccessToken$>";     var dAppLinkRoleName="<$appLinkRole$>";     var embedPreview = "true";     var iframe= document.getElementById("content_frame");     var iframewindow= iframe.contentWindow ? iframe.contentWindow : iframe.contentDocument.defaultView;      var msg = {      message: 'setAppLinkTokens',      appLinkRefreshToken:dAppLinkRefreshToken,      appLinkAccessToken:dAppLinkAccessToken,      appLinkRoleName:dAppLinkRoleName,      embedPreview:embedPreview     }     console.log("DOCSRestFilterSample: sending message to iframe with access and refresh tokens");     iframewindow.postMessage(msg, '*');    }   };   window.addEventListener && window.addEventListener('message', OnMessage, false); <@end@> 

If the appLinkReady event is not handled, the AppLink URL will show a generic loading page that will never fully render.



AppLink in Action: Checkin a Job Posting and Add Cloud Attachments


The end user usage of this example is a WebCenter Content user that creates Job Postings and attaches or views submitted resumes for candidates seeking the job. The steps are basic content server tasks:

1. Checkin a content item

2. Go to the DOC_INFO page for the content item.

3. Drag and drop attachments into the iframe.

4. View the attachments.

5. When the user navigates away from the DOC_INFO page, the AppLink is no longer used. Each time the DOC_INFO page is opened, a new AppLink is generated.


After checking, the DOC_INFO page will show an empty cloud folder in the iframe.


Information page displaying newly created Cloud Folder with no files

Files can be dragged into the folder or uploaded using the Upload icon on the AppLink. The AppLink can be opened in a separate window as well using the popout icon.

To verify the folder's creation, log into the Documents Cloud Service as the owner of the folders in a separate browser or window. As users create Job Postings, folders will begin appearing in the Documents Cloud interface, but using the AppLink feature, users will only see the folder tied to the content item in WebCenter Content.

Documents uploaded to Cloud folder



Sample Component

Download the attached DOCSRestFilterSample.zip file for the sample component described in this post. Note that this sample is as-is and not supported, and solely to show the possibilities of WCC integrations to Documents Cloud throught the REST API.





Join the discussion

Comments ( 1 )
  • Bridgett Tuesday, August 6, 2019
    Spot on with this write-up, I absolutely believe that this website
    needs far more attention. I'll probably be back again to
    read through more, thanks for the information! https://www.youtube.com/watch?v=Zj4WlFiePbE
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha