UCF Troubles

September 1, 2008

The UCF feature in Documentum seems to be a necessary evil.   I have had more complaints from our users regarding UCF than any other Documentum component.

The most common complaint is – It is DAMN slow to launch!!

I agree with the users.   Launching UCF in our organization’s environment takes around 50-70 seconds!!!

Imagine that you read around 20 documents at different times in the day and every time you want to read a document, you have to wait for 50 seconds. Can you imagine the wait while your browser displays a blank page?   No wonder the users complain about this.

After running several rounds of tests, we identified the factors that cause these really slow response times.
1) UCF uses Java JRE 1.5.x.  JRE needs time to initialize itself and load into memory before UCF kicks into action.  On a laptop with 1GB RAM and a reasonably fast CPU, JRE takes around 20-30 seconds to launch.
2) Another key factor is the presence of Anti-virus software on the user’s laptop.  Since AV will scan all the JRE related .jar files, the JRE initialization will be further slowed slowed down.

From our tests,  (Only the first operation after browser restart takes this long. Next operations should finish between 10-15 seconds)
– Export operation using UCF with Anti-virus takes around 50-70 seconds
– When Anti-virus is turned off, the Export operation takes around 35-45 seconds.

Some suggestions:
– Tune your anti-virus settings to exclude JRE folders and UCF folders
– Exclude anti-virus from scanning into .zip and archive files
– TEST with various settings

Here are some of the other common problems with UCF:
– JRE gets updated automatically when a new update is available from sun.com.  Sometimes this breaks the UCF.  Solution could be to try deleting all the UCF related folders and files in UCF folder (usually found in C:\Documents and Settings\Documentum\ucf)

Customers who are planning to use Documentum should test the performance of UCF in their environment.
1) Setup a pilot environment on a test server.  Access Webtop from a typical end-user PC and check the response times.  Make sure you are running anti-virus and any other software that are standard to your environment.  This is one way to ensure that the system performance will be acceptable to the end-users.
2) If you are planning to conduct any peformance testing and load testing, ensure that you specify that some scenarios include testing with UCF enabled.

Advertisements

Documentum Certification – WDK Beta Exam Experiences (CMWP, CMA)

March 28, 2007

I had the opportunity to sit for the WDK Beta (CMWP) Test-the-Test exam this Monday.  It was a challenging experience.  

My Preparation:  I registered for the exam at the last minute on Friday so I had the weekend to prepare for the exam. 

I had attended the Documentum WDK Fundamentals and Advanced training offered by EMC.   I have hand-on experience with WDK customization.  Anyway, due to the time constraints, I decided to stick exclusively to the course materials provided  by EMC.   I prepared some notes and paid special attention to the syntax of XML files and WDK Tags and Classes.

My Experience:

I took around 3 hrs to complete the exam of 223 questions.  The exam was quite rigorous. It tested my technical knowledge and also my ability to concentrate 🙂  The questions were a curious mixture of – easy questions that touched only high-level concepts  & really difficult questions that dug deep.   I found myself staring at the difficult ones with no clue about the right answer.  There are a lot of code snippets to analyse.  I found that studying ONLY the WDK course material was NOT sufficient. 

Some Suggestions: 

The exam goes into depth on some topics that are not covered well in the WDK training.  The topics I found I was weak in were – Message handling, Error handling  and  Containers.  Pay attention to these topics if you are planning to prepare for the exam.  You may have to refer to the WDK Development guide for these topics.  

You should also have a good knowledge of how J2EE applications are deployed and configured in web.xml.  A basic understanding of Javascript is necessary to analyse the course snippets. 

Conclusion:

The exam is of very high-quality.  I think that someone who passes the exam can be called a “specialist” in WDK.  I would compare the difficulty of this exam to Sun Certification exams like SCJP or SCBCD, where, you cannot pass the exam just because you know Java or EJB.  You will be tested for the depth of your knowledge. 

For everyone planning to take the WDK exam when it is released, Best of Luck!  

To the people who came up with the questions for this exam….  A part of me hates you all :p   No… seriously…  Great Job!! 


Documentum Search Audit Trails

February 17, 2007

Q) How to collect audit trails of searches performed by users?   
Documentum does not capture any audit events for searches performed.  However, search statistics and reports can also be used to identify frequently used keywords and tune the search engine to provide accurate results. 

The statistics can also be used for creating management reports if needed.

Design Approach:
1. Create a new persistent object (“sp_search_log”) to store Search log information
2. Customize the search component’s behaviour class’s onRenderEnd() method to create a new “sp_search_log” object
3. Save the object before displaying the JSP

Alternate Approaches:
1. Use JDBC to capture the information in a database table.  Complicated approach involving opening database connections.
2. Create custom audit trails to create dm_audittrail objects.  I have not yet studied the implications of this. 

CREATING  A NEW TYPE to store Search Logs:
CREATE TYPE "sp_search_log"
( "r_search_id" ID,
"userid" CHAR(10),
"userdisplayname" CHAR(200),
"deptcode" CHAR(6),
"keyword" CHAR(100) REPEATING,
"location" CHAR(250) REPEATING,
"attrib_namevalue" CHAR(250) REPEATING,
"starttimeofsearch" DATE,
"endtimeofsearch" DATE,
"noofresults" INT,
"noofvieweddocs" INT
) WITH SUPERTYPE NULL PUBLISH

OUTPUT OF DQL > new_object_ID  030004d2800001b9 

ALTER TYPE “sp_search_log” DROP_FTINDEX ON “userid”
ALTER TYPE “sp_search_log” DROP_FTINDEX ON “userdisplayname”
ALTER TYPE “sp_search_log” DROP_FTINDEX ON “deptcode”
ALTER TYPE “sp_search_log” DROP_FTINDEX ON “location”
ALTER TYPE “sp_search_log” DROP_FTINDEX ON “attrib_namevalue”

Use this DQL to drop any fields if needed:
ALTER TYPE “sp_search_log” DROP “Field-Name” PUBLISH

Use this DQL to add new fields if needed later:
ALTER TYPE “sp_search_log” ADD “New-Field-Name” DATE PUBLISH
Note:
– “attrib_namevalue” CHAR(250) REPEATING will be used to store the params from advanced search in the form date=22/01/2006, etc.
– If the user uses a phrase search like “new york”, it can be stored in one keyword.  If new york is used without quotes, it will be stored as two keywords

SAMPLE JAVA SOURCE CODE FRAGMENTS
Note:  This code is meant to prove the concept.  This may not be the best approach for performance.
A better approach could be to store the “starttimeofsearch” in an instance variable then, create & save the

sp_search_log object only once after the search operation is completed.

public class SearchEx extends com.documentum.dam.search.SearchEx
implements IControlListener, IDfQueryListener, Observer,
IReturnListener, IDragDropDataProvider, IDragSource, IDropTarget
{
private boolean m_loggedToDB = false;
private boolean m_loggedNoOfResultsToDB = false;
private boolean m_isFirstCall = true;
public void onInit(ArgumentList args)
{
System.out.println("## Inside custom search");
String strQuery = args.get("query");
System.out.println("## strQuery: " + strQuery);
super.onInit(args);
}

public void onRenderEnd()
{
super.onRenderEnd();

if(m_loggedToDB == false && m_isFirstCall==true) {
createSearchLogObject();
m_isFirstCall = false;
}

if(m_loggedToDB == true && m_isFirstCall==false && m_loggedNoOfResultsToDB==false) {
updateSearchLogObject();
}
}

private void createSearchLogObject(){
String objectId = null;

IDfSession sess = this.getDfSession();

String userid = "Not found";
try {
userid = sess.getLoginUserName();
System.out.println("### userid: " + userid);

String queryDesc = getQueryDescription();
System.out.println("### queryDesc: " + queryDesc);

IDfPersistentObject searchLog =
(IDfPersistentObject)sess.newObject("sp_search_log");
searchLog.setString("userid", userid);
searchLog.setString("userdisplayname", userid);
//searchLog.setString("deptcode", "DEPT_CODE GOES HERE");

IDfTime timeNow = new DfTime();
searchLog.setTime("starttimeofsearch", timeNow);
searchLog.setInt("noofresults",-1);
setNewValuesForAttribute("keyword", queryDesc, " ", searchLog);

String searchLocations = getSearchSources();
setNewValuesForAttribute("location", searchLocations, ",", searchLog);

searchLog.save();

m_NewSearchLogObjectId = searchLog.getObjectId().getId();
System.out.println("************ Saved Search Log ************" + objectId);
m_loggedToDB = true;
} catch (DfException e) {
e.printStackTrace();
}

}

private void updateSearchLogObject(){
System.out.println("### Updating the record");

Datagrid datagrid = (Datagrid)getControl("doclistgrid",

com.documentum.web.form.control.databound.Datagrid.class);
//Get total number of results available from the underlying DataHandler
//Note that a value of -1 indicates that the DataHandler does not support results counting.
int noOfResults = datagrid.getDataProvider().getResultsCount();
System.out.println("Datagrid noOfResults: " + noOfResults );
if(noOfResults != -1) {
IDfSession sess = this.getDfSession();

IDfClientX clientx = new DfClientX();
try {
IDfPersistentObject searchLog = (IDfPersistentObject)sess.getObject(
clientx.getId(m_NewSearchLogObjectId));

IDfTime timeNow = new DfTime();
searchLog.setTime("endtimeofsearch", timeNow);
searchLog.setInt("noofresults",noOfResults);

searchLog.save();
m_loggedNoOfResultsToDB=true;
System.out.println("************ Updated Search Log ************");
} catch (DfException e) {
e.printStackTrace();
}
}

private void setNewValuesForAttribute(String attributeName,
String queryString, String delimiter, IDfPersistentObject obj) throws DfException {

StringTokenizer st = new StringTokenizer(queryString, delimiter);
for (int i = 0; st.hasMoreTokens(); i++) {
obj.appendString(attributeName, st.nextToken());
}