Show Comma Separated String as Detailstamp in Table

In this blog I’ll show how to implement a very specific use case which was asked on the Developer Community (the renamed OTN).

Use case

A table has a comma-separated list of values which are FK for another data store which might be from a different table or WebService. So the list of value can’t directly be related to the master table as an association or view link.

Here is a sample look of such a table or view

The resulting table in the UI should show this data as table and in the detailStamp facet show the data for each number in the list like

Implementation prerequisites

The use case is implemented using JDeveloper 11.1.1.7.0 and uses the HR DB schema.

To make it easy we use the normal HR DB and create a view to show the department together with a comma-separated list of all employeeId, EmpList in the above table. For this we use the SQL code:

CREATE OR REPLACE FORCE VIEW "HR"."DEPT_EMPS" ("DEPT_ID", "DEPARTMENT_NAME", "MANAGER_ID", "LOCATION_ID", "EMP_LIST") AS 
 SELECT DEPT.DEPARTMENT_ID,
 DEPT.DEPARTMENT_NAME,
 DEPT.MANAGER_ID,
 DEPT.LOCATION_ID,
 cclist.EMP_LIST
 FROM DEPARTMENTS DEPT,
 
 ( SELECT EMP_LIST.DEPARTMENT_ID,
 LTRIM (
 MAX (SYS_CONNECT_BY_PATH (EMPLOYEE_ID, ', '))
 KEEP (DENSE_RANK LAST ORDER BY curr),
 ', ')
 AS EMP_LIST
 FROM (SELECT ccr.DEPARTMENT_ID,
 ccs.EMPLOYEE_ID EMPLOYEE_ID,
 ROW_NUMBER ()
 OVER (PARTITION BY ccr.DEPARTMENT_ID
 ORDER BY ccs.EMPLOYEE_ID)
 AS curr,
 ROW_NUMBER ()
 OVER (PARTITION BY ccr.DEPARTMENT_ID
 ORDER BY ccs.EMPLOYEE_ID)
 - 1
 AS prev
 FROM EMPLOYEES ccs, DEPARTMENTS ccr
 WHERE ccs.DEPARTMENT_ID = ccr.DEPARTMENT_ID) EMP_LIST
 GROUP BY EMP_LIST.DEPARTMENT_ID
 CONNECT BY prev = PRIOR curr
 AND EMP_LIST.DEPARTMENT_ID = PRIOR EMP_LIST.DEPARTMENT_ID
 START WITH curr = 1
 ORDER BY EMP_LIST.DEPARTMENT_ID) cclist
 WHERE DEPT.DEPARTMENT_ID = CCLIST.DEPARTMENT_ID;

And the data store for the employees is created as a DB view too:

 CREATE OR REPLACE FORCE VIEW "HR"."EMP_LOOKUP_VW" ("EMPLOYEE_ID", "NAME", "EMP_ID_CH") AS 
 SELECT emp.EMPLOYEE_ID,
 emp.FIRST_NAME || emp.LAST_NAME,
 TO_CHAR(emp.EMPLOYEE_ID)
 
 FROM EMPLOYEES emp;

There is no association or viewlink between the two views. This data model came together with the original test case I used to implement the use case.

Implementation

The implementation starts with generating a normal ADF Web Application. Details on how to do this you find at Why and how to write reproducible test cases.

In the model layer we add EO/VO for the two views we generated to end with the following data model

And the model project

How to implement the use case

Before we start to make changes, let us think about the solution. The problem is that we have a string with a list of values which we want to show as single elements in the detail stamp of a table. We need to split the string into pieces (the employeeId) and iterate over them in the detail stamp of the table.

To iterator over a data collection, we can use a CollectionModel or an ArrayList which JDev converts internally into a CollectionModel.

In this solution, we are adding the array if employee Ids to the DeptEmpsEO as a transient attribute. This will make the array available for each row as part of the row itself.

Step 1

We add a transient attribute to the DeptEmpsEO like

null

We set the type as Object as JDev 11.1.1.9 can handle SQL Array types only. In our case, we build an ArrayList from the string.

Next, we create the Java implementation class for the EO

In the generated class we exchange the getEmpArray() method with

 

The method gets the comma-separated list and splits it into an employeeId and adds them into an ArrayList of Numbers (oracle.jbo.domain.Number). The resulting arrayList is returned.

As we have added a new attribute to the EO we have to add this attribute to the VO too:

Step 2

As we later only have employee IDs, we need a method to look up more data for the employee. Remember, the data might be from different sources so there is no association or viewlink available we can use like in normal ADFbc applications. We have to build a lookup view for this (or is the data is from a WebService we use this WebService).

We use the second DB view build at the beginning from which we created the EO EmpLookupVw and the VO EmpLookupVwView.

The data this VO presents is very simple as it’s just the employeeId, the first name, and the last name.

In this VO we create a view criteria to lookup a single employee by its Id like

Which uses a bind variable of Integer type

To make this available in the DataControl, we add the VO as special Vo to the ApplicationModule

 

 

 

Now the VO named EmpLookupById always add the viewCriteria when executed. All we have to do is to add the integer parameter for the employeeID we are looking for. In the DataControl we can use the ‘ExecuteWithParam’ method from the VO and set the parameter

Step 3: ViewController

The test case comes with some pages in the ViewController project. However, we use a fresh jspx page to show the solution. We create a new page by dragging a ‘View’ component onto the adfc-config.xml (the main unbounded task flow) and create the file by double-clicking it. In the dialog, we select a ‘Quick Layout’

 

Once the page shows up in the design view we add an af:outputText for the caption and drag the DeptEmpsVO1 view object from the data control onto the center facet of the layout and select to create an ‘ADF Read-only Table’. We show all attributes (if you like you can deselect the EmpArray attribute).

This is the base of the work. Now we enable the ‘Detail Stamp’ facet of the table. In this facet we want to iterate over the comma-separated list of employees.

We add an af:panelGroupLayout into the facet with vertical layout. Inside the panelGroupLayout we add an af:iterator which we use to iterate over the array of employeeId we created in the row as attribute EmpArray. ADF is intelligent and converts the array into a collection. This collection can be used as other collection. We define a name for the variable we can use for each single element of the collection like ‘emp’. Using an af:outputText we can print out the value of the current element of the collection like

<af:iterator id="i1" value="#{row.EmpArray}" var="emp">
                    <af:panelGroupLayout id="pgl2" layout="horizontal">
                      <af:outputText value="- #{emp} -”/>
…

What’s missing is that we like to get more information than just the Id of the employee. So we add another EL to the af:outputText which points to a bean method which will retrieve this additional information. The final facet looks like

  <f:facet name="detailStamp">
 <af:panelGroupLayout id="pgl1" layout="vertical">
 <af:iterator id="i1" value="#{row.EmpArray}" var="emp">
 <af:panelGroupLayout id="pgl2" layout="horizontal">
 <af:outputText value="- #{emp} - #{viewScope.EmpBean.empNameById}" id="ot8"/>
 </af:panelGroupLayout>
 </af:iterator>
 </af:panelGroupLayout>
 </f:facet>

Or as an image:

Finally, we have to create the bean with the method to retrieve the additional data. We create a Java class in the view folder

 

 

 

End add this bean class as managed bean to the adfc-config.xml

Before we go into writing the method to retrieve the data, we have to add the EmpLooupById.ExecuteWithParams method to the pageDef to make it available in the page. The easiest way to do this is to open the EmpLookupById Vo in the Data Control and open the ‘Operations’ node. Select the ExecuteWithParams method and drag it onto the page. We can drop it anywhere as ‘ADF Button’

 

 

 

In the final dialog, we add the value of the parameter as ‘#{emp}’ which is the EL to access the current employeeId when we iterate the array.

This will create the needed pageDef entries. As we don’t need the button, we switch to source mode and delete the button from the page. This will keep the pageDef entries.

Now we open the bean class and write the method to get the additional data for an employee as

Lines 25-38 we get the method binding from the pageDef.

Line 39 retrieves the current value of the employeeId by evaluating the EL ‘#{emp}’ by calling

Lines 40-42 set the value (the employeeId) as parameter to the methods bindId parameter

Lines 43-48 execute the method and check for any errors. If there is an error, the stack trace is printed into the log and the method return “Not found!”.

Lines 50-56 if the call the ExecuteWithParams method returns without an error, the current row of the EmpLookupById VO points to the employee we are looking for. We get the iterator from the pagedef and from the currentRow we read the ‘Name’ attribute. The value of this attribute we return and it will be printed in the af:outputText in the page.

Running the page now will produce

You can download the test case from GitHub BlogCommaSeparatedListDetail. It uses the HR DB schema with some additional DB views created. The scripts are part of the workspace. The application is developed using JDev 11.1.1.7.0.

Advertisements

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

Using one ViewObject for Global Lookup Data (Part 3): Multi Language Support

In an earlier post I blogged about a how to use a DB table to hold look up data like gender, weekdays or title which can used in different locations as ‘List Of Value’ data (Using one ViewObject for Global Lookup Data (Part 2)).
This third part adds a use case where we add multi language support when we read data from the DB table. This is an enhancement of the use case implemented in part 2. The old use case could load data in one language only. Now we add the the language to the data in the db table to allow retrieval of language specific data.

To make the use case work, we add two columns to the existing table GENERALLOOKUP. The first one holds the language code and the second one an id which is unique in the type and language. We use this new id named ‘GROUPID’ in the LOV to show the data of the selected type.
The ‘GROUPID’ remains identical for each type and language so that we can enter data in different languages. For the language code we use the codes we get from a Locale class. A sample for the data of WEEKDAY Monday:

Sample for Multilanguage Entry

Sample for Multilanguage Entry

We start by changing the DB table GENERALLOOKUP we used in the sample introduced in part 2. The sql script setup_db_multilangual.sql which is part of the project workspace, adds the two mentioned columns. You find the link to download the workspace at the end of this post. The script holds the needed data for the multi language look up too.
The final DDL for the GENERALLOOKUP table is

  CREATE TABLE "GENERALLOOKUP" 
   (	
    "ID" NUMBER, 
	"TYPE" VARCHAR2(20 CHAR), 
	"DATA" VARCHAR2(255 CHAR), 
	"LANGUAGE" VARCHAR2(5 CHAR), 
	"GROUPID" NUMBER
   ) ;

Next we synchronize the existing EO with the new GENERALLOOKUP table to get the new attributes into the EO

Synchronize with DB

Synchronize with DB


Here are some of the dialogs which you see when synchronizing the DB to the business objects

Now we have to add the new columns to the VO as well

These changes now allow to use the VO GeneralLookup to select language dependent look up data from the db. We now a new view criteria, named TypeLookupByLanguageViewCriteria, to use the language information to only select data for one language and one type from the table.

This new view Criteria uses two bind variables to select only data of one type and one language. How to set these variables we see later in this post.
As we don’t want to break the old application we create a new VO based again on the existing Lookuptest entity object and name it ‘LookupMultiLanguageView’

Next step is to set up the LOVs accessors for the attributes WeekdayId, GenderIs, PositionId and TitleID. Here we only show how to do this for the WeekdayId attribute. The images below showing that we not only set the bindType to ‘WEEKDAY’, but the bindLanguage variable too. Here we use a groovy expression to get the current language from the current locale

The real work is done in pictures 3, 4, 5 and 6 where we use the view criteria we’ve build earlier (TypeLookupByLanguageViewCriteria) to select the type and language from the GeneralLookupView.
In Image 3 we use the GenerallookupView as view accessor for the weekdayId. We rename the accessor to WeekdayMultiLanguageLOV for better understanding what the view accessor does. As the WeekdayMultiLanguageLOV can select any type in any language, we have to use the view criteria and set its bind variables to only get the data we want. In this case we set the bindType variable to ‘WEEKDAY’ and the bindLanguage variable to the current language used in the browser. As this language can change, we can’t use a static string like we used for the bindType. The language has to be calculated. For this we use a groovy expression:

oracle.adf.share.logging.ADFLogger LOGGER = oracle.adf.share.logging.ADFLogger.createADFLogger(source.getClass()); 
loc = adf.context.locale; 
if (loc == null) {
  LOGGER.info("Language not set -> en");
  return 'en';
} else {
  lang = loc.language;
  shortLang = lang.substring(0,2);
  if (!"#de#it#fr#en#".contains(shortLang)) {
    LOGGER.info("Language not recognized -> en");
    shortLang="en";
  } else {
    LOGGER.info("Language set to -> "+lang.substring(0,2));
    shortLang = lang.substring(0,2);
  } 
}

Yes, you can do more with groovy then just simple calculations like “sal *12”!
We use groovy expression like a java function to get the locale from the AdfContext (groovy: loc = adf.context.locale;) and from the locale we get the language (groovy: lang = loc.language;). Now, there are some checks to make, like is the locale is set and if the language found is one of the languages we support. In this sample we only support ‘en’, ‘de’, ‘it’ and ‘fr’ but you can add other languages too. As the language we get from the locale can look like ‘en_US’ or ‘de_CH’ we only use the first two characters (groovy: shortLang = lang.substring(0,2);). You can even use a logger to print out information into the log.

We can now test the switching of languages in the application module tester. Before we start the tester we make sure we can change the locale in the tester. Open the menu ‘Tools’->’Preferences’ and select the node ‘Business Components’, then select the ‘Tester’ node where you can add different languages for the tester.

Supply Languages to Tester

Supply Languages to Tester


Running the tester

Please notice the log output which is visible below the tester which shows the groovy log messages.

Finally we adjust the UI by adding a new page MultiLanguageLookup and hook it up with the existing LookupTest page.

Setup New Test Page

Setup New Test Page


Then we need to setup the faces-config.xml to support multiple languages
Setup New Test Page

Setup New Test Page


Now, if we run the application and change the browser language, reload the page we see the language change

The sample used in this blog can be downloaded from the ADF-EMG Sample repository. The sample uses JDeveloper 11.1.1.7.0 and the HR DB schema.

JDev and IE11 Patches are finally available for all ‘current’ versions

Aside

[UPDATE 2014-03-07] added correct patch number for JDev 11.1.2.4.0
————————————————————————————————-

Since February 25th 2014 the missing patch for JDev 12.1.2.0.0 to make ADF application work with IE11 has arrived. In OTN forum thread https://community.oracle.com/thread/2624119 you find the details of the delay.
The patch for 12.1.2.0.0 has Id 18091042
The patches for the other versions are mentioned in doc ID 1599898.1. The document itself is a bit outdated as the patch for JDev 12.1.2 is missing in the doc.
Here is the summary of the available patches:

    JDeveloper/ADF 11.1.1.6.0
    patch 18277457 – MERGE REQUEST ON TOP OF 11.1.1.6.0 FOR BUGS 17236592 18176711
    patch 17236592 – IE11 SUPPORT: ADF APPS DON’T LOAD WITH IE11 Superseded by patch 18277457
    JDeveloper/ADF 11.1.1.7.0
    patch 18277370 – MERGE REQUEST ON TOP OF 11.1.1.7.0 FOR BUGS 18071063 18176711
    patch 18071063 – MERGE REQUEST ON TOP OF 11.1.1.7.0 FOR BUGS 17236592 17672146 17723555 17723794 Superceded by patch 18277370
    patch 17236592 – IE11 SUPPORT: ADF APPS DON’T LOAD WITH IE11 Superceded by patch 18071063
    JDeveloper/ADF 11.1.2.3.0
    patch 18436342 – MERGE REQUEST ON TOP OF 11.1.2.3.0 FOR BUGS 17236592 17672146 17723555 17723794
    JDeveloper/ADF 11.1.2.4.0
    Patch 18277436: MERGE REQUEST ON TOP OF 11.1.2.4.0 FOR BUGS 18070879 18176711
    JDeveloper/ADF 12.1.2.0.0
    patch 18091042 – MERGE REQUEST ON TOP OF 12.1.2.0.0 FOR BUGS 17236592 17663878 17672146 17723555

JDeveloper: Showing a Popup when Selecting an af:selectOneRadio

A question on the new OTN JDeveloper and ADF forum (or space if you like the new name better) inspired this post.

Use Case
The use case is to show a popup each time the user clicks on one of the radiobuttons of a button group. This popup should show an inputText component to let the user enter some text for the selection. After the selection the text is shown on the page under the button group.
To make it more interesting, it was not ask for this in the question, the popup should show which radio button was clicked.
UPDATE
An additional question on the OTN forum asks how to display the label of the selected radiobutton and not it’s value. I updated the workspace to show how to implement this.

Implementation UPDATE
The use case sounds easy first: add a showPopupBehavior to the selectItem to show the popup and show the selected radioValue as the title of the dialog inside the popup. Problem with this solution is that a selectItem can’t handle client listeners, which a showPopupBehavior is under the hood. If you try this you get an error

Caused by: javax.servlet.jsp.JspException: ADF_FACES-60023:Component: RichSelectItem[UIXFacesBeanImpl, id=si10] does not support client listeners.

Putting the showPopupBehavior onto the af:selectOneRadio isn’t a solution either as this would would allow to show the popup, but would prevent the selection of the radio button. This is because the showPopupBehavior is a client behavior tag which prevents the event to
go further.

The solution to the problem is to use a valueChangeListener on the af:selectOneRadio and show the popup from the bean code. Before showing the popup we need to process the value change event to get the selected value in the dialog of the popup.

The UI part looks like

        <af:panelGroupLayout id="pgl2">
          <af:selectOneRadio label="Activation" id="sor1" value="#{bindings.SelectedActivation1.inputValue}"
                             inlineStyle="border-width:thin; border-color:Blue; border-style:solid;"
                             valueChangeListener="#{RadiobuttonPopupBean.activationChangedListener}" autoSubmit="true">
            <af:selectItem label="Active" value="active" id="si1"/>
            <af:selectItem label="Inactive" value="not active" id="si2"/>
          </af:selectOneRadio>
          <af:outputText value="Last reason: #{bindings.PopupText1.inputValue}" id="ot1" partialTriggers="d1"/>
        </af:panelGroupLayout>
        <af:popup id="p1" contentDelivery="lazyUncached" binding="#{RadiobuttonPopupBean.radioPopup}">
          <af:dialog id="d1" title="Selected: #{bindings.SelectedActivation1.inputValue}">
            <af:inputText label="Reason" id="it1" value="#{bindings.PopupText1.inputValue}"/>
          </af:dialog>
        </af:popup>

The listener code in the bean, which is created in request scope as it contains a reference to a ui component, look like

import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;

import oracle.adf.view.rich.component.rich.RichPopup;

import org.apache.myfaces.trinidad.util.ComponentReference;

public class RadiobuttonPopupBean {
    private ComponentReference radioPopup;

    public void setRadioPopup(RichPopup radioPopup) {
        this.radioPopup = ComponentReference.newUIComponentReference(radioPopup);
    }

    public RichPopup getRadioPopup() {
        if (radioPopup != null)
            return (RichPopup)radioPopup.getComponent();

        return null;
    }

    public void activationChangedListener(ValueChangeEvent valueChangeEvent) {
        // process updates to get the selected value inside the popup
        FacesContext contxt = FacesContext.getCurrentInstance();
        valueChangeEvent.getComponent().processUpdates(contxt);
        //show the popup
        RichPopup.PopupHints hint = new  RichPopup.PopupHints();
        getRadioPopup().show(hint);
    }
}

The application running look like


You see that the popup shows the value of the selected radio button as the title and the entered text in the popup is shown as ‘last reason’ below the radio button group. This ‘magic’ is done by simply adding a partial trigger to the outputText component which is listening to the dialog inside the popup.
As the sample don’t store the value of the selection of the radio group or the entered text in the db, I use the variable iterator to store these values. More about this technique can be found in my blog

UPDATED IMPLEMENTATION
As the use case changed a bit I changes the solution in a way to show how to implement this. First step is that I added another attribute to the variables iterator to store the selected label. The attribute is name ‘SelectedActivation1’ and is of type String. This attribute is needed as it’s not easyly possible to get the selected label of the radiobutton outside the selection listener. The changed valueChangeListener looks like the code below:

    public void activationChangedListener(ValueChangeEvent valueChangeEvent) {
        // process updates to get the selected value inside the popup
        FacesContext contxt = FacesContext.getCurrentInstance();
        valueChangeEvent.getComponent().processUpdates(contxt);
        //get the selected lable from the radio button
        // for this we need to iterate over the children of hte af:selectOneRadio
        // and find the child which has the same value as the new value
        RichSelectOneRadio rsoc =
            (RichSelectOneRadio)valueChangeEvent.getSource();
        List childList = rsoc.getChildren();
        String newVal = (String)valueChangeEvent.getNewValue();
        for (int i = 0; i < childList.size(); i++) {
            if (childList.get(i) instanceof RichSelectItem) {
                RichSelectItem csi = (RichSelectItem)childList.get(i);
                if (((String)csi.getValue()).equals(newVal)) {
                    // get the binding container
                    BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();

                    // get an ADF attributevalue from the ADF page definitions
                    AttributeBinding attr = (AttributeBinding)bindings.getControlBinding("SelectedLabel1");
                    // and store the label there
                    attr.setInputValue(csi.getLabel());
                }
            }
        }
        //show the popup
        RichPopup.PopupHints hint = new RichPopup.PopupHints();
        getRadioPopup().show(hint);
    }

The images below showing the changes application

You can download the workspace from the ADF EMG Sample Project BlogTestRadiobutton.zip. The sample uses the HR DB schema (even as this is not really needed for the sample).

JDeveloper: Navigation after Return from Bounded Task Flow

A question on the OTN JDeveloper and ADF ‘Space’ asked for a sample and/or tutorial on how to navigate after a bounded task flow, based on pages, returns from its work.
I setup a sample application to show how this works. The application is built using JDeveloper 11.1.1.7.0 and uses the HR DB schema. The sample can be loaded using the link provided at the end of the post.
Before we start let’s take a look at the running application:


Now that we have seen how the application works let’s check out how it’s implemented.

We start with an unbounded task flow (adfc-config.xml) which look like

Unbounded Task Flow: adfc-config.xml

Unbounded Task Flow: adfc-config.xml


We see that the application is built from two pages, a ‘Start’ page and an ‘End’ page. The ‘Start’ page can call a bounded task flow employee-btf. The start page holds one button ‘Start’ which calls the bounded task flow which is shown below.

Bounded Task Flow: employee-btf.xml

Bounded Task Flow: employee-btf.xml

This task flow shows a table of employees and allows to navigate to a detail page where the employee data can be changed. Depending on the result of the change, we navigate back to the start page (when the rollback button is clicked) or we navigate to the ‘end’ page if the changes are committed (using the commit button). The buttons are calling the navigation case named ‘start’ or ‘end’ which are task flow return cases which are looking like

Task Flow Return: Start

Task Flow Return: Start

or

Task Flow Return: End

Task Flow Return: End

As you see the return for ‘start’ calls a parent navigation ‘start’ which is implemented using a ‘wild card’ navigation ‘*’. Same is true for ‘end’ task flow return call. Once the navigation is given back to the parent task flow, it looks for a navigation with the name of the outcome ‘start’ or ‘end’ and executes the navigation.

The sample can be loaded from the ADF EMG Samples workspace BlogUBTBTFNavigation.zip

Case Study: Backporting a JDeveloper Project from11gR2 to 11gR1

I got a couple of queries to backport my file and image handling sample, which was built using JDeveloper 11.1.2.x version, to JDeveloper 11.1.1.x version. This I have done and like to share the steps on how to do this.

Here are the general steps:

  1. copy the workspace from 11gR2 into a fresh folder which you should onöy access with 11gR1. It’s essential to don’t open one workspace with different workspaces as this might change descriptors and or files.
  2. open the ‘.jsw’ file in 11gR1. If you get a message if you want to migrate answer ‘yes’
  3. recompile the model project.
  4. make the necessary changes to the viewController project files: ui pages and descriptors
  5. Test the application

As base for this case study we use the sample BlogUploadDownloadV2.zip which is the final sample from the Part 3.

1) unzip the sample into a fresh folder. We use the same folder names from the zip archive, but use a different base directory (backport in this case)
2) The images below show the steps to take after copying the files into the backport folder and opening the ‘.jws’ file. Don’t be fooled by the wizard which tell you that the files are converted from jsf1.0 to jsf1.2. Somehow hte wizard only knows that that the files are not jsf1.2 so it assumes that they are jsf1.0.

3) The database project don’t need any attention. The model project can just be recompiled. Here are the last lines after the rebuild. If you like you can test application module using the application module tester

...
Validating Business Component: de.hahn.blog.uldl.model.businessobjects.Catalog
  copying de/hahn/blog/uldl/model/businessobjects/Catalog.xml to output directory
Updated file:/Q:/backport/BlogUploadDownload/ULDLModel/classes/META-INF/adfm.xml
[7:11:42 PM] Successful compilation: 0 errors, 0 warnings. 

4) Now we have to make some obvious changes to the ViewController project. Fist we have to check if the ui pages are built using JSPX pages of JSF pages. A look into the public_html folder reveals that there is only one page (Catalog.jsf) which was built as JSF page. In a first step we rename the file to Catalog.jspx. Fragments have the suffix ‘.jsff’ in both versions, but are based on different schemas. However, we don’t need to rename the fragments.
Now we refresh the viewController project to see the new jspx file.

Refresh Project

Refresh Project


When we open the file we’ll see an error which is hte result that we tried to open a jsf (renamed only to jspx) page in 11gR1.
Error opening the Catalog.jspx

Error opening the Catalog.jspx


We ignore the error and continue our work. To fix the error we have to look into the file (source) and see that the jsf page uses a different root element:

<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">

whereas a jspx page uses

<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 xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">

After changing this root element and adding the close tag, the whole page look like

<?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 xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
        <af:document title="Catalog.jspx" id="d1">
            <af:form id="f1" usesUpload="true">
                <af:panelStretchLayout topHeight="50px" id="psl1">
                    <f:facet name="top">
                        <af:outputText value="Upload Download Test" id="ot1" inlineStyle="font-size:xx-large;"/>
                    </f:facet>
                    <f:facet name="center">
                        <af:region value="#{bindings.catalogtaskflowdefinition1.regionModel}" id="r1"/>
                        <!-- id="af_one_column_header_stretched"  -->
                    </f:facet>
                </af:panelStretchLayout>
            </af:form>
        </af:document>
    </f:view>
</jsp:root>

We still see red marks in the right side gutter of the file. These are because we have not jet changes the libraries used by the project. After removing the JSF2.0 library we use the ‘Code Assist’ function of JDeveloper to add the correct JSF1.2 tag libs and libraries.


You may have to close JDeveloper and reopen it again to get rid of the red marks. After this you can open hte Catalog.jspx file in design mode:
Migrated Catalog.jspx

Migrated Catalog.jspx


The next step is to look into the ‘.jsff’ fragments. These too are using a different schema. This is the JSF2.0 fragment which uses a tag:

<?xml version='1.0' encoding='UTF-8'?>
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
                xmlns:f="http://java.sun.com/jsf/core">

The JSF1.2 fragment uses a jsp:root element instead:

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:af="http://xmlns.oracle.com/adf/faces/rich" xmlns:f="http://java.sun.com/jsf/core">

Changing the ui:component tag to the jsp:root tag (don’t forget to change the end tag) the fragments can be opened in design mode too. Depending on the components you have used in your may have to change some ui components to get your page to work.
Next step is to adjust the Databindings.cpx which still holds a link to the Catalog.jsf page.

Old Databindings.cpx

Old Databindings.cpx


Open the file in source mode and change the ‘Catalog.jsf’ to ‘Catalog.jspx’. Next we have to check the ‘adfc-config.xml’ which holds a reference to the ‘Catalog.jsf’ page. This we change to ‘Catalog.jspx’.
Now we are ready to try to run the backported app the first time. We check the DB connection first, then run the application.

<20.05.2013 20:58 Uhr MESZ> <Error> <J2EE> <BEA-160197> <Unable to load descriptor P:\jdeveloper\system\system11.1.1.7.40.64.93\o.j2ee\drs\BlogUploadDownload/META-INF/weblogic-application.xml of module BlogUploadDownload. The error is weblogic.descriptor.DescriptorException: Unmarshaller failed
	at weblogic.descriptor.internal.MarshallerFactory$1.createDescriptor(MarshallerFactory.java:161)
	at weblogic.descriptor.BasicDescriptorManager.createDescriptor(BasicDescriptorManager.java:323)
	at weblogic.application.descriptor.AbstractDescriptorLoader2.getDescriptorBeanFromReader(AbstractDescriptorLoader2.java:788)
...
	at weblogic.work.ExecuteThread.run(ExecuteThread.java:178)
Caused by: com.bea.xml.XmlException: weblogic.descriptor.BeanAlreadyExistsException: Bean already exists: "weblogic.j2ee.descriptor.wl.LibraryRefBeanImpl@eadc995c(/LibraryRefs[[CompoundKey: adf.oracle.domain]])"
	at com.bea.staxb.runtime.internal.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:54)
	at com.bea.staxb.runtime.internal.RuntimeBindingType$BeanRuntimeProperty.setValue(RuntimeBindingType.java:539)
...

This error points to one of the descriptors we did not check up to now. The ‘/LibraryRefs[[CompoundKey: adf.oracle.domain]]’ is defined in the weblogic-application.xml file which we find ‘Application Resources’->’Descriptors’->’META-INF’ node.

weblogic-appliaction.xml

weblogic-appliaction.xml


Here we see that there are multiple entries for the same listeners and library-ref entries. These entries are getting duplicated each time you open the project (or restart JDeveloper). The reason for this is that the ‘xsi:schemaLocation’ is pointing to the wrong version ‘1.1’ (and location) in this case. The correct version for the 11gR1 is ‘1.0’. We replace the wrong schema with the correct one and remove the duplication entries.

<weblogic-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-application http://www.bea.com/ns/weblogic/weblogic-application/1.0/weblogic-application.xsd"
                      xmlns="http://www.bea.com/ns/weblogic/weblogic-application">

Before we run the application again we check need to check the web.xml which contains a couple of entries which are not needed using 11gR1.
There ‘context-param’s javax.faces.PARTIAL_STATE_SAVING, oracle.adf.view.rich.security.FRAME_BUSTING, javax.faces.FACELETS_VIEW_MAPPINGS, javax.faces.FACELETS_SKIP_XML_INSTRUCTIONS, javax.faces.FACELETS_SKIP_COMMENTS, javax.faces.FACELETS_DECORATORS and javax.faces.FACELETS_RESOURCE_RESOLVER should be removed.
After these changes the application is ready to run.
5) Test run the application

Running Backported Application

Running Backported Application

If you like to use hte new skyros skin you neet to alter the trinidad-config.xml to:

<?xml version="1.0" encoding="UTF-8"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
  <skin-family>skyros</skin-family>
  <skin-version>v1</skin-version>
</trinidad-config>

The last things to check is the ‘transaction isolation level’ of the task flows and the ‘ChangeEventPolicy’ of the iterators. The default ‘ChangeEventPolicy’ has been changed for 11gR2 applications to ‘ppr’ whereas it was ‘none’ for 11gR1. This change might not be visible at first, but you may notice some flicker in the table of the sample. This is the result of the new ‘ChangeEventPolicy’. If you check it back to none, you have to app the partial triggers to the components yourself. This is done in the sample which comes with Part 4 of the file and image handling sample.

There sure are more things to change, which I did not mention. This is because I did not need to change them ot did not find them. If you come across suhc a missing thing, please drop me a note so that I can add this to this article.

Master Detail Insert/Delete Sample

In the last couple of weeks a question on the OTN JDeveloper & ADF forum about how to insert and delete in a master-detail szenario was asked frequently. There are some threads in the forum talking about this, but apparently no solution which answers all the questions. This post tries to fill in the gap.

Use case
The use case we implement in this blog is as follows: we have a master detail relationship defined by a foreign key in the detail table. We use the REGION and COUNTRIES tables of the HR DB schema. There is a 1..* relationship between the REGION and the COUNTRIES tables. When you look at the REGIONS table only four regions are defined. We want to be able to create a new region and add some new countries into the detail table COUNTRIES before committing the whole transaction. Likewise we want to delete a region together with the countries attached to it, but also a country by itself without deleting a region.

Implementation
The insert part is pretty simple as the ADF framework does it automatically for you. You only have to use the right view objects from the data control and the framework does all the magic of propagating the FK to the child table when you insert a new row into the child table. This even works if you just added a new row to the master without committing it before inserting new child records.
The cascading delete part is not as obvious as the framework offers some help, but delegates the real work to the DB. If the DB knows about the cascading delete it works out OK. However, when you build the business components from the DB tables, the framework does not tell the DB that you want the cascading delete to happen. So without a small change the use case won’t work without us manually deleting all child rows ourselves.

In this blog we build a sample which shows how this use case can be implemented with the help of the DB. In an OTN Harvest post What happens when you choose cascade delete on an association Frank Nimphius talked about what it means if we choose the cascade delete option on an association. Here is a quote from the article:


One of the configuration options in the visual editor is Implement Cascade Delete that is located in the
Relationship menu under the Behavior header. Selecting this option indicates that all detail rows that are
associated with a parent entity should be deleted when the parent entity is deleted.
However, ADF Business Components does not itself perform the cascade delete, but expects a database
constraint to be defined for this. All that the Implement Cascade Delete does is to change the delete
command issued by ADF Business Components to the database.
To quote the “Oracle Fusion Middleware Fusion Developer’s Guide for Oracle Application Development
Framework”

” … When selected, this option allows the composing entity object to be removed unconditionally together with any composed
children entities. If the related Optimize for Database Cascade Delete option is deselected, then the composed entity objects
perform their normal DELETE statement at transaction commit time to make the changes permanent. If the option is
selected, then the composed entities do not perform the DELETE statement on the assumption that the database ON
DELETE CASCADE constraint will handle the deletion of the corresponding rows.

In summary, the article tells us what to do to make the cascading delete work. We need to make the foreign key constraint aware of that by setting it’s delete action to ‘CASCADE’. This we can do from within JDeveloper by using the DB Navigator. Open the HR connection and then open the ‘Tables’ node. Here we select the COUNTRIES table and edit it:

Change FK Constraints on the DB Table COUNTRIES

Change FK Constraints on the DB Table COUNTRIES


Select ‘CASCADE’ in the ‘On Delete’ drop down box in the Foreign Key dialog box:
Set Constraint to CASCADE

Set Constraint to CASCADE


After this change the rest of the use case is just setting up the UI.

Setting up the UI
First we take a look at the ‘Data Controls’ where we see the master detail relationship between the Regions and Countries:

Data Controls

Data Controls


The UI is build in on fragment ‘SchowMasterDetail.jsff’ which consists of a vertical splitter. In the upper part we put the master (Region) as a form and in the bottom part the detail (Countries) as editable table.

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1" xmlns:af="http://xmlns.oracle.com/adf/faces/rich" xmlns:f="http://java.sun.com/jsf/core">
  <af:panelStretchLayout id="psl1">
    <f:facet name="center">
      <af:panelSplitter id="ps1" orientation="vertical" splitterPosition="150">
        <f:facet name="first">
          ...
        </f:facet>
       <f:facet name="second">
         ...
       </f:facet>
      </af:panelSplitter>
      <!-- id="af_one_column_stretched"   -->
    </f:facet>
    <f:facet name="bottom"/>
  </af:panelStretchLayout>
</jsp:root>

To get the master data into the form we drag the Region view object from the data controls on the first splitter facet and drop it as form with submit and navigation buttons. After this we add another panelGroupLayout where we put some additional buttons which we use to create a new region, delete a region, rollback the changes and commit the changes.

For the detail we use the Countries view object from the data controls and drop it as table (editable) on the center facet of a panelStretchLayout which we put on the second facet of the panelSplitter. We mark the table singe selection and sortable. As we did for the regions we add some buttons to the bottom facet which we use to create a new country, delete a country, rollback the changes or commit them. Rollback and commit are for the whole transaction. Even if we click the rollback in the detail part, the whole transaction is rolled back.

Final Page Layout

Final Page Layout


The image above shows the final page layout. Only two things are left to do. We change to the bindings tab of the ShowMasterDetail.jsff and set the ‘ChangeEventPolicy’ for both iterators (Region and Countries) to ‘ppr’. This spares us to update the input fields and the table after each click on one of the navigation buttons. If you are using 11.1.1.7.0 or 11.1.2.x this is the default for newly created iterators. For older versions you have to do it yourself.
Set ChangeEventPolicy to PPR

Set ChangeEventPolicy to PPR

Set ChangeEventPolicy to PPR

Set ChangeEventPolicy to PPR


Running Application
The images below are showing the running sample. We start by verifying that no countries for attached to region id 5 are present, then create a new region with the id 5. Without committing the new region we create some new countries for the fresh created region 5. Finally we commit the transaction and verify that the new data is present in the DB.

Now we delete the new region 5 with all countries created.

The sample was built using JDeveloper 11.1.1.7.0 and uses the HR DB schema. It should be work with JDeveloper 11.1.1.5.0, 11.1.1.6.0 and JDeveloper 11.1.2.3.0 too. You can download the sample from the ADF-EMG Sample Project ‘BlogMasterDetail.zip’

How to Add New Choice to an af:selectOneChoice at Runtime

A question on the JDeveloper and ADF OTN forum asked for help on how to add a new choice to an af:selectOneChoise on the page during runtime. The selectOneChoice is based on is a table in the DB.
This sample shows how to do this using the HR DB schema. The workspace is built using JDev 11.1.1.7.0 but should work without a problem using 11.1.1.4.0 and higher too. You may be asked if you want to migrate, which you should answer with ‘Yes’. The sample can be downloaded using the link provided at the end of this blog.

As this is a real simple data model it only uses the REGIONS table from the HR DB schema. As we want to add new values to the regions table we need to generate entity and view object for the Regions table. We name the VO RegionsLov to make clear that we use this VO as base for a LOV in the UI. To add a new row into this table we implement a public method in the application module which we expose through the client interface of the am. This method isn’t needed as we could simply use createWithParams method from the VO, but the use case asked on the forum did use a method in the application module too.

    public void insertRegion(Integer id, String name) {
        if (id == null || name == null)
            throw new JboException("No Data!");
        Row row = getRegionsLov().createRow();
        row.setAttribute("RegionId", id);
        row.setAttribute("RegionName", name);
        getRegionsLov().insertRow(row);
        // commit is used here only because of the use case from the forum!
        this.getTransaction().commit();
    }

You see that the method uses a commit statement, which I prefer not to do. The reason for this is that you can’t reuse this method in other cases where no commit is allowed because there are other pending changed which you don’t want to commit at the moment. We can remove the commit and call the commit from a bean or directly from a button in the UI to make the method reusable.

We use the VO as dynamic list in the UI so we don’t need to add ViewAccessors or list attributes on the VO. Before we built the UI we add three temporary variables into the variables iterator of the pageDef file. Refer to Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef for information about how to do this. One variabel ‘RegionId’ (as Integer) is used to store the selection of the LOV we build, the other two ‘newRegionId’ (as Integer) and ‘newRegionName’ (as String) are used to create a new region row in the REGIONS table.
The LOV is built from a af:selectOneChoice which we generate by dragging the RegionsLov’s RegionId attribute onto the page and drop it as SelectOneChoice. The image below shows the binding as dynamik list

Define af:SelectOneChoise

Define af:SelectOneChoise


The whole UI looks like
Final UI

Final UI


Here we see the form with the two inputText components which store their values in temporary pageDef variables ‘newReginId’ and ‘newRegionName’. Here is the full pageDef file for reference:

<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel" version="11.1.1.64.93" id="StartPageDef" Package="de.hahn.blog.addvaluesoc.view.pageDefs">
  <parameters/>
  <executables>
    <variableIterator id="variables">
      <variable Name="region" Type="java.lang.Integer"/>
      <variable Name="showregion" Type="java.lang.Boolean" DefaultValue="false"/>
      <variable Name="newRegionId" Type="java.lang.Integer"/>
      <variable Name="newRegionName" Type="java.lang.String"/>
    </variableIterator>
    <iterator Binds="RegionsLov" RangeSize="-1" DataControl="BAVSOCAppModuleDataControl" id="RegionsLovIterator"/>
  </executables>
  <bindings>
    <list IterBinding="variables" id="RegionId" DTSupportsMRU="true" StaticList="false" ListIter="RegionsLovIterator" NullValueId="PLEASE_SELECT"
          NullValueFlag="start">
      <AttrNames>
        <Item Value="region"/>
      </AttrNames>
      <ListAttrNames>
        <Item Value="RegionId"/>
      </ListAttrNames>
      <ListDisplayAttrNames>
        <Item Value="RegionName"/>
      </ListDisplayAttrNames>
    </list>
    <attributeValues IterBinding="variables" id="newRegionId1">
      <AttrNames>
        <Item Value="newRegionId"/>
      </AttrNames>
    </attributeValues>
    <attributeValues IterBinding="variables" id="newRegionName1">
      <AttrNames>
        <Item Value="newRegionName"/>
      </AttrNames>
    </attributeValues>
    <methodAction id="insertRegion" InstanceName="BAVSOCAppModuleDataControl.dataProvider" DataControl="BAVSOCAppModuleDataControl" RequiresUpdateModel="true"
                  Action="invokeMethod" MethodName="insertRegion" IsViewObjectMethod="false">
      <NamedData NDName="id" NDValue="" NDType="java.lang.Integer"/>
      <NamedData NDName="name" NDType="java.lang.String"/>
    </methodAction>
  </bindings>
  <ResourceBundle>
    <PropertiesBundle xmlns="http://xmlns.oracle.com/adfm/resourcebundle" PropertiesFile="de.hahn.blog.addvaluesoc.view.BAVSOCViewControllerBundle"/>
  </ResourceBundle>
</pageDefinition>

Now we look at the running application:


Here we see that originally are only four regions present in the selectOneChoice, and we add a new one with the Id=5 and the name ‘aaaaaaaa’ which is inserted with a click on the ‘Insert New Region’ button. The button calls a method in a managed bean

    public void insertRegionListener(ActionEvent actionEvent) {
        // GET A METHOD FROM PAGEDEF AND EXECUTE IT
        // get the binding container
        BindingContainer bindings =
            BindingContext.getCurrent().getCurrentBindingsEntry();
        // get an ADF attributevalue from the ADF page definitions
        AttributeBinding attrId =
            (AttributeBinding)bindings.getControlBinding("newRegionId1");
        Integer id = (Integer)attrId.getInputValue();
        AttributeBinding attrName =
            (AttributeBinding)bindings.getControlBinding("newRegionName1");
        String name = (String)attrName.getInputValue();
        // get an Action or MethodAction
        OperationBinding method = bindings.getOperationBinding("insertRegion");
        if (method == null) {
            FacesMessage msg =
                new FacesMessage(FacesMessage.SEVERITY_ERROR, "Method insertRegion not found!",
                                 "");
            FacesContext.getCurrentInstance().addMessage(null, msg);
            return;
        }

        // if there are parameters to set...
        Map paramsMap = method.getParamsMap();
        paramsMap.put("id", id);
        paramsMap.put("name", name);
        // execute the method
        method.execute();
        List errors = method.getErrors();
        if (!errors.isEmpty()) {
            Exception e = (Exception)errors.get(0);
            FacesMessage msg =
                new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(),
                                 "");
            FacesContext.getCurrentInstance().addMessage(null, msg);
            return;
            // handle errors here errors is a list of exceptions!
        }

        // Get a attribute value of the current row of iterator
        DCBindingContainer dcBindings = (DCBindingContainer)bindings;
        DCIteratorBinding iterBind =
            (DCIteratorBinding)dcBindings.get("RegionsLovIterator");
        iterBind.executeQuery();

        // gat the form for refresh
        UIComponent ui = (UIComponent)actionEvent.getSource();
        ui = ui.getParent();
        ResetUtils.reset(ui);
        attrId.setInputValue(null);
        attrName.setInputValue(null);

        // PPR refresh a jsf component
        AdfFacesContext.getCurrentInstance().addPartialTarget(ui);

    }

The method first gets the entered parameters for the new region id and name, then calls the method to insert the data in the AM (insertRegion) and finally requires the iterator which is used for the selectOneChoice in the UI (RegionsLovIterator).

You can download the sample workspace which was built with JDev 11.1.1.7.0 and which uses the HR schema from the ADFEMG Sample Project BlogAddValueSOC.zip page.

Make Disclosed Row the Current Row when using a Detail Facet of a Table

A question on the OTN JDeveloper & ADF forum asked for help on a use case using a table with an active detail facet. The problem is that when you disclose a row to see the detail, the current row is not changing to the disclosed row.

To make this work we add a RowDisclosureListener where we get the row which should be disclosed and make the row the current row.

The sample, which you cam download using the link provided at the end of the post, uses the HR schema. The master table show the employees and the detail facet is used to show details of the current job the employee has.

Running Application

Running Application


The data model looks like
Data Model

Data Model


The tabel is build by dropping the Employees table onto a page as read only table. The detail facet is build by dropping the JobsDetail as read only form onto the facet. The resulting page code looks like

        <af:table value="#{bindings.Employees.collectionModel}" var="row" rows="#{bindings.Employees.rangeSize}"
                  emptyText="#{bindings.Employees.viewable ? 'No data to display.' : 'Access Denied.'}" fetchSize="#{bindings.Employees.rangeSize}"
                  rowBandingInterval="0" selectedRowKeys="#{bindings.Employees.collectionModel.selectedRow}"
                  selectionListener="#{bindings.Employees.collectionModel.makeCurrent}" rowSelection="single" id="t1"
                  rowDisclosureListener="#{ShowEmployees.rowDiscloseListener}">
...
          <f:facet name="detailStamp">
            <af:panelFormLayout id="pfl1" partialTriggers="::t1">
              <af:panelLabelAndMessage label="#{bindings.JobId.hints.label}" id="plam3">
                <af:outputText value="#{bindings.JobId.inputValue}" id="ot10"/>
              </af:panelLabelAndMessage>
              <af:panelLabelAndMessage label="#{bindings.JobTitle.hints.label}" id="plam1">
                <af:outputText value="#{bindings.JobTitle.inputValue}" id="ot7"/>
              </af:panelLabelAndMessage>
              <af:panelLabelAndMessage label="#{bindings.MinSalary.hints.label}" id="plam2">
                <af:outputText value="#{bindings.MinSalary.inputValue}" id="ot8">
                  <af:convertNumber groupingUsed="false" pattern="#{bindings.MinSalary.format}"/>
                </af:outputText>
              </af:panelLabelAndMessage>
              <af:panelLabelAndMessage label="#{bindings.MaxSalary.hints.label}" id="plam4">
                <af:outputText value="#{bindings.MaxSalary.inputValue}" id="ot9">
                  <af:convertNumber groupingUsed="false" pattern="#{bindings.MaxSalary.format}"/>
                </af:outputText>
              </af:panelLabelAndMessage>
            </af:panelFormLayout>
          </f:facet>

The problem now is, that if we click on the disclose arrow on the left hand side we see the detail information of the current employee row (the master row). The disclosure of a row doesn’t set the current row to the disclosed row.
The solution is to make the disclosed row the current row. For this we add a rowDisclosureListener to the table which points to a method in a bean. The code of hte listener look like

    /**
     * Disclosure event
     * @param rowDisclosureEvent
     */
    public void rowDiscloseListener(RowDisclosureEvent rowDisclosureEvent) {
        RowKeySet addedSet = rowDisclosureEvent.getAddedSet();
        Object object = rowDisclosureEvent.getSource();
        // iterate over the disclosed row (hopefully only one)
        for (Object obj : addedSet) {
            List<Key> rowKeys = (List<Key>)obj;
            // make the disclosed row the current row
            this.makeDisclosedRowCurrent(rowDisclosureEvent,
                                         (Key)rowKeys.get(0));
        }
    }

    /**
     * Synchronizes the table UI component row selection with the
     * selection in the ADF binding layer
     * @param rowDisclosureEvent event object created by the table
     * component upon row selection
     */
    public static void makeDisclosedRowCurrent(RowDisclosureEvent rowDisclosureEvent,
                                               Key key) {
        RichTable _table = (RichTable)rowDisclosureEvent.getSource();
        //the Collection Model is the object that provides the
        //structured data
        //for the table to render
        CollectionModel _tableModel = (CollectionModel)_table.getValue();
        //the ADF object that implements the CollectionModel is
        //JUCtrlHierBinding. It is wrapped by the CollectionModel API
        JUCtrlHierBinding _adfTableBinding =
            (JUCtrlHierBinding)_tableModel.getWrappedData();
        //Acess the ADF iterator binding that is used with
        //ADF table binding
        DCIteratorBinding _tableIteratorBinding =
            _adfTableBinding.getDCIteratorBinding();

        //get the row key from the added rowdisclosure event
        Key _rwKey = key;
        _tableIteratorBinding.setCurrentRowWithKey(_rwKey.toStringFormat(true));
    }

The method rowDiscloseListener is used to get the key of the disclosed row and the method makeDisclosedRowCurrent is used to make the row the current row and synchronise the binding layer to reflect this.

You can download the sample thos the ADFEMG Sample side BlogTableDetail.zip
The sample uses the HR schema and was built with JDev 11.1.1.7.0 but works works with 11.1.1.4.o and higher too.