Saturday, March 20, 2010

ADF Faces RC: Change the Global Date Format of Your Application

To change the date format of your application, without necessarily affecting other locale settings like Language(localized text) ,or to established a standard global date format across different locales/countries, define the <formatting-locale> element of the trinidad-config.xml as illustrated below:
<?xml version="1.0" encoding="windows-1252"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
  <formatting-locale>en-GB</formatting-locale>
</trinidad-config>
You can also define it with an EL that resolve to a java.util.Locale instead of a hard text (like en-GB)
<?xml version="1.0" encoding="windows-1252"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
  <formatting-locale>#{userSession.formattingLocale}</formatting-locale>
</trinidad-config>
default (based on my browser's locale):

with formatting-locale set to "en-GB":

with formatting-locale set to "fr-CA":

with formatting-locale set to "ja":


Below is a quotation from section A.6.2.8 (Formatting Dates and Numbers Locale) of the Web User Interface Developer's Guide:
By default, ADF Faces and MyFaces Trinidad will format dates (including the first day of the week) and numbers in the same locale used for localized text (which by default is the locale of the browser). If, however, you want dates and numbers formatted in a different locale, you can use the element, which takes an IANA-formatted locale (for example, ja, fr-CA) as its value. The contents of this element can also be an EL expression pointing at an IANA string or a java.util.Locale object.
Two things that I have observed:
  1. The formatting-locale is case sensitive. For example, replacing "en-GB" with "en-gb" will NOT make localization work.
  2. When using EL, the expression should resolved to a java.util.Locale. An expression that resolves to a string like "en-GB" will throw an exception.

To learn how to bind configuration values to managed bean and/or how to configure general locale settings, please see the following post:
ADF Faces RC : Supplement on How to Configure Pages for an End User to Specify Locale at Runtime

Cheers!

Friday, March 19, 2010

ADF UI Shell: Extending the UI Shell to Allow Passing of Parameters to Bounded Task Flows

I am trying to create a blog-post demonstrating a pattern of having a list, show details, edit, and create activities based on the HR Schema. But upon testing, I was stacked on the issue of parameter passing to bounded task flows of the UI Shell which was discussed on this thread. Since in the previous post, I had just demonstrated how to make minor modification to the layout of the Dynamic Tab Shell template, so I thought that it would be better to consolidate the information and post here the step-by-step guide on how to support parameter passing while the fix is not yet available.

To allow parameter passing to the bounded task flows that will be launched on a separated tab, we need to modify the dynamicTabShellDefinition.xml file in the oracle-page-templates-ext.jar. Below is the step by step procedure to do this:
  1. Copy the oracle-page-templates-ext.jar from %MiddlewareHome%\jdeveloper\adfv\jlib\ folder
  2. Create a new folder in drive C like C:\temp and paste the copied jar.
  3. Extract the contents of the jar.
    1. Open command prompt and navigate to the C:\temp folder.
    2. Enter the following command:
      jar xf oracle-page-templates-ext.jar
          
    3. If the jar command cannot be recognized, then you need to add the java bin folder path into the "Path" system variables. Ex. "C:\Oracle\Middleware\jdk160_14_R27.6.5-32\bin;"
  4. Delete the jar that we have copied into the temp directory. We do this so that we can easily repack a jar later.
  5. In windows explorer, browse the "dynamicTabShellDefinition.xml" file in "C:\temp\oracle\ui\pattern\dynamicShell\model" directory
  6. Right click the file and click "open with...". Open it with JDeveloper.
  7. Modify the parameter definition to as follows:
    <parameters>
        <parameter id="tabContext" value="${viewScope.tabContext}"/>
        <parameter id="parameterMap" value="${requestScope.parameterMap}"/>
    </parameters>
    
  8. Redefine all the taskFlow/region bindings to define the "parametersMap" attribute value to "#{bindings.parameterMap}".
    <taskFlow id="r0"
                  taskFlowId="${viewScope.tabContext.taskflowIds[0]}"
                  activation="deferred"
                  xmlns="http://xmlns.oracle.com/adf/controller/binding"
                  parametersMap="#{bindings.parameterMap}">
          <parameters>
            <parameter id="tabContext" value="${bindings.tabContext}"
                       xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
          </parameters>
    </taskFlow>
    
    You need to do this to all the 15 region definition.
  9. Save your modification.
  10. Repackage the file in a jar. Open a command prompt navigate to the temp folder we created earlier then run the following command:
    jar cfM0 oracle-page-templates.jar *
    
    0 (Zero)
    You can give the jar a different name if you like.
  11. Modify libraries and classpath.
    1. Open your application in JDeveloper.
    2. Right-click your ViewController project and click project properties.
    3. Remove the Oracle Extended Page Templates in the classpath entries.
    4. Add the jar file that we created in the step above.

If you wanted to see a sample test case using the ADF UI Shell that connects to HR Schema, see the following post:
UI Shell in Action

Cheers!

Wednesday, March 17, 2010

ADF UI Shell: Customizing the Dynamic Tab Shell Layout

A user in Oracle UI Shell Functional Pattern thread asked me on a step by step guide on how to re-size the space taken by the Legal area at the bottom part of the UI Shell, thus I came up with this post.


If you wanted to modify some layout of the Oracle Dynamic Tabs template, you can do the following:
  1. Copy the oracle-page-templates-ext.jar from %MiddlewareHome%\jdeveloper\adfv\jlib\ folder
  2. Create a new folder in drive C like C:\temp and paste the copied jar.
  3. Extract the contents of the jar.
    1. Open command prompt and navigate to the C:\temp folder.
    2. Enter the following command:
      jar xf oracle-page-templates-ext.jar
          
    3. If the jar command cannot be recognized, then you need to add the java bin folder path into the "Path" system variables. Ex. "C:\Oracle\Middleware\jdk160_14_R27.6.5-32\bin;"
  4. Delete the jar that we have copied into the temp directory. We do this so that we can easily repack a jar later.
  5. In windows explorer, browse the "dynamicTabShell.jspx" file in "C:\temp\oracle\ui\pattern\dynamicShell" directory
  6. Right click the file and click "open with...". Open it with JDeveloper.
  7. Modify the bottom height of the panelStretchLayout component highlighted in the following screen shot. Modify the size from the default "50px" to the value you desired like for example "15px".
    The effect of your modification can be immediately noticed on the design view of the page.
  8. Save your modification.
  9. Repackage the file in a jar. Open a command prompt navigate to the temp folder we created earlier then run the following command:
    jar cfM0 oracle-page-templates.jar *
    
    0 (Zero)
    You can give the jar a different name if you like.
  10. Modify libraries and classpath.
    1. Open your application in JDeveloper.
    2. Right-click your ViewController project and click project properties.
    3. Remove the Oracle Extended Page Templates in the classpath entries.
    4. Add the jar file that we created in the step above.
  11. Run your application. In my case I applied it to the accompanying UIShellSherman_v02 sample app of the UI Shell.

For more information about unpacking and packaging jar files, see the following links:
If you wanted to extend the UI Shell to allow passing of parameters to the bounded task flows, click here.

Cheers!

Friday, March 12, 2010

ADF Faces RC: Grab that ADF Skins in 5 minutes

In this post, I applied skinning to the accompanying demo application (UIShellSherman_V02)of the ADF UI Shell to demonstrate how easy it is to apply skinning into an existing ADF application . You can do the same into your current application in just a matter of 5 minutes by following this blogpost. Impress your boss now! :D
Oracle ADF Faces RC has the following skins which could jump-start implementation of skinning into your application:
  • fusion

  • rainforest

  • sporty

  • princess

  • blafplus-rich
  • blafplus-medium
  • fusion projector
  • simple

Before you start the steps below, be sure that you have an extracted copy of the latest adffacesdemo. You can download the same here.

The time starts now...

To apply skinning into your app, do the following:
  1. Copy the skins folder of adffacesdemo.
  2. Copy the trinidad-skins.xml of the adffacesdemo
  3. Add additional "skinFamily" string attribute into your existing session scope managed bean that holds user session information.
  4. Add a skin menu into your menu bar.

Copy the skins folder of adffacesdemo

Copy the skins folder of adffacesdemo located in .../adffacesdemo/public_html and paste it into the public_html directory of your application.

Copy the trinidad-skins.xml of the adffacesdemo

Copy the trinidad-skins.xml of the adffacesdemo located in .../adffacesdemo/public_html/WEB_INF folder and paste the same on the same directory in your application

Add additional "skinFamily" string attribute into your existing session scope managed bean

You can add the "skinFamily" string attribute into an existing session scope manage bean that holds user session information. If nothing is applicable, you can create something like the following class and declare it as a session scope managed bean in adfc-config.xml
package soadev.view.managed;

import java.io.IOException;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import oracle.adf.view.rich.component.rich.nav.RichCommandMenuItem;

public class UserSession {
    private String skinFamily;

    public void setSkinFamily(String skinFamily) {
        this.skinFamily = skinFamily;
        reloadThePage();
    }

    public String getSkinFamily() {
        if (skinFamily == null) {
            skinFamily = "fusion";
        }
        return skinFamily;
    }

    public void skinMenuAction(ActionEvent event) {
        RichCommandMenuItem menuItem = (RichCommandMenuItem)event.getComponent();
        setSkinFamily(menuItem.getText());
        reloadThePage();
    }
    
    //taken from adffacesdemo
    public static void reloadThePage() {
        FacesContext fContext = FacesContext.getCurrentInstance();
        String viewId = fContext.getViewRoot().getViewId();
        String actionUrl =
            fContext.getApplication().getViewHandler().getActionURL(fContext,
                                                                    viewId);
        try {
            ExternalContext eContext = fContext.getExternalContext();
            String resourceUrl =
                actionUrl; //eContext.encodeResourceURL(actionUrl);
            // Use the action URL directly since the encoding a resource URL will NPE in isEmailablePage()
            eContext.redirect(resourceUrl);
        } catch (IOException ioe) {
            System.err.println("Problem trying to reload the page:");
            ioe.printStackTrace();
        }
    }  
}

Add a skin menu into your menu bar

Add a skin menu into your existing menu bar plus menuCommandItems for the skins to be supported. Below is a sample menuBar with a skin menu:
    <af:menuBar id="menuBar" styleClass="AFHideSidePadding">
      <af:menu text="Skin" id="ptm1">
        <af:commandMenuItem text="blafplus-rich" type="radio"
                            actionListener="#{userSession.skinMenuAction}"
                            selected="#{userSession.skinFamily=='blafplus-rich'}"
                            id="ptcmi1"/>
        <af:commandMenuItem text="blafplus-medium" type="radio"
                            actionListener="#{userSession.skinMenuAction}"
                            selected="#{userSession.skinFamily=='blafplus-medium'}"
                            id="ptcmi2"/>
        <af:commandMenuItem text="fusion" type="radio"
                            actionListener="#{userSession.skinMenuAction}"
                            selected="#{userSession.skinFamily=='fusion'}"
                            id="ptcmi31"/>
        <af:commandMenuItem text="fusion-projector" type="radio"
                            actionListener="#{userSession.skinMenuAction}"
                            selected="#{userSession.skinFamily=='fusion-projector'}"
                            id="ptcmi31_1"/>
        <af:commandMenuItem text="simple" type="radio"
                            actionListener="#{userSession.skinMenuAction}"
                            selected="#{userSession.skinFamily=='simple'}"
                            id="ptcmi4"/>
        <af:commandMenuItem text="rainforest" type="radio"
                            actionListener="#{userSession.skinMenuAction}"
                            selected="#{userSession.skinFamily=='rainforest'}"
                            id="ptcmi6"/>
        <af:commandMenuItem text="sporty" type="radio"
                            actionListener="#{userSession.skinMenuAction}"
                            selected="#{userSession.skinFamily=='sporty'}"
                            id="ptcmi7"/>
        <af:commandMenuItem text="princess" type="radio"
                            actionListener="#{userSession.skinMenuAction}"
                            selected="#{userSession.skinFamily=='princess'}"
                            id="ptcmi8"/>
      </af:menu>
    </af:menuBar>
Your done? Congratulations! :D

How did I apply it to the accompanying app (UIShellSherman_V02)?

  • I created a global_links.jspx with the following code:
    <?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"
              xmlns:trh="http://myfaces.apache.org/trinidad/html">
      <jsp:directive.page contentType="text/html;charset=UTF-8"/>
      <af:panelGroupLayout id="pgl1">
        <af:menuBar id="menuBar" styleClass="AFHideSidePadding">
          <af:menu text="Skin" id="ptm1">
            <af:commandMenuItem text="blafplus-rich" type="radio"
                                actionListener="#{userSession.skinMenuAction}"
                                selected="#{userSession.skinFamily=='blafplus-rich'}"
                                id="ptcmi1"/>
            <af:commandMenuItem text="blafplus-medium" type="radio"
                                actionListener="#{userSession.skinMenuAction}"
                                selected="#{userSession.skinFamily=='blafplus-medium'}"
                                id="ptcmi2"/>
            <af:commandMenuItem text="fusion" type="radio"
                                actionListener="#{userSession.skinMenuAction}"
                                selected="#{userSession.skinFamily=='fusion'}"
                                id="ptcmi31"/>
            <af:commandMenuItem text="fusion-projector" type="radio"
                                actionListener="#{userSession.skinMenuAction}"
                                selected="#{userSession.skinFamily=='fusion-projector'}"
                                id="ptcmi31_1"/>
            <af:commandMenuItem text="simple" type="radio"
                                actionListener="#{userSession.skinMenuAction}"
                                selected="#{userSession.skinFamily=='simple'}"
                                id="ptcmi4"/>
            <af:commandMenuItem text="rainforest" type="radio"
                                actionListener="#{userSession.skinMenuAction}"
                                selected="#{userSession.skinFamily=='rainforest'}"
                                id="ptcmi6"/>
            <af:commandMenuItem text="sporty" type="radio"
                                actionListener="#{userSession.skinMenuAction}"
                                selected="#{userSession.skinFamily=='sporty'}"
                                id="ptcmi7"/>
            <af:commandMenuItem text="princess" type="radio"
                                actionListener="#{userSession.skinMenuAction}"
                                selected="#{userSession.skinFamily=='princess'}"
                                id="ptcmi8"/>
          </af:menu>
        </af:menuBar>
      </af:panelGroupLayout>
    </jsp:root>
    

  • I added a <jsp:include> tag to the globalLinks facet of the pages that implement the ADF UI Shell template. Below is a code snippet:
    <f:facet name="globalLinks">
      <f:subview id="sv1">
        <jsp:include page="/global_links.jspx" flush="true"/>
      </f:subview>
    </f:facet>
    

Conclusion

In this post we learn the following "how tos":
  • How to easily apply skinning into an existing ADF Faces RC application.
  • How to reload a page programmatically.
  • How to use <jsp:include> tag in ADF pages.
Cheers!

Wednesday, March 10, 2010

ADF Faces RC : Supplement on How to Configure Pages for an End User to Specify Locale at Runtime

I am completely lost when I read section 21.4 (Configuring Pages for an End User to Specify Locale at Runtime) of the Web User Interface Developer's Guide. Correct me if I am wrong, but it seems that, that section is still in draft mode as of this blog-posting, and that motivated me to write this post to supplement it. This post references some other sections of the guide to as not recreate instructions here. This post assumes that you already know how to configure resource bundles and how to bind the component labels to those resource bundles (also available on the guide).

We can configure our application so that end users can specify the preferred locale at runtime without them going through the browser settings. To support this we need to do the following:

  1. Register the locales and resource bundles used in your application in the faces-config.xml file.
  2. Add a String locale attribute to a session scope managed bean that holds relevant information for the user session.
  3. Create a selectOneChoice that list the supported locales from step 1 above.
  4. Set the locale attribute of the <f:view> tag of our pages to the attribute the we created.

Register the locales and resource bundles used in your application in the faces-config.xml file.

Please see section 21.3.3 (How to Register Locales and Resource Bundles in Your Application) of the Web User Interface Developer's Guide.


Add a String locale attribute to a session scope managed bean that holds relevant information for the user session

We can add the locale attribute to an existing session scope managed bean that holds other user session information or create a new one if nothing is available. We use "session scope" here so that the locale information is applied to the whole session instead of the user setting the local on every page. Below is a sample managed bean class:

package soadev.view.managed;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
public class UserSession implements Serializable {
    private String locale;
  
    public void setLocale(String locale) {
        this.locale = locale;
    }

    public String getLocale() {
        if (locale == null) {
            locale = FacesContext.getCurrentInstance().getViewRoot()
                .getLocale().toString();
        }
        return locale;
    }

    //gets all the supported locals plus the default locale defined in the faces-config.xml
    //and add it to the list of selectItems
    public List<SelectItem> getLocales() {
        List<SelectItem> items = new ArrayList<SelectItem>();
        Application application = FacesContext.getCurrentInstance()
                            .getApplication();
        Iterator supportedLocales = application.getSupportedLocales();
        while (supportedLocales.hasNext()) {
            Locale locale = (Locale) supportedLocales.next();
            items.add(new SelectItem(locale.toString(), locale.getDisplayName(locale)));
        }
        Locale defaultLocale = application.getDefaultLocale();
        items.add(new SelectItem(defaultLocale.toString(), defaultLocale.getDisplayName(defaultLocale)));
        return items;
    }
}

Create a selectOneChoice that list the supported locales from step 1 above.

Add somewhere in the page or in a user preferrence popup a selectOneChoice like below:
<af:selectOneChoice label="Select Locale" id="pt_soc2"
                     value="#{userSession.locale}"
                     autoSubmit="true">
  <f:selectItems id="si10" value="#{userSession.locales}"/>
</af:selectOneChoice>

Set the locale attribute of the <f:view> tag of our pages to the attribute the we created.

<?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 locale="#{userSession.locale}">

Caveat: To simplify the illustration in this post, we added our locale attribute directly to the session scope managed bean, but in practice we usually create a sort of UserPrefence class to hold the user preferred locale plus other preferences like adf skin, rowBandingIntervals, and etc... I just thought that maybe creating a post on how to implement user preference maybe worth. Is it?


Cheers!

Friday, March 5, 2010

ADF Model: How to Programmatically Access the DataProvider Thing

"One of the 'power tools' of ADF Binding is the dataProvider attribute that pretty much every binding object supports." -Duncan Mills

Duncan Mills has a comprehensive blogpost about this DataProvider thing here. When I was still on my first steps in learning ADF, I found it a challenge to programmatically access this DataProvider thing without using the JSFUtils.resolveExpression() method, that is why I thought that maybe it would be helpful for others who are still beginning their Oracle ADF experience if I will post here what I have figured out.

    public Object getCurrentRowDataProvider(String iteratorName) {
        BindingContainer bindings = getBindings();
        DCIteratorBinding dcib =
            (DCIteratorBinding)bindings.get(iteratorName);
        RowSetIterator iter = dcib.getRowSetIterator();
        DCDataRow row = (DCDataRow)iter.getCurrentRow();
        return row.getDataProvider();
    }
    public BindingContainer getBindings() {
        return (BindingContainer)JSFUtils.resolveExpression("#{bindings}");
    }
Illustrative usage:
    public void save(ActionEvent event){
        Employee employee = (Employee)getCurrentRowDataProvider("findAllEmployeesIterator");
        //some other codes like setting some default properties for the employee
        ...
        //or accessing the object attributes from employee
        System.out.println(employee.getDepartment().getDepartmentName());
        //invoke saveEmployee methodAction
        OperationBinding oper = getBindings().getOperationBinding("saveEmployee");
        oper.execute();
    }
Cheers!