JDeveloper & ADF: Use the Application Module Tester (BC4J Tester) to Test all your BusinessLogic

This blog post describes how to use the Application Module Tester provided together with JDeveloper to test all your business logic without having to write or design the client UI (view controller part of a Fusion Web Application).
I consider this a ‘best practice’ to do this in each project. It allows the developer(s) of the business logic to run, debug and test all business logic before the designers have worked out the UI. Changes to interfaces can be done without an impact to the UI this way.

One other advantage of using the Application Module Tester is that it starts up really fast compared to the long init phase the full web application needs.

Before going into detail lets talk about what this blog shows and how it’s implemented. A sample work space build in JDeveloper 11.1.2.1.0 using the HR schema as DB can be downloaded using the link provided at the end of the blog.
First of all you can test the data model you designed into the application module, meaning that you can execute the VO and walk over the result set or show the result as table

Data Model of the Application Module

Data Model of the Application Module

The application module can implement service methods which your later use in the UI to archive a business need. In the sample the application module defines just one service method ‘howManyEmpEarnMoreThen’ which returns the number of employees who are earning more (or equal) to the given amount.

Service Method 'howManyEmpEarnMoreThen'

Service Method 'howManyEmpEarnMoreThen'

and the implementation of this method

    public oracle.jbo.domain.Number howManyEmpEarnMoreThen(oracle.jbo.domain.Number aSalary)
    {
        _logger.info("Call with salary: "+aSalary);
        EmployeesViewImpl lEmployeesView1 = this.getEmployeesView1();
        oracle.jbo.domain.Number n = lEmployeesView1.countEmpWihtSalaly(aSalary);
        return n;
    }

Next a VO might have some ViewCriteria attatched which you also want to test

ViewCriteria to Test

ViewCriteria to Test

and the last thing to test are service methods which are exposed in the VO instead of the application module (‘countEmpWihtSalaly’ in the sample)

Exposed Methods in a View Object

Exposed Methods in a View Object

and the implementation of the method

    public Number countEmpWihtSalaly(Number aSalary)
    {
        RowSet lCreateRowSet = this.createRowSet("counterView");
        ViewCriteriaManager lCriteriaManager = this.getViewCriteriaManager();
        String[] lAvailableViewCriteriaNames = lCriteriaManager.getAvailableViewCriteriaNames();
        for (String vcName :lAvailableViewCriteriaNames)
        {
            this.removeApplyViewCriteriaName(vcName) ;
        }
        lCreateRowSet.ensureVariableManager().setVariableValue("bindSal", aSalary);
        ViewCriteria lCriteria = this.getViewCriteria("EmpSalaryVC");
        this.applyViewCriteria(lCriteria);
        lCreateRowSet.executeQuery();
        long count = lCreateRowSet.getEstimatedRowCount();
        _logger.info("Count Emp wiht salay > " + aSalary + " = " + count);      
        lCreateRowSet.closeRowSet();    
        return new Number(count);
    }

Now let start up the the Application Module Tester and debug the business logic. To start the tester in debug mode, right click the application module facade and select ‘Debug’

Start the Application Module Tester in 'Debug' Mode

Start the Application Module Tester in 'Debug' Mode

which shows the tester like

Running Tester

Running Tester

A double click on a ViewObject (e.g. EmployeesView1) opens one record in form mode. You can navigate the result set using the icons in the header

Navigate Result Set

Navigate Result Set

One of the icons (the field glass) allows you to specify one or more of the available view criteria and hitting the ‘Find’ button after selecting a view criteria opens a dialog to enter bind variables

Specify View Criteria

Specify View Criteria

Specify Bind Variables

Specify Bind Variables

The result of this is again shown as a result set with navigation. If you like to see the result a table you can right click on the VO and select ‘Show Table’.

Show Result as Table

Show Result as Table

So how do we execute the ‘countEmpWihtSalaly’ method which is exposed in the client interface of the VO?
If you look at the image above you’ll see the option ‘Operations’ which will open a new tab showing all available operations which are exposed to the client interface of the VO.

Available Operations

Available Operations

After selecting the method you can hit execute and the method is executed

Result of Operation

Result of Operation

To access the public available methods from the application module you right click on the application module and select ‘Show’. After that you see a tab like the one for the VO operation.

Application Module Operations

Application Module Operations

Result of Operation

Result of Operation

This might not look as much to you, but the advantage of the Application Module Tester is that you can debug the code in the application module methods or view object operation easily. You can set needed variables when calling the methods and use the outcome of the operations in calls to other operations. This way you can easily test the whole business logic without the need to have an UI present.

For more information you can check the Oracle® Fusion Middleware Fusion Developer’s Guide for Oracle Application Development Framework 11g Release 2 (11.1.2.1.0)

You can download the sample workspace, build with JDev 11.1.2.1.0 and depending on the HR db schema, from here: BlogBC4JTester.zip.doc
Please rename the file to ‘.zip’ after downloading it!

Advertisements

JDev: Always Test Your App with ApplicationModule Pooling turned off

In the last couple of weeks I saw a couple of question which mentioned a sporadic misbehavior of the application under different circumstances. In the end they could be answered:

“Your application has not been tested with application module pooling turned off”

Whenever you encounter a sporadic misbehavior of the application under different circumstances, this should ring a bell. Most developers came across a situation like this when programming ADF applications. At some point the application does not react normal or throws exceptions. The errors are not reproducible (most of the times) and you only see them on the production server (never on the developer machine).

The first time I came across this problem it took me about a week to figure it out and solve it. Basically the problem has been private data (stored with the session) which is part of the application module and is stored in the user data hash map. This is not a problem as long as you can guarantee that each user always works with the same application module. This is the case when you test run your application on the developers machine (you are the only user and the application module pool always returns the same application module to the client). On a production server where more then one user uses the application at the same time, the application may be forced to reuse an application module which was formally used by a different user. At this point application module pooling take over.

The general algorithm used is that the application module pool has a number of application module available to use. If more requests arrive the pool generates the additional module until a high water mark is reached. Further requests getting rejected. Currently not used modules are given back to the pool and are available for the next request. The pool tries to return the same application module as long as it’s available to the same session for subsequent requests. If it is not available it uses a currently available module stores the current status of the module into a store (DB or file), clears the module from all information and reconstructs the state from the other session from saved state information. Once the application module is restored you can’t distinguish if it’s a new application module or a reused one. This way your application don’t need an module for each user request, but it shares the available modules between the requests, saving lots of resources.

All this can be read about here 43.2 Introduction to Fusion Web Application State Management.

After this more theoretical prologue, lets do a practical project (workspace for JDev 11.1.2 available, see end of article). To make it as simple as possible, but still useful for anybody running into problems with activation/passivation, we use the HR schema and try to emulate a scenario where a user only sees employee data which depends on a department number. This department number should be set in the application module and be accessible for all queries. In a real world scenario this information is connected to the login of a user and stored in a central place. In the sample we use the user data of the application module to store the number.
The applications start page (I do spare the login part) has a af:query panel to select employees which might be filtered by their last name. As there is no login I added a field to enter the department number which should be used to further filter the result set.The Web UI lokke like

Test app UITest Application UI

As you see there is an input field for the last name, in the bottom era an input field to insert the department number and an other panel to retrieve the currently set department number from the application module.
Lets have a look at the service method to get/set the department number and how it is stored:

    private static final String PRIVATEDATA = "privData";

    public void setPrivateToUserData(String aVal) {
        mLogger.info("Set PrivData:" + aVal);
        if (aVal == null)
            return;

        Session lSession = getSession();
        if (lSession == null) {
            mLogger.warning("getSession returned null!");
            return;
        }
        Hashtable lHashtable = lSession.getUserData();
        if (lHashtable == null) {
            mLogger.warning("getUserData returned null!");
            return;
        }
        lHashtable.put(PRIVATEDATA, aVal);
    }

    public String getPrivateToUserData() {
        Session lSession = getSession();
        if (lSession == null) {
            mLogger.warning("getSession returned null!");
            return null;
        }
        Hashtable lHashtable = lSession.getUserData();
        if (lHashtable == null) {
            mLogger.warning("getUserData returned null!");
            return null;
        }
        String lData = (String) lHashtable.get(PRIVATEDATA);
        mLogger.info("Get PrivData: " + lData);
        return lData;
    }

As you see the string from the UI is stored and retrieved under the key PRIVATEDATA = “privData” in the userData hash map of the application module. This is the place to store data for the current user session (Storing Information About the Current User Session)
The configuration of the application module is the default you see after generating a ‘Fusion Web Application’.

ApplicationModule Default Configuration

ApplicationModule Default Configuration


As you can see ‘Application Module Pooling’ is on. When we run the application, set the department number to e.g. 30, send the data to the AM and hit the search button in the query panel we see
Pooling On

Pooling On


You can hit the edit button in a row to edit the employee and come back to the page and see the application running as expected.
Now, we switch ‘Application Module Pooling’ off and run again:
Application Module Pooling Off

Application Module Pooling Off


Doing the same actions as earlier it look like the application does not see the department number at all:
Application Module Pooling Off

Application Module Pooling Off


Even if you set the department number and directly hit the ‘Get User Data’ button you’ll don’t get the department number back.

The reason for this behavior is that we store the department number in the user data hash map which is NOT passivated when the application module is given back to the pool and given to an other requester. This happens every time you go the the server when am pooling is switched off.
What we need to to is to passivate the session user data together with the other state data stored by the framework and load it back when the AM is requested the next time (when it gets activated again). To do this we have to overwrite two methodes in the ApplicationModuleImpl class.

    @Override
    protected void activateState(Element aElement) {
        super.activateState(aElement);
        mLogger.info("++++++++++ activateState");

        Hashtable lData = getSession().getUserData();
        if (aElement != null) {
            // 1. Search the element for any <PrivData> elements
            NodeList nl = aElement.getElementsByTagName(PRIVATEDATA);
            if (nl != null) {
                // 2. If any found, loop over the nodes found
                for (int i = 0, length = nl.getLength(); i < length; i++) {
                    // 3. Get first child node of the <PrivData> element
                    Node child = nl.item(i).getFirstChild();
                    if (child != null) {
                        // 4. Set the data value to the user data hashmap
                        String lDataString = child.getNodeValue();
                        String[] lSplitkeyval = lDataString.split(";");
                        for (int ii = 0; ii < lSplitkeyval.length; ii++) {
                            mLogger.fine("..."+lSplitkeyval[ii]);
                            String[] lSplit = lSplitkeyval[ii].split("=");
                            lData.put(lSplit[0], lSplit[1]);
                        }
                        break;
                    }
                }
            }
        }
    }


    @Override
    protected void passivateState(Document aDocument, Element aElement) {
        super.passivateState(aDocument, aElement);
        mLogger.info("---------- passivateState");

        // 1. Retrieve the value of the user data to save and build a string representation
        Session lSession = getSession();
        Hashtable lData = lSession.getUserData();
        String lDataString = "";
        Set<String> keyset = lData.keySet();
        if (!keyset.isEmpty()) {
            Iterator<String> keys = keyset.iterator();
            while (keys.hasNext()) {
                String key = keys.next();
                mLogger.fine("..."+key + "=" + lData.get(key));
                lDataString += key + "=" + lData.get(key) + ";";
            }
        }

        // 2. Create an XML element to contain the value
        Node node = aDocument.createElement(PRIVATEDATA);
        // 3. Create an XML text node to represent the value
        Node cNode = aDocument.createTextNode(lDataString);
        // 4. Append the text node as a child of the element
        node.appendChild(cNode);
        // 5. Append the element to the parent element passed in
        aElement.appendChild(node);
    }

The passivateState method gets a Document and an Element as parameter. After calling super() to let the framework do its work, we get the user data hash map and store each key-value pair in a string which is then appended as a node to the element we got as parameter. In a real world application I would use a java to XML serialization tool like XStream which is capable to store more complex data.
The activateState method gets an Element as parameter which we search for the node we save when passivateState was called and restore the user data hash map.

After putting the two methods in the ApplicationModuleImpl class (the one you get when you create the java classes for an application module) the application run OK again, application module pooling still turned off.

When you examine the sample workspace which you can get here BlogActivatePassivateSample_V1.zip (remove the ‘.doc’ extension after downloading the workspace!) you’ll notice, that EmployeesViewImpl class too have the two methods to store private data which is not automatically saved by the framework. Further there are log messages throughout the code to let you follow the action in the log window.

As you see it’s essential to test an application with application module pooling turned off to find activation/passivation errors before the application goes to production.