Monday, March 22, 2010

Understanding Task Flow Transaction and Savepoint Support in Oracle ADF

This article is based on Steve Muench's "Not Yet Documented ADF Sample #140" and you can download his sample code here.

JDeveloper 11.1.1.2.0 is used in this demonstration. To get what is shown here, you need to modify code snippets in PageTemplate.jspx and create-department.xml on the ViewController project.

Change PageTemplate.jspx from :
<f:facet name="center">
<af:panelHeader text="#{attrs.PageTitle}"
inlineStyle="background-color:White;" id="pt_ph1">
<f:facet name="context"/>
<f:facet name="menuBar"/>
<f:facet name="toolbar"/>
<f:facet name="legend">
<af:facetRef facetName="ButtonArea"/>
</f:facet>
<f:facet name="info"/>
<af:facetRef facet/Name="MainArea"/>
</af:panelHeader>
</f:facet>

to:
<f:facet name="center">
<af:panelHeader text="#{attrs.PageTitle}"
inlineStyle="background-color:White;" id="pt_ph1">
<f:facet name="context"/>
<f:facet name="menuBar"/>
<f:facet name="toolbar"/>
<f:facet name="legend">
<af:facetRef facetName="MainArea"/>
</f:facet>
<f:facet name="info">
<af:facetRef facetName="ButtonArea"/>
</f:facet>
</af:panelHeader>
</f:facet>
Change create-department.xml from:
<task-flow-return id="Cancel">
  <outcome>
    <name>Cancel</name>
    <rollback/>
  </outcome>
</task-flow-return>

to:
<task-flow-return id="Cancel">
  <outcome>
    <name>Cancel</name>
    <rollback/>
    <restore-save-point/>
  </outcome>
</task-flow-return>


Before proceeding, adjust the properties of the scott connection in the Application Resources zone of the Application Navigator until you can successfully test a connection to a SCOTT schema. If you need to create the tables, use the provided CreateDeptEmpTables.sql.

Task Flow Transaction

Task flow transaction is different from database transaction (I mean the transaction returned by am.getDBTransaction()). Each task flow is associated with a data control frame. A data control frame is the container associated with a task flow that contains multiple data control instances. The ADF Model layer exposes the DataControlFrame interface to manage a transaction in which the data controls within the frame participate. DataControlFrame interface exposes methods such as:
  • beginTransaction()
  • commit()
  • rollback()
  • createSavepoint() -- Note that this is the savepoint maintained by the Oracle ADF Controller and is similar to database-level savepoint
  • isTransactionDirty()
To handle nested task flows, task flow transaction provides a high-level implementation using ADF Controller transaction across task flow boundaries. As a Fusion application developers, it is essential to understand what happens when commit or rollback in a nested task flow is issued.

Data changes can happen at different levels. Let's use Cancel as an example. With the cancel operation, data changes could be in four possible states:
  1. The user didn't change a value
  2. The user changed a value but this hasn't been submitted to the model
  3. The user has changed a value and the change has been submitted to the model but hasn't been persisted to the database.
  4. The user has changed a value; the change has been submitted to the model; and the change has been persisted to the database.
In this article, we will examine what happens when task flow returns with either commit or rollback property set. Specifically, we will discuss in which layer that data have been updated:
  • UI layer
  • Model layer
  • Database layer

Task Flow Transaction

There are four transaction options that a bounded task flow can use (note that unbounded task flow doesn't support transaction):
  1. No Controller Transaction: The called bounded task flow does not participate in any ADF Controller transaction management.
  2. Always Begin New Transaction: A new transaction starts when the bounded task flow is entered, regardless of whether or not a transaction is in progress. The new transaction completes when the bounded task flow exits.
  3. Always Use Existing Transaction: When called, the bounded task flow participates in an existing transaction already in progress.
  4. Use Existing Transaction if Possible: When called, the bounded task flow either participates in an existing transaction if one exists, or starts a new transaction upon entry of the bounded task flow if one doesn't exist.
There are three task flows in the sample implementation:
  1. manage-employees task flow: it has its transaction property set to Always Begin New Transaction to indicate that it should only be used as a top-level task when no other current transaction is in effect.
  2. modify-employee task flow: it has its transaction property set to Always Use Existing Transaction to indicate that it only makes sense to be called as part of an existing transaction (but cannot be called on its own), since it requires parameters to work correctly.
  3. create-department task flow: it has its transaction property set to Use Existing Transaction if Possible which allows it to be used either as a top-level transactional flow, or else as a part of another task flow with a transaction already in effect.

Data Control Scope

For each task flow that has data to be committed, you need to set data-control-scope property in the task flow in addition to the transaction property.

data-control-scope property is used to specify whether data control instances are shared between the calling and called task flows. You must set a data-control-scope value of either shared or isolated on the called bounded task flow. shared is the default value.

Based on the settings of data-control-scope and transaction properties, the following table summarizes the runtime behaviors of created task flow:






























Transaction
Setting
Share
Data Control Scope

Isolate
Data Control Scope
No Controller TransactionThe DataControlFrame is shared without an open controller transactionA new DataControlFrame is created without an open controller transaction
Always begin new transactionIf a transaction is already open, throws an exception; otherwise, begins a new transaction.Always begins a new transaction.
Always
Use Existing Transaction
Throws an exception if the
transaction is not already open.
Invalid.
Use
Existing Transaction if Possible

Begins a new transaction if one
is not already open.
Always begins a new transaction.
The most important rule to remember is:
  • Data control instances cannot be shared across more than one Controller transaction at the same time.

Examining the Runtime Behavior of Task Flows

In this article, we'll examine an application flow that consists of all three task flows in the sample code:
  1. manage-employees task flow
  2. modify-employee task flow
  3. create-department task flow
From the home page, select Manage Employees:

This will launch manage-employees task flow which has the following setting:
  • transaction option = "Always Begin New Transaction"
  • data-control-scope = "shared task flow with calling task flow" (This is the default value)
A new data control frame was created for the ADF unbounded task flow. manage-employees task flow will begin a new transaction and share the data control frame with calling task flow (i.e., unbounded task flow).

On Manage Employees page, specify Smith in the search field and click the arrow button. This brings up Smith's record in the table:


Let's click Edit button and this will launch modify-employee task flow which has the following settings:
  • transaction option = "Always Use Existing Transaction"
  • data-control-scope = "shared task flow with calling task flow"
Before we modify any value on Smith's record. Let's click Add New Department button. This will launch create-department task flow which has the following setting:
  • transaction option = "Use Existing Transaction if Possible"
  • data-control-scope = "shared task flow with calling task flow"
Up to now, all three task flows share the same data control frame and transaction. On Create Department page, let's create a new department named Finance:

Notice that there are two buttons (i.e., OK and Cancel) which can trigger two different Task Flow Return Activities, which are set up this way:
  • OK button
    • End Transaction = "commit"
    • Restore save point = "false"
  • Cancel button
    • End Transaction = "rollback"
    • Restore save point = "true"
Because create-department task flow participates in an existing transaction, an implicit savepoint will be created when the task flow is entered. So, what Cancel needs to do is just restore to the previous save point and what OK needs to do is nothing because we don't want to commit the whole transaction yet.

So, the new Finance record will be seen in:
  • UI layer and model layer, but not database layer if OK button is pressed
  • None of the layers if Cancel button is pressed
Let's click OK button and return to the Edit Employee page. Notice that the new department Finance is shown on the dropdown list:
On Edit Employee page, let's enter "10" in Comm field. After doing that, we can click on either OK or Cancel button. For OK button, it will trigger a Task Flow Return Activity with the following settings:
  • End Transaction = "none"
  • Restore save point = "false"
while Cancel button will trigger a Task Flow Return Activity with the following settings:
  • End Transaction = "none"
  • Restore save point = "true"
Because modify-employee task flow is set up with a transaction option: Always Use Existing Transaction, an implicit save point will be saved when the task flow is entered. So, what Cancel needs to do is just restore to the previous save point and what OK needs to do is nothing because we don't want to commit the whole transaction yet.

If you click on Cancel button at this point, it will restore to a previous save point when modify-employee task flow is entered. That means our new Comm field and new Department record will all be canceled.

If you click on OK button at this point, on the Manage Employees page, you will find the new Comm value (i.e., 10) for Smith.


Notice that new changes have not been persisted to database layer yet. The new Comm value and new Department record have only been saved in model layer.

On Manage Employees page, there are also two buttons: OK and Cancel. For OK button, it will trigger a Task Flow Return Activity with the following settings:
  • End Transaction = "commit"
  • Restore save point = "false" (default)
So, if you click on OK now, all changes will be persisted to the database layer.

For Cancel button, it will trigger a Task Flow Return Activity with the following settings:
  • End Transaction = "rollback"
  • Restore save point = "false" (default)
So, if you click on Cancel now, all changes will be rolled back.

Page Refresh

In this article, we saw changes on one page was reflected on another page (i.e., referred to as UI layer update). Sometimes this happens automatically and sometimes you need to set it up appropriately. The issue of how to refresh a page or region to reflect changes in model layer or database layer is another interesting topic.

Read More...

  1. A Ride at the OK (or Cancel) Corral by Steve Muench
  2. Oracle Fusion Middleware Fusion Developer’s Guide for Oracle Application Development Framework 11g Release (11.1.1)
  3. Book Review: Developing Web Applications with Oracle ADF Essentials (Xml and More)

Tuesday, March 16, 2010

Oracle ADF Task Flow in a Nutshell

The concept of unbounded and bounded task flows is new to JavaServer Faces (JSF) and is an extension exclusively available to Fusion web application developers who use the ADF Controller, which extends the JSF navigation model, for their application flow handling.

In this article, we will describe what a task flow is and compare unbounded task flows with bounded task flows.

ADF Task Flow

ADF task flows provide a modular approach for defining control flow in an application. Instead of representing an application as a single large JSF page flow, you can break it up into a collection of reusable task flows. Each task flow contains a portion of the application's navigational graph. The nodes in the task flows are activities. There are two types of activities: visual vs. non-visual. The transitions between the activities are called control flow cases.

Because only pages and page fragments are displayed in a browser, ADF Controller continues any navigation to non-visual activities until a visual activity is reached. This turns non-visual activities into intermediary actions that are performed on the navigation path between two views.

Task flows don't own the pages they reference. All ADF Faces pages are located in the web project HTML root directory or a subdirectory of it. Independent of whether or not a page is referenced in a bounded or unbounded task flow, or both, it is always directory accessible from the browser URL field. Developers should be aware of this and implement a protection strategy, such as one through ADF security, that prevents users from directly accessing pages that they are not authorized to access.

At design time, developers need to consider the following issues with task flows:
  • Whether to use a shared data control frame or its own, isolated data control frame (this creates a new application module connection). Note that a data control frame is the container associated with a task flow that contains data control instances.
  • Whether a new transaction is to be begun or not
  • Whether a task flow should allow reentry or not
  • Whether an unbounded task flow or a bounded task flow should be used

Bounded Task Flow

Bounded task flow represents modular and reusable application flows with a defined entry (i.e., default activity) and zero to many defined exit points (i.e., task flow return activities). They can be called from other task flows, referenced from a browser URL or embedded as a region in a view (see Andrejus Baranovskis' article for an example). They support reuse, parameters, transaction management and reentry.

In addition, bounded task flows have the following features:
  • Operate within their own private memory scope--page flow scope
  • Are loaded lazily at runtime
  • A new instance of TaskFlowContext (can be accessed using EL ${controllerContext.currentViewPort.taskFlowContext}) will be created each time a bounded ADF flow is entered. This context:
    • Manages the lifespan of all DataControl instances within the bounded task flow
    • Holds the information about the task flow ID and whether or not it contains uncommitted data
  • Don't support multiple transactions when sharing the data control frame with parent flow
  • To call a bounded task flow directly from a URL, the default activity must be a view activity
  • Can be set to be critical (i.e., dictates the framework to create an implicit save point when entering a bounded task flow. Also helps to prevent users from closing the browser window or browser tab if uncommitted data is present).
  • If protected by ADF Security, authorization is checked first.
  • You can create train-based activities in a bounded task flow and only one train is allowed in each.
Bounded task flow should be used if it:
  • Should be reused in same or other applications
  • Should run as part of a page within a region container
  • Requires an isolated transaction context
  • Changes data and then either commits or rollbacks on exit
  • Has a requirement for a single entry point

Unbounded Task Flow

A Fusion web application always contains an ADF unbounded task flow, which contains the entry point or points to the application. Its XML configuration file (i.e., adfc-config.xml) is automatically created when building a new application using the Fusion Web Application (ADF) application template or when adding the ADF Page Flow Technology Scope to an existing or new web project. There will always exist a single instance of unbounded task flow at runtime, even if there is no activity added to it.

A unbounded task flow has the following features:
  • You cannot declaratively specify input parameters for it.
  • It cannot contain a default activity (i.e., an activity designated as the first to run in the task flow). This is because an unbounded task flow does not have a single point of entry.
  • It can be configured by one or many configuration files that are parsed and loaded the first time the user starts the application.
  • View activities of an unbounded task flow can be configured bookmarkable
  • Managed beans that are in session or application scope should be configured in the unbounded task flow definition.
  • You cannot create a train from activities in an unbounded task flow.
  • You cannot use a task flow template as the basis for creating a new unbounded task flow

You typically use an unbounded instead of a bounded task flow if:
  • You want to take advantage of ADF Controller features not offered by bounded task flows, such as bookmarkable view activities.
  • The task flow will not be called by another task flow.
  • The application has multiple points of entry. The task flow can be entered through any of the pages represented by the view activity icons on the unbounded task flows.
  • You want to bookmark more than one activity on the task flow.
  • For application flows that are not restrictive on where a user enters the flow.

Read More...

  1. Oracle Fusion Middleware Online Documentation Library 11g Release (11.1.1.2)
  2. Oracle Fusion Developer Guide by Frank Nimphius and Lynn Munsinger
  3. Oracle ADF Controller
  4. Open Source sample applications for Oracle Fusion Middleware technology
  5. ADF Task Flow in JDeveloper 11g

Monday, March 15, 2010

Database Connections in JDeveloper 11

JDeveloper 11 Technology introduces a new way to create and manage database connections. Now Database connections can be defined in two ways:
  1. For an application--You can create a database connection owned by and deployed with the current application, which is listed in Application Resource
  2. For the IDE as a whole--You can also create a connection that can be added to any application (i.e., IDE Connections), which is listed in both Resource Palette and Database Navigator.

Before you can use an IDE connection, you need to add it to your application as shown in the figure:

Creating Database Connections

If the data sources of your business components are from database, you need to create database connections to access them in your applications. Creating database connection can be either initiated by the user or triggered by other events such as creating the first business component in your application or importing an ADF library JAR which includes a connection.

In this article, we'll look at these different scenarios.

Creating the First Business Component

When you create the first business component, say, use the following steps:
  • New > Business Tier > ADF Business Components > Business Components from Tables
JDeveloper will prompt you with an "Initialize Business Components Project" dialog because it detects that your selected project has not yet been initialized for Business Components. In this initialization step, you need to specify the database connection as shown here:

Importing an ADF Library JAR with Connections

If the project you are packaging into an ADF Library JAR includes a database connection, that information can be included in the JAR and may be available to the consuming project. Oracle ADF uses connection architecture which defines a connection as two parts:
  1. Connection name
  2. Connection details (or endpoint definition).
JDeveloper will present the producer of the JAR the option to package the connection name only or to include connection details with the connection name.

If a connection is present in the project, the packaged ADF Library JAR will contain a jar-connections.xml file and a jar-adf-config.xml file. They will be added to the META-INF directory. The jar-connections.xml file contains the connection name and other relevant connection information. The jar-adf-config.xml file stores the information about the credentials used for the connections. If connection credentials were also specified, then a jar-credential-jazn-data.xml will also be included for the credential store.

If the connection defined in the JAR is fully configured and there are no connection name conflicts with the consuming project, a new application connection will be added.

If you don't create extra business components after importing the packaged ADF Library JAR, you don't need to go through Initialize Project for Business Components step as described below. Just make sure your database connection settings are correct by opening it in Application Resources panel.

Initialize Project for Business Components


If you choose, you can create database connections in advance and initialize your project for business components using them.

As described before, IDE connections are located in either Resource Palette or Database Navigator. You can create new database connections from context menu as shown below:


After creating your IDE connections, you then add them to your application. Besides IDE connections, you can also create database connections for your application only. All database connections used by your application will be listed in Application Resources panel.

Besides creating database connections from context menu, you can also create database connections (in either IDE or application scope) from New Gallery using the following steps:
  • New > General > Connections > Database Connection

After creating database connections in your application, the next step is to initialize your project for business components. Selecting model project from your application, open its property from the context menu.
Selecting Business Component tab, check "Initialize Project for Business Components" and then select the database connection to be used for creating business components. Note that this connection is used specifically for:
  • JDeveloper Design-time and
  • Business Components Tester

and it is different from the one used at runtime when your web application is deployed to an Application Server. That's another topic for the next time.

Read More...

  1. Database connection in ADF BC 11g
  2. Oracle Fusion Middleware Online Documentation Library 11g Release (11.1.1.2)
  3. Database Connections in JDeveloper 11
  4. Oracle JDeveloper