Skip to Content

“Load data when it’s actually needed. Do not load data in advance.” – Lazy Data Access principle, the main WebDynpro principle.

Prologue

With the blog I begin series of articles covering aspects of performance in WebDynpro Java applications. I really think that only fast and well tuned application may be named with honored term ‘effective application’. The main goal is to show the places in WebDynpro applications that in some cases can be very slow or even be an application bottleneck.

Dynamic Value Help Selectors (SVS, EVS)

As you know WebDynpro offers two basic types of value help selectors, SVS and EVS. (Well, there is also OVS, but the type is out of scope of the blog). You can read a lot about them in the official SAP documentation and other sources. For example, here: Value Help in Web Dynpro Applications.

A little bit well known things about SVS/EVS…

  • On UI SVS is represented with simple Drop Down element; EVS – with a special popup window bound to a regular Input Field element.
  • On UI SVS mainly displays user-friendly often localized texts; EVS – technical keys, guids, etc.
  • SVS should be used for small value sets; EVS – for large ones.
  • And both selectors obtain displayed keys/texts from the dedicated context attribute of a Simple Type with enumeration which the UI elements are bound to.

And almost always in real enterprise applications the help value selectors shall get data for the displayed value set dynamically at runtime. This is not because developers do not like static enumerations stored as simple types in WebDynpro Dictionary, but because the data most probably shall be retrieved from a database or from ABAP backend, or from Web or from somewhere else. And very often in order to retrieve the data for a value help we shall made some invocation – invoke BAPI, Web service, JDBC query, etc. And sometimes the invocation is remote.

How to implement dynamic SVS, EVS with remote invocation as data supplier?

Widespread, but Not Effective code

Well, the code is also well known. A set of help values for a value attribute is dynamically created at runtime by modifying the corresponding data type. Something like this one taken from the already mentioned article:

// Access interface ISimpleTypeModifiable for modifying the attribute's datatype
IWDAttributeInfo attributeInfo = wdContext.getNodeInfo().getAttribute("country");
ISimpleTypeModifiable countryType = attributeInfo.getModifiableSimpleType();
// Populate valueset
IModifiableSimpleValueSet valueSet = countryType.getSVServices().getModifiableSimpleValueSet();
for (int i = 0; i<40; i++) {
     valueSet.put("Key_" + i, "Country " + i);
}

What’s wrong with the code, you may ask? The answer is simple – it does not follow to the main WebDynpro principle of Lazy Data Loading. Here we are trying to fill in the value help with a data in advance, without being sure that user will really use our control. In fact we can perform the task of value help initialization (in some cases heavy and time-consuming task) just for nothing – user can skip the control without even having a look at the data.

Personally I saw one business application with a form displaying about 10 input fields with help values (EVS). And about 10 remote BAPIs were invoking in advance (in wdInit() method) for supplying the data for the help values. The time of the form opening was so annoying for business users, that they soon began to complain about this.

Use Supply Functions for Value Help data loading

WebDynpro supply-function is the mechanism which can bring a lazy-loading behavior to help value selectors. In order to do this we have to use a context node with attached supply-function for loading help values. Such context node could be of any form (value or model or mapped) and could be located anywhere in the context hierarchy. The main requirement that we shall apply to such node – it must have two attributes for representation help value key/text pair.

But how to connect help value enumeration type with the context node? There is ‘unknown’ WebDynpro API for the purpose. The API is used by WebDynpro itself when it’s initialized DropDownByIndex control (DropDownByIndex shall be bound to a context node with key/text attributes). We are going to use the API for DropDownByKey (SVS) & InputField controls (EVS):

public static IWDBoundValueSet WDValueServices.createBoundValueSet(
    IWDAttributeInfo attrInfoSelectedKey,
    IWDNode dataSource,
    IWDAttributeInfo attrInfoKey,
    IWDAttributeInfo attrInfoValue);

So the API allows to modify help value’s attribute type attrInfoSelectedKey and create a special value set bound to context node dataSource with key/text attributes. The context node will be accessed (validated) only at runtime when the help values are really necessary. This happens when user expands Drop Down or clicks on Input Field’s icon first time. Only then the supply-function will be invoked which will invoke BAPI, Web service, etc. in order to load the help values.

Lazy EVS example

In my example, two help value selectors (DropDownByKey & InputField) are bound to attribute countryCode(string) in node FormData. In general the FormData node represents the business object which the form on UI is working on. Of course the node may contain lots of other attributes – properties of the business object.

image
   View context structure and design

Node Countries[0..N] represents the list of countries – help values for the two help value selectors. It has supply-function which loads the list. In my example the supply-function is very simple, but in real application it can be heavy and time-consuming (BAPI, Web service, JDBC query, etc.).

public void supplyCountries(IPrivateHVTestView.ICountriesNode node, IPrivateHVTestView.IContextElement parentElement)
{
  //@@begin supplyCountries(IWDNode,IWDNodeElement)
     ICountriesElement el = node.createCountriesElement();
     el.setCode("BY");
     el.setText("Belarus");
     node.addElement(el);
     // ... add DE and US countries similarly
     // Or invoke time-consuming BAPI, Web service, JDBC query, etc.
  //@@end
}

Then we have to write a code which will modify the type of attribute countryCode and create an enumeration value set inside it. The value set will be bound to context node Countries. Help value key is represented by attribute Countries->code; help value text – by attribute Countries->text. The code shall be executed just one time at time of view creation, so view’s wdDoInit() is a proper place for it:

public void wdDoInit()
{
  //@@begin wdDoInit()
    IWDNodeInfo formDataInfo = wdContext.getNodeInfo().getChild("FormData");
     // Country Code HV
     this.countryCodeVS =
          createBoundValueSet(
               formDataInfo.getAttribute(IFormDataElement.COUNTRY_CODE),
               wdContext.nodeCountries(),
               ICountriesElement.CODE,
               ICountriesElement.TEXT);
  //@@end
}
...
//@@begin others
private IWDBoundValueSet countryCodeVS;
public static IWDBoundValueSet createBoundValueSet(
     IWDAttributeInfo hvTargetAttr,
     IWDNode hvNode,
     String keyAttr,
     String valueAttr)
{
    IWDNodeInfo hvNodeInfo = hvNode.getNodeInfo();
     return WDValueServices.createBoundValueSet(
          hvTargetAttr,
          hvNode,
          hvNodeInfo.getAttribute(keyAttr),
          hvNodeInfo.getAttribute(valueAttr));
}
//@@end

Note: Here invocation wdContext.nodeCountries() will not bring to execution of the supply-function. Node Countries will still have invalid (not validated) state.

How to invalidate Value Help Selectors?

Usually we load help values just one time per a user session. However, sometimes we have to invalidate (clear) the value set. For example, in case of help values depends on some input parameters we have to invalidate such value set when the input parameters changed.

For the purpose I practise the following code. The code invalidates value set’s context node and the value set itself. The rule is we have to invalidate value set when we invalidate the context node.

/** If Country HV depends on Region invoke the method each time the Region is being changed. */
public void invalidateCountryHV()
{
     wdContext.nodeCountries().invalidate();
     countryCodeVS.invalidate();
}

Final notes

  • For EVS this is all that it’s necessary to do to support lazy data loading. For SVS we have to add additionaly some small piece of code. See my next blog with SVS examples.

  • In a real applications we should separate pure help value selector’s logic from the logic of data supply. This is a question of good application architecture. Help value selector is logically belong to a view with UI controls, so the code in wdDoInit() shall be located in a view controller.
    At the same time, node Countries with supply-function shall be moved to component controller (or custom controller). If we do this the code in a view controller shall be the following:

    IWDNodeInfo formDataInfo = wdContext.getNodeInfo().getChild("FormData");
    IContextNode ccContext = wdThis.wdGetHVTestController().wdGetContext();
    // Country Code HV
    this.countryCodeVS = createBoundValueSet(
        formDataInfo.getAttribute(IFormDataElement.COUNTRY_CODE),
        ccContext.nodeCountries(),
        ICountriesElement.CODE,
        ICountriesElement.TEXT);

    Important !: Do not try to create a mapping for node Countries between component controller and view controller. I do not know why, but the mapping will force the supply-function to execute directly in wdDoInit() which breaks the idea of lazy-loading. I think that this is a bug or just not accurate implementation in WDValueServices API (I saw the strange behaviour on NW 7.00 SP13).

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

Leave a Reply