Documentum D6 Aspects

December 12, 2007

Aspects are new in D6. You can find an excellent post on this blog regarding the features and usage of aspects. http://doquent.wordpress.com/2007/09/26/new-in-d6-platform-aspects/ I will not repeat the information.

What I will do is talk about how we are using Aspects in our current implementation.

Scenario:
We are migrating about 50,000 documents from IBM Domino.doc to Documentum. We decided to build a custom application to do this. We have to make sure that all the meta-data from Domino.Doc is moved to Documentum. In addition, we want to keep track of the migration audit trail such as – Who migrated this document, when, what was the document’s audit history in Domino.Doc.

Approach:
We have several custom doctypes. But the parent of all these doctypes is a document called sp_document which has all the standard meta-data derived from Dublin-core and SGMS. Our initial approach in V5.3 was to create an attribute called sp_migration(2000 chars) and store the migration audit trail here. But this was problem because this was going to impact both migrated documents and the future documents. Future documents will have an empty sp_migration attribute which is not desirable.

Creating an aspect to hold the migration audit trail was a much better solution. We created the sp_document without the sp_migration attribute. We created an aspect “my_migration_aspect” with an attribute “migration_comments”. In our custom migration code, we attached the aspect to the newly migrated documents and set the migration history.

What do you need?

As of today, the documentation provided by Documentum for aspects is meager. Javadocs are missing for com.documentum.fc.client.aspects package. The tool you will definitely need to create aspects – “AspectCreator” tool. I got this tool from EMC during our Documentum Training session – “What is new in D6?” Unless D6 SP1 comes up with tools for aspects, you will have to get this tool from EMC.

I have tried ALTER of aspects from DQL. I got some parser exceptions. So you will have to depend on the “AspectCreator” tool.

Steps involved:

- You will have to create an Interface and Implementation class for the aspect you are creating. You can find the example code in DFC 6 Development Guide. Compile these and create separate .jar files. This step involves some simple programming.

 - Update the .xml file provided with “AspectCreator” tool to specify the details of the aspect you are creating and the doctype you are allowing the aspect to be applied. I could not find any documentation for this.
VERY IMPORTANT: From trial and error, I learnt that if you are creating a STRING attribute, you have to specify the type along with the string length as STRING(2000) . If you don’t specify the length you will get a StringIndexOutOfBounds exception.

- Run the command line tool create.cmd provided with “AspectCreator” to create the aspect in the docbase. Make sure you update this .cmd file with the correct paths and parameters.

Some Gotchas:

I have faced some strange errors such as ClassCastException when running Aspect code from IDE (Eclipse). The same code ran fine when run from command line using ANT.

I will try to upload some sample code in my next post.

Good luck.
Ram


Some issues developing/migrating DFC programs to D6

September 14, 2007

This is a new post after a long time. I have started working on D6. Here is the first set of problems I faced and some solutions.

If you see this exception, it is most probably because you have some classes compiled with JDK 1.4.x and others with JDK 1.5.x
[java] java.lang.UnsupportedClassVersionError: com/documentum/fc/common/DfE xception (Unsupported major.minor version 49.0)

Solution:
Change to JDK 1.5 for compilation, execution, etc.
If you are using an IDE like eclipse make sure you change there too. Infact if you have access to all the source code, I recommend you delete all the .class files and do a complete rebuild. This will save you a lot of head-aches.
I am not sure if this has any impact on exisiting ANT scripts. If anyone tried this out and has a solution, please share

If you see the following exceptions, it caused because new attributes have been added to the dm_type in D6 which are not available in v5.3.

Solution:

This happens if you are using DFC 6 to connect to 5.3 Repository

From my tests, I could confirm that:

DFC 6 connecting to 5.3 Repository throws exceptions/errors. DFC 6 connecting to D6 Repository works fine (of course)

DFC 5.3 connecting to D6 Repository works fine
Exception #1:

DfException:: THREAD: main; MSG: [DM_OBJECT_E_LOAD_COUNT_ERROR]error: “error loading object — wrong object count for object of type dm_type; found 15 attributes, but type says there should be 21″

[DM_OBJECT_E_LOAD_COUNT_ERROR]error: “error loading object — wrong object count for object of type dm_type; found 15 attributes, but type says there should be 21″

Exception #2: (During import operation)

java.lang.ArrayIndexOutOfBoundsException: 19

at com.documentum.fc.client.DfAttrTable.putAttr(DfAttrTable.java:84)

at com.documentum.fc.client.DfTypedObjHelperSessionBased.getAttr(DfTypedObjHelperSessionBased.java:51)

at com.documentum.fc.client.DfTypedObjHelperSessionBased.getAttr(DfTypedObjHelperSessionBased.java:73)

at com.documentum.fc.client.DfTypedObject.getAttr(DfTypedObject.java:671)

at com.documentum.fc.client.DfTypedObject.hasAttr(DfTypedObject.java:1056)

at com.documentum.fc.client.DfFormat___PROXY.hasAttr(DfFormat___PROXY.java)

at com.documentum.operations.DfXMLUtils.checkFormat(DfXMLUtils.java:182)

at com.documentum.operations.DfXMLUtils.isXML(DfXMLUtils.java:60)

at com.documentum.operations.DfApplyXMLForImport.applyXMLApplication(DfApplyXMLForImport.java:104)

at com.documentum.operations.DfApplyXMLForImport.execute(DfApplyXMLForImport.java:55)

at com.documentum.operations.DfOperationStep.execute(DfOperationStep.java:135)

at com.documentum.operations.DfOperation.execute(DfOperation.java:432)

Good luck.


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!! 


Thumbnail Server and McAfee Anti-virus Port Clash

February 27, 2007

I recently installed Documentum Thumbnail Server on a Windows box and I had strange problem.   The thumbnail server showed status as “Started” in the Windows Services console.  But the DAM refused to show any thumbnails.  

 To test if the Thumbnail server was running correctly,  I used IE to hit this URL: http://localhost:8081/thumbsrv/getThumbnail

If the thumbnail server was running allright, IE should display a default document icon.  Instead I saw the logs of my McAfee Anti-virus.  This meant that McAfee was using the port:8081 which is the default port used by Thumbnail server.

Fix: 

 Since Thumbnail server uses Tomcat internally,  I had to change the port to 8082. 

a) Using Notepad, open the server.xml in the  D:\Documentum\product\5.3\thumbsrv\container\conf
b) Search for “<!– Normal HTTP –>” and in this section for connectors,  change the port number <parameter name=”port” value=”<newport>”/>  I used 8082 successfully.
c) Restart the thumbnail server
d) Test using http://localhost:<newport>/thumbsrv/getThumbnail

Update the configuration of the thumbnail file-store to change the base_url attribute. 
a) Open Documentum Administrator (DA), 
b) Look for  the file-store – “thumbnail_store_01″
c) View properties and update the base url.
d) Restart the docbase
e) Open DAM and test if thumbnails are being displayed correctly


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());
}


Psuedo-Code for the User-rename Job

February 16, 2007

Start Here

 

Check if the NewUserName is same as the OldUserName, if so, don’t make any changes

Unlock (or only report) all sysobjects which are locked by the Old User. QueryStr = “query,c,select r_object_id from dm_sysobject ” & _

              “(all) where r_lock_owner = ‘” & DqlOldUserName & “‘”

Update all the sysObjects that have a reference to the OldUserName in the following attributes – “owner_name”, “acl_domain”, “r_creator_name”

In some cases, the Super-user may not have WRITE permissions to update the object.  In such cases, print an Error to the log

If the sysObject has been previously flagged as immutable, save the status of the object before making changes and then reset the object to its original immutable state

Save the last modifier (user name) to a variable and reset the last_modifier attribute to this variable after making the necessary changes to the object.  If the last_modifier was the OldUserName,  then update this with the NewUserName

Before calling any API methods make sure that the Single-quotes in the user-names are escaped by calling a function –  DmEscapeSingleQuotes(inputStr As String, byref msg_result As String)

Ensure that either NewUser or OldUser exist in the repository, else it is a FATAL error

If the NewUser Object does not exist just rename the existing user OldUser.
If the NewUser already exists, No need to update the dm_user objects, Just set a flag to delete the OldUser

Note: The OldUser object is really destroyed.  It is not set to be deactivated/disabled. 

Q) What is the implication on audit log entries because of this?

If the user object is successfully updated, we need to write to federation log.

Update Acl Objects

Get the list of ACL objects which have reference to the OldUser

DmQuery = dmAPIGet(“query,c,select r_object_id from dm_acl where ” & _

                      “owner_name = ‘” & DqlOldUserName & “‘ or ” & _

                      “any r_accessor_name = ‘” & DqlOldUserName & “‘”)

For Each ACL object,  Check if owner_name refers to OldUser, if so update it to NewUser

 Check if any of the r_accessor_name[i] values refer to OldUser, if so update them to NewUser

Update Alias Set Objects

Get the list of alias set objects that refer to OldUser:

DmQuery = dmAPIGet(“query,c,select r_object_id from dm_alias_set where ” & _

                      “owner_name = ‘” & DqlOldUserName & “‘ or ” & _

                      “any alias_value = ‘” & DqlOldUserName & “‘”)

For Each Alias Set object, 
 Check if owner_name refers to OldUser, if so update it to NewUser

 Check if alias_category[i] = 1.  This denotes a reference to a user

 Check if any of the alias_value [i] values refer to OldUser, if so update them to NewUser

If user’s default acl (acl_domain) is still pointing to the OldUser, then update it.

Update (or only report) sysobjects which are not locked

This call updates all the sysobjects that are not locked by ANY User, but whose attributes refer to the oldusername

QueryStr$ = “query,c,select r_object_id from ” & _

              “dm_sysobject (all) where ” & _

              “(owner_name = ‘” & DqlOldUserName & “‘ or ” & _

              “r_creator_name = ‘” & DqlOldUserName & “‘ or ” & _

              “r_modifier = ‘” & DqlOldUserName & “‘ or ” & _

              “acl_domain = ‘” & DqlOldUserName & “‘) and ” & _

              “r_lock_owner = ‘ ‘”

Update (or only report) sysobjects which are locked by ANY user (not just Old User)

  QueryStr$ = “query,c,select r_object_id from ” & _

              “dm_sysobject (all) where ” & _

              “(owner_name = ‘” & DqlOldUserName & “‘ or ” & _

              “r_creator_name = ‘” & DqlOldUserName & “‘ or ” & _

              “r_modifier = ‘” & DqlOldUserName & “‘ or ” & _

              “acl_domain = ‘” & DqlOldUserName & “‘) and ” & _

              “r_lock_owner <> ‘ ‘”

Update Workflow and related objects.

Update routers referencing the Old User. 

Note that router functionality is superseded in Documentum 4.0 and above by the workflow functionality. Documentum clients at version 4.0 and above won’t allow you to start new routers.  Depending on your version of Documentum, this may not be applicable

  DmQuery = dmAPIGet(“query,c,select r_object_id from dm_router (all) ” & _

              “where supervisor_name = ‘” & DqlOldUserName & “‘ or” & _

                     ” any task_owner = ‘” & DqlOldUserName & “‘ or” & _

                     ” any r_task_user = ‘” & DqlOldUserName & “‘”)

For Each Router object,

  Check if “supervisor_name” refers to OldUser if so, update it

  For Each of the task_owner values, if the attribute “task_owner[i]” refers to OldUser, update it

  For Each of the r_task_user values, if the attribute “r_task_user [i]” refers to OldUser, update it

Update activites referencing the Old User.

Check if there are any activity objects that refer to OldUserName

  DmQuery = dmAPIGet(“query,c,select r_object_id ” & _

        “from dm_activity (all) where any performer_name = ‘” & DqlOldUserName & “‘”)

If so , update all dm_activity objects using DQL:

    qry = “update dm_activity_r set performer_name = ‘” & DqlNewUserName & “‘ where performer_name = ‘” & DqlOldUserName &   

    //Directly call the DQL to run the query

    ret = dmAPIExec(“execsql,c,” & qry)

Update Workflow objects referencing OldUser

    change dm_workflow:

    qry = “update dm_workflow_s set r_creator_name = ‘” & DqlNewUserName & “‘ where r_creator_name = ‘” & DqlOldUserName &   

    qry = “update dm_workflow_s set supervisor_name = ‘” & DqlNewUserName & “‘ where supervisor_name = ‘” & DqlOldUserName & 

    qry = “update dm_workflow_r set r_performers = ‘” & DqlNewUserName & “‘ where r_performers = ‘” & DqlOldUserName & “‘”

    qry = “update dm_workflow_r set r_last_performer = ‘” & DqlNewUserName & “‘ where r_last_performer = ‘” & DqlOldUserName 

Update Workitem objects referencing OldUser:

Note: Work items are generated by Content Server from an activity object. Users cannot create work items.r_performer_name string(32) S Contains the name of an activity performer, or the owner name of the method if this is an automatic work item.r_ext_performer string(32) R Lists new performers to repeat the same activity. If a group name, only one work item is generated and any group member can  acquire the work item. 

   Change dmi_workitem:

  qry = “update dmi_workitem_s set r_performer_name = ‘” & DqlNewUserName & “‘ where r_performer_name = ‘” & DqlOldUserName

  qry = “update dmi_workitem_r set r_ext_performer = ‘” & DqlNewUserName & “‘ where r_ext_performer = ‘” & DqlOldUserName &

Update  dmi_queue_item objects referencing the Old User.

A queue item object stores information about an object placed on a user’s queue (Inbox).

   ‘ change dmi_queueitem:

    qry = “update dmi_queue_item_s set name = ‘” & DqlNewUserName & “‘ where name = ‘” & DqlOldUserName & “‘”

    qry = “update dmi_queue_item_s set sent_by = ‘” & DqlNewUserName & “‘ where sent_by = ‘” & DqlOldUserName & “‘”

    qry = “update dmi_queue_item_s set supervisor_name = ‘” & DqlNewUserName & “‘ where supervisor_name = ‘” & DqlOldUserName

    qry = “update dmi_queue_item_s set dequeued_by = ‘” & DqlNewUserName & “‘ where dequeued_by = ‘” & DqlOldUserName & “‘”

    qry = “update dmi_queue_item_s set sign_off_user = ‘” & DqlNewUserName & “‘ where sign_off_user = ‘” & DqlOldUserName &

 

Update GROUPS referencing the Old User.

DmQuery = dmAPIGet(“query,c,select r_object_id from dm_group where ” & _

                     “owner_name = ‘” & DqlOldUserName & “‘ or ” & _

                     “any users_names = ‘” & DqlOldUserName & “‘”)

For each group above, update owner_name attribute

There is an attribute called – group_admin CHAR(32).  This needs to updated if this refers to OldUser

The group has an array or user names.  If any of the user names refer to OldUser, update them (“users_names[i]“)

Deleting the OldUser object

Delete the old user object AFTER the references in group objects are  updated. Otherwise, the old user entries will be removed when the

 user object is destroyed.

    Call DmDeleteOldUserObj() {

      //The oldUserID is really – destroyed

      ret = dmAPIExec(“destroy,c,” & OldUserId)

    }

Update dmi_registry objects (reregister) 

A registry object contains information about a registered event.

Users can register to receive notification of an event’s occurrence. Users can also initiate auditing of events. Both actions generate registry objects.

The attribute user_name string(32) of dmi_registry contains the Name of the user that registered for the event or initiated auditing. This name should be updated

 ret = dmAPIExec(“execsql,c,update dmi_registry_s set user_name = ‘” & DqlNewUserName & “‘ ” & _

                ” where user_name = ‘” & DqlOldUserName & “‘”)

Other Objects

List all the dm_registered, dm_type and dmi_type_info objects referencing the old user. The script does not modify objects, administrators have to do it manually.

dm_registered

This represents a registered object contains information about an underlying RDBMS table that has been registered with Content Server.

DmQuery = dmAPIGet(“query,c,select r_object_id, object_name, ” & _

                     “table_name, table_owner from dm_registered ” & _

                     “where table_owner = ‘” & DqlOldUserName & “‘”)

dm_type

The following dm_type objects have owner_name attribute referencing user – OldUserName.  The script will not update the objects. You have to modify them manually.

A type object stores structural information about an object type in the repository.

There is minimal impact to leave this name as it is.  However if the user wants to update/modify this the type object, the Administrator will have to update this object

  DmQuery = dmAPIGet(“query,c,select r_object_id, name, super_name ” & _

         “from dm_type where “”" & “owner” & “”" = ‘” & DqlOldUserName & “‘”)

dmi_type_info

The following dmi_type_info objects have acl_domain attribute. The script will not update the objects. You have to modify them manually.

dmi_type_info  - A type info object stores non-structural information about an object type.  Storing non-structural  information separately from the type’s structure definition (the dm_type object) enhances the performance when a type is altered. This contains information about the ACLs, index settings etc.  The attribute – acl_domain may contain the user name.

DmQuery = dmAPIGet(“query,c,select r_object_id, r_type_name, ” & _

         “r_type_id, acl_domain, acl_name from dmi_type_info where ” & _

         “acl_domain = ‘” & DqlOldUserName & “‘”)

}

Note: I am not sure if this job is missing out any other objects. You may want to run a dql to find where else the user names are used. 


Summary of objects updated by User-Rename job in Documentum

January 11, 2007

Documentum provides a job written in Docbasic to allow administrators to rename users and to reassign the jobs.  Documentum uses the user-name in several places – determining the owner of an document/object.

I am yet to understand why Documentum decided to use this approach instead of relating documents/objects to the users using object-ids in some form of foreign-key relationship.  It could be performance reasons.  But due to this design, there will be some issues when it comes to user-names in Documentum.  I will go into details in another post.

I will add another post with a more detailed summary of the steps involved in the process of renaming a user.  For now I am adding a summary table of the objects impacted.

(The table is getting truncated when I copy & paste from MS Word.  While I try to fix this problem, try using the original Word document.  Sorry about this.)

Download: Object-types-impacted-by-user-rename-job.doc


Best Practices in Naming Documentum Users and Groups

January 10, 2007

Constraints:

  1. The maximum allowed length for User and group names is 32 characters.
  2. Do not begin a name with the prefix dm. Documentum reserves this prefix for its own use.
  3. The name must consist of ASCII characters.
  4. User and group names are case sensitive.
  5. It is good practice not to choose names that conflict with DQL reserved words.

Note:

  1. Avoid using SQL and DQL reserved words for names. If a name must match a DQL reserved word, enclose the name in double quotes whenever you use it in a DQL query.
  2. The name must consist of characters compatible with the server os code page of the Content Server
  3. The name of user or group must be unique among the user and group names in the repository. (This means a user cannot have the same name as a group)
  4. It is better to restrict names to letters, digits, underscores. If special characters are used, third-party applications must take special care to escape them while working with Documentum (epecially while using DQL queries)
  5. To escape special characters such as spaces, apostrophes, – enclose the string in Single quotes
  6. To escape DQL reserved keywords, enclose the string in double quotes when referenced
  7. For groups, it was noticed that System changes all characters in the name to lower-case
  8. It was observed in practice that using ‘/’ & ‘\’ in the name causes problems during creation of accounts and user home-cabinets
  9. Other special characters tested were (, ), and the system accepts these names. However, they must be escaped while using DQL queries.

Recommendation:For ensuring that other applications can work with a unified group-set, I recommend that the names should be kept as simple as possible and restricting to letters, digits & underscores. Spaces, apostrophes, brackets, slashes, single-quotes, double-quotes, $, #, @, &, *, etc should be avoided as other applications