Wednesday, March 21, 2012

UCM file operations (add, edit, delete, search) using RIDC


Top of Form
RIDC provides a thin communication API for communication with UCM Server. This API removes data abstractions to the content server while still providing a wrapper to handle connection pooling, security, and protocol specifics. RIDC supports three protocols: Intradoc, HTTP, and WebServices/JAX-WS of these Intradoc is very popular because it does not require a password, but instead relies on the UCM server's ip filter to trust the connection.
The official RIDC documentation (http://docs.oracle.com/cd/E14571_01/doc.1111/e16819/toc.htm) and java docs (http://docs.oracle.com/cd/E14571_01/apirefs.1111/e17274/toc.htm) provide more information. This post has some sample code that can help implement some of the standard requirements that may come up as part of the project.
Import
The following classes would typically be needed to be imported to perform the following operations
import oracle.stellent.ridc.IdcClient;
import oracle.stellent.ridc.IdcClientException;
import oracle.stellent.ridc.IdcContext;
import oracle.stellent.ridc.model.DataBinder;
import oracle.stellent.ridc.model.DataObject;
import oracle.stellent.ridc.model.DataResultSet;
import oracle.stellent.ridc.model.TransferFile;
import oracle.stellent.ridc.protocol.ServiceResponse;
Connection
Please check the documentation about various types of connections available and how the connectivity is different for each type. This following code allows you to connect using Intradoc (See above).
IdcClientManager myIdcClientManager = new IdcClientManager();
IdcClient myIdcClient = myIdcClientManager.createClient("idc://abc.def.com:4444");
IdcContext myIdcContext = new IdcContext(myUcmUsername);  

Note: The values passed to the DataBinder (e.g. myRequestDataBinder.putLocal("dDocAuthor", myUsernameInSession);) are name-value pairs and are strings.

Search content
The search is typically done using the query string and we can control the number of records returned and how the returned records are sorted.
Quick Tips about query string
  • Passing blank ("") query text fetches all files
  • Passing "dDocAuthor <matches> `WebLogicUser`" as query text fetches all files whose author is WebLogicUser
  • Other keywords being <contains>, <and>, <or> 
  • Use ` to pass value (located under ~ on keyboard) 
  • Use of the below 2 lines is to display uploaded files sorted as per upload date, with most recent at the top
myRequestDataBinder.putLocal("SortField", "dInDate");
myRequestDataBinder.putLocal("SortOrder", "Desc");.
 ServiceResponse myServiceResponse = null;
  
try {
  DataBinder myRequestDataBinder = myIdcClient.createBinder();
  myRequestDataBinder.putLocal("IdcService", "GET_SEARCH_RESULTS");
  myRequestDataBinder.putLocal("QueryText", "");
  myRequestDataBinder.putLocal("ResultCount", "20");
  myRequestDataBinder.putLocal("SortField", "dInDate");
  myRequestDataBinder.putLocal("SortOrder", "Desc");
  myServiceResponse = myIdcClient.sendRequest(myIdcContext, myRequestDataBinder);
   
  DataBinder myResponseDataBinder = myServiceResponse.getResponseAsBinder();
  DataResultSet myDataResultSet = myResponseDataBinder.getResultSet("SearchResults");
  System.out.println("Printing file details...");
  for (DataObject myDataObject : myDataResultSet.getRows()) {
    System.out.println("Id: " + myDataObject.get("dID"));
    System.out.println("ContentId: " + myDataObject.get("dDocName"));
    System.out.println("Title of content: " + myDataObject.get("dDocTitle"));
    System.out.println("Author: " + myDataObject.get("dDocAuthor"));
    System.out.println("Release date: " + myDataObject.get("dInDate"));
    System.out.println("Folder id: " + Long.toString(getFolderIdFromPath("/Contribution Folders/")));
    System.out.println("Mapped folder URL: " + myDataObject.get("mappedFolderURL"));
    System.out.println("Total rows: " + myDataObject.get("TotalRows"));
  }
} catch (IdcClientException idcce) {
  System.out.println("IDC Client Exception occurred. Unable to retrieve files list. Message: " + idcce.getMessage() + ", Stack trace: ");
  idcce.printStackTrace();
} catch (Exception e) {
  System.out.println("Exception occurred. Unable to retrieve files list. Message: " + e.getMessage() + ", Stack trace: ");
  e.printStackTrace();
} finally {
  if (myServiceResponse != null) {
    myServiceResponse.close();
  }
}
 Get folder id from path
Use the below code to upload a file to a particular folder on the UCM server.
ServiceResponse myServiceResponse = null;
Long myFolderId = null;
  
try {
  DataBinder myRequestDataBinder = myIdcClient.createBinder();
  myRequestDataBinder.putLocal("IdcService", "COLLECTION_INFO");
  myRequestDataBinder.putLocal("hasCollectionPath", "true");
// Folder path for which ID is needed.
  myRequestDataBinder.putLocal("dCollectionPath", myFolderPath);
  myServiceResponse = myIdcClient.sendRequest(myIdcContext, myRequestDataBinder);
  DataBinder myResponseDataBinder = myServiceResponse.getResponseAsBinder();
  DataResultSet myDataResultSet = myResponseDataBinder.getResultSet("PATH");
  DataObject myDataObject = myDataResultSet.getRows().get(myDataResultSet.getRows().size() - 1);
  myFolderId = new Long(myDataObject.get("dCollectionID"));
  System.out.println("Folder id: " + myFolderId);
} catch (IdcClientException idcce) {
  System.out.println("IDC Client Exception occurred. Unable to retrieve folder id from path. Message: " + idcce.getMessage() + ", Stack trace: ");
  idcce.printStackTrace();
} catch (Exception e) {
  System.out.println("Exception occurred. Unable to retrieve folder id from path. Message: " + e.getMessage() + ", Stack trace: ");
  e.printStackTrace();
} finally {
  if (myServiceResponse != null) {
    myServiceResponse.close();
  }
}
Add new content
This sample code allows you to upload a local (or remote file) in to UCM. Please refer to "Add new content - additional comments" towards the end of this post.
ServiceResponse myServiceResponse = null;
  InputStream fileStream = null;
  
  try {
    String fileName = "UploadFile.html";
    fileStream = new FileInputStream(fileName);
    long fileLength = new File(fileName).length();
    DataBinder myRequestDataBinder = myIdcClient.createBinder();
    myRequestDataBinder.putLocal("IdcService", "CHECKIN_UNIVERSAL");
    myRequestDataBinder.putLocal("dDocType", "Application");
// Title of the Uploaded file
    myRequestDataBinder.putLocal("dDocTitle", myFileTitle);
// Name of Author
    myRequestDataBinder.putLocal("dDocAuthor", myUsernameInSession);
// Security for the content (Group and Account)
    myRequestDataBinder.putLocal("dSecurityGroup", "Public");
    myRequestDataBinder.putLocal("dDocAccount", "");
    myRequestDataBinder.putLocal("dFormat", "text/html");
    myRequestDataBinder.putLocal("xCollectionID", Long.toString(getFolderIdFromPath("/Contribution Folders/")));
    myRequestDataBinder.addFile("primaryFile", new TransferFile(fileStream, fileName, fileLength, "text/html"));
    myServiceResponse = myIdcClient.sendRequest(myIdcContext, myRequestDataBinder);
     
    InputStream myInputStream = myServiceResponse.getResponseStream();
    String myResponseString = myServiceResponse.getResponseAsString();
    System.out.println("Uploaded file details: \n" + myResponseString);
  
    DataBinder myResponseDataBinder = myServiceResponse.getResponseAsBinder();
  
    System.out.println("File uploaded successfully");
  } catch (IdcClientException idcce) {
    System.out.println("IDC Client Exception occurred. Unable to upload file. Message: " + idcce.getMessage() + ", Stack trace: ");
    idcce.printStackTrace();
  } catch (IOException ioe) {
    System.out.println("IO Exception occurred. Unable to upload file. Message: " + ioe.getMessage() + ", Stack trace: ");
    ioe.printStackTrace();
  } catch (Exception e) {
    System.out.println("Exception occurred. Unable to upload file. Message: " + e.getMessage() + ", Stack trace: ");
    e.printStackTrace();
  } finally {
    if (myServiceResponse != null) {
      myServiceResponse.close();
    }
    if (fileStream != null) {
      try {
        fileStream.close();
      }catch(Exception e) {
        e.printStackTrace();
      }    
    }
 }
Download content
This code allows you to get access to the content.
String myFileToEdit = null;
try {
  DataBinder myRequestDataBinder = myIdcClient.createBinder();
  myRequestDataBinder.putLocal("IdcService", "GET_FILE");
// Document ID
  myRequestDataBinder.putLocal("dID", myId);
// Content ID
  myRequestDataBinder.putLocal("dDocName", myContentId);
  ServiceResponse myServiceResponse = myIdcClient.sendRequest(myIdcContext, myRequestDataBinder);
  int intReportedFileSize = Integer.parseInt(myServiceResponse.getHeader("Content-Length"));
  int intRetrievedFileSize = 0;
  
  InputStream myInputStream = null;
  InputStreamReader myInputStreamReader = null;
  try {
    myInputStream = myServiceResponse.getResponseStream();
    myInputStreamReader = new InputStreamReader(myInputStream, "UTF-8");
    int intRead = 0;
    char[] myCharArray = new char[4096];
    StringBuffer myStringBuffer = new StringBuffer();
    while ((intRead = myInputStreamReader.read(myCharArray)) != -1) {
      myStringBuffer.append(myCharArray, 0, intRead);
      intRetrievedFileSize += intRead;
    }
    myFileToEdit = myStringBuffer.toString();
  } catch (IOException ioe) {
    System.out.println("IO Exception occurred. Unable to retrieve file. Message: " + ioe.getMessage() + ", Stack trace: ");
    ioe.printStackTrace();
    myFileToEdit = null;
  } finally {
    if (myInputStream != null) {
      try {
        myInputStream.close();
      } catch(Exception exception) {
        System.out.println("IO Exception occurred while closing.", Stack trace: ");
        exception.printStackTrace();
      }
    }
    if (myInputStreamReader != null) { 
      try { 
        myInputStreamReader.close();
      } catch(Exception exception) {
        System.out.println("IO Exception occurred while closing.", Stack trace: ");
        exception.printStackTrace();
      }
    }
  }
} catch (IdcClientException idcce) {
  System.out.println("IDC Client Exception occurred. Unable to retrieve file. Message: " + idcce.getMessage() + ", Stack trace: ");
  idcce.printStackTrace();
  myFileToEdit = null;
} catch (Exception e) {
  System.out.println("Exception occurred. Unable to retrieve file. Message: " + e.getMessage() + ", Stack trace: ");
  e.printStackTrace();
  myFileToEdit = null;
}
Update content
The editing of an existing content consists of checking out and then checking in the updated content. The check in file process is same as file upload code above.
Checking out content
ServiceResponse myServiceResponse = null;
  
try {
  DataBinder myResponseDataBinder = null;

  DataBinder myRequestDataBinder = myIdcClient.createBinder();
  myRequestDataBinder.putLocal("IdcService", "CHECKOUT");
  myRequestDataBinder.putLocal("dID", myId);
  myRequestDataBinder.putLocal("dDocName", myContentId);
  myServiceResponse = myIdcClient.sendRequest(myIdcContext, myRequestDataBinder);
  myResponseDataBinder = myServiceResponse.getResponseAsBinder();
  DataObject myDataObject = myResponseDataBinder.getLocalData();
  
  System.out.println("File checked out successfully");
} catch (IdcClientException idcce) {
  System.out.println("IDC Client Exception occurred. Unable to checkout file. Message: " + idcce.getMessage() + ", Stack trace: ");
  idcce.printStackTrace();
} catch (Exception e) {
  System.out.println("Exception occurred. Unable to check out file. Message: " + e.getMessage() + ", Stack trace: ");
  e.printStackTrace();
} finally {
  if (myServiceResponse != null) {
    myServiceResponse.close();
  }
}

Undo checkout
In case uploading of a file fails, the file should not remain checked out. Hence, checking out operation is required to be undone.
ServiceResponse myServiceResponse = null;
  try {
    DataBinder myRequestDataBinder = myIdcClient.createBinder();
    myRequestDataBinder.putLocal("IdcService", "UNDO_CHECKOUT");
// Document ID
    myRequestDataBinder.putLocal("dID", myId);
// Document Name
    myRequestDataBinder.putLocal("dDocName", myContentId);
    myServiceResponse = myIdcClient.sendRequest(myIdcContext, myRequestDataBinder);
    DataBinder myResponseDataBinder = myServiceResponse.getResponseAsBinder();
    System.out.println("Undone check out file successfully");
  } catch (IdcClientException idcce) {
    System.out.println("IDC Client Exception occurred. Unable to undo check out file. Message: " + idcce.getMessage() + ", Stack trace: ");
    e.printStackTrace();
  } catch (Exception e) {
    System.out.println("Exception occurred. Unable to undo check out file. Message: " + e.getMessage() + ", Stack trace: ");
    e.printStackTrace();
  } finally {
    if (myServiceResponse != null) {
      myServiceResponse.close();
    }
  }
Delete content
It may be required to delete the uploaded file.
ServiceResponse myServiceResponse = null;
  
try {
  DataBinder myRequestDataBinder = myIdcClient.createBinder();
  myRequestDataBinder.putLocal("IdcService", "DELETE_DOC");
// Document ID
  myRequestDataBinder.putLocal("dID", myId);
// Document Name
  myRequestDataBinder.putLocal("dDocName", myContentId);
  myServiceResponse = myIdcClient.sendRequest(myIdcContext, myRequestDataBinder);
  DataBinder myResponseDataBinder = myServiceResponse.getResponseAsBinder();
  System.out.println("File deleted successfully");
} catch (IdcClientException idcce) {
  System.out.println("IDC Client Exception occurred. Unable to delete file. Message: " + idcce.getMessage() + ", Stack trace: ");
  idce.printStackTrace();
} catch (Exception e) {
  System.out.println("Exception occurred. Unable to delete file. Message: " + e.getMessage() + ", Stack trace: ");
  e.printStackTrace();
} finally {
  if (myServiceResponse != null) {
    myServiceResponse.close();
  }
}


 Add new content – additional comments
There may be 2 types of requirement while adding a new file to the UCM server.
1) Upload a local file and save it on server’s disk
       ServiceResponse myServiceResponse = null;
        try {
            if (myFile != null) {
                if (myFile.exists()) {
                    DataBinder myRequestDataBinder = myIdcClient.createBinder();
                    myRequestDataBinder.putLocal("IdcService", "CHECKIN_UNIVERSAL");
                    myRequestDataBinder.putLocal("dDocType", "Application");
                    // File title may be accepted in form of string from UI (e.g. Input Text) and passed in function call
                    myRequestDataBinder.putLocal("dDocTitle",  myContentTitle);
                    myRequestDataBinder.putLocal("dDocAuthor", myUsernameInSession);
                    myRequestDataBinder.putLocal("dSecurityGroup", "dSecurityGroup");
                    myRequestDataBinder.putLocal("dDocAccount", "");
                    myRequestDataBinder.putLocal("dFormat", "text/html");
                    myRequestDataBinder.putLocal("xCollectionID", Long.toString(getFolderIdFromPath("/Contribution Folders/")));  
                    // File may be passed in function call
                    myRequestDataBinder.addFile("primaryFile", new TransferFile(myFile));
                    myServiceResponse = myIdcClient.sendRequest(myIdcContext, myRequestDataBinder);
                    //// 1
                    //  InputStream myInputStream = myServiceResponse.getResponseStream();
                    // BufferedReader myBufferedReader = new BufferedReader(new InputStreamReader(myInputStream));
                    // StringBuffer myStringBuffer = new StringBuffer();
                    // String myLine = "";
                    // while ((myLine = myBufferedReader.readLine()) != null) {
                    // myStringBuffer.append(myLine);
                    // myStringBuffer.append("\n");
                    // }
                    // System.out.println("Uploaded file details: \n" + myStringBuffer.toString());
                    //
                    //// Or
                    //// 2
                    String myResponseString = myServiceResponse.getResponseAsString();
                    System.out.println("Uploaded news details: \n" + myResponseString);
        
                    //// Or
                    //// 3
                    DataBinder myResponseDataBinder = myServiceResponse.getResponseAsBinder();
        
                    System.out.println("File uploaded successfully");
                }
            }
        } catch (IdcClientException idcce) {
            System.out.println("IDC Client Exception occurred. Unable to upload file. Message: " + idcce.getMessage() + ", Stack trace: " + idcce.getStackTrace());
        } catch (FileNotFoundException fnfe) {
            System.out.println("File Not Found Exception occurred. Unable to upload file. Message: " + fnfe.getMessage() + ", Stack trace: " + fnfe.getStackTrace());
        } catch (IOException ioe) {
            System.out.println("IO Exception occurred. Unable to upload file. Message: " + ioe.getMessage() + ", Stack trace: " + ioe.getStackTrace());
        } catch (Exception e) {
            System.out.println("Exception occurred. Unable to upload file. Message: " + e.getMessage() + ", Stack trace: " + e.getStackTrace());
        } finally {
            if (myServiceResponse != null) {
                myServiceResponse.close();
            }
        }

2) Accept text in form of string from UI (e.g. Rich Text Editor) and convert it to a file in server’s memory without saving it on the server’s disk
DataBinder myResponseDataBinder = null;
ServiceResponse myServiceResponse = null;

InputStream myInputStream = null;
BufferedReader myBufferedReader = null;

try {   
    // File contents may be accepted in form of string from UI (e.g. Rich Text Editor) and passed in function call
    if (myFileContents != null) {
        if (myFileContents.length() > 0) {
            myInputStream = new ByteArrayInputStream(myFileContents.getBytes());
            myBufferedReader = new BufferedReader(new InputStreamReader(myInputStream));

            System.out.println("Title: " + myContentTitle + ", Contents of file saved: " + myFileContents);

            if (myInputStream != null) {
                System.out.println("In uploadFile - Title: " + myContentTitle);
            
                DataBinder myRequestDataBinder = myIdcClient.createBinder();
                myRequestDataBinder.putLocal("IdcService", "CHECKIN_UNIVERSAL");
                myRequestDataBinder.putLocal("dDocType", "Application");
                // File title may be accepted in form of string from UI (e.g. Input Text) and passed in function call
                myRequestDataBinder.putLocal("dDocTitle", myContentTitle);
                myRequestDataBinder.putLocal("dDocAuthor", myUsernameInSession);
                myRequestDataBinder.putLocal("dSecurityGroup", "Public");
                myRequestDataBinder.putLocal("dDocAccount", "");
                myRequestDataBinder.putLocal("dFormat", "text/html");
                myRequestDataBinder.addFile("primaryFile", new TransferFile(myInputStream, "UploadFile.html", myFileContents.length(), "text/html"));
                myServiceResponse = myIdcClient.sendRequest(myIdcContext, myRequestDataBinder);
               
                myInputStream = myServiceResponse.getResponseStream();
                
                String myResponseString =  myServiceResponse.getResponseAsString();
                System.out.println("Uploaded file details: \n" + myResponseString);
            
                myResponseDataBinder = myServiceResponse.getResponseAsBinder();
                
                System.out.println("File uploaded successfully");
            }
        }
    }
} catch (IdcClientException idcce) {
    System.out.println("IDC Client Exception occurred. Unable to upload file. Message: " + idcce.getMessage() + ", Stack trace: ");
    idcce.printStackTrace();
}catch (IOException ioe) {
    System.out.println("IO Exception occurred. Unable to upload file. Message: " +  ioe.getMessage() + ", Stack trace: ");
    ioe.printStackTrace();
} catch (Exception e) {
    System.out.println("Exception occurred. Unable to upload file. Message: " + e.getMessage() + ", Stack trace: ");
    e.printStackTrace();
} finally {
    myFileContents = null;
    myContentTitle = null;

    if (myInputStream != null) {
        try {
            myInputStream.close();
        } catch (IOException ioe) {
            System.out.println("IO Exception occurred. Unable to upload file. Message: " + ioe.getMessage() + ", Stack trace: ");
            ioe.printStackTrace();
        }

        if (myBufferedReader != null) {
            try {
                myBufferedReader.close();
            } catch (IOException ioe) {
                System.out.println("IO Exception occurred. Unable to upload file. Message: " +  ioe.getMessage() + ", Stack trace: "));
                ioe.printStackTrace();
            }
        }
    }
    
    if (myServiceResponse != null) {
        myServiceResponse.close();
    }            
}

Monday, March 19, 2012

Using UCM Global Profile Rules with RIDC

The question came up on another forum:
  • Can I use profiles to validate my data input when I'm using RIDC?
Profiles can indeed be used in such a case, for example, to halt a RIDC checkin if a given metadata field is empty. 

There are a couple of prerequisite steps to follow:
  • In the Content Server general configuration, enable the following setting:
    IsDpSubmitErrorFatal=true
  • In the RIDC code, do not use CHECKIN_UNIVERSAL as the service call, but use CHECKIN_NEW instead.  (CHECKIN_UNIVERSAL doesn't load the profile engine.) 

Now build the rule solution.  The following bare-bones example makes the "Comments" field "required" to be a non-blank value for a new checkin.
  • In Configuration Manager. create a new global profile rule.  Set the activation conditions to "on submit" and "checkin new".
  • Add the "Comments" field to the rule.  In the "Use Derived Value" section, add the following code in the custom section:

    <$if strEquals(strTrimWs(#active.xComments),"")$>
     <$abortToErrorPage("Cannot submit item without any comments provided.")$>
    <$endif$>
  • Save the rule and exit Configuration Manager.
  • To test the solution, execute your RIDC code without providing any comments.  If you didn't provide any comments, the error message thrown is "Cannot submit item without any comments provided."

Thursday, March 15, 2012

UCM 11g and Quirks with Security Providers

This is a forum post that I put out on the Yahoo! user forum for Content Server.  I had a couple of folks to tell me it was worth blogging, so here it is.

The original question came from a user passing a SOAP request with seemingly the correct credentials, but the request failing to return any content other than content accessible to anonymous users.  The poster noted that if they reordered their JPS user providers within WLS, the issue cleared up for some users, but now appeared for other users.  Depending on the order of appearance of the providers in WLS, the users were being dictated as to who could see more than publicly available content.

So the response:

This issue is a known issue with the WebLogic JPS user provider code. The first
listed provider will have all user data returned (like roles and groups, along
with extended attributes like email address). Any subsequent provider will only
validate the user login, and not return any extended data. Swapping the order
of the providers results in the new top listed provider correctly returning all
expected attributes (as you have noted).

OK, so what's the solution? There are several, all with certain pain involved.

1 - Invest in Oracle Virtual Directory (OVD). OVD can gather all of the roles,
groups, and extended attributes from disparate LDAP repositories like OID,
Active Directory, OpenLDAP, etc, and present the data to WLS in a single
provider. The pain here is additional cost, but this is the supposed "ultimate"
fix.

2 - If you are still reading this, the cost is a big pain point. If you are
running PS3 or later (and I would stress "later" here, because this fix in PS3
was not completely tested), you can emulate a similar OVD experience with a
relatively simple configuration change in Enterprise Manager
("virtualize=true"). Look here for the
details. (http://andrejusb.blogspot.com/2011/06/webcenter-11g-ps3ps4-aggregating.\
html
). The pain here comes if your domain contains a WebCenter
self-registration process. I've seen the self-register function fail with this
setting in place. Your mileage may vary.

3 - Still reading? The virtualize setting must be problematic.
-- Put the OID provider first, and create a new group called "Administrators"
in the OID role container that you defined in WLS.
-- Add any users that need admin access to this OID group. With the OID
provider listed first, it should "just work".

4 - Already have "Administrators" group created, but can't add the users due to
company policy? That's why you're still reading...
There is one more "trick" here. The pain here -- "Is this approach supported?"
and "I don't know if other WebCenter applications (BAM, SOA, etc) will still
complain with the mis-ordered provider." If this is a UCM only domain, you may
be ok.

-- Log into UCM, and go into User Manager.
-- Change the weblogic user from "external" to "local", and give the weblogic
user the appropriate roles and accounts in the applet.
-- List OID first in the provider list. Log in as "weblogic" anyway.
Surprisingly, since you've already "authenticated" in WLS, the "authorization"
for weblogic will now come from UCM.

5 - Least favorite option - disable the JPS user provider in UCM, and create a
10g style LDAP provider to OID. I'm not sure if that is possible with OID, but
is achievable with AD. Again, your mileage may vary.