cancel
Showing results for 
Search instead for 
Did you mean: 

Paradigm Shift: the WDP Model & the Power to Bind

Former Member
0 Kudos

As a developer coming from an OO/java background, I recently started to study and use the Java Web Dynpro framework for creating enterprise portal applications.

Up to this point, I've developped 2 or 3 WDP projects - and in so doing, I've tried to reconciliate my java-influenced development methods with the SAP way of doing things. I'd say for the most part it was rather painless. I did, however, find a serious problem as far as I'm concerned in the way SAP has promoted the use of the java bean model importer.

<a href="https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/u/251697223">david Beisert</a> created this tool and presented it to the SDN community in 2004 in his <a href="/people/david.beisert/blog/2004/10/26/webdynpro-importing-java-classes-as-model The same year (don't know if it was before or after), SAP published '<a href="https://www.sdn.sap.com/irj/servlet/prt/portal/prtroot/docs/library/uuid/1f5f3366-0401-0010-d6b0-e85a49e93a5c">Using EJBs in Web Dynpro Applications</a>'. Both of these works presented simplified examples of invoking remote functions on EJB backends (an add() function in the case of David Beisert's example, and a calculateBonus() function in the case of the SAP publication). Accordingly, they both recommended the use of the Command Bean pattern as an implementation strategy for their respective examples. Which I don't totally disagree with, in these particular circumstances. A simple execute() method is perfectly suitable if one needs to EXECUTE a remote function call - whether it be a calculate() method invoked on a EJB Session Bean or an RFC call made to some remote ABAP system.

Problem is, not everything in life is a function call ! To me, it makes very little sense to model everything as a command if it doesn't match your business model. The needs of your application should dictate the architecture of your model and not the other way around.

This unjustifiable fixation on the Command Bean pattern is probably to blame for the fact that very little up to this point seems to have been written on the subject of the power of the binding mecanism as a most powerful tool in the arsenal of the Web Dynpro developer.

What's this ?

Binding can make it possible to abstract away most of the nitty gritty context node navigation and manipulation logic and replace it with more intuitive and more developer-friendly model manipulation logic.

There was a time when programs that needed persistence were peppered with database calls and resultset manipulation logic. Hardly anyone codes like that anymore.. and with good reason. The abstraction power of Object Oriented technologies have made it possible to devise human friendly models that make it possible for developers to concentrate on business logic, and not have to waste time dealing with the low-level idiosyncrasies of database programming. Whether it be EJBs, JDO, Hibernate... whatever the flavour... most serious projects today utilize some sort of persistence framework and have little place for hand-coding database access logic.

I feel that the WD javabean model offers the same kind of abstraction possibilities to the Web Dynpro developer. If you see to it that your WD Context and javabean model(s) mirror each other adequately, the power of binding will make it possible for you to implement most of your processing directly on the model - while behind the scenes, your context and UI Elements stay magically synchronized with your user's actions:


+-------------+        +-------------------+         +--------------+        +------------+
|    Model    |<-bound-| Component Context |<-mapped-| View Context |<-bound-| UI Element |
+-------------+        +-------------------+         +--------------+        +------------+

                       o Context Root                o Context Root
                       |                             |
ShoppingCartBean <---- +-o ShoppingCart Node <------ +-o ShoppingCart Node
{                        |                             |
  Collection items <---- +-o CartItems Node <--------- +-o CartItems Node <-- ItemsTable
  {                        |                             |
    String code; <-------- +- Code <-------------------- +- Code <----------- CodeTextView
    String descrip; <----- +- Description <------------- +- Description <---- DescTextView
  }
}

Let's examine an example of this concept. I propose a simple but illustrative example consisting of a shopping cart application that presents the user with a collection of catalog items, and a shopping cart in which catalog items may arbitrarily be added and/or removed.

The Component and View contexts will be structured as follows:


   o Context Root
   |
   +--o ProductCatalog       (cardinality=1..1, singleton=true)
   |  |
   |  +--o CatalogItems      (cardinality=0..n, singleton=true)
   |     |
   |     +-- Code
   |     +-- Description
   |
   +--o ShoppingCart         (cardinality=1..1, singleton=true)
      |
      +--o ShoppingCartItems (cardinality=0..n, singleton=true)
         |
         +-- Code
         +-- Description

Let's examine how a conventional Command Bean implementation of this component could be coded. Later on, I'll present a more object-oriented model-based approach. We can then compare the differences.


public class ProductCatalogCommandBean
{
   // collection of catalog items
   Collection items = new ArrayList();

   public void execute_getItems()
   {
      // initialize catalog items collection
      items = new ProductCatalogBusinessDelegate().getItems();
   }
}

This command bean will serve as a model to which the ProductCatalog node will be bound. This happens in the supply function for that node in the component controller:


public supplyProductCatalog(IProductCatalogNode node, ...)
{
   // create model
   model = new ProductCatalogCommandBean();
   
   // load items collection
   model.execute_getItems();

   // bind node to model
   node.bind(model);
}

No supply function is needed for the ShoppingCart node, since it is empty in its initial state. Its contents will only change based on the user adding to or removing items from the cart. These operations are implemented by the following two event handlers in the view controller:


public void onActionAddItemsToCart()
{
   // loop through catalog items
   for (int i = 0; i < wdContext.nodeCatalogItems().size(); i++)
   {
      // current catalog item selected ?
      if (wdContext.nodeCatalogItems().isMultiSelected(i))
      {
         // get current selected catalog item
         ICatalogItemsElement catalogItem = wdContext.nodeCatalogItems().getElementAt(i);

         // create new element for ShoppingCartItem node
         IShoppingCartItemsElement cartItem = wdContext.createShoppingCartItemsElement();

         // initialize cart item with catalog item
         cartItem.setCode       (catalogItem.getCode());
         cartItem.setDescription(catalogItem.getDescription());

         // add item to shopping cart
         wdContext.nodeShoppingCartItems().addElement(cartItem);
      }
   }
}

public void onActionRemoveItemsFromCart()
{
   // loop through cart items
   for (int i = 0; i < wdContext.nodeShoppingCartItems().size();)
   {
      // current shopping cart item selected ?
      if (wdContext.nodeShoppingCartItems().isMultiSelected(i))
      {
         // get current selected item
         IShoppingCartItemsElement item = wdContext.nodeShoppingCartItems().getElementAt(i);

         // remove item from collection
         wdContext.nodeShoppingCartItems().removeElement(item);
      }
      else
         // process next element
         i++;
   }
}

From what I understand, I believe this is the typical way SAP recommends using Command Beans as a model in order to implement this type of simple component.

Let's see how the two same event handlers could be written with a more comprehensive object model at its disposal. One whose role is not limited to data access, but also capable of adequately presenting and manipulating the data that it encapsulates. (The actual code for these model beans will follow)


// I like to declare shortcut aliases for convenience...
private ProductCatalogBean catalog;
private ShoppingCartBean   cart;

// and initialize them in the wdDoInit() method...
public wdDoInit(...)
{
   if (firstTime)
   {
      catalog = wdContext.currentNodeProductCatalog().modelObject();
      cart    = wdContext.currentNodeShoppingCart  ().modelObject();
   }
}

Now the code for the event handlers:


public void onActionAddItemsToCart()
{
   // add selected catalog items to shopping cart items collection
   cart.addItems(catalog.getSelectedItems());
}

public void onActionRemoveItemsFromCart()
{
   // remove selected shopping cart items from their collection
   cart.removeItems(cart.getSelectedItems());
}

I feel these two lines of code are cleaner and easier to maintain than the two previous context-manipulation-ridden versions that accompany the command bean version.

Here's where the models are bound to their respective context nodes, in the Component Controller.


public supplyProductCatalogNode(IProductCatalogNode node, ...)
{
   node.bind(new ProductCatalogBean(wdContext.getContext()));
}

public supplyShoppingCartNode(IShoppingCartNode node, ...)
{
   node.bind(new ShoppingCartBean(wdContext.getContext()));
}

Notice that a context is provided in the constructors of both models (a generic context of type IWDContext). We saw earlier that our model needs to be able to respond to such requests as: catalog.getSelectedItem(). The user doesn't interact directly with the model, but with the Web Dynpro UI Elements. They in turn update the context... which is where our model will fetch the information it requires to do its job.

Also note that a model is provided for the shopping cart here, even though it has no need to access or execute anything on the back-end. Again, the model here is not being used as a command bean, but rather as a classic object model. We simply take advantage of the power of binding to make ourselves a clean and simple little helper that will update for us all the relevant context structures behind the scenes when we tell it to.

Here are the ShoppingCartBean and ProductCatalogBean classes (I've omitted a few getter/setter methods in order to reduce unnecessary clutter):


public class ShoppingCartBean
{
   Collection items = new ArrayList();
   IWDNode    itemsNode;

   public ShoppingCartBean(IWDContext context)
   {
      // initialize shortcut alias for ShoppingCartItems node
      itemsNode = context.getRootNode()
                         .getChildNode("ShoppingCart", 0)
                         .getChildNode("ShoppingCartItems", 0);
   }

   public void addItems(Collection items)
   {
      this.items.addAll(items);
   }

   public void removeItems(Collection items)
   {
      this.items.removeAll(items);
   }

   public Collection getSelectedItems()
   {
      return ItemDTO.getSelectedItems(itemsNode);
   }
}

public class ProductCatalogBean
{
   Collection items;
   IWDNode    itemsNode;

   public ProductCatalogBean(IWDContext context)
   {
      // fetch catalog content from back-end
      items = new ProductCatalogBusinessDelegate().getItems();

      // initialize shortcut alias for CatalogItems node
      itemsNode = context.getRootNode()
                         .getChildNode("ProductCatalog", 0)
                         .getChildNode("CatalogItems", 0);
   }

   public Collection getSelectedItems()
   {
      return ItemDTO.getSelectedItems(itemsNode);
   }
}

Notice that both classes delegate their getSelectedItems() implementation to a common version that's been placed in the ItemDTO class. It seems like a good place to put this type generic ItemDTO-related utility.

This DTO class could also have been used by the Command Bean version of the event handlers.. would reduce somewhat the number of loops. At any rate, the ItemDTO class shouldn't be viewed as an "overhead" to the model-based version, since it usually will have been created in the J2EE layer,for the marshalling of EJB data (see <a href="http://java.sun.com/blueprints/corej2eepatterns/Patterns/TransferObject.html">Data Transfer Object Pattern</a>). We just take advantage of what's there, and extend it to our benefit for packaging some common ItemDTO-related code we require.

// DTO made available by the EJB layer

import com.mycompany.shoppingcart.dto.ItemDTO;


public class ItemDTO extends com.mycompany.shoppingcart.dto.ItemDTO 
{
   String code;
   String description;

   public ItemDTO()
   {
   }

   public ItemDTO(String code, String description)
   {
      this.code = code;
      this.description = description;
   }

   //
   // returns ItemDTOs collection of currently selected node elements
   //
   public static Collection getSelectedItems(IWDNode node)
   {
      // create collection to be returned
      Collection selectedItems = new ArrayList();

      // loop through item node elements
      for (i = 0; i < node.size(); i++)
      {
         // current item element selected ?
         if (node.isMultiSelected(i))
         {
             // fetch selected item
             IWDNodeElement item = node.getElementAt(i);

             // transform item node element into ItemDTO
             ItemDTO itemDTO = new ItemDTO(item.getAttributeAsText("Code"),
                                           item.getAttributeAsText("Description"));

             // add selected item to the selectedItems collection
             selectedItems.add(itemDTO);
         }
      }
      return selectedItems;
   }
}

Notice that the getSelectedItem() method is the only place in our model where context node navigation and manipulation actually takes place. It's unavoidable here, given that we need to query these structures in order to correctly react to user actions. But where possible, the business logic - like adding items and removing items from the cart - has been implemented by standard java constructs instead of by manipulating context nodes and attributes.

To me, using a java bean model as an abstraction for the Context is much like using EJBs as abstractions of database tables and columns:


                     abstracts away
           EJB model --------------> database tables & columns

                     abstracts away
  WDP javabean model --------------> context  nodes  & attributes

Except that a javabean model (residing in the same JVM) is much more lightweight and easy to code an maintain than an EJB...

Before concluding, it might be worth pointing out that this alternative vision of the Web Dynpro Model in no way limits the possibility of implementing a Command Bean - if that happens to suit your business needs. You will of course always be able to implement an execute() method in your WDP Model if and when you feel the need to do so. Except that now, by breaking free of the mandatory Command Bean directive, you are allowed the freedom to ditch the execute() method if you don't need such a thing... and instead, replace it with a few well-chosen operations like getItems(), addItems(), removeItems(), getSelectedItems()... which, as we've just seen can add significant value to the javabean model made available to your WDP component.

Comments would be appreciated on this issue (if anyone has had the time/courage/patience to read this far...;). Am I alone here intrigued by the potential of this (up until now) scarcely mentionned design strategy ?

Romeo Guastaferri

Accepted Solutions (0)

Answers (1)

Answers (1)

Former Member
0 Kudos

Hi Romeo,

thanks for sharing this with the community. I am little bit surprised that the command pattern was understood as the only way on how to use the Javabean model in conjunction with EJBs. The command pattern blog of mine was just a very simplified example of how a functional call can be translated to a Java Bean model. Actually it was to show how the paradigm of a model works. I personally use a similar approach to yours. It seldomly makes sense to map an EJB method one to one to a model, but the javabean model must be driven by the Userinterface and represents a bridge between the business service layer and the ui. I personally even think that often it does not make sense to map RFC function like they are to the Web Dynpro Context. Most often you end up writing ZBAPIs that return structures like they are used in the UI. But if you use a java bean model as a layer in between your service layer, you are more flexible in evolving the application. Anyways design patterns for the java bean model need to be discussed more on SDN as they really add very valuable possibilities you would never have when working with value nodes alone. With the Javabean model we are back in the real OO world where things like inheritance work, things that are really not too well supported by the native WD features. I encapsulate every context of mine as javabeans. This has nothing to do with EJBs (which I am personally not a fan of) but only with the fact that I want to work with the power of the OO world.

rgds

David