JDeveloper: How to setup and use a converter

JDeveloper: How to setup and use a converter

In this post I show how to setup the server side part of a converter and how to use it in an application. Converters can have a client side too and all af:converter do have one. For a nice sample on what you can do with client side converters see ADF: Smart Input Date Client Converter. The big difference is that the client side converter is done on the client side with JavaScript and no server round trip is done for the conversation.

Why are converters needed at all?

Sometimes the data you get from a source like the database table is not in a format you like to show to the user. Common cases are showing strings in special formatting, e.g. social security numbers or phone numbers. You can use converters to show the content of clob and blob columns in the UI too.

The ADF framework provided some converters out of the box:

These can be used without the need to program anything.

What is missing from the out of the box converters is one which can be used to format a string.

One thing to remember that the new format should only be used in the UI to show the data in a specific format. You normally don’t want to store it in this special format.

We create a converter which exchanges each uppercase character ‘B’ in a string with the string “-Z-”. The sample is not very useful, but it shows what can be done with converters.

Use Case

A string can contain any character. However when the string is shown on the UI there should be no ‘B’ visible. Instead of the ‘B’ we should show ‘-Z-’. This should only be done when the string is visible on the UI. When the string is stored in the db or some other place it should be stored with the ‘B’.

Implementation

I used JDev 11.1.1.7.0 for this sample, which is the oldest JDev version I have access to. The steps to create a converter should be almost equal in all versions, but I deliberately choose the oldest JDev I have so that other users with other version should have no problem migrating this sample to their version.

The final sample can be downloaded from GitHub at BlogConverterSample.

Model Project

We start by creating a fresh ADF Web Application. If you want a detailed description on how to do this, you can follow Writing Reproducible Test Cases: Why and How. For the model part I only use one DB table, the EMPLOYEES table. The resulting model project looks like

We don’t need to make any change to the generated project. This model project is only created to show that the converter works on data read from the DB table too.

ViewController Project

For users interested in more details about converters, please read the doc at http://docs.oracle.com/cd/E48682_01/web.1111/b31973/af_validate.htm#BABGIEDH. To start with the converter, we create a java class in the ViewController project and name it MyB2ZConverter.java. As package we choose ‘de.hahn.blog.convertersample.view.converter’

As the class will be a converter we have to implement the javax.faces.convert.Converter interface. For this you click on the green ‘+’ sign and can search for the right interface

This process will create the java class and two methods

These are the methods we have to implement for our use case. The first method ‘getAsObject’ is called when the data from the UI is send to the server for further processing. The ‘getAsString’ method is called when data from a storage (DB, bean property or pagedef variable) is going to be rendered to the UI.

As our use case is to exchange every “B” with the string “-Z-” we can implement the getAsString method easily by replacing every “B’ with “-Z-”. The method has three parameters, the current FacesContext which you can use to write messages, the UIComponent for which the converter is called and finally an Object representing the data which we want to convert. The result of the conversion must be a String. The resulting method look like

/** Method to get the string representation of hte object to use in the UI
* @param facesContext current facesContext
* @param uIComponent component which was used to deliver the data
* @param object data from storage to be converted
* @return sting to use for in the UI
*/
public String getAsString(FacesContext facesContext, UIComponent uIComponent, Object object) {
  if (object != null) {
    String ret = object.toString().replaceAll("B", "-Z-");
    return ret;
  } else {
    return null;
  }
}

After the check if the object to convert is null (in this case there is nothing to do), we use the String.replaceAll(…) method to search for ‘B’ and replace it with “-Z-”.

Keep in mind that the first parameter to the replaceAll method is a regular expression (see String.replaceAll(java.lang.String, java.lang.String)).

Now, if the data from the UI is send back to the model layer, it has to be converted back into the original format. So we have to do the conversion backwards by replacing all “-Z-” with “B” in the getAsObject(…) method:

/** Method which get the data from a uiComponent and should return it in the format we like to store in the DB (or elswhere)
* @param facesContext current facesContext
* @param uIComponent component which was used to deliver the data
* @param string data from the ui component
* @return object to use for further work (e.g. storage in the DB)
*/
public Object getAsObject(FacesContext facesContext, UIComponent uIComponent, String string) {
  if (string != null) {
    String ret = string.replaceAll("-Z-", "B");
    return ret;
  } else {
    return null;
  }
}

The result will be an Object which will be passed back to the model layer. If you don’t implement the getAsObject(…) method and just return the third parameter as resulting object, you would change every data in the back end to the new format. This may be your intention, but most often you don’t want to do this. It would mark every row of data dirty you have visited without any user interaction. This is because you pass different data back to the model than you read from it.

The last step to do is to register the custom converter in the faces-config.xml file of the ViewController project. Open the faces-config.xml file in JDev and select the ‘Converter’ tab

Click the green ‘+’ sign to get the an empty row in the converter section. Go to the property window and you see

Where we click on the ‘…’ button on the right end of the ‘Class’ field. We get the search for a class dialog where we look for the MyB2ZConverter class

Select the class and enter an ID fro the converter. This ID will be used in the UI to tell a component to use this converter.

Finally the converter section look like

UI Page

Now we can use the converter in a page or fragment. We start with a simple page where we define a inputText field and a button to submit the content of the field to see the converter working.

In the adfc-config.xml we add a JSPX page named ‘index’

And this page uses a quick layout as seen here

We add a title and the inputText field, a button to submit the data and two outputText fields to show what the converter has done to the data. The page layout looks like

or in code

If you like to copy the code use the following representation:

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html"
 xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
 <jsp:directive.page contentType="text/html;charset=UTF-8"/>
 <f:view>
 <af:document id="d1">
 <af:form id="f1">
 <af:panelStretchLayout topHeight="50px" id="psl1">
 <f:facet name="top">
 <af:outputText value="Converter Sample" id="ot1" inlineStyle="font-size:x-large;"/>
 </f:facet>
 <f:facet name="center">
 <af:panelGroupLayout layout="scroll" xmlns:af="http://xmlns.oracle.com/adf/faces/rich" id="pgl1">
 <af:inputText label="Enter String" id="it1" value="#{bindings.myInput1.inputValue}">
 <f:converter converterId="B2ZConverter"/>
 </af:inputText>
 <af:commandButton text="refresh" id="cb1"/>
 <af:outputText value="current data: #{bindings.myInput1.inputValue}" id="ot2"/>
 <af:outputText value="current data with converter: #{bindings.myInput1.inputValue}" id="ot3">
 <f:converter converterId="B2ZConverter"/>
 </af:outputText>
 <af:commandButton text="Converter with DB Data" id="cb2" action="emp"/>
 </af:panelGroupLayout>
 <!-- id="af_one_column_header_stretched" -->
 </f:facet>
 </af:panelStretchLayout>
 </af:form>
 </af:document>
 </f:view>
</jsp:root>

Hint: you might notice another component, a button which is later used to navigate to a second page. This is described later.

For the inputText field we need to store the data a user enters. For this we can either use a DB table, a bean property or a pagedef variable. We use a pagedef variable (more on see see Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef) which we bind to the value property of the inputText component (value=”#{bindings.myInput1.inputValue}”). The converter is setup by adding an f:converter tag like

<af:inputText label="Enter String" id="it1" value="#{bindings.myInput1.inputValue}">
  <f:converter converterId="B2ZConverter"/>
</af:inputText>

The converterId points to the ID defined in the faces-config.xml file. Running the page will show

Enter ‘Hello’ into the field and clicking outside the field (so that it looses the focus) will show

As we see, the two outputText fields don’t show anything as the data in not submitted jet. Clicking the ‘refresh’ button submits the data and the converter goes to action

Well, as the input did not have any ‘B’ nothing changes. So lets us add another word ‘Beta’ and click outside the inputText

As we did not submit the data to the server, we still see ‘Hello Beta’ and the outputText fields show ‘Hello’ both. Now click the ‘refresh’ button to get

The inputText has changed to the new format where the “B” is exchanged with the “-Z-”, however the outputtext ‘current data’ still shows the ‘Hello Beta’. The reason for this is that the data send to the binding layer was converted back using the getAsObject(…) method which exchanged the “-Z-” with “B”.

This implements the use case described at the beginning.

Now, to show that the same converter works with data from a DB table as well we add another two pages to the adfc-config.xml. One showing the employees in a read only table with a link on the employeeId which navigates to the employee details in a form.

The navigation to the second use case is done with the button mentioned earlier (‘Converter with DB data’)

Clicking on the button will show a table with employees where the EMail column was used to add the converter

The column tag looks like

<af:column sortProperty="#{bindings.EmployeesView1.hints.Email.name}" filterable="true" sortable="true"
    headerText="#{bindings.EmployeesView1.hints.Email.label}" id="c3">
  <af:outputText value="#{row.Email}" id="ot5">
    <f:converter converterId="B2ZConverter"/>
  </af:outputText>
</af:column>

Like with the inputText we just add a f:converter tag with the right ID “B2ZConverter”. With this use case we see why the getAsObject(…) method should undo the formatting. You don’t want to store the Email like this. You only want to show it this way, but not overwrite the correct Email fro the employee. You can check the DB data and see that the Email is still stored with the “B” and not the “-Z-”

To verify this we can click the link in the first column to goto the detail page of the selected employee

Again, we see the ‘Email’ in the new format and the original data ‘NO CONVERTER Email’ in the normal data. The tags used for this are

  <af:inputText value="#{bindings.Email.inputValue}" label="#{bindings.Email.hints.label}" required="#{bindings.Email.hints.mandatory}"
      columns="#{bindings.Email.hints.displayWidth}" maximumLength="#{bindings.Email.hints.precision}"
      shortDesc="#{bindings.Email.hints.tooltip}" id="it1">
    <f:validator binding="#{bindings.Email.validator}"/>
    <f:converter converterId="B2ZConverter"/>
  </af:inputText>
  <af:panelLabelAndMessage label="NO CONVERTER #{bindings.Email.hints.label}" id="plam1">
    <af:outputText value="#{bindings.Email.inputValue}" id="ot2"/>
  </af:panelLabelAndMessage>

When using the binding for the Email without the converter we see the data as it’s stored in the DB. Using the converter we see the converted data.

The sample was build with JDeveloper 11.1.1.17.0 using the HR DB schema. You can download the sample from GitHub BlogConverterSample.zip

Use LOV without af:selectoneChoice

A question on the JDev & ADF forum caught my attention. A user asked how to get the attribute value from a list of value (LOV) without using an af:selectOneChoise component. To make the use case clear, let’s look at a listview from the Departments table of the HR DB schema.

this will produce a very rudimentary output like

Selection_030

This doesn’t look charming. OK we can change this to something more meaningful like

Selection_031

But still we see only the key values instead meaningful attribute values like we get if we use a af:selectOneChoce component.

To get the output using an af:selectOneChoise we need to define list of values at the attributes in the view object, DepartmentsView in this case:

Now, when we drag the DepartmentsView onto a page and drop it as a form or table we would get the af:selectOneChoice component. However, if we create the listview again, nothing changes. JDev uses af:outputText components in this case.

To show the managers name behind the ManagerId, we can e.g. add another attribute to the view and get the manager name via a join in the sql query.

Or we put a af:selectOnChoice in the list view cell like we get for a cell in a table. This would look like

 <af:panelGroupLayout id="pgl3" layout="horizontal">
   <af:outputFormatted value="ID: #{item.bindings.ManagerId.inputValue} Name:" id="of2"/>
   <af:selectOneChoice value="#{item.bindings.ManagerId.inputValue}" label="#{row.bindings.ManagerId.label}"
     required="#{bindings.DepartmentsView1.hints.ManagerId.mandatory}"
     shortDesc="#{bindings.DepartmentsView1.hints.ManagerId.tooltip}" id="soc3" disabled="true">
     <f:selectItems value="#{item.bindings.ManagerId.items}" id="si3"/>
     <f:validator binding="#{item.bindings.ManagerId.validator}"/>
   </af:selectOneChoice>
 </af:panelGroupLayout>

and generate

Selection_039

The gray rectangle is because we have set the disabled property to true to disable the component. To get a better look we can set the readOnly property instead to get

Selection_040

which look much better. However to get this result we have to add a lot of tags to the page.

The final solution is to use the data which is present in the model to show the attribute name instead of the value like it’S done ba the framework for af:selectOneChoice. For this we only need one af:outputText tag like

 <af:outputFormatted value="ID: #{item.bindings.ManagerId.inputValue} Name: #{item.bindings.ManagerId.items[item.bindings.ManagerId.inputValue].label}"
 id="of1"/>

This will generate

Selection_041.png

The magic is the expression language

#{item.bindings.ManagerId.items[item.bindings.ManagerId.inputValue].label}"

which uses the items defined for the selectOneChoice and located the right display attribute in the collection using the attribute value.

You can download the sample application which is build with JDev 12.2.1.2 and uses the HR DB schema from GitHub BlogShowLOVattributeWithoutLOV

JDeveloper: Advanced Skin Technique

This post is about an advanced technique to change the look and feel of an ADF application. Changes to the look & feel are normally done via a skin which you use to change descriptors which are used by the ADF components. The general technique to do this is described in many blogs and articles like ADF Faces Skin Editor – How to Work with It and the official documentation at Oracle ADF Skin Editor.

In this blog we look at an advanced technique which helps to change the look and feel of components like af:query and pf:panelCollection which you can’t change using the normal available descriptors. In the below image you see the Skin Editor showing the ADF components skin descriptors.

selection_985

Use Case

In this use case we work with the af:panelCollection component. This component is used to wrap af:tree, af:treeTable and af:table components to provide additional functions. From the documentation of af:panelCollection

A panel component that aggregates collection components like table, treeTable and tree to display standard/application menus, toolbars and statusbar items.

The default top level menu and toolbar items vary depending on the component used as the child of the panelCollection.

  • For table, tree and treeTable, the default top level menu item is View.
  • For table and treeTable with selectable columns, the default top level menu items are View and Format.
  • For table and treeTable, the default toolbar item is Detach.
  • For table and treeTable with selectable columns, the default top level toolbar items are Freeze, Detach and Wrap.
  • For tree and treeTable, if the pathStamp facet is used, the toolbar buttons Go Up, Go To Top, Show as Top also appear.

The component allows us to switch off some function

Value Turns off
statusBar Status bar
viewMenu ‘View’ menu
formatMenu ‘Format’ menu
columnsMenuItem ‘Columns’ sub-menu item
columnsMenuItem:col1,col20 Columns with column ID: ‘col1’ and ‘col20’ inside ‘Columns’ sub-menu
freezeMenuItem ‘Freeze’ menu item
detachMenuItem ‘Detach’ menu item
sortMenuItem ‘Sort’ menu item
reorderColumnsMenuItem ‘Reorder Columns’ menu item
resizeColumnsMenuItem ‘Resize Columns’ menu item
wrapMenuItem ‘Wrap’ menu item
showAsTopMenuItem Tree/TreeTable ‘Show As Top’ menu item
scrollToFirstMenuItem Tree/TreeTable ‘Scroll To First’ menu item
scrollToLastMenuItem Tree/TreeTable ‘Scroll To Last’ menu item
freezeToolbarItem ‘Freeze’ toolbar item
detachToolbarItem ‘Detach’ toolbar item
wrapToolbarItem ‘Wrap’ toolbar item
showAsTopToolbarItem Tree/TreeTable ‘Show As Top’ toolbar item
wrap ‘Wrap’ menu and toolbar items
freeze ‘Freeze’ menu and toolbar items
detach ‘Detach’ menu and toolbar items

As a sample the image below shows a normal af:panelCollection (upper half) and an af:panelCollection with the view menu and the toolbar switched off (lower half)

selection_979

Looking at the possible things to switch off we don’t see anything to switch off the ‘Query by Example’ (QBE) icon. There is no feature toggle to turn this function on or off. An easy way to get rid of the icon would be to make the table not filterable. However, if we like the table to be filterable but don’t want to show the icon to switch the feature off, we have to use an advanced skin technique.

What can we do to get rid of the icon in the tool bar?

The idea is to use a skin or special css to hide the icon or the container which holds the icon. To find the container we first inspect the page in the browser using the browsers ‘Developer Tools’ which you can reach by hitting F12 in your browser. Below you see Chrome 55 with activated ‘Developer Tools’

Selection_211.jpg

The image shows the toolbars QBE image as selected element on the page (left red rectangle) and the style classes which are in use for this element (right red rectangle). The names ‘.xfo’ and ‘.xfr’ are the names of the style classes. They are minimized to reduce the download size of the page, but they are not ‘readable’. 

The first thing to do is to make the names ‘readable’ for us. We need to know which skin selector generated the style class. For this we set a context parameter in the web.xml file

 <context-param>
 <param-name>org.apache.myfaces.trinidad.DISABLE_CONTENT_COMPRESSION</param-name>
 <param-value>true</param-value>
 </context-param>

Setting this parameter to true will show us the clear names. The image below shows the same selection only this time with the real names

selection_212

One other nice feature of the ‘Developer Tools’ is that you can inspect elements by just hover over them on the page. This allow us to easily find the element we want to hide via css. Click on the icon marked in hte below image

selection_213

and move the mouse cursor over the page. You see the HTML and the active styles of the element under the cursor. This feature we use to find an element which holds the icon we want to hide and which we can address via css .

selection_214

CSS allows us to address elements inside a skin selector.  For this you need to know the skin selector, the tag or container and it’s ID inside the selector you want to address. In the image above we see the ID of the icon container we want to hide as “id=’pc1:_qbeTbr'” and the container or tag itself which is a ‘div’. The skin selector is the af|panelCollection. With this information we can can change the style attached to the ‘div’ container with the id ‘*_qbeTbr’  in the af|panelCollection as

af|panelCollection div[id$='_qbeTbr'] {
    display: none;
}

This we can add to our skin.css file. However, if we just add it this way it’s changing all af:panelCollection in our application.  If we want this only to be active for specific af:panelColletion we can add a style class name like

af|panelCollection.myPCClass div[id$='_qbeTbr'] {
    display: none;
}

Now we can add the stale class name ‘myPCClass’ to the af:panelCollection when we like the QBE icon not to be shown

 <af:panelCollection id="pc1" styleClass="myPCClass">
   <f:facet name="menus"/>
   <f:facet name="toolbar"/>
   <af:table value="#{bindings.EmployeesView1.collectionModel}" ...
   ...
 <af:panelCollection id="pc2" featuresOff="detachToolbarItem viewMenu">
 <f:facet name="menus"/>
 <f:facet name="toolbar"/>
 ...

will generate this UI output

selection_217

 

As we see, the QBE icon is gone. In the original page we have placed two af:panelCollection components. As you added the new style class only to one of them, the other QBE icon is still visible.

Extending

You can use hte same technique for other complex ADF components like af:query. Here you can style the save button which normally not  supported.

Download

You can download the sample which is build using JDev 12.2.1.2.0  and uses the HR DB schema from GitHub BlogAdvancedSkin

Summary of Day 3 at Oracle Open World 2016

Started with the (early) morning keynote ‘Oracle OpenWorld Tuesday Morning Keynote‘ hosted by Bhanu Murthy B. M., Safra Catz, Hon. Chief Minister Shri. Devendra Fadnavis and Thomas Kurian.

As the keynote and it’s content is covered all over the media already I won’t add to this. Oh, one thing I like to say is that the ‘live’ demos did not really look live to me. Would you risk that your ‘live’ demo is going to hell because of some technical problem with Thomas Kurian on stage?


Next on my list for today was ‘Agile Development and DevOps Done Even Faster with Oracle IaaS and PaaS‘ by Michael Lehmann, Suhas Uliyar and  Siddhartha Agarwal. This session talked about agile development in the cloud using IaaS, PaaS and Microservices together with DevOps tools like Docker.

First a Cloud Navtive Architecture was introduced:

img_bqnk2w

Cloud Native Architecture

 Multiple services working together to build the cloud native architecture
img_-68tuak

Services for the Cloud Native Architecture

The practical part was a sample which showed how to build, deploy, or manage mobile-fronted, API-first autoscaling application, a microservice build on Node.js here, live on stage. New here is htat you can use the Management Cloud Service to introspect the microservice to see how it runs on your environment. The just build service then is consumned by anohter app (mobile using MAX) to visualize the data.

The final dashboard build for the mobile app, it took only about 20 minutes to build and deploy:

img_20160920_113941

Dashboard for the Mobile Application

and the final detailed architecture of the application:

Detailed Architecture

Detailed Architecture


Next on my Cloud program was ‘Development Operations in the Cloud: A Use Case and Best Practices‘ by Greg Stachnick and Jeff Stephenson. They talked about best practices using the Cloud Services to develop applications from the modern DevOps point of view.

img_20160920_123652

Modern DevOps

The case study was about the development of the Developers Cloud Service itself, neat!

img_20160920_124923

Developers Cloud Service Outline

This is a big project which is running completely in the cloud. Here is an image that shows a code review screen (sorry for the poor quality)

img_20160920_125529

Code Review

After accepting the changes the changes are pushed back to the mail line, triggering the next integration cycle in the continuous integration system. The typical cloud developers life is

IMG_20160920_125920.jpg

Day  in the Life of a Devloper

and the day of a manager

to summarize these points

img_20160920_131605

Summary

This summary hit the nail on the head. I’ve bin a contractor in many projects, always asking for more machines or more power. I would be happy if I could spin up another machine to do some testing instead of waiting for some other things to finish using the machine I wait for.


Before my day is over there are two sessions about ADF and JDeveloper to attend. First was Shay Schmeltzer with ‘Oracle Application Development Framework and Oracle JDeveloper: What’s New‘ which reveals what’s coming up in the world of ADF and JDeveloper. Shay started with the short history of ADF and JDev

img_20160920_160536

which is even longer if you count JBO to it too, which started 1999. Impressive. The session was more about features which are new in JDev 12.2.1 and JDev 12.2.1.1, both versions are out quite some time.  So, nothing new for seasoned ADF developers at the beginning.

Not so well known are ADF Business Components Triggers which are more known by Forms developers. They allow to do things right before or after some DB events fires.

IMG_20160920_161311.jpg

ADF BC REST Services and REST DataControl are better known if you work in the cloud or with mobile applications:

Remote Regions where introduced with JDev 12.2.1 but needed a patch to make them run (fixed in 12.2.1.1):

img_20160920_162051

Remote Task Flows:

img_20160920_162302

UI stuff like responsive support through templates (Tablet First), Massonry Layout and matchmediaqueries:

IMG_20160920_162555.jpg

Lots of new and changed data visualization components:

and finally to sum things up, other enhancements behind the scenes:

IMG_20160920_163029.jpg

For the future we can expect more and easier support for REST services and writing Groovy code. The biggest change will be the integration of JET Composite Components into ADF pages. JET Composite Components are an equivalent to ADF Declarative Components. You can build components from using other components, add properties to them to influence their behavior. Composite Components fire events which you can use to interact. Not sure how this will work, other that in the end you have HTML. Bad thing is that there is not even a time frame for this. More details in hte next section.

Anyway, ADF is not dead! There will be future development and enhancements in JDeveloper and ADF.


Final session for this long day ‘Oracle Development Tools and Frameworks: Which One Is Right for You?‘ by Shay Shmeltzer (again) and Denis Tyrell. As some of the features are not available at the moment the ‘Safe Harbor’ statement comes to play. So if you see something which you don’t find in the available version, you have to patiantly wait for it. No time frame given 😦

Shay summarized the different frameworks ADF, MAF, JET and ABCS and pointed out their key features. As the frameworks are well known I spare most details. As promised I give more detail about the Oracle JET Composite Components.

Sample JET Composite Components

Sample JET Composite Components

Key features of JET Composite Components and there basic structure is shown below

(Coming soon!) The composite components end up together in a Tenant Component Catalog where the components can be filtered by their characteristics

 

Which late will be extended so that components are available from different channels

img_20160920_183844

In the end there will be Project Visual Code provides a low code environment

Project Visual Code

Project Visual Code

After this deep dive into JET Composite Components I present the summary of the session which shows which development framework is used for which development

At the end of the session Shay and Denis answered some question which are noteworthy. I Cant remember all question but tried to summarize the key points from the answers:

  1. Oracle focuses on JET as the future development environment Future focus on jet. Why? ADF is already feature rich and the developer don’t ask for much more.
  2. Developers want more client side development. Demand on server generated UI is going to decline.
  3. JET will get offline capabilities! This can’t be done easily with ADF.
  4. JET allows faster exchange of libraries. JavaScript developers tend to rewrite their UI faster then ADF developers (see yesterdays summary where Geertjan Wielenga made the same point).
  5.  Public Component Catalog is only public to a point. You have to submit components which then will be vetted by someone before other users can use them.
  6. Cloud IDE (writing code in the cloud) will have JavaScript capabilities
  7. ABCS (Application Builder Cloud Service) is not available on premise right now
  8. For declarative JET development look at ABCS. ABCS allows to get the underlying JET code (save as) so you can look at the code and change it, e.g. to use it elsewhere.

Naviagting an af:table in pagination mode from a bean

A question on the JDeveloper and ADF OTN forum asked about how to navigate to a specific page of an af:table in pagination mode. As of JDeveloper 11.1.1.7.0 adf tables can be rendered in scroll mode or in pagination mode where only a specific number of rows are visible in the table.

af:table in pagination mode

To navigate the pages there is a small navigation toolbar below the table which allows to enter a page number or to navigate to the previous, next, first or last page.

The problem to solve is how to navigate the paginated table from within a java bean?

The table doesn’t offer any navigation listeners or methods you can bind bean methods to. Luckily there is the RangeChangeEvent one of the FacesEvents which can e used to notify a component that change in the range has taken place.

All we have to do to navigate the table in pagination mode is to calculate the needed parameters

  • oldStart: The previous start of this UIComponent’s selected range, inclusive
  • oldEnd: The previous end of this UIComponent’s selected range, exclusive
  • newStart: The new start of this UIComponent’s selected range, inclusive
  • newEnd: The new end of this UIComponent’s selected range, exclusive

We add an input field to the page which allow us to enter a page number and a button which we use to call an action listener in a bean.

The running application looks like

Running application

Another button is used to calculate the index of the selected row in the whole rowset, the index on the page and the page number. The row index and the index of the row on the page are zero based, page numbers start with 1. Let’s look at the code:

public void onGotoPage(ActionEvent actionEvent) {
BindingContainer bindingContainer = BindingContext.getCurrent().getCurrentBindingsEntry();
// get number of page to goto
AttributeBinding attr = (AttributeBinding) bindingContainer.getControlBinding("gotopage1");
Integer newPage = (Integer) attr.getInputValue();
if (newPage == null) {
return;
}
// page one starts at index 0 so subtract 1 from the pagen number
newPage--;
DCIteratorBinding iter = (DCIteratorBinding) bindingContainer.get("EmployeesView1Iterator");
// calculate the old and new rages for the RangeChangeEvent
int range = iter.getRangeSize(); // note both the table and we take the page size from the iterator's RangeSize
int oldStart = iter.getRangeStart();
int oldEnd = oldStart + range;
int newStart = newPage * range;
int newEnd = newStart + range;
// find the table
UIViewRoot iViewRoot = FacesContext.getCurrentInstance().getViewRoot();
UIComponent table = iViewRoot.findComponent("t1");
// build the event and fire it
RangeChangeEvent event = new RangeChangeEvent(table, oldStart, oldEnd, newStart, newEnd);
((RichTable)table).broadcast(event);
// update the table
AdfFacesContext.getCurrentInstance().addPartialTarget(table);
}

Line 2-8 we get the new page number we want to navigate to. Line 9-10 we subtract 1 from the given number as the page is zero based internally. In Line 11 we get the iterator which we need to get the range size and the start of the current range (lines 13-15). These values are oldStart and oldEnd. Lines 16-17 we calculate the new start range as page to go multiplied with the range. The newEnd parameter is the newStart pus the range size.
In lines 18-20 we get to the table component on the page. Then we create the RangeChangeEvent and broadcast the event to the table component in lines 21-23. Finally we ppr the table to see the change in the UI.

To show how to calculate the other way around, to get from the selected row in a table to the index on the page, the page number and the index in the rowset we added another button ‘GetPageOfSelectedRow’which calls a listener in the same bean which builds a string with the needed information.

public void onGetCurrentPage(ActionEvent actionEvent) {
BindingContainer bindingContainer = BindingContext.getCurrent().getCurrentBindingsEntry();
DCIteratorBinding iter = (DCIteratorBinding) bindingContainer.get("EmployeesView1Iterator");
// calculate index and page number. Index is zero based!
int currentRowIndex = iter.getRowSetIterator().getCurrentRowIndex();
_logger.info("CurrentRowIndex: " + currentRowIndex);
int currentPage = currentRowIndex / iter.getRangeSize();
currentPage++;
_logger.info("Current Page:" + currentPage);
int indexOnPage = (currentRowIndex % iter.getRangeSize());
_logger.info("Current index on Page:" + indexOnPage);
// get an ADF attributevalue from the ADF page definitions
AttributeBinding attr = (AttributeBinding) bindingContainer.getControlBinding("selectedRow1");
StringBuffer sb = new StringBuffer();
sb.append("row index overall: ");
sb.append(currentRowIndex);
sb.append(" row index on page: ");
sb.append(indexOnPage);
sb.append(" Page: ");
sb.append(currentPage);
attr.setInputValue(sb.toString());
}

To get the index of the selected row in the whole rowset we need the iterator and get the RowSetIterator from it. The rowSetIterator method getCurrentRowIndex() returns the index of the current row (line 5). The current page is calculated by dividing the current index through the range size (line 7). The final information is the index of the selected row on the page which is calculated as the current index modulo the range size (line 10). The rest of the listener build a string out of this information and writes it to a pageDef variable which is referenced in an outputfield on the page.

<af:outputText value="#{bindings.selectedRow1.inputValue}" id="ot8" partialTriggers="b2"/>

Here are some images from the sample application.

The sample application is build using JDev 12.1.3 and uses the HR DB schema. The sample can be downloaded from  Github

The power of calculated fields in ADFbc

Lately I saw a couple of posts on the OTN JDev & ADF forum where users tried to add redundant data into their data model and store it to the DB table. One common use case here is to have the result of a calculation as an attribute of a table.

In general you should be very careful when doing this. This is error prone and will you get into trouble almost every time. If you do add an attribute for such a calculation to a table in the DB, you have to think of the integrity of the data. Let’s look into the use case and the integrity problem.

Use Case

We have a table in the DB which holds start and end for multiple data types like integer, data and timestamp:

Selection_719

We use the different start and end attributes to calculate the difference between start and end.

We do have the option to add attributes to the table and calculate the difference using a trigger in the DB each time the data is inserted or updated. Problem here is that the user will see the result only after the insert or update is done. For web pages this isn’t a good design.

Another option is to add the fields but do the calculation in the business component layer in ADFbc and store them in the DB together with all other changes done to the data. The your see the calculation, but other applications won’t see them until you store the record.

Problem with storing redundant data in a DB table

Both options have one flaw. When you store the result of a calculation in the DB, what happens if someone, person or program, changes one of the attributes used in the calculation?

Assume STARTINT is set to 5, ENDINT is set to 10. The result of the calculation is 5. This result we store in an attribute in the DB table. Now a bad programmer who does not know about the calculation, changes the ENDINT to 15 and commits the change.

When the other program looks at the data again the data is inconsistent. Which of the values is correct? The result? The STARTINT value? The ENDINT value? Or is the calculation simply wrong?

In this simple use case it’s fairly easy to find the problem. In more complex use cases where other workflows depend on the numbers it’s not as easy.

This leads to the solution shown in this post: don’t store results of calculations in the DB if possible. Do the calculation when they are  needed.

There are cases where storing the result would be the better way to archive the whole use case, but this has to be decided on the use case and weighted against the complications. Most simple use cases don’t need to store the results and should not.

The remainder of this post we see how to implement such calculated fields using ADFbc.

Implementing calculated fields in ADFbc using Groovy

We start with creating a new Fusion Web Application and building the ‘ADF Business Components from a Table’. The sql script to create the table is

CREATE TABLE "HR"."CALCULATION"
 ( "ID" NUMBER(*,0) NOT NULL ENABLE,
 "STARTINT" NUMBER(*,0),
 "ENDINT" NUMBER(*,0),
 "STARTTIME" DATE,
 "ENDTIME" DATE,
 "STARTTIMESTAMP" TIMESTAMP (6),
 "ENDTIMESTAMP" TIMESTAMP (6),
 CONSTRAINT "CALCULATION_PK" PRIMARY KEY ("ID")
 );
REM INSERTING into CALCULATION
 SET DEFINE OFF;
 Insert into CALCULATION (ID,STARTINT,ENDINT,STARTTIME,ENDTIME,STARTTIMESTAMP,ENDTIMESTAMP) values ('1','1',null,to_timestamp('24-DEZ-15','DD-MON-RR HH.MI.SSXFF AM'),to_timestamp('26-DEZ-15','DD-MON-RR HH.MI.SSXFF AM'),null,null);
 Insert into CALCULATION (ID,STARTINT,ENDINT,STARTTIME,ENDTIME,STARTTIMESTAMP,ENDTIMESTAMP) values ('2','4','6',to_timestamp('31-DEZ-15','DD-MON-RR HH.MI.SSXFF AM'),to_timestamp('05-JAN-16','DD-MON-RR HH.MI.SSXFF AM'),null,null);

We use the HR DB schema to add the table, but it can be added to any schema you want. The CALCULATION table consists of some start and end values of different types to later show how to work with them. To work with the table we add two records resulting in the following data

Selection_720.jpg

I don’t show the steps to create the basic application from the wizards as the application is available via the link GitHub base application.

Once you downloaded and unzipped the workspace you should see the base application as it will be created by following the wizard.

Selection_721

The first step is to create a transient field in the Calculation EO to hold the result of the calculation of the difference of STARTINT and ENDINT. The difference here  is, that we store the result in the EO as transient attribute which is not stored into the DB.

The real work is shown in the third image above ‘edit expression…’. Here we enter a Groovy expression to calculate the difference between STARTINT and ENDINT as

if (Endint == null) 
  {return 0} 
else 
  {return Endint-Startint}

The Groovy expression uses the attribute names from the EO not the ones from the DB table. First we check if the Endint is given, if not we return 0. If there is an Endint we return the (Endint-Startint).

We then add notifications to the calculated attribute whenever the attributes Startint or Endint change to recalculate the Durationint attribute (lower half of the dialog). Next we set the AutoSubmit  property of the Startint and Endint attributes to true to make sure we get the new values when we calculate the result.

Finally we add the new calculated attribute to the VO. We can now test the application module using the application module tester:

We now add a index page to the View Controller project to add an UI to the application. We can just drag the CalculationView1 and drop is as an ADFForm with navigation and submit onto the page.

In the resulting form we set the Startint and Endint fields to autosubmit=’true’ to make sure the new values are submitted. As the Durationint field isn’t updateble we set it to read only.

Running the application will show you

The application in this state can be downloaded from GitHub (feature/calculated_int_field).

To show that this can be done with other data types we can use the other attributes of the table. As the way to do this is the same I spare to give detailed instructions. You can download the final application from GitHub (final).

All samples yre using the HR DB schema and table called CALCULATION. The needed SQL code to create the table and to insert data to the table is posted in here.

Export to Excel enhancements in JDeveloper 11.1.1.9.0 and JDeveloper 12.1.3

In the current JDeveloper version 11.1.1.9.0 and 12.1.3 the af:exportCollectionActionListener got enhanced by options to filter the data to export.

Enhanced options of exportCollectionListener

Enhanced options of exportCollectionListener


The option this blog talks about is the one marked, the FilterMethod. The ducumentation for 12.12 Exporting Data from Table, Tree, or Tree Table does not reveal too much about how to use this FilterMethod.
The sample we build in this blog entry shows how the FilterMethod can be used to filter the data to be exported to excel.
In older version of JDev you hadto use a trick to filter the data which was downloaded from a table see Validate Data before Export via af:exportCollectionActionListener or af:fileDownloadActionListener. The new property of the af:exportCollectionActionListener allows to filter the data without using the trick.
The sample just load the employees table from the HR DB schema and shows it in a table on the screen. In the toolbar we add a button which has the af:exportCollectionActionListener attached.
Running application

Running application


Below is the page code of the toolbar holdign the export button:

                            &lt;f:facet name=&quot;toolbar&quot;&gt;
                                &lt;af:toolbar id=&quot;t2&quot;&gt;
                                    &lt;af:button text=&quot;Export to Excel&quot; id=&quot;b1&quot;&gt;
                                        &lt;af:exportCollectionActionListener type=&quot;excelHTML&quot; exportedId=&quot;t1&quot; filename=&quot;emp.xsl&quot; title=&quot;Export&quot;
                                                                           filterMethod=&quot;#{ExportToExcelBean.exportCollectionFilter}&quot;/&gt;
                                    &lt;/af:button&gt;
                                &lt;/af:toolbar&gt;

The filterMethod of the af:exportCollectionActionListener points to a bean method exportCollectionFilter in a request scoped bean ExportToExcelBean. The method gets called for each cell of the table which gets exported.

    /**
     * This method gets called for each cell which is to be exported.
     * It can be used to filter data to be exported. In this case salary values &gt; 6000 are not exported
     * @param uIComponent component of the cess which gets to be exported
     * @param exportContext context of the exported data (holds e.g. file name, character set...)
     * @param formatHandler format to be exported
     * @return true if cell value is exported, false if not
     */
    public Boolean exportCollectionFilter(UIComponent uIComponent, ExportContext exportContext, FormatHandler formatHandler) {
        if (exportContext.isFirstInRow()) {
            count++;
            _logger.info(&quot;Start a new Row &quot; + count);
        }
        _logger.info(&quot;Export Collection UIComponent: &quot; + uIComponent.getId());
        if (uIComponent instanceof RichOutputText) {
            RichOutputText rot = (RichOutputText) uIComponent;
            Object val = rot.getValue();
            String headerText = &quot;&quot;;
            UIComponent component = rot.getParent();
            if (component instanceof RichColumn) {
                RichColumn col = (RichColumn) component;
                headerText = col.getHeaderText();
            }
            StringBuilder sb = new StringBuilder();
            sb.append(&quot;Name: &quot;);
            sb.append(headerText);
            sb.append(&quot; Value: &quot;);
            sb.append(val);
            _logger.info(sb.toString());
            // check if the salary is greater than 6000
            if (&quot;Salary&quot;.equals(headerText)) {
                if (((BigDecimal) val).intValue() &gt; 6000) {
                    // if yes return false so that the value isn't exported
                    _logger.info(&quot;Skip Vals &gt; 6000&quot;);
                    return false;
                }
            }
        }

        return true;
    }

The method gets the uiComponent which represents the current cell to be exported, the ExportContext and the FormatHandler for the export. The ExportContext hold information about the filename, title, the used character set and status information about the row and cells currently exported. The status can be used to find out is a new row just starts to be exported ro is a cell is part of a span of cells. In the sample we use this information to print a log message for each row exported.
The FormatHandler is used to generate the document to be exported and the data in it. I did not find a way to use my own handler and there is no documentation about how to use another handler, so we leaf this as is for the moment.
In the sample method we like to filter the employee data in a way, that salaries greater than 6000 are not exported to the resulting file. As the method is called for each cell, the first thing to find out is which cell currently used. In lines 15-29 we use the current UIComponent to find out which column we are in. In lines 31-37 we check the salary column. In case the salary value is greater than 6000 we return false as this will trigger that the cell value is not exported. If the salary is below or equal to 6000 we return true and the cell value is exported.
Below we see the result we get if we export the table without the filterMethod set:

Exported table without filter

Exported table without filter


and the result with the filter method set:
Exported table with filter

Exported table with filter

You can download the sample application which was build using JDeveloper 12.1.3 and the HR DB schema from GitHub.

dvt:treemap showing node detail in popup

This post describes how to implement an dvt:treemap which shows a af:popup when the user clicks on a detail node in the map.
The documentation of the dvt:treemap component tell us that the dvt:treemapnode supports the af:showPopupBehaviortag and reacts on the ‘click’ and ‘mouseHover’ events.
This is part of the solution and allows us to begin implementing the use case. We add an af:showPopupBehavior to the nodes we want to show detail information for.

After creating a default Fusion Web Application which uses the HR DB schema, we begin with creating the data model for the model project. For this small sample the departments and employees tables will be sufficient.


The views are named according to their usage to make it easier to understand the model. This is all we need for the model.

Let’s start with the UI which only consist of a single page. The page has a header part and a center part. In the center area we build the treemap by dragging the Departments from the data controls onto the page and dropping it as treemap. After that, in the dialog we specify the first level of the map to be the departmentId (which shows the department name as the label) and the for the second level we choose the employeeId (which shows the last name of the employee as label) from the employees. The whole process is shown in the gallery below.


The resulting treemap is very basic in it’s features, e.g. there is no legend as you see later.
In the next step we create an af:popup to show the nodes detail information. This process is outlined in the next gallery. We drag the popup component onto the page below the af:treemap component

One thing to take note of are the properties of the popup. First we set the content delivery to ‘lazyUncached’, which makes sure that the data is loaded every time the popup is opened. Otherwise we’ll see only the data from the first time the popup has been opened. Second change is to set the launcherVar to ‘source’. This is the variable name we later use to access the node data. Third change is to set the event context to ‘launcher’. This means that events delivered by the popup and its descendents are delivered in the context of the launch source.

The treemap for example, when an event is delivered ‘in context’ then the data for the node clicked is made ‘current’ before the event listener is called, so if getRowData() is called on the collectionModel in the event listener it will return the data of the node that triggered the event. This is exactly what we need.

Finally we add a popupFetchListener to the popup which we use to get the data from the current node to a variable in the bindings. In the sample this variable ‘nodeInfo’ is defined in the variable iterator of the page and an attribute binding ‘nodeInfo1’ is added. More info on this can be found here.


The code below shows the popupFetchListener:

package de.hahn.blog.treemappopup.view.beans;

import javax.el.ELContext;
import javax.el.ExpressionFactory;

import javax.faces.application.Application;
import javax.faces.context.FacesContext;

import oracle.adf.model.BindingContext;
import oracle.adf.share.logging.ADFLogger;
import oracle.adf.view.rich.event.PopupFetchEvent;

import oracle.binding.AttributeBinding;
import oracle.binding.BindingContainer;


/**
 * Treemap handler bean
 * @author Timo Hahn
 */
public class TreemapBean {
    private static ADFLogger logger = ADFLogger.createADFLogger(TreemapBean.class);

    public TreemapBean() {
    }

    /**
     * listen to popup fetch.
     * @param popupFetchEvent event triggerd the fetch
     */
    public void fetchListener(PopupFetchEvent popupFetchEvent) {
        // retrieve node information 
        String lastName = (String) getValueFromExpression("#{source.currentRowData.lastName}");
        Integer id = (Integer) getValueFromExpression("#{source.currentRowData.EmployeeId}");
        //build info string
        String res = lastName + " id: " + id;
        logger.info("Information: " + res);
        // get the binding container
        BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();

        // get an ADF attributevalue from the ADF page definitions
        AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("nodeInfo1");
        //set the value to it
        attr.setInputValue(res);
    }

    // get a value as object from an expression
    private Object getValueFromExpression(String name) {
        FacesContext facesCtx = FacesContext.getCurrentInstance();
        Application app = facesCtx.getApplication();
        ExpressionFactory elFactory = app.getExpressionFactory();
        ELContext elContext = facesCtx.getELContext();
        Object obj = elFactory.createValueExpression(elContext, name, Object.class).getValue(elContext);
        return obj;
    }
}

Finally we have to design the popup to show the node info from the attribute binding ‘nodeInfo1’. The popup uses a dialog with an af:outputText like

Show the node info in the popup

Show the node info in the popup


and set an af:showPopupBehavior to the node showing the employees

Running the finished application brings up the treemap, not pretty but enough to see this use case working. If we click on an employee node we see the popup with the last name of the employee and the employee id, the primary key of the selected row in the employees iterator.

You can download the sample application which was build using JDeveloper 12.1.3 and the HR DB schema from GitHub.

Initialize an execute af:quickQuery default criteria on page load

Recently a question on the OTN JDeveloper & ADF Space caught my interest. The question was how to initialize an af:quickQuery component with a parameter passed to a task flow on load of a page.
At first I thought that this would be a simple case of setting a property (InitialQueryOverwritten=true) as mentioned by Frank Nimphius in his article How-to query af:quickQuery on page load ?, but after a short test it turned out, that this setting only executes the query but can’t be used to initialize the criteria.

This blog is about a solution to this problem. The question can be divided into two smaller problems to solve. The first is to pass a parameter to a bounded task flow and use the passed parameter in the bounded task flow. The second problem is to initialize a default query attribute of a af:quickQuery component and execute the query.

Let’s have a look at the running application.

Start Page

Start Page


On the start page the user can enter a parameter, which is used as input parameter in the second page, which holds a region (as bounded task flow) with the quick query component. Clicking on the ‘Go Query’ button passes the entered parameter to a pageFlowScope variable. and navigates to the second page.
Start Page Page with initialized af:quickQuery

Start Page Page with initialized af:quickQuery


As we see, the passed parameter is visible in the quick query component and the table shows the corresponding data in the table.

The first problem mentioned isn’t really one as the solution the well documented. So passing a parameter from an af:inputText to a bounded task flow will only showed briefly here. The button on the start page uses a af:setPropertyListener to set the parameter to a pageFlowScope variable. On the second page the parameter is passed as input parameter to the bounded task flow which assembles the af:quickQuery.


The images above showing the navigation between the two pages and the region (QuickQuery.jsf) which holds the af:quickQuery.

First Try
The first method I tried to initialize the af:quickQuery was to overwrite the QueryListener of the af:quickQuery component to set the parameter to the default search attribute. The already mentioned property InitialQueryOverwritten=true would then execute the query with the parameter set. This should show the right result in the table. As it turned out, if the property InitialQueryOverwritten is set to true, the QueryListener is not called on load of the page. No change to set the parameter which is passed to the bounded task flow.

Second Try
For the next try I used a method activity in the bounded task flow and tried to set the parameter from this method. This will not work as the component is not present when the method is called as default activity in the task flow. You can set the parameter to the view object and filter the data after it, however, the overwritten property InitialQueryOverwritten then executed the default query again, this time without the parameter. If you set the property to false, you see the data, but the parameter is not set in the af:inputText component.

Final Try: Working solution
The working solution uses a trick which is kind of lazy initializing the component. For this we bind a property of the component to a bean and overwrite the getter method for the property. In the getter we check a private variable of the bean if the component has been called already or not. In case the getter has already been called we just return the value for the property. In case the getter method is called the first time we initialize the component before returning the value of the property.

Let’s look at the af:quickQuery in the region:

                        <af:quickQuery label="Search" searchDesc="#{viewScope.QuickQueryBean.dummy}" id="qryId1"
                                       value="#{bindings.ImplicitViewCriteriaQuery.quickQueryDescriptor}"
                                       model="#{bindings.ImplicitViewCriteriaQuery.queryModel}"
                                       queryListener="#{bindings.ImplicitViewCriteriaQuery.processQuery}" binding="#{viewScope.QuickQueryBean.quickQuery}">
                            <f:facet name="end">
                                <af:commandLink text="Advanced" rendered="false" id="cl1"/>
                            </f:facet>
                        </af:quickQuery>

Two things to note are
1. the component is bound to the viewScope bean QuickQueryBean
2. the searchDesc property is bound to the same QuickQueryBean bean
The component is bound to the bean as a convenience to get the query descriptor easily in the initialization method. To make this save we use a ComponentReference to store the component.

    private ComponentReference quickQuery;
    /**
     * setter for component to ComponentReference
     * @param quickQuery the component
     */
    public void setQuickQuery(RichQuickQuery quickQuery) {
        this.quickQuery = ComponentReference.newUIComponentReference(quickQuery);
    }

    /**
     * getter for the component from the component reference
     * @return
     */
    public RichQuickQuery getQuickQuery() {
        if (quickQuery != null) {
            return (RichQuickQuery) quickQuery.getComponent();
        }
        return null;
    }

For more information about this technique see Rules and Best Practices for JSF Component Binding in ADF

The lazy initialization is done by binding the searchDesc property to the QuickQueryBean. The trick is that the component has to call the getter for this property to get it’s value. In the getter in the bean

    /**
     * getter for a string value names dummy in EL
     * @return value of the dummy property
     */
    public String getDummy() {
        if (needInit) {
            needInit = false;
            initQuickQuery();
        }
        return "Search";
    }

we check a local variable ‘needInit’ which is set to true when the bean is created each time the page gets loaded. As the bean is in viewScope it guarantees that the bean is created each time the page is loaded and stays active until the page is visible.
The real work is done in the initQuickQuery() method:

    /**
     * Initialize the quickQuery component if a parameter tpCityName is found in the pageFlowScope. Once this is done, the pageFlowScope
     * variable tpCityName is set to null or removed.
     */
    public void initQuickQuery() {
        // get the PageFlowScope Params
        AdfFacesContext adfFacesCtx = AdfFacesContext.getCurrentInstance();
        Map<String, Object> scopePageFlowScopeVar = adfFacesCtx.getPageFlowScope();
        String paramCity = (String) scopePageFlowScopeVar.get("tpCityName");
        if (paramCity != null && !paramCity.isEmpty()) {
            // get query descriptor (the components value property)
            FilterableQueryDescriptor queryDescriptor = (FilterableQueryDescriptor) getQuickQuery().getValue();
            // get the current selected criterion (which should set in the ImplicitViewCriteriaQuery in hte pageDef
            AttributeCriterion attributeCriterion = queryDescriptor.getCurrentCriterion();
            // get the attribute name and check if it'S 'City'
            AttributeDescriptor attribute = attributeCriterion.getAttribute();
            String name = attribute.getName();
            // only set parameter if hte attribute matches the parameter
            if ("City".equalsIgnoreCase(name)) {
                attributeCriterion.setValue(paramCity);
                // remove value to allow new one in component
                scopePageFlowScopeVar.put("tpCityName", null);
                // set the parameter to the attributeCriterion 
                QueryModel model = getQuickQuery().getModel();
                model.setCurrentDescriptor(queryDescriptor);
                // create a queryEvent and invoke it
                QueryEvent qe = new QueryEvent(getQuickQuery(), queryDescriptor);
                invokeMethodExpression("#{bindings.ImplicitViewCriteriaQuery.processQuery}", Object.class, QueryEvent.class, qe);
            }
        }
    }

In this method we check if a parameter named ‘tpCityName’ is present in the pageFlowScope (lines 8-10). If yes the next check is if the current selected criterion the for the selected parameter, in this case the ‘City’ (lines 11-19) . Only if this test is positive the value from the parameter is set to the criterion (line 20), the pageFlowScope variable ‘tpCityName’ is removed and the new criterion is set back to the query model (lines 21-25). Finally to execute the af:quickQuery we create a new QueryEvent and invoke it via an EL (lines 26 -28).
The solution does not need to set the InitialQueryOverwritten property to true to run. The query is fired after setting the attribute via the QueryEvent. Here is an image of the af:quickQuery binding

Definition of the ImpliciteViewCriteriaQuery

Definition of the ImpliciteViewCriteriaQuery

The sample needs the HR DB schema. You can download the code for the sample, which was build using JDeveloper 12.1.3, from GitHub. Please note that if you run the sample in your environment, that you have to change the DB connection to the HR DB schema according to your environment.

How-to filter ADF bound tables by date range (JDeveloper 12.1.x)

Based on an older article from Frank Nimphius How-to filter ADF bound tables by date range JDeveloper 11.1.1.4 I got a interesting question on the OTN JDeveloper & ADF forum why the solution provided in the article does not work in JDev 12c.

The solution from Frank’s article is designed for JDev 11.1.1.4.0. Today’s version of JDev is 12.1.3 where the solution does not seem to work. Migrating the source of the article and running it under JDev 12.1.3 indeed shows, that filtering the employees records for a date range does not work at all. Setting dates into the filter and hitting enter to activate the filter does not filter the data in the table.
The reason for this was easily found by debugging the code. Set a breakpoint into the query listener which is setup in the table

<af:table value="#{bindings.allEmployees.collectionModel}" var="row" 
  rows="#{bindings.allEmployees.rangeSize}"
  emptyText="#{bindings.allEmployees.viewable ? 'No data to display.' : 'Access Denied.'}"
  fetchSize="#{bindings.allEmployees.rangeSize}" rowBandingInterval="0"
  filterModel="#{bindings.allEmployeesQuery.queryDescriptor}" filterVisible="true" 
  varStatus="vs" selectedRowKeys="#{bindings.allEmployees.collectionModel.selectedRow}"
  selectionListener="#{bindings.allEmployees.collectionModel.makeCurrent}" 
  rowSelection="single" id="t1" styleClass="AFStretchWidth"  partialTriggers="::cb1"
  queryListener="#{EmployeeQueryBean.onEmployeeQuery}">

As you can see it’s pointing to a bean method ‘onEmplyoeeQuery’. A look into this method reveals that the method FilterableQueryDescriptor.getFilterCriteria() has been deprecated.

        FilterableQueryDescriptor fqd = (FilterableQueryDescriptor) queryEvent.getDescriptor();
        Map map = fqd.getFilterCriteria();

Instead of the deprecated method you should use the method FilterableQueryDescriptor.getFilterConjunctionCriterion() which now holds the map of parameters.

        FilterableQueryDescriptor fqd = (FilterableQueryDescriptor) queryEvent.getDescriptor();
        ConjunctionCriterion cc = fqd.getFilterConjunctionCriterion();
        Map<String, Criterion> criterionMap = cc.getCriterionMap();

When you set a breakpoint in this method and step through the code you see that the values entered into the filter fields in the UI are not visible in the map as Frank describes in his article.

Criterion Map and old FilterCriteria Map

Criterion Map and old FilterCriteria Map


As you can see there are no map entries for the made up variables ‘HireStartRange’ and ‘HireEndRange’. This is the reason the filter by date range does not work. There are simply not dates to filter the rows.

I’m not sure if this is a bug or a change in behavior which was made for a reason. Anyway, you can’t just simply add values to the map anymore.

The solution to fix the problem is simple. As you can’t store additional values in the criterion map, you have to store the values entered by the user somewhere else. A valid storage area is the variables iterator each pagedef holds.
In one of my other blogs Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef I showed how to add temporary variables in this iterator.

Create two new variables inside the variable iterator of type oracle.jbo.domain.Date, name them ‘startDate’ and ‘endDate’. Then create attribute bindings for them.
The final touch is to wire the new variables up in the HireDate filter for start range and end range:

                                    <af:column sortProperty="HireDate" filterable="true" sortable="true"
                                               headerText="#{bindings.allEmployees.hints.HireDate.label}" id="c1" width="277">
                                        <f:facet name="filter">
                                            <af:panelGroupLayout id="pgl2" layout="horizontal">
                                                <af:panelLabelAndMessage label="From: " id="plam1">
                                                    <af:inputDate id="id2" value="#{bindings.startDate1.inputValue}" clientComponent="false">
                                                        <af:convertDateTime pattern="#{bindings.allEmployees.hints.HireDate.format}"/>
                                                        <f:validator binding="#{bindings.HireDate.validator}"/>
                                                    </af:inputDate>
                                                </af:panelLabelAndMessage>
                                                <af:spacer width="5" height="5" id="s1"/>
                                                <af:panelLabelAndMessage label="To:" id="plam2">
                                                    <af:inputDate id="id3" value="#{bindings.endDate1.inputValue}" required="false" clientComponent="false">
                                                        <f:validator binding="#{bindings.HireDate.validator}"/>
                                                        <af:convertDateTime pattern="#{bindings.allEmployees.hints.HireDate.format}"/>
                                                    </af:inputDate>
                                                </af:panelLabelAndMessage>
                                            </af:panelGroupLayout>
                                        </f:facet>
                                        <af:inputDate value="#{row.bindings.HireDate.inputValue}" label="#{bindings.allEmployees.hints.HireDate.label}"
                                                      required="#{bindings.allEmployees.hints.HireDate.mandatory}"
                                                      shortDesc="#{bindings.allEmployees.hints.HireDate.tooltip}" id="id1" styleClass="AFStretchWidth">
                                            <f:validator binding="#{row.bindings.HireDate.validator}"/>
                                            <af:convertDateTime pattern="#{bindings.allEmployees.hints.HireDate.format}"/>
                                        </af:inputDate>
                                    </af:column>

The code above shows the new column for the HireDate and the new storage location for the startDateRange as ‘value=”#{bindings.startDate1.inputValue}”‘ and EndDateRange as ‘value=”#{bindings.endDate1.inputValue}”‘. Next we change the bean method which reads the filter values and calls the query:

    public void onEmployeeQuery(QueryEvent queryEvent) {
        //default EL string created when dragging the table
        //to the JSF page
        //#{bindings.allEmployeesQuery.processQuery}

        BindingContext bctx = BindingContext.getCurrent();
        DCBindingContainer bindings = (DCBindingContainer) bctx.getCurrentBindingsEntry();

        //access the method bindings to set the bind variables on the ViewCriteria
        OperationBinding rangeStartOperationBinding = bindings.getOperationBinding("setHireDateRangeStart");
        OperationBinding rangeEndOperationBinding = bindings.getOperationBinding("setHireDateRangeEnd");

        // get the start date and end date from the temporary valiables
        AttributeBinding attr = (AttributeBinding) bindings.getControlBinding("startDate1");
        oracle.jbo.domain.Date sd = (oracle.jbo.domain.Date) attr.getInputValue();
        attr = (AttributeBinding) bindings.getControlBinding("endDate1");
        oracle.jbo.domain.Date ed = (oracle.jbo.domain.Date) attr.getInputValue();

        //set the start and end date of the range to search
        rangeStartOperationBinding.getParamsMap().put("value", sd);
        rangeEndOperationBinding.getParamsMap().put("value", ed);

        //set bind variable on the business service
        rangeStartOperationBinding.execute();
        rangeEndOperationBinding.execute();

        invokeMethodExpression("#{bindings.allEmployeesQuery.processQuery}", Object.class, QueryEvent.class, queryEvent);
    }

In line 14-17 you see that we read the values from the newly created attribute bindings for the temporary variables. After removing the unnecessary parts of the code, which tried to read the values from the map, the rest of the code remains as is.

Here is an image of the now working filter by date range

Filter Table by Date Range

Filter Table by Date Range

Please note that if you run the sample in your environment, that you have to change the DB connection to the HR DB schema according to your environment. You can download the changed code for the sample from GitHub