Professional Documents
Culture Documents
Framework
Developer's Guide
Chapter 1 Introduction
Documentum Business Objects Framework is a new framework for developing and
executing reusable business logic components. This framework is built in to the
Documentum DFC and accessible from applications written using DFC.
New to DFC Version 5.1, the Documentum Business Objects Framework (DBOF)
provides a framework and a methodology to develop reusable middleware server
components for business logic called Business Objects. This chapter introduces
Documentum Business Objects and the benefits this technology provides. It contains the
following major sections:
• "Overview " on page 1-1
• "Business Objects in Perspective" on page 1-2
Overview
The Documentum Business Objects Framework (DBOF) provides an object oriented
framework for building, testing, discovering, and deploying server business logic as
Business Objects. In addition, it provides a way for developers to hook their own logic
into normal DFC procedures by extending DFC classes.
DBOF lives wherever DFC lives, either on the Content Server or application servers, or
on any of the other clients.
DBOF is part of the DFC library and consists of several Java classes and interfaces for
constructing and running Documentum Business Objects (DBO) (see Figure 1-1). There
are two types of Documentum Business Objects, type-based business objects, and
service-based business objects. A type-based business object can extend a Documentum
Server persistent object type (e.g. "com_accelera_catalog" or
"com_accelera_autonumber" might extend "dm_document") and extend its capabilities
by providing new methods for those types (actually, new user defined Java classes) and
allowing overriding of existing methods, like save(), checkinEx() and checkoutEx().
A service-based business object provides methods that perform more generalized
procedures that are not usually bound to a specific object type or even Docbase.
The specific business logic within the business objects is implemented using the standard
DFC framework allowing developers to implement a new DBO without a steep learning
curve. Since DBOF objects are based on the Documentum Foundation Classes (DFC),
the developer can maintain a high degree of compatibility with existing DFC
applications.
This framework was designed to provide the ability to develop pluggable components,
each component implementing one or more middle-tier business rules. Figure 1-2 shows
a diagram of DFC and which parts of it are related to DBOF. Notice that the DBO
framework is implemented mostly in the DFC core layer. Custom business objects can
hook in and extend parts of DFC. In the future, DFC might implement some of its own
business objects. Service-based business objects can call into DFC, but are not
considered part of DFC.
Data Layer
Content and Data Schema
• Content Object Hierarchy
• Aggregated Business Data
• Common Schema
• Presentation Layer
The presentation layer contains components dealing with user interfaces and user
interaction. It is used to display content and structured information in a web browser
or stand alone application. For example, the presentation layer of a stand alone
application could be written in Visual Basic or Java Swing. In a web environment,
Documentum WDK would be the choice to present the layout and to manage the flow
and sequence of user interactions. The presentation layer consists of commercial or
custom UI components that display information in different ways depending on such
factors as the user role or their privileges. For most implementations, this layer is
highly customized and must be very configurable. The presentation layer relies on
the components (business objects) of the business logic layer to implement and
enforce the rules and policies of the company.
• Business Logic Layer
The business logic layer is completely independent of visualization and of the toolkit
used to display information. It provides application specific generalization as a layer
of abstraction and implements the customer specific business rules and policies. It is
based on a component object model with components shared between different
application areas. In most cases, the policies and procedures of a given company are
not very likely to change greatly so the business object components implement those
policies and procedures. Alternatively, if a policy or procedure does change, the
implementer can just modify the corresponding business logic component and all the
dependent applications and components will automatically conform to the new
policy. The business logic layer is the focus of the Documentum Business Objects
Framework described in this document.
One of the major improvements of Documentum 5.1 is the ability to support both
statefull and stateless Docbase sessions. This allows the business object to focus on
the business rules and to leave the session management and transaction handling to
the framework.
• Data Layer
The data layer is used by the business logic layer to permanently store the state of the
objects, content, and other information. Central to the data layer is one or more data
servers that house the stored state. The Documentum implementation of the data
layer is comprised of one or more eContent Servers with their associated RDBMS
servers. The data layer is logically separated into two useful parts, the Content and
Schema Layer and the Core Content Management Services Layer.
• Content and Data Schema Layer
The data schema layer defines the content object hierarchy as well as the physical
schema used to store the aggregated business data. Creation and management of
the schema can be achieved using the Documentum Administrator utility or
directly by using DFC calls. The business objects in the business object layer
provide a logical abstraction of the details of the DFC interfaces to the content
and data schema. Therefore, the business objects can manipulate content and its
metadata on behalf of the presentation layer applications without relying on those
applications being aware of how the data and content is physically organized.
Separation of the schema from the business logic layer and the database layer
promotes the ability to scale to a different database and continue to use the
business objects and applications unmodified.
• Core Content Management Services Layer
The core content management services layer provides the library services, process
automation, and content authentication to the business objects. This layer is
implemented by the Documentum eContent Server. It provides the basic building
blocks to construct higher-level business logic components.
DBO Factory
Typed
Typed B.O.
B.O.
DBO
DBO
Registry
Registry
Docbase
DBO Factory
Typed
Typed B.O.
B.O.
DBO
DBO
Registry
Registry
Docbase
IDfClient interface. When requesting access to a service, the IDfClient checks the DBO
Registry for the service name and instantiates its associated Java class. The interface
provides methods to the actual service that implements the business rules of the service.
SBOs can access objects across several Docbases and are usually implemented such that
they can be called by an application server component and the service can obtain and
release the Docbase sessions dynamically as needed.
DBO Factory
Service
Service B.O
B.O
DBO
DBO
Registry
Registry
fc.client
DBO Factory
Service
Service B.O
B.O
DBO
DBO
Registry
Registry
Session manager
Whenever an application acquires any access to a Docbase, authentication is required.
Another improvement supplied by DFC 5.1 is "managed sessions" using the session
manager. It encapsulates the DFC session object and automatically manages the session
pool. It also provides the transaction handling and authentication of multiple identities on
multiple Docbases. For reestablishing a managed session, the session manager can hold
several Docbase login "identities", one for each Docbase or a "principal" identity, an
identity used by the service to authenticate the service as if the service is that principal
identity. It also provides methods to get access to the DFC session handle and hooks to
manage a session pool for stateless operation. It manages access to multiple Docbases
and controls transaction processing across multiple services. For convenience to the web
programmer, it also provides methods to associate state with the session to make session
management more straightforward.
The main requirement for an SBO is for it to request a DFC session through the session
manager and to release it as soon as it no longer needs to access a Docbase. With TBOs,
a reference to the TBO's session is embedded inside the object by default. This session
may become invalid if the requesting method releases the pooled session. There is a
special case where the DFC session can be "separated" from the current DFC session and
the TBO is put under session manager control. This is required in cases where a stateless
SBO creates a TBO, which is stateful, that is returned to the caller.
Session Pooling
Session pooling provides for an application to connect and disconnect from a Docbase
session many times without incurring the overhead of real sessions with the server. The
general idea is to release the session back to the session pool often when acting in a
stateless manner. The session manager does not disconnect the session with the server
when a client releases the session. Rather, the session is put back into the session pool,
free to be used by other requests for other sessions.
Client
uses uses
creates uses
implements
uses
IAutoName
IDfPrincipalSupport extends
implements IDfSessMgrStatistics
uses
DfService implements
uses
AutoName
extends
Figure 2-6 show a class diagram for a Type based Business Object. The custom Product
class extends the DFC DfSysObject class. The IProduct interface generalizes access to
the Product object. The client gains access to the Product object by using the
IDfSession.getObject(), which causes the IDfClient of the framework to query the
DBOR registry for the class to instantiate.
uses
uses
creates
uses
extends
implements
Summary
When developing with Documentum Business Objects Framework, it is important to
understand that DBOF is built-in to DFC 5.1 and is an integral part of DFC. Remember
that DBOF was primarily designed to provide an environment for stateless access to
Docbase data from middle-tier business objects. You should do your best to use the
session manager and release sessions as soon as possible. You can rely on the session
manager to manage connections and transactions with the Docbases. Using DBOF, you
can implement business logic in reusable business object components that can be plugged
in to your middle-tier applications.
used to calculate the next number. A new Docbase type will be created (e.g.
"com_accelera_autonumber" that is a subtype of dm_sysobject) that will have an
attribute, "current_number", storing the value of the last number generated as a seed
for the next number in that numbering type. (Note: Accelera.com is a fictitious
domain name of a fictitious conglomerate that Documentum created for
demonstration purposes.)
° In addition, a business rule might impose a restriction on the maximum value of a
number for a given numbering type, or that the number is zero-filled to a certain
number of digits.
° Different numbering sequences, like purchase orders and invoice numbers, would
need a separate storage for each sequence since it is usually not desirable for to
sequence numbers as, PO001, INV002, PO003, INV004.
° Additional rules might require that purchase order numbers are prefixed with "PO",
whereas invoice numbers begin with "INV". In this simple example, this will be
handled by the user of the IAutoNumber interface. A future subclass of the
autonumber type could handle as well.
° The application of these business rules and validation would need to be performed
each time the value for the "next number" would be calculated, stored, and
fetched. This is accomplished by overriding the methods of dm_sysobject like
checkin(), checkout(), save(), etc. that would be used to retrieve the number,
increment the number, and save the object.
(A more elaborate kind of sequencing described above is the subject of a sample
business object service, called "AutoName", that accompanies DFC 5.1 but is too
complex to present in this manual.)
2. Service-based business object (SBO), the subject of the Simple_SBO example, will
provide a simpler interface to obtaining the correct centralized number and saving it
to the TBO. It provides methods for getting the value stored and saving the next
number back into the Docbase. These methods can hide the details of locking and
unlocking the TBO objects, fetching and saving the other attributes of the TBO
objects, and so on.
° The most important method of this service is the getUniqueNumber() method that
fetches and increments the number according to the number type (PO, INV, etc.).
The other methods of this service are mainly for management purposes.
° Other methods will include setNumber(), used internally to set the initial number
value for a given number type and to save the calculated value back to the
Docbase.
° A fully functional service of this type will be designed generically enough to
provide number formatting patterns and other filters.
«interface»
IDfBusinessObject
DfPersistentObject +getVersion()
+getVersionString()
+isCompatible()
+supportsFeature()
DfService
«interface»
IAutoName
+getVersion()
+getNextNumber() DfSysObject «interface»
+getVendorString()
+isCompatible() IAutoNameType
+checkin() +getUniqueNumber()
+save() +setNumber()
AutoNameType «datatype»
AutoName Docbase Type:
-type_name
-type_name com_accelera_autonumber
-current_number
-current_number
-max_number
-max_number
+getUniqueNumber()
+getNextNumber()
+setNumber()
+checkin()
«uses»
+save()
#checkRange()
#validate()
-setNextNumber()
Introduction
As described in the "Solution: Documentum Business Objects" section, this Simple_TBO
program will be an application that fetches the Docbase object containing the existing
number, increments it, and saves the newly incremented value back to the Docbase. In
order to accomplish this, the following must be implemented:
• Assume that this example will work with a variety of separate, distinct numbering
sequences.
• A new Docbase type must be created, called "com_accelera_autonumber", that is a
subtype of the Docbase "dm_sysobject" type. This new type defines the schema for
the different numbering schemes, called numbering types in this example.
• Each com_accelera_autonumber object will store in its "current_number"
field/attribute the last value returned from the getUniqueNumber() method. This
"current_number" value will be used to calculate the next number in the sequence
defined for that numbering type. The object will also store any other fields that
require persistence for this example.
r_object_id name type_name current_number max_number
090000000101a8b7 Purchase Order PO 590124 99999999999
090000000101a8cb Invoice INV 30822 99999999999
import com.documentum.fc.client.*;
import com.documentum.fc.common.DfException;
2.1. Create a new class, AutoNumberType, and extend the desired DFC class that
provides the closest behaviors and features to your needs. For example,
extending DfSysObject allows your objects to inherit nearly 500 public methods
import com.documentum.fc.client.*;
import com.documentum.fc.common.*;
3.2. Since the AutoNumberType class implements the IDfBusinessObect class, and
the IDfBusinessObject interface defines four abstract methods, your new class
must implement overrides for each of the four methods, getVersion(),
getVendorString(), isCompatible(), and supportsFeature(), as follows:
/* AutoNumberType.java
*/
package com.accelera.autonumber;
import com.documentum.fc.client.*;
import com.documentum.fc.common.*;
return true; }
}
The save() and checkinEx() methods were chosen because they are the points
at which the validation of the business rules need to be applied. The service, and
any other user of the Docbase object will modify it using checkout() / save() or
checkout() / checkin() combinations. Intuitively, you might have chosen the
checkin() method instead of the checkinEx() method. However, in this case,
all the DfSysObject.checkin() method does is call the checkinEx() method. So
we have chosen to implement the checkinEx() method instead.
(Note: Sometimes you call the superclass implementation first, and other times you call
it after your extending code. There will even be times where the superclass
implementation should not be called at all.)
public void save() throws DfException
{
validate();
super.save();
}
The checkin() method is final so you cannot override it. This is because the checkin()
method just calls checkinEx() polymorphically. There is no reason to validate the
object twice. The checkinEx() method performs a check in with additional application-
specific attributes.
public IDfId checkinEx( boolean fRetainLock,
String strVersionLabels,
String strOldCompoundArchValue,
String strOldSpecialAppValue,
String strNewCompoundArchValue,
String strNewSpecialAppValue ) throws DfException
{
validate();
return super.checkinEx( fRetainLock,
strVersionLabels,
strOldCompoundArchValue,
strOldSpecialAppValue,
strNewCompoundArchValue,
strNewSpecialAppValue );
}
Note: Certain DFC methods forward their calls to other methods. When overriding
methods, be sure to consider whether the method forwards its call to another method to
avoid performing the same override twice. With this in mind, DFC has declared some of
these methods as final to help you avoid overriding the method unnecessarily. This
includes methods like IDfSysObject.checkin() and checkout(), getContent(), and
several others.
Due to the fact that the three methods on lines 22, 23, and 24, listed below need to
perform a checkout(), modify attributes, and save() as an atomic operation,
they need to be part of a managed transaction. Since nesting session transactions
is not supported, the setNumber() method throws an exception if either the caller
has not supplied a managed transaction. The code on lines 13-18 performs this
test. In other words, you need to make the user supply a transaction for this
operation to function correctly. If the setNumber() fetches the next number, but
it is not saved back into the Docbase, the next person calling getUniqueNumber()
will get the same number. This is against the specified business rules.
5.2. There are two methods related to the implementation of the business rules for this
type of object, validate() and checkRange(). The checkRange() method
checks a numeric value for validity before attempting to save it into the Docbase
object. The validate() method fetches the "current_number" from the object
and forwards it to the checkRange() method for validation. Either of these
methods will throw an exception if the number does not follow the business
rules.
protected void validate() throws DfException
{
String s = getString( "current_number" );
checkRange( Long.parseLong(s) );
}//end validate()
number.setObjectName( strAutoNumberType );
number.setString( "number_type", strAutoNumberType );
number.setString( "max_number", strMaxNumber );
number.setNumber( strStartingNumber );
number.save();
7.2. Use the IDQL tool to use DQL to manually create the new
com_accelera_autonumber type to a Docbase.
create type com_accelera_autonumber (number_type char(128), current_number
char(18), max_number char(18)) with supertype dm_sysobject
com_accelera_autonumber=type,com.accelera.autonumber.AutoNumberType,1.0
This registers the new business object with Documentum 5.1so that any time any
Docbase objects from this new table are fetched, the Docbase object is used to populate
the AutoNumberType object.
(see Caution regarding manually editing the DBOR on page 3-2.)
You should write a program the uses the new IDfDbor and IDfDborEntry interfaces to
register the new AutoNumberType business object.
dbor.register( entry );
System.out.println( "New dbor registry entry successfull" );
}
catch( Exception e2 )
{
System.out.println( "Unable to map dbor entry" );
System.out.println( e2.toString() );
e2.printStackTrace();
bRetVal = false;
}
}//end catch( DfDborNotFoundException )
return bRetVal;
}
Editing the DBOR by hand is strongly discouraged. Doing so could create serious
runtime problems with the other Documentum based applications on your system.
If the dbor.properties file does not exist, the register() method will cause one to
be created.
9. Deploy and Set up the new Business Object
Once the program successfully compiles, it needs to exist in the CLASSPATH or
installed into the Java Extension Library.
Optionally, you can pass a CLASSPATH as a command-line argument as follows:
java -cp "%classpath%" Simple_TBO DocbaseName "PO" username password [domain]
import java.util.Collection;
import com.documentum.fc.client.*;
import com.documentum.fc.common.*;
import com.documentum.com.*;
import com.accelera.autonumber.*;
3. Write the main() and begin() methods and instantiate the Simple_TBO class. This
involves parsing the command line arguments into variables, instantiating the
Simple_TBO class to prevent writing all the methods and variables as static,
establishing a session manager identity, and calling the test routine.
3.1. In the begin() method, construct a new session manager object using the
newSessionManager() method of IDfClient. Then register a user's credentials
as an identity in the session manager, using the
IDfSessionManager.setIdentity() method.
IDfClient client=null;
client = DfClient.getLocalClient();
IDfClientX clientx = new DfClientX();
IDfLoginInfo login = clientx.getLoginInfo();
login.setUser( m_strUserName );
login.setPassword( m_strPassword );
test( sMgr,
m_strDocbaseName,
m_strAutoNumberTable,
m_strAutoNumberType );
}
catch( Exception e )
{
DfLogger.error(this, e.toString(), null, e);
}
}
return true;
}
// GOOD EXPERIMENT!
// Find the r_object_id of the AutoNumberType record and use it in
// the query below as follows:
// String strDQL = "dm_sysobject where r_object_id = '09....'";
number = (IAutoNumberType)
session.getObjectByQualification( strDQL );
number = (IAutoNumberType)
session.getObjectByQualification( strDQL );
}
sMgr.abortTransaction();
// failed to create or update
if( e instanceof DfException )
throw (DfException)e;
Executing this command line several times for different numbering types will prove that
the service is producing sequential numbers by type:
Figure 3-3 - Run the test TBO program
Introduction
As described in the "Solution: Documentum Business Objects" section, this Simple_SBO
program will be an application that fetches the next sequence number according to the
specified number type in a given Docbase and increments it. In order to accomplish this,
the following issues must be implemented:
• Assume that this example will work with a variety of separate, distinct numbering
sequence types ("PO", "INV", etc.).
• A new DBOF service interface will be created, called IAutoNumber, which is
implemented by the AutoNumber class, and provides the public access to the
numbering service for the given types.
• A Simple_SBO test application will be created to call the service and fetch the next
number in sequence of the given type if an instance of that type exists in the Docbase.
Trying it from different computers will still obtain the correct number in sequence.
1.2. In the IAutoNumber interface, prototype the minimal number of methods that
abstract the details of the service. In this case, you will prototype the
getNextNumber() method only.
/* IAutoNumber.java
*/
package com.accelera.autonumber;
import com.documentum.fc.client.*;
import com.documentum.fc.common.DfException;
2.2. In the AutoNumber class, add the String constants that will be used for the
getVersion() and getVendorString() methods.
public static final String strCOPYRIGHT =
"Copyright (c) Documentum, Inc., 2002";
public static final String strVERSION = "1.0";
2.3. Since the AutoNumber class extends the DfService class, and the DfService
class defines three abstract methods, your new class must implement overrides
for each of those methods, getVersion(), getVendorString(),
isCompatibile() as follows:
/* AutoNumber.java
*/
package com.accelera.autonumber;
import com.documentum.fc.client.*;
import com.documentum.fc.common.DfException;
try
{
session = getSession( strDocbase ); //managed session
try
{
IAutoNumberType numType = (IAutoNumberType)
session.getObjectByQualification( strDql );
}
finally
{
releaseSession( session );
}
if( bNewTransaction )
sMgr.commitTransaction();
}
catch( Exception e )
{
if( bNewTransaction )
sMgr.abortTransaction();
}
return strRetVal;
}
4. Map the new IAutoNumber service interface to the AutoNumber service class.
Better known as "registering the service with the DBOR", you need to map the interface
to the class so that the newService() method will know how to construct an AutoNumber
object as opposed to any others. This registration can be done manually, or you can write
a program to perform the installation of the service.
4.2. More appropriately, you should allow IDfDbor do this work since it will not
allow duplicate entries. Writing a method that calls the
mapDocbaseTypeToClassName() method on page 3-16. The beginInstall()
method below is designed to register either TBO or SBO entries. If it is called
with bService set to true, then the strNewDocbaseType parameter represents the
service interface (e.g. com.accelera.autonumber.IAutoNumber).
private boolean beginInstall( IDfSessionManager sMgr,
boolean bService,
String strDocbaseName,
String strNewDocbaseType,
String strJavaClass )
{
boolean bRetVal = true;
IDfSession session = null;
try
{
//setupLog();
session = sMgr.getSession( strDocbaseName );
if( ! bService )
{
if( addDocbaseType( sMgr, session, strNewDocbaseType ) )
{
bRetVal = mapDocbaseTypeToClassName( session,
bService, // TBO
strNewDocbaseType,
strJavaClass );
addDocbaseFolder( session, strNewDocbaseType, "/System" );
}
}
else
{
bRetVal = mapDocbaseTypeToClassName( session,
bService, // SBO
strNewDocbaseType,
strJavaClass );
}
}
catch( Exception e )
{
bRetVal = false;
}
finally
{
if( session != null )
sMgr.release( session );
}
return bRetVal;
}
5.1. Once the program successfully compiles, it needs to exist in the CLASSPATH or
installed into the Java Extension Library. Optionally, you can pass a CLASSPATH
as a command-line argument as follows:
java -cp ".;%classpath% " Simple_SBO accelera "INV" username password, domain
import java.util.Collection;
import com.documentum.fc.client.*;
import com.documentum.fc.common.*;
import com.documentum.com.*;
import com.accelera.autonumber.*;
3. Write the main() methods and instantiate the class. This involves parsing the
command line arguments into variables, instantiating the Simple_SBO class to prevent
writing all the methods and variables as static, establishing a session manager
identity, and calling the test routine.
3.1. Construct a new session manager object using the newSessionManager()
method of IDfClient. Then register a users credentials as an identity in the
session manager, using the IDfSessionManager.setIdentity() method.
}
catch( Exception e )
{
System.out.println( e.toString() );
e.printStackTrace();
}
}
3.2. Parse the command line and save the values into the private variables.
}
catch( Exception e ) // array bounds exceptions, etc.
{
System.out.println( "Error parsing command line arguments.\n" );
System.out.println( "Usage: java Simple_SBO " +
"<docbase> <newAutoNumberType> " +
"<user> <password> [<domain>]\n\n" );
System.out.println( "Be sure that the following DMCL.INI file " +
"has the following settings:" );
System.out.println( " [DMAPI_CONFIGURATION]" );
System.out.println( " connect_pool_enabled=T\n\n" );
e.printStackTrace();
}
return true;
}
3.3. Write the test() method that accesses the type-based object, gets the next
number from the TBO, and instructs it to increment itself as follows.
3.3.A. Note the use of the getSession() method that obtains a session from the
session manager. This method is supplied by the DfService class and will
return the session created by the session manager.
try
{
String s = IAutoNumber.class.getName();
svcNumber = (IAutoNumber)client.newService( s, sMgr );
}
finally
{
sMgr.release( session );
}
}//end test()
Executing this command line several times for different numbering types will prove that
the service is producing sequential numbers by type:
DborManage utility included in dfc.jar. In absence of this tool, the DBOR properties file
can be viewed and modified using a standard text editor such as Windows notepad.exe or
vi on Unix. It follows the standard Java properties file syntax. Each line is a
configuration item, and comment lines start with '#'. It is allowed to have empty lines
between configuration items.
You can modify the DBOR programmatically using the IDfDbor interface. The IDfDbor
interface will guarantee that no duplicate entries exist.
A DBOR item uses the following configuration syntax:
Item
(Interface or Docbase Type) Type Java class Version
IProduct service com.documentum.services.Product 1.0
com.documentum.services. type com.documentum.services.
IProductType ProductType
For example, use the DborManage tool to register the IProduct service:
java DborManage register com.documentum.services.IProduct service
com.documentum.services.Product 1.0
Where the DBOR entry fields correspond to similar IDfDborEntry setters and getters:
DBOR IDfDborEntry
Field Method Description
name setName() The item being bound or mapped to a Java class.
getName()
The item is a service name (SBO), a Docbase type
name (TBO) for example, "my_sop", or the name
of an interface mapped to the same Docbase type
as another class. For service based business
objects (SBOs) use the fully qualified Java class
name of the service interface. (example:
IAutoName.class.getName() returns
com.documentum.services.AutoName)
type setServiceBased() Specifies either "type" or "service". The keyword
getServiceBased()
for TBO is "type", for SBO it is "service"
java setJavaClass() The fully qualified Java class name that
class getJavaClass()
implements the TBO or SBO. The Java class must
be located within the current CLASSPATH of the
JVM. Note: Java does not allow mixing classes
from different class loaders. For example, a
DBOR call will fail if the dfc.jar file is referenced
in the system CLASSPATH environment (e.g.
using standard DFC as installed with the DFC
installer) but the Java class that implements the
SBO or TBO is located in a WAR file. Therefore,
The following is sample output from the DborManage utility showing examples of valid
DBOR items:
TYPE Docbase type : com_accelera_autonumber
mapped to class : com.accelera.autonumber.AutoNumberType
Version : 1.0
Caution: Documentum does not support the remapping of any standard "dm_" .Docbase
types.
DBOR Deployment
A valid copy of the DBOR properties file must be deployed to every computer where
TBOs and SBOs are used in a DFC-based application. For example, if a TBO or SBO is
used in Desktop Client customizations, the DBOR file must be deployed to all those
computers running the customizations. For WDK, ASP, and JSP environments, the
DBOR needs to be deployed on the application server computers. The DBOR properties
file must be located in the file system in a directory defined with DFC_DATA.
The Business Object developer is responsible for the following:
• Report an error if DFC 5.1 or above is not installed. The DBOF framework does not
work on DFC 4.2 or earlier systems.
• Copy the JAR or WAR file (archive) of the Business Objects to the file system.
Either copy the archive into the JRE/lib/ext directory of the JVM, or modify the
CLASSPATH so that the applications can find the Java classes in the archive.
• Handle Business Object version issues. Potential version conflicts can occur when
the JAR file of an existing BO is merely replaced with a new one.
• The DBOR registry must be updated to map the Business Object Java classes to their
associated interfaces or Docbase types.
• For TBOs, the new Docbase type must be added. The actual Docbase records of the
new Docbase type are usually added later.
• Constructing and deploying a COM interface. The classes and the methods of the
business objects are available to COM through the IDispatch interface. This is only
required if the business object adds new methods to a TBO. You could also create an
IDL file and/or a TypeLib file (.TLB) but that approach involves more manual work
for the programmer.
DBOR Management
The IDfDbor Java interface not only provides lookup functions but also provides
methods to add and remove items from the DBOR properties file. This functionality is
intended for tools that need to automatically modify the DBOR such as installers or
convenience tools for the developer.
A reference to the DBOR can be obtained through the IDfClient.getDbor() method.
There are five methods available from DBOF that can be used by DBOR configuration or
management tools:
Dbor Method Description
register() Registers a new "service" or "type" in the DBOR. This method
maps the service interface or the Docbase type to a Java class. Pass
a configured IDfDborEntry object to this method. An entry can
only be registered if it does not already exist. Otherwise, an
exception is thrown.
unregister() Removes the matching entry from the DBOR registry file.
lookupService() Given the entry name (left of the equals), returns the fully qualified
class name of the Java class of the service.
lookupObject() Given the entry name (left of the equals), returns the fully qualified
class name of the Java class for this TBO.
getAll() Gets a list of all entries (service based and type based) in the
registry.
The following Java sample code shows how to configure the DBOR entry if it does not
already exist:
// You should get here...the service should not exist if you're adding.
if( !bAlreadyExists )
{
try
{
strLookup = dbor.lookupObject( strDborName );
bAlreadyExists = true;
}
// This catch list eats all the exceptions because it expects
// that any of the following conditions should still allow us
// to continue
catch( DfDborNotFoundException e )
{ // dbor.properties file not found
}
catch( DfServiceCriticalException e )
{ // "type" found, but it's a "service"
}
catch( DfServiceNotFoundException e )
{ // type not found
}
}
if( bAlreadyExists )
{
System.out.println( strDborName +
" already mapped to " + strLookup );
} else
{
// You should expect to get here...
try
{
String s;
// if not already registered...
IDfDborEntry entry = new DfDborEntry();
entry.setName( strDborName );
entry.setServiceBased( bDborService );
entry.setJavaClass( strJavaClass );
entry.setVersion( strVersion );
dbor.register( entry );
System.out.println( "Successful:" );
dbor = null; // unlock the dbor so lookup() can use it.
beginList( strDborName ); // displays the entry
bRetVal = true;
}
catch( DfServiceCriticalException e2 )
{
// You get here if MSG_DBOR_NOT_DEFINED or MSG_SERVICE_EXISTS
System.out.println( "Unable to map dbor entry" );
System.out.println( e2.toString() );
e2.printStackTrace();
bRetVal = false;
}
catch( DfServiceException e2 )
{
// You get here if there is an (DM_VEL_DBOR_IO_ERROR) error
// on the registry.
System.out.println( "Unable to map dbor entry" );
System.out.println( e2.toString() );
e2.printStackTrace();
bRetVal = false;
}
}
return bRetVal;
}//end mapBusinessObjectToClassName()
Error Handling
Applications access the DBOR indirectly for lookup or directly for registration or
removal of services. There are several scenarios where one or more operations may fail
for a number of reasons. There are cases where standard exceptions are thrown and there
are some cases where Java runtime exceptions are thrown. The Java exception
mechanism is used to indicate problems with DBOR access.
The following error handling is implemented for DBOR access:
• DBOR properties file not present in expected location: Check the DFC_DATA
environment variable or the "dfc.data" system property.
• DBOR properties file is there but the item is not configured: For TBOs that are
retrieved using getObject()-like methods, the default behavior will ignore this
condition when the DBOR can't be found. This may cause some problems when
accessing the TBO later. This should not be a problem if only standard DFC
classes such as IDfPersistentObject, IDfFolder are used. For SBOs that or
obtained using the IDfClient.newService() factory method, an exception will
be thrown because there is no fallback mechanism. The exception thrown is of
type DfDborNotFoundException and the application should catch this exception
to handle this scenario. The factory method only indicates that newService()
will throw a DfServiceException, which is a supertype of the real exception,
DfServiceInstantiationException. If the exception message refers to a
"class not found" condition, the item might not be registered correctly or the
classpath is not correct.
• DBOR item not configured properly: For both TBOs and SBOs, if the DBOR
properties file contains an item that does not follow the specified syntax, an
exception of type DfServiceCriticalException is thrown. This is an
unchecked Java runtime exception (RuntimeException). This can occur due to
manual entry or corrupt contents to the registry. This will manifest itself when
registering a service with the same name as an entry that already exists as a type.
It will also occur when the lookupService() method finds a matching entry but
it is a "type" instead. Likewise, this will occur when the lookupObject()
method finds a matching entry but it is a "service" instead.
• Problems with DBOR registration: Always throws
DfServiceCriticalException, which is an unchecked Java runtime exception.
This can include DBOR not found, cannot write to file, or service already
configured exceptions.
• Problems with DBOR unregister(): Throws either a
DfServiceCriticalException or a DfServiceException. The
DfServiceException indicates there was a problem performing IO on the
registry. The DfServiceCriticalException indicates bad arguments to the
method, such as an empty name. The DfServiceCriticalException is a runtime
exception and does not show up in the "throws" clause of the method signature.
Please refer to the DFC manual for all the options for configuring the client.
Essentially, when session pooling is enabled, disconnecting from and releasing the
session does not actually disconnect the session. The pooling mechanism leaves the
session active to be used later.
With managed sessions, the session manager does not immediately put released sessions
back into the session pool. Instead, released sessions remain available for reuse for a
small amount of time.
Session Timeout
The session manager does not provide methods to adjust the timeout handling for the
client application. A DFC session will not timeout and recycle to the session pool as long
as its session manager object lives and the DFC session is not released by a server
method. However, it is still possible for a server side session to expire without the client
application being aware. The session manager transparently always guarantees that a
given session is available for its clients. Therefore, the client could hold on to a session
manager object forever if it needed to and at any time it would always be able to
reconnect managed sessions that had been released.
During idle time, the session manager will release session manager sessions and save
them in the session pool to be reused by other session managers that may require a
Docbase connection.
Once the session manager is no longer needed, it can be discarded without any special
action. For example, if the session manager is stored in an HTTP session object, there is
no need for additional cleanup work once the session object has expired. However, there
are a number of issues that you must take into account. In order for the session manager
to be discarded there must not be pending sessions. The following scenarios must be
avoided as they are programming errors:
• sMgr.beginClientControl() with no matching end method
At first glance, it would seem that since the persistent object, doc, has a getSession()
method to retrieve the session through which the object was fetched, would fetch a valid
session. In this case, the retrieved session might be stale. Even f the session were not
released, the session could still be stale due to a timeout.
Prior to session manager, this would never have been expected to work since it was never
appropriate to touch an object after its session was released. Since DFC 5.1 recommends
releasing sessions frequently back to the session pool, this kind of logic could be
inappropriately performed within a service call if the service developer does not
completely understand this issue. The key point is that sometimes line 12 works and
sometimes it does not. It works if the session manager has not yet timed out the session
that was released on line 5. Setting the DebugSessionManager system property will
invalidate the session immediately upon release. In which case, line 12 would always
fail, thereby uncovering the faulty logic.
To assist with debugging managed session timeout issues, define the
DebugSessionManager Java Virtual Machine system property when you start your
program or add the property to you Java debugger environment.
java -D DebugSessionManager -classpath %classpath% YourProgram
When this system property is defined, the session manager times out the sessions and
recycles them as often as possible to assist you in locating logic errors.
Multiple Docbases
The session manager is able to manage connections to different Docbases transparently.
For this behavior, the client application must register different identities (IDfLoginInfo
/ Docbase pairs) for the different Docbases, in which case the session manager will use
the appropriate IDfLoginInfo object when connecting to a given Docbase.
The following example shows calling the same service for different Docbases:
IDfClient client = DfClient.getLocalClient();
IDfSessionManager sMgr = client.newSessionManager();
sMgr.setIdentity( strDocbase1, loginInfo );
sMgr.setIdentity( strDocbase2, loginInfo );
anyService.call(strDocbase1);
anyService.call(strDocbase2);
When the "Principal identity" mechanism is used, the principal name must be the same
name on all Docbases. Furthermore, principal support must be enabled. The following
example shows calling services in different Docbases using a principal:
sMgr.setPrincipalName( principal );
anyService.call(strDocbase1);
anyService.call(strDocbase2);
Note now the Principal Name was only set once but two different Docbases are accessed.
Managed Sessions
Service Implementation
This section presents Session Manager related considerations for the implementation of
service based business objects. Please refer to Chapter 6: "Service Based Business
Objects" for details on implementing them.
Overview
When implementing a service BO, the session manager is inherently available to the
service methods. The session manager object is a member of the DfService class that
you extend and can be accessed from any method of a service. In most cases however,
the implementation does not directly use the session manager but uses the two
convenience methods in DfService (getSession(), and releaseSession()) which are
used to obtain a DFC session handle and return it when finished.
Requesting a DFC Session
For services, the DFC sessions are not typically requested directly from the session
manager but through the service parent class, DfService, using the inherited
getSession() method. The methods of the service then use normal DFC calls to
execute the service functionality (business rules). To request a new session, use the
sMgr.newSession() method. For details on requesting a DFC session, please refer to
the section IDfService and DfService objects in Chapter 6: Service Based Business
Objects.
Releasing a DFC Session
When a service has finished processing (usually before the service method returns), the
DFC session object MUST be returned to the session manager even if a program error
occurs. Otherwise, the session pool becomes stalled. Use Java's try/finally
termination handler to guarantee the release of the session object. A catch statement is
not mandatory.
The following example shows a service that requests a session, performs some action,
then returns the session back to the session manager.
When you need the flow of a program to continue when transaction errors occur, use the
sMgr.setTransactionRollbackOnly() method to ensure transactions will be aborted
without throwing an exception; even if the calling program attempts to commit their
transaction. Use the setTransactionRollbackOnly() method as you would use
sMgr.abortTransaction(). Any attempt to commit that transaction afterward will be
ignored and no error will be generated. In other words, the owner of the transaction
would not be aware that one of its method calls aborted the transaction for it unless it
calls the getTransactionRollbackOnly() method, which returns true if some part of
the program ever called setTransactionRollbackOnly(). The way it actually works is
that when the sMgr.commitTransaction() method is eventually called, internally it is
redirected to sMgr.abortTransaction(). A key point about this is that
setTransactionRollbackOnly() does NOT throw an exception, so the program
continues as if the batch process is valid.
When more than one thread is involved in session manager transactions, calling
sMgr.beginTransaction() from a second thread causes the session manager to
automatically create a new session for the new thread.
try
{
IDfPersistentObject obj = session.getObject( idDoc );
obj.checkout()
modifyObject( obj );
obj.save();
}
catch( Exception e )
{
// setTransactionRollback() acts like an abortTransaction()
// so that even if they commit after this, this checkout()
// and save() are omitted.
setTransactionRollbackOnly();
throw new DfException();
}
}
Transaction Handling
The session manager supports transaction handling across multiple services. The session
manager handles session pooling details and prevents sessions from being disconnected
or released while transactions are pending.
When session pooling is used, it is possible that DFC sessions could timeout and be
recycled back to the pool between individual service calls. For example, let’s say there
are two services: one service creates a few folders and then the second service stores
some documents in these folders. To make sure the folders are only created when all the
document creation has succeeded there must be one transaction around the two service
calls. The DFC session transaction is bound to one DFC session so it is important to use
the same DFC session across the two services calls. Each service performs its own
atomic operation. At the start of each operation, they request a DFC session and at the
end they release this session back to the session pool. In between these calls, the service
session could have been recycled back to the session pool and released, due to a session
timeout. Calling session.beginTrans() / session.commitTrans() /
session.abortTrans() could fail.
To "lock" a managed session to prevent it from being recycled due to timeouts, the
session Manager defines methods that allow transactions to work across multiple service
calls and even across Docbases. The Session Manager automatically determines when a
session can be put back into the pool. A managed session will not be recycled back into
the session pool as long as there is a pending transaction.
The sMgr.beginTransaction() method is used to start a new transaction,
sMgr.commitTransaction() commits all changes to the Docbases and ends the
transaction lock. In case of an error, sMgr.abortTransaction() is used as a rollback to
reverse any changes made so far in the transaction. You must call getSession()
AFTER beginTransacion() or the session object will not be able to participate in the
transaction.
Use the isTransactionActive() method to ask whether a "managed transaction" has
already begun and the session manager currently has a transaction active that you can
join. (You would not want to interfere with an outer transaction by blindly attempting to
start a nested transaction, which is not allowed).
The implementation of the transaction mechanism handles the following issues:
With multiple threads, transaction handling operates on the current thread only. For
example, if there is an existing DFC session for one thread, a new DFC session is created
for the second thread automatically. This also means it is not possible to begin a
transaction in one thread and commit it in a second thread.
The transaction mechanism guarantees that the Session Manager provides a separate DFC
session for each thread that calls beginTransaction(). For those threads that already
have a DFC session before the transaction begins a transaction, a new DFC session is
provided and required to be used once beginTransaction() is called.
When a client starts a transaction using the sMgr.beginTransaction() method, the
Session Manager will not allow any other DFC-based transactions to occur (neither
session.beginTrans() nor sMgr.beginTransaction()). It is not possible to nest
transactions.
The following example illustrates a client application calling two services that must be
inside a transaction in which case both calls must succeed or nothing is done:
sMgr.setIdentity(docbase, loginInfo);
IMyService1 s1 = (IMyService1)
client.newService(IMyService1.class.getName(), sMgr);
IMyService2 s2 = (IMyService2)
client.newService(IMyService2.class.getName(), sMgr);
s1.setDocbase( strDocbase1 );
s2.setDocbase( strDocbase2 ) ;
If either of these service
sMgr.beginTransaction(); methods throws and exception,
try
{ commit is bypassed and abort
s1.doDocbaseUpdate(); is executed.
s2.doDocbaseUpdate();
sMgr.commitTransaction(); Each of the
}
catch (Exception e) doDocbaseUpdate() methods
{ call sMgr.getSession().
sMgr.abortTransaction();
}
An interesting point to note in the above example is that the two services are updating
different Docbases. The session manager, internally, automatically handles the real
sessions and their real Docbase transactions (which translate to RDBMS transactions).
Since the session manager is aware of all these Docbase handles within its own scope,
committing or aborting the managed transaction causes the session manager to loop
through its real session list and commit or abort those. The session manage supports
transactions in this manner across multiple Docbases. However, if, for example, there are
three Docbases, and the first two commit successfully, but the third does not update
correctly during its commit, the session manager will not be able to undo the already
committed transactions on those Docbases. Doing so would be a "two-phased commit"
and is not supported by DFC.
Client Control
Client Control is a mechanism used to control how the session manager handles session
pooling.
Session manager sessions are managed by the session manager object. The client
application is not able to directly control the lifetime of those sessions. (There are no
methods to increase or decrease the timeout). All it can do is to set the identity and then
either authenticate immediately or leave it to the service methods to obtain a session for a
given Docbase.
The client can control the session pool indirectly using either the transaction mechanism
(that locks the session from being recycled to the session pool until commit or abort) or
the "Client Control" mechanism (using the beginClientControl() and
endClientControl() methods). As long as a transaction is active or the session pool is
locked, the session is not recycled. For transaction management this is required because
a transaction cannot work across different sessions. Client control is needed when you
need to prevent the session manager from recycling and disconnecting a session that is
still in use even if the service has already released the session.
A typical scenario for client pool locking is when a service that is called returns a typed
DFC object (e.g. IDfCollection) which needs to be manipulated by its caller. DFC
persistent objects internally store a reference to the session with which they were fetched.
If the session is still valid, the session can be reacquired directly from the object using the
IDfPersistentObject.getSession() method. You cannot always depend on this
session with managed sessions. However, if you have a session manager, it is best to
acquire the session from that
For objects that will be used in this manner, there is a need to lock the session until the
typed object is discarded. In this case, the sMgr.beginClientControl() method is used
prior to calling the service and accessing the DFC object.
When the client application is finished with this object it must call the
endClientControl() method to re-enable session manager recycling.
The following example shows a service call that returns a collection of objects to be
displayed. Client Control is used to ensure that the session associated with the collection
is not recycled while the object names are printed out:
IDfClient client = DfClient.getLocalClient();
IDfSessionManager sMgr = client.newSessionManager();
sMgr.setIdentity(docbase, loginInfo);
IDfSession session = null;
sMgr.beginClientControl();
try
{
session = sMgr.getSession( strDocbase1 );
coll.close();
}
finally
{
sMgr.release( session );
sMgr.endClientControl();
}
NOTE: Since you are required to guarantee that endClientControl() is always called
after calling beginClientControl(), call endClientControl() within a finally
block. Additionally, when an exception is thrown while a call is open, you must make
sure the session is released. Use the Java finally block to ensure the client application
control is switched off when the client application is done or in the case of an exception.
Calls to beginClientControl() can also be nested as long as there is a matching
endClientControl() for each beginClientControl().
When a program performs a query or accesses an object, it currently requires that you
obtain a session and apply that session to any subsequent calls requiring authentication
and operations on a content server. This can pose a problem for middle-tier application
servers involving the maintenance of that session information between HTTP requests.
When a DBOF service returns a persistent object, the session manager must maintain the
session in case access to the object is needed.
DFC 5.1 separates the dependency of persistent objects from a session. This involves a
new method to the IDfTypedObject interface called setSessionManager().
When the setSessionManager() method is called, the entire object is fetched from the
server and its attributes are cached on the Java side. This allows the session manager to
use that same session for other operations on the application server. When modifications
are made to the cached persistent object, they are made to the local cache. When the
object is saved or checked back in, the session manager supplies a session for that
operation.
Use setSessionManager() judiciously. Because a "fetch" of the entire object is
performed in order to cache the object to the Java side, it could be very costly. This
should only be considered when a DBOF service object returns a persistent object.
A new class, DfCollectionEx has been added for your convenience. The DfCollectionEx
class can be used when returning a collection of typed objects from a service.
DfCollectionEx locks the session until the collection is closed with the
DfCollectionEx.close() method.
Statistics
For testing or performance tuning purposes, the session manager provides an interface for
querying the special session manager state parameters such as reference counters, number
of sessions, Docbases currently connected, etc. Specifically, use the
sMgr.getStatistics()method to retrieve an IDfSessionManagerStatistics object
that contains the state information. The statistics object takes a snapshot of the session
manager internal data when getStatistics() is called. This means that the data may
not be accurate by the time you look examine the data.
The IDfSessionManagerStatistics provides the following information:
• Session pooling mode (e.g. enabled/disabled)
• Whether there are active sessions
• Get a list of all Docbase names that have active sessions or identities
• Get a list of all IDfLoginInfo identities for a given Docbase (passwords are
removed)
• Get a list of all active DFC IDfSession objects for a given Docbase
• Get reference count for a given Docbase session object
Error Handling
There are different scenarios for error handling in session manager, including:
Authentication
The Session Manager supports two different ways for it to connect to sessions on your
behalf. The first is relatively straightforward. You register a Docbase name you would
like the session manager to connect you to in the future. Along with the Docbase name,
you supply a typical IDfLoginInfo object that specifies the user name and password the
session manager should use to connect you to that specified Docbase. This Docbase /
IDfLoginInfo pair is called a "Manual Identity", or "Identity" for short. The other
connection mechanism uses what is called a "Principal Identity" (similar in concept as
J2EE Principal Identities).
You register a typical IDfLoginInfo object with the session manager along with the
Docbase you would like the session manager to connect you to when needed.
The Session Manager allows the program to register a list of IDfLoginInfo/Docbase
pairs (list of "Identities") with the session manager so the session manager can
transparently reconnect to that Docbase whenever necessary.
In addition, another new authentication mechanism is supported, called a "Principal
Identity", which allows the session manager to use a special trusted user identity to act
as a proxy for connecting to different Docbases without the user supplying a password to
each service or session manager.
First, a data object which implements the standard DFC interface IDfLoginInfo must be
constructed which is composed of the following properties (with the corresponding getter
and setter methods):
• User: user name
• Password: password for user
• Domain (optional): NT domain name
Example:
IDfLoginInfo loginInfo = clientx.getLoginInfo();
loginInfo.setUser( user );
loginInfo.setPassword( password );
This is the standard mechanism used since DFC version 1.0. The only difference is that
the IDfClient.newSession() method is not called.
When a new identity is registered to a session manager object, the login parameters, as
described above, must be specified together along with the Docbase name. The Session
Manager allows multiple identities to be defined using the setIdentity() method, up to
one for each Docbase. The Session Manager maintains a lookup table, keyed on Docbase
name.
Example:
IDfClient client = DfClient.getLocalClient();
IDfSessionManager sMgr = client.newSessionManager();
sMgr.setIdentity( strDocbase, loginInfo );
Once identities are defined, the service implementation only needs to establish a session
along with the corresponding Docbase name. The session manager automatically obtains
the identity that matches the given Docbase and creates a session with that Docbase.
Where possible, instead of creating a new session, a session pool session is reused.
NOTE: A consecutive call to the setIdentity() method using the same Docbase will
produce a DfServiceException. If such a call is required, be sure to call the
clearIdentity() method first.
Authenticate Identity
Identities can be authenticated on demand by using the
sessionManager.authenticate() method.
In some cases, you may want to have immediate user authentication, for example, in a
login dialog. In this case, the method sessionManager.authenticate() method can be
called after the identities are defined. The session manager then immediately tries to
connect to the Docbase and throws an exception (DfAuthenticationException) if
either the user or password is invalid. The authenticate()method does nothing more
than request a session. The session is cached to lessen the performance issues when
calling a service shortly after authenticating a user.
Example:
IDfClient client = DfClient.getLocalClient();
IDfSessionManager sMgr = client.newSessionManager();
sMgr.setIdentity( strDocbase, loginInfo );
sMgr.authenticate( strDocbase );
NOTE: When using the Session Manager in a web application, make sure there is a
separate Session Manager instance for each user session. If you use the same Session
Manager, different users will share the same Docbase permissions, which may
compromise security.
Remove Identity
Identities can be removed from a session manager by using the
sessionManager.clearIdentity() method together with the Docbase name. Remove
all the identities using the sessionManager.clearIdentities() method.
To release the session to a specific Docbase, the clearIdentity() method must be
called. To disconnect from all Docbases, call the sessionManager.clearIdentities()
method. Any Docbase requests made after clearing the identity for that Docbase causes a
DfIdentityException. Design applications in a way that would not attempt access to
Docbases without valid identities.
Check Identity
To check for a specific user identity registered in the Session Manager for given
Docbase, use the sMgr.hasIdentity() method. Optionally, a collection of all the
identities for a specified Docbase can be retrieved using the sMgr.getIdentities()
method. In addition, you can obtain a list of Docbases to which the Session Manager has
identities using the IDfSessionManagerStatitics.getDocbases() method. Obtain a
reference to an IDfSessionManagerStatistics object using the
sessionManager.getStatistics() method.
System.out.println( ""+count );
The following example demonstrates how to set a Docbase identity, call a method, and
disconnect from that Docbase using the Session Manager:
void test()
{
IDfClient client = DfClient.getLocalClient();
IDfSessionManager sMgr = client.newSessionManager();
sMgr.setIdentity( strDocbase, loginInfo );
anyService.serviceMethod( strDocbase );
sMgr.clearIdentity( strDocbase );
}
Principal support is not switched on by default. To enable Principal support, you must
call the setPrincipalSupport() method.
client.setPrincipalSupport( new YourPrincipalSupport(loginSuper) );
When calling the client.setPrincipalSupport() method, supply a reference to an
active IDfLoginInfo user of a super user or system administrator. The Principal Support
object can save this login object in its memory.
Example:
Principal support is used to generate the DFC session for the principal being
authenticated. The principal service support interface IDfPrincipalSupport defines a
method getSession() which returns the DFC session for a given Docbase/principal
combination. It is up to the implementation how to obtain the trusted identity. A sample
class is available that demonstrates an implementation of a class that implements the
IDfPrincipalSupport interface, called SamplePrincipalSupport. The constructor
takes login information of a super user that is able to generate login tickets for users. The
limitation of this implementation is that the super user must have the same login
credentials for all Docbases it serves.
The login credentials for the super user need to be passed or read by the service to get
started. Great care must be taken so as not to expose any security risks to your
corporation.
file.properties.encrypt
sysAdmin,
sysAdmin,saPwd
saPwd
Docbase 1 Docbase 2 Docbase 3
is used for all Docbases being accessed. It is not possible to define a principal for a
specific Docbase.
Example:
NOTE: Principal support is only used when no manual identity is available. In other
words when both principal and manual identities are set, the manual identity has priority
and is used for user authentication.
Architecture
Design Time
A Service based Business Objects is created by extending the
com.documentum.fc.client.DfService class and implementing its corresponding
IDfService interface. The DfService class is an abstract class that implements some of
the common methods for most services.
The service interface and service abstract class (IDfService, and DfService) define the
following methods:
• The getName() method returns a logical service name. The default implementation
in DfService returns the fully qualified interface name that is implemented by the
service class. (such as com.documentum.services.inbox.IInbox )
• The getVersion() method returns the current version of the service as a string. The
version is a string and must match the following pattern:
<major version>.<minor version>. For example, "1.0". In the DfService
class, this is an abstract method and must be overridden by all services.
• The getVendorString() method returns copyright statement. If a service were
provided by Documentum, the vendor string would be
"Copyright (c) Documentum, Inc. 2002. All Rights reserved."
• The isCompatible() method can be used to check if the implementation of a service
is compatible with a specific service version. This allows a service client to use
different versions of a service and the client can then determine if the service is
compatible with specific functionality. Versioning is possible only for the
implementation of a service but not for the interface. Java does not support multiple
versions of the interface.
• The supportsFeature() method is used to define a feature catalog for every service
which can then be queried by the client. Based on this information, specific UI
features can then be switched on or off. The default implementation in DfService
returns false.
• The getSessionManager() method allows a service to access the service’s session
manager handle.
• The getSession() method returns a DFC session object. Each time one of these
methods is called, a corresponding releaseSession() method is required. The
default implementation in DfSession calls the session manager to return the DFC
session for the given Docbase name.
• The releaseSession() method is used to return a session established with
getSession(). In this case, DfSession just invokes the session manager.
outside of the namespace of the service, and is more convenient for calling the factory
methods.
Example: The following example shows how you can simply pass the class name to the
factory method when using the interface name as the service name
IAutoName autoname = (IAutoName)client.newService(
IAutoName.class.getName(), sMgr);
Service Parameters
Wherever possible, avoid using Java system classes as method parameters or return
values. If a DBOF service interface were exposed to a non-Java environment like
Microsoft COM, these classes would make the porting even more difficult. The DFC
common package provides a few Java interfaces and classes that could be used in
replacements for some Java system classes:
• Remember that the new DJCB COM Bridge supports IDispatch interface for COM
programs.
• IDfList / DfList in a place of java.util.Vector.
. . .
. . .
}
Example: The following example uses the stateless approach in which case all
parameters are passed to the method at once. This approach can be confusing especially
if there are many optional method parameters to specify. However, this approach may be
useful if the number of optional method parameters is small.
. . .
}
Example: The following example also uses the stateless approach but passes the method
parameters using a data object. This is a cleaner approach than above when dealing with
many parameters. Therefore, this is the preferred approach as long as overhead is not an
issue:
Example:
. . .
}
An ICheckinConfig object holds all parameters required for check-in. A data holder
object contains getters and setters for all parameters.
• getVendorString()
• isCompatible()
In most cases, it is not possible to anticipate the environments in which a service is going
to be used and therefore it is recommended that the Docbase name be passed to every
method call.
Caching persistent data in the service implementation
Caching dynamic persistent data in the service implementation is not recommended. It is
better to rely on the client cache. Otherwise, the Java based cache may be out of synch
with the client cache when either a client application aborts a transaction or another
service modifies the object.
Extended Service Methods
The service methods must be defined to implement the different service functions. This
can be one or more independent (i.e. stateless) methods. Each method must get a session
and release it when finished. It is important to release the session even in case of an error
in order to make it available.
The following example shows how to structure a service method:
instantiated, executed and then discarded when the request is done. The required steps to
instantiate a service are:
1. Define the user login information with an IDfLoginInfo object.
2. Instantiate a session manager object.
3. Call the service factory method:
Example:
IDfClient client = DfClient.getLocalClient();
Error Handling
The session factory method may throw an exception indicating one of the following
problems:
• DfDborNotFoundException: This exception is thrown in case the DBOR does not
exist.
• DfServiceNotFoundException: The DBOR was found but the service is not
configured.
• DfServiceInstantiationException: In this case, the Java class configured in
DBOR cannot be instantiated. This may happen if the Java class was not found in the
CLASSPATH or is an invalid data class. Java classes on application servers sometime
have security protection that may also cause this exception to be thrown.
Architecture
They key point of Type-based Business Objects is to allow a programmer to implement
new business rules for new Docbase types. In order to succeed in this, all other programs
that access the Docbase objects of this new type must be forced to use the overridden
methods supplied by the business object. In Documentum 5.1, whenever a program calls
newObject() or getObject() like methods, the Documentum 5.1 system will perform
the following tests:
• The system determines the actual Docbase object type and fetches that object along
with its supertype parts.
• Checks the dbor.properties registry file (DBOR) to determine if there is a mapping
between the custom type and a Java class.
o If so, that Java class is instantiated and getObject() returns that object.
You will not need to downcast to the subtype to use any of the overridden
methods, like getObjectId(), getString(), save(), etc.
You will need to downcast to the interface of the new subtype in order to
call any new methods (or methods of subclasses of
IDfPersistentObject)
o If the object type is not mapped in the DBOR to a Java class, the standard
DFC class is instantiated (e.g. DfDocument).
(Caution: Because this mapping will not occur with ANY "UPDATE" queries,
modifying the attributes of type based objects by way of "UPDATE" queries is
discouraged.)
The following three code samples would retrieve the same "com_accelera_autonumber"
Docbase object AND execute the code in the overridden checkout() method (of
AutoNumberType) because of the DBOR mapping and polymorphism.
String strDQL =
"dm_document where r_object_id = '091a64c18001b382'";
String strDQL =
"com_accelera_autonumber where r_object_id = '091a64c18001b382'";
String strDQL =
"dm_document where r_object_id = '091a64c18001b382'";
Object Factory
A client application uses the same standard DFC factory method to construct a new TBO
as any other Docbase type. Use session.newObject() to create a new TBO object.
ICatalog catalog = (ICatalog)session.newObject( "catalog" );
Use any of the getObject() series of methods to create an instance of the new class
(TBO) designed to implement the new Docbase type.
ICatalog catalog = (ICatalog)session.getObject( idDoc );
The session.newObject() method constructs a new type based business object (TBO)
that is mapped to a specific Docbase type. Usually, there is a one-to-one relationship
between the Docbase type and the business object type (e.g. "catalog" is mapped to
ICatalog). Therefore, the factory method checks the DBOR to determine which Java
class is mapped to a given Docbase object type.
Figure 7-1 - Sample DBOR Entry
catalog type com.documentum.catalog.Catalog 1.0
In this case, the Docbase object type "catalog" is configured in the dbor.properties file
and the object type is mapped to the com.documentum.catalog.Catalog Java class
which implements the ICatalog interface. This interface exposes the specific catalog
functions and the client application can use this interface to handle and control a catalog
object.
For a given Docbase object type, there may be multiple entries registered in the DBOR.
This may be the case when there are different applications that handle the same object
type slightly differently. For this reason there are additional factory methods that can be
used to explicitly specify the interface used to handle the given object. These methods
are defined as follows:
catalog type com.documentum.catalog.Catalog 1.0
ICatalog2 type com.documentum.catalog.Catalog2 1.0
These methods will lookup the interface name in the DBOR and instantiate the registered
implementation used to handle the object instance. If the interface is not registered, an
exception will be thrown. In addition, when using this method, the interface name is not
the Docbase name but the key to lookup the implementation in DBOR. The Docbase
type name is used to implement the default behavior of a type based object. For special
handling the interface name is used.
only be accomplished when the object is persisted. This requires overriding the
save(), checkinEx(), etc. methods.
Use the following points as a checklist when developing with type based business objects
(TBOs).
• When designing a TBO, the interface of the type based object must inherit from
IDfBusinessObject. The IDfBusinessObject interface defines four abstract
methods that must be implemented by its implementing classes: getVersion(),
getVendorString(), isCompatible(), supportsFeature().
public interface IAutoNumberType extends IDfBusinessObject
{
}
• You should then "abstract" the interface to the new Docbase type by defining a
collection of methods that you recommend programmers use to access objects of this
new type.
While it seems natural to define the interface to also extend the interface of its
corresponding Docbase supertype interface, a best practice recommendation is to
completely define all the methods of the interface. For example, if checkin() and
checkout() are methods that will be called by programmers on the object directly,
they should be defined in the interface instead of merely extending IDfDocument.
This prevents "polluting" of the new interface and makes it less confusing to other
programmers as to which methods there are to choose from.
interface IAutoNumberType extends IDfBusinessObject, IDfDocument
• When defining the Java class that will be mapped to the new Docbase type, you must
extend the DFC superclass that matches the Docbase supertype AND implement the
matching interface of that new class.
public class AutoNumberType extends DfDocument implements IAutoNumberType
{
}
• At the same time, the TBO class must implement the four methods of the
IDfBusinessObject class as follows:
IDfBusinessObject
methods Description
getVersion() Define a constant String in the class that describes the version (e.g.
"1.0", "2.1", etc.) of the Java class for this object type. This
method should return that constant version String.
getVendorString() Define a constant String in the class that describes the copyright
information (e.g. "Copyright (C), 2002, Documentum, Inc., All
Rights Reserved". This method should return that constant String.
• Always keep in mind that eventually your new TBO class might be subclassed by a
future TBO. If you need to prevent subclassing, consider defining the class as "final".
(You should exercise caution when defining a class as final as doing so could prevent
anyone from developing a work-around method if the method does not perform
exactly as needed.)
• In overridden methods, be sure to call the method of the superclass if its behavior is
still required for overridden methods. Sometimes, your extra code should be added
before the superclass method, other times, the superclass method should be called
first, and sometimes, the superclass method should not be called at all.
public void save()
{
validate(); // new extra functionality
super.save(); // inherited functionality
}
• Some inherited superclass methods call other methods for their implementation. For
example, DfSysObject.checkout(), calls DfSysObject.checkoutEx(null,
null, null). If you override both checkout() and checkoutEx() in your
subclass, provide the extra functionality in both methods, and then call the inherited
superclass methods in both cases, the extra functionality will be provided twice,
which could be undesirable. For this reason, some methods, like checkin(),
checkout(), getContent(), and several others, have been changed to final,
meaning, they can't be overridden in a subclass.
• For new methods, try to write them so that they can be implemented as stateless as
possible. This can be quite involved but the following guidelines should be used:
Type based business objects are always tied to a DFC session object during the entire
life of a Java object instance. During the life of such a type based business object, the
session manager must be locked to make sure the session is not recycled back to the
pool.
o Initiate Client Control using the beginClientControl() method before
obtaining or creating a TBO instance. Release Client Control using the
endClientControl() method when finished.
public void doSomething( IDfId idDoc )
{
IDfSession session = getSession();
IDfSessionManager sMgr = getSessionManager();
try
{
sMgr.beginClientControl();
try
{
session = getSession( strDocbase );
doc = (IDfDocument)session.getObject( idDoc );
doc.setSessionManager( getSessionManager() );
}
finally
{
releaseSession( session );
}
return doc;
}
Error Handling
The factory methods getObjectWithInterface() and
getObjectByQualificationWithInterface() may throw an exception indicating one
of the following problems:
Business Objects Exception class Description
DfDborNotFoundException Indicates that the dbor.properties file can't be
found in the DFC_DATA\config directory.
Check the DFC_DATA variable.
DfServiceNotFoundException Indicates the dbor.properties file was found but
the service was not found.
DfServiceInstantiationException The Java class was configured in the DBOR
but cannot be instantiated. The may occur if
the Java class was not found in the
CLASSPATH or is an invalid data class. Java
classes on application servers sometimes have
security protection that may also cause this
exception to be thrown.
Best Practices
Don't extend the DFC interface
For example, if your TBO class extends the DfDocument class, your interface for that
class could extend the IDfDocument interface. If you were to extend the interface, a user
of the interface would need to choose from over 500 methods. This "polluting" of the
interface seems unnecessary when designing a new type. The actual object still has those
methods available because the class extends DfDocument, so it does not prevent DFC
operations from internally utilizing those methods. Documentum recommends not
extending the interface and designing a new interface with only those methods that are
deemed necessary. The implementation can simply call super.method() inside the
methods of the class.
The remainder of this section demonstrates the steps involved to call a DBOF service
from a WDK component. The sample service implements a simple phone book service
that provides a customer address lookup based on a given customer or phone number.
The following sample application involves:
• ICustomer service based business object.
• JSP / HTML form for user interface.
• A custom WDK form event handler to initiate the action.
Define an ICustomer service interface that extends IDfService and provides the
methods as shown:
public interface ICustomer extends IDfService
{
IDfId getCustomerIdFromPhone(String docbase, String phone)
throws DfServiceException;
These methods return the object ID of a customer object. Using that ID, you can retrieve
selected customer data attributes using the getCustomerData() method.
You can create a JSP page as shown in "Figure 8-1 - Customer Service user interface
WDK form". The form is implemented using a WDK form class named CustomerForm.
The button and text field labels are handled using the CustomerFormNls class. They are
configured using a properties file. The resources are referenced using Java constants that
are defined in the CutomserFormNls class. For example, the work "Phone" is mapped to
an ID_PHONE constant, which maps to the value of a number configured in a properties
file.
When the "Lookup" button is pressed, the CustomerForm.onLookupPhone() event
method is invoked. The JSP form layout is named, CustomerForm.jsp:
When the "Lookup" button is pressed, the onLookupPhone() event method is invoked
and uses the ICustomer service to lookup the customer information based on the
provided number. The onLookupPhone() method uses the
SessionManagerHttpBinding WDK class to obtain a DFC session manager object prior
to instantiating the service. Once the service is constructed, the interface is downcasted
to the ICustomer type and then the two methods can be used.
The implementation of the ICustomer methods will use the Docbase name and their
session manager objects to obtain and release a pooled session.
The onLookupPhone() method shown above performs the following functions:
• Obtains the phone number from the "PHONE" text box of the HTML form.
• The String[] data array is a detail related to the ICustomer.getCustomerData()
method. It happens to return an array of string references, one for each customer
attribute. (Hopefully, in the same order as the for fields are laid out. This
implementation detail that might be better served using a Hashtable object).
• To obtain a session manager reference, construct a new WDK
SessionManagerHttpBinding object. Then use its getSessionManager() method.
(Note: DfClient.getLocalClient() can be called as many times as needed during
an application without worrying about memory leakage since it is a singleton).
Once the session manager is obtained, call the client.newService() method to obtain
the ICustomer service object. The newService() method will use the DBOR to locate
the Java class associated with the interface and instantiate that object for you. The
newService() method requires two arguments, the service name, and a session manager
reference. Since the service name must be a fully qualified name of the service's
interface, use the ICustomer.class.getName() construct to obtain the fully qualified
interface name. You can only do this if the service interface can be imported during
attrList(0) = "phone"
attrList(1) = "name"
attrList(2) = "address"
attrList(3) = "city"
ctlPhone = custData(0)
ctlName = custData(1)
ctlAddress = custData(2)
ctlCity = custData(3)
Packaging
Each Business object (TBO or SBO) comes with a minimum of two Java class files.
These are the business object interface and the Java implementation of that interface.
These two Java class files must be accessible through the Java CLASSPATH from the
JVM that will load and execute business object class files.
Each business object requires an entry in the DBOR on every computer that runs the
application. This entry must match the Java classes installed. The DBOR registry is
located in a directory referenced by the DFC_DATA environment variable. In other
words, when a TBO or SBO is deployed we not only need to copy the Java classes but
also make sure the DBOR matches the deployed components.
DFC Version 5.1 or later is required to be able to run the SBO or TBO because DBOR
registry is part of DBOF, which is new with DFC Version 5.1. It is very important that
the deployed business object Java classes of the TBO and SBO are accessed through the
same Java class loader as DFC. This means that when running an application server that
loads DFC through the CLASSPATH variable the deployed Java classes also need to be
referenced by the CLASSPATH.
However, when the application is deployed in a WAR file for an application server, the
deployed Java classes for SBOs and TBOs must be located in the WAR file. The correct
version of dfc.jar would need to be referenced in the CLASSPATH used by the
application server servlet container, or supplied as a command line parameter when it is
invoked. The rule is, either everything is referenced through the classpath, or everything
is in the WAR file. Avoid mixing .jar files. The DMCL40.DLL file is excluded from
this rule. It can be in a WAR file or referenced with the PATH variable.
The WAR file DFC (dfc.jar) would then be located in WEB-INF/lib. If the deployed
business objects are in a JAR file, this JAR file must be located in WEB-INF/lib. If the
TBO and SBO class files are not shipped in a JAR file, they could also be located under
WEB-INF/classes in the WAR file.
In addition, client connection pooling should be enabled.
Packaging example
Assume that we want to deploy the IAutoName sample service, which consists of a
several Java classes. These classes are shipped in a JAR file named "autoname.jar". The
JAR file is built as follows (assuming a Windows server and default installation
locations):
jar.exe -cvf autoname.jar com
com/
com/documentum/
com/documentum/services/
com/documentum/services/autoname/
com/documentum/services/autoname/AutoName.class
com/documentum/services/autoname/AutoNameScheme.class
com/documentum/services/autoname/IAutoName.class
com/documentum/services/autoname/IAutoNameScheme.class
com/documentum/services/autoname/INameGenerator.class
com/documentum/services/autoname/sample/
com/documentum/services/autoname/sample/RunAutoName.class
com/documentum/services/autoname/UniqueNameException.class
com/documentum/services/autoname/Installer.class
The DFC and the Business Objects Framework require the dfj.jar file. If it is not installed
with the DFC installer, the dfc.jar should be installed in the Documentum Shared files
directory, (e.g. c:\Program Files\Documentum\Shared).
To make the services available to the application, servlet, or application server, copy the
business object JAR file (e.g. the autoname.jar file created above) into a directory on
your hard drive.
There are several options for this and a few things to remember:
• Documentum tries not to impose restrictions on where these files can be located
• The files are loaded and executed by a JVM. All the rules governing the location,
loading, securing, and executing the bytecode in the JAR files follows the rules and
recommendations of Java.
• Considering the Java class loading algorithms and DFC requirements, we have
determined that the following locations are good recommendations:
• Place your business object .JAR file in the JRE\lib\ext directory. This directory is
known as the Java Extension Library. Files placed in this directory are implicitly
scanned after rt.jar when the JVM searches for classes. Only after the JVM can't
find a class in the bootclasses, then rt.jar, then the extension library, will it start to
use the CLASSPATH variable. The downside to this approach is that this .jar file
becomes visible to all Java applications on that computer. This is especially bad
for DFC which contains libraries from Apache and Xerces. Those libraries
contained in the dfc.jar file are not guaranteed to be the latest versions and a non-
Documentum related application may break. Another problem is that IDEs, like
IntelliJ, load all classes located in the JRE\lib\ext directory by default and can be
overly time-consuming.
• A benefit of using the Java Extension Library is that the CLASSPATH
environment variable is not used to locate your class.
• Another benefit is that the CLASSPATH variable is much shorter.
There are a few drawbacks to using the extension library.
• If you install another JVM (e.g. when you upgrade from 1.3.1_03 to 1.4.1), you
would need to remember to copy all the JAR files from c:\Program
Files\JavaSoft\JRE\1.3.1_03\lib\ext to c:\Program
Files\JavaSoft\JRE\1.4.0_01\lib\ext.
• You need to choose a name for the .JAR file the does not conflict with a file
already there. (Remember, the namespace is determined by the actual package
names, like com.accelera.autoname, not the name of the JAR file).
• Place your business object .JAR file in the any directory of your choosing - possibly a
repository directory of business object .JAR files.
• Each Jar file would need to be added to the CLASSPATH variable. The
CLASSPATH will begin to get very long.
• Place your business object .JAR file in the a WAR file in the WEB-INF\lib directory.
• The drawback here is that dfc.jar would also need to be located there.
You must ensure that the CLASSPATH environment variable includes the JAR file of the
new business objects, including dfc.jar.
To be able to run DFC also the PATH environment variable has to be configured to the
Documentum Shared directory where Documentum stores the shared libraries, DLLs and
other necessary files. This is typically done by the DFC installer. If this is not the case
the PATH variable has to be setup to reference dmcl40.dll for example located in
c:\Program Files\Documentum\shared:
Documentation
When you develop a business object, you should also develop a set of Javadocs for it so
that other developers (including yourself) may use it as a reference when calling the
business object methods.
Since Javadocs can be automatically generated from Java source code it tends to be very
good for describing the details of an individual classes. Very often, however it is hard to
get the overall picture. Try to give extended descriptions and overviews on what a
business objects does and how it relates to other business objects for each class.
The recommendation is to always ship Javadocs with a set of business objects.
AutoName Components
If you want to install the AutoName sample business object, follow these steps:
• Copy the autoname.jar to JAVA_HOME\lib\ext
• Define the DFC_DATA environment variable.
• Create DFC_DATA\config\dbor.properties file and configure it as shown above.