Sunday, April 21, 2013

Life of Dynamic View Object & Entity Object Across Passivation/Activation

For one of the requirement I use Dynamic VO & EO along with dynamic form extensively. This works perfectly until the AM passivates. But when it activates it started throwing exception:

javax.el.PropertyNotFoundException: Target Unreachable, 'DynamicForm_dynamic_VO_form1' returned null

This issue is visible only on JDeveloper 11.1.2.0.0 & even it exists in later versions but its hidden by some other bug as per Oracle dev.

Looking into the code deeper.. I have put my code as per dev guide:


private static final String DYNAMIC_EO = "dynamic.EO";
private static final String DYNAMIC_VO = "dynamic.VO";
private static final String DYNAMIC_VO_INSTANCE_INTERNAL = "DynamicVOInternal";


//Entity creation
EntityDefImpl newEntity = new EntityDefImpl(DYNAMIC_EO);
newEntity.setSource("TEST_TABLE");

//Code for AttributeDefImpl creations..

newEntity.resolveDefObject();

newEntity.registerDefObject();

//ViewDef creation

ViewDefImpl newView = new ViewDefImpl(DYNAMIC_VO);
newView.addEntityUsage("e", DYNAMIC_EO, false, false);
newView.addAllEntityAttributes("e");

newView.resolveDefObject();
newView.registerDefObject();

//ViewObject instance creation
ViewObject dynmVO = createViewObject(DYNAMIC_VO_INSTANCE_INTERNAL, DYNAMIC_VO);

And this view instance is used for dynamic form in UI. Debugged and found that the View Instance not exists after activation hence the above error is thrown. Why its not recreated, no clue so I reached back to Oracle.

It was one of my interesting technical discussion with Oracle.

When the def is created dynamically its called temporal def and its deleted at the time of passivation. The code pattern we used to create view def is by calling registerDefObject() is meant for a view def which is meant for a singleton VO, i.e., view def with one VO instance. When ADFbc see such a temporal view def, it delete the view def from memory when the singleton VO instance is removed. Because the framework doesn't know how to create the temporal view def back.

Since the view def does not exists after the activation the dynamic form throws above exception.

Below are few suggestions from Oracle.

First suggestion:
1. Set up the mds-config section in adf-config.xml to declare a proper MDS repos
2. On the above code replace:

newEntity.resolveDefObject();
newEntity.registerDefObject();

with
newEntity.resolveDefObject();
newEntity.writeXMLContents();
newEntity.saveXMLContents();
newEntity = EntityDefImpl.findDefObject(<full-EO-name>);

3. And replace
newView.resolveDefObject();
newView.registerDefObject();

with
newView.resolveDefObject();
newView.writeXMLContents();
newView.saveXMLContents();
newView = ViewDefImpl.findDefObject(<full-VO-name>);

Here the dynamic view def is stored in MDS so that its not considered as temporal def and also its not deleted when its getting passivated.

But I can't enable MDS just like that without doing complete re-test of my whole application. So I thought of storing the vo def at the time of passivation & recreate the same on my own overidding prepareForActivation. Reached Oracle with this suggestion and they provided another good suggestion:

Second suggestion:
If the dynamic eo & vo is created with a package and put the def object in a package, then the view def is not considered temporal view def any more. So, the view def is not deleted when the VO instance is removed at the time of passivation and the vo instance will be recreated & available after activation. And the new code is:


private static final String DYNAMIC_PACKAGE = "dynamic";
private static final String DYNAMIC_EO = "EO";
private static final String DYNAMIC_VO = "VO";

public void createDynamicViewDef(){
//Create dynamic package

        PackageDefImpl newPackage;
        java.lang.reflect.Method setParentMth;
        try {
            java.lang.reflect.Method mth = MetaObjectManager.class.getDeclaredMethod("createContainerDefObject", new Class[] { boolean.class });
         
            setParentMth = oracle.jbo.server.DefObject.class.getDeclaredMethod("setParent", new Class[] { oracle.jbo.common.NamedObjectImpl.class });
         
            mth.setAccessible(true);
         
            setParentMth.setAccessible(true);
         
            newPackage = (PackageDefImpl) mth.invoke(MetaObjectManager.getSingleton(), new Object[] { true });
        }catch(Exception ex) {
            throw new JboException(ex);
        }
     
        newPackage.setDefScope(PackageDefImpl.DEF_SCOPE_SESSION);
        newPackage.setName(DYNAMIC_PACKAGE);
        newPackage.setFullName(DYNAMIC_PACKAGE);
        MetaObjectManager.insertSessionMetaObject(newPackage.getFullName(), newPackage);

//Create Entity
        EntityDefImpl newEntity = new EntityDefImpl(DYNAMIC_EO);
        newEntity.setFullName(DYNAMIC_PACKAGE + "." + DYNAMIC_EO);
        try {
            setParentMth.invoke(newEntity, new Object[] { newPackage });
            }catch(Exception ex) {
                throw new JboException(ex);
            }
     
        newEntity.setSource("TEST_TABLE");

//Code for AttributeDefImpl creations..

        newEntity.resolveDefObject();
        newEntity.registerDefObject();

//Create ViewDef
        ViewDefImpl newView = new ViewDefImpl(DYNAMIC_VO);
        newView.setFullName(DYNAMIC_PACKAGE + "." + DYNAMIC_VO);      
        try {
            setParentMth.invoke(newView, new Object[] { newPackage });
        }catch(Exception ex) {
            throw new JboException(ex);
        }
       
        newView.addEntityUsage("e", newEntity.getFullName(), false, false);      
        newView.addAllEntityAttributes("e");

        newView.resolveDefObject();
        newView.registerDefObject();
}

//Create ViewObject Instance
        ViewObject dynmVO = createViewObject(DYNAMIC_VO_INSTANCE_INTERNAL, DYNAMIC_PACKAGE + "." + DYNAMIC_VO);

Thats it.. I have modified my code as per their second suggestion and it works perfectly...

Please note that when the AM gets activated in different node on a cluster then the view def needs to re-created. Override prepareForActivation and use the below code to recreate view def:

    protected void prepareForActivation(Element element) {
        super.prepareForActivation(element);
        ViewDefImpl existing = null;
        try{
            existing = ViewDefImpl.findDefObject(DYNAMIC_PACKAGE + "." + DYNAMIC_VO);
        }
        catch(NoDefException ex){
            existing = null;
        }
     
        if (existing == null){
            this.createDynamicViewDef();
        }      
    }

None of the above information is available in dev guide and Oracle have logged an internal documentation bug to update the same.

All the above discussions are tracked on
Bug 16320619 : DYNAMIC FORM WITH DYNAMIC VO & EO THROWS EXCEPTION ON PASSIVATION/ACTIVATION

Happy ADF Coding...!!






3 comments:

  1. Good information thiru....

    Just one doubt, if we use writeXMLContents() method the definition will not be available across all user sessions..?

    ReplyDelete
    Replies
    1. Thanks Praveen.

      Yes. When you use writeXMLContents() it will be available across user session. I haven't tested this and also I don't see any info on javadoc. But as per Jobinesh post http://jobinesh.blogspot.com/2011/08/long-living-dynamic-business-components.html it will be available across user sessions.

      Delete
  2. hi thiru thanks for the post ,can you provide sample application for the same use case

    ReplyDelete