Wednesday Feb 20, 2013

Implementing “Contains” autosuggest behavior for an Input Combo Box LOV

The framework provided autosuggest behavior performs a “Starts with” search for displaying the values in the autoSuggest of an Input Combo Box List of Values. In this post, we will see how to implement a custom autosuggest behavior which returns values by performing a “contains” search.

The following image shows an input combo box list of values dropped on a .jsff:

We can add an autosuggest behavior from the Structure window by clicking af:inputComboBoxListOfValues -> Insert inside af:inputComboBoxListOfValues -> ADF Faces -> Auto Suggest Behavior

This is the autosuggest behavior provided by the framework which returns all values which START WITH the partial string typed by the user in the input combo box list of values. We will write a custom method in the backing bean which returns the values we want to show as follows:

As seen above, this method takes a String parameter which is the partial string entered by the user and it returns a List of SelectItems which have that string as a substring. The list is retrieved in the AMImpl as follows:


In this method, we get the VO which is used to populate the input combo box list of values and apply the following view criteria on it:


The bind variable ‘Bind_ResourceName’ is set to the partial string entered by the user. By applying the view criteria we get an iterator to all the rows matching this condition, i.e. all rows where the value of “DisplayName” contains the string entered by the user.

Finally, we use the custom method from the bean instead of the framework generated suggestedItems:


Now, when the user types in the input combo box list of values, all the values containing that partial string are displayed in the autosuggest as shown:


Monday Feb 11, 2013

Restricting the number of items Select One Choice (Choice List) LOV

While implementing an LOV, the choice has to be made about the UI component that will be used for displaying the list of values. When this list is small the preference is given to the Select One Choice (choice list). Otherwise Combo Box is preferred because it provides additional functionality like search, filter etc. Select One Choice executes database query on page load to retrieve the list data whereas the LOV queries are not executed at page load. These are executed only on demand, when the user opens the LOV component. This implies that for faster page loading, we should use Select One Choice only for those attributes for which the returned list contains few rows. On second load, however, the values for the Select One Choice will be retrieved from the cache. Thus, there might be inconsistencies if the data is changed. So, if there is a long LOV the Select One Choice might not be the best choice.

In the UI Hint tab, if the component selected is Combo Box then the UI provides the facility to add the Search/Filter facility; it allows to specify the MRU count, query limit and other options. The Query Limit serves to control the VO fetch size and to change the number of records the Combo Box can display. Specifying a large value is not recommended. When the Query Limit is set, then the VO’s source XML file is modified internally. The <ListBinding> tag created for the LOV is changed and the attribute ListRangeSize is set to the value specified for the Query Limit. The <ListBinding> element maps the list data source attribute to the LOV-enabled attribute. The ListRangeSize attribute governs the number of items that will be shown at time in the component.


The number of rows that the LOV fetches is determined in part by the ListRangeSize setting in the LOV-enabled attribute’s list binding definition. This definition can be specified in the Edit List of Values dialog (of this attribute, in the VO Overview editor). If the number of records fetched is very large, the default value for ListRangeSize may truncate the values (to be displayed in the component). The default number of fetched records for LOV depends on the type of component used to display the records. For instance, in the case of the Combo Box with List of Values component and the Input Text with List of Values component, the default value for ListRangeSize is 10. For all other types of list components including Select One Choice list, the default value for ListRangeSize is set to -1, meaning all. However, the ListRangeSize value has no effect on the records that the end user can search on in the lookup dialog displayed for the List of Values type components.

Query Limit is disabled for all other component types including the Select One Choice List component. These components place no restriction on the number of rows that the LOV will access. However, it is not recommended to set ListRangeSize=-1 since it may lead to a performance hit. For components like Select One Choice list this value can be modified manually in the VO source XML file and set to a smaller size.

Apart from setting the above properties, it is required to set some tuning options for improving the performance of the LOVs. The View Accessor is used to access the data source for LOV attribute. It is an ADF BC mechanism that lets one obtain full list of possible values from the row set of the data source VO. The View Accessor can also be used to limit the set of values the LOV displays. The View Accessor Edit dialog provides the facility to set the tuning options.

The count specified for In Batches of specifies the number of rows to be fetched from the database in each batch. The As Needed and All at Once options work in conjunction with the value of the In Batches of for determining the number of round trips. Thus the in Batches of specifies the fetch size. When the access mode is set to Range Paging, Range Paging Auto Post, or Range Paging Incremental, the range size is used to specify the number of rows to be fetched for the row set. The default range size is -1 and fetches all the rows.

Thus, to conclude, the fetch size and range size specify the number of rows fetched from the database and the number of round trips to the database. They do not control the number of LOV items displayed at runtime. This is controlled by the Query Limit attribute on the UI Hint Tab for the Combo box list of values LOV component and the ListRangeSize attribute of the < ListBinding> tag in the VO xml for all other LOV components.

Friday Aug 24, 2012

In-Memory Filtering and RowMatch

Assumption

We have a view object named ExampleVO which is based on entity object ExampleEO, and this VO has an attribute called ExampleFlag. ExampleVO has a clause in the base query that only allows rows that match the criteria ExampleEO.ExampleFlag = ‘Y’. We have another view object named CreateExampleVO that is used to add new rows to the ExampleEO. Both these view objects have view link consistency enabled.

Observation


Now if we insert a row into CreateExampleVO with the value of ExampleFlag as ‘N’ one would expect that this row will not be included in the ExampleVO. This is not the behavior that is observed. We now have a row in the ExampleVO that does not match the clause. If view link consistency is enabled, any new row created by any view object based on the same entity is added (regardless of the query) to the row set of all VO’s based on that EO.

Solution

If the view object has a design-time WHERE clause like ExampleEO.ExampleFlag = ‘Y’, you can apply a RowMatch object to your view object to perform the same filtering in-memory.

The filtering expression of the RowMatch object you specify prevents new rows that don’t match the condition from being added.

You can add a RowMatch to the view object by creating the same clause in a view criteria and applying it. The mode for said view criteria should be set to ‘In-memory’.
Note: You don’t have to remove the where clause from the query

Relevant Links.
http://docs.oracle.com/cd/E21043_01/web.1111/b31974/bcadvvo.htm#BCGHDDAD

https://blogs.oracle.com/workingwithadf/entry/little_mysterious_rowmatch


Monday Jul 30, 2012

Transient Instance Members in AM - Their Passivation and activation

There are cases where everything works well during development and testing but we face a lot of exceptions in the production environment. In most of the cases, this strange behavior will be related to wrong transient attribute usage, transient attribute whose value is not passivated and then it is lost after activation. In production, when there will be high workload and many concurrent users, ADF will start to passivate Application Module instances and we will face a problem. Production environment can be simulated by disabling Application Module Pooling.

Disabling AM Pooling
For disabling the AM pooling, checkout the AM and right click on it, select Configuration and then select Edit. Click on the “Pooling and Scalability” tab and disable the “Enable Application Module Pooling” check box.

Passivation of Instance Variable in AM
Instances variables are not passivated by the framework. Framework recommends not using any instance variables by the developer. If at all developer badly needs instance variables in any of the Impl class, Developer needs to explicitly override 'passivateState' and 'activateState' methods(in corresponding Impl) to passivate and activate those variables.

Passivating and Activating Custom Information in the State Snapshot XML Document

/**

* Overridden framework method to passivate custom XML elements
* into the pending state snapshot document
*/
public void passivateState(Document doc, Element parent) {

// 1. Retrieve the value of the value to save

int counterValue = getCounterValue();

// 2. Create an XML element to contain the value

Node node = doc.createElement(COUNTER);

// 3. Create an XML text node to represent the value

Node cNode = doc.createTextNode(Integer.toString(counterValue));

// 4. Append the text node as a child of the element

node.appendChild(cNode);

// 5. Append the element to the parent element passed in

parent.appendChild(node);

}

/**

* Overridden framework method to activate custom XML elements

* into the pending state snapshot document

*/

public void activateState(Element elem) {

super.activateState(elem);

if (elem != null) {

// 1. Search the element for any <jbo.counter> elements

NodeList nl = elem.getElementsByTagName(COUNTER);

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 <jbo.counter> element

Node child = nl.item(i).getFirstChild();

if (child != null) {

// 4. Set the counter value to the activated value

setCounterValue(new Integer(child.getNodeValue()).

intValue()+1);

break;

}

}

}

}

}

private int getCounterValue() {

String counterValue = (String)getSession().getUserData().get(COUNTER);

return counterValue == null ? 0 : Integer.parseInt(counterValue);

}

private void setCounterValue(int i) {

getSession().getUserData().put(COUNTER,Integer.toString(i));

}

private static final String COUNTER = "jbo.counter";

Passivating/Activating Binary Objects

@Override
protected void passivateState(Document doc, Element parent) {
super.passivateState(doc, parent);
String stringifiedValue=getBase64EncodedObject( objectTobePassivated )

// Add them to the XML
Node nodeUserData = doc.createElement("USERDATA");
Element elem = doc.createElement("AM_OBJECT");
elem.setAttribute("KEY", key);
elem.setAttribute("VALUE",(String)base64EncodedProperties.get(key));
elem.setAttribute("TYPE", "java.lang.Object");
nodeUserData.appendChild(elem);
parent.appendChild(nodeUserData);
//...........Your code goes here........
}
private String getBase64EncodedObject(Object propertyValue) {
// Converts object to Base64 format
ByteArrawetputStream bos = new ByteArrawetputStream();
FastByteArrawetputStream fbos = null;
try {
fbos = new FastByteArrawetputStream();
ObjectOutputStream out = new ObjectOutputStream(fbos);
out.writeObject(propertyValue);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
return Base64.byteArrayToBase64(fbos.getByteArray());
}

@Override
protected void activateState(Element parent) {
if (parent != null) {
NodeList nl = parent.getElementsByTagName("USERDATA");
if (nl.getLength() > 0) {
Node n = nl.item(0);
NodeList nl2 = n.getChildNodes();

for (int i = 0; i < nl2.getLength(); i++) {
Element e = (Element)nl2.item(i);

String key = e.getAttribute("KEY");
String value = e.getAttribute("VALUE");

try {
byte[] obj = Base64.base64ToByteArray(value);
ObjectInputStream in =
new ObjectInputStream(new
FastByteArrayInputStream(obj));
setValue(key, in.readObject());
in.close();
} catch (Exception ex) {
ex.printStackTrace();
}

}
}
}
super.activateState(parent);
}

Passivate state of a view object

1. In the Application Navigator, double-click a view object to open it in the overview editor.

  1. On the General page, expand the Tuning section.
  2. Select Passivate State to make sure the view object data is saved.

Optionally, you can select Including All Transient Attributes to passivate all transient attributes at this time but see Fusion Dev Guide Section 39.8.4, "What You May Need to Know About Passivating Transient View Objects" for additional information.

References

        1. http://andrejusb.blogspot.in/2010/01/demystifying-adf-bc-passivation-and.html
2. 
http://docs.oracle.com/cd/E12839_01/web.1111/b31974/bcstatemgmt.htm#sthref3211
3.
http://jobinesh.blogspot.in/2010/09/annotate-your-applicationmodule-to.html
4.
http://jdeveloperfaq.blogspot.in/2010/02/faq-14-how-to-test-application-module.html
5.
http://productivitywithchoice.blogspot.in/2011/03/is-your-application-passivation-safe.html

Button Alignment not working in Chrome

Suppose we have a popup with a panelGroupLayout containing 2 command buttons as given below :

<af:popup id="p1">
      <af:panelWindow id="pw1" closeIconVisible="false" modal="true"
                      resize="on">
        <af:group id="pgl2">
          <af:inputText label="Label 1" id="it1"/>
          <af:inputText label="Label 2" id="it2"/>
          <af:spacer width="10" height="100" id="s1"/>
          <af:panelGroupLayout id="pgl3" valign="bottom" layout="horizontal" >
            <af:commandButton text="Save" id="cb2"/>
            <af:commandButton text="Close" id="cb3"/>
          </af:panelGroupLayout>
        </af:group>
      </af:panelWindow>
    </af:popup>

We want the buttons to be right aligned so we do the following:

  <af:panelGroupLayout id="pgl3" halign="right" valign="bottom" layout="horizontal">

 This works in Firefox (find screenshot below)


But it does not work in Chrome ( find screenshot below )

To make it work in Chrome we need to do the following :

 <af:panelGroupLayout id="pgl3" halign="right" valign="bottom"
                               layout="horizontal" styleClass="AFStretchWidth">

AFStretchWidth is a marker style class that will declaratively stretch a component horizontally in a wide flowing container. This style class should be used instead of attempting to use a percentage width as an inlineStyle width to stretch a component horizontally.

Thursday Jul 05, 2012

Bind Variable and SQL error during statement preparation

 I was getting the following exception at run-time.

JBO-27122: SQL error during statement preparation. Statement: SELECT AxEO.A_ID, AxEO.B_ID, AxEO.C_ID, ByEO.A_ID, ByEO.B_ID, ByEO.C_ID, Cz.A_ID, Cz.B_ID, Cz.C_ID FROM ABC_x AxEO, ABC_y ByEO, ABC_z CzEO WHERE AxEO.A_ID = ByEO.A_ID AND  CzEO.A_ID = :Bind_PId

I copied and pasted the query on SQL worksheet, replaced :Bind_PId with a valid id, and executed the query. The query worked alright, implying the query was alright. I tried to connect to different DBs but the issue persisted, meaning it was not a DB issue either.

Finally, the root cause was found to be in the concerned VO; one of the bind variables (say Bind_TId) was marked "Required". De-selecting the Required check-box resolved the issue.

In retrospect, the issue looks to be rather straight-forward. However, the error message is not very helpful, if not misleading. Besides, it's counter-intuitive to think that a bind variable which is not being used in a query can cause error while statement preparation. The other bind variable - Bind_TId - was being used in other view criteria, not the view criteria involved in the given query. Still, it was required.

Wednesday Jun 27, 2012

Using Exception Handler in an ADF Task Flow

Problem Statement:

Exception thrown in a task flow gets wrapped in an exception that gives an unintelligible error message to the user.

Figure 1

Solution 1. Over-writing the error message with a user-friendly error message.

Figure 2


Steps to code

1. Generating an exception: Write a method that throws an exception and drop it in the task flow.
2. Adding an Exception Handler: Write a method (example below) to overwrite the Error in the bean or data control and drop the method in the task flow.

Figure 3

This method is marked as the Exception Handler by Right-Click on method > Mark Activity> Exception Handler or by the button that is displayed in this screenshot

Figure 4

The Final task flow should look like this. This will overwrite the exception with the error message in figure 2.
Note: There is no need for a control flow between the two method calls (as shown below).

Figure 5

Solution 2: Re-Routing the task flow to display an error page

Figure 6

Steps to code

1. This is the same as step 1 of solution 1.
2. Adding an Exception Handler: The Exception handler is not always a method; in this case it is implemented on a task flow return.  The task flow looks like this.

Figure 7

In the figure below you will notice that the task flow return points to a control flow ‘error’ in the calling task flow.

Figure 8

This control flow in turn goes to a view ‘error.jsff’ which contains the error message that one wishes to display.  This can be seen in the figure below. (‘withErrorHandling’ is a  call to the task flow in figure 7)

Figure 9

Monday Jun 25, 2012

View Link inConsistency

What is View Link Consistency?

When multiple instances (say VO1, VO2, VO3 etc) of an EO-based VO are based on the same underlying EO, a new row created in one of these VO instances (say VO1)can be automatically added (without re-query) to the row sets of the others (VO2, VO3 etc ). This capability is known as the view link consistency. This feature works for any VO for which it is enabled, regardless of whether they are involved in a view link or not.

What causes View Link inConsistency?

View Link Consistency for a VO could be disabled by disabling jbo.viewlink.consistent for the VO (or globally), or by applying setAssociationConsistent(false). Otherwise, any of the following can cause View Link inConsistency. 

1. setWhereClause

2. Unreferenced secondary EO

3. findByViewCriteria()

4. Using view link accessor row set

Why does this happen - View Link inConsistency? Well, there can be one of the following reasons.

a. In case of 1 & 2, the view link consistency flag is disabled on that view object.

b. As far as 3 is concerned, findByViewCriteria is used to retrieve a new row set to process programmatically without changing the contents of the default row set. In this case, unlike previous cases, the view link consistency flag is not disabled, meaning that the changes in the default row set would be reflected in the new row set. 

However, the opposite doesn't hold true. For instance, if a row is deleted from this new row set, the corresponding row in the default row set does not get deleted. In one of my features, which involved deletion of row(s), I observed that my changes were not reflected on UI. To resolve this apparent "view link inconsistency" issue, I replaced findByViewCriteria by applyViewCriteria.

b. For 4, it's similar to 3 - whenever a view link accessor row set is retrieved, a new row set is created.

Now, creating new row set does not mean re-executing the query each time, only creating a new instance of a RowSet object with its default iterator reset to the "slot" before the first row. Also, please note that this new row set always originates from an internally created view object instance, not one you that added to the data model. This internal view object instance is created as needed and added with a system-defined name to the root application module.

Anyway, the very reason a distinct, internally-created view object instance is used is to guarantee that it remains unaffected by developer-related changes to their own view objects instances in the data model.

JUnit: How to access rootAM from within nestedAM?

In normal model project, the way to access root AM from within nested AM is simple -

RootAMImpl rootAM =  (RootAMImpl)this.getRootApplicationModule();

However, the same approach doesn't help in JUnit model project. Use the following approach - 

Inside setUp() method --  RootAM rootAM =  (RootAM)Configuration.createRootApplicationModule(ROOT_AM, ROOT_AM_CONFIG);

Inside tearDown() method -- Configuration.releaseRootApplicationModule(rootAM , true);


About

ADF Tips and Tricks

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today