Updated Documentum Tools and Applications

I had a little free time this week and made some updates to tools and applications I use frequently.  These updates represent ideas sent to me by users or were necessary to address additional situations I encountered in the field.  I hope they are helpful for you too.

QuikDIE v1.5 – This application exports content and metadata from a Documentum repository based upon a query.  Content is exported in its native format, and metadata is exported as XML.  Updates include:

  • detects and eliminates content “parked” on BOCS server
  • “dmi_” object types are ignored
  • implemented DCTMBasics JAR

For additional information regarding QuikDIE (including important info in the comments section), search for “QuikDIE“.

DeepExport v1.1 – This application exports CONTENT ONLY from a folder in a Documentum repository.  Content is exported in its native format and the folder hierarchy in the repository is replicated on the export drive.  Updates include:

  • detects and eliminates objects parked on BOCS server
  • updated to use DCTMBasics v1.3
  • refactored to remove Utils class
  • added password encryption/decryption

For additional information regarding DeepExport, search for “DeepExport“.

DCTMBasics v1.3 – This JAR file contains a set of helpful methods for interfacing with Documentum via the DFC.  It contains implementations of methods discussed in “The Basics” series.  Updates include:

  • getDFCVersion() – returns DFC version of your client
  • checkDFCVersion() – compare the current DFC version with a minimum version
  • createDocbasePath() – create a folder hierarchy in the repository
  • encryptPassword() – encrypt a password using the RegistryPasswordUtils class (used to encrypt dm_bof_registry user)
  • decryptPassword() – decrypt a password encrypted with the encrypt() method
  • runDQLQueryReturnSingleValue() – run a query that returns a single value, for example:  select count(*) from …
  • isSysObject() – tests it an IDfPersistentObject is a sys_object or not
  • createLogFile() – create a log file and return it to the caller as a PrintWriter
  • other misc updates, changes, corrections, and refactoring

For additional information regarding “The Basics” series, search for “The Basics“.

 

 

 

Advertisements

The Basics – Specialized Query Methods

Following the Running a DQL Query post, this post looks at some simple ways to specialize DQL queries based upon their type (e.g., select, set, create, delete, insert, drop, cached, and update) and expected return values, and the benefits such specialization provides.  The Basics library of Documentum methods containing the specialized query methods as well as all other methods discussed in this series, can be found here.

The Basics library contains 10 query methods.  The method signatures follow:

  1. public static IDfCollection runSelectQuery(String query, IDfSession session) throws DfException
  2. public static dmRecordSet runSelectQueryAsRecordSet(String query, IDfSession session) throws Exception
  3. public static int runUpdateObjectQuery(String query, IDfSession session) throws DfException
  4. public static IDfCollection runCachedQuery(String query, IDfSession session) throws DfException
  5. public static String runCreateObjectQuery(String query, IDfSession session) throws DfException
  6. public static int runDeleteObjectQuery(String query, IDfSession session) throws DfException
  7. public static IDfCollection runExecQuery(String query, IDfSession session) throws DfException
  8. public static int runInsertQuery(String query, IDfSession session) throws DfException
  9. public static int runDeleteQuery(String query, IDfSession session) throws DfException
  10. public static int runUpdateQuery(String query, IDfSession session) throws DfException

I will leave the exploration of the code to you, and instead discuss a few of the benefits of these specialized query methods.

  1. Using specialized query methods makes your primary code much simpler to create, read, and maintain.  You don’t have to clutter the code with IDfQuery and IDfCollection objects.
  2. These methods provide simpler handling of return values.  For example, if you are only interested in a single result value (e.g., number objects updated), you don’t have to clutter your code with loops, etc. to retrieve that value.
  3. Using specialized query methods, you don’t have to worry about using the correct or most efficient query constants.
  4. These methods provide rudimentary syntax checking, which could be improved/extended for your purposes.  This can help alleviate receiving cryptic error messages from the query engine.
  5. Because all of these methods eventually call a single “uber” query method, error checking/trapping is centralized.

Here are some quick examples:

				// SELECT
				query = "select r_object_id, object_name from dm_document where folder('/Temp')";
				System.out.println("running SELECT query: " + query);
				col = DCTMBasics.runSelectQuery(query,session);
				// do something with IDfCollection
				col.close();

				// CACHED
				query = "select r_object_id, object_name from dm_document where folder('/Temp',descend)";
				System.out.println("running CACHED query: " + query);
				long start = System.currentTimeMillis();
				col = DCTMBasics.runCachedQuery(query,session);
				long stop = System.currentTimeMillis();
				long dif = stop - start;
				// do something with IDfCollection
				col.close();
				System.out.println("duration=" + dif);

				System.out.println("[running second query]");
				start = System.currentTimeMillis();
				query = "select r_object_id, object_name from dm_document where folder('/Temp',descend)";
				col = DCTMBasics.runCachedQuery(query,session);
				stop = System.currentTimeMillis();
				dif = stop - start;
				// do something with IDfCollection
				col.close();
				System.out.println("duration=" + dif);

				// OBJ CREATE
				query = "create dm_document object set object_name = 'DCTMBasics Test Object' link '/Temp'";
				System.out.println("running OBJ CREATE query: " + query);
				String objId = DCTMBasics.runCreateObjectQuery(query,session);
				System.out.println("created " + objId);

				// EXEC
				query = "execute db_stats";
				System.out.println("running EXEC query: " + query);
				col = DCTMBasics.runExecQuery(query,session);
				// do something with IDfCollection
				col.close();

				// OBJ UPDATE
				System.out.println("running OBJ UPDATE query");
				query = "update dm_document object set object_name = 'DCTMBasics updated object name' where r_object_id = '" + objId + "'";
				int cnt = DCTMBasics.runUpdateObjectQuery(query,session);
				System.out.println("update " + cnt + " objs");

				// OBJ DELETE
				System.out.println("running OBJ DELETE query");
				query = "delete dm_document object where r_object_id = '" + objId + "'";
				cnt = DCTMBasics.runDeleteObjectQuery(query,session);
				System.out.println("deleted " + cnt + " objs");

The Basics – Running a Query

Next to logging on/off, executing DQL queries is one of the most frequent tasks developed in a Documentum application.  There are three primary classes used to query the repository and process the results. These classes are:

  • IDfQuery – The query class encapsulates all of the data and functionality necessary to run a DQL query against a repository.
  • IDfCollection – The collection class encapsulates the results of a query in a read-once, forward-only object.  A better alternative is to use the dmRecordSet class.
  • IDfTypedObject – A typed object is a non-persistent object used to model row data in the IDfCollection object.

There are potentially more classes involved depending upon circumstances, for example whether you choose to use a query builder class (IDfQueryBuilder) to construct your query, or the dmRecordSet to hold query results.

The basic structure of the query code looks like this:


public IDfCollection runQuery(String query, IDfSession session) {
  IDfCollection col = null;

  try {
    // create query object
    IDfQuery q = new DfQuery();

    // set query string
    q.setDQL(query);

    // execute query
    col = q.execute(session, DfQuery.DF_READ_QUERY);

  } catch (DfException e) {
    e.printStackTrace();
  }
  return col;
}

Note the use of the DfQuery.DF_READ_QUERY constant. The use of the proper query type constant can affect your query performance. See here for more on query type constants.

The basic structure of the code used to process the results returned in the IDfCollection object is:


// do query
IDfCollection col = runQuery("select r_object_id, object_name from dm_document where folder('/Templates')", session);

// process results
while (col.next()) {

 // get each row
 IDfTypedObject tObj = col.getTypedObject();

 // get value in each column and do something with results
 String id = tObj.getString("r_object_id");
 String name =  tObj.getString("object_name");
 System.out.println(id + "\t" + name);
}

// it is very important to close each collection after you process it
if ( (col != null) && (col.getState() != IDfCollection.DF_CLOSED_STATE) )
 col.close();

In this example, the IDfTypedObject represents a row in the IDfCollection and its getter methods are used to retrieve each column’s data.  Note that an IDfCollection object can only be read once, and only in the forward direction.  For a more robust and capable query result object, use the dmRecordSet class.

UPDATE:  I have created a DCTMBasics JAR file that contains most of the code discussed in this series.

The Basics – Logging On/Off

Establishing a session with the Content Server is one of the most basic and first operations a Documentum programmer must master.  There are three primary classes involved in establishing a session with the Content Server:

  • IDfSessionManager – The session manager manages identities, pooled sessions and transactions.  It is also required to release a session (i.e., log off ) when you are through with it.
  • IDfLoginInfo – The login info object encapsulates the information needed to validate and login a user to a repository. It stores the user’s credential information in addition to options that affect the server login process.
  • IDfSession – The session object encapsulates a session with a Documentum repository.

Here are my logon and logoff methods:


public static IDfSession logon(String docbase,
                               String username,
                               String password) throws DfException {
    IDfSession session = null;

    // validate arguments
    if ((docbase == null) || (docbase.trim().isEmpty()) )
      throw new DfException ("Docbase name is null or blank.");

    if ((username == null) || (username.trim().isEmpty()) )
      throw new DfException ("Username name is null or blank.");

    if ((password == null) || (password.trim().isEmpty()) )
      throw new DfException ("Password is null or blank.");

    // create login info
    IDfLoginInfo li = new DfLoginInfo();
    li.setUser(username);
    li.setPassword(password);
    li.setDomain("");

    // get session manager
    IDfSessionManager sessionMgr = DfClient.getLocalClient().newSessionManager();

    // login
    if (sessionMgr != null) {
      sessionMgr.setIdentity(docbase, li);
      session = sessionMgr.getSession(docbase);
     } else {
       throw new DfException("Could not create Session Manager.");
     }
     return session;
}

public static void logoff(IDfSession session) {
  // release session when done
  if (session != null) {
    session.getSessionManager().release(session);
  }
}

Here is an example of using the logon() and logoff() methods in a program:


public static void main(String[] args) {
  private IDfSession session = null;

  try {
    System.out.println("Logging on...");
    session = login("repo1", "dmadmin", "dmadmin");
    if (session != null) {
      System.out.println("Success: session ID = " + session.getSessionId());

      // do stuff here

      // release session when done
      System.out.println("Logging off...");
      logoff(session);

    } else {
      System.out.println("Logon failed: Session is null");
    }

  } catch (DfException dfe) {
    System.out.print("Logon failed:  ");
    System.out.println(dfe.getMessage());
  }

}

UPDATE:  I have created a DCTMBasics JAR file that contains most of the code discussed in this series.

“The Basics” Series

I have been kicking around an idea to run a recurring series of posts demonstrating basic Documentum DFC programming techniques.  The idea will be to demonstrate techniques to implement DFC operations programmers routinely implement, like logging on/off, running queries, creating jobs, checking out objects, etc.  in short, concise, and best practice code snippets.  These are operations that everyone has (re)written a hundred times and should be kept in a library JAR file instead of recreated every time they are needed.  Having said that, perhaps you have a library JAR of operations you would like to share?

If you are new to Documentum and looking for DFC best practices and code examples, this series will be for you.  To find all of the posts in this series, search the tag library for “The Basics”.  Look for the first post of this series soon, with others to follow at random intervals.

There have been several “The Basics” topics previously covered in this blog already, in addition to some that are not-so-basic.  The following topics may only be touched upon in this series since they have been covered in depth previously:

UPDATE:  I have created a DCTMBasics JAR file that contains most of the code discussed in this series.

%d bloggers like this: