Wednesday, November 5, 2014

Oracle ADF - Performance Testing Tools

Following post list some tools that can be used for Oracle ADF application performance testing, along with other available tools.
  
JMeter

JMeter is required in order to make multiple calls to the application. A test plan should be created which specifies the steps to be replicated by the JMeter
Test plan configured for sample application is attached here

Following are the changes that are required to be done in order to use this test plan with sample application. 

1)  Open the test plan file in JMeter.

2)  Replace the <Server Name or IP> and <Port Number> field corresponding to five HTTP requests that exist in the test plan as shown below, with the details of the server where  ADF application is running. This is not required if the application is running on local host


3) Now click on Thread Group – Sample App in order to specify the number of users, loop count per user and the ramp-up time in which the threads should start sending the request to the application.



JDeveloper Memory Profiler

JDeveloper memory profiler is required in order to see the number of objects that are getting created in each request and the number of objects that are getting garbage collected. It also displays the number of objects and their size that’s left in the memory after application finishes the requests.


Following are the steps to use JDeveloper memory profiler.

1)  Open JDeveloper.
2)  Browse the application that is to be analyzed.
3)  Start the memory profiler from the JDeveloper Run menu as shown below.




4)  This will start/restart the integrated weblogic server. Once the server is up, the application will start at local host and JDeveloper weblogic port.
5)  In order to access the application, either we can directly hit the application from browser or we can use JMeter to hit the application.
6)  Following is a sample report generated using JDeveloper which is showing the total number of instances created and total number of instance collected during garbage collection.


JVisualVM

JVisualVM is required to create the heap dump of the JVM on which the ADF application is deployed and running. The heap dump file generated using JVisualVM can be used to analyze the memory usage, objects count and any suspect for memory leak.

Following are the steps to create the heap dump using JVisualVM

1)  Open JVisualVM from the JDK directory like 

…/jdk160_29/bin/jvisualvm.exe

2)  Select the weblogic server instance on which ADF application is running.


3)  Now open the JMeter and click start button to initiate the requests to <ADF application>
4)  Analyze the heap allocation and garbage collection activities in JVisualVM.


5)  Once the threads started from JMeter complete, click on Heap Dump button to generate the heap dump file.


Eclipse Memory Analyzer

Eclipse memory analyzer is required to analyze the heap dump created using JVisualVM. It helps in identifying the possible issues that might have been reported while running the application.

Following are the steps required to analyze the heap dumps file using Eclipse Memory Analyzer

1)  Start the eclipse memory analyzer standalone application or eclipse IDE if it has eclipse memory analyzer plug-in installed.
2)  Browse for the heap dump file created above using JVisualVM.
3)  Click finish when prompted to generate the Leak suspect report.


4)  Click on various other options provided in the IDE to analyze the dump file.


Sample Report

The below screenshot shows the problem suspects in the application.


Wednesday, August 6, 2014

Oracle ADF TreeTable CRUD Operations

Following post describes the CRUD operation on ADF TreeTable component. In order to create a TreeTable component on the page, drag and drop the collection from data control on to the page as treeTable.

Following is the code for showing department's name as tree and its employees as table under selected tree node.

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}"  var="node" columnStretching ="last" selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}" rowSelection="single" id="tt1" partialTriggers="::cb2 ::cb4">
            <f:facet name="nodeStamp">
              <af:column id="c1">
                <af:outputText value="#{node.DepartmentName}" id="ot2"/>
              </af:column>
            </f:facet>
            <f:facet name="pathStamp">
              <af:outputText value="#{node}" id="ot1"/>
            </f:facet>
            <af:column id="c7" headerText="EmployeeId">
              <af:inputText value="#{node.EmployeeId}" id="ot6"/>
            </af:column>
            <af:column id="c8" headerText="FirstName">
              <af:inputText value="#{node.FirstName}" id="ot7"/>
            </af:column>
            <af:column id="c9" headerText="LastName">
              <af:inputText id="it1" value="#{node.LastName}"/>
            </af:column>
            <af:column id="c2" headerText="Email">
              <af:inputText id="it2" value="#{node.Email}"/>
            </af:column>
            <af:column id="c3" headerText="HireDate">
              <af:inputText id="it3" value="#{node.HireDate}"/>
            </af:column>
            <af:column id="c4" headerText="JobId">
              <af:inputText id="it4" value="#{node.JobId}"/>
            </af:column>
          </af:treeTable>

Now add buttons on the page. One for Adding employee under a department, one for deleting the selected employee and one "Commit" button for saving the changes in database.

Create actionListener for "Add Employee" button and write following code in the managed bean:

    public void actionCreate(ActionEvent actionEvent) {
        try {
            //tt1 is table id in jspx page. Instead of this, table binding can also be used to access table.
            RichTreeTable richTreeTable = (RichTreeTable)actionEvent.getComponent().getParent().findComponent("tt1");
            RowIterator ri = getSelectedNodeRowIterator(richTreeTable);
            Key selectedNodeKey = getSelectedNodeRowKey(richTreeTable);
            Row[] found = ri.findByKey(selectedNodeKey, 1);
            if (found != null) {
                Row foundRow = found[0];
                String nodeDefname = foundRow.getStructureDef().getDefFullName();
                String deptViewDefName = "com.model.DepartmentsView";
                String empViewDefName = "com.model.EmployeesView";
                if (nodeDefname.equals(deptViewDefName)) {
                   //EmployeesView is name of Employee View object.
                    RowSet parents = (RowSet)foundRow.getAttribute("EmployeesView");
                    Row childrow = parents.createRow();
                    parents.insertRow(childrow);
                } else {
                    RowSet parents = (RowSet)foundRow.getAttribute("EmployeesView");
                    Row childrow = parents.createRow();
                    childrow.setAttribute("DepartmentId", foundRow.getAttribute("DepartmentId"));
                    parents.insertRow(childrow);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Key getSelectedNodeRowKey(RichTreeTable richTreeTable) {
        if (richTreeTable != null &&
            richTreeTable.getSelectedRowKeys() != null) {
            for (Object opaqueFacesKey : richTreeTable.getSelectedRowKeys()) {
                richTreeTable.setRowKey(opaqueFacesKey);
                return ((JUCtrlHierNodeBinding)richTreeTable.getRowData()).getRowKey();
            }
        }
        return null;
    }

    public RowIterator getSelectedNodeRowIterator(RichTreeTable richTreeTable) {
        if (richTreeTable != null &&
            richTreeTable.getSelectedRowKeys() != null) {
            for (Object opaqueFacesKey : richTreeTable.getSelectedRowKeys()) {
                richTreeTable.setRowKey(opaqueFacesKey);
                return ((JUCtrlHierNodeBinding)richTreeTable.getRowData()).getRowIterator();
            }
        }
        return null;
    }

For "Delete Employee" button actionListoner, write following code:

    public void actionDelete(ActionEvent actionEvent) {
        try {
            RichTreeTable richTreeTable = (RichTreeTable)actionEvent.getComponent().getParent().findComponent("tt1");
            RowIterator ri = getSelectedNodeRowIterator(richTreeTable);
            Key selectedNodeKey = getSelectedNodeRowKey(richTreeTable);
            Row[] found = ri.findByKey(selectedNodeKey, 1);
            if (found != null) {
                for (Row row : found) {
                    row.remove();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Commit button can be directly dragged and dropped on the page from data control.

Wednesday, July 9, 2014

Oracle ADF WebSerice - XMLGregorianCalendar to Util DateTime

In our Oracle ADF applicaiton, if we are consuming a web service by creating JAX-WS proxy client and exposing its methods as data control using our facade java class, then the xsd:dateTime attributes in web service input will be shown as collection attribute in data control palette and will be created as XMLGregorianCalendar object in generated java class getter's and setter's, as shown below




In order use the attribute as original date time, we can convert that to java util date, using the steps mentioned below:

1) Create a jax-ws-jaxb-customization xml file having mapping of xsd types to java types:

<?xml version="1.0" encoding="UTF-8"?>
<jaxws:bindings node="wsdl:definitions/wsdl:types/xsd:schema"
                xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
                xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
                xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                wsdlLocation="http://localhost:9001/soa-infra/services/default/SyncService/syncprocess_client_ep?WSDL">
  <jaxb:globalBindings>
    <jaxb:serializable/>
    <jaxb:javaType name="java.util.Date" xmlType="xsd:dateTime"/>
    <jaxb:javaType name="java.util.Date" xmlType="xsd:date"/>
  </jaxb:globalBindings>
</jaxws:bindings>

2) Add the above customization file in the web service property wizard, as shown in below screen shot:
Open the properties window
Click on Add File and select the customization file created above.
3) Click "Ok" to close the wizards.

4) The web service proxy files will get regenerated. Also the XMLGregorianCalendar objects in the request or response java files, will get converted into java.util.date


4) Under the web service files, new adapter files will get created based on the number of mappings added in the customization file. The files can be modified to format the date time as per required format, as shown in the code below:



Code::
public class Adapter1 extends XmlAdapter<String, Date>
{
    private String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS";

    public Date unmarshal(String value) throws ParseException {
        return new SimpleDateFormat(pattern).parse(value);
    }

    public String marshal(Date value) {
        String dateAsString = "";
        if (value == null) {
            return null;
        }
        try{
            SimpleDateFormat format = new SimpleDateFormat(pattern);
            dateAsString = format.format(value);
        }catch(Exception e){
        //Handle
    }
        return dateAsString;
    }
}


5) Recreate the data control based on facade class. The attributes will now be shown as simple attributes:
Note: Each time the proxy service is regenerated, the Adapter files will get regenerated as well. So we need to keep the backup of adapter file code (if the default code was modified) before regenerating the proxy and update it again after regeneration.

Saturday, February 1, 2014

Oracle ADF WebService LoadGrammer Error

Following error might be encountered while regenerating web service JAX-WS proxy either by directly right clicking on service and selecting "Regenerate WebService Proxy" or by opening the service properties windows and clicking "Ok".

oracle.jdeveloper.webservices.tools.WsdlValidationException: Error creating model from wsdl "http://localhost:9001/soa-infra/services/default/SyncService/syncprocess_client_ep?WSDL": org.apache.xerces.impl.xs.XMLSchemaLoader.loadGrammar([Lorg/apache/xerces/xni/parser/XMLInputSource;)V
    at oracle.jdevimpl.webservices.tools.wsa.WsaAdaptor.newWsdlValidationException(WsaAdaptor.java:825)
    at oracle.jdevimpl.webservices.tools.wsa.WsaAdaptor.createProxy(WsaAdaptor.java:295)
    at oracle.jdeveloper.webservices.tools.WebServiceTools.createProxy(WebServiceTools.java:293)
    at ....
Caused by: java.lang.NoSuchMethodError: org.apache.xerces.impl.xs.XMLSchemaLoader.loadGrammar([Lorg/apache/xerces/xni/parser/XMLInputSource;)V
    at org.apache.xerces.jaxp.validation.XMLSchemaFactory.newSchema(Unknown Source)
    at com.sun.tools.xjc.reader.internalizer.DOMForest.weakSchemaCorrectnessCheck(DOMForest.java:487)
    at com.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.bind(SchemaCompilerImpl.java:227)
    at com.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.bind(SchemaCompilerImpl.java:85)
    at ...

An unofficial solution to above issue is to remove "ADF Model Runtime" and "ADF Model Generic Runtime" libraries from the project properties (the project in which proxy is getting generated).


After removing the libraries, the proxy should get regenerated by using either of the above two ways,