Print Header

Related Topics

Related Case Studies

Contact Blue Fish

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

Introduction to WDK Scoping

April 10, 2006 - Article by Mirza Fazlic

This article will introduce the reader to the concept of “scope” as used in the WDK framework, specifically addressing fundamental framework services and related design and implementation details that should be understood when customizing and developing WDK-based application

About This Article

Though the intention of this article is to help novice WDK developers quickly get up to speed and overcome some of the common development hurdles, it is recommended that you are familiar with at least some of the basics, including controls, actions, components, behavior classes, etc. Preferably, you have already completed some customizations. It is also highly recommended that you at least keep the WDK Development Reference Manual on hand. Finally, though most of the discussion applies across all of the versions, this document has been written with WDK 5.3 specifications in mind.

Introduction to Scoping

One of the core architectural layers of the WDK framework is the component model. It allows WDK-application architects to develop applications using a set of existing pre-built components and actions, each one encapsulating specific repository functionality. The term “scope” or “scoping” refers to the ability of the framework to select appropriately which component definition (the XML configuration file) will be used for execution of a requested component or action depending on the current runtime conditions (the current state) of the application.

The motivating factor behind the introduction of scoping in WDK is reusability. Of course, we should always develop software with reusability in mind, but this concept is even more critical to the applications developed using WDK. Through its application layers of wdk, webcomponent, webtop and numerous other WDK-based applications, Documentum provides a very sophisticated set of out-of-the-box functionality. Nevertheless, in the real world of customized business repositories and content-driven applications, all of this functionality would be useless without the ability for developers to plug in their own customizations rapidly. Very often the entire out-of-the-box Documentum application may work just fine for 90 percent of our business; however, the inevitable requirement “to process these types of documents in these work flows with these versions by these users” comes along sooner or later. Among other WDK-development details, let’s also investigate how component scoping can help us design a reusable application in the most effective way

Framework Support

Though requirement specs typically are foggy and unclear, let’s assume that we understand them somewhat. We know what we want to happen in our WDK application and we know there are several variables in this particular equation; we are just not exactly sure what, where, when, and how “to scope.” In WDK, there are several framework concepts and services that work together. It is imperative that we become familiar with the internal implementation of these concepts and services before proceeding. After these are defined, it will become clear why “menu options are still enabled,” “my custom qualifier degrades performance,” etc. Let’s take a quick inside look at some of the responsible players. Then, we will be ready to come back and put the “<scope a=b, d not c>” part of your component configuration files under a microscope.

From a high-level viewpoint, the entire processes that start with a request for a particular component and terminate with the framework dispatching that component with a specific definition file are always the same. Components and actions are specified using the XML configuration files, and all of these definitions are loaded into memory during application start-up. These XML configurations use very specific terms (or qualifications) to describe situations when the component in question is to be applied. As the application is running and user is interacting, the executing components are driving and describing the current state of the application. When the request for a component is made, the WDK framework will interrogate and translate this runtime state and match it to the desired definition. In WDK terminology, the runtime state of the application is referred to as the context. Various qualifications used to describe that context — as well as above-mentioned “situations” in the component XML configuration files — are referred to as scope qualifiers. Part of the framework responsible for appropriately interpreting the context using scope qualifiers and dispatching a component is known as configuration service.

Tip

The context sensitivity of the WDK UI, such as menu items being disabled for a particular scoped action, is merely a by-product of the above process. It is the nature of the menu control —or any control extending the action control — to disable itself when a requested component definition file cannot be found. For example, the configuration service could not match the current context to the appropriate definition (or the context has been successfully matched to a definition file described as “not defined”).

Context and the Application Runtime

As we have mentioned above, we need to monitor and keep track of the application runtime state. This runtime state is described using a list of name/value pairs, and in WDK, it is typically implemented using the context. The term “typically” is important to note here. The runtime state can also be described by any arbitrary name/value pair; for example, the innate qualifications describing security permissions of the currently logged-in user.

The context is initialized and driven by all of the other components that the user is interacting with. For example, when a user displays contents of a specific folder and selects a specific document, the browser-tree component is keeping track of the location (selected folder or cabinet), while the document-listing component is keeping track of the details of the currently selected object (document type = article, lifecycle state = work in progress, etc). You can initialize the context either in Java code, as in your component behavior class, or using controls on the JSP page, as in your components view:

Example: Setting the context using controls in the JSP page
        
<dmf:datagrid ...
  <dmf:datagridRow...
    <dmfx:actionmultiselectcheckbox name='check'>
      <dmf:argument name='type' datafield='r_object_type' />
    </dmfx:actionmultiselectcheckbox>
    ...
  </dmf:datagridRow>
  ...
</dmf:datagrid>
...

    

Tip

When you are setting the context in the JSP page, you have to make sure that the controls are providing enough information to do so. In the above example, the data grid must be provided a valid ‘r_object_type’ field by the underlying data provider (i.e., selected for by the DQL query, a valid column in the scrollable result set, etc). Only then the contained argument controls will be able to extract the value and properly initialize the context.

Example: Setting the context in the code (i.e., component behavior, action execution, etc.)
        ...
// Initialize context 
Context context = getContext();
context.set("type", "dmdeveloper_article");

    

Tip

If you find yourself developing an application similar to the Documentum Administrator, where you are using the navigation tree on the left to launch different “management components,” such as “user administration,” “taxonomies administration,” and “jobs administration,” you will probably also require certain menu items to be context sensitive. For example, those related to jobs should only be visible in the job administration node. In this case, you can simply set the context; for example, in the onInit method of the corresponding management component, and scope the desired job actions to the type “job.” Note: “Job” doesn’t even have to be a valid docbase type

Configuration Service

The configuration service comes into play immediately upon the start-up of the application. During initialization, it will diligently parse and crunch through all of the XML component configuration files it encounters. It will store these files into an internal lookup dictionary, where each file will be uniquely identified by a specific lookup key. Once it is finished verifying and loading all of the definition files, the configuration service is ready to assume the role of a hybrid MVC controller.

When a request for a component or an action comes in, the configuration service will first translate the context and try to create a lookup key. It will then try to match a previously loaded definition file to that key. So what does a lookup key look like, and why do we even need to care about it?

We should at least be aware of several related points. First, it can be hard to debug a WDK-application scoping problem, especially for more complex scoping scenarios, such as multiple scope qualifier inclusions and exclusions (i.e., scope type=”dm_article,” role=”not administrator”). It can be very useful to turn on the configuration-service tracing and see the exact keys and the matching configuration files the service stored upon start-up.

Tip

To turn on tracing, set the CONFIGSERVICE flag to TRUE in “trace.properties” file. Another useful trace flags are CONTEXT and COMPONENT.

Second, if you are writing your custom qualifier, you need to be aware of the existence of the above lookup key and the mentioned context translation and lookup key generation method call by the framework, as it can significantly affect performance (see the Custom Qualifiers section for more information).

The action definition file called “playground_actions.xml” is scoped as follows:

Example scoped action configuration file
        ...
<scope type='dmdeveloper_article' role='administrator'>
<action id=publish_article_action>
    

The configuration service will generate a similar set of information as the following:

Example Configuration Service lookup-key
        ...
Setting: action[id=publish_article_action];
Scope: type=dmdeveloper_article, role=administrator, clientenv=*, application=wdkplay, version=latest, location=*, entitlement=*’;
Config File: C:\export\workdir\playground\wdkplay\app/wdkplay/config/library/playground_actions.xml

    

Scope Qualifiers

As mentioned above, the configuration service will translate the context and try to create the appropriate lookup key; nevertheless, there is only one configuration service out there responsible for all of the WDK application. How does it appropriately handle various qualifications used only in our custom application? In other words, a qualification such as “lifecycle state” may make sense for a compliance-manager application and a qualification such as “document type” for a web-publishing one.

The mechanisms that help the configuration service appropriately accomplish the above are known as scope qualifiers and all they speak the language of the IQualifier interface. The scope qualifiers are listed in the application configuration file, app.xml, in the order of precedence. To add your own custom qualifiers, you would also implement the IQualifier interface and add the implementing class in the above file.

Tip

Every qualifier affects the performance of your application. If you are familiar with all of the required business functionality of your WDK application (i.e., which components are being used), you should remove the un-used qualifiers (i.e., none of the components of your application are scoped using them).

Documentum provides us with the following scope qualifiers we can use out of the box:

Docbase Type Qualifier - One of the most used qualifiers, used to qualify on a specific docbase type, such as dm_document, dm_sysobject, dmdeveloper_article, and other.

        
<scope type="dmdeveloper_article"> 
    

User Role Qualifier - Another often-used qualifier is a specific role as defined in the repository, such as administrator, developer, and other similar roles..

        
<scope role="administrator"> 
    

Docbase Name Qualifier - This qualifier is used to qualify against different docbases (repositories). It can be very useful when you need to disable certain actions or components for a specific repository. Another typical example is customizing the doc-list component to show some custom attributes, yet these attributes only exist in one docbase. Using docbase qualifier, you can easily scope several doc/object listing components.

        
<scope docbase="mydocbase">  
    

Tip

You cannot use this scope, for example, to scope those components that are applicable across the repositories, such as, unfortunately, the menu bar. If you need to provide a different menu for different repositories, you will have to scope the individual actions and mark them as “not defined.”

WDK Component Version Qualifier - When you have invested serious man-hours into a specific component and are simply too attached to it, yet the time has come to upgrade to the new version of WDK base, you can bring along your old component and scope the desired scenarios to use it instead of using the version qualifier.

        
<scope version="5.2.5">

    

These are some of the commonly used scope qualifiers. Each WDK application — such as Compliance Manager, Web Publisher, etc. — will provide scope qualifiers that are useful and relevant to that application. The entire list of available qualifiers (no pun intended) is beyond the scope of this article. Please consult the WDK documentation of your application on the specifics.

In case you are not happy with the above selection and believe that you deserve your own custom-made qualifier, here is how to do so and what to watch out for during the process. Indeed, implementing a custom qualifier can be very beneficial, and completely reduce or minimize the numerous precondition logic of your WDK application; nevertheless, you should be aware that adding it will probably result in a significant change of your entire architecture, and should therefore be considered and decided upon early in the design phase.

Custom Qualifiers and IQualifier Interface

To add a custom qualifier, you need to implement the IQualifier interface and add the implementing class to the app.xml <qualifiers> section.

Tip

It is possible that the app.xml file you are working with does not even contain the “qualifiers” section. Remember that even the application configuration files are inherited, so that means that your current application was satisfied with all of the qualifiers from the layer below. In that case, find the app.xml file of the application you are extending, and copy ALL of the qualifiers and add your custom one.

The IQualifier interface is described as follows:

        
public interface IQualifier {
    /**
     * This method returns zero or more related context names used by this
     * qualifier, such as "type," "location," "objected," etc. It will usually
     * be the same String, such as returned by the getScopeName() (see below).
     * During application start-up, configuration service will call this method
     * on all of the qualifiers and create an internal list of valid context
     * names. You do not have to be overly concerned with the above list — just
     * be aware that the configuration service needs it during various component
     * dispatching, (i.e., to determine which of the arguments contained in the
     * HttpRequest object need to be propagated further)
     * 
     */
    public String[] getContextNames();

    /**
     * This method will return the official name of the scope the qualifier
     * resolves, i.e., the x of your <scope x=’y'> component definition. When
     * configuration service is loading up all the configuration files, whenever
     * it encounters a scope value, it will run a check against this method. If
     * you want something like <scope lifecyclestate=”draft,” one of the
     * IQualifier implementations better return “lifecyclestate” in this method.
     * This is why you can safely try to remove various unused qualifiers.
     * 
     */
    public String getScopeName();

    /**
     * Before we discuss the remaining IQualifier methods, it is important that
     * we take a step back and review couple of points. As we remember from the
     * Configuration Service section, when a request for a specific component
     * comes in, the configuration service is going to interrogate the context
     * and create a lookup key that it can use to try and locate the appropriate
     * component definition file. In order to make the lookup key, it will
     * simply ask EVERY qualifier the following public String
     * getScopeValue(QualifierContext context). Unfortunately, as it is
     * mentioned above, this means that in order to render appropriately any
     * Action Control, such as those hidden away under <dmf:menuitem> or
     * <dmf:actionlink>, the configuration service will try to make the lookup
     * key for ALL which are currently rendered. This means that for 20 menu
     * items, the service will call getScopeValue method for EVERY qualifier. If
     * the getScopeValue method implementation of your custom qualifier is
     * performing anything expensive, such as fetching the object, the amount of
     * work that needs to be done just to render will be significant. Therefore,
     * the fetch for the object should be your last option. Instead, you should
     * first try to retrieve simply the value from the passed in Context
     * argument, i.e., String lifeCycleState = context.get(”lifecyclestate”).
     * For this to work, of course, either your component behavior class or the
     * JSP page had to initialize the context with the appropriate value for the
     * lifecyclestate. Consequently, in order to take full advantage of using
     * custom qualifiers, you will have to customize various driving components
     * as well, such as doclist, objectlist, etc.
     */

    /**
     * After configuration service calls getScopeValue() and creates a lookup
     * key, it will try to find a matching definition. If no match has been
     * found, it will then try to “generalize” the key and “move” up the
     * hierarchy. In other words, it will try to create a different, more
     * general key to use for the lookup. The following methods, if applicable,
     * returns the appropriate parent or alias scope values. For example, for
     * the docbase type, this method returns the parent type. For a specific
     * role, it would return parent role, if any. If, for example, we had a
     * custom ‘lifecycle state’ qualifier — it could return the “parent”
     * lifecycle state, which, for example, could be simply the previous state
     * of the life cycle (if it made hierarchical sense). If you will be using
     * the hierarchical qualifiers design in your application, you should
     * therefore implement the following two methods. Otherwise, for the most
     * common cases, you can simply return nulls.
     */

    public String getParentScopeValue(String strScopeValue);

    public String[] getAliasScopeValues(String strScopeValue);

}
    

Scoping Actions and Components

The scope of your component or action configuration file is defined as the outer XML element: <scope> that wraps one or multiple configuration elements. The general syntax is:

            
<scope qualifier name = qualifier value>, i.e. <scope type=dm_document> 
        

Tip

Typically, the convention is to scope components on an individual basis. Therefore, except for tightly coupled components (i.e., specific locator and its corresponding container), you should store them into individual files. On the other hand, actions are typically scoped together, i.e., all of the actions applicable only to dm_sysobject should be defined in one physical configuration file.

In addition, remember that the component can be scoped using both multiple qualifiers as well as multiple values for that qualifiers.

Tip

Obviously, there can be more than one matching qualification. For example, if we had two components — one scoped with user role as role=”administrator’ and the other component scoped with document type qualifier, i.e., type=’dm_article’ — the order of the qualifiers specified in the app.xml determines which of the two valid definitions takes precedence

Finally, as mentioned above, to disable (undefine) a particular component definition for a particular scope, use the not defined keyword.

Tip

When changing the component scope, remember that the configuration service needs to reload all of the definition files and update the lookup dictionary. You can either restart the server or visit the following link, which will instruct the configuration service to reload the changes: http://your_web_server:port:/your_application/wdk/refresh.jsp

Filtering

We use scope to determine the entire definition file to be applied for particular qualifications. The WDK framework also gives us a higher level of granularity with the use of filters, by which we can further decide which parts of the above definition to use (hide or show). A very typical example of filtering is hiding or showing contained components in a container, depending on a specific qualifier, such as for example object type. For the following example, we assume that we have different types of jobs. All types have basic informational metadata that can be set, such as name of the job, etc. Some of them can be scheduled to run at regular intervals, and some are supposed to run only once. In that case, we can simply filter the scheduler component out of the container using the following

        
<component id="job_info_container"> 
  ... 
    <contains> 
      <component>iim_job_info</component> 
        <filter type="scheduled_job"> 
          <component>iim_job_scheduler</component> 
        </filter> 
    </contains> 
  ...
</component> 			

    

Another common example would be to filter which component view (JSP page) to use for a particular component, such as a general view vs. portlet view.

Tip

You can filter ANY part of your component definition file. If you are using your component configuration file to store various “properties” that are later looked up in the behavior class, or if for example, you are passing some static arguments, you can also filter these if there is a qualifier that fits your needs.

For example, assume that we want to use the same create new job component. The only difference will be the available list of valid job types, depending on the parameter called job group. We can accomplish the above simply by filtering the argument that are passed in by the launch action to the component it is launching:

        
<scope> 
  <action id="new_job"> 
    <execution class="com.documentum.web.formext.action.LaunchComponent"> 
    <component>job_info</component> 
    <container>job_info_container</container> 
    <arguments> 
      <filter type="dm_basic_job"> 
        <argument name="jobGroup" value="1" /> 
      </filter> 
      <filter type="dm_advanced_job">
        <argument name="jobGroup" value="2" /> 
      </filter> 
    </arguments> 
    </execution> 
  </action> 
</scope>	

    

Conclusion

Scoping your actions and components can be somewhat confusing, especially for novice WDK developers. Things often appear to work magically (or not), and debugging and troubleshooting can be very daunting. As we have seen, quite a few things have to align properly for the entire scenario to work, and hopefully this article will help you shed some light at and point you into the right direction. Once, however, you get in the scoping “zone,” you will begin to appreciate all of the development advantages it can provide.

 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 (0 votes, average: 0 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.