Provides a practical guide to help a developer get started using the Business Object Framework (BOF), including real-world examples and sample code, as well as best practices for creating, configuring, and deploying your BOF objects.
Blue Fish Development Group
701 Brazos St. #700
Austin, TX 78701
(512) 469-9300
Provides a practical guide to help a developer get started using the Business Object Framework (BOF), including real-world examples and sample code, as well as best practices for creating, configuring, and deploying your BOF objects.
The Business Object Framework (BOF) is a set of functionality included starting with DFC 5.1 that provides ability to hook into any of the methods in the standard DFC object interfaces. This enables a developer to customize business logic behavior at the DFC level, such that any application which leverages DFC will automatically use the BOF extensions you provide.
For an overview of what BOF is and the value it can provide, you should probably start with Michael Trafton’s article Introduction to the Business Object Framework, which provides some high level understanding of what BOF is used for and how it is used, as well as a discussion of some real-world examples.
This article provides a practical guide for implementing BOF extensions and and deploying into your Documentum applications. Once you’ve read this article, you can expect to have an understanding of how to implement simple type based objects (TBOs) and service-based objects (SBOs), how to package them and deploy them into both pre- and post-5.3 environments, and some of the common pitfalls and challenges you might face along the way.
Any application which uses DFC to interact with the repository will automatically use BOF, assuming your TBOs/SBOs are registered in that environment/repository.
Code which does not use DFC to interact with the docbase will not be able to leverage your BOF customizations. The big thing to keep in mind here is docbasic code which runs either as part of a Method configured in the docbase or as part of the lifecycle scripts. Since dmbasic talks directly to the DMCL API, it does not go through DFC and therefore does not get the benefit of the BOF layer.
Documentum provides a number of recommended guidelines for BOF development. Although somewhat out-of-date, the 2003 white paper “BOF Development Guidelines” is an excellent resource. At a high-level, you should always take care when writing BOF code. It can easily be a source of bugs which are tricky to track down. But the basics of BOF development, and most of the simpler tasks for which one might use BOF, are fairly straightforward.
At a high level, here are some of the author’s recommendations for BOF development, gleaned from Documentum guidelines and experience with BOF:
The main value of a type based object is the ability it gives a developer to override selected functionality of DfSysObject, the base class for most objects you’re likely to work with in a docbase. With just a small about of TBO code, a developer can significantly alter the behavior of the object. This is done by overriding the various DfSysObject methods.
Before DFC 5.3, the names and signatures of the DfSysObject methods that needed to be overridden was inconsistent and unintuitive. Often it was not clear which methods needed to be overridden in order to catch every possible case. DFC 5.3 introduced a new set of methods designed to make TBO development easier, by providing clear extension points that require little understanding of the inner workings of DFC. Before 5.3, if you wanted a piece of code to execute every time a file was checked out, you had to override the checkoutEx method and the getFileEx2 method. This wasn’t intuitively obvious, so many developers likely overlooked it. In 5.3, you can simply override the new doCheckout method with the confidence that your code will be executed every time a document is checked out.
Chapter 19 of the Documentum 5.3 System Migration Guide provides more detailed information about the specific method names and the situations each is for.
If you know your BOF code will only be used in a DFC 5.3 environment, you are strongly encouraged to override these new “do” methods.
Deploying your BOF code can be a tricky enterprise. It is especially challenging in a pre-5.3 environment, since the BOF code must be registered in DBOR on every single client machine. This section provides an overview of what you need to do to deploy your BOF code in both pre- and post-5.3 environments.
In a pre-5.3 environment, there are two steps to deploying your BOF code:
We will begin with an overview of DBOR, then we’ll get into the specifics of registering your BOF code in DBOR, and finally we’ll provide instructions for actually deploying the BOF code into your client application(s).
Until 5.3, the only way to use BOF customizations was to register them in the Documentum Business Object Registry on every client machine which needed that BOF functionality. This could include the WDK application server machine, the Java Method Server, the Webcache Source, and other client machines. The word “client” here is meant to imply an application that is talking to the content server over DFC, not necessarily the end-user client machine. (In the case of Desktop Client, the end-user machine would be the client, but for a WDK application, the app server is actually the DFC client.)
This method of deployment, which is still supported (though not preferred) in 5.3, requires that TBOs and SBOs be registered in the DBOR, which is essentially a text file (dbor.properties) located under the DFC installation on that machine. TBOs and SBOs can be registered directly by editing the text file, or through a DFC API (preferred).
In addition to registering the TBO and SBO classes in DBOR, it is also necessary to include the jar or jars containing the TBO/SBO code in the classpath of any JVM which might call out to DFC on that machine. In a pre-5.3 environment, DBOR is a global registry for the entire machine, so there is no way to deploy your BOF code to one client on that machine without deploying it to all clients on that machine.
The 5.3 code line resolved this problem by abandoning the DBOR in favor of hosting BOF code directly in the docbase. More on this later.
For a typical WDK application (such as Webtop or Web Publisher), you will need to register your BOF classes in the DBOR registry on any machine which you want to make use of your BOF code. In most cases, this is the application server machine that is running WDK, but it could also include the Java Method Server (which typically runs on the content server machine). For Web Publisher applications, BOF might also need to be deployed into the Webcache (SCS) Source, which talks to the docbase through DFC (and therefore through BOF) when publishing website files from the docbase out to a Webcache (SCS) Target.
The DBOR in DFC 5.2.5 is a properties file, a simple text file called
dbor.properties that lives in your DFC config directory. This file is read
by DFC to construct a lookup table for BOF objects, so that any DFC code that is
running on that machine knows how and when to instantiate BOF classes.
The structure of the file is very simple. Each line in the file must be one of the following:
The format for a TBO configuration instruction is as follows:
[object_type]=type,[TBO class name],[version]
In an actual DBOR configuration instruction, [object_type] would be
replaced with an object type name for an object type in the docbase. The [TBO
class name] would be replaced with the fully-qualified Java class name
(including the full package structure) of the TBO class to use for that object type.
The [version] would be replaced with the specific version of that TBO
class to use. The version number specified here will be passed into the TBO
constructor; it is up to the TBO code itself to do something useful (or not) with this
version number.
The following line registers the MenuItemContentTbo TBO class against
the bf_web_content object type in DBOR:
bf_web_content=type,com.bluefishgroup.navigation.bof.MenuItemContentTbo,1.0
With this line in my dbor.properties file, any time I use DFC to load
a bf_web_content object from the docbase, the object I get back will be an
instance of MenuItemContentTbo. Anytime DFC code is used to manipulate
this object, it will use an instance of MenuItemContentTbo, which
guarantees that the custom TBO code in MenuItemContentTbo will run.
The format for an SBO configuration instruction is as follows:
[SBO interface name]=service,[SBO class name],[version]
In an actual DBOR configuration instruction, [SBO interface name]
would be replaced with a fully-qualified Java interface name (including the full
package structure) an object type name for your SBO’s interface. The [SBO class
name] would be replaced with the fully-qualified Java class name (including the
full package structure) of the SBO instance class that should be loaded when client
code requests an SBO with that interface. And, like the TBO configuration, the
[version] would be replaced with the specific version of that SBO class to
use. Again, the version number specified here will be passed into the SBO constructor;
it is up to the SBO code itself to do something useful (or not) with this version
number.
The following line registers the MenuItemService SBO class against the
IMenuItemService SBO interface in DBOR:
com.bluefishgroup.navigation.bof.IMenuItemService=service,com.bluefishgroup.navigation.bof.MenuItemService,1.0
With this line in my dbor.properties file, any time I use DFC to load
a service for the IMenuItemService interface, the object I get back will
be an instance of MenuItemService. This makes it extremely straightforward
for someone to come behind me and implement a custom version of
MenuItemService. All they have to do is register their
MenuItemService SBO class against the IMenuItemService
interface, and any DFC code which uses the IMenuItemService will be
guaranteed to use their SBO code (assuming the client code is using the SBO model
correctly and coding to the interface, not the instance class).
The dbor.properties file is simple, which makes it easy to edit and
understand, but editing this file manually is not the preferred way of managing DBOR
registrations. Documentum provides a Java API for managing DBOR registrations, and it
is the recommended way of working with DBOR.
The following example illustrates how to register the
MenuItemContentTbo TBO class against the bf_web_content object
type:
// Set up some variables.
String tboObjectName = "bf_web_content";
String tboClassName = "com.bluefishgroup.navigation.bof.MenuItemContentTbo";
String tboVersion = "1.0";
// Get a handle on an IDfDbor.
DfClientX clientx = new DfClientX();
IDfClient client = clientx.getLocalClient();
IDfDbor dbor = client.getDbor();
// Create a new DBOR entry.
DfDborEntry entry = new DfDborEntry();
entry.setName(tboObjectName);
entry.setJavaClass(tboClassName);
entry.setVersion(tboVersion);
entry.setServiceBased(false);
// Register the entry.
dbor.register(entry);
When run, this code will actually create the necessary configuration instruction
line in the dbor.properties file on the local machine.
Similarly, the code to register the MenuItemService SBO class is
included below:
// Set up some variables.
String sboInterfaceName = "com.bluefishgroup.navigation.bof.IMenuItemService";
String sboClassName = "com.bluefishgroup.navigation.bof.MenuItemService";
String sboVersion = "1.0";
// Get a handle on an IDfDbor.
DfClientX clientx = new DfClientX();
IDfClient client = clientx.getLocalClient();
IDfDbor dbor = client.getDbor();
// Create a new DBOR entry.
DfDborEntry entry = new DfDborEntry();
entry.setName(sboInterfaceName);
entry.setJavaClass(sboClassName);
entry.setVersion(sboVersion);
entry.setServiceBased(true);
// Register the entry.
dbor.register(entry);
The author has also found it useful to query the DBOR before registering anything, to
be sure some other client has not already registered a TBO or SBO for the specified
object type or interface name. The method IDfDbor.isRegistered can be used
to perform this query.
The instructions above describe how to register your BOF code in DBOR. This is necessary in order for Documentum/DFC to know which TBO/SBO classes to use when a request is made for a TBO or SBO instance. But ultimately, this code cannot be used by DFC untless it knows where to find it.
Documentum’s BOF guidelines suggest that BOF code be packaged in one or two jar files: One jar file should contain the interfaces, and the other should contain the implementation classes. Most TBO classes do not require corresponding interface classes, since usually the purpose of a TBO is to override existing functionality in the IDfTypedObject interface. Therefore, simple TBO deployment should only include one jar which contains the implementation classes. An SBO must always have both an interface and an implementation class, which means you would need to have an interface jar and an implementation jar; in practice, you may find yourself coding an SBO implementation to an existing (perhaps already-deployed) SBO interface, in which case you would only have the implementation class.
BOF classes need to be loaded by the same classloader which loads DFC. For this
reason, BOF classes can not be installed as part of a web application (i.e. in
WEB-INF/lib or WEB-INF/classes), as web applications are
loaded by a classloader specific to that web application. Placing your BOF code
inside the WEB-INF/classes folder of your webapp will not work. Instead, the BOF classes
must be referenced in the main classpath for the entire application server.
For each of the three common DFC clients discussed below, there is no set location for BOF code to be deployed. You can choose any directory you want, as long as the client application’s JVM has access to that directory. Once you deploy the jar file(s) into the directory of your choice, you must update the client JVM’s classpath to include a reference to the BOF jar(s).
For a typical WDK environment, the easiest way to ensure that your BOF code will be
found by the DFC classloader is to include your BOF jar file(s) on the same classpath
that includes the DFC jar file (dfc.jar). For example, an Apache Tomcat
application server will have a batch file (or a shell script) that is used to setup the
classpath and launch the server. To include your BOF code on the classpath, all you
have to do is update it to include a reference to the location where your BOF jar(s)
are deployed. Other application servers may have batch files, configuration files, or
configuration consoles through which you can update the classpath.
Another environment where BOF code often needs to be deployed is the Documentum
Java Method Server. If your BOF code needs to be used by the method server, it must be
deployed to this machine. The Java Method Server is actually a Tomcat instance which
typically runs on the content server machine (though not necessarily), but rather than
relying on the familiar catalina.bat (or catalina.sh) scripts
to start up, it is configured as a service in the Windows registry. In a Windows
environment, the classpath it uses comes from a Windows registry setting, which can be
found in the following location:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DmJavaMethodServer\Parameters
In a Web Publisher environment, you may also need for your BOF code to be deployed to the Site Caching Services (SCS) Source, which is also a Tomcat instance that runs on the content server. On a Windows machine, the configuration for this service can be found in the following location (where the port number of the SCS source in this case was 6677):
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Documentum SCS_source_6677\Parameters
Remember, the only way for either of these applications (or any other application) to leverage your BOF code is for your BOF jars to be deployed on the application’s classpath, and for the BOF implementation classes to be registered in DBOR on the machine where the BOF code is deployed.
In DFC 5.3, Documentum has introduced a new way of managing BOF code that is far superior to the DBOR. BOF code is now stored in the repository and dynamically loaded by DFC as needed by application code running against that docbase. SBOs can be hosted in a single “global” repository, providing the same functionality to multiple docbases; TBOs can now be hosted and configured in only the docbase that uses them, allowing for different TBO registrations to be set up for different docbases, even if they are being accessed by a single client application (something that simply wasn’t provided for in the pre-5.3 BOF framework).
Chapter 3 of the DFC 5.3 Development Guide provides an excellent overview of this new BOF architecture, and the reader is encouraged to read this through a couple times to gain some familiarity with the new concepts.
With 5.3, Documentum has introduced the concept of a “module”, which is a unit of executable code that is stored in the docbase. Service Based and Type Based Objects are one kind of module. To support this new module framework, a few new object types have been introduced: dmc_jar, dmc_module, and dmc_java_library.
The dmc_jar object type is a subtype of dm_document, and it is used for JAR archives containing Java code. This might include a JAR containing your TBO implementation class(es), or perhaps your SBO interface class(es), or even a third-party library that is leveraged by your SBO. How a dmc_jar object is used depends on whether it is a part of a module or a Java library.
The dmc_java_library object type is a subtype of dm_folder. It is a special folder type which has additional attributes indicating how the JARs within should be configured by the DFC class loader. In DFC 5.3, the only additional attribute is whether or not the Java code within should be “sandboxed”, meaning isolated from other, possibly conflicting, Java code in the classpath.
The dmc_module object type is a subtype of dm_folder. It is similar to the dmc_java_library object type, in that it is a special kind of folder with additional attributes governing how the member objects should be used. The contents can be dmc_jar objects containing the BOF code, as well as dmc_java_libraries containing any libraries the BOF module code depends on. The metadata loosely corresponds to a single entry in DBOR, specifying whether the module is an SBO or a TBO, and how the code it contains should be run, what other modules it depends on, etc. Unlike a dmc_java_library, a dmc_module is always sandboxed.
There can be other types of modules in DFC 5.3. Methods can now be implemented by repository-based module code. Documentum’s new (limited) support for aspects is also implemented using repository-based module code.
Although the back-end mechanics are important to understand, Documentum insulates the developer from most of this: the recommended process for creating and managing BOF objects in the docbase is to use Documentum Application Builder (DAB) 5.3, which provides a way of packaging modules in a Docapp. Consequently, the creation and management of modules and java libraries may actually require little understanding of how module code is actually represented in the repository; however, it helps to know what is going on behind the scenes — it will make your life easier when the time comes to debug!
Documentum uses the term registry to describe the accumulated configuration information of all the various modules and libraries that are registered in a repository. When you install a module into a docbase, you are ‘registering’ that module in that repository.
A TBO is specific to its repository; that is to say, a TBO must be deployed into the repository that you want it to be used with, and it will only be available to code operating on that repository. To put this another way, a TBO must always appear in the registry of the repository that it will be used for.
An SBO, however, is more likely to contain code that needs to be shared across multiple repositories. Rather than requiring this SBO code to be deployed into every single repository, you can specify one repository’s registry as the global registry, and that repository can provide the SBO module code for client application code running against many other docbases.
In order for an application to be able to access the global repository, it needs user login credentials. Documentum recommends you create a specific BOF user for this purpose, and indeed the installation can do this for you. The user credentials are configured in the familiar dfc.properties file. A single DFC installation can have only one global repository, but a repository can serve as the global repository for multiple DFC installations.
For SBOs, the interface JARs still must be deployed into the client application environment, even though nothing needs to be registered in DBOR. In practice, the author has found in convenient enough to simply deploy the BOF interfaces as part of the overall WDK application code, so that they are automatically available without the need for building and deploying a separate BOF interface JAR. If you package code separately for deployment to the method server, then you will want to be certain to include the BOF interface classes, either as their own JAR or as part of the method jar(s).
Although BOF code is provided to the JVM dynamically, it still runs inside the JVM the same as any other Java code, and so you can debug into it the same way. Debugging BOF code is as simple as attaching a debugger to the JVM your BOF code is running in, whether it is your own application server or even the Java Method Server (which is just an apache tomcat server).
Logging from BOF is no different from logging from other DFC-based code. It is recommended that you use DfLogger. You can control how the logs are generated using the familiar log4j.properties file.
It is recommended that all BOF methods escalate any DfExceptions which are thrown by code that they call. This vastly simplifies development.
Implement a TBO customization to log a warning message when a document of a certain type is checked out.
Suppose that we need to log a warning message whenever objects of a certain type are checked out. The warning message should include the email address of the lock owner, so that system administrators can have a simple external record of who has checked the document out. It is a somewhat contrived example, but it is easy to imagine an email service in place of the simple logging; or perhaps we might call out to a web service somewhere to synchronize checkouts with an external system.
With BOF, you can implement a TBO that automatically logs the message when a document of the specified type is checked out.
The TBO class, which we will call BfExampleOneTbo, must override the doCheckout method. In the overridden method’s code, it will call the supertype’s doCheckout method first, to ensure that the document is checked out. Then it will look up the the lock owner’s email address within Documentum, and then log the checkout activity using the DfLogger.warn method.
The file below provides a basic example of the TBO class described above.
BfExampleOneTbo.java [ download ]The TBO class is completely self-contained, with no dependencies on anything other than DFC. We have overridden only existing methods, and have not added any custom methods to the object, so there is no need for an interface class. Deploying this TBO is as simple as adding it to a JAR file and then packaging that JAR as a module in Documentum Application Builder.
The JAR file below includes the compiled BfExampleOneTbo class.
bf_example1.jar [ download ]To install this simple TBO into the repository, take the following steps in Documentum Application Builder.
Figure 1: bf_example object type properties in Application Builder
Figure 2: bf_example TBO module implementation properties in Application Builder
Assuming your Log4J configuration is set to log warning message somewhere, you should soon begin seeing the warning message when objects of type bf_example are checked out.
Implement an SBO customization to provide logging capability to multiple TBOs.
The previous example illustrated how simple it is to implement a rudimentary TBO. But in the real world, your TBO may require functionality that is more generic and should be represented somewhere other than the TBO. Or there could be functionality that more than one TBO needs to execute. Rather than implementing this in your TBOs, you can extract this common functionality into an SBO. Let’s take the previous example one step further, to illustrate how the same functionality could be implemented by a TBO that calls out to an SBO.
For this example, we will extract the logging functionality into a logging service BfLogService, which will implement the interface IBfLogService. Although we will still only be implementing the one method for logging a warning about checkout, it is easy to imagine a more complex and complete service.
The rule for SBO development is that you must have an SBO interface and implementation class. Our SBO interface will be extremely simple: aside from the standard TBO methods from IDfBusinessObject, it will have one method: logCheckout. This method will take in an IDfDocument, look up the lock owner’s email address, and log the warning with DfLogger.
The files below provide a basic examples of the interface and implementation class described above.
Now we must update our TBO implementation to use the SBO. The new class BfExampleTwoTbo provides the new implementation of the doCheckout method, which instantiates the IBfLogService SBO and calls its logCheckout method.
The file below provides a basic example of the updated TBO class described above.
BfExampleTwoTbo.java [ download ]The solution for Example 2 is actually made up of two separate modules: the TBO and the SBO. Each one will need to be set up in Application Builder. First we will insert a new SBO module into the docapp, and then we will pick up where we left off in Example 1, modifying the existing TBO configuration for the bf_example type to use our new BfExampleTwoTbo class instead of the old BfExampleOneTbo class.
The SBO must be deployed as two separate JAR files: one containing the interface, the other containing the implementation.
The JAR files below include the compiled classes for IBfLogService and BfLogService, respectively.
To install the SBO into the repository, follow these steps:
Figure 3: IBfLogService SBO module implementation properties in Application Builder
To update the existing TBO configuration so that it uses the new BfExampleTwoTbo class, follow these steps:
Figure 4: bf_example TBO module implementation properties in Application Builder
Figure 5: bf_example TBO module dependencies in Application Builder
The new TBO and SBO should be downloaded automatically by DFC and go into use very quickly, usually within minutes. I have seen some WDK session instability during these “hot swaps”, but it is usually cleared up by logging out and logging back in.
At the end of these examples, the docapp hierarchy for the BOF Examples docapp should look something like the following:
Figure 6: bf_example object type properties in Application Builder
Be sure to take a look at the following documents, as they provide excellent background for understanding and using the BOF.
Subscribe to our newsletter to be notified when new articles are posted. You can unsubscribe at any time.
You must be logged in to post a comment.