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.

Using one ViewObject for Global Lookup Data (Part 2)

Based on the other post ‘Using one ViewObject for Global Lookup Data’ I got an other question on how to use this approach in a way, that a LOV is initalized with an area of lookup data before the page in shown to the user.
This can be done, all declarative without the need to write java code e.g. in an onLoad() method. The solution is to use a bounded task flow which initializes the LOV before navigation to the page the LOV is used.
To show this we create a simple navigation model in the adfc-config.xml like

Navigation Model

Navigation Model

‘Generic Lookup’ is used to show how to do this inside a page, where the lookup is presented in a region, the ‘LookupInitPosition’ shows how to preset an area for the lookup data (POSITION in this case) before showing the page in a region too.
Before we go into detail on how to setup the UI we need to add another view object to our data model which we use the acquire the lookup data

VO to get Lookup Data

VO to get Lookup Data


The ‘GeneralLookup’ view object is the same we used in part 1 to get the lookup data of an area, only this time we don’t specify lookup area but a dummy value (‘1’ in this case) for the area. This dummy value is set so that we don’t get any result back. If the ‘GeneralLookup’ view is executed without an area given from the outside, we don’t get any result. Keep this in mind when we are looking at the running sample later on.

Back to the UI. As both use cases uses a region it’s a nice sample how a bounded task flow (btf) can be reused in different use cases. The bounded task flow itself looks like

Navigation Model Bounded Task Flow

Navigation Model Model Bounded Task Flow

Bounded Task Flow to Show preselected Lookup Data

Bounded Task Flow to Show preselected Lookup Data


As you see the bounded task flow uses a method call (ExecuteWithParams) as it’s start activity. A task flow parameter is used to pass the name of the lookup area we want the LOV to initially show.
The name of the input parameter does not really matter, what you have to note down is the value part ‘#{pageFlowScope.selectedType}’ as this is the the variable we use to set the bindType parameter of the method call activity ExecuteWithParams.

Setup of the ExecuteWithParams Method Call

Setup of the ExecuteWithParams Method Call

After executing the query with the new set bindType we navigate to to page to show the result:

Region View1 which shows the LOV with the Preselected Lookup Data

Region View1 which shows the LOV with the Preselected Lookup Data


We see the LOV as af:selectOneChoice which stores the selected value in an attribute from the variable iterator (see Creating Variables and Attribute Bindings to Store Values Temporarily in the PageDef for more info on this). The input text below the LOV can be used to change the area parameter used for the LOV. If we enter e.g. WEEKDAY and click the ‘Refesh’ button, the input value is read and transferred to the parameter (#{pageFlowScope.selectedType}) for the executeWithParameter method via a af:setPropertyListener. This allows to change the LOV on the fly from within the region.
The LOV source is a dynamic list generated from the Generallookup VO as seen in the next image
Setup LOV for af:selectOneChoice

Setup LOV for af:selectOneChoice

After we know how the region (the bounded task flow) works, we can build the GenericLookup.jspx page. This page allows to enter the name of an ares into an input text field and then to refresh the bounded task flow, which we put on the page as region.

The final use case is to navigate to a page with with a preselected lookup area, POSITION in this case. For this we use the button ‘goto Page with init on POSITION’ at the bottom of the page.

You can download the sample workspace which was build JDeveloper 11.1.1.7.0 and using the HR schema from the ADF EMG Samples side BlogStaticVOLov_V3.zip. You can open the workspace using JDev 11.1.1.6.0 without a problem. If you are asked if you like to migrate the workspace to 11.1.1.6.0 answer with Yes.

Installing JDeveloper 11.1.1.7.0 from the Generic Installer Jar on 64bit Windows System

If you have installed JDev 11.1.1.7.0 lately, which I strongly recommend, you may have noticed, that the windows installer jdevstudio11117install.exe still ships with jdk160_24. Please don’t ask why Oracle don’t includes a JDk 1.7, I don’t know.
Well, it’s time to use JDK1.7 on my WIn7x64 system so I loaded hte jdevstudio11117install.jar which is a lot bigger (1.9GB) but comes without a bundeld JDK. As I have already installed JDK 1.7.0_17 on my system I pointed the installation to this jdk when I asked during the installation.
Everything went smooth and i took only a couple of minutes to install JDev and generate the embedded WLS 10.3.5 instance.

However, when I tried to start the embedded WLS instance I got the following error message

*** Using port 7101 ***
r:\jdeveloper\system\system11.1.1.7.40.64.93\DefaultDomain\bin\startWebLogic.cmd
[waiting for the server to complete its initialization...]
.
.
JAVA Memory arguments: -Xms256m -Xmx512m
.
WLS Start Mode=Development
.
CLASSPATH=...
.
PATH=...
.
***************************************************
*  To start WebLogic Server, use a username and   *
*  password assigned to an admin-level user.  For *
*  server administration, use the WebLogic Server *
*  console at http:\\hostname:port\console        *
***************************************************
starting weblogic with Java version:
<strong>Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Unrecognized option: -jrockit</strong>
Starting WLS with line:
...
Process exited.

Hm, ‘Unrecognized option: -jrockit‘, how’s that? I’m running Sun JDK!
Right at the beginning of the server start we see the command used to start the server (the path may be different on your system)
r:\jdeveloper\system\system11.1.1.7.40.64.93\DefaultDomain\bin\startWebLogic.cmd
A look into this command shell reveals that another command shell script is called
r:\jdeveloper\system\system11.1.1.7.40.64.93\DefaultDomain\bin\setDomainEnv.cmd
In this script we find the problem

...
set BEA_JAVA_HOME=C:\Program Files\Java\jdk1.7.0_17

set SUN_JAVA_HOME=


if "%JAVA_VENDOR%"=="Oracle" (
	set JAVA_HOME=%BEA_JAVA_HOME%
) else (
	if "%JAVA_VENDOR%"=="Sun" (
		set JAVA_HOME=%SUN_JAVA_HOME%
	) else (
		set JAVA_VENDOR=Oracle
		set JAVA_HOME=C:\Program Files\Java\jdk1.7.0_17
	)
)
...

As you see hte BEA_JAVA_HOME is set and if you put an
echo %JAVA_VENDOR%
before the if statement you see that the vendor is null. This sets the JAVA_HOME correct, but sets the JAVA_VENDOR to ‘Oracle’. This then adds the wrong option -jrockit to the command line later on in the startWebLogic.cmd script.

Now that we know that the solution is to make a small change to the
r:\jdeveloper\system\system11.1.1.7.40.64.93\DefaultDomain\bin\setDomainEnv.cmd
script. We only have to set the SUN_JAVA_HOME and set the JAVA_VENDOR to ‘Sun’

set BEA_JAVA_HOME=

set SUN_JAVA_HOME=C:\Program Files\Java\jdk1.7.0_17
set JAVA_VENDOR=Sun

After this change the embedded WLS server starts without a problem.