Opentext Documentum D2: Developers Guide
Opentext Documentum D2: Developers Guide
Version 4.7
Developers Guide
Tel: +1-519-888-7111
Toll Free Canada/USA: 1-800-499-6544 International: +800-4996-5440
Fax: +1-519-888-0677
For more information, visit https://www.opentext.com
Adobe and Adobe PDF Library are trademarks or registered trademarks of Adobe Systems Inc. in the U.S. and other countries.
Disclaimer
Every effort has been made to ensure the accuracy of the features and techniques presented in this publication. However, Open Text
Corporation and its affiliates accept no responsibility and offer no warranty whether expressed or implied, for the accuracy of this
publication.
Intended audience
The information in this guide is for developers who create and configure extensions for D2, using the D2
and D2 Foundation Services (D2FS) API.
Extending D2
This chapter contains the following topics:
• Introduction to Extending D2
Introduction to Extending D2
Before customizing D2 by using extensions, you must carefully re-examine the functionalities available
through D2 and its official plug-ins. In almost all cases, using existing capabilities to achieve your use
cases provides a less expensive and more reliable solution.
D2 offers the following methods of extending D2 by using the D2 API:
• Building integrated solutions by using the D2FS services to interact with the D2 web
application.
• Using Open Ajax Hub (OAH) and Javascript to create external web applications that
can be manipulated as iframes (External Widgets) from within the D2 web
application.
• Using D2 Java packages to create and configure D2 plug-ins that add custom actions
or override existing D2 services.
You can use any Integrated Development Environment (IDE), such as Eclipse, for building the code.
Understanding D2 Architecture
This chapter contains the following topics:
Browser Layer
D2 Client is a Web 2.0 browser-based application. End users can navigate to the D2 Client web application
URL to log in to use D2. D2 Client is:
• GWT/GXT-based: D2 Client uses Sencha GXT and the Google Web Toolkit (GWT) to
manage JavaScript. As a result, the D2 Client JavaScript code is delivered hidden and in a
single large file. You cannot extend or debug D2 Client without the original source.
• The Sencha website (http://www.sencha.com/products/gxt) contains further information on
GXT.
• Loaded onto a single HTML page: D2 Client interprets and loads JavaScript into the browser
memory at the start. As a result, D2 manipulates HTML DOM to remove and replace
interface portions rather than switching the HTML pages upon user navigation.
• GWT RPC-based: D2 uses standard HTTP requests for all communication between the
browser client and the D2 web application. For requests for static resources such as images,
D2 uses GWT RPC calls backed by the D2FS services layer, to retrieve dynamic data.
The Google developer site (https://developers.google.com/) has more information on GWT RPC.
Browser Layer
D2 Config is a Web 2.0 browser-based application. End users can navigate to the D2 Config web application
URL to log in to use D2. D2 Config is:
• ActiveX control-based: D2 Config uses the ActiveX control such as the old D2 3.x
architecture and is accessible only by using the Microsoft Internet Explorer browsers.
• XML-based: D2 Config uses the old D2 3.x architecture for D2 and delivers the interface to
the web browser in XML representation.
In basic installations, with most users in close geographic proximity, the D2 applet uses a standard HTTP
connection to connect directly to the D2 web application.
Use the Documentum Branch Office Caching Services (BOCS) cache to speed up the transfer of large files to
users in remote locations. You must install the D2-BOCS plug-in on the same server as the BOCS cache to:
• Facilitate communication between the D2 applet and the BOCS cache.
• Allow D2 to manipulate content in and out of the repository. For example, the C2 plug-in can
be used to insert configuration-based watermarks.
D2-BOCS must apply configuration rules to complete the C2 and the O2 content operations. As a result, D2-
BOCS opens a DFC session with the repository, which makes Documentum RPC calls to the repository to
retrieve configuration objects and content metadata. Due to the relatively high latency of the connection between
the BOCS cache and Content Server, administrators and end users must limit the number of applicable O2 and
C2 configurations. Configure D2 so that only large files are transferred using the BOCS cache to reduce load on
the connection and to connect directly to the D2 application for the transfer of smaller files.
2. Use the following command to generate the Java source files for the stub libraries that
are needed to connect to D2:
wsimport —keep http://localhost:8080/D2/ws/d2fs.wsdl
The -keep parameter saves the files so that you can copy them to your project.
3. If you have generated stubs by using a different D2 server than the one you have used to
call D2FS, modify the URL. to do this, open ModelPortService.java and edit
the following line:
static { url = new URL("http://<D2 server address and
port>/D2/ws/d2fs.wsdl"); }
D2FS Examples
Navigate to the D2FS/Java/D2FSExamples/src/com/OpenText/d2/d2fs/examples folder in
the D2 SDK package for the following examples of Java classes:
Class Description
ContextAndLoginExample.java Demonstrates how to establish a D2FS login context
with the D2 server.
ImportAndUploadDocumentExample.java Demonstrates how to use createproperties()
to import and upload content.
DestroyExample.java Demonstrates how to destroy content.
SavePropertiesExample.java Demonstrates how to set content attributes.
SearchQuickExample.java Demonstrates how to run a quick search.
SearchAdvancedExample.java Demonstrates how to:
• Use SearchService and ContentService to search
the repository.
• Save or run an advanced search.
• List saved searches.
• Run a saved search.
CheckinContentExample.java Demonstrates how to use getCheckinUrls() to :
• Check out content.
• Request a checkin URL.
• Post new content with checkin parameters.
TaxonomyEnumAndExportUrlExample.java Demonstrates how to use ContentService to:
• Enumerate taxonomy objects.
• Download and save the exported taxonomy.
Setting Up the D2FS Examples contains instructions for setting up the D2FS examples after you
generate the Java stub libraries.
2. Delete the com.emc.d2fs.* and org.w3.* packages if they are available in the
project.
4. Update LoginInfo.java to specify the repository name, login ID, password, and
URL of the web application.
5. Check the setup comments available above the example, to confirm the required inputs.
5. Log in to D2 Config:
a. Navigate to File > Import configuration from the menu bar.
e. Modify the Widget url field to match the location of the web application server that you have
deployed. For example: http://myserver:8080/X3–Ext-UpdateDoclist
The sample external widget also contains a second URL that displays an Open Ajax Hub publish and subscribe
application: http://myserver:8080/X3–Ext-UpdateDoclist/devindex.html
http://YourServer:8080/D2/container/external-api/OpenAjaxManagedHub-all.js
OpenAjaxManagedHub-all-obf.jsis the smaller version that is used by D2 for production purposes and
OpenAjaxManagedHub-all.jsis the full version that is used for development purposes.
http://YourServer:8080/D2/container/external-api/D2-OAH.js
You can then set a callback for a successful connection to the hub to perform initialization tasks. For example, the
sample waits for the hub client to connect, and then initiates the subscription to events.
function connectCompleted(hubClient, success, error) {
if (success) {
// subscribe to events
subscribeEvents();
} else
logit("Hub client NOT connected - please check console");
}
The sample widget always subscribes to events even if you do not request that the widget track and display objects of
the selected type.
// onInitWidget is called to allow widget initialization (can subscribe or //
initialize as needed)
// message contains an OpenAjaxMessage() object
function onInitWidget(message) {
logit("onInitWidget()");
}
Subscribing to Events
You can interact with the external widget to set or change subscriptions to D2 events. You can then use the
subscription to listen to messages and to extract information. Select Track selected type to track the content that is
selected by users in D2 Client, receive notifications, and view the document type
in the external widget form.
Call the subscribeToChannel() method to use this feature of Open Ajax Hub as shown in the sample code:
function subscribeEvents() {
OpenText™ Documentum® D2 Version 4.7 User 21
Using OAH to Create and Configure a Sample External Widget
logit("subscribe to events...");
d2OpenAjaxHub.subscribeToChannel ("D2_EVENT_SELECT_OBJECT",
selectObjectCallback, true);
}
The selectObjectCallback method is an example method in the sample that dictates how the widget uses the
message received through this event.
Sending Messages
You can interact with the external widget both as a widget in a D2 Client workspace or by accessing the URL directly
through a web browser. The sample web application contains an HTML form that contains the same fields as the QF
HR a_status query form which is imported when the external widget is installed. Type a document type, select a
document status, and click the Update Doclist button. The external widget sends an Open Ajax Message to D2 to
execute the QF HR a_status query form search. The query form then functions as a Doclist widget filter because
it updates the Doclist widget with the search results.
Call the sendMessage() method to use this feature of Open Ajax Hub as shown in the sample code:
function updateDoclist() {
//To update the doclist a new OpenAjax message will be build to be posted
//in the Hub using the D2-OAH API
var messageToSend = new OpenAjaxMessage();
//set the query form config name which will be used to update the doclist
messageToSend.put("config", queryFormConfigName);
//Then we define what service and what method in the service will be called.
//We call the Search service and the runQueryFormSearch method.
//Calling this service will update the user’s last search object
messageToSend.put("eService", "Search");
messageToSend.put("eMethod", "runQueryFormSearch");
22 OpenText™ Documentum® D2 Version 4.7 User Guide
Using OAH to Create and Configure a Sample External Widget
// As the last search has been updated by the web service call, we will post
// the D2_ACTION_SEARCH_DOCUMENT event to display the search results
messageToSend.put("rAction","D2_ACTION_SEARCH_DOCUMENT::oam_id==node_last_sea
rch");
2. Add one entry per line to the properties file, using only alphanumeric lowercase (a-z, 0-
9) and no special characters except underscore. For example,
proj_event_custom
c2_controled_print
c2_controlled_print_recall
3. Make the JAR available inside the D2-Config web application. Your events appear in
the D2 audit list.
2. Add, at a minimum, a list of keys=values where the keys should be the event names
and the values the corresponding label. For example,
This is all the labelling that is required for a “normal” display. If you don’t intend to use “extended”
display, you can skip the next section.
Note: If you use the extended feature but do not define the extended label, D2 will fall back to the
normal label. You will also miss the display of the strings data holder in this mode.
• If you want one simple label with the only string_1 information:
proj_event_custom.extended = In Proj Custom, "$value(string_1)" was
applied
OpenText™ Documentum® D2 Version 4.7 User 25
Understanding Audit Events
string_1 to string_5 is available. Again, depending of the audit creator, some may be empty. The
bundle definition logic is tightly linked with audit usage.
In most audit use cases, the available string_#s (string_1 through string_5) are filled in ascending
order (1-5). You would not usually fill in information in string_5 if string_4 were vacant. As a
best practice, use string_# in order.
• If you generally use all five strings, but occasionally one of the string_#s is not
present or is empty, you can avoid an odd-looking message by using an empty
placeholder:
proj_event_custom.extended = Link to
"/$value(string_1)/$value(string_2)
/$value(string_3)/$value(string_4)/$value(string_5)"
If string_5 is not provided, you can add a third property node saying “nostring5”. When the D2 audit
bundle logic sees the audit to display and that you have provided the following bundle key, it will use
the following message instead of the generic message above:
proj_event_custom.extended.nostring5 = Link to "/$value(string_1)
/$value(string_2)/$value(string_3)/$value(string_4)"
If no string_4:
proj_event_custom.extended.nostring4 = Link to "/$value(string_1)
/$value(string_2)/$value(string_3)"
You can see that the nostring4 is not using string_5 either. That is because the normal case is that
you do not fill string_5 if string_4 is missing. There is no conditioning logic that detects
string_5 but no string_4. We detect the key definition that looks the best suitable to the audit case
we have; it may not match all your different use cases.
• If you want to make the message more reusable for other labels, you can use five
different combinations of bundle keys.; In each of them, define what represents the
“string_#” data as it will provide more meaning to the end-user and represents only
one bundle key to change:
proj_event_custom.extended.string_3Label = reason
The bundle definition will look like this (avoid making any cyclic use of key):
proj_event_custom.extended.nostring4 = Link to "/$value(string_1)
/$value(string_2)" where $value(string_3Label) is "$value(string_3)"
proj_event_custom.extended.string_3Label = reason
For example in D2, we have this simple declaration where all strings are being used. The unlink event
has its strings filled with each folder level; sometimes five levels deep, sometimes two levels deep:
dm_unlink.extended=Unlink from
"/$value(string_1)/$value(string_2)/$value(string_3)
/$value(string_4)/$value(string_5)"
dm_unlink.extended.nostring5=Unlink from
"/$value(string_1)/$value(string_2)
/$value(string_3)/$value(string_4)"
dm_unlink.extended.nostring4=Unlink from
"/$value(string_1)/$value(string_2)
/$value(string_3)"
dm_unlink.extended.nostring3=Unlink from
"/$value(string_1)/$value(string_2)"
dm_unlink.extended.nostring2=Unlink from "/$value(string_1)"
Another example in D2 where signoff is not always configured with user intention. When intention is
available, we display it, when not, we do not:
d2_import_signoff=Signoff for import
d2_import_signoff.extended.nostring1=Signoff for import
d2_import_signoff.extended=Signoff for import with this intention :
$value(string_1)
1. Create your code level event according to the use cases described here, which employ
two different APIs (the first one conditioning the use of the second one):
• If you want to create an audit only if the configuration engine has the event name
set up to be displayed:
com.emc.d2.api.config.modules.audit.D2AuditConfig
OpenText™ Documentum® D2 Version 4.7 User 27
Understanding Audit Events
Two static methods are provided:
IDfPersistentObject apply(IDfPersistentObject persistentObject,
String eventName, String string1,
String string2, String string3, String string4, String string5)
throws DfException;
The only difference between the two declarations is the passing of the user name. In the first API call,
the username (event creator) is not passed (it is null). This will cause the “loginUserName” to be
retrieved from the current DFC Session object currently being used by the persistentObject.
In the second API call, the username (event creator) is passed. Your event will not be created if the D2-
Config context engine and audit configuration module indicates it is not audited (checking D2-Config
context engine either based on type and/or group).
• If you want to create an audit and have it display only if the configuration engine
has the event name set up.
com.emc.common.dctm.objects.DfAuditTrailEx
There are multiple method signatures available, mainly relying on this first one, simplifying the
different parts that are not meant to be used.
IDfPersistentObject create(IDfPersistentObject persistentObject,
IDfSession privateSession, String eventName,
String userName, String string1, String string2, String string3,
String string4, String string5, List<String>
attributes, String eventSource, IDfId workflowId, String version)
throws DfException;
In the method shown above, the session is retrieved from the persistent object directly.
IDfPersistentObject create(IDfPersistentObject
persistentObject, IDfSession privateSession,
String eventName, String userName, String string1, String
string2,
String string3, String
string4, String string5, List[String] attributes) throws
DfException;
In the method shown above, the session object can be defined, and will be used to extract the username
and create the audit object.
Note: You can see a list of string attributes. These are used, for example, to audit properties. Each of
the strings are the attribute/property name of the “persistentObject”. The previous values from the
persistent object are extracted and both the old and new list are part of the audit trail. They will be used
by our audit engine to present them as old value and new value or added/removed value.
28 OpenText™ Documentum® D2 Version 4.7 User Guide
Understanding Audit Events
Here is a longer example, with the context of use:
D2 create user is auditing using D2AuditConfig statics since audit is needed only if configured.
C2 recall printing system will use “DfAuditTrailEx” statics, since the plugin is basing its control logic
for printing on audit trail existence.
The decision point between those two APIs, is dependent on whether or not you have a plugin logic
base on audit event existence.
You have to take into consideration the cost of creating an in terms of resources (DB, JVM) but also in
terms of space (DB). For example, it is preferable to create an audit for a batch of work, rather than an
audit per task in a batch.
When possible, it is recommended to use the D2 API, as it has been optimized for session management
when a lot of events are to be created. Although it is not always possible to use the API for external
development, you can still rely upon the DFC API for the same functionality (please refer to the
corresponding DFC documentation).
4. The D2 applet detects the event and triggers a download using the ExportContent servlet.
6. The D2ExportServicePlugin intercepts the call and sends the custom content.
e. Click New and fill out the form as described in the following table:
Field Description
Name Type D2_4x_LIB
Path Type the path to the WEB-INF/lib folder in the D2 web application. For example:
f. Click New and fill out the form as described in the following table:
Field Description
Name Type D2_4x_CLASSES
Path Type the path to the WEB-INF/classes folder in the D2 web application. For example:
g. Click New and fill out the form as described in the following table:
Field Description
Name Type D2_DFC_JAR
Path Type the path to the folder containing DFC.jar file. For example:
C:/OpenText-dfs-sdk-6.7.2/lib/java/dfc/dfc.jar
Eclipse will not show any errors now.
D2_DFC_JAR = C:/OpenText-dfs-sdk-6.7.2/lib/java/dfc/dfc.jar
a. Log in to D2 Client.
b. Navigate to Help > About D2 from the menu bar.
1. Log in to D2 Config and navigate to Go to > Menu D2 to open the D2 Client menu
configuration page.
2. Add a new menu item to the menu in which you want the button to appear. OpenText Documentum D2
Administration Guide contains further instructions for configuring a D2 Client menu.
3. Fill out the form for the new menu item as described in the following table.
Field Description
Label en Export Custom Content
Shortcut Ctrl+D
Action Calling service’s method
Service D2CustomService
Method getCustomDownloadURL
Selection Select MULTI to allow end users to select multiple
content for the download action.
Type Select EVENT to post an event if the web service
call is successful.
Action D2_ACTION_EXPORT_FROM_URL
34 OpenText™ Documentum® D2 Version 4.7 User Guide
Creating and Configuring a Custom Plug-in
4. Click Save.
6. Remove the example plug-in classes and replace them with your plug-in implementation. For
example: *.webfs.services.content and *.webfs.services.create
The OpenText Documentum D2 D2FS API JavaDoc contains more information about the packages that
you can use to configure a custom action.
3. Implement IPluginAction:
4. The class must contain a method that has at minimum the following format:
public List<attribute> <methodName>(D2fsContext context);
For example, the YourCo-PluginName plug-in sample contains the following method declaration for
D2CustomService.java:
5. After building and installing the plug-in, create a menu button in D2 Config to allow end users
to perform the custom action. The OpenText Documentum D2 Administration Guide contains
further instructions for configuring a D2 Client menu.
For example, Configuring a Menu Item for the Sample Plug-in, page 34 contains the instructions for creating a
menu button for the YourCo-PluginName plug-in sample.
Understanding Service Interface Overrides
You can override all D2 services interfaces using the @override annotation.
Pre-Processing
You can add data processing before a service by adding code before the super call. The following example code
adds a custom timestamp to the end of a content title before the service creates content properties. The pre-
processing ensures that content created using the plug-in have a timestamp at the end of the title:
/** Override createProperties to add custom pre-processing.
*
*/
@Override
public String createProperties(Context context, java.util.List<Attribute>
parameters) throws Exception {
// pre-processing -- look for ’title’ attribute and add a custom
//timestamp at the end.
Post-Processing
You can add data processing after a service by adding code after the super call. The following example code runs
the result of the service through a capitalization process. The post-processing ensures that the plug-in returns the
name of the object_name attribute in upper-case letters.
/** Override getContent() to demonstrate post processing
(make object_name attributes upper case).
*
NOTE: If overriding getContent() you may want to similarly override
getFilteredContent().
*
return result;
}
Throwing Exceptions
You can use Exception() and D2fsException() to report exceptions or errors encountered in your Service
extension or Custom Action code back to D2 and the end user. The following example shows how to use
D2fsException():
/**
Error reporting -- Note that when you need to report an error to D2
and the user, you can simply throw an Exception() or D2fsException(),
whichever is compatible with the api your are overriding
For example, this custom action throws D2fsException()
*
*/
Boolean bNeedToReportAnError = false;
//
if (bNeedToReportAnError) {
// Error detected; Build your localized message and throw the
//Exception() to notify user.
throw new D2fsException("A problem was encountered with your custom
export...");
}
return result;
}
4. Extend the service class with the <name of the service> and implement the
OpenText™ Documentum® D2 Version 4.7 User 39
Creating and Configuring a Custom Plug-in
ID2fsPlugininterface.
For example, the sample declares the class:
public class D2ExportServicePlugin extends D2ExportService
implements ID2fsPlugin
• getFullName: Returns the full plug-in name with its version number. The
method is called by the About dialog box in D2.
Deploying D2 Plug-ins
1. Create the plug-in .jar file.
2. Copy the plug-in .jar file to the <install path of D2>/WEB-INF/lib/ folder. For
example:
C:\apache-tomcat-<version>\webapps\D2\WEB-INF\lib
4. Verify the plug-in installation by logging in to D2 Client and navigating to Help >
About.
|--<D2 App>/WEB-INF/classes
|--xml
|--dialogs
|--> <name of the Dialog>.xml
|--<D2 App>/WEB-INF/classes
|--strings
|--dialogs
CustomDialog.java:
package com.ot.myapp.webfs.dialogs
CustomDialog.xml
</fieldset>
</content>
<buttons>
<button id="buttonOk" action="validDialog()"></button>
<button id="buttonCancel" action="cancelDialog()"/>
</buttons>
</dialog>
buttonCancel=Cancel
buttonOk=OK
2. Copy the .jar file to the <install path of D2>\WEB-INF\lib\folder. For example:
C:\apache-tomcat-<version>\webapps\D2\WEB-INF\lib
3. Copy XML file to the <install path of D2>\WEB-INF\classes folder. For example:
C:\apache-tomcat-<version>\webapps\D2\WEB-INF\classes\xml\dialog
2. Implement IPdfProcessorListener
public class implements IPdfProcessorListener
3. Provide the name of the class. The sample uses public class PdfConverterListener
An example of the sample code is as follows:
package your.package.name;
import java.io.File;
import java.io.OutputStream;
import com.emc.pdf.api.IPdfProcessorListener;
In this sample:
• <OutputStream> generates the output after the change is made by the library, code, or
logic.