Tuesday, December 28, 2010

Displaying special characters on UI

You can use Unicode to display special charters on UI components. You may need to use the resource bundle to hold the Unicode values (of the special characters), and the same can be referred from the UI using the key.


ResourceBundle Entry
-----------------------
view.copyRightSymbol=\u00a9

JSF
----
<c:set var="viewcontrollerBundle"
 value="#{adfBundle['view.ViewControllerBundle']}"/>
<af:outputText 
value="Copyright #{viewcontrollerBundle['view.copyRightSymbol']}" id="ot1"/>


The output on the page may look like as shown in the following image.

Friday, December 24, 2010

Updating supporting business data before committing the transaction

In some specific business scenarios you may need to manipulate(create/modify/delete) related business records before committing the parent record(EntityObject). For example, inserting entries in to an audit table whenever some business data changes. Here the audit table its not updated by a user, instead the table is populated based on the updated data from the transaction tables. This post discusses a recommended pattern for such scenarios where you may need to create/modify child/supporting entities whenever the parent entity changes.

Where do I keep the extra 'code' to deal with related entities?

Here you are looking for a place to hook your logic for creating/modifying supporting entities whenever the parent entity gets modified. The right place to keep this 'extra' logic is your parent EntityObject's prepareForDML(int operation, TransactionEvent e) method. With this approach, the newly created EntityObject(s) would get a chance to participate in the 'entity validation and post' cycle. ADF BC run time would take care the rest :)

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS2 + HR Schema]

A glance at the implementation

This example would let you to create Department records. For each newly created department record, system would generate a dummy employee record. This is done be overriding DepartmentsEntityImpl::prepareForDML(int operation, TransactionEvent e) as shown in the following code snippet.
@Override
protected void prepareForDML(int operation, TransactionEvent e) {
super.prepareForDML(operation, e);
  if (operation == EntityImpl.DML_INSERT) {
    createDummyEmployees();
  }
}

How to run this sample?

Run the main.jspx. This page displays Departments-Employees hierarchy. You can create new Department by clicking the 'Create Dept' button. When you commit the data by pressing 'Commit' button, behind the scene, system would create a dummy employee record for each new Department.

Friday, December 17, 2010

Programmatically disclosing a ShowDetailItem in a <af:panelTabbed>

In this post I'm sharing an example illustrating the programmatic disclosure of <af:showDetailItem> which is added inside a <af:panelTabbed>. Please note that while programmatically disclosing a showDetailItem, it's your responsibility to set the 'Disclosed' to false for the previously displayed item. To illustrate this, take a look at the following code snippet:

RichPanelTabbed richPanelTabbed = getPanelTabbed();
for (UIComponent child : richPanelTabbed.getChildren()) {
    RichShowDetailItem sdi = (RichShowDetailItem)child;
    sdi.setDisclosed(isThisItemToBeDisclosed(sdi));
}

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS2]

How to run this sample?

Run the test.jspx. This page displays drop down list displaying the tab names and a button close to it for disclosing  the selected tab programatically.

Saturday, December 11, 2010

Identifying the source of a Class file

Sometimes you may need to identify the jar file from where a specific Class has been loaded. This information is useful when you need to deal with some weird ClassLoader issues. The following API may help you here.
YourClz.class.getProtectionDomain().getCodeSource().getLocation();

Friday, December 10, 2010

Tips on configuring timezone for a fusion web application

You can set the timezone for a fusion web application using the time-zone element in trinidad-config.xml
Doc says:
Apache Trinidad will attempt to default the time zone to the time zone used by the client browser. If needed, you can use an EL expression that evaluates to a TimeZone object. This value is used by org.apache.myfaces.trinidad.converter.DateTimeConverter while converting strings to Date.

Please note that the above configuration is valid only for the view layer. ADF BC layer would use the time zone configured in the adf-config.xml. And this value can be retrieved by using oracle.jbo.common.DefLocaleContext.
e.g: DefLocaleContext.getInstance().getUserTimeZone();

How to make these two layers to work on same 'timezone' ?

Yes, you can do that using EL for configuring timezone in adf-config.xml. Please see the EL #{adfFacesContext.timeZone.ID} used in the following configuration. This refers the timezone used by the view layer.
#{adfFacesContext.timeZone.ID} => AdfFacesContext.getCurrentInstance().getTimeZone().getID();
In effect, this expression help us to pass the timezone from the view layer to the business service layer in a smart way.
<?xml version="1.0" encoding="windows-1252" ?>
<adf-config xmlns="http://xmlns.oracle.com/adf/config"
 ......
<user-time-zone-config xmlns="http://xmlns.oracle.com/adf/usertimezone/config">
<user-timezone expression="#{adfFacesContext.timeZone.ID}"/>
</user-time-zone-config>

Thursday, December 9, 2010

Why do I get oracle.jbo.JboException: JBO-29000 ORA-01882: timezone region not found

Some of you you might have encountered the below shown error while deploying the ADF application on a stand alone weblogic server.

oracle.jbo.JboException: JBO-29000: Unexpected exception caught:
java.sql.SQLDataException, msg=ORA-01882: timezone region  not found
at oracle.jbo.server.OracleSQLBuilderImpl.setSessionTimeZone(OracleSQLBuilderImpl.java:4964)

((OracleConnection) conn).setSessionTimeZone(regionName);

What goes wrong here?

Let us take a step back and see what happens when a client checks out an ApplicationModule to serve it's request. Please note that, on each checkout of an application module from the pool, that application module pool will acquire a connection from the database connection pool and would try to refresh the connection metadata to reflect the current use context. The timezone from the current locale context is set on the database connection by calling

oracle.jdbc.OracleConnection::setSessionTimeZone(regionName)

API Doc Says:
This method is used to set the session time zone. This method must be invoked before accessing any TIMESTAMP WITH LOCAL TIME ZONE data. Upon invocation of this method, the Jdbc driver sets the session timezone of the connection and saves the session timezone so that any TSLTZ data accessed via Jdbc are adjusted using the session timezone.

This call is equivalent to ALTER SESSION SET TIME_ZONE='regionName' command on the database for a session. If the database is not able to find the regionName from V$TIMEZONE_NAMES table( SELECT * from V$TIMEZONE_NAMES ), then you may get the error that I mentioned at the beginning.

Solution

Solution is to get rid of the unsupported default timezone. You can set the timezone either using adf-config.xml or as JVM arguments.
1. Configuring using adf-config.xml
<adf-config ....
<user-time-zone-config xmlns=
"http://xmlns.oracle.com/adf/usertimezone/config">
<user-timezone expression= "EL exp" />
</user-time-zone-config> 
2. Passing as JVM argument
Java -Duser.timezone=Asia/Calcutta


Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Wednesday, December 8, 2010

Sorting a Transient ViewObject

Transient ViewObject is a value holder for non persistable data. In this post I'm sharing a very simple example illustrating the built in sorting support for a transient ViewObject. Please note that if you have table built using a transient ViewObject, a click on the sort icon for a column in turn would invoke ViewObjectImpl::setSortBy(String sortBy) to perform in memory sorting. Whereas for a non transient VO, sorting is done by setting the OrderByClause using ViewObjectImpl::setOrderByClause(String orderByClause). This example is built based on the above theory :). I'm populating the the rows programmatically from the overridden ViewObjectImpl::executeQueryForCollection(...) and the rest is taken care by the framework. Please see Fusion Developers Guide to know the key framework methods to override for programmatic View Objects

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS2 + HR Schema]

How to run this sample?

Run the main.jspx and try sorting the table columns.

Thursday, December 2, 2010

Using oracle.jbo.domain.Array with ViewCriteria

Sometimes you may need pass multiple values to the SQL WHERE clause. Apparently you may end up in using SQL IN clause to pass values. A possible solution (using ADF BC) for such use case has been shared in one of my previous post(Using bind variable for....). This is not the only one possibility, you can realize similar use cases using oracle.jbo.domain.Array as well. This approach doesn't require the custom database function that we used in the previous post to parse the comma separated input string. In this post I'm sharing sample application built using oracle.jbo.domain.Array to pass multiple arguments to a SQL statements with IN clause.

A glance at the implementation

In this example user can search departments using comma separated values as shown in the following diagram.


Custom Converter for oracle.jbo.domain.Array

As you can see in the above picture, DepartmentName field in the search panel takes comma separated values. This inputText has a custom converter(view.CustomDomainArrayConverter) added to convert comma delimited values to oracle.jbo.domain.Array. To get a feel of it, please explore the class view.CustomDomainArrayConverter in the sample workspace - attached at the end of this post.

<af:inputText value="#{bindings.ArrayOfDeptNames.inputValue}"
        ......
        id="it1" >
<f:converter converterId="ArrayConverter"/>
</af:inputText>

ViewCriteria Definition

Search part is implemented using a ViewCriteria where the attribute DepartmentName is mapped to a bind variable operand(ArrayOfDeptNames) of type oracle.jbo.domain.Array. Please take a look at the bind variable 'ArrayOfDeptNames' definition in the following diagram.


<Variable
    Name="ArrayOfDeptNames"
    Kind="viewcriteria" ColumnType="CHARTABLETYPE"
    Type="oracle.jbo.domain.Array"
    ElemType="java.lang.String">   
</Variable>

The ColumnType="CHARTABLETYPE" in the above definition is mapped to a custom database type to hold the value types used in the query. Obviously, to run this sample you may need to have this type defined in your database.

CREATE OR REPLACE TYPE  "CHARTABLETYPE"  as table of varchar2(4000);

Generating Native SQL for CriteriaItem having Array with IN clause

I overrode the getCriteriaItemClause(ViewCriteriaItem vci) to generate the native SQL for Array with IN clause. Please see the following code snippet.

@Override
public String getCriteriaItemClause(ViewCriteriaItem vci) {
  if (vci.getAttributeDef().getName().equals("DepartmentName") &&
    vci.getViewCriteria().getName().contains("DepartmentsViewCriteria")) {
    if (vci.getViewCriteria().getRootViewCriteria().isCriteriaForQuery()) {
    return getINClauseForDatabaseUse(vci);
    } else {
    return getINClauseForCache(vci);
    }
  } else {
    return super.getCriteriaItemClause(vci);
  }

}

protected String getINClauseForDatabaseUse(ViewCriteriaItem vci) {

  String whereCluase = "1=1";
  if (getArrayOfDeptNames() != null) {
    whereCluase =
        this.getEntityDef(0).getAliasName() + ".DEPARTMENT_NAME IN (SELECT * FROM TABLE(CAST(:ArrayOfDeptNames AS CHARTABLETYPE)))";
  }
  return whereCluase;
}


protected String getINClauseForCache(ViewCriteriaItem vci) {
  String whereCluase = "1=1";
  return whereCluase;
}


Search form used in this example is built by dragging and dropping the ExecuteWithParams from the Data Control Pallet. DepartmentsViewCriteria, that I explained initially, is mapped to DepartmentsView1 instance in the AppModule.

The final SQL generated at run time may look like as shown below.

SELECT Departments.DEPARTMENT_ID,   Departments.DEPARTMENT_NAME, .... Departments WHERE
( ( Departments.DEPARTMENT_NAME IN (SELECT * FROM TABLE(CAST(:ArrayOfDeptNames AS CHARTABLETYPE)))) )

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS2 + HR Schema]

How to run this sample?

1. Unzip the source to your local drive.
2. Setup the required DB objects in your local schema(HR) by running the \select_in_list.sql
3. Run the main.jspx. This page displays query field (which takes comma separated values) and a result table.



Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html