Friday, October 9, 2015

ADF Session Replication on a Cluster for High Availability

Running an ADF application on a weblogic cluster is fairly simple. Just deploy the application to the cluster and your done. This gives you scalability as the load is distributed over multiple managed servers. However, it does not give you protection against machine failure out of the box. For that to work, you need to make sure all state in your ADF application is replicated across multiple nodes in the cluster. This post will explain the things you have to think about when setting up this session replication.

Most of this information is based on Configuring High Availability for Fusion Web Applications chapter of the Fusion Web Applications Developer's Guide but we've added some undocumented features and best practices based on our experience.

View

  • First thing you need to do is tell weblogic to replicate a http session to other nodes in the cluster. This is done by setting persistent-store-type to replicated_if_clustered in your weblogic.xml file:
    <weblogic-web-app>
      <session-descriptor>
        <persistent-store-type>
          replicated_if_clustered
        </persistent-store-type>
      </session-descriptor>
    </weblogic-web-app>
    
  • During development on my local workstation I like to set the session-description slightly differently:
    <weblogic-web-app>
      <session-descriptor>
        <persistent-store-type>file</persistent-store-type>
        <persistent-store-dir>c:/temp/</persistent-store-type>
        <cache-size>0</cache-size>
      </session-descriptor>
    </weblogic-web-app>
    
    This tells weblogic to write the http session request to file after each request. Since the cache-size is set to 0 it will not keep any session in memory. This means each subsequent request from the same session has to restore the session from file first. Since this (de)serializes the http session on each request it behaves the same as a session failover on a cluster after each request. This is a great way to test if you application is really cluster-safe and if none of the managed beans or other objects will loose their state.
  • In your web.xml you have to set CHECK_FILE_MODIFICATION to false:
    <web-app>
      ...
      <context-param>
        <param-name>
          org.apache.myfaces.trinidad.CHECK_FILE_MODIFICATION
        </param-name>
        <param-value>false</param-value>
      </context-param>
    <web-app>
    
    Having this on could lead to errors when a failover occurs. It is best practice to disable this for a production system anyhow. If you want this on during local development you should look into deployment plans to override such a setting per environment.

Tuesday, February 17, 2015

ADF Faces 12c Components Demo and Test Automation

You might have noticed that I am working on a series of blog articles on using Selenium to automate testing of Oracle ADF applications. This includes work on a little framework to make this easier and a set of sample JUnit tests against the public Oracle ADF Faces 12c Components Demo.

Getting the Faces 12c Component Demo running with test automation had some challenges. I wanted to write them down here in case somebody wants to try the same. It starts by downloading the Oracle ADF Faces Components Demo from OTN. This also includes instructions on how to run this application in your integrated weblogic server, but those instructions have some caveats and are for JDeveloper 11g, not version 12c. Please follow the instructions below as an alternative (I've marked the differences with the normal Oracle instructions and included screenshots at the end).

In the end this needed more work than I expected, so I also offer the fixed versions for download. If anyone from Oracle feels that this is a problem please contact me and I'll remove the download and you can follow the instructions below to create your own fixed version.

If you want to start from the downloads from Oracle and not use my fixed versions you would have to follow these steps:
  1. Download the ADF Faces 12c Components Demo WAR file, but don't unpack it.
  2. Start JDeveloper 12.1.3
  3. Instructions updated from 11g: Choose File > New > From Gallery from the menu to create a new application. Select General > Applications in the tree and select Custom Application as application type and press Ok.
  4. In the Create Application dialog type adffacesdemo as the application name, select a directory, leave the rest of the options alone and press Finish. This creates new application workspace and project.
  5. Instructions updated from 11g: The default created project is not needed and you may delete the project. Right click it and select Delete Project. In the subsequent dialog choose to not only delete the project form the application but also delete it from disk.
  6. Instructions updated from 11g: In the now empty workspace choose File > New > From Gallery from the JDeveloper menu. In the list of items select Projects and on the right hand side Project from WAR and press Ok. In the next dialog provide a name for the project, e.g. adffacesdemo, and keep the directory information. On the second panel, use the file browser to select the downloaded ADF Faces demo WAR file and finish the wizard.
  7. Double click onto the project node to open the project properties and select the Run > Debug > Profile option. Press the Edit button and select the Tool Settings. In the Before Running section, uncheck the Make Project and Dependencies option and close the dialog pressing Ok.
  8. Extra step added by me: Unfortunately some files are missing from the exploded WAR which will result in javascript errors and missing images.
    • Manually copy the META-INF directory from the src directory to the classes directory so you end up with classes/META-INF
    • Copy the src/oracle/adfdemo/view/js directory (and all of its subdirectories) to classes/oracle/adfdemo/view/js
    • Copy the two .properties files from src/oracle/adfdemo/view/resource to classes/oracle/adfdemo/view/resource
    • Copy the src/oracle/adfdemo/view/resource/fileExplorer directory and the .properties file it contains to classes/oracle/adfdemo/view/resource/fileExplorer
    • Copy all xml file from src/oracle/adfdemo/view/components/rich/tageditor to classes/oracle/adfdemo/view/components/rich/tageditor
    • Copy the xml and csv file from src/oracle/adfdemo/view/feature/rich/diagram/data to classes/oracle/adfdemo/view/feature/rich/diagram/data
    • Copy the src/oracle/adfdemo/view/feature/rich/dvt/data/election directory to classes/oracle/adfdemo/view/feature/rich/dvt/data/election
    • Copy all xml files from src/oracle/adfdemo/view/feature/rich/hv to classes/oracle/adfdemo/view/feature/rich/hv
  9. Extra step added by me: To be able to use test automation on this ADF sample application, enable automation by opening public_html/WEB-INF/web.xml in JDeveloper and remove the comment markers around the existing oracle.adf.view.rich.automation.ENABLED context parameter.
    For test automation to work you also have to copy JDEV_HOME/oracle_common/modules/oracle.adf.view_12.1.3/adf-richclient-automation-11.jar to the  public_html/WEB-INF/lib directory of the project.
  10. Extra step added by me: As a final step I prefer a simple URL when running the application. Double click the project to open the project properties. In the Java EE Application section change both the application name and context root to adf-richclient-demo
  11. Finally, expand the project and select index.jspx under the Web Content node. Choose Run from the right mouse context menu.

Sunday, February 15, 2015

ADFLogger 12.1.3 ignoring message parameters and resource keys

In JDeveloper 12.1.3 Oracle made a switch to a new log formatter that has its shortcomings. It no longer knows how to replace resource bundle keys with their actual message and also fails to replace the {0}, {1}, ... placeholders with their actual values. You can end up with logging looking like this:
<oracle.adf.view> <RichRenderKit> <isAutomationEnabled> <AUTOMATION_ENABLED_WITHOUT_AUTOMATION_JAR> 
<oracle.adf.common> <ADFConfigFactory> <findOrCreateADFConfig> <Resource {0} not found on the classpath.> 

Notice the AUTOMATION_ENABLED_WITHOUT_AUTOMATION_JAR that is not replaced with its actual message as well as the Resource {0}... message which should clearly mention the missing resource.

I don't know how this made it past basic QA, but this is now the default configuration for your Integrated WebLogic server in JDeveloper 12.1.3. Luckily there is an easy fix to revert to the 12.1.2 behaviour which is working fine.

Either start your integrated weblogic server in JDeveloper and click the actions button the log window and select Configure Oracle Diagnostics Logging or directly edit the JDEV_USER_HOME/system12.1.3.0.41.140521.1008/DefaultDomain/config/fmwconfig/servers/DefaultServer/logging.xml file.

Find the log_handler declaration at the beginning of the file for the console-handler. Change its formatter attribute from oracle.adf.share.logging.internal.diagnostic.ConsoleFormatter to oracle.core.ojdl.weblogic.ConsoleFormatter.

Restart your integrated weblogic server for the changes to take effect. You can now notice the resource keys and message parameters are correctly replaced:
<Feb 15, 2015 3:59:01 PM CET> <Warning> <oracle.adfinternal.view.faces.renderkit.rich.RichRenderKit> <ADF_FACES-60118> <Your application is running with the automation enabled in your web.xml file but the automation jar is unavailable. Please ensure that the jar is in the classpath.> 
<Feb 15, 2015 3:59:01 PM CET> <Warning> <oracle.adf.share.config.ADFConfigFactory> <BEA-000000> <Resource META-INF/adf-config.xml not found on the classpath.> 

Sunday, February 8, 2015

Waiting for Oracle ADF Partial Page Rendering in Selenium tests

One of the main reasons people fail to use Selenium (or any other tool) for automated web testing with Oracle ADF is timing issues. The test automation tool typically wants to execute its actions as quickly as possible. This can be a challenge in dynamic applications that load parts of the pages on demand or in response to user interactions. This is what ADF typically does with partial page rendering requests.

For example, the test could "click" on a tab in a af:pannelTabbed component. The content of the new tab will be retrieved with a partial page rendering request. If the test automation tool would continue immediately after "clicking" the tab and try to interact with elements on the new tab it would be too early and fail. Most automated testing tools have features to wait for new page DOM elements to appear before interacting with them. But even this can be too soon. When the response to the partial page rendering request is processed by the web browser client it adds the new elements to the page and then binds all sorts of javascript events and other goodies to them. Most automated testing tools will not wait for this to complete and will still fail to interact with the new elements.

One quick and dirty solution is to introduce sleep statements in your test and just wait a second or two for the request to complete. This will unnecessarily slow down your tests if the partial request is completed in less than your sleep time and will still break your test whenever the page load will take longer then normal and thus longer than your sleep time.

A much better way is to actually tell Selenium (or your other testing tool of choice) when the partial page rendering request is fully completed and the browser is ready for its next command. ADF includes a javascript method that does just that; AdfPage.isSynchronizedWithServer.

This has been available in older ADF versions as well. In version 12c Oracle even introduced AdfDhtmlPage.whyIsNotSynchronizedWithServer which tells you why the client is not ready yet. Please not this is part of the Oracle internal AdfDhtmlPage class and therefor not part of the public javascript API which means it might change or disappear in future versions.

Selenium WebDriver has the notion of explicit waits where you can instruct Selenium to wait for a certain condition. I figured it would be nice if we could just hook into that mechanism. As part of my effort to automate ADF testing I've created a subclass of org.openqa.selenium.support.ui.ExpectedCondition called ClientSynchedWithServer:
public class ClientSynchedWithServer implements ExpectedCondition<Boolean> {
    // return false if AdfPage object and functions do not exist
    // if they do exist return true if page is fully loaded and ready or reason why this is not completed yet
    String js =
        "return typeof AdfPage !== 'undefined' && " + 
        "typeof AdfPage.PAGE !== 'undefined' && " +
        "typeof AdfPage.PAGE.isSynchronizedWithServer === 'function' && " +
        "(AdfPage.PAGE.isSynchronizedWithServer() || " +
        "(typeof AdfPage.PAGE.whyIsNotSynchronizedWithServer === 'function' && " +
        "AdfPage.PAGE.whyIsNotSynchronizedWithServer()))";

    @Override
    public Boolean apply(WebDriver driver) {
        JavascriptExecutor jsDriver = (JavascriptExecutor) driver;
        Object result = jsDriver.executeScript(js);
        System.out.println("client ready: " + result);
        return Boolean.TRUE.equals(result);
    }
}

The javascript tries to be as careful as possible not to throw an exception when not on an ADF page by first checking if AdfPage, AdfPage.PAGE and AdfPage.PAGE.isSynchronizedWithServer are available. If so, it will execute AdfPage.PAGE.isSynchronizedWithServer() to see if the client is completely finished processing all events. If this is not the case it will check if the whyIsNotSynchronizedWithServer function is available and will invoke it if it is. This would return the reason why the client is not finished yet. If the whyIsNotSynchronizedWithServer function is not available it would just return the false result from isSynchronizedWithServer.
In the end the javascript function will only return true if isSynchronizedWithServer returned true, otherwise it might return the reason why the page is not finished or any other non-true value.

The apply method from this ExpectedCondition will invoke the javascript and would print the reason why the page is not finished yet. If you don't want any System.out.println in your tests you could just remove this. The important part is that it checks if the javascript returned true. If it did, the apply method will also return true and Selenium would know the condition has been met and it no longer needs to wait.

Using this class to wait for PPR after interacting with the page is similar to any explicit wait in Selenium WebDriver:
public void test() throws Exception {
    FirefoxProfile profile = new FirefoxProfile();
    profile.setEnableNativeEvents(true);
    profile.setPreference("app.update.enabled", false);
    WebDriver driver = new FirefoxDriver(profile);
    System.out.println("load demo page...");
    driver.get("http://jdevadf.oracle.com/adf-richclient-demo");
    System.out.println("wait for completion...");
    new WebDriverWait(driver, 10).until(new ClientSynchedWithServer());
    System.out.println("click search button...");
    driver.findElement(By.id("tmplt:gTools:glryFind:doFind")).click();
    System.out.println("wait for completion...");
    new WebDriverWait(driver, 10).until(new ClientSynchedWithServer());
    driver.quit();
}

This will setup a new Selenium session in lines 2 through 5. It will then navigate to the Oracle Rich Client Demo site. This already includes quite some client side javascript processing, so we need to wait for this to complete in line 9. We then click on the search button and again wait for that to complete in line 13. Both waits use a timeout of 10 seconds. If the condition is not met within that timeout Selenium will throw an exception.
Without the new waits this script would fail as it would try to click the search button before the page has completely rendered and attached all of its javascript listeners. With the new waits you can see the results in the console and why the page wasn't ready yet in lines 3-5 and 9-10:
.load demo page...
wait for completion...
client ready: WAITING_FOR_USER_INPUT_PHASE
client ready: WAITING_FOR_USER_INPUT_PHASE
client ready: Event queue is not empty
client ready: true
click search button...
wait for completion...
client ready: DTS is not ready
client ready: WAITING_FOR_USER_INPUT_PHASE
client ready: true

Time: 9.756

OK (1 test)

This post is part of a series on how to get Selenium to work with Oracle ADF.

Configuring an Oracle ADF Project for Selenium testing

Running Selenium testing against an ADF application requires direct interaction with the HTML DOM, JavaScript and CSS. An ADF application is normally optimised for performance and scalability which means CSS classes and javascript are minified and obfuscated. This makes testing with Selenium very difficult and brittle.

Fortunately you can configure quite a few settings in your project's web.xml file to make it more development friendly. I've written about these before but some are even more important for automated testing. The ADF Faces documentation also states a number of configuration changes have to be made for automated testing.

These are the settings to change when using automated testing:
  • oracle.adf.view.rich.automation.ENABLED should be set to true in web.xml. This ensures a javascript client component is created for each ADF component regardless the value of the clientComponent attribute on the JSF component. This makes interacting with the page from Selenium much easier. It also seems to enable some other client and server side features. Search for isAutomationEnabled in the ADF source code to get a feeling for things that will change when enabling this. One of the things it enables is to find objects using scope IDs (also known as Sub IDs). This is explained in the Oracle Application Testing Suite Open Script User Guide.
    For this to work you also need to put adf-richclient-automation-11.jar in your project classpath. The simplest way is to just put this JAR in the WEB-INF/lib folder of your project. The file itself can be found in JDEV_HOME/oracle_common/modules/oracle.adf.view_12.1.3/
  • org.apache.myfaces.trinidad.DISABLE_CONTENT_COMPRESSION to true in web.xml which will disable the compression of CSS classes like af_button to something like x7k. Having readable and deterministic CSS class names makes it possible to use CSS selectors in your Selenium scripts.
  • org.apache.myfaces.trinidad.DEBUG_JAVASCRIPT to true in web.xml o disable the minification of ADF's javascript files. This gives you human readable javascript which makes it easier to figure out how to interact with those scripts from Selenium. You should also get a copy of the ADF Source code from Oracle Support so you even have the versions with all the inline comments in place. But don't let this scare you. For simple testing scenarios you won't be needing javascript interactions. It's just when you want to go all the way and have very detailed interactions or tests with ADF components.
  • javax.faces.PROJECT_STAGE to Development in web.xml as your application will otherwise fail to start since you have enabled a number of development-only features. You can revert this to Production with deployment plans for other environments.
  • The aforementioned Oracle Application Testing Suite documentation also advises to set animation-enabled to false in trinidad-config.xml. This is not only to speed up the running of the testing script as it won't have to wait for the animations but will also make sure tests don't fail as they want to interact with things like tree nodes before the expanding animation of a tree node is finished. In my own testscripts I also make sure to execute the javascript AdfPage.PAGE.setAnimationEnabled(false) on each page for situations where we forgot to set this parameter. Unfortunately values in trinidad-config.xml cannot be overridden with deployment plans, but there is a neat trick where you can refer to web.xml context param values from trinidad-config.xml.

You can revert these settings to the optimised values with deployment plans. No need to change this in the source code or build artefact each time. Just set the development optimised versions in your source files so local runs in JDeveloper use the correct values. Then use deployment plans to override these for production and other environments.

This post is part of a series on how to use Selenium automated tests with Oracle ADF.

Testing Oracle ADF with Selenium WebDriver Page Objects

Selenium is an awesome (and free) tool to automate browser-based user interface testing. Selenium offers two ways of working; Selenium IDE; a firefox add-on for simple record-and-playback of interactions with your browser and Selenium WebDriver; a collection of language specific bindings to drive a browser -- the way it is meant to be driven.

For testing ADF applications you'll need to use Selenium WebDriver as that sends native events to the browser it is controlling. This means it will fire actual mouse, keyboard, and other events, which is something the javascript-heavy ADF framework needs.

I've built an example on how to test the public Oracle ADF Faces 12c Components Demo for a real life demo using JUnit and Selenium WebDriver. The example project is at github and I'll explain the important parts in a number of blog posts. This first post will show how the JUnit test classes interact with the page objects. The nitty gritty details on how to work with ADF and Selenium will be covered in separate posts.

My advice is to follow the Page Objects Pattern proposed by the Selenium team. It creates a clear separation between Page Objects and Test Objects. Page Objects know about the HTML, the page components and how to interact with them while the Test Objects drive these page objects and contain the real test logic and assertions. An alternative approach would be the Bot Style tests which combines page logic and Selenium interactions in a single class.

In an ideal world making changes to the ADF page itself only involves updating the Page Objects while the Test Objects can remain the same. Unless you're actually changing functionality on the page that requires changes to the tests itself. But even then it helps you to separate these two objects. The Page Object is more "developer oriented", while the Test Object is more "tester oriented".

Let's look at an example that drives the public Oracle ADF Faces Components Demo:

@Test
public void testNavigationToFileExplorer() throws Exception {
    FileExplorer page = getPage().clickFileExplorerLink().clickTreeTableTab();
    page.getScreenshotAs(new ScreenshotFile(new File("explorer-tree-table.png")));
}

This is a JUnit test method in the RichClientDemoTest test class. It starts in line 3 by navigation to the component demo homepage by invoking getPage(). This method returns a RichClientDemo page object. This is one of the aforementioned page objects that allow interaction with the web page.
ADF 12c Rich Client demo page with File Explorer link highlighted
Invoking the clickFileExplorerLink() method on the RichClientDemo page object will find the link on the page to navigate to the File Explorer demo and click that link to navigate to that page. Navigation methods on page objects will return other page objects. In this example we invoked a navigation method on RichClientDemo which returns a FileExplorer page object.
File Explorer page with Tree Table tab highlighted
Finally we invoke the clickTreeTableTab() method to click on the Tree Table tab in the right hand side af:pannelTabbed. Methods on Page Objects return the same page object instance or another when navigation occurs. In either case this means you can chain numerous of these methods on a single line when needed.

The last line of the test method shows how you can take a screenshot of what the browser is showing at the time. This can be very helpful especially with failing test cases where it could be useful to have a screenshot of the failed state.
Screenshot taken by Firefox ran by Selenium WebDriver

This example showed how to interact with page objects and it will fail if any of the required links or other elements are not available on the page. But it doesn't have any real test assertions yet. One final example shows how to assert that the number of expanded nodes in an af:tree should have increased when clicking one of the (collapsed) nodes in the tree:

@Test
public void testExpandTagGuideNodeA() {
    RichClientDemo page = getPage();
    int expandedNodesBefore = page.getTagGuideTreeExpandedNodeCount();
    page.clickLayoutTreeNode();
    Assert.assertEquals("number of expanded node should increase", 
                        expandedNodesBefore + 1,
                        page.getTagGuideTreeExpandedNodeCount());
}

Line 3 shows how we again first navigate to the rich client demo homepage as that is the starting point for all of our tests. Line 4 uses the RichClientDemo page object to retrieve the number of expanded nodes in the af:tree component. Notice how the knowledge on how to interact with the page to get this information is contained in the page objects and not exposed to the tester. Line 5 clicks the node in the tree labeled Layout. Again notice the knowledge on how to locate and click this node is hidden from the tester class. Finally in line 6 we have a JUnit assertion that the number of expanded nodes in the tree should have been increased by 1. If that is not the case this JUnit test will fail.

By the way; a Page Object doesn't have to represent a full page. Especially with ADF regions it is conceivable that each region would have its own Page Object (or rather Page Fragment Object). For complex page fragments this could be split up even further to keep the code manageable. More on that in other blog posts. I'll explain the other bits and pieces of this demo in separate blog posts. If you want to look at the full demo just hop over to github.

This post is part of a series on how to get Selenium to work with Oracle ADF.

Friday, January 23, 2015

showPopupBehavior align property examples

I always struggle to understand the official descriptions for the align attribute of the af:showPopupBehavior tag. A picture is worth a thousands words so I just created screenshots of all the possible values:

afterStart: The popup appears underneath the element with the popup's upper-left corner aligned with the lower-left corner of the element. The left edges of the element and the popup are aligned


afterEnd: The popup appears underneath the element with the popup's upper-right corner aligned with the lower-right corner of the element. The right edges of the element and the popup are aligned.

beforeStart: The popup appears above the element with the popup's lower-left corner aligned with the upper-left corner of the element. The left edges of the element and the popup are aligned.
 

beforeEnd: The popup appears above the element with the popup's lower-right corner aligned with the upper-right corner of the element. The right edges of the element and the popup are aligned.
 

endAfter: The popup appears to the right of the element with the popup's lower-left corner aligned with the lower-right corner of the element. The bottom edges of the element and the popup are aligned.

endBefore: The popup appears to the right of the element with the popup's upper-left corner aligned with the upper-right corner of the element. The top edges of the element and the popup are aligned.

startAfter: The popup appears to the left of the element with the popup's lower-right corner aligned with the lower-left corner of the element. The bottom edges of the element and the popup are aligned.

startBefore: The popup appears to the left of the element with the popup's upper-right corner aligned with the upper-left corner of the element. The top edges of the element and the popup are aligned.