When working with containers, the usual trend is to make them as compact as possible by removing any file or executable that is not used by what is deployed in it. Typically, interactive commands are good candidates not to be installed. Sometimes, this trend is so extreme that even simple utilities such as ping or even the less pager are missing from the containers. This is generally fine once a container reaches the production stage but not so much while what’s deployed in it is still in development and has not reached its full maturity yet. Without such essentials utilities, one might as well be blind and deaf.
Recently, I wanted to check if a containerized D2 installation could reach its target repository but could not find any of the usual command-line tools that are included with the content server, such as iapi or dctmbroker. Bummer, I thought, but less politely. Admittedly, those tools belong to the content server binaries and not to DFCs clients such as D2 and DA, so that makes sense. Maybe, but that does not solve my problem.
In article A Small Footprint Docker Container with Documentum command-line Tools, I showed how to have a minimalist installation of the iapi, idql and dmdocbroker command-line tools that could be containerized but for my current need, I don’t need so much power. A bare repository ping, a dctmping if you will, would be enough. Clearly, as D2 is a DFCs application, a simple DFCs-based java utility would be just what the doctor ordered. There are probably hundreds variants of such a basic utility floating on the Web but here is my take on it anyway.
The code
The following listing generates the dctmping.java program.
$ cat - << dctmping.java // a healthcheck program to check whether all the repositories whose docbroker's hosts are defined in dfc.properties are reachable; // cec at dbi-services.com, June 201; // to compile: export CLASSPATH=/path/to/the/dfc.jar javac dctmping.java // to execute: java -classpath .:/path/to/dfc/config:$CLASSPATH dctmping [target_docbase user_name password] // if target_docbase is given, the programm will attempt to connect to it using the command-line parameters user_name and password and run a SELECT query; import com.documentum.fc.client.IDfClient; import com.documentum.fc.client.DfClient; import com.documentum.fc.client.DfQuery; import com.documentum.fc.client.IDfCollection; import com.documentum.fc.client.IDfDocbaseMap; import com.documentum.fc.client.IDfQuery; import com.documentum.fc.client.IDfSession; import com.documentum.fc.client.IDfSessionManager; import com.documentum.fc.common.DfLoginInfo; import com.documentum.fc.common.IDfLoginInfo; import com.documentum.fc.client.IDfTypedObject; import java.io.RandomAccessFile; public class dctmping { IDfSessionManager sessMgr = null; IDfSession idfSession; public void showDFC_properties_file() throws Exception { System.out.println("in showDFC_properties_file"); IDfClient client = DfClient.getLocalClient(); IDfTypedObject apiConfig = client.getClientConfig(); String[] values = apiConfig.getString("dfc.config.file").split(":"); System.out.printf("dfc.properties file found in %snn", values[1]); RandomAccessFile f_in = new RandomAccessFile(values[1], "r"); String s; while ((s = f_in.readLine()) != null) { System.out.println(s); } f_in.close(); } public void getAllDocbases() throws Exception { System.out.printf("%nin getAllDocbases%n"); IDfClient client = DfClient.getLocalClient(); IDfDocbaseMap docbaseMap = client.getDocbaseMap(); for (int i = 0; i < docbaseMap.getDocbaseCount(); i++) { System.out.println("Docbase Name : " + docbaseMap.getDocbaseName(i)); System.out.println("Docbase Desc : " + docbaseMap.getDocbaseDescription(i)); } } public void getDfSession(String repo, String user, String passwd) throws Exception { System.out.printf("%nin getDfSession%n"); IDfLoginInfo login = new DfLoginInfo(); login.setUser(user); login.setPassword(passwd); IDfClient client = DfClient.getLocalClient(); sessMgr = client.newSessionManager(); sessMgr.setIdentity(repo, login); idfSession = sessMgr.getSession(repo); if (idfSession != null) System.out.printf("Session created successfully in repository %s as user %sn", repo, user); else throw new Exception(); } public void releaseSession() throws Exception { sessMgr.release(idfSession); } public void API_select(String repo, String dql) throws Exception { System.out.printf("%nin API_select%s%n", repo); System.out.printf("SELECT-ing in repository %sn", repo); System.out.printf("Query is: %sn", dql); IDfQuery query = new DfQuery(); query.setDQL(dql); IDfCollection collection = null; String r_object_id = null; String object_name = null; final int max_listed = 20; int count = 0; System.out.printf("%-16s %sn", "r_object_id", "object_name"); System.out.printf("%-16s %sn", "----------------", "-----------"); try { collection = query.execute(idfSession, IDfQuery.DF_READ_QUERY); while (collection.next()) { count++; if (max_listed == count) { System.out.printf("... max %d reached, skipping until end of result set ...n", max_listed); continue; } else if (count > max_listed) continue; r_object_id = collection.getString("r_object_id"); object_name = collection.getString("object_name"); System.out.printf("%-16s %sn", r_object_id, object_name); } System.out.printf("%d documents foundsn", count); } finally { if (collection != null) { collection.close(); } } } public static void main(String[] args) throws Exception { dctmping dmtest = new dctmping(); dmtest.showDFC_properties_file(); dmtest.getAllDocbases(); String docbase; String user; String passwd; if (0 == args.length || args.length > 3) { System.out.println("nUsage: dctmping [target_docbase [user_name [password]]]"); System.exit(1); } if (1 == args.length) { docbase = args[0]; user = "dmadmin"; passwd = "trusted:no_password_needed"; } else if (2 == args.length) { docbase = args[0]; user = args[1]; passwd = "trusted:no_password_needed"; } else { docbase = args[0]; user = args[1]; passwd = args[2]; } String[] queries = {"SELECT r_object_id, object_name from dm_document where folder('/System/Sysadmin/Reports', descend);", "SELECT r_object_id, object_name from dm_cabinet;"}; for (String dql_stmt: queries) { try { dmtest.getDfSession(docbase, user, passwd); dmtest.API_select(docbase, dql_stmt); } catch(Exception exception) { System.out.printf("Error while attempting to run DQl query", docbase, user); exception.printStackTrace(); } finally { try { dmtest.releaseSession(); } catch(Exception exception) {} } } } } eoj
Compile it as instructed in the header, e.g.:
$ export DOCUMENTUM=/home/dmadmin/documentum $ export CLASSPATH=$DOCUMENTUM/shared/dfc/dfc.jar $ javac dctmping.java
Use the command below to execute it:
$ java -classpath .:$DOCUMENTUM/shared/config:$CLASSPATH dctmping [target_docbase [user_name [password]]]
When no parameter is given on the command-line (line 110), dctmping attempts to reach the dfc.properties file and displays its content if it succeeds (function showDFC_properties_file starting on line 25). This proves that the file is accessible through one of the paths in $CLASSPATH.
It then displays the help message (line 16 below):
$ java -classpath .:$DOCUMENTUM/shared/config:$CLASSPATH dctmping in showDFC_properties_file dfc.properties file found in /home/dmadmin/documentum/shared/config/dfc.properties dfc.data.dir=/home/dmadmin/documentum/shared dfc.tokenstorage.dir=/home/dmadmin/documentum/shared/apptoken dfc.tokenstorage.enable=false dfc.docbroker.host[0]=dmtest.cec dfc.docbroker.port[0]=1489 in getAllDocbases Docbase Name : dmtest73 Docbase Desc : a v7.3 test repository Usage: dctmping [target_docbase [user_name [password]]]
Starting on line 12 above (see function getAllDocbases starting on line 39), a list of all the reachable docbases is given, here only one, dmtest73. In some installations, this list be more populated, e.g.:
Docbase Name : global_repository Docbase Desc : Global Repository Docbase Name : SERAC Docbase Desc : SERAC CTLQ Repository Docbase Name : CARAC Docbase Desc : CARAC CTLQ Repository
At least one parameter is needed: the repository name. The user default to “dmadmin”. A password must be given when connecting remotely from the content server. If connecting locally, it can be anything as the user is trusted (if trusted authentication is allowed), or even left empty. The session is opened by function getDfSession() starting on line 49.
A Real Case Application
To check a remote repository’s access, all 3 parameters are required, e.g.:
$ java -classpath .:$CLASSPATH dctmping dmtest73 dmadmin my_password in showDFC_properties_file dfc.properties file found in /home/dmadmin/documentum/shared/config/dfc.properties dfc.data.dir=/home/dmadmin/documentum/shared dfc.tokenstorage.dir=/home/dmadmin/documentum/shared/apptoken dfc.tokenstorage.enable=false dfc.docbroker.host[0]=dmtest.cec dfc.docbroker.port[0]=1489 in getAllDocbases Docbase Name : dmtest73 Docbase Desc : a v7.3 test repository in getDfSession Session created successfully in repository dmtest73 as user dmadmin in API_selectdmtest73 SELECT-ing in repository dmtest73 Query is: SELECT r_object_id, object_name from dm_document where folder('/System/Sysadmin/Reports', descend); r_object_id object_name ---------------- ----------- 0900c35080002a1b StateOfDocbase 0900c350800029d7 UpdateStats 0900c35080002a11 ContentWarning 0900c35080002a18 DBWarning 0900c3508000211e ConsistencyChecker 5 documents founds in getDfSession Session created successfully in repository dmtest73 as user dmadmin in API_selectdmtest73 SELECT-ing in repository dmtest73 Query is: SELECT r_object_id, object_name from dm_cabinet; r_object_id object_name ---------------- ----------- 0c00c35080000107 Temp 0c00c3508000012f Templates 0c00c3508000057b dm_bof_registry 0c00c350800001ba Integration 0c00c35080000105 dmadmin 0c00c35080000104 dmtest73 0c00c35080000106 System 0c00c35080000130 Resources 8 documents founds
After displaying the content of the dfc.properties file, dctmping attempts to connect to the given repository with the given credentials and runs both queries below (see function API_select() starting on line 68):
SELECT r_object_id, object_name from dm_document where folder('/System/Sysadmin/Reports', descend); SELECT r_object_id, object_name from dm_cabinet;
As we don’t need a full listing for testing the connectivity, especially when it may be very large and take several minutes to complete, a maximum of 20 rows by query is returned.
Checking the global_registry
If a global_registry repository has been defined in the dfc.properties file, the credentials to access it are also listed in that file as illustrated below:
... dfc.globalregistry.repository=my_global_registry dfc.globalregistry.username=dm_bof_registry dfc.globalregistry.password=AFAIKL8C2Y/2gRyQUV1R7pmP7hfBDpafeWPST9KKlQRtZVJ4Ya0MhLsEZKmWr1ok9+oThA== ...
Eventhough the password is encrypted, it is available verbatim to connect to the repository as shown below:
$ java -classpath .:$DOCUMENTUM/shared/config:$CLASSPATH dctmping dm_bof_registry 'AFAIKL8C2Y/2gRyQUV1R7pmP7hfBDpafeWPST9KKlQRtZVJ4Ya0MhLsEZKmWr1ok9+oThA==' ... in getDfSession Session created successfully in repository my_global_registry as user dm_bof_registry in API_selectmy_global_registry SELECT-ing in repository my_global_registry Query is: SELECT r_object_id, object_name from dm_cabinet; r_object_id object_name ---------------- ----------- 0c0f476f80000106 System 0c0f476f800005b4 dm_bof_registry 2 documents founds ...
Note that the utility does not check if the given docbase is a global_registry, so the connection attempt may raise an authentication error, but at least the given docbase has been proved to be reachable (or not).
Conclusion
This innocent little stand-alone utility can be easily included in any docker image and can assist in ascertaining at least 5 dependencies in the context of a post-deployment’s sanity check or of a problem troubleshooting:
- the correct installation of the DFCs
- the correct configuration of the dfc.properties
- the correct initialization of $DOCUMENTUM and $CLASSPATH environment variables
- the accessibility of the remote repository(ies)
- and finally, the credentials to connect to said repository.
That is one big chunk of pre-requisites to check at once even before the DFCs clients start.
As a bonus, it also lists all the repositories potentially accessible through the docbrokers listed in the dfc.properties, which is useful in case the local machine hosts a mutualized service serving several repositories. Another freebie is that, if a global_registry is defined in the dfc.properties file, it can be checked too; actually, the utility could be improved to do that automatically (or manually in a subsequent re-run) as the needed credentials are given in that file and require no user interaction but, as they say, let’s leave that as an exercise for the reader.