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...!!






PanelCollection features

Blogging after long time... Was quite busy at work...

very useful post for panel collection..

https://blogs.oracle.com/aramamoo/entry/what_does_featureoff_attribute_do

Saturday, December 15, 2012

Never forget resetActionListener when there is rollbackTransaction action


Whenever you put a button (any action component) to rollbackTransaction make sure you have resetActionListener also.. Usually when you drag and drop rollback operation as action component this is created by default. But in the scenarios where you add action component separately you have to put it manually..

Many times I missed to put resetActionListener inside my button and the application data looks very odd when I click Cancel button... And I spent hours to debug the issue and found that resetActionListener  is missing..

<af:commandToolbarButton text="#{viewcontrollerBundle.DISCARD_CHANGES}" id="ctb2"
                                                   icon="/images/Discard.png" accessKey="Z"
                                                   actionListener="#{ManagedBean.rollbackChanges}" immediate="true">
         <af:resetActionListener/>
</af:commandToolbarButton>

So never forget(saying to myself) resetActionListener and rollbackTransaction!!

Jdeveloper 11.1.2.0 & Debug Mode

When you run the application in Debug mode in Jdeveloper 11.1.2.0, compile any java file and refresh the page you get java.lang.NullPointerException.

This issue is discussed in detail at
https://forums.oracle.com/forums/thread.jspa?threadID=2244097&tstart=0

and the summary is:
------------------------------------------------------------------------------------------
Option 1:  Change Tools->Preferences->Run->WebLogic and setting Select Hot Class Reload Policy to Fast Swap.

Option 2 : Turn off copying of any rebuilt files into the exploded EAR.
Usually the jdev shortcut target will be:
C:\Oracle\Middleware\jdeveloper\jdeveloper.exe

Modify this to C:\Oracle\Middleware\jdeveloper\jdeveloper.exe -J-Doracle.jdeveloper.adrs.incr.buildMonitor=false
and then restart jdeveloper.

Please use option 2 as a last resort. Since the debugger already provides some support for class redefinition, perhaps this will be useful for many who use debug mode.
------------------------------------------------------------------------------------------

I set Option 1 and it was working fine. But my weblogic server started crashing intermittently. I'm still working on exact reproducible steps but meanwhile I tried option 2 and it works properly.

East to West

Moved to California from New York.. Just got settle down..

Next posts are on the way...

Thiru

Monday, October 22, 2012

Wednesday, October 3, 2012

Jdeveloper Remote Debugging

Sometimes we see application issues on standalone weblogic server which is not reproducible while running the same application in integerated weblogic server inside Jdeveloper..

The best option is remote debugging.. Its very simple to setup..

1. Open the file $DOMAIN_HOME/bin/setDomainEnv.cmd
    a. set debugFlag = true
    b. Disable assertions: search for -ea and replace it with -da (keep a backup to restore it)
    c. Note down the debug_port (default is 8453).
    d. Start weblogic server. Make sure that you see Listening for transport dt_socket at address: 8453 in the
        Server log file.


2. On Jdeveloper:

Right click your project -> Project Properties


Run/Debug/Profile -> Edit


 Select Remote debugging



On Same Window, Select Tool Settings -> Debugger -> Remote and have the following settings.

Protocol -> Attach to JPDA
Host: Host of your WLS Server
Port: WLS Debugger port, default is 8453.


Right click your project, Start Remote Debugger.






That's it.. Now you have connected your remote application in debug mode.. Put breakpoints on your code and see what's going on..

Happy Debugging :-)

Note : Please make sure that the code you have deployed in weblogic server and the code you have connected from jdeveloper are same.