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.
Blue Fish Development Group
701 Brazos St. #700
Austin, TX 78701
(512) 469-9300
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.
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.
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.
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.
IDfSessionManager before the session is acquired by calling setIdentity.
newSession) requires only the name of the docbase.
IDfSessionManager is used
to release the session at the end of the example.
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.
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.
The pattern of session usage is quite different in DFC 5.x than in previous versions. It follows three steps:
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);
}
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.
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.
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).
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.
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.
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:
DebugSessionManager Java system property in development environmentsComponent.getDfSession(Component.REQUEST_LIFETIME) whenever possible instead of
Component.getDfSession()
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.