Skip to Content

Working in a very large project with an impressive amount of WD components I need a way to share some data between all of them. Such technique like external context mapping were not desired for me, because it would increase the amount of dependencies between components and would add lots of effort in mapping the context. That would also reduce the maintainability, because  every change on the shared component would lead to adaption of the context of externally mapped components. So I started to search for another way to implement such a “cross cutting” functionality without doing changes on every component using it. The requirements for that could be summarized as follows:

  • Introduce a method which can be easily accessed from everywhere  by means of Java (e.g. a static method) and store an object sharing it among different wd components in the context of the user session.
  • Let Web Dynpro control the lifecycle of this object
  • If the WD application is disposed, the object should do the same
  • Every WD component should be able to access the same instance of the object in the user session once created and modify it.

As you can see here, it is a typical functionality of the session context in e.g.  a servlet. However WD introduces much higher layer of UI abstraction, so going back to the roots using such things like WDProtocolAdapter would not be a good option. So going through Java Doc I’ve found a solution which is based on the IWDModel. Let me introduce an example to explain how it works.

First of all you have to implement the IWDModel interface. I implemented it only partly providing the values for two methods. All others still return null. Our implementation will also hold an object which is shared in the context of the application. That is assured by the scope type we return in the getModelScopeType method – WDModelScopeType.APPLICATION_SCOPE and in the way how we will register it by WD later on.

/**
 * The object holder shared in the context of the application.
 *
 * @author Dimitri Lubenski
 */
public class SharedObjectHolder implements IWDModel {

     private Object sharedObject;
     
     public String getModelInstanceId() {
          return String.valueOf(this.hashCode());
     }

     public WDModelScopeType getModelScope() {
          return WDModelScopeType.APPLICATION_SCOPE;
     }

     public Object getSharedObject() {
          return sharedObject;
     }

     public void setSharedObject(Object sharedObject) {
          this.sharedObject = sharedObject;
     }

...     

The instantiation of our SahredObjectHolder model is done by the WDModelFactory which will manage the lifecycle of the model for us. Let us create a static method in another class for that. We will also use generics to avoid casts in the code using our method.

/**
 * Manages the IWDModel instances.
 * 
 * @author Dimitri Lubenski
 */
public class SharedObjectManager {

     @SuppressWarnings("unchecked")
     public static  T getSharedObject(Class modelClazz)
     {
          return (T)WDModelFactory.getModelInstance(modelClazz, WDModelScopeType.APPLICATION_SCOPE);
     }
}

Called first, the WDModelFactory will instantiate our object and manage it, till the application is disposed. Hence calling the same method again will return the same object instance which was created during the first invocation. Let us check that with this code added to a WD button action handler:

  public void onActionGetId(com.sap.tc.webdynpro.progmodel.api.IWDCustomEvent wdEvent )
  {
    //@@begin onActionGetId(ServerEvent)
      SharedObjectHolder sharedObjectHolder = SharedObjectManager.getSharedObject(SharedObjectHolder.class);
     
      Object sharedObject = sharedObjectHolder.getSharedObject();
     
      IWDMessageManager mm = wdComponentAPI.getMessageManager();
     
      if (sharedObject==null) {
           sharedObjectHolder.setSharedObject("Hello World");
           mm.reportSuccess("Object created first with the ID =" + sharedObjectHolder.getModelInstanceId());
      }
      else {
           mm.reportSuccess("Object is alredy created. Value is " + (String)sharedObject);
      }
     
    //@@end
  }

In the screens below you can see the results of clicking the button first time and the second time.

Please keep in mind, the provided code has been tested on the CE 7.1 EhP1. Looking shortly into Java Doc of NW 04s I’ve found the same interfaces, so I suppose it to work there also.

What next?

The code displayed above is pretty well for my project because I need to share only one object. If I would need to share two I would have following options:

  • Create a new instance variable in the SharedObjectHolder
  • Derive a new class from the IWDModel and register it.

Both of them may be suboptimal depending on your project and need additional code changes. So it might be better to use java 5 generics with the same implementation of the SharedObjectHolder. To assure uniqueness of shared object instances with different class types an additional id can be passed to the WDModelFacrtory(see overloaded methods of getModelInstance)

To report this post you need to login first.

6 Comments

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

  1. Ivan Mirisola
    Hi Dimitri,

    Good job! It is always good to have other options at hand.
    I would like to know your thoughts on how this approach would differ from a standard EJB.

    Wouldn’t it be more compliant to have implemented an stateful EJB with all the needed information?

    Kind regards,
    Ivan

    (0) 
    1. Dimitri Lubenski Post author
      Hi Ivan,

      in case of a Stateful Session Bean you have to hold the reference to the bean instance after its lookup to use the bean statefully. Where could you hold it?  How would you share the same reference between different usages of the component holding it? Sounds not rely trivial but probably could work if you application has some kind of “a central component”…

      From another perspective, if your project does not use EJBs at all (or more precisely session beans), adding this “machinery” only to share something between components may be a bit overengineered.

      Best regards
      Dimitri

      (0) 
  2. Siarhei Pisarenka
    Hi Dimitry

    Helpful blog actually! An interesting approach has been chosen with WD Model API.

    Though, for storing simple String objects the standard WebDynpro API WDScopeUtils fits very well. The only a problem here is how/where to store generic Java objects. The solution in the blog solves the problem -, as I understand, any type of object can be stored in the WebDynpro scope.

    Another way which tried to extend WDScopeUtils to generic objects I read in blog “Sharing session context between parent and external windows running on same host”: Sharing session context between parent and external windows running on same host

    However, that XML based solution seems to be much more complicated and restricted then yours solution.

    BR, Siarhei

    (0) 
    1. Dimitri Lubenski Post author
      Hi Sergej,

      WDScopeUtils is not most reliable way for doing that, hence
      – according to the JavaDoc:  “Users must not rely on objects being available within the scope i.e. a get might fail even after a put with the same key has been done” (http://help.sap.com/javadocs/NW04S/current/wd/com/sap/tc/webdynpro/services/session/api/WDScopeUtil.html)
      – It shares strings, which is seldom enough and tends to be rather cryptic in cases you want to share anything else (use of (XML) serialization etc. brings runtime overhead (performance drawbacks) and has also some side effects)

      (0) 
      1. Siarhei Pisarenka
        Hi Dimitry

        It’s too late to reply, but anyway…

        >>WDScopeUtils is not most reliable way for doing that…

        I completely agree with you that the WebDynpro Scope is not 100% reliable thing. Still in my practice I never faced with causeless invalidations or any other fails of the scope.

        In addition I almost sure that all the IWDModel object instances are internally stored in the same place where WDScopeUtils puts its string keys – in the WebDynpro scope. I think that WebDynpro internally uses the same mechanism and storage for both API – for controlling lifetime of the IWDModel instances and for storing keys coming from WDScopeUtils. Indirectly the fact is confirmed by reference to WDModelScopeType in IWDModel interface:

        public WDModelScopeType getModelScope() {
             return WDModelScopeType.APPLICATION_SCOPE;
        }

        So personally I’ll continue to use WDScopeUtils as a storage for simple keys. In the complex cases I’ll try to use your solution.

        Regards, Siarhei

        (0) 
        1. Dimitri Lubenski Post author
          Hi Sergej,

          >>It’s too late to reply, but anyway…

          Better late than never 🙂

          >>Still in my practice I never faced…

          There is a JavaDoc describing the behaviour of the method. That should be strong enough for the developer not to “practice” 🙂 and not use it in the way not supported by the framework provider, because:

          – Internal implementation may change, and you will not be informed about.
          – Side effects may occure, which are not covered by your test and first come up in the production or in two years of production after e.g. the next patch.

          >> I think that WebDynpro internally uses the same mechanism and storage for both API…

          I don’t like to speculate about probable implementation details of the same reason written above. I cannot control that and I cannot change that hence I cannot rely on that.

          >> So personally I’ll continue to use WDScopeUtils

          That is your decision. I think WDScopeUtils is a nice way to give over something, if there is no need to assure, it will arrive the desired destination. Moreover, I would not combine that with the serialization because of:
          •     Performance effects
          •     Side effects of the serialization common to Java (that is yet another topic)

          Regards
          Dimitri

          (0) 

Leave a Reply