Introduction
I have been recently building ADF UI components and noticed a small difference between Data Controls of Custom Methods in an Application Module vs in a POJO Service. For example, consider the following method “getCountByStatus”
public List<StatusCountTableRow> getCountByStatus() { ... }
when implemented in an POJO Service, you create a data control by right-clicking on the Java service class in JDeveloper and will find the Data Control looks something like this – the fields of the List element are displayed (and when dropped on a JSF page there are two columns, for example, Count and Status)
but when you implement the same in an Application Module you get a “method” binding with “element” returned.
Problem:
The issue is that when you drag and drop the Data Control (DC) created from the POJO you get the columns based on the attributes of the method element. However, when you use the DC from the Application Module – you only get one column in the table component.
An alternative is to use a POJO Service as a wrapper on the Application Module and … wait! Let me stop you there. Bad idea! You will need to track when to release the AM etc and it is better to use the framework components as intended.
Solution:
The answer is to create a View Object to wrap the method call into a programmatic query and associate this VO with the Application Module. When you do this, the data control of the App Module is able to render the View Object’s Row elements into the table component on a JSF page. (Phew! I hope I said the right).
So this is how it looks like ….
How to steps:
In my example, I have a query that does a
"Select count(*) from <table> where status=:bindStatus"
and gets a “COUNT by STATUS” from the table. I have a list of STATUS which I need to iterate over and build a list which contains the “status” and “count” values.
Notice how the “status” and “count” are in a class called
"StatusCountTableRow"
and the
"UserStatusData"
class holds a
"List<StatusCountTableRow>".
I also expose the “Iterator” for the list in the UserStatusData class so as to allow a client to get “next()” and “size()” etc.
Now we are ready to create the Read Only View Object – programmatically … create an ADF View Object and select the following option
Next create the fields and assign the ROVO to the Application Module (easy steps)
Finally edit the View Object Impl ….
…. and override the following methods:
Override “Create”:
Obviously called when VO Instance is created, calls super.create() and nullifies the Query elements that are required for a SQL based VO
[See: http://mucahiduslu.blogspot.com/2008/05/programmatic-view-object-using-ref.html]
protected void create() { super.create(); getViewDef().setQuery(null); getViewDef().setSelectClause(null); setQuery(null); }
Override “executeQueryForCollection”:
This method gets the “Query Collection” as the variable “qc” along with query params. I used this spot to execute my query … i.e. I get an instance of the Application Module and execute the method on it to get the List<StatusCountTableRow> from the AM and create an instance of the “UserStatusData” and set the result from the AM on the new UserStatusData instance …
After that I set the user data on the view object, the method call for “setUserDataForCollection()” takes in objects – so your user data class can have anything, as long as it can iterate over a result set and get you the data. This is a key part of using the framework … we are caching the User Data using setUserDataForCollection and later we retrieve it using “getUserDataForCollection( )”
[See: http://download.oracle.com/docs/cd/E14004_01/books/SSDev/SSDev_CustomizingModel13.html#wp1031402]
protected void executeQueryForCollection(Object qc, Object[] params, int noUserParams) { UserStatusData usd = new UserStatusData(getOrderByClause()); usd.setResultsList(((AssetMgmtAMImpl)getApplicationModule()).getCountsByStatus()); setUserDataForCollection(qc, usd); super.executeQueryForCollection(qc, params, noUserParams); }
Override “hasNextForCollection”:
This is important so as to tell the ViewObject when to stop … notice how I do a “getIterator” and not a “getList().getIterator()” – try this and see why for yourself.
protected boolean hasNextForCollection(Object qc) { if (((UserStatusData)getUserDataForCollection(qc)).getIterator().hasNext()) return true; else { setFetchCompleteForCollection(qc, true); return false; } }
Override “createRowFromResultSet”:
So far we have been dealing with creating the query, storing the result and finding out if we have reached the end or not… but how does the type “StatusCountTableRow” get converted to a Table Row on a page? (Remember our core issue). The magic happens here. This is where we take the “next()” row in the “Iterator” in the “UserData” which we had cached in the framework and “map” the values to the fields we created in the View Object.
See how “populateAttributeForRow(row, 0, cd.getStatusName())” is used to map the nth column on the “row” with a value in the “StatusCountTableRow” instance retrieved using iterator.next()
protected ViewRowImpl createRowFromResultSet(Object qc, ResultSet resultSet) { ViewRowImpl row = createNewRowForCollection(qc); StatusCountTableRow nextRow = ((UserStatusData)getUserDataForCollection(qc)).getIterator().next(); populateAttributeForRow(row, 0, nextRow.getStatusName()); populateAttributeForRow(row, 1, nextRow.getCount() + ""); return row; }
Override “getQueryHitCount”:
Override this method to return the size of the result
public long getQueryHitCount(ViewRowSetImpl viewRowSet) { return ((UserStatusData)getUserDataForCollection(viewRowSet.getQueryCollection())).getResultsList().size(); }
Hi,
Can you please share the code.
Regards,
Satya.
LikeLike
hasNextForCollection() code method that you have mentioned above is not working for me.
I tried like this:
if (((UserStatusData)getUserDataForCollection(qc)).getResultsList().iterator().hasNext()){
hasNextForCollection(qc);
return true;
}
else {
setFetchCompleteForCollection(qc, true);
return false;
}
in AM’s getCountsByStatus() method, I have added multiple objects to StatusCountTableRow list.
But my VO is not iterating through all the values of StatusCountTableRow. in UI it is showing only one element for me.
I guess there might be a problem in hasNextForCollection method.
Could you please correct me how to get the list of StatusCountTableRow values in UI.
LikeLike
Hi there,
Is it possible to share an archive of the project source which you’ve used for this example?
Or maybe only provide me with the implementation of UserStatusData please?
Regards.
LikeLike
Thank you for this great post,
Regarding the question above, I think the problem is simply with the “getIterator()” method, since it can not return the “ResultsList.iterator()” because that will return a new iterator each time and lead to stuck in the first row,
so the solution is simply to define the iterator as an instance variable of the class and initialize it only once,
for example:
public Iterator getIterator() {
if(iterator == null)
iterator = resultsList.iterator();
return iterator;
}
but i still have a question regarding the ApplicationModuleImple, and it will be great if you can include the method implementation in that module in this post,
because I am thinking of how the parameters will be passed to that query.
thank you again,
regards,
LikeLike