Академический Документы
Профессиональный Документы
Культура Документы
Db::Documentum
M. Scott Roth
September 2001
Version 1.2
1 INTRODUCTION................................................................................................................. 1
1.1 REQUIREMENTS ................................................................................................................ 1
1.2 CONVENTIONS .................................................................................................................. 2
1.3 INSTALLATION.................................................................................................................. 2
1.4 USE .................................................................................................................................. 3
2 DB::DOCUMENTUM MODULE OVERVIEW ............................................................... 4
2.1 DMAPIEXEC().................................................................................................................. 4
2.2 DMAPIGET().................................................................................................................... 5
2.3 DMAPISET()..................................................................................................................... 6
2.4 DMAPIINIT() AND DMAPIDEINIT().................................................................................. 7
4 REAL-WORLD EXAMPLES............................................................................................ 13
4.1 INBOX TICKLER ............................................................................................................. 13
4.2 IDQL ............................................................................................................................. 14
4.3 CONFIG SCRIPT............................................................................................................... 18
4.4 SERIAL PORT LISTENER .................................................................................................. 23
4.5 WEB ACCESS .................................................................................................................. 30
5 CLOSING ............................................................................................................................ 37
Revisions
1.2 September 2001 Updated for Db::Documentum 1.5. Updated the idql.pl script. Minor
editorial changes.
1.1 October 2000 Updated for Db::Documentum 1.4. Editorial improvements.
1.0 July 2000 Initial Release
1.1 Requirements
To implement the examples discussed in this tutorial:
• You must have access to a working EDMS 98 or 4i Docbase. You will need sufficient
privileges to create types, cabinets, and folders.
• You must have either the Documentum WorkSpace or 4i Desktop Client installed on
your workstation.
• Depending on how you choose to install the Db::Documentum module, you may need a
C/C++ compiler. If you choose the traditional method, you will need the compiler. If
you choose the PPM method you will not.
• You will need the Db::Documentum module (version 1.5 or later).
• You will need a good working knowledge of Perl and Documentum’s API.
1.3 Installation
If you are using Microsoft Windows NT, you have two options for installation: the traditional
method or the PPM method. If you are using UNIX, use the traditional method.1
1.3.1 The Traditional Method
Download the module from the CPAN (http://www.perl.com/CPAN-local/modules/by-
module/Db/), unpack it, and carefully read the README and the Makefile.PL files.
Makefile.PL requires some tweaking to ensure the DMCL libraries and header files are in the
proper locations on your hard drive and that those locations are communicated to make.
Makefile.PL details all of the necessary files and paths. After tweaking, it's business as usual
for the install:
>perl Makefile.PL
>nmake2
>nmake test
>nmake install
1.3.2 The PPM Method
If you are installing Db::Documentum on Windows NT and don’t have a C/C++ compiler, you
can download compiled versions of the module from: http://www.erols.com/theroths/
perl_stuff.html. After downloading and unpacking the archive, install it by typing:
>PPM install Db-Documentum.ppd
1.3.3 Installation Test
You can test your Db::Documentum installation by running the test.pl script in /etc of
your installation directory. If you used the PPM method of installation, you will need to unpack
1 If you don’t have a C compiler on your UNIX box, you can download the pre-compiled module for Solaris 8 from:
http://www.erols.com/theroths/ perl_stuff.html.
2 In UNIX, this command is simply make.
1.4 Use
To use Db::Documentum and Db::Documentum::Tools, just use them in your Perl script.
#!/usr/local/bin/perl
Because the modules do not automatically import their functions into Perl’s namespace, you
must declare which functions in Db::Documentum and Db::Documentum::Tools you want to
use. The keyword :all imports all of the functions. See the modules’ source code for more
details.
That’s all there is to it!
2.1 dmAPIExec()
The dmAPIExec() function executes EDM Server and Workspace methods. dmAPIExec()
returns TRUE (1) or FALSE (0) based upon the success or failure of the method it executes.
2.1.1 Syntax
$api_stat = dmAPIExec("<method name>,<session id>,<method arguments>");
dmAPIExec("close,c,$col_id");4
runs a DQL query against the Docbase. Remember, dmAPIExec() returns only TRUE or FALSE
3 Consult the Documentum Server Reference Manual for a complete list of all methods and their arguments.
4 You will often see the Documentum shorthand "c" used for the current session ID.
2.2 dmAPIGet()
The dmAPIGet() function retrieves information from the EDM Server. dmAPIGet() returns a
scalar containing the information that was requested.
2.2.1 Syntax
$rv = dmAPIGet("<method name>,<session id>,<method arguments >");
returns the collection ID for the last executed query. Remember, the dmAPIGet() function
returns a scalar, not TRUE or FALSE.
Using dmAPIExec() and dmAPIGet() you can query the Docbase and print the names of the
documents you own.
# do query
$api_stat = dmAPIExec("execquery,c,’F’,select * from dm_document where owner_name =
user");
# if query successful
if ($api_stat) {
$col_id = dmAPIGet(“getlastcoll,c”);
# if collection id obtained
if ($col_id) {
sets the attribute title to the value contained in $title for the object identified by
$obj_id.
$api_stat = dmAPISet("append,c,$obj_id,my_date,yyyymmdd","19890805");
appends ‘19890805’ to the repeating attribute, my_date, of the object identified by $obj_id
(using a custom date format).
Using dmAPIExec(), dmAPIGet(), and dmAPISet() you can search the Docbase for the
documents you own and touch them.
# assume $now = today's date
# do query
$api_stat = dmAPIExec("execquery,c,'F',select * from dm_document where owner_name =
user");
# if query successful
if ($api_stat) {
$col_id = dmAPIGet("getlastcoll,c");
# if collection ID obtained
if ($col_id) {
3.1 dm_LastError()
dm_LastError() returns a scalar containing error messages for a particular session.
3.1.1 Syntax
$errors = dm_LastError(<session id>,<level>,<number>);
All of dm_LastError()'s arguments are optional. However, you will almost always call it with, at
least, <session id>. The only exception is when you retrieve errors for a failed logon
attempt and no <session id> exists. With no <session id> defined, dm_LastError()
uses the apisession session ID. The apisession identifies the session created by
dmAPIInit() when it initially connects to the server.
The arguments operate as follows:
<session id> = the session ID of the messages to return. If not present,
apisession assumed.
<level> = the level of messages to return. This argument returns all messages equal to
or less than the level setting. If not present, level 3 is assumed.
1 = Informational Messages
5 There are additional subroutines in the Db::Documentum::Tools module that are not discussed here.
returns all level 3 error messages for the apisession session (i.e., failed logon).
3.2 dm_Connect()
dm_Connect() logs a user onto the Docbase and returns the session ID if successful, or undef
on failure.
3.2.1 Syntax
$sessionID = dm_Connect(<docbase>,<username>,<password>,<domain>,<user_arg>);
Depending upon which OS you use and how you authenticate users, the dm_Connect() syntax
differs slightly. All OSs require the first three arguments: <docbase>, <username>, and
<password>. <domain> is optional. If you use an authentication method other than that
provided by Documentum, you can pass an additional argument, <user_arg>, to your
authentication program. See the Documentum Server Reference Manual and the
Db::Documentum::Tools source code for details and examples.6
3.2.2 Examples
# assume $DOCBASE, $USER, $PASSWD defined previously
$sessionID = dm_Connect($DOCBASE,$USER,$PASSWD);
die dm_LastError() unless $sessionID;
attempts to logon to $DOCBASE as $USER with password $PASSWD and returns the session ID
6 The Db::Documentum::Tools module contains subroutines and information to assist users of Kerberos authentication.
3.3 dm_CreateType()
New object types are usually created in a Docbase using DQL because the Documentum API
does not include a method to perform this task. Have no fear! Db::Documentum::Tools
provides an easy-to-use subroutine for this job: dm_CreateType().
3.3.1 Syntax
$api_stat = dm_CreateType(<object name>,<super type>,<attribute hash>);
where <object name> is any valid object name that you desire, <super type> is any
Documentum object type that you can subtype, and <attribute hash> is an optional list of
custom attributes. The <attribute hash> is a Perl hash where the attribute names are used
as the hash keys, and the database field definitions as their values. The function can be called
without a hash defined, in which case no additional attributes are added to the new object type.
The dm_CreateType() returns TRUE or FALSE based upon its success or failure.
3.3.2 Examples
$api_stat = dm_CreateType ("my_document","dm_document");
3.4 dm_CreateObject()
dm_CreateObject() allows you to easily create a new instance of an existing object type, and
optionally assign attribute values to it.
3.4.1 Syntax
$obj_id = dm_CreateObject (<object type>,<attribute hash>);
where <object type> is any valid object type that you can instantiate and <attribute
hash> is an optional list of custom attributes and their values. The <attribute hash> is a
Perl hash where the attribute names are used as the hash keys, and the attribute values as the hash
values. In the case of repeating attributes, the hash values must be delimited by
creates and saves a new document in the Docbase with attribute values defined in %ATTRS.
Note: dm_CreateObject() does not save the object. You must execute the save() method
separately.
$obj_id = dm_CreateObject ("dm_document");
$api_stat = dmAPIExec("save,$sessionID,$obj_id");
creates and saves a document in the Docbase with no attribute values assigned.
3.5 dm_CreatePath()
dm_CreatePath() allows you to easily create folder hierarchies in a Docbase. dm_CreatePath()
returns the r_object_id of the newly created folder upon success, or undef on failure.
3.5.1 Syntax
$folder_id = dm_CreatePath(<path>);
creates the folder Test and returns its r_object_id. If /Temp and /Temp/Db-
Documentum do not exist, they are also created.
@years = ('/Data/Years/1998','/Data/Years/1999',
'/Data/Years/2000','/Data/Years/2001');
3.6 dm_LocateServer()
The dm_LocateServer() subroutine returns a scalar containing the hostname of the server running
the Docbase named in its argument. If a server cannot be found for the Docbase, the subroutine
returns undef.
3.6.1 Syntax
$hostname = dm_LocateServer(<docbase name>);
prints the name of the Docbase entered at the prompt and the name of the server hosting it.
4.1.2 Discussion
The beauty of scripts like tickler.pl is that virtually no effort was invested in their
production. They can be cobbled together quickly, used once, and discarded with no loss of
investment. Or, they can be used and reused with great utility. In addition, because
tickler.pl is a script (and not a compiled program), it can be changed tomorrow or next
week with little effort. Isn't Perl great?
Though this script is pretty simple, it does contain a few points I want to highlight. First, the
script does not define $DOCBASE, $USER, and $PASSWD. There are a number of ways to
obtain the $DOCBASE, $USER, and $PASSWD if you don't want to hardcode them in the script.
You could prompt the user to enter them (as I will demonstrate later), read them from a file,
glean them from the OS, or pass them on the command line. I leave this as an exercise for you.
The second point I want to highlight is the loop between lines 26 - 28. This loop accesses the
data in the collection returned by the query on line 21. This loop demonstrates the basic
construct for accessing data in a collection. You must execute at least one next() method on a
collection to obtain any data from it, and as noted earlier, always close() your collections. In
between, you can get() any of the attributes you named in your query.
4.1.3 Output
When tickler.pl is run, the output looks like this:
4.2 IDQL
As I’m sure you are aware, the IDQL editor is a very handy Documentum tool. As a test of my
Documentum API programming skills, and to exercise the Db::Documentum module, I wrote the
4.2.3 Output
Here is output from a sample session using idql.pl (edited slightly to conserve space):
object_name
title
----------------------------------------------------------------
----------------------------------------------------------------
------------------------------------------------- -------------
----------------------------------------------------------------
----------------------------------------------------------------
------------------------------------
getfile.pl
show_files.pl
dmquery.html
intro-db-dctm.doc
Intro to Db::Documentum
tickler.pl
[5 row(s) affected]
1>_
7 After completing these installations, I began to investigate releasing my Perl-Documentum integration to the CPAN. I
discovered that only weeks before, Brian Spolarich had published the Db::Documentum module. After examining it, I found that
we both came to essentially identical solutions--except that his module was UNIX-based and mine was NT-based. I contacted
Brain and we collaborated on versions 1.1, 1.2, and 1.3 of the module. Since 1.4, I have been the sole maintainer.
4.3.2 Discussion
This script is pretty straightforward in what it does: logs on, creates some new object types,
creates a few folders, creates a new user, and logs off (see lines 33 - 41). It's the details that are
of interest, so let's examine them.
First, note the use of the data structures in lines 10 -31. These structures make visualizing and
maintaining objects in the script much easier. In fact, the ease-of-use and flexibility of data
structures in Perl is on of the primary reasons I chose Perl to create my installation scripts. I
encourage the use of data structures like these everywhere possible.
The cabinet/folder hierarchy is contained in the list, @FOLDERS; the order of the paths is
irrelevant. The users are defined in %USERS, a hash of hashes (HoH). The HoH makes it clear
by inspection what users I am creating and their attribute values. %TYPES is also a HoH making
it clear the types of objects I will create. In this example I am creating only one new object type
(news_wire_type) with a single custom attribute (news_agency). Notice that this new
type is a subtype of dm_document and that dm_document has been hardcoded into the
dm_CreateType() function call in the create_new_object_types() subroutine (line 106).
If I were creating a number of new types with different supertypes, I would need to modify the
data structure or subroutine to account for them.
The first subroutine encountered after logging on is create_new_object_types() (lines
92 - 108). This subroutine uses embedded foreach loops to iterate over the %TYPES HoH and
extract each type definition. The outer foreach loop iterates over each key (type name) in the
%TYPES HoH and the inner loop extracts each type's definition into %attrs. Line 106 creates
the object in the Docbase using %attrs. Notice that I used warn statements to indicate when
errors occur. die is probably more appropriate because an unnoticed error in configuration can
be disastrous later.
The next step in the configuration is to create a new storage location for the content of our
news_wire_type objects. This is done by create_new_storage() on lines 110 - 161.
Three things are notable about this subroutine: 1) the objects it creates; 2) the order in which
they are created; and 3) the fact that the leaf directory of $CONTENT_DIR cannot exist. This
subroutine was adapted from Documentum's primtp.dql script found in the DocPage Server
System Administrator's Guide. See the guide for further explanation.
Next, let's examine the creation of the cabinet/folder hierarchy in the Docbase.
build_folder_hierarchy() on lines 62 - 69 creates the cabinet/folder hierarchy by
simply iterating over the @FOLDERS list and passing each line (containing a path) to
dm_CreatePath(), where the real work is done.
The final step in the configuration is to create users. This is done by the create_users()
subroutine on lines 71 - 90. This subroutine functions in a manner nearly identical to
create_new_object_types() in that it uses embedded foreach loops to iterate over an
Logging off...
4.4.2 Discussion
listener.pl uses the Win32::SerialPort module. Win32::SerialPort provides an object-
based interface to your PC's serial port8. If you don't have this module, you need to get it from
the CPAN. For more information regarding the use of Win32::SerialPort, see the
Win32::SerialPort documentation.
To begin, this script defines some constants (lines 10 - 26), logs on to the Docbase (lines 29 -
30), creates a local working directory (lines 32 - 35), creates a folder hierarchy in the Docbase
(lines 37 - 38), and opens and configures the serial port (lines 40 - 52). Then, this script lives
within the infinite while loop defined on lines 54 - 130. Each story is read from the serial port
by the until loop on lines 62 - 66. When the story delimiter (defined on line 60) is
encountered in the input data, the story is assigned to $gotit (line 63) and the until loop
exits. Note: the sleep in this loop is paramount. Without it, the script won't give any CPU
time to other processes on your computer!
Once a complete story is contained in $gotit, it is cleaned up on lines 69 - 74 and saved to a
8 The UNIX equivalent of Win32::SerialPort is Device::SerialPort; also available from the CPAN.
When that is the case, and the reporter followed the format, this loop does a good job of
extracting the title and assigning it to the title key in %Attrs. However, the news wire often
carries stories that don't adhere to this format such as: corrections, multi-part stories,
announcements, and stock market reports. In those cases, no title is extracted and $filename
is assigned to the title key of %Attrs.
Second, notice that on line 105 I explicitly flag the story for full-text indexing. This is important
if you want to be able to find the story using the web interface in the next example. This, of
course, assumes that your server is running the full-text indexing job.
This is a powerful script for only 160 lines of code. It contains the basic elements needed for
real-time and bulk data loading, and it wouldn't take much to convert it from a script that listens
to a serial port to one that reads a file or a database table as input. Scripts of this type are
invaluable when bulk loading or migrating data into Documentum.
I have found that listener.pl runs well as a Windows NT service. To learn how to convert
a Perl script into a Windows NT service, read Kevin Meltzer's Perl Journal (http://www.tpj.com)
article "Turning a Perl Program Into an NT Service" in issue #15.
4.4.3 serv.pl
listener.pl makes a big assumption. It assumes that you have access to an AP serial news
feed. I realize that most people do not have access to such a thing. In fact, I didn't either when I
wrote this script. That's why I wrote the serv.pl script. serv.pl will serve any text file to a
serial port. You can think of it as the reciprocal of listener.pl in that it talks instead of
listens to the serial port!
1 #!/usr/bin/perl
2 # serv.pl
3 # (c) 2000 MS Roth
4
5 use Win32::SerialPort;
6
7 $PORT_NAME = "COM2";
8 $DATA_FILE = "ap.txt";
4.4.4 Discussion
This script simply opens the serial port, reads the $DATA_FILE into memory, and endlessly
spews it out. A few things to note here:
1. My ap.txt was a text file containing real, captured data from my customer's AP serial
news feed. You can use any text file that you like, but you will want to change the
regular expression on line 33 to match your story delimiter. You will need to make this
change in listener.pl also (line 60).
2. Line 35 executes a random sleep between successive stories. This pause between
stories more closely emulates the timing of a real AP serial news feed.
3. To use listener.pl and serv.pl together on the same machine, run them in
separate command windows, and connect your serial ports together with a null modem
cable.9
4.4.5 Output
The output generated by the serv.pl script looks like this. Each "#" represents an iteration of
the data file, and each "." represents a story sent to the serial port.
9 This should also work on UNIX systems, although I confess, I have never tried it.
The output generated by the listener.pl script is a little more interesting. listener.pl
informs you at which port and at what time a story was received, what it named the file (and
hence the object_name in the Docbase), and what the r_object_id was when it was
checked into the Docbase.
In WorkSpace, the result of running config.pl and listener.pl looks like this:
10 And now with Documentum 4i v4.1, the Web Developers Kit (WDK).
4.5.2 show_files.pl
The show_files.pl script is the action of the form created by dmquery.html. It does
the query and displays the results as hyperlinks.
1 #!/usr/bin/perl
2 # show_files.pl
3 # (c) 2000 MS Roth
4
5 $| = 1;
6 use Db::Documentum qw(:all);
7 use Db::Documentum::Tools qw(:all);
8 use CGI qw(:standard);
9 use CGI::Carp 'fatalsToBrowser';
10
11 $q = new CGI;
12
13 # get input from form
14 $input = $q->param('search');
15 $docbase = $q->param('docbase');
16 $username = $q->param('username');
17 $password = $q->param('password');
18
19 # logon to Docbase
20 $session = dm_Connect($docbase, $username, $password);
21 die dm_LastError() unless $session;
22
23 # set cookie for use by getfile.pl
24 %dm_session = ('docbase' =>$docbase,
25 'username'=>$username,
26 'password'=>$password);
27
28 $cookie = $q->cookie(-name => 'dm_session',
29 -value =>\%dm_session,
30 -path => '/cgi-bin',
4.5.3 Discussion
There are three aspects of this script I want to discuss. The first is the use of CGI.pm on lines 8
and 9: don't do CGI without it. The second is the form of the URL printed on line 54. This URL
points to the getfile.pl script and passes (via the query-info portion of the URL) the object
ID of the document to retrieve. For example, an <A> tag produced by line 54 might look like
this:
<A HREF="cgi-bin/getfile.pl?obj_id= 0901653800111d8">.
Third, I want to discuss the cookie. Lines 23 - 33 save your logon information to a cookie. This
is necessary because Db::Documentum executes a dmAPIDeInit() automatically when
show_files.pl terminates. dmAPIDeInit() destroys the apiconfig object created by
dmAPIInit() and effectively closes the session (RightSite certainly has an advantage over us
here!). To retrieve a document from the Docbase, getfile.pl will need an active session.
To provide it one, I stash your logon information in the cookie and retrieve it in getfiles.pl
to logon again.
Now, I will be the first to admit that saving logon information--especially passwords--in a cookie
4.5.5 Discussion
The getfile.pl script is simple enough. The basic idea is to get the object ID from the URL,
query the Docbase to determine what type the object is (text vs. binary), and retrieve the content
of the object in an appropriate manner.
The script begins by retrieving the object ID from the URL (line 14) and the cookie from the
browser (lines 17 - 20). Both of these tasks are made possible by CGI.pm. Lines 22 - 38 logon
to the Docbase and execute a query to retrieve the content type information about the document
referenced by $object_id.
Starting at line 40, I branch based upon the content of $type. If I am dealing with a text
document (crtext), I execute lines 42 - 54, which use getcontent() to retrieve the object's
content and print it. Notice the loop on lines 47 - 50. This is necessary because
getcontent() returns a collection of pages. For this example, the collection will contain
only one page.
If the object's type is not text, the else condition is executed on lines 55 - 59. This is a bit over
designed for this example because I am only ever retrieving text files (news_wire_type), but
I wanted to show you an interesting trick if you ever expect to retrieve binary files. Line 57
downloads the file from the Docbase to the web server. Line 58 then redirects the browser to
$dl_file and lets the server and the browser resolve the MIME type and launch the
appropriate plug-in or helper application. Pretty neat trick, eh?
Two notes:
1. You will need to modify the path and host names on lines 57 - 58 to reflect your
configuration.
2. If you do retrieve binary files, make sure you occasionally delete the $dl_file files
from your web server; they can really add up after a while.
4.5.6 Output
To use the web interface, point your browser at http://<host>/dmquery.html, enter your logon
information and a word to search on.
Clicking the Submit Query button results in the following response from show_files.pl
This is a very simple example to demonstrate the basics of using CGI, Perl, and
Db::Documentum to host a Docbase on the web, it is far from complete. Besides the security
issue with the cookie, you might consider:
• There is little or no error checking or recovery in either Perl script. If empty collections
or object IDs are returned, you are often presented with only a blank page in your
browser.
• In the show_files.pl script, you could display the cabinet/folder path associated
with each $title by querying for the i_folder_id and associating it with the
corresponding dm_folder object. This would provide the user some additional context
(i.e., date and wire service name) about the story.
• You could try using tickets instead of passwords in the cookie.
• You could give everyone anonymous access to the Docbase, do away with the cookie,
and hard code the logon in the script.
• You could improve the DQL query to process Boolean search terms.
• You could employ templates to display the news articles in a more pleasing style and add
navigational aids so the directory structure can be traversed.
• You could use the ideas here to create an entirely different web application. For instance,
a guest book. The possibilities abound!
I would like to express my thanks to the people who have assisted me with this tutorial: Frances,
Matt, John, and Scott. Thanks!
M. Scott Roth is co-author of the Db::Documentum module, and "just another Perl hacker" at
SAIC in Reston, VA. Mr. Roth has also built a Perl interface to the DFC (Db::DFC) available
from the CPAN. Feel free to contact him with your comments, suggestions, complaints, etc.
regarding this tutorial or Db::Documentum in general. Mr. Roth can be reached at
michael.s.roth@saic.com.