Print Header

Related Topics

Related Case Studies

Contact Blue Fish

Blue Fish Development Group
701 Brazos St. #700
Austin, TX 78701
(512) 469-9300

Session Management in DFC 5.x and WDK

Author Photo

December 20, 2003 - Article by A.J. Whitney

This article will describe the appropriate way to manage sessions in DFC 5.x and how this differs from the way they were managed in prior versions. It will also describe session management in WDK 5.x applications.

Introduction

Session management is an integral aspect of most client-server toolsets. Connections to remote machines are expensive to setup and teardown and operations over client-server connections are typically expensive. As a result it makes a lot of sense to limit the number of times a connection need be made and to perform data caching wherever possible to reduce communication overhead. This “extended” connection is modeled in most systems using sessions. The concept of sessions have existed in the Documentum client libraries starting with the basic DMCL and continues forth in each layer built on top of DMCL (including DFC).

Sessions in Documentum are a valuable and scarce resource. Each session requires memory on the client and the server and therefore there are a limited number of them. Historically this hasn’t been a very large issue as clients were typically single-user native applications. With the proliferation of content-based web applications, however, much larger numbers of users are connecting to docbases with each web application server servicing hundreds or thousands of users. Allocating a session to each of these users is an inefficient use of precious resources. The changes to session management in DFC 5.x aim to provide a mechanism for managing sessions that is convenient to the user of the client library while alleviating some of the burden on the server.

The DFC 4.x Mechanisms for Session Management

In DFC 4.x, session lifetime is very long. For single-user native applications this is not typically an issue. The user acquires a session and only after it has gone unused for a (relatively) long time is it collected to make system resources available for another user. A reference to the session object is typically held globally for the application. For web applications this isn’t acceptable. With tens or hundreds of users connecting to a web application server in a short period of time, system resources were rapidly exhausted. In most cases this isn’t necessary as each of these users is performing docbase activity for only brief moments (during the processing of their HTTP request).

The following example code shows a brief example of how a session (IDfSession) is acquired and used to retrieve an object from the docbase in DFC 4.x.

                
import com.documentum.fc.client.DfClient;
import com.documentum.fc.client.IDfClient;
import com.documentum.fc.client.IDfDocument;
import com.documentum.fc.client.IDfSession;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.DfLoginInfo;
import com.documentum.fc.common.IDfLoginInfo;

....

try {
	
	IDfClient dfClient = new DfClient();
	IDfLoginInfo loginInfo = new DfLoginInfo();
	loginInfo.setUser("myUserName");
	loginInfo.setDomain("myDomain");
	loginInfo.setPassword("myPassword");
	IDfSession dfSession = dfClient.newSession("myDocbase", loginInfo);
	
	IDfDocument myDocument = (IDfDocument) dfSession.getObjectByPath("/My Cabinet/My Document");
	
} catch (DfException dfe) {
	// log the error and/or do something useful
}

            

In the example, the client and the session are local to the block of code. Typically, they would be maintained globally to the application or in the case of a J2EE web application probably maintained in the HttpSession. Documentum provided no means for handling the sessions more efficiently.

Session Management in DFC 5.x

The IDfSessionManager

DFC 5.x introduced a way to more efficiently manage sessions. In order to do this, a session management abstraction was necessary and Documentum introduced the IDfSessionManager to fill this void.

The most important aspects of the IDfSessionManager are its facilities to act as an IDfSession factory and its ability to reclaim the sessions that it manufactures. This is accomplished through the methods newSession and release.

The example below is the equivalent (sanctioned) way to acquire a session and retrieve an object from the docbase using this session. The code above for DFC 4.x continues to work in DFC 5.x (ie DFC 5.x is backward compatible with respect to sessions) but this example demonstrates the correct way to do this in DFC 5.x.

                
import com.documentum.fc.client.DfClient;
import com.documentum.fc.client.IDfClient;
import com.documentum.fc.client.IDfDocument;
import com.documentum.fc.client.IDfSession;
import com.documentum.fc.client.IDfSessionManager;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.DfLoginInfo;
import com.documentum.fc.common.IDfLoginInfo;

...

IDfClient dfClient = null;
IDfSessionManager dfSessionManager = null;
IDfSession dfSession = null;
try {
	dfClient = new DfClient();
	dfSessionManager = dfClient.newSessionManager();
	
	IDfLoginInfo loginInfo = new DfLoginInfo();
	loginInfo.setUser("myUserName");
	loginInfo.setDomain("myDomain");
	loginInfo.setPassword("myPassword");
	dfSessionManager.setIdentity("myDocbase", loginInfo);
	
	dfSession = dfSessionManager.newSession("myDocbase");
	
	IDfDocument myDocument = (IDfDocument) dfSession.getObjectByPath("/My Cabinet/My Document");
	
} catch (DfException dfe) {
	// log the error and/or do something useful
} finally {
	if (dfSession != null) dfSessionManager.release(dfSession);
}

            

There are three differences between the DFC 5.x example and the previous DFC 4.x example.

  1. Authentication information is set on the IDfSessionManager before the session is acquired by calling setIdentity.
  2. Acquisition of a session (via newSession) requires only the name of the docbase.
  3. The IDfSessionManager is used to release the session at the end of the example.
We’ll look at each of these in a bit more detail in the following sections.

Note: Although it is possible to use sessions as they were used in DFC 4.x, it is highly recommended to utilize the more efficient mechanisms provided in DFC 5.x. Mixing session management techniques in the same application is cause for heart ache. Don’t do it.

IDfSessionManager.setIdentity()

The session manager maintains a mapping of docbase name to authentication information. Sessions are acquired much more frequently in DFC 5.x than they were in previous versions since sessions are only kept for as long as they are needed to perform a specific operation. In the past, a reference to the session was kept for the duration of a running program so providing authentication information when acquiring the session was quite natural. With frequent session acquisition (which is promoted with the new session management techniques in DFC 5.x), it would be quite burdensome to have to provide this information each time a session is acquired.

Each docbase to which the application connects may require different authentication information. IDfSessionManager.setIdentity() may be called multiple times to build the map of docbase name to authentication information.

It’s often the case for an individual to have the same authentication information for multiple docbases within an organization. A default set of authentication information may be provided by specifying the docbase name as "*". When acquiring a new session, the specified docbase name is used to look up authentication information in the map. If no matching authentication information is found, the session manager, looks for an entry for the docbase “*” and if found, attempts to acquire a session using that authentication information.

This is shown in the following example.

                
...

dfSessionManager = dfClient.newSessionManager();

// Set the default login info
IDfLoginInfo loginInfo = new DfLoginInfo();
loginInfo.setUser("defaultUserName");
loginInfo.setDomain("defaultDomain");
loginInfo.setPassword("defaultPassword");
dfSessionManager.setIdentity("*", loginInfo);

// Set the login info for docbase "myDocbase"
loginInfo = new DfLoginInfo();
loginInfo.setUser("myDocbaseUserName");
loginInfo.setDomain("myDocbaseDomain");
loginInfo.setPassword("myDocbasePassword");
dfSessionManager.setIdentity("myDocbase", loginInfo);

// uses the specific login info provided for myDocbase
dfSession = dfSessionManager.newSession("myDocbase");

// uses the default login info provided for "*"
dfSession = dfSessionManager.newSession("otherDocbase");

...

            

The astute observer may have noticed that in order to acquire a session using newSession, you must specify the name of a docbase. Typically, the session manager is maintained globally in an application (or in the HttpSession in a web application). Along with the session manager, the application developer will need to maintain the name of the docbase to which the application is connecting. In most applications this would be managed in the same manner that the session manager is managed (global or in the HttpSession).

If the docbase name is not conveniently available in a “global” area it will need to be provided along with the session manager in order to acquire a session. It’s not unusual to pass a session manager as a method argument. This might happen if you are calling a piece of code from your web application that is not web application aware. In this case, you wouldn’t want the non-web application aware code to attempt to retrieve the session manager from the HttpSession so you would probably pass the session manager as an argument. Remember in cases like this, that you must also pass the name of the docbase.

Note: In the author’s experience, when developing interface specifications for services called from web applications, the docbase name is often forgotten from the method signatures.

IDfSessionManager.release()

The pattern of session usage is quite different in DFC 5.x than in previous versions. It follows three steps:

  1. Acquire session
  2. Perform operation
  3. Release session (in a finally block)
With this pattern, sessions are released frequently. DFC utilizes session pooling to make the acquisition and release of sessions inexpensive but in order for session pooling to work correctly, sessions must always be released back to the pool. It is important to always release the session in a finally block to prevent an unexpected Exception or Error from leaking a session.

Note: IDfSessionManager.release() throws a java.lang.NullPointerException if you attempt to release a null session so it’s important to always check to make sure that the session is not null before you release it.

                
...

IDfSession dfSession = null;
try {
	// Step 1: Acquire session
	dfSession = dfSessionManager.newSession("myDocbase");

	// Step 2: Perform operation
	// Do some work using dfSession
	
} catch (DfException dfe) {
	// log the error and/or do something useful
} finally {
	// Step 3: Release session (in finally)
	if (dfSession != null) dfSessionManager.release(dfSession);
}

            

Objects and Sessions

With all of this session acquisition and release, how do I hang on to docbase objects if I need them for a long time? The simple answer is not to do that. With one exception, you must always have a valid session to use an object acquired from that session. Releasing a session makes that session invalid. So if you get an object from a session and then release that session, the object you have is no longer valid.

That all seems simple enough but it’s easy to get lazy and hang on to a reference to an object that you shouldn’t be using anymore. This is especially the case if you’re used to keeping long-term references to objects since that’s perfectly acceptable in DFC 4.x and before. The trickiest aspect of this deviant coding behavior is that your application may run perfectly fine written in this incorrect style.

Thankfully, there’s a way to force this bad style to break so you can catch it before it catches you. Setting the DebugSessionManager Java system property (provide -DDebugSessionManager as an argument when starting your VM) forces sessions to become invalid when they are released. Running with this flag is much less efficient because DFC can’t reuse sessions (which it normally does and is the reason the incorrect code you wrote still worked) so you don’t want to use this switch on a production system. It is highly recommended to always use this switch on development platforms, however, so you will find problems early on with the way sessions are being used.

As the author of this article it’s easy to tell you not to hang on to object references for long periods of time. As a DFC developer, it’s often not quite that easy. It’s very common to need to refer to an object over and over throughout the lifetime of your application.

Consider a simple web application that enables the updating of object attributes. The application validates the information and sets the attributes on the object. If a validation fails (trying to set a string on a numeric attribute for example), the application should display this failure to the user. The user may set attributes a couple of times before she successfully saves the changes.

If you have been reading between the lines a little, you have probably realized that to implement this simple little application, you need to maintain state across HTTP requests. Coming from a DFC 4.x world, your first though may be to get a reference to the object when the application initializes and keep a reference to it across HTTP requests. This isn’t a valid approach in DFC 5.x because you should be acquiring the session via a session manager and releasing it (at the very least) at the end of each HTTP request. As described above, a reference to an object obtained using one of these “transient” sessions is not valid once the session has been released.

The application developer must therefore retrieve the object on each request. Typically this would be done via object ID. Each time you retrieve an object this way from a new session, the information is loaded from the docbase. Any state set on the object during a previous HTTP request which was not saved (via IDfPersistentObject.save() or IDfSysObject.checkin()) will not exist on the newly retrieved object.

This makes the application somewhat more complicated to write than in the DFC 4.x world in which you can use the DFC object to store state that hasn’t been saved across HTTP requests. This inconvenience is the price you pay for better scalability.

IDfTypedObject.setSessionManager()

This poorly named method provides an optional solution to the transient state problem described above. This method dissociates a DFC object from its session. This method copies all of the state associated with the underlying DMCL object into the Java object (DFC usually operates in a pass-through mode in which all methods are delegated to the underlying DMCL object). The DFC object is now valid without a valid session. When an operation is performed which requires a session (such as save()), the DFC object uses the associated session manager to acquire a session and perform the operation.

Note: This is an expensive operation as it copies all state to the Java object. The method has a peculiar name because it was never really intended to be a feature of DFC. It was added as a convenience for an internal Documentum application developer and ended up becoming part of the published DFC API. Whether to manage an application-specific state mechanism across calls or to use the dissociative IDfTypedObject.setSessionManager() method depends on what the application is doing. In many cases, it’s probably fine to use IDfTypedObject.setSessionManager(). The author typically shies from this approach in lieu of a more application-specific state management technique based upon the recommendation of the DFC development team.

WDK Session Mangement

The changes to session management in DFC 5.x were made primarily to improve the scalability of web applications supporting hundreds or thousands of concurrent users. The development of the WDK application toolkit was the primary driver for this feature and as such WDK makes use of the new DFC 5.x session management features. The specifics of what is going on inside WDK are hidden from the application developer by a couple of simple-to-use interfaces but it’s often useful to understand what is going on under the covers in order to make sense of what is happening in your application.

WDK bahaves just as the theoretical web application that was described earlier. An IDfSessionManager is stored in the HttpSession. Although the session manager is not meant to be retieved from the HttpSession directly by the user (it should be retrieved via the com.documentum.web.formext.session.SessionManagerHttpBinding interface), it works just like the simple web application described above. If a session is needed for an HTTP request, it is acquired from the IDfSessionManager and released at the end of the request (unless it is acquired for the entire session as we’ll see in Component.getDfSession).

com.documentum.web.formext.session.SessionManagerHttpBinding

SessionManagerHttpBinding is the mechanism used to retrieve the session manager in a WDK application. In components there is a convenience method as we’ll see below but in actions or other non-component WDK code, SessionManagerHttpBinding is the mechanism to get the session manager and the docbase name which are used to acquire a session.

Note: Remember that the session acquired using the session manager obtained from SessionManagerHttpBinding.getSessionManager() must be released back to the session manager as soon as possible. Do not retain it across Http requests.

You may have noticed that SessionManagerHttpBinding has static methods for obtaining the session manager and the docbase name. You may have also noticed that these methods do not take an HttpSession as an argument. That’s a convenience since you may not have access to an HttpSession (such as from within a WDK action). Behind the scenes, the SessionManagerHttpBinding is pulling the session manager from the HttpSession. It accomplishes this by mapping the current thread to a particular HTTP request which it uses to get the HttpSession.

This solves the common case nicely but it is problematic if you are executing multi-threaded code within your request processing code (unusual) as the SessionManagerHttpBinding only works for the request servicing thread. This isn’t typically an issue and you can always obtain the session manager using the SessionManagerHttpBinding and pass it to any thread you spawn but there’s nothing in the interface that indicates that you need to do this so it’s something to be aware of.

Note: The author has also run across code that was originally written for use in a WDK application which later migrated to a non-WDK environment (ie. into a docbase method running in the method server). This code which was attempting to obtain a session manager using the SessionManagerHttpBinding obviously failed but there is nothing in the interface that requires an HttpSession so it’s possible to unknowingly use SessionManagerHttpBinding to retrieve a session manager from an HttpSession that is non-existent (of course it fails at runtime but the code will compile). You wouldn’t normally have WDK libraries on your method server but the author has seen it done so it’s something to watch out for. As a rule, obtain your session manager as close to components, actions, etc as possible and pass it on to other code rather than having that other code use the SessionManagerHttpBinding.

Component.getDfSession()

Much of the code written in WDK applications is in the implementation of components. And here, more than any other place is where sessions are needed. Conveniently, com.documentum.web.formext.component.Component provides Component.getDfSession() and Component.getDfSession(int sessionLifetime) to facilitate acquiring a session. Internally to Component, the session manager and docbase name is obtained using the SessionManagerHttpBinding class. As an added bonus, sessions obtained via calls to Component.getDfSession() are automatically released when the component completes rendering so there is no need to explicitly release the session. Sweet!

Of course that convenience comes at the expense of obscuring what is happening behind the scenes so it’s important to remember when calling the Component.getDfSession() methods that you must not hang on to objects obtained from those sessions for longer than the session’s lifetime.

Session’s lifetime? The authors of WDK realized that it is not uncommon for application developers to want to get a session for longer than a request. Component.getDfSession(int sessionLifetime) may take Component.REQUEST_LIFETIME or Component.COMPONENT_LIFETIME as an argument. REQUEST_LIFETIME is just what you would expect and is how you would typically write a web application. The session is good for that request only at which time it is released. This is the most efficient mechanism but is unfortunately not the default behavior.

COMPONENT_LIFETIME stores the session on the component and it is not released until the component’s lifetime ends (which could be a long time). This has the added benefit of allowing the application developer to store references to objects obtained from that session on the component and utilize them across requests but that’s a heavy price to pay indeed. Consider managing your own state or using IDfTypedObject.setSessionManager if you need to do this because sessions are very expensive in a multi-user web application environment.

Note: Component.getDfSession() calls Component.getDfSession(Component.COMPONENT_LIFETIME) so always use Component.getDfSession(Component.REQUEST_LIFETIME) whenever possible. The convenience method in this case is convenient indeed, but leads to inefficient code. Specifying the extra argument is easy to forget but the little bit of extra thought required to get a request-scoped session will be effort well spent when it comes time to performance test and tune your application.

Conclusion

This article covered the basics of session management in DFC 5.x and how those changes are exposed for use in the WDK toolkit. Follow these simple rules and your applications will behave correctly and be far more scalable than in DFC 4.x:

  • Always release your sessions (in a finally)
  • Don’t use objects obtained from a session which has been released
  • Use the DebugSessionManager Java system property in development environments
  • Always call Component.getDfSession(Component.REQUEST_LIFETIME) whenever possible instead of Component.getDfSession()

1 Votes | Average: 5 out of 51 Votes | Average: 5 out of 51 Votes | Average: 5 out of 51 Votes | Average: 5 out of 51 Votes | Average: 5 out of 5 (1 votes, average: 5 out of 5)
Loading ... Loading ...

Comment on this article:

You must be logged in to post a comment.

Notification

Subscribe to our newsletter to be notified when new articles are posted. You can unsubscribe at any time.