Various code samples published by Oracle relating to the Oracle Application Development Framework (ADF)
This project contains an historical set of code samples relating to various versions of the Oracle Application Development Framework (ADF). Many of these samples where previously hosted on Java.net and will relate to various versions of ADF.
This repository is laid out with each sample expanded in it's own subdirectory under the root /src directory. This allows you to browse the code without downloading. Each sample is also available as a downloadable zip from the releases area
DynamicCredentialsNoController.jws
is a "Model 1" style application with no controller layer; DynamicCredentialsStruts.jws
is Struts-based; DynamicCredentialsTrinidad.jws
is a 10.1.3 ADF Faces application that's been migrated to use the Apache Trinidad components; and DynamicCredentialsRichFaces.jws
uses the new ADF Faces Rich Client components that are new in 11g. Each project references the same Model
project and FwkExtensions
project. The generic framework extension classes involved in implementing the solution live in the FwkExtensions
project. The Model
project contains the simple ADFBC components used in the example: a TestModule
application module with a single DeptView
VO instance of a DeptView
view object based on the DEPT
table in the SCOTT schema. While updating the example for 11g, I took the liberty of using two new features that allowed me to simplify and streamline the implementation. The first of these was using the new ErrorHandlerClass
attribute on the root <Application>
element of the DataBindings.cpx
file in each of the client web projects in order to register a custom ADFM error handler implementation (test.DynamicJDBCErrorHandlerImpl
). This error handler is designed to notice the DMLException
thrown by a failed database connection attempt (due to incorrect username/password combination), and it throws that particular exception rather than simply caching it on the binding container. The second new feature employed is the ability to register a global customization to the ADF Page lifecycle by using a META-INF/adf-settings.xml
file in each client web project. Each project registers the test.DynamicJDBCPagePhaseListener
in order to conditonally execute some custom logic before the ADF "prepare model" phase of the lifecycle. This custom logic references the data controls in the current page definition, causing them to be checked out of the AM pool (if they were not already checked out during the current lifecycle) before the JSF page rendering begins. This allows the error handling code to catch the failed connection attempt in time to redirect the user to the login page before the rendering phase begins. The web.xml file for each ViewController project contains a context parameter (RedirectToLogin
) that defines the login page to redirect to for that particular web application. The JSF project contains a second context parameter FacesURLPattern
as well. Due to Bug# 5080057, setting the jbo.ampool.sessioncookiefactoryclass
property in the configuration is not correctly saved to the bc4j.xcfg
file. So, in order to configure a custom session cookie factory class as required to repurpose the code in this example in your own projects, you'll need to hand-edit the bc4j.xcfg
file to insert the child element <jbo.ampool.sessioncookiefactoryclass>test.DynamicJDBCSessionCookieFactory</jbo.ampool.sessioncookiefactoryclass>
inside the appropriate <AppModuleConfig>
element for the configuration in question.onReturnFromDialog()
in the BrowseEmployees
backing bean is configured as the ReturnListener
for the button. It calls a refreshCurrentPage()
helper method to accomplish the page refresh. It works in combination with setting the partialSubmit
property and the useWindow
properties to true
on the (Add New Employee) button in the page. The onSaveNewEmployee()
method is required for the ActionListener
on the (Save New Employee) button since we want to conditionally return from the dialog, only when no errors occur during the execute operation on the "Commit" action binding. Using the declarative <af:returnActionListener>
, we couldn't accomplish this conditional behavior. As a bonus, the example also illustrates edit and delete behavior as well. By choice, the add a new employee is performing an immediate commit, while the edit and delete operations need to be explicitly committed or rolled-back by the user.SearchPage.jspx
built using the technique described in section 18.3.5 How To Create Search and Results on the Same Page of the ADF Developer's Guide for Forms/4GL Developers that has been enhanced with a small amount of backing bean code that customizes the ADF page lifecycle to add a JSF FacesMessage
in case the EmployeesResultsIterator
returns either no rows or too many rows (with the limit hard-coded for this example to 10). The SearchPage
backing bean inherits from the BackingBeanBase
class as described in section 10.5.4.3 Using Custom ADF Page Lifecycle to Invoke an onPageLoad Backing Bean Method of the Dev Guide (simplified slightly by using my favorite EL helper class). The rendered
attribute on the af:table
showing the search results in the SearchPage.jspx
uses an EL expression to cause the table to render only when the number of rows found is between 1 and the maximum allowed (10).NoDatabaseApplicationPoolImpl
is a custom application module pool implementation class. The NonDatabaseConnectionStrategy
is a custom connection strategy class . The CustomSessionImpl
is a custom session class, which returns a custom transaction handler factory. The CustomTxnHandlerFactoryImpl
is a custom transaction handler factory used to return an instance of the custom CustomTxnHandlerImpl
class, which is a custom transaction handler implementation. The SessionClass
configuration property is set to the name of the custom session implementation class, which in turn bootstraps the custom transaction handler factory and custom transaction handler. The PoolClassName
configuration property is set to the name of the custom application module pool implementation class. This class overrides the isSupportsPassivation()
method to return false, indicating to the runtime that this particular application module pool does not support passivation. The jbo.ampool.connectionstrategyclass
configuration property is set to the name of the custom connection strategy class which overrides the createApplicationModule()
method to set some key properties in the environment hashmap before calling super. The StaticVO
is a static view object that returns rows whose data is in the ModelBundle.properties
file.af:convertNumber
tag supports a number of attributes that allow you to customize the detail error message when conversion fails. For the basic number conversion failure, this attribute is named convertNumberMessageDetail
. In order to reference a resource bundle message, normally you would expect to be able to use an f:loadBundle
tag in your page to define a variable for a Map of the resource bundle strings, and then reference the message using an EL expression like #{res.NOT_A_NUMBER}
. The complication is that the resource bundles loaded with loadBundle
are only available during the page rendering phase, but not during the validation phase. So, in order to drive the convertNumberMessageDetail
from a message bundle, I needed to borrow some of the code from the JSF reference implementation's loadBundleTag
class that exposes a resource bundle as a Map, and I put it in the AppliationMessageMap
managed bean class in this example. I've configured that to be a session-scoped managed bean and configured its basename
managed property to have the value view.resources.JSFMessages
in faces-config.xml
. This allows me to reference my custom error message for "Field 2" in the demo, in its f:convertNumber
tag's convertNumberMessageDetail
attribute using the EL expression AppMessages.messages.NOT_A_NUMBER
. To see the custom error message, try typing a value like "adf" into Field 2 and clicking (Submit). You can run the demo with your browser set to prefer "Italian [it]" to see the custom error message translated in Italian.AppModule
application module's custom java class, and that both the processSelectedEmployees()
and processSelectedDepartments()
methods are exposed on the client interface so they are available for declarative data binding in the data control palette. Notice they these accept a java.util.Set
(which we expect at runtime to contain oracle.jbo.Key
objects. The reason we're not using Java generics like Set<Key>
in the method signature is that the ADFBC client interface mechanism does not yet support using generics in the method signature. The implementation of these methods processes the set passed in, casting each object in the set to a Key
and then using the findByKey()
on the appropriate view object instance in the data model to find the row by its key and then act on it. There are two pages in the demo view1.jspx
and view2.jspx
. Start by looking at the view2.jspx
page, which includes a table of DeptView
rows from the view object instance named Departments
. Importantly, notice that to enable the multi-select on the table three required steps have been taken: (1) The rowSelection
attribute is set to "multiple
", (2) the selectedRowKeys
attribute has been removed, and (3) the selectionListener has been removed. Steps (2) and (3) are automatically setup to support single-row selection and must be removed whenever you enable multiple row selection in the table, or else the multiple selection will not work correctly. The custom processSelectedDepartments
method was dropped from the Data Control Palette onto the page as a button, and the EL expression for the method argument is set to ${View2.selectedAdfRowKeys[View2.myTable]}
which makes use of a generic helper backing bean that the ViewController project includes to simplify working with multiple selection in tables, trees, and treeTables. The View2
backing bean is defined in adfc-config.xml
and rather than using a specific backing bean class for the View2 page, its using the generic helper class MultiSelectSupportBackingBean
. That bean conveniently contains a property named myTable
of type RichTable
which is designed to be referenced by the binding
property of a page containing a RichTable where you'd like to perform some processing on its multiple-selection. In fact, if you look at the binding
property of the af:table
component in the view2.jspx
page, you'll see that it's set to this property using the expression #{View2.myTable}
. So the ${View2.selectedAdfRowKeys[View2.myTable]}
expression used as the parameter value of the declarative method action binding accesses the selectedAdfRowKeys
Map-valued property of the backing bean, passing the View2.myTable
instance of the RichTable on that page as the map key. As you can see in the MultiSelectSupportBackingBean
class, the selectedAdfRowKeys
is an anonymous subclass of java.util.Hashmap
that overrides the get()
method to perform a custom operation when the EL expression evaluator tries to access the map's properties. In effect, this pattern allows us to introduce something that works like a custom function that accepts one argument into our EL expressions. In this case, the argument is the UI component for which you'd like a Set of selected Keys. The view1.jspx
page shows the same generic MultiSelectSupportBackingBean
class in use as the page's View1
backing bean, and three different instances of method action bindings in the page definition for view1.jspx
are used to invoke the processSelectedEmployees()
custom method with the appropriate UI component passed as the "argument" to the EL expression to reference the Set of selected Keys. A last thing to note about the example is how the EmployeeHierarchy
view object instance in the data model is setup. Notice that the EmpView
view object defines a view criteria named EmployeesWithNoManager
that finds employees that have a Mgr
attribute equal to null. The EmployeeHierarchy
view object instance was added to the data model in the application module editor, then I used the (Edit) button just above the Data Model tree to edit that view object instance to apply the EmployeesWithNoManager
view criteria to it. This means that initially the view object will only show employees with no manager (e.g. KING). Then, due to the presence of the test.model.links.Manages
view link between EmpView
and itself, based on the Empno->Mgr
relationship, the direct reports of the employees with no manager will appear as children in the tree and treeTable due to the related tree binding rules that uses the DirectReports
view link accessor attribute to access the nested child collections of direct reports rows at each recursive level. Notice as well that there are no view link instances required in the data model to work with the tree or treeTable since the related tree bindings use the view link accessor attributes to get detail information, and not data model view instances linked via view link instances in the data model. I highly recommend reading section 35.1.3 Understanding View Link Accessors Versus Data Model View Link Instances to help understand the important distinction.BaseADFWebApp
workspace in the download contains a simple, one-page DeptView data entry form. Its Model
project contains a test.Dept
entity object, test.DeptView
view object, and a test.AppModule
application module containing an instance DeptView1
of test.DeptView
. Its ViewController
project contains a Departments.jspx
page with the data entry form. The Model
project contains a ADFBC deployment profile to deploy the ADFBC components as a simple java archive. The ExtendsAndSubstitutes
workspace contains a ExtendsProject
project which has imported the test.Dept
entity from the simple ADFBC JAR archive created in the other workspace's Model
project. It then has created an extended entity object extendsproject.DeptEx
which extends the test.Dept
entity, overrides its Loc
attribute, and adds an attribute-level validator on Loc
to prevent the value from being the single, uppercase letter "X". The ExtendsProject.jpx
defines the component substitution metadata saying that dept.Dept
should be substituted with extendsproject.DeptEx
. This metadata was defined using the Substitutions detail panel of the Business Components project properties. In addition, the Extends Project
contains a file ExtendsProject.cpx
to workaround bug 6629321, whose contents can be anything as long as it is a well-formed XML file. In the example, the workaround file contains the single element <workaround_for_bug_6629321/>. The ExtendsProject
project contains another ADFBC deployment profile to deploy the extended components as a simple JAR archive file. Finally, the ViewController
project adds the runtime Java VM argument in its Run/Debug profile of -DFactory-Substitution-List=ExtendsProject
to use the ExtendsProject.jpx
file found in the classpath at runtime as the component substitution list. The ViewController
project also includes a project-level library defined to include the two simple Java archive files for the extended components. To see the effect of the substituted components, run the Departments.jspx
page and try to enter the value of "X" for the Loc
attribute of any row in the DeptView
view object's results and press (Commit). You'll see the validation error added by the substituted extendsproject.DeptEx
component to know that the component substitutions are working.WHERE
clause bind variable, leveraging the Oracle database's CAST AS
operator to achieve a variable number of values in a SQL IN
clause by treating the values in the array as a nested table. Run the CreateTableOfVarcharType.sql
script before running the example, which features example of using view objects with both the Oracle Positional and Oracle Named bind variable styles. The latter includes a workaround in the EmpViewWithNamedBindingStyleImpl
class' overridden create()
for Bug 5849504, where the view object editor fails to correctly test the syntax of the SQL statement involving an array-valued named bind variable and the TABLE
and CAST AS
clauses.CreateTypes.sql
script before using the demo to create the SCORED_KEY
and SCORED_KEYS
types in the database. The ViewObjectImplWithScoredKeyBindVariable
framework extension class centralizes the code required to work with the bind variable of type oracle.jbo.domain.Array
. In this case, its code is binding the SCORED_KEYS
type (which is a TABLE OF SCORED_KEY
) as an Array of SCORED_KEY
types. Both the ReadOnlyScoredEmployees
and EntityBasedScoredEmployees
view objects inherit this helper code from this class. The setScoredKeysArray()
method in the custom view object class of both view objects calls the helper method newScoredKeysArray()
in the superclass to create the Array of STRUCT and set the value of the view object's bind variable. It expects to receive a List
of Map
objects, expecting each Map in the List to contain Map keys "Score" and "Key". The view objects both use the JDBC positional binding style, indicating the (zero-based) integer position of the bind variables as additional metadata in order to still work with the bind variable as a named bind variable in the view object API's. Run the TestClient
class to see the results of joining in the static data with the EMP table.newValue
expression in the view accessor bind variable default Groovy expression - you need to either use a groovy expression validator, a Java method validator, or a custom validation rule. This workspace provides examples of the first and the last of these three options. The Java method validation alternative is left as an exercise to the reader. The Emp EO has two view accessors: CheckDivisibilityBy3
based on view object definition test.ValidationViewObject
, configured to have the VarModulo
value set to 3, and CheckDivisibilityBy7
based on the same view object, configured to have the VarModulo value set to 7 The attribute-level Groovy validator on the Sal
attribute sets the value of the VarValue
bind variable on the view accessor, then executes the query. It returns value if the view accessor's first()
function returns a non-null result (i.e. at least one row was found). The example also illustrates a more generic approach that doesn't rely on Groovy. I created a new, reusable validation rule called AttrLevelViewAccessorValidator
. I've used an instance of it on the Comm
attribute, declaratively configuring the validation rule beans two properties: attrNameToBindNewValueTo
= "VarValue" and viewAccessorName
= "CheckDivisibilityBy7". Run the AM in the tester, and verify that you must enter a Sal value that is divisible by 3 and a Comm value that is divisible by 7. Of course, the example view object would normally be a more interesting query with one or more bind variables going against a table other than DUAL
, but this simple query hopefully will get the basic idea across. If the value being validated were the key attribute of the target view object, you could have used the "Key Exists" validator based on a view accessor instead. This approach is valid when the query you need to validate needs to lookup something that is not the key attribute in the target VO used for validation.CustomViewObjectImpl
framework extension class that adds an interesting feature to any view objects that extend it. It allows such view objects to have named bind variables whose value will be automatically derived from an attribute in the current row of another view object. In the example, the Globals
view object is a transient view object with no query, one updateable Number attribute named DepartmentNumber
, and a max fetch size set to zero ("No Rows" setting on the "Tuning" panel). The ExampleModule
application module overrides the prepareSession() method to insure there is a blank row in the "Globals" view object instance. The Employees
view object has a named bind variable TheDeptno
that has a bind-variable-level custom metadata property named VOAttributeReference
set to the value Globals.DepartmentNumber
. Alternatively, the implementation supports setting the view object bind variable to an expression at the view instance level by setting an application module custom property named *ViewInstanceNAme*_*BindVariableName*_VOAttributeReference
. This allows you to use the same view object multiple times and have different automatic value expressions for different view object instances. At runtime, the CustomViewObjectImpl
's overridden bindParametersForCollection()
method detects any named bind variables with such a custom property set, evaluates the value of the indicated attribute in the current row of the named view object instance, and automatically binds that evaluated value for the bind variable's value. The view object's query has a WHERE clause that accommodates the value of the bind variable bind both NULL or non-NULL. When you run the JSF page, type in a department number and click (Execute) to narrow the list down to only those departments.<af:query>
component's search when certain conditions are true about the values the user has entered into the search form. Run view1.jspx
. After selecting a value for Dname
and Loc
in the search form, the query is automatically executed without the user's having to press the (Search) button. The View1Bean
is the backing bean for the view1.jspx
page in the example. The <af:query>
component's queryOperationListener
property is set to an EL expression that "wires" it to fire the onQueryOperationPerformed
method in the backing bean. The backing bean's event handler code conditionally executes code if the query operation is a criterion update. It accesses the view criteria that is the model-layer object that is bound to the search form and peeks at the values in the Dname
and Loc
view criteria items. If it notices that the user has filled in both criteria items, then it creates a new QueryEvent
and queue's it up for the same <af:query>
component on which the QueryOperationEvent is firing. To ensure that the search form renders the Dname
and Loc
items in the search form with their autoSubmit property set to true, the autoSubmit
UI hint is set to true on both Dname
and Loc
attributes in the DeptView
view object.DeptView
which is an entity-based view object, one named DeptViewNonEntityBased
which is a read-only, non-entity-based view object, and one named TransientViewObject
that is programmatically populated and not based on a query. For read-only SQL-based view objects and transient view objects, you can prevent a rollback from clearing the view row cache by overriding both the beforeRollback()
and afterRollback()
method and commenting out the call to super
. For an entity-based view object, you need to override these two methods as well as call the setClearCacheOnRollback()
method on the oracle.jbo.Transaction
interface, passing false
. Note that this technique applies only to queried rows and not to newly added rows. This is due to the fact that during a rollback, as noted in the state diagram in section 9.2.5 Understanding Entity Objects Row States of the ADF Developer's Guide for Forms/4GL Developers, any entity object with 'New' status is removed (which transitions its status to 'Dead'). Note that the TransientViewObject
is populated by the prepareSession()
method of the application module, and that on its "Tuning" panel in the view object editor it is set to never query rows by using the "No Rows" setting in the "Retrieve From Database" section. This is equates to declaratively setting the max fetch size of the view object to zero.CreateTable.sql
script to create the DEPT_WITH_BOOLEAN
table, then run Example.jspx
. When you commit, the "T"/"F" value in the SELECTED
column in the table will reflect the change to the selected radio group button.TestPage.jspx
with a tree binding based on a Departments
view object instance of type DeptView
. The WorksInDeptLink
view link defines a view link accessor attribute named EmpView
that is used by the tree binding to access the detail rowsets of employees for each department. The DeptView
view object defines a custom method named setLowHighSalaryRangeForDetailEmployeesAccessorViewObject()
that is exposed on the view object's client interface. The TestPage.jspx
includes a declaratively-bound parameter form for invoking this method, passing in values for a low-salary and high-salary range. This method accesses the view object instance that the system creates at runtime to support the view link accessor accesses, and sets the values of the two named bind variables defined by the EmpView
. The DeptViewImpl
class also contains an overridden createViewLinkAccessorRS()
method that applies the bind variable values to each view link accessor rowset created. Run the TestPage.jspx
page and enter values for the low and high salary range you want to see for employees in the tree. For more information on the difference between the view object instances in the application module's data model and the system-created view object instance used to provide the view link accessor attribute rowset, see 27.1.3 Understanding View Link Accessors Versus Data Model View Link Instances.SessionHelper
class to simplify referencing entries in the ADFBC session's userData map from the Groovy expressions providing the default values for bind variables. The user data map is referenced via EL expressions in the two JSPX pages, and it is referenced in Groovy bind variable expressions in two places: (1) at the AM data model level for the VO instance named AnotherInstanceOfQueryWithBindVarValue
(which you can see by selecting the VO instance in the Data Model list and clicking the (Edit) button, and (2) on the view accessor named QueryWithBindVarValue1
of the EmpView
view object. This latter view accessor is used by the LOV definition on the EmpView
view object's Mgr
attribute. To try the application, run the SetUserMapValue.jspx
page, type NameFilter
into the setUserMapValue_key
field, and (for example) the letter S
into the setUserMapValue_value
field, and click (setUserMapValue). The table in the page updates to show an example of a data model view object instance using the bind variable value from the userData Map. Clicking on the (GoTo Page Referencing Session Value in LOV Bind Var) button takes you to a page where the search form shows that the view accessor's rowset (based on the same QueryWithBindVarValue
view object) is using the bind variable value from the userData map as well. The Groovy expressions that access the userData map use the helper class via the expression test.model.SessionHelper.userData(adf.object).NameFilter
. A future release of ADF will make it simpler to reference the ADFBC session object without the need for a helper class.CreateDeptWithFlagTable.sql
creates a variant of the DEPT
table named DEPT_WITH_FLAG
that has one additional FLAG
column whose valid values are "Y" or "N". The DeptWithFlag
EO indicates a UI hint on its Flag
attribute to indicate that it prefers to render as a "Checkbox". Dragging and dropping the DeptWithFlagView1
data collection from the data control palette as an "ADF Table", JDeveloper infers the correct selectBooleanCheckbox
control for the Flag
attribute. However, there is two additional (simple) steps required to make it work correctly in a table. First, using the overview editor for the pageDefinition, I created a new button binding named "Flag" for the Flag
attribute, indicating the values of "Y" for selected and "N" for unselected. With the view1PageDef
page definition as the active editor, I expanded the DeptWithFlagView1
tree binding, its nodeDefinition folder, and the AttrNames folder inside it to select the Flag
attribute of the tree binding node definition. Then, using the Property Inspector, I set the value of the Binds
property of this Flag
attribute name element to "Flag" (without quotes), which references the name of the button binding created above.CreatePLSQLPackages.sql
script to create the PL/SQL package named example_pkg
used by the demo. This package contains a single procedure do_something
which accepts a string parameter, number parameter, and date parameter and inserts these values into a new row in the EXAMPLE_PKG_TABLE
table. In the J2EE application, the stored procedure invocation is encapsulated inside the business service inside the custom application module method collectDataUsingStoredProcedure
. It accepts one string parameter, one number parameter, and one date parameter. Internally the method calls the stored procedure using the helper routine described in section 25.5.2 Invoking Stored Procedure with Only IN Arguments of the developer's guide. This custom application module method is published on the client interface as described in section 8.4.1 How to Publish Custom Service Methods to Clients. The JSPX page in the ViewController
project was created by dragging the collectDataUsingStoredProcedure
from the Data Control Palette and dropping it onto the page as an ADF Parameter Form. The page defintion variables get automatically cleared after each page submit due to a navigation rule that redirects back to the same page. The table below the input form was created by dropping the data collection named ExamplePkgTable
from the Data Control Palette as a read-only table. Both the view object attributes and the page definition variables are leveraging ADF UI control hints to supply translatable prompts and format masks.ApplicationModule
method. Includes examples for invoking it both from an ADF DataAction
(in the ViewController project) as well as a JClient button event handler method (in the SwingClient project).EmpView
view object in this example features two transient, Groovy calculated attributes. The attribute named ValueUsingVOFunction
references a custom myFunctionAtVOLevel()
method on the EmpViewImpl
class, while the attribute named ValueUsingVORowFunction
references a custom myFunctionAtVORowLevel()
method on the EmpViewRowImpl
class, in both cases passing in the value of the Sal
attribute. The methods simply return the value passed in surrounded by either parenthesis or square brackets.EmpViewRow
class overrides the createViewRowAttrHints
method to return a new instance of the EmpViewRowAttributeHintsImpl
class (which extends the framework base class ViewRowAttrHints
. In the EmpViewRowAttributeHintsImpl
class, notice that we've overridden the getHint()
API to conditionally return a value for user-defined attribute hints named "mindate" and "maxdate". These hints are coded to look at the value of the Hiredate
in the current row, and return a date String that represents three days before that date for "mindate" and three days after that date for "maxdate". The class also overrides the getLabel
API to customize the value of the "label" UI hint for the Job attribute. Run the TestPage.jspx
and try to edit a "Hiredate" value. You'll see that the date picker only allows you to change the date to a date that is within a span of three days before to three days after the current value. This occurs due to the UI component's referencing the custom "mindate" and "maxdate" hints appropriately. Also, you'll notice that the label for the "Job" field is the default label with the value of the Sal
attribute appended to it.CustomFacesPageLifecycle
class is a custom ADF Faces page lifecycle class that overrides the prepareRender
phase of the ADF page lifecycle to adjust the locale of the JSF UI View Root (from which the current user's preferred locale is derived for the rest of ADF at runtime). For more details about how its configured in the project see 10.5.4.1 Globally Customizing the ADF Page Lifecycle of the ADF Developer's Guide for Forms/4GL Developers. It references the App
managed bean to see what the user's preferred locale is. In the overridden prepareRender
method, if App.preferredLocale
is different from the current UI View Root's locale, then the method sets the locale of the view to the preferred locale before calling super.prepareRender
. If it ends up changing the locale, it also forces any List Binding objects with a translatable label for their null entry (like "<No Department>") to have their list of valid values recalculated so the null entry will render in the new current locale. The TestPage.java backing bean sets the value of the App.preferredLocale
to either ENGLISH
or ITALIAN
depending on the button you click. Notice that the JSF resource-bundle-based message strings change, as well as the ADF BC component UI hints that supply the strings for the table column titles and field prompts. Also, the AnotherPage.jspx
page allows the user to change the value of a transient number attribute that was added to the entity object. The format mask of #0.00
is specified as part of the UI control hints at the entity object level and you can see that when in Italian a number like 3.45
is presented and edited using the locale-appropriate decimal symbol 3,45
. The regular JSP page RegularJSPPageNotUsingJSF.jspx
in the project illustrates how a regular JSP page might test and set the JSF managed bean that holds the user's preferred locale setting. This might be interesting to understand if you are trying to allow the user to set their preferred language from a regular JSP acting as a login page in an otherwise-JSF-based application.CreateTable.sql
script in the Model
project to create a SETTINGS
table with three rows of example data. Since SQL does not have a native boolean datatype to use for the type of a column in a table, the STATUS
column in the SETTINGS
table is defined as NUMBER(1)
with 0
representing false and 1
representing true. The example illustrates how you can introduce a transient Boolean
-valued attribute StatusAsBoolean
to work with the numerical true/false value as a Boolean. In the custom SettingsViewRowImpl
row class, the getStatusAsBoolean
and setStatusAsBoolean
methods have been written to convert the numerical value to a boolean upon reading it and convert a boolean value to the correct Number
value upon writing it. In the JSPX page in the ViewController
project, the af:selectBooleanCheckbox
is bound declaratively to the StatusAsBoolean
attribute in the current row using the EL expression #{row.StatusAsBoolean}
. When you run the page, try changing the state of the checkboxes and clicking (Commit). Then from SQL*Plus, you can verify that the changed boolean checkbox states have been correctly saved as 0
or 1
appropriately.testConsistency
method in the CustomEntityImpl
class. Run the sample Form.java class and click on the (testConsistencyOfCurrentRow) button to try it out. The textConsistencyOfCurrentRow()
method is a custom method the view object's custom interface which gets the current row, then delegates to it's testConsistency()
method mentioned above. NOTE: this solution could be optimized by comparing only a "change indicator" column in the entity if it is marked and it also doesn't do any special treatment for LOB attributes whose comparisons you generally want to avoid due to their overhead.af:table
component's filter fields. The test.jspx
page's af:table
component has its binding
property set to expose the UI component in the TestPage
backing bean as a property named table
. The TestPage
bean has four button event handler methods that are referenced by the four different buttons on the page. One clears all table search fields, one clears only the Dname
search field, the third method clears all the search fields and also queues an event to reexecute the table's query, and the fourth one programmatically populates selected search filter fields and reexecutes the table's query. The backing bean contains helper methods that illustrate how to access the table component's FilterableQueryDescriptor and the "Filter Criteria" Map
it contains. Clearing selected entries from this filter criteria map clears the corresponding search field in the UI. While not required to implement the search field manipulation, the example also illustrates how you can override the default QueryListener method to call custom onTableQueryExecuted
query handler method which can include code before or after its invocation of the built-in query listener event handler method provided by the search binding object.TestClientUIModel.xml
file to open it in the code editor before it will get migrated to the 10.1.3 format correctly.INFO
. This example includes a CustomErrorHandler
class that implements handling for informational messages. The addInformationMessageTest()
method in the AppModule
custom java class adds an informational message by using the addWarning()
API, passing a custom subtype of JboWarning
called InformationalMessage
. Due to the way warnings are wrapped before they are passed to the custom error handler's getDisplayMessage()
method by the ADF framework, the InformationalMessage
constructor sets a value into the error parameters that the CustomErrorHandler
uses to recognize the warning subtype as an informational message. The overridden getDisplayMessage()
method in the error handler class detects whether the exception is an informational warning, and if it is, it adds an informational message to the FacesContext. It uses the setProperty()
and getProperty()
API of the JboWarning object to set a flag it can use to detect whether the exception has already been reported by this custom mechanism. This is required because ADF will invoke the getDisplayMessage() two times for each exception reported, later only one of which gets reported to the user. Run the Page.jspx
to try the example. To see an error be reported, enter a salary of 5002 and click (Next). To try a warning, enter a salary of 5001 and click (Next). To see an informational message, click the (addInformationMessageTest) button. Notice that the custom error handler class is declaratively configured via the ErrorClass
attribute on the root element of the DataBindings.cpx
file (which shows as the ErrorClass
property of the root element in the Property Inspector.ListOfAccounts
domain that implements the necessary interfaces to allow its contents - a list of Account
beans - to be passivated and activated correctly to the application module persistent state snapshot. It shows a combination of required aspects including implementing the XMLDomainWriter
and DomainInterface
interfaces, as well as containing a public static getXMLDomainFactory()
method that returns an instance of a class that implements the XMLDomainReaderFactory
and XMLDomainFactory
interfaces. The ExampleTransientView
in the project is a transient view object with one String
-valued attribute marked as the key attribute, and another ListOfAccounts
attribute of type model.types.common.ListOfAccounts
, our custom domain. Both of these attributes are marked as updateable and have their "passivate" property set to true. This view object's create()
method contains the best practice code required for a transient view object that will be programmatically populated by creating and inserting rows. Run the TestClient
class to exercise the creation of two rows in the transient view, passivating the transient state, releasing the application module, acquiring a new application module, activating the passivated state, and iterating the transient rows again.TestPage.jspx
page with an af:table
component whose selectionListener
property has been modified from the default EL expression dropped when the table was created, to instead call a selection listener method named onTableSelectionChanged()
in the TestPage
backing bean. This default EL expression normally looks like #{bindings.*TableBindingName*.collectionModel.makeCurrent}
which declaratively "wires" the table selection event to invoke the makeCurrent()
method on the CollectionModel
of the table binding. In order to preseve the default data binding functionality that reacts to the table selection change, this backing bean method uses the invokeMethod()
helper method of the EL
helper class to call the default functionality with custom code before and after it. The EL helper class is in the FwkExtensions
project, which is marked as a project-level dependency.view1.jspx
page with a default ADF Form dropped for the EmpView. The Deptno
attribute of the EmpView
defines an LOV that references a view accessor based on DeptView
. The DeptView
view object has an additional SQL-calculated attribute named ExtraAttr
of type Boolean
. The SQL expression for the attribute is just the quoted string 'FALSE'. There is a view criteria named LOVSearchCriteria
defined on DeptView
and it includes view criteria items for Dname
, Loc
, and ExtraAttr
. The ExtraAttr
has the following UI hints defined: Label Text is set to 'Show Only Multiples of 20?'; Tooltip Text is set to some helpful text, and Control Type is set to 'Checkbox'. The DeptView
view object uses the CustomViewObjectImpl
class as its base framework class, and so the DeptViewImpl
custom view object class extends that CustomViewObjectImpl
to inherit a few helper methods. The base CustomViewObjectImpl
overrides the getCriteriaItemClause()
framework API invoke two other methods getCriteriaItemClauseForDatabaseUse()
and getCriteriaItemClauseForCache()
to customize the SQL fragment returned for a particular view criteria item. The DeptViewImpl
class overrides these two methods so that if the view criteria is "LOVSearchCriteria" and the view criteria item is "ExtraAttr" and beging asked for the fragment to use for database filtering, then we return the SQL fragment "MOD(DEPTNO,20)=0" if the value of the view criteria item is Boolean.TRUE, otherwise we return the harmless "1=1" predicate. For the in-memory cache fragment, we override the other method and return "1=1". Run the form, click the dropdown list on the combobox and click on the "Search..." link to access the popup LOV dialog. You'll see that if you check the checkbox, then only departments whose id's are multiples of 20 appear in the list, otherwise they are not restricted by this additional (custom) criteria.CustomFacesPageLifecycle
class is registered in the faces-config.xml
file as an application-scoped managed bean named PageLifecycle
(the name is arbitrary, of course). The CustomADFPhaseListener
class is registered in the "Life Cycle" section of the faces-config.xml
as well. Its overridden createPageLifecycle()
method uses the EL
helper class to return the custom page lifecycle class using the method EL.get("#{PageLifecycle}")
. Why would you want to do this, you ask? The TestPage.jspx illustrates two simple examples (and there may be more). The (Some Button) on the page has its Action
property set to the EL expression #{PageLifecycle.onButtonClicked}
, illustrating that the custom lifecycle class (being EL-accessible) can have global helper routines like an action listener method. Another example is the Dname
af:inputText fields whose Value
property is set to the EL expression #{PageLifecycle.attributeBindingWrapper[bindings.Dname]}
. This shows that the helper routines in the custom page lifecycle class can leverage a customized Map
approach to effectively provide simple, one-argument callable methods. In this case, the getAttributeBindingWrapper
map wraps the AttributeBinding
passed in as an argument. The second of the two Dname
inputText's shows that by wrapping the attribute binding in this way and setting the Disabled
property to true, you can get an inputText that shows as a disabled field instead of a read-only label which is the default rendering of read-only inputText fields.create()
method of your entity object to programmatically populate its primary key attribute from a sequence at row-creation time. Even though this is only a couple of lines, if many of your entity objects have sequence-valued primary key attributes and you are not using database-trigger-assigned sequence values (described in section 6.6.3.8 Trigger-Assigned Primary Key Values from a Database Sequence), then you might be interested in a more generic, metadata-driven solution. This example illustrates a simple CustomEntityImpl
framework extension class on which both the Dept
and Emp
entities in the sample are based. It's overridden create()
method tests for the presence of a custom attribute-level metadata property named SequenceName
and if detected, populates the attribute's default value from the next number in that sequence using the same basic code as above. The Dept
entity defines the custom SequenceName
property on its Deptno
attribute with the value DEPT_TABLE_SEQ
, while the Emp
entity defines that custom property on its Empno
attribute with the value EMP_TABLE_SEQ
. The supplied CreateDeptAndEmpTables.sql
script creates the tables and sequences for you.EmployeeAllInfo
view whose SQL mode is set to use the new-in-11g setting of 'Declarative'. It contains one editable Employees
entity usage, and six additional reference entity usages showing related information about the employee's department, the department's manager, the department's location, the department's region, the employee's job, and the department's country. When a view object is in declarative mode, its SQL statement is determined at runtime instead of at design time. Attributes in the view object can have their Selected in Query property set to false, and these attributes will only be selected if they are referenced by the page definition for the current page, otherwise they will be left out of the query. When multiple entity usages are involved, if all the attributes from a given entity usage are left out of the query, then the ADFBC runtime "prunes" that table related to that entity usage out of the query and does not perform that join. The application module in the Model
project includes three different instances of the same EmployeeAllInfo
view object. You can observe the different runtime queries performed by the three different JSPX pages by looking in the log while performing a search in each page. The file Queries.txt
included in the ViewController
project contains a formatted version of the different queries. You can see in the query for the MinimalEmployeeInfo.jspx
page, that only the EMPLOYEES
table is included. In the query for MediumEmployeeInfo.jspx
you see EMPLOYEES
, DEPARTMENTS
, EMPLOYEES
(a second time for the manager), and JOBS
. The query for the final page joins all of the tables.#{requestScope.creating}
attribute, and on the value of a #{sessionScope.locDefaultValue}
attribute. The #{requestScope.creating}
flag is set declaratively using an <af:setActionListener>
on the (Create) button in the Browse.jspx page. The example works by using a backing bean property to "decorate" the normal Loc attribute binding. If certain conditions are true, then the getLoc()
method in the backing bean returns the value of the session variable instead of returning the normal value of the Loc binding. Try setting the session value to some value, then experiment with clicking on the different buttons in the browse page to show the defaulting occurring or not occurring as inidicated on the button labels. We adopt this approach instead of trying to programmatically set the binding immediately to the conditional controller-layer default value so that we don't cause the new row to change from its initialized status to a "new" status (which will add it to the rowset. See section 10.4.4 What You May Need to Know About Create and CreateInsert in the developer's guide for more information on this.rendered
property of several components in the FindDepartments.jspx
page in this example use the queryPerformed
property of the search binding to conditionally hide the results table until the user has performed a query. In addition, the same property is used to conditionally show an 'Enter some criteria...' message or a 'No departments found' message as appropriate. For more information about the declarative options the search binding supports, see the section entitled 'Changes to search form binding default behavior since Technology Preview 4' in the JDeveloper 11g release notes.DEPT
-based view object presented in both a table and a form display. The (Create), (Delete), and (Rollback) buttons are set to have their immediate
property to the value true
which causes validation to be circumvented. There are two functionally-identical JSPX pages, one which programmatically calls resetValue()
on the Faces UI components in the backing bean to cause their values to be refreshed, and the other which uses the <af:resetActionListener>
to accomplish the same thing declaratively.<f:selectItems>
tag that's generating the set of choices for the selectOneChoice dropdown list in each row of the table. Its value
attribute is set to the EL expression #{DropdownListInTableChangingByRow.lovList[row.DepartmentId]}
. This syntax accesses a backing bean property named lovList
and if that property is a Map
, then the EL resolver will call the get()
method on the Map
-valued property to access the entry with the key equal to the expression row.DepartmentId
inside the square brackets. By implementing the lovList
property in the backing bean as an anonymous subclass of java.util.HashMap
with an ovverridden get(Object key)
method, we can intercept the attempt to access the Map entry with the key of the current row's department id, and instead have its value come from an invocation to an application module method that returns the correct set of employee choices for that current department. Not a totally obviously technique, but I've included further comments in the code that explain more of the implementation details.DumpPoolStatisticsServlet
is configured in the web.xml
to map to the URL /DumpPoolStatistics
. The DumpConnectionPoolStatisticsServlet
is configured in the web.xml
to map to the URL /DumpConnectionPoolStatistics
. In contrast to using a JSP page to dump these statistics, using servlets configured this way instead avoids engaging the ADF Binding Filter, the ADF Faces filter, or the ADF Faces servlet when viewing pooling statistics. Run the EditEmps.jspx
and click (Next) or (Previous) a few times. Then click the "DumpPoolStatistics" and/or "DumpConnectionPoolingStatistics" links in the page to open a new browser window with the pooling statistics. Recall that if your ADF application using J2EE datasources instead of a JDBC URL connection, then you will be using your application server's connection pool instead of the ADFBC connection pool. In that case, the database connection pooling statistics won't appear using this mechanism. CAVEAT: the connection pools are named using the fully-qualified JDBC connection credentials, which includes the password. We recommend using the DumpConnectionPoolStatistics
servlet with care in a production environment and not leaving the configured permanently (unless you protect access to it with a password using HTTP authentication).FacesURLPattern
as well. Due to Bug# 5080057, setting the jbo.ampool.sessioncookiefactoryclass
property in the configuration is not correctly saved to the bc4j.xcfg
file. So, in order to configure a custom session cookie factory class as required to repurpose the code in this example in your own projects, you'll need to hand-edit the bc4j.xcfg
file to insert the child element <jbo.ampool.sessioncookiefactoryclass>test.DynamicJDBCSessionCookieFactory</jbo.ampool.sessioncookiefactoryclass>
inside the appropriate <AppModuleConfig>
element for the configuration in question. (NOTE: The version of this workspace that works for JDeveloper 9.0.5 and 10.1.2 is still available here).Database
project in the workspace contains CreateTables.sql
and InsertData.sql
scripts that define and populate four simple tables: PARAMETER_FORM_DEFINITION
, PARAMETER_DEFINITION
, PARAMETER_CHOICES
, and PARAMETER_FORM_ITEM_SET
. See the database diagram in that project for a visual view of how they are related. The insert data script populates some sample data for two parameter forms named "Form1" and "Form2", each of which has some collection of parameters in its form item set. The ParameterFormItems
view object queries the list of parameters for a given parameter form by its form id, which is passed in as a named bind variable (TheFormId
). There is a view link defined between the ParameterFormItems
view object and the ParameterChoicesView
that enables a view link accessor attribute named ParameterChoices
in each row of the ParameterFormItems
view object's results. In the ViewController
project, you will find three pages. The TestParameterForm.jspx
page allows you to select any of the defined parameter forms and test it by setting the #{processScope.ParameterFormId}
attribute to the parameter form you've selected and navigating to the DynamicParameterForm.jspx
page. That page is the real meat of the example. It includes a ADFM tree binding to expose the hierarchical collection of data for the ParameterFormItems
and its collection of available choices (via the ParameterChoices
view link accessor attribute). The page definition for this page includes an invokeAction
that triggers the firing of an ExecuteWithParams
action binding when the page is not handling a post-back (RefreshCondition="#{adfFacesContext.postback==false}"
). This allows the ParameterFormItems
view object's TheFormId
named bind variable to be declaratively set to the value in the #{processScope.ParameterFormId}
attribute that defines which data-driven parameter form to show. The page defines uses an <af:forEach>
to iterate over the parameter form items. Inside this loop, it uses an <af:switcher>
based on the value of the DisplayType
attribute of the parameter definition. The switcher determines whether to display the item as an inputText
, a selectOneChoice
(i.e. dropdown list), or a selectOneRadio
(i.e. radio group) depending on whether the DisplayType
of each parameter is 'I
', 'S
', or 'R
' respectively. In the case of rendering the parameter as a dropdown list or radio group, an inner <af:forEach>
accesses the nested collection of available choices defined to populate the list. The last part of the example involves understanding where the user's entered values are posted and stored. This is handled by a transient view object attribute named UserValue
that has been added to the ParameterFormValues
view object. The ShowUserEnteredValues.jspx
page renders the parameters along with the user-entered values, and the DynamicParameterForm.java
backing bean class contains code that illustrates how you would access the values of the users parameter item values in the backing bean if needed. For extra credit, you can use the ADF Business Components Tester on the ParameterFormModule
to maintain the parameter form metadata that drives the parameter forms, adding a new form, new items, or new item choices as needed.CreateAndPopulateCitiesTable.sql
script to create and populate an additional CITIES
table that it uses. The sample includes two JSF pages, illustrating two different styles of cascading poplists you might need to implement. Use the first style, represented by the CascadingLists.jsp
page, when the driving list serves only to partition the set of choices in the dependent list. In that page, only the CountryId
is actually an attribute of the LocationsView
row. The RegionId
of the selected region is not present. On the other hand, use the second style, represented by the CascadingListsUsingTwoListBindings.jspx
when both the driving list and the dependent list have to update the value of an attribute in the row being edited. In that page, a row in the CitiesView
includes both a RegionId
and a CountryId
attribute. The CascadingLists.jsp
page allows creating, updating, and deleting locations. The page has a "Regions" dropdown list and a "Countries" dropdown list. The Regions selectOneChoice
is bound using a navigation list binding, and the Countries selectOneChoice is bound using a normal (LOV-style) list binding. The onRegionListChanged
backing bean method fires for the ValueChangedEvent
of the Regions dropdown, and simply sets a request-scoped attribute to signal that the user has changed the regions choice. This is a sign they want to be editing the the current row to be a country in a different region. The invokeAction
in the page definition invokes an application module method during the renderModel phase (Refresh="renderModel"
) if the request-scoped property just mentioned is not set. The onCountryChanged
backing bean method fires for the ValueChangeEvent
of the Countries dropdown, setting a different request-scoped attribute to signal that the user has changed the country choice. Since the navigation list causes the current row in the RegionsIterator
to change based on the user's current selection, and since the Countries
view instance in the data model is an actively-coordinated detail view based on the Regions
master view, the changing of the current row in the Regions
view automatically coordinates the Countries
view to have the correlated set of countries for the new current region. The CascadingListsUsingTwoListBindings.jspx
page uses two list bindings instead of having the driving this be based on a navigation list. The RegionId
list gets its set of valid choices from the RegionsList
in the data model. The CountryId
gets its set of valid choices from the CountriesListForRegion
view instance in the data model. This latter view instance is based on the CountriesListForRegion
view defintion that uses a named bind variable (TheRegionId
) in order to filter the list of countries by a region id supplied as a parameter. The lists use ValueChangeListener
methods like the ones used in the other page. The difference in this example is what the refreshCountryListUnlessCountryChanging
invokeAction invokeAction executable in the page def is doing. In this page's page definition, this invokeAction causes an ExecuteWithParams
action binding on the CountriesListForRegionIterator
to be triggered during the prepareRender
lifecycle phase whenever the user is not changing the country. Notice that this invokeAction is sequenced after the iterator binding for CitiesViewIterator
so that the value of the RegionId binding will reflect the up-to-date state of the current CitiesView
row. That ExecuteWithParams
action binding is configured to pass the value of the #{bindings.RegionId.inputValue}
as the value of the TheRegionId
parameter of the view object. This ensures that the whenever the page is displayed the country list correctly reflects the current region. Both pages' page definition references the ConditionalValidationPageController
class as described in section 10.5.4.2 Customizing the Page Lifecycle for a Single Page of the ADF Dev Guide in order to avoid validating the row if the page request was caused due to the auto-submit of the Regions or the Country poplists. Also, this custom page controller overrides the prepareRender() method to conditionally set the CountryId
binding to null if the user is changing the region poplist, detected based on the presence of the request-scope RegionChanged
attribute mentioned previously. Also note that the example contains a group of classes in the test.view.bug5930745
package to workaround Bug# 5930745 that causes a JSF selectOneChoice control to incorrectly display a blank entry when the value of a ADFM List Binding is null and it has registered exceptions. The ViewController
project in this example also implements the same custom error handling code as example# 107 and contains a slightly-modified routine in its CustomFacesPageLifecycle
class for "dirtying" the first updateable, mandatory attribute binding found to have a null value. This avoids skipping validation when the user tries to save the row without changing any values. **NOTE:**fortunately all of this becomes easy and declarative in JDeveloper/ADF 11g!createViewObjectFromQueryStmt()
. The TestClient
program calls an application module's client interface method named createViewObjectAndViewLinks()
. That method constructs two view objects instances and a view link between them. Then, the client code finds and iterates through the results of these view objects. Since we programmatically define the names and data types of the view object's attributes, ADF will not need to perform a runtime DESCRIBE (involving a round-trip to the database) to discover that information at runtime.Dept
entity object based on the DEPT
table, we assume that in addition to the Deptno
primary key's uniqueness that is declaratively enforced using the ADF UniqueKeyValidator, we also want to enforce the uniqueness of the Dname
attribute value. For the sake of argument, we assume we'd like the value to be unique in a case-insensitive way so that we won't allow one department called "Sales" while another is named "SALES". The DeptDefImpl
class in this example implements an existsByDname()
method following the technique outlined in section 9.6.2 Implementing an Efficient Existence Check of the ADF Developer's Guide for Forms/4GL Developers. This routine on the custom entity definition class uses the FindDeptByUniqueDname
view object to test case-insensitively whether a row exists in the DEPT
table with the supplied department name. If it doesn't exist in the database, it then searches the entity cache to detect whether perhaps another new instance created in the current transaction might already be using the candidate department name value, too. The Dept
entity object class has an attribute-level method validator defined to trigger the validateDname()
method in the DeptImpl
class. This method gets the custom entity definition class and calls the existsByDname()
method, returning true if the candidate department name does not already exist. See section 9.3.1 How to Create an Attribute-Level Method Validation for more information on creating an attribute-level method validator. You can use the ADF Business Components tester to verify that it's impossible to insert two departments with the same department name.DeptADFBCWebService
and EntityAndViewBasedOnWebService
. The former contains two projects. The first is named Project
and contains a simple DeptService
application module, and a default Dept
entity object, and entity-based DeptView
view object. The application module has custom methods on its client interface like retrieveDepartmentData()
, estimateCount()
, findDepartmentByDeptno()
, insertDepartment()
, updateDepartment()
, mergeDepartment()
, and deleteDepartmentByDeptno()
. The DeptService
application module has been published as a J2EE Web Service as described in section 33.4 Publishing Application Modules as Web Services of the ADF Dev Guide. The second project in the DeptADFBCWebService
workspace is a Tests
project that contains JUnit tests to test both the application module's local client interface as well as testing the deployed web service. The tests should all succeed if you have run the supplied CreateEmpAndDeptTables.sql
in the SCOTT
account. The WAR deployment profile in the Project
project deploys the web service to an external OC4J instance, which you can launch most easily by running the start_oc4j
batch file (or shell script) in the ./jdev/bin
subdirectory of your JDeveloper installation home directory. After deploying, you can test the web service by pointing your browser at the URL http://localhost:8888/DepartmentServices/DeptServiceSoapHttpPort
. Note that the web service unit test fixture makes use of a web service proxy class named DeptServiceSoapHttpPortClient
that was generated using JDeveloper's Web Service Proxy wizard, using the WSDL URL of http://localhost:8888/DepartmentServices/DeptServiceSoapHttpPort?WSDL
The second EntityAndViewBasedOnWebService
workspace contains an HRModule
application module, and default entity objects and view objects for the DEPT
and EMP
tables. The DeptImpl.java
has overridden appropriate methods in a way similar to what is described in section 26.4 Basing an Entity Object on a PL/SQL Package API of the ADF Dev Guide to have this Dept entity based on a web service. Similarly, the DeptViewImpl.java
class has overridden appropriate methods, similar to the technique described in section 27.8.4 How to Create a View Object on a REF CURSOR of the guide. When you use the Business Components Tester to test the HRModule
- and you make sure you have the webservice in the other workspace deployed and currently running - you can work with the data in the EmpView
and DeptView
view object instances. When the data is read for the DeptView
it will pull it in from the underlying web service. If you update the data for an existing department, create a new department, or delete an existing department, the changes are saved back using the web service in the other workspace. Also try using simple view criteria in the tester to search for department rows in the DeptView. That should work, too.Dept
entity in the project has published an event named OnDnameOrLocChanged
. When you define a new event on the Publish tab of the Entity Object Editor, you indicate zero or more entity attributes that will be delivered as part of the event "payload" to any subscribed listeners. The event name is user-defined and your code decides when it will fire by calling the automatically generated method of the same named as the event. In this case, looking in the DeptImpl.java
class, you can see that I've added a call to this OnDnameOrLocChanged()
method inside the setDname()
and setLoc
methods. When you publish an event, in addition to the generated method used to fire the event, JDeveloper also generates code into a custom Entity Definition class for the entity publishing the event. If you look in the DeptDefImpl.java
, you'll see generated methods to addOnDnameOrLocChangedListener()
and removeOnDnameOrLocChangedListener
. Other entities that want to subscribe to the event can do so programmatically by looking up the entity definition object of the publishing entity, casting it to the more specific custom entity definition class name, and then calling the add*EventName*Listener()
method. The Emp
entity in the demo declaratively subscribes to the OnDnameOrLocChanged
event, indicating that the event should be delivered to all associated Emp
entities based on a particular association that relates it to the publishing entity. Note that the method to be invoked to receive the notification must have a signature that matches the number, type, and order of the attributes delivered in the payload of the published event. Otherwise, you can choose to have the event only delivered to entities that have programmatically registered to receive the event using the methods mentioned above. If you test the AppModule
application module and try changing the value of the Loc
or Dname
attribute of a department, you can observe in the JDeveloper Log window that the associated Emp
entities in that department receive the notification.RETURNING INTO
clause when it tries to refresh-on-insert or refresh-on-update. The RETURNING INTO
clause throws database-level errors when used in some kinds of database views having INSTEAD OF
triggers. Also shows that you need at least one attribute in the entity other than the rowid marked as "Unique" to make the insert situation work correctly.ViewController
project to start the demo. Enter a string value and an integer value on the StartPage
and click the button to start the emp-task-flow
task flow, passing in values for the task flows two parameters stringTaskFlowParam
and intTaskFlowParam
. In the TestPage.jspx
that runs when emp-task-flow
starts, the parameter values passed in appear in the title bar of the box at the top. Nine buttons in this page illustrate different combinations of invoking application module, view object, and view row methods using action bindings in the page's pageDefinition. For the methods that accept arguments, the nested elements inside the action binding (which you can see in the structure window) reflect the names of the method arguments and provide a declarative EL expression that ADF will evaluate when the action binding is executed to supply the method arguments. Three of the nine buttons are bound to action event handler methods in the TestPage
backing bean. This backing bean illustrates the best-practice technique to invoke methods on the AM, VO, or VO Row client interface, optionally providing some or all of the method arguments in code as well. In particular, it illustrates that you should NOT use the Configuration.createRootApplicationModule()
API to access an application module in your backing bean. See this blog article for more info on why you should not use it in backing beans. For a bit more information on how the StartPage
captures and passes parameters to the task flow, read on. The initial StartPage.jspx
uses page definition variables named pageDefStringVariable
and pageDefIntVariable
to declaratively provide temporary storage for the string- and int-valued parameters in the parameter form. These variables were added to the pageDefinition using the Structure window by selecting the variables node inside the executables section, and choosing *{ Insert inside variables > variable }*from the right-mouse menu. Also in the structure window for the page definition of the Start page, we created two attribute bindings to expose the values of the pageDefinition variables to the UI. One way to create these bindings is to select the bindings folder in the Structure window and choosing { Insert inside bindings > Generic Bindings > attributeValues }. The other way is to use the "Bindings" overview editor tab for the StartPage and click the Create Control Binding icon (green plus-sign) in the Bindingsbox on the left. When creating the bindings, choose the iterator binding named variables
and pick the pageDef variable name whose value you want to bind to as the attribute name. The af:inputText
fields in the StartPage
bind to this attribute bindings using the EL expressions #{bindings.pageDefStringVariable1.inputValue}
and #{bindings.pageDefIntVariable1.inputValue}
. The button on the StartPage
contains two nested af:setActionListener
components to assign the values of the pageDefinition variables collected from the user into two request-scope attributes. The value of #{bindings.pageDefStringVariable1.inputValue}
is assigned to #{requestScope.pageDefStringVariable}
, and similarly the value of #{bindings.pageDefIntVariable1.inputValue}
is assigned to #{requestScope.pageDefIntVariable}
. The task flow call activity named emp-task-flow
in the adfc-config.xml
unbounded task flow is configured to assign the values of the task flow parameters from these request-scope attributes. The stringTaskFlowParam
task flow parameter gets its value from the #{requestScope.pageDefStringVariable}
, while the intTaskFlow
param gets its value from the #{requestScope.pageDefIntVariable}
. As part of its parameter definitions, the emp-task-flow
itself defines pageFlowScope attributes that will hold the values of the task flow input parameters for the duration of the task flow. The stringTaskFlowParam
parameter value is stored in the attribute #{pageFlowScope.stringTaskFlowParam}
while the intTaskFlowParam
is stored in the attribute #{pageFlowScope.intTaskFlowParam}
. Any pages or backing beans that execute in the context of this task flow can reference the parameter values using these same EL expressions. As noted above, the action bindings in the page definition are configured to reference these pageFlowScope attributes using EL to declaratively pass their values as method arguments to the invoked method.getPostedAttribute()
API you can use to access the value of any attribute as it existed at the beginning of the current transaction. This example illustrates a technique for exposing that API in a generic way to the ADF binding layer so that your application pages can display that original value to the end-user. This could come in handy to allow the user to visually review their pending changes in the transaction before finally confirming to save the changes with a (Commit) button. In the Model
project, The technique involves: (1) exposing the getPostedAttribute()
API as public
in a CustomEntityImpl
framework extension class since otherwise the method would be protected, (2) automatically adding a "shadow" attribute named *OrigAttrName*_orig
for each attribute in the view object (in the overridden create()
method of the CustomViewObjectImpl
class, and (3) returning the original value of the appropriate underlying entity attribute using getPostedAttribute()
in an overridden getAttributeInternal()
method of the CustomViewRowImpl
class. The Dept
entity is configured to use the CustomEntityImpl
and the DeptView
view object is configured to use the CustomViewObjectImpl
component class and the CustomViewRowImpl
row class. In the ViewController
project, the technique involves referencing the "_orig
" attribute names in the EL expressions for the values of certain UIComponents. Try navigating through multiple rows in the DeptView
and making changes to the Dname
and or Loc
attributes in any of them. As you scroll back and forth before issuing the final transaction commit, you will notice that the previous value of the changed attributes appear next to your modified values wherever you've modified them.EmpView
view object in this example features a WHERE clause with a "reverse" CONNECT BY
expression to identify parent employee rows that match the Ename
search criteria. The START WITH
clause filters employees in a case-insensitive way based on the value of the varEname
bind variable. The built-in ExecuteWithParams
operator for the EmpView
was dropped onto the page as a parameter form to declaratively apply the user's search filter and re-execute the query. The EmpViewImpl
custom view object class overrides the framework's createViewLinkAccessorRS()
method in order to set the value of the varEname
bind variable from the EmpView1
view instance in the data model to also be set on the framework-created view link accessor view object, as well as on any view link accessor rowsets created. This "cascades" the value of the bind variable to the nested queries appropriately. In the page definition for the FilteredTreeShowingInterimNodes.jspx
page, the automatically-created ExecuteWithParams_varEname
page definition variable has a custom label hint associated with it to define the prompt that will appear next to the search field. Last but not least, the TracingViewObjectImpl
framework extension class instruments the bindParametersForCollection()
method to dump useful information about each query executed. The EmpView
view object specifies this class as its framework base class.EditDepartment.jspx
page in the example allows the user to edit a row in the DEPT table. A button on that page has its Action
property set to dialog:selectDepartment
and has its useWindow
property set to true
so that it invokes a modal, popup dialog containing the page SelectDepartment.jspx
. The commandButton on that page contains a nested af:returnActionListener
component whose value is provided by the EL expression #{row.rowKeyStr}
. That will evaluate to the string format of the current row's key (described in more detail in section 10.5.6 Understanding the Difference Between setCurrentRowWithKey and setCurrentRowWithKeyValue of the ADF Developer's Guide for Forms/4GL Developers.) The onReturnFromSelectDepartmentDialog
return listener method in the EditDepartment
backing bean shows how to process the selected key value returned from the dialog and to set that row as the current row for editing, followed by forcing the calling page to refresh.CustomViewObjectImpl
that overrides the bindParametersForCollection
method to conditionally uppercase any String-valued bind variable parameter values that happen to have a custom metadata property named Uppercase
set on them in their bind variable definition. You can use the view object editor to inspect the bind variable named TheName
on both the DeptView
and EmpView
view objects to see that they both have this Uppercase
custom metadata property set. Both of these view object specify CustomViewObjectImpl
as their base class in inherit this new, generic functionality that is metadata-driven. To experiment with the example, run the application module and enter a department name is lower/mixed case like "research". Then click on the ":id" icon of the detail Employees view instance and try filtering the employees to ones whose Ename
starts with the letter "s" (entered in lowercase, too).MyViewRowImpl
framework extension class that will achieve that. The DeptView
view object in the example indicates that it should use the MyViewRowImpl
class as its view row implementation class. The Dept
entity object supplies default values for its Loc
and Dname
attributes, and the DeptView
view object specifies a further view-object-attribute-level default value for the Loc
attribute as well. You can use the ADF Business Components tester to try creating a new row to observe the behavior.test()
method in the TestModule
in this example illustrates the important point discussed in section 27.1.3 Understanding View Link Accessors Versus Data Model View Link Instances of the ADF Developer's Guide for Forms/4GL Developers. It highlights how the view link accessor rowset is based on a distinct, system-created view object instance as compared with the developer-created view linked detail view object instance in the data model, despite the fact that they are both instance based on the same base view object definitionEmpView
. The code shows adding an additional runtime WHERE
clause filter to the view link accessor rowset by accessing its view object instance, and then performs a similar task on the data model detail view object instance to show they are indepdent. Run the TestClient.java
class to see the results of the test()
method in action.Example.jspx
page in this workspace illustrates a tree based on the Departments
view object instance. The DeptView
view object definition on which this view instance is based, is viewlinked to the EmpView
view object with a view link accessor named EmployeesInDepartment
. The tree binding in the page definition is configured with two rules in order to show the Dname
attribute for the DeptView
rows, to "drill down" into the EmpView
details for each DeptView
row using the EmployeesInDepartment
view link accessor attribute, and to display the Ename
attribute for each EmpView
in the tree. The tree control has an id
of myTree
so that the panelGroup component surrounding the employee detail information can have its partialTriggers
property set to that id. This way, any user interaction with the tree control will repaint the panelGroup and everything inside it. Since we want to be able to conditionally toggle the rendered
property of the panelBox on and off, combined with the fact that you cannot trigger a partial page refresh event for a UI component that is not rendered, we need to introduce this wrapping panelGroup component to be a stable UI component in the page on which to configure the partial page updating. The nodeStamp
facet of the tree component includes an switcher
component to conditionally render the tree nodes based on the current level of the tree. The current level of the tree is determined by referencing the expression node.hierType.viewDefName
which allows us to access the fully-qualified view definition name of the hierarchy type (representing the tree binding rule) of the current node. This will return a string of "test.model.DeptView
" for the nodes in the tree coming from the DeptView
view object, and a string of "test.model.EmpView
" for the nodes coming from the EmpView
view object. In order to avoid using these "raw" view definition names as the switcher
component's facet names, we introduce a treeLevel
map in the Example
backing bean to map the view definition name to a logical facet name. The entries in this map {(""test.model.DeptView
", "DeptNode
"), ("test.model.EmpView
", "EmpNode
")} are configured declaratively in the faces-config.xml
file using the JSF managed bean facility. The facetName
property of the switcher is set to the EL expression "#{Example.treeLevel[node.hierType.viewDefName]}
" which resolves to the logical facet name corresponding to the view definition name of the current node in the tree, via this map. In other words, DeptView
nodes render using the contents of the DeptNode
switcher facet as a outputText, while EmpView
nodes render using the contents of the EmpNode
facet as a commandLink. The command link is configured to have its action listener invoke the declarative action binding setCurrentRowInEmployeesIteratorWithKey
which is the name of the action binding in the page definition that uses the built-in setCurrentRowWithKey
operation against the EmployeesIterator
iterator binding. This action binding is configured to pass the value of the EL expression "#{node.rowKeyStr}
" as the one parameter this built-in operation requires. It is the stringified row key of the current node in the tree that the user has clicked on. In the Example
backing bean, the onTreeNodeDisclosed
method is configured as the tree's disclosureListener. It has a bit of code to set the set of expanded tree nodes to be only the currently selected node, as well as a line of code that hides the employee information panel box by setting its rendered property to false. The onClickEmployeeCommandLink
is configured as the action of the commandLink in the tree and it includes one line of code that shows the employee info panel box. This panelbox includes a panelForm that shows the employee detail information and allows the user to edit the salary. In order to avoid the tree interaction from posting data, it has its immediate
property set to true. This way, any user change to the form is saved only if the user explicitly clicks on the (Save) button. Last, but not least, the commandLink includes a nested resetActionListener component to ensure that the editable UI components on the page "pull" or "reset" their bound values after the partial page request caused by clicking on the tree. This ensures that the Sal field on the employee detail panel is always correctly showing the value of the current employees salary. [NOTE: An 11g version of this example that has been migrated to use the Apache Trinidad components is available here.]perform-isolated-work
bounded task flow in the example has its Data Control Scope set to "isolated" so that the work it performs is done in a separate transaction. Run the Main.jspx
page to try the demo. The example-dept-region
bounded task flow includes a button on its ExampleDept.jsff
page that navigates to a task flow call activity in the taskflow to perform the work done by the isolated "subroutine" call. The perform-isolated-work
includes a method call activity that invokes the performAppModuleLevelWork()
client interface method in the AppModule
application module. This method accepts an integer representing the department number to modify, and a String representing the value that department's Loc
attribute should be updated to, then it commits the changes. The taskflow accepts parameters which it passes into this method call. The af:commandButton
includes two nested af:setPropertyListener
components to assign values to the requestScope.deptno
and requestScope.locValue
attributes which are referenced by the task flow call to pass these values into the task flow. The page is configured to pass the current row's Deptno
value, and to pass the literal value 'Q
' as the value to assign to the Loc
attribute in the current row. The net effect is that clicking the button will update the current department row to have the value 'Q
' as its LOC
column value (in a separate transaction. The checkbox in the page controls whether or not the Router activity will declaratively refresh the DeptView1Iterator
iterator in the page, or not, upon returning from the task flow method call. If you leave the box unchecked, you can observe the effect of the update committed by a separate transaction by attempting to update the current row in the UI after having clicked the button. You'll see an error that "Another user has change the row with primary key ...". If you perform the refresh, you'll see that the requery causes the LOC
value updated in the separate transaction to be reflected in the web page. The custom PagePhaseListener CalledTaskflowErrorReporterPagePhaseListener
, which is configured in the adf-settings.xml
file, ensures that any exception thrown in the nested taskflow gets reported to the binding container of the page that is marked as an exception handler activity. To see this exception reporting in action, use SQLPlus to lock the row in the DEPT table corresponding to the current row in the web page. Then, when clicking on the button to perform the work in the isolated transaction, you'll see a "Row Already Locked" exception in the UI.**dname like upper('%'||:0||'%')**
, that is a "zeroth" bind variable using the Oracle-style positional bind variable notation. The Panel1.java class was created by dropping a JScrollPane followed by dropping the DeptView1 view object instance as a Table from the data binding palette onto that JScrollPane. The "Go!" button was dropped from the data control palette as an Execute button to cause the query to execute. The field accepting the parameter is just an unbound JTextField named jTextField1. There is code added in two places in Panel1.java that is highlighted with the string ADDED CODE
in the comment preceding it. The first added code short-circuits the initial execute query by setting the underlying view object's maximum fetch size to zero (0) initially, just before the call to refreshControl(). The second added code adds an ActionBindingListener to the action binding named "Execute" that got created when we dropped the "Execute" action from the data control palette. That listener has a couple of lines of code in its beforeActionPerformed()
method that sets the maximum fetch size back to unlimited (-1) and sets the where clause parameter value by position.Boolean
attribute named Selected
has been added. The View
project in the example illustrates a custom ADF Business Components data control class (MyCustomADFBCDataControl
) that extends the default ADFBC data control class (JUApplication
). The custom version overrides the setTransactionModified()
method to avoid dirtying the transaction unless the transaction related to the underlying application module is actually dirty. The companion MyCustomDataControlFactoryImpl
is required to instantiate the custom data control class, and the DataBindings.cpx
file references the fully-qualified class name of the custom data control factory class in the FactoryClass
property of the <BC4JDataControl>
element in the <dataControlUsages
section of that file. Run the Panel1
class to see the code in action. Clicking on the Selected? checkbox toggles the value of the transient flag, as visible by the message that the setSelected()
method in the custom view row class prints to the console, but the JClient toolbar does not enable the Commit or Rollback buttons until you actually modify some persistent attributes.TestPage.jspx
with a button whose action navigates to an XSQL page TestPage.xsql
to format the data in the iterator using XML/XSLT techniques. The TestPage.xsql
uses the custom XSQL action class test.view.xsql.ADFViewObject
that is identical to the one that appeared in the ADF Toy Store Demo, only refactored to live in a different package. The interesting things to note about getting this example to work are: (1) The web.xml file defines a filter-mapping
for the *.xsql
URL pattern, (2) the DataBindings.cpx
has been modified in the code editor to contain an additional page mapping entry for the /TestPage.xsql
path so ADF knows what page definition you want to use for that page, and (3) by insuring that the URL requesting the page contains the "/faces/" prefix in the path, this triggers the handling of the request by the Faces Servlet (and indirectly the ADFPhaseListener
that is registered to be used by the Faces infrastructure). This makes sure that the request-scoped bindings
attribute is correctly set to reference the current page's runtime binding container.ORDImage
. Run the CreateTable.sql
script in thae Model
project to create the IMAGE_TABLE
table. See the README.TXT
file in the ViewController
project for some of the manual steps that were necessary to create the working example since JDeveloper 10.1.3 does not currently offer automatic design-time support for ORDImage attributes, even though the runtime support is there as the example illustrates.PageWithDataAndGraph.jspx
page in this example illustrates how you can still use the <graph:Graph>
tag inside the JSF page by wrapping it with an <f:verbatim>
tag. In addition, to allow this non-JSF component to still be updated dynamically (AJAX-style) you further wrap the <f:verbatim>
tag by a panel like panelGroup
. The backing bean for the page illustrates how to allow an updateable UIComponent in a table to trigger partial page rendering for other elements in the page by programmatically calling the ADF Faces addPartialTarget
API. I initially created the graph tag by creating a regular (i.e. non-JSF) JSPX page, and dropping the EmployeesInDepartment
data collection from the Data Control Palette as a Graph, then I copied the graph tag and its corresponding tag namespace to the page I wanted to include it in. Lastly, I then copied the Graph binding into the page definition of the target page, and ensured that its binding id was unique. After renaming the graph binding to be unique, I updated the EL expression in the Graph
tag's data
attribute to reference the renamed graph binding. Finally, I edited the chart's visual properties by double-clicking on the BIGraphDef1.xml
node in the navigator and using the graph definition editor, the Structure Window, and the Property Inspector. Also of interest is that the chart binding is setup to graph a transient entity object attribute called TotalComp
which simply is the sum of the Sal
and Comp
attributes to provide an employees total compensation. Notice that you can edit the Sal
or Comm
values in the table and the graph dynamically updates. You can also use the navigation list or the (Next) / (Previous) buttons to change the department, and the graph updates to reflect the employees in the current department. The Emp
entity in the Model
project uses the generic, automatic attribute recalculation logic explained in section 26.8 Implementing Automatic Attribute Recalculation of the ADF Developers Guide for Forms/4GL Developers, and implemented in the SRDemo Sample ADFBC Version.Page1.jspx
in this example illustrates how to bind an af:selectOneChoice
dropdown list to a session-scope managed bean property #{myBean.selectedDepartment}
. Rather than using an ADF list binding (which is designed to target a row attribute or page definition variable), instead we create a table binding in the page definition (DeptView1
) that is bound to the DeptView1Iterator
iterator binding and which exposes the attributes we want to use for the label and value in the list of valid choices (Dname
and Deptno
, respectively). Nested inside the af:selectOneChoice
an af:forEach
loops over the List
-valued rangeSet
property that the table/range binding exposes, and create the select list items based on the data in the rows of theDeptView1Iterator
(which is bound to the default rowset of the DeptView1
view object instance in theAppModule
application module.MyBean
bean is registered in the adfc-config.xml
file and the method action binding in the page def for view1.jspx
references the bean using the InstanceName
property of the binding. Note that the EL expression you provide needs to use the older-style with the dollar-sign rather than the pound-sign. Run the page in the example and click the only button in the page. You'll see the message "Foo" print out in the log window.EmployeesView
view object that queries employees from the HR schema. It contains four levels of reference entity usages, showing for the current employee: (1) the name of the Department they work in, (2) the StreetAddress of the Location of that Department , (3) the Country name in which that location resides, and (4) the Region name in which that country resides. Run the DemoModule
in the ADF Business Components browser and double-click on the Employees
view object instance to see its data. Experiment with changing the DepartmentId foreign key attribute of an existing employee to notice that all four levels of reference information correctly synchronize. You can use the AllDepartments
view object instance to remind yourself of what some valid department id numbers are, and you can use the four-level master/detail-coordinated view object instances (Regions
-> Countries
-> Locations
-> Departments
) to understand better what to expect when changing the DepartmentId
. The trick to having this multi-level reference information synchronization work correctly is that at each level the foreign key attribute referencing the next level is included in the view object's attribute list. Notice that the attributes whose names start with "PK" in the view object have a Display control hint set to "Hide", so the ADF Business Components browser hides them.USERS
, PRODUCTS
, EXPERTISE_AREAS
. The Staff
view object is an entity-based view object based on the User
entity object that has a WHERE clause to retrieve only the users having a USER_ROLE
is either 'technician'
or 'manager'
. The ProductExpertiseAreas
is an entity-based, expert-mode view object whose SQL statement is crafted to use outer joins to return exactly one row for each available product. The view object has the Product
entity object as its primary entity usage, and has ExpertiseArea
as a secondary, reference entity usage. It defines a named bind variable Bind_UserId
that is consciously named exactly the same as the system bind variable the framework will add for the view link between Staff
and ProductExpertiseAreas
(based on the UserId
). That view link has a customized view link SQL clause to allow the USER_ID
to be NULL
so that the detail rowset retains a row for every product, even when the current user does not have expertise for that product currently. Due to the outer join, rows retrieved retrieved in the ProductExpertiseAreas
view object will have a non-null ExpertiseArea
entity row part when the current user has expertise area in that product. Conversely, if the current user does not have expertise in a given product, the ExpertiseArea
entity row part will be a blank entity row. To simplify the client, we introduce a transient Boolean
-valued attribute on the ProductExpertiseAreas
view object named HasExpertise
. We override the getHasExpertise()
and setHasExpertise()
methods in the view row class to handle retrieving and setting this transient attribute. The setter method handles either creating a new ExpertiseArea
instance corresponding to the current userId
and prodId
combination, or removing an existing entity row, depending on whether HasExpertise
is being toggled on or off. Implementing a model-centric solution gives three interesting benefits: (1) You can test and debug the complete functionality just using the ADF Busines Components Tester, (2) UI can just bind a checkbox to the Boolean HasExpertise
property, and the functionality works the same in JSF, JSP, or Swing. The ViewController
project contains a simple JSF page that displays a table containing a selectBooleanCheckbox
bound directly to the HasExpertise
attribute. In other words, it is not using a special tableSelectMany
component in its selection facet. The checkbox is just a part of the "data" in each row. Note: This example uses the SRDEMO schema from the SRDemo sample application.manage-employees
task flow has its transaction
property set to new-transction
to indicate that it should only be used as a top-level task when no other current transaction is in effect. The modify-employee
task flow has its transaction
property set to requires-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. The create-department
task flow has its transaction
property set to requires-transaction
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. To run the example, run the ViewController
project. The home page has links that start either the create-department
task flow, or the manage-employees
task flow. As part of the task of managing employees, creating or editing an employee calls the modify-employee
task flow. While modifying an employee, if you need to create a new department, the create-department
task flow is called. The 'Cancel' return actions of the modify-employee
and create-department
are configured to have the restore-save-point
property set to true so that, if they are not the task flow that is controlling the transaction, then they will use savepoints to allow canceling any pending work performed in that flow without rolling back the entire transaction. Experiment with performing some changes and doing different combinations of (OK) and (Cancel) buttons to see the effect of the nested transaction support. Before deciding to save or cancel all changes you have made to employees, you can see the modified rows' data in italics in the table, with the modified attributes in bold/italic. This display is facilitated by the use of custom row-specific attribute hints named rowState
and valueChanged
that are enabled due to the use of the CustomViewRowImpl
framework extension class in the FrameworkExtensions
project. The CustomViewObject
in that project works around a couple of issues in the 11.1.1 release related to the quick query component (forcing the view criteria item operator to be "Starts With" instead of "Like", and working around bug# 7660871 which causes the quick query criteria to not be applied correctly in some situations). The CustomEntityImpl
in the project implements a generic solution for declarative control over the posting order of associated (but not composed) entities by allowing an entity to specify a custom PostAfterAccessors
property whose value is a comma-separated list of association accessors which should be checked for a new referenced row that should be posted before the current entity. The Emp
entity in the example is configured with that property in order to cause a new, referenced Dept
entity instance to post first.EmployeesView
view definition, but we use two distinct instances so that ADFBC can optimize the SELECT list for both use cases. The search page only shows the EmployeeId
, LastName
, and FirstName
of the employee, so only those three columns are selected from the database. In the details page, all EmployeesView
columns are bound to UI components (and reflected in page definition attribute bindings) exceptfor the ManagerId
and CommissionPct
fields, so the SELECT
list reflects this extended set of fields. The EmployeesView
view object is marked to have SQLMode = "Declarative" so that ADFBC can calculate its runtime SELECT
list based on the attributes bound in the current page definition. All of the EmployeesView
attributes except for the primary key EmployeeId
are marked with IsSelected='false'
(labeled in the design time user interface as "Selected in Query" = false. This allows the declarative SQLMode view object mechanism to prune these attributes out of the SELECT
list when they are not needed by the current page. The pageDef of the View2 page fragment contains a "RequeryDetail" action binding in the View2.jsff page's pageDef This is wired to the built-in action "ExecuteWithParams" The value of the VO's bind parameter comes from the EL expression #{bindings.SummaryEmployeeId.inputValue}
. This SummaryEmployeeId
Attributes binding to the View2 pagedef to more easily bind to the EmployeeId attribute value in the current row of the summary VO instance's iterator binding The default activity of the employee-details
taskflow invokes this "RequeryDetail" action binding to execute the detail query when the task flow first starts. The DataBindings.cpx file is configured so that this method activity uses the same pageDef as the "View2" view activity that it forwards to. This is important so that the VO is executed in the context of the pageDef that has all the attribute bindings that will inform ADFM/ADFBC of what attributes to select. A ValueChangeListener in the View2.java backing bean to execute the "RequeryDetail" action binding Notice that when you run the Home.jspx
page, you can perform a search in the summary page to identify a set of desired results. Clicking on the (Details) button takes you to the details page. On the details page you can use the selectOneChoice "navigation list" at the top to navigate the current row in the SummaryEmployeesView
VO instance's result set. The ValueChangeListener in the View2 backing bean executes the "RequeryDetail" action binding to re-execute the detail query with the new bind variable value related to the new current row in the SummaryEmployeesView. If you click the (Summary) button, you return to the summary page with the current row reflecting the current row you had see in the details. In order to prevent any implicit executeQuery calls on the DetailEmployeesView
view object instance, we have set the Refresh
property of the DetailEmployeesViewIterator
iterator binding in the view2
page's pageDef to the value of "never". Since the default taskflow activity and the backing bean value change listener are already configured to (re)execute the VO query when we need it to, we do not need or want any implicit query execution to be performed (otherwise the VO might be executed with a null bind variable value and "waste" a trip to the database only to have no rows returned). The TracingViewObjectImpl
class logs the queries being executed to more easily see the declarative-mode SQL SELECT list pruning optimization at work.NotifyChangeOnValidatedEntityImpl
and a framework extension class for view objects OnlyAddNewRowIfValidViewObjectImpl
. The entity framework extension class overrides the validateEntity()
framework method to send an attribute change notification after the entity object successfully passes validation. The view object framework extension class exposes getter and setter methods for a boolean property named onlyAddNewRowIfValid
and the overridden rowQualifies()
method ensures that a new row does not qualify for inclusion in the view object's rowsets unless it is valid. The AppModuleImpl
class overrides the prepareSession()
method to set the property on the DeptView1
view object instance. To run the demo, run the view1.jspx
page. Click (Create) to create a new row. A data entry form appears at the top of the page. To cause a validation error to occur, leave the fields blank, or try entering a Dname
value of upper-case X
and a Loc
value of upper-case Y
. Notice that until the row validates, it's not added into the table.CustomApplicationModuleImpl
contains a method enableDetailsForViewInstances()
that accepts an array of view instance names representing the views displayed on the current page. It proceeds to explore the view link instances in the data model related to the supplied view object names, and enable/disable master/detail coordination as appropriate so that only the view objects in the supplied list are queried. The ExampleModule
application module extends this base class, and exposes the method on its client interface. In the ViewController
project, the CustomJSFPageLifecycle
class overrides the prepareModel()
method to augment this phase of page processing to add a call to the abovementioned enableDetailsForViewInstances()
method, passing in a list of view instances corresponding to the iterator bindings in the current page's binding container. Another framework extension class CustomViewObjectImpl
overrides the executeQueryForCollection()
method to short-circuit any attempt to query a detail view object when the framework-supplied named bind variable connecting it to the current master row has a null value. It accomplishes this by temporarily setting the maxFetchSize
for the view object to zero, calling the super, then restoring the original maxFetchSize
value. The view controller project is instrumented with a customized DCJboDataControl in order to add educational log output at the beginning and ending of each request, and the CustomViewObjectImpl
class overrides the bindParametersForCollection
method to log the execution of each query sent to the database. These two elements make it easier to observe which view object queries are getting executed on each request for educational purposes. The data model includes a main "CurrentEmployee" view over the EMP table, with a number of first-level, and one 2nd-level detail queries. The data in these detail queries is just dummy detail data queried again from cartesion products over the same EMP table to simulate various kinds of detail data. There are four JSF pages in demo: Home.jspx
, Drill.jspx
, AnotherPage.jspx
, and ThirdPage.jspx
. Each shows some subset of the overall data model. Again for educational purposes, you can experiment with enabling or disabling the generic optmizations using the value of the AM configuration parameter namedenable.optimizations
. The ExampleModule
comes with two different configurations, one that has this parameter enabled, and the other where its disabled. By selecting theDataBindings.cpx
file in the ViewController
project, and selecting the ExampleModuleDataControl
node under the dataControlUsages
section in the Structure Window, you can use the Property Inspector to set the Configuration
property to toggle between using the ExampleModuleLocal
configuration where the optimizations are disabled and theExampleModuleLocalOptimizationsEnabled
configuration where the optimizations are enabled. The example is configured by default to use the configuration that has the optimizationsenabled. By experimenting with the various pages in the demo that present different parts of the overall hierarchy of view objects in the data model, you can observe in the Log Window the difference in which queries get executed as you navigate to different pages, navigate to different master rows with the (Previous)/(Next) buttons, drill down to third-level details, etc. The ThirdPage
also illustrates two techniques for forcing data in view objects to be requeried to have the user eagerly see the very latest data. The technique illustrated for EmpDetails5
involves setting the CacheResults
property to false
on its iterator binding. This causes the framework to perform a clearCache
on the view object at the end of the request. Requerying the EmpDetails6
iterator is done using an invokeAction
in the page definition, bound to an ExecuteQuery
action binding. This invokeAction
has a RefreshCondition
set to conditionally perform the data-refreshing query when the page is rendered, but not during post-back. In both cases you can observe when the data is refreshed by looking at the value of the CurrentTime
column in the tables. In your testing, make sure to visit all of the pages (even several times) and perform various levels of change master/detail current rows to get the full effect of the difference between the optimized and unoptimized cases.dname
to the named bind variable on the DeptViewWithBindVariable
. The second page illustrates an alternative technique that may be applicable if the where clause that needs to be added is too complex or too dynamic to create at design time. It uses a combination of an invokeAction executeable in the page definition, bound to a method action, to conditionally invokes either the clearFilterOnDeptView()
or the filterDeptViewByDname()
on the application module's custom interface. Try tacking on an additional dname
URL parameter to either of the pages like ?dname=acc or dname=s to see the effect of the filter. Intererstingly, the pages use the ADF Faces af:switcher
component to conditionally present the results in a table or in a form depending on how many results are identified by the query.CreateTables.sql
script to create the OPTIONAL_REQUIRED_TEST
table, run the TestPage.jspx
to see that when a row has its RowType
attribute set to the value A
then the ValueA
inputText component renders, while if its RowType
attribute is instead set to the value B
then the ValueB
inputText field renders. These fields set the showRequired
property to true
to show the required field indicator to the end-user even though the underlying ValueA
and ValueB
attribute are not marked as mandatory. The conditional mandatory validation is performed in the TestEntity
entity object using entity-level method validators. The radio group UI component is marked to have its autoSubmit
property true, and the ValueChangeListener
method in the backing bean for the page sets a request-scope attribute as a flag to indicate that the row type is changing. This flag is then referenced by the ConditionalValidationPageController
- identical in function to the one you might have seen in Example# 62 "Cascading Lists in JSF" - which is configured to be used by the TestPage by setting the ControllerClass
property of its page definition. This customized page controller avoids validating the row if it detects that the row type is changing. The conditional rendering of the ValueA or ValueB inputText components is handled declaratively using an appropriate EL expression for the component's rendered
property. When the autoSubmit radio group (with component id "RowTypeRadio") causes its partial-page request, the panelForm component in which the ValueA and ValueB inputText's reside is declaratively "repainted" by having its partialTriggers
attribute set to that "RowTypeRadio" id value. By performing the partial-page triggering on the enclosing panelForm component, you avoid the gotchas related to having PPR events delivered to currently-unrendered components.TestClient
example uses an instance of a "People" VO but illustrates that the client can cast "MenRow" rows to the MenRow interface supporting a getManAttr()
, and "WomenRow" rows can be cast to a WomenRow
interface to access a getWomanAttr()
method that is specific to women. ADF Business Components supports two distinct kinds of polymorphism: ViewRow polymorphism (which you setup at the application module level) and EntityObject
polymorphism (which you setup on an individual VO). This example shows both kinds of polymorphism at work, but they can be used independently.Departments.jspx
page illustrates how to use an invokeAction
combined with an Execute
operation binding (and appropriate settings for the Refresh
and RefreshCondition
properties of the invokeAction
) to conditionally refresh a view object's query results based on the presence of a request-scope flag attribute. You can read more about these settings in section 10.5.5 How to Use Refresh Correctly for InvokeAction and Iterator Bindings. The AnotherPage.jspx
, to which you can navigate from Departments.jspx
, contains two af:commandLink
components that navigate back to the Departments.jspx
page. One of those links uses a nested, af:setActionListener
component to declaratively set the value of the request-scope attribute named refreshFlag
to the value true
. The presence of this flag attribute causes the invokeAction
mentioned above to force a requery of the rows. The DeptView
view object is instrumented to print a diagnostic message to let you see when its base query and its estimated row count query get executed. The range size of the iterator binding used on this page is set to 2 to you can see the table paging behavior even with only 4 rows of DEPT table data. When you use an af:table
and have a range size of N
set on your iterator binding to show only N
rows per page, then the estimated row count query will be used since the table component needs that count to build its paging control. The DepartmentsProgrammatic.jspx
page is nearly identical to the Departments.jspx
page, except that it illustrates how to accomplish the conditional requery of the view object from code using the basic technique described in section 10.5.4.3 Using Custom ADF Page Lifecycle to Invoke an onPageLoad Backing Bean Method section of the ADF Developer's Guide for Forms/4GL Developers. It also uses a range size of 2. The DepartmentsAlwaysRequestOnPageEntry.jspx
page illustrates how to create a page the refreshes its data each time you navigate to it from another page (but not when you post-back to it from the same page). And finally, the DepartmentsTimedAutoRefresh.jspx
page illustrates how to use the combination of an af:poll
component and an Execute
action binding to refresh the page without user intervention every 5000 milliseconds (i.e. 5 seconds). Notice that the af:poll
component has an id
attribute set to the value "timer
" and that an af:outputText
on the page as well as the af:table
UI components list this "timer
" id in their partialTriggers
property so that they will be partial-page refreshed to "repaint" any updated results. The af:outputText
just shows the current date/time to make it easier to see when the timer is firing. In constrast to the other three pages, this page has no explicit (Refresh) button, so to create the Execute
action binding in the page definition I selected the Go to Page Definition choice in right mouse context menu of the JSPX page visual editor window and then used the Structure Window view of the page definition for that page to select the bindings heading and {Insert Inside bindings -> action} right-mouse menu choice to create an action binding for the DepartmentsIterator
using the built-in operation named Execute
.ProgrammaticScroll
JSF backing bean in this example illustrates three different approaches to perform programmatic scrolling of the 25 rows in the HR schema's COUNTRIES
table. Run the ProgrammaticScroll.jspx
page and try using the (<<) and (>>) buttons, optionally changing the scrolling increment value in the text field located between these buttons. You can use the drop-down list to pick among the three different programmatic scrolling approaches.selectOneChoice
and a selectOneRadio
, each with its respective value bound to an Integer
-valued backing bean property. The switcher
controls use the respective property value to toggle the display of an appropriate PanelBox, and the partialTriggers
property setup on the containing panelGroup
component causes the panel to repaint when the value of the respective selectOneXXX component changes.readXML
/writeXML
API's. By passing appropriate command line arguments, it will dump the XML for a view object to a file, or read in an XML file and "ingest" the contents in the file and apply inserts, updates, and deletes (and engaging entity-object level business validation) as appropriate.Collection
-valued property, you need to manually trigger the rebuild of the iterator. This sample illustrates the line of code required to do that in the ResultsAction.java
custom data action class.JboExceptionHandler
. This allows it to receive the handleWarning()
notification when any business components warning is signalled in custom code. As illustrated in the DeptImpl
class in the example, you signal a warning by constructing an instance of the JboWarning
class and passing it to the addWarning()
method of the ApplicationModule
. To distinguish between a warning and a purely information message, the example introduces a subclass InformationalMessage
that extends JboWarning
and the handleWarning()
method in the CustomFacesPageLifecycle
class reports the warning as a FacesMessage
with the appropriate severity. To experiment with the demo, run the TestPage.jspx
page and try entering the value "ERROR
" for the Dname
field. That will cause an attribute-level validation to fail for that attribute. If you instead enter a Dname
value of "WARN
", then you'll see a warning reported. And if you simply make a change and save it successfully, you'll see a purely informational message reported.%
or _
). It also shows how you might force the end user to provide at least three leading non-wildcard characters in the search criteria so that the index on the selectively-required column is more selective and hence more performant. To try the demo, run the TestPage.jspx
. Try submitting the search form without entering any of the selective-required criteria items (Ename
and Job
in our example). You'll see an error asking the user to provide a value for at least one of those fields. If you try to provide a search value like %
and click (Search), you'll get a custom exception saying that your search cannot start with a wildcard. If you enter two letters like sm
(to find employee SMITH
) and click (Search), you'll get a different error telling you to please enter at least three characters.TestPage.jspx
page in this example contains an ADF Form dropped from the data control palette based on the Employees
view object. It consciously chooses to avoid performing client-side required-field validation by notsetting the required
property of the inputText
controls (which defaults to false
). Instead, it sets the showRequired
property of the components to the same EL expression that would normally be used by the client-side required
property. This has the effect that required fields are rendered with the visual required indicator, but the mandatory field validation is performed by the ADF BC layer instead of on the client. The ViewController
project includes a custom ADF Phase Listener, a custom Faces Page Lifecycle class, and a custom ADF Error Handler class just like the SRDemo sample application (explained in section 20.8.1of the Dev Guide) in order to customize the error reporting behavior. A few enhancements that have been made in this example's version of these classes are (1) The use of the getComponentClientId
method on the FacesCtrlAttrsBinding
object to retrieve the Faces component client id in order to construct a FacesMessage
that is specifically related to a given UI component, (2) the use of the ADF Faces LabeledFacesMessage
class to create a FacesMessage
that remembers the label of the component to which it's associated, and (3) the special-case handling of AttrSetValException
errors for mandatory fields in order to display a custom string from the CustomMessageBundle using the end-user-friendly UI label string of the attribute name that is required. In addition, since we've disabled the use of client-side mandatory field enforcement, combined with the fact that the ADF model layer does not by default validate the binding container if the end-user has not entered any values into any of the fields, we've overridden the validateModelUpdates()
method in the CustomFacesPageLifecycle
class in order to call a helper method forceValidationOfNewRowWithAllNullMandatoryAttributes()
which does what it's name suggests. This ensures that if the user creates a new row and immediately clicks (Save) without entering any values for any fields, they see the expected errors about any mandatory fields. In addition, by running the TestHomePage.jspx
you can running two different standalone create forms by clicking on an appropriate command button. The CreateNewEmployeePage.jspx
page is a more traditional, declarative create form based directly on a view object as described in section 13.6 Creating an Input Form of the Dev Guide. The CreateNewEmployeePageUsingAMMethodViaEntity.jspx
page shows an alternative approach that requires coding an application module custom method, exposed on its client interface, dropped onto the page as an ADF Parameter Form. Note that using the method-based approach, the method parameters are named the same as the underlying entity object attributes so that the created page definition variables and bindings have names that match the entity object attributes. Also note that the application module method uses an entity-based view object to create the new employee rather than directly creating the entity object so that the automatic bundled-exception handling is preserved. Try setting the "Ename" to the value XXXXX
in order to observe what happens when an attribute-level validation rule fails. Finally, also note that the UI Hints for the parameter form page definition variables are defined in a message bundle specific to the page definition. With the view object based approach, those are automatically inherited from the underlying entity object. The example also illustrates the use of selectOneChoice "dropdown list" controls for mandatory fields. Their corresponding list binding is configured to allow a null value to correctly handle the fact that in a newly-created row the value of that attribute will be null if your entity object does not provide an alternative default value. The example contains workaround code in the form of the viewcontroller.util.ListBindingHelper
to workaround bug# 5930745 where an additional blank entry can inadvertently be added to the list when the list binding related to a selectOneChoice control has a validation exception due to its being mandatory. Finally, the backing beans for the CreateNewEmployeePage
and the CreateNewEmployeePageUsingAMMethodViaEntity
pages were created in order to workaround bug# 5930784 where ADF/JSF incorrectly performs page navigation even when exceptions have been registered on the current page. By double-clicking on the declaratively-bound button and allowing JDeveloper to generate the ADF binding code in the backing bean, this reliably avoids page navigation when the action binding has caused exceptions to be raised. If you notice that sometimes the attribute-level exceptions are reported with errors that appear next to the fields and other times with errors reported only at the top, this is related to bugs# 5918276 and 5929646 where the getComponentClientId()
function of the FacesCtrlAttrsBinding
returns null if the binding has not previously "seen" a non-null value during the session as well as the fact that the FacesCtrlListBinding
does not support a getComponentClientId()
method. It is the ability to retrieve the client id of the component related to a binding that allows this example's custom error reporting to construct an ADF Faces Message that will be rendered next to the component to which it relates. You can run the demo with your browser set to prefer "Italian [it]" to see the page translated in Italian.AppModule
contains only a single view instance in the data model called Departments
. The data model does not contain any view link instance or any "Employees" view instance. While those are useful for some master/detail UI scenarios, they are not needed/used-by a tree binding. Instead, the tree binding materializes the employee rows in each department by directly interacting with each DeptView
row's view link accessor attribute, producing separate EmpView
row sets for each of the distinct department rows that the user expands to see the details for. The example depends on a slightly modified version of the usual EMP
and DEPT
tables called DEPT_RANK
and EMP_WITH_RANK
. The latter table contains one additional column named EMP_RANK
which is used to order the employees in a given department. You should run the included CreateTables.sql
script in your SCOTT account before trying the demo. A few interesting points that the demo shows off is the use of the generic TableSupportBean
which exposes some useful properties to the EL expression language like selectedNodeRowIterator
and selectedNodeRowKey
. The method action bindings in the page definition for the TreePage.jspx
page reference these properties in order to declaratively pass the correct RowIterator and the key of the selected row in the table to the application module custom methods which resequence the selected employee's rank either up or down. Also note that in the Tuning section of the General panel for the DeptView
view object, the Retain View Link Accessor Rowset checkbox is checked on. This setting is required for the tree binding to correctly reflect programmatic manipulations in the pending transaction made by developer-written code in the AM.EmployeePanel
in this example was created using the Panel wizard in the ADF Swing category in the New Gallery. Then, I manually parameterized its data control and iterator name using two page definition parameters named dcParam
and rsiParam
(where 'rsi' is short for RowSetIterator). The example includes four other ADF Swing panels that then reuse the EmployeePanel
, each passing in appropriate values for the dcParam
and rsiParam
parameters. To drop an instance of a reusable ADF Swing panel, simply select the Component Palette page named ADF Swing Regions and you'll see that the EmployeePanel
automatically appears there as a reusable component. When you drag/drop the EmployeePanel
onto a panel where you want to use it, JDeveloper pops up a dialog asking you to fill in EL expressions to give appropriate values for the panel's dcParam
and rsiParam
parameters. In this way, you can reuse the same panel with different view objects. You'll see a special "page" binding appear in the Executablessection of the including panel's page definition, along with metadata capturing the EL expressions you've entered. The example features a Panel1
panel that reuses EmployeePanel
to display the results of the data collection named EmpView1
in TestModule1DataControl
. A Panel2
panel reuses the EmployeePanel
panel bound to the data collection named AllEmployees
in the TestModule2DataControl
, based on a different application module. The Panel3
panel illustrates that you can reuse the EmployeePanel
for a data collection that is a view-linked detail view object instance, in particular the EmployeesInDepartment
data collection in the TestModule2DataControl
. And finally, the Panel4
panel shows that you can reuse the EmployeesPanel
bound to a view object that's completely different than the original one that was designed to create it, provided that it has the expected attribute names with the expected data types. In this case, it is bound to the data collection named EmployeesReadOnly
in the TestModule2DataControl
, an instance of the non-entity-based (and hence read-only) view object of the same name.DataForwardAction
to automatically use the String
-based result of a declaratively-invoked business service method as the name of the Struts forward to use for "next page" navigation.handleError()
method to conditionally handle the JBO-35007 exception ('Row currency has changed since the user interface was rendered.') in a way that might be more useful to the end-user. This error can arise if the user accidentally gets the current row in and editor form out of sync with the current row in its underlying iterator. One classic way this can occur is if the user is running Internet Explorer and presses the [Ctrl]-[N] key to "fork" a new window with the current edit form (typically in an innocent attempt to view/edit two different rows side by side using two separate browser windows. Another way this error can occur is through use of the browser back button. To observe the custom behavior, try running the listEmployee.jspx
page using Internet Explorer. Select an employee in the table and click the (Edit) button. When the edit form appears, press [Ctrl]-[N] to open a new browser window with the same form. Then, in that new window, press (Back to List), select a different employee row and click (Edit) again. At this point, return to the original window and click the (Submit) button on the edit form. Since this form is editing a row that is no longer the current row in the underlying iterator, a JBO-35007 error will be signalled, however the ControllerClass
property of the editEmployeePageDef
XML file is configured declaratively to use the custom page controller RedirectJBO35007ExceptionToAnotherPagePageController
(in the bug.controller
package) to handle the JBO-35007 and redirect to a problem.jspx
page that tells the user something about what just happened, and gives them a way to get back to the list page. If you edit the value of the ControllerClass
property of the editEmployeePageDef
to be instead the name of the SetRequestScopeAttributeFlagForJBO35007PageController
class instead, then when you repeat the above scenario to cause the JBO-35007 error to occur you'll see that instead the page becomes disabled and only allows the user to return to the list page.autoSubmit
property of the af:tableSelectOne
component your af:table
's selection facet, then it can be a little puzzling why the other information on the page does not update when you change the current row. This example illustrates how to declaratively configure your af:table
and an af:panelForm
that displays additional information about the selected row in the table. The secret lies in configuring the partialTriggers
property of the component that you want to be updated when the table selection changes. In the example, you see that I'd assigned an id
of myTable
to the af:table
component, and on the af:panelForm
component I've listed myTable
as one of the component id's that should trigger a partial-page request "repaint" (of the af:panelForm
). To allow you to experiment with toggling the autoSubmit
property of the tableSelectOne
on or off at runtime, I've configured a simple SessionSettings
session-scoped managed bean that has a single property named useAutoSubmit
. By setting the value
property of the af:selectBooleanCheckbox
at the top of the page to the EL expression #{SessionSettings.useAutoSubmit}
we can allow the user to use the checkbox to toggle the value of that managed bean property. By also using the same EL expression #{SessionSettings.useAutoSubmit}
for the value of the af:tableSelectOne
component's autoSubmit
property, we allow the changing value of the managed bean property to influence the runtime behvaior of the table selection to be auto-submitting or not. Finally, since a change in state of the checkbox needs to cause the table to repaint, I have assigned an id
value of tableAutoSubmitControl
to the checkbox, and listed that tableAutoSubmitControl
component id in the partialTriggers
property of the af:table
. At runtime, you'll see that if auto-submit it off, then the current row changes when you click on the (testCurrentRow) button. If auto-submit is on, then the current row changes as soon as you click into the radio group button on the new current row. In both cases, the panelForm
to the right is updated to reflect the values of the new current row. The (testCurrentRow) button is a bound declaratively to the testCurrentRow()
method on the application module's client interface. It was created by dropping that method from the Data Control Palette onto the page. When you click on the button, you can see that the getDeptView1().getCurrentRow()
method call inside that method already "sees" the new current row (and prints out its Deptno
value to the console).currentSelectedValue1
and currentSelectedValue2
as temporary storage for the chosen attribute value from the selected row. The corresponding attribute bindings currentSelectedValue1Binding
and currentSelectedValue2Binding
are bound to these variables. Where these attribute bindings are being used in the demo, you could use any attribute binding that represented where you wanted to set the current value of the selected radio group.EmpView
editing form. The EmpView
view object includes the Emp
entity object as its primary entity usage, as well as the Dept
entity object as a secondary, reference entity usage, in order to display the related Loc
value for the department to which the current employee is assigned. The Deptno
is bound to a af:selectOneChoice
component whose autoSubmit
property has been set to true
. This causes a PPR postback whenever the end-user changes the value of the list. The Loc
field is displayed as a read-only af:inputText
, and has its partialTriggers
property set to the value of the id of the af:selectOneChoice
component ("DeptnoPoplist
") that should trigger the partial page re-paint of the that Loc``af:inputText
component. When you run the page notice that if you navigate between rows in the EmpView
the PPR counter at the top of the page increments to illustrate that the entire JSF page is being re-rendered. In contrast, if you just change the value of the autoSubmit-enabled Deptno
poplist, then the value of the corresponding Loc
field repaints on the page, but the PPR counter at the top stays the same (since only the bit of the page that needed to be repainted was redrawn). Notice that other than the PPRDemo
managed bean that facilities showing a counter on the page to better visualize when PPR is occurring and not, the example requires no Java code to implement.ExecuteWithParams
built-in operation dropped onto a task flow method call activity that is marked as the default activity to initialize the task flow by setting some view object bind variable values based on task flow parameters passed in from the caller. The TestTaskFlow.jspx
page has two buttons that are configured to invoke the bounded task flow named show-employees-for-dept
, passing in the value of the department number whose employees should be visualized. The task flow defines a parameter named p_deptno
whose value is stored into the pageFlowScope attribute named p_deptno
using the EL expression #{pageFlowScope.p_deptno}
. The ExecuteWithParams
operation for the EmployeesByDepartment
view object instance (of type EmpView
) was dropped onto a method call activity that has been set as the task flow's default activity. The EmployeesByDepartment
view object instance has been configured in the application module data model panel to declaratively apply the view criteria named ByDepartment
, so when you drop the ExecuteWithParams
operation for this view object instance, the list of appropriate bind variables appears in the dialog. The varDeptno
bind variable's EL expression is configured on the ExecuteWithParams
binding in the page definition to the expression #{pageFlowScope.p_deptno}
to pickup the value to assign the bind variable from the task flow parameter passed in. If you right-click on the method call activity and choose "Go to Page Definition" notice that the EmployeesByDepartmentIterator
iterator binding's Refresh
property is configured to the value never
to avoid the ADF framework's performing any implicit refreshing of the iterator binding before the action binding has a chance to set the view object bind variables.Dept
entity object is based on the DEPT_WITH_OBJ_VERSION
table. This is the standard DEPT
table to which has been added a single, additional NUMBER
column named OBJECT_VERSION
. The ObjectVersion
attribute in the Dept
entity is marked as a 'Change Indicator' attribute, and it's also marked as a 'History Column' of type 'version number'. The history column setting tells ADF to automatically set the value of this column to 1 in a newly created row, and to increment the version number each time the row is modified and saved. The change indicator setting tells ADF to compare only the value of this attribute in order to determine whether the row has been changed by another user or not. In the absence of a change indicator attribute, ADF must compare the values of all persistent attributes which can take longer. The view1.jspx
page uses the tr:inputHidden
component to include the ObjectVersion
attribute value in the page. By doing this, the object version of the row being edited by user U2 will be submitted to the server along with the other attribute values, and it will be used to compare against the ObjectVersion
attribute value of the row being edited. Since these values differ, the RowInconsistentException
is thrown as desired. Try re-enabling the AM Pooling to convince yourself that the technique will work under normal circumstances as well. To run the example, start by running the CreateTables.sql
script to create the DEPT_WITH_OBJ_VERSION
table. Then run the view1.jspx
page. Once the page appears in your default browser, copy the URL http://localhost:7101/SignalRowInconsistent/faces/view1
and paste it into a different browser. For example, if Google Chrome is your default browser, paste it into Firefox or Internet Explorer. This will allow you to test having two distinct browser users using your application. Both browsers should be looking at the row for DEPTNO=10. In the first browser window, update the value of Dname
and click (Commit). Now the second browser is looking at a "stale" value for the Dname
. In this second browser, update the value of Loc
and click (Commit). The user gets the expected error "Another user has changed the row with primary key oracle.jbo.Key[10]". The AppModule
application module in the example has its 'Enable Application Module Pooling' configuration setting (jbo.ampool.doampooling) set to false for testing purposes. Your application module will never have this pooling setting disabled in production, but it is useful for testing the activation-safety of your application module by stress-testing the passivation/activation on each HTTP request.<af:returnActionListener>
and <af:setActionListener>
as nested children components of the submit button. These cause the popup dialog page to return to the calling page and to set the selected value into the Deptno attribute binding that I've added to the pickDept page's page definition.MyServiceBean
in the Model
project is the stateless EJB 3.0 Session Bean that acts as the business service. To keep the example simple, the session bean works with an in-memory set of Contact
POJOs (Plain Old Java Objects). The Session Bean's service methods each prints a diagnostic message to the console so you can better understand and appreciate when the ADF Model layer is working with the cached results of the finder methods as well as when it invokes business service facade methods to add, edit, and remove a Contact. The example is designed so that when the name of a Contact
is changed, the Contact
's updated
property is updated inside the business service to reflect the update time. When a new contact is added, edited, or deleted, the application causes the executeQuery() method on the method iterator to be executed to rebuild the RowSet
of data being managed by the RowSetIterator
of the iterator binding. When an iterator is refreshed during the ADF page lifecycle it gets a chance to decide whether its data needs to be recalculated by asking the business service, or whether it can use its cached data. For a method iterator, this decision is based on checking whether the associated method action has stored the result of its execution yet for a given set of its parameter values. If either its result has not yet been calculated, or if any of its parameter values has changed, then the method iterator is implicitly requeried. When a method iterator is requeried, it invokes its related method action binding and caches the result (unless the CacheResults
property is explicitly set to false
, which is not the case in this example!) and then rebuilds the RowSet
of Row
objects that wrap the actual data beans - Contact
beans in this example. These Row
objects adapt all bean data to have the same generic API and delegate to the wrapped "dataProvider" bean in each row to get/set the attribute values. It illustrates both programmatic and declarative techniques for accomplishing this: the former by invoking the executeQuery()
in backing bean code, while the latter using a combination of an Execute
action binding related to the method iterator and an invokeAction
executable whose Refresh
and RefreshCondition
properties have been set to control the ADF lifecycle phase during which the Execute
action should fire as well as a boolean EL expression to qualify under what conditions it should fire. You can read more about these settings in section 10.5.5 How to Use Refresh Correctly for InvokeAction and Iterator Bindings. It's important to note that since the findContactsByName(String)
method takes a parameter, we take maximum benefit of the ADF Model layer's results caching when we insure that the EL expression used to pass the method action parameter continues to evaluate to the same value used when the data was originally retrieved. In this example, the value of the nameSearch
that the end-user might apply to filter the results is kept in the session-scoped UserInfo
managed bean so it can be referenced by the method action on both the ListPage.jspx
and the EditPage.jspx
pages. If you are not careful to insure this, then having the parameter values be different on different pages, given the conditions described above for the implicit execution of the method iterator, will cause potentially unwanted re-execution of the business service methods. The FwkExtensions
project in the example includes generic helper objects and framework extension classes to customize the error handling very similar to those described in the ADF Developer's Guides. The FindOrMergePage.jspx
illustrates an example of performing data binding to a method result that returns a single Contact
bean instead of a collection of beans (List<Contact>), using the helper method executeQueryForMethodIterator
to re-execute the method iterator's query (since JDeveloper design time prevents the creation of a declarative Execute binding against such a method iterator at the moment). If you enter an email address on this page, it will find an existing contact by email if it exists and update its name if you supply a non-null value for the name parameter. If you enter an email that does not exist, it will add it to the list. This page also shows off some programmatic techniques for conditionally clearing the page definition variables used by the ADF parameter form dropped onto the page. Since the FindOrMergePage
can make updates as well as add new rows, the af:commandLink
on the page sets the requestScope flag to signal to the ListPage
to refresh its data on navigating there.SocialSecurityNumberFormatter
class that extends the default ADF Business Components number formatter class (DefaultNumberFormatter
). It overrides the parse()
and format()
methods to implement the parsing and formatting of a United States social security number whose format is 000-00-0000
. The default number formatter does not work with this format mask as is because the JDK DecimalFormat
class on which it's based doesn't correctly support that format mask as you might expect. You'll need to run the provided CreateEmpWithSocialSecurityNumber.sql
script to create a version of the EMP
table with an addition SSN
column to store the social security number as a numerical value. The Emp entity object sets the custom property named Ssn_FMT_FORMATTER
to the fully-qualified class name of the custom formatter class. This property and its value are stored in the entity object's companion EmpImplMsgBundle.java
message bundle class. The CustomDCErrorHandler
class in the FwkExtensions
project contains logic that sets the attribute information on the DataCreationException
that is thrown if that information is not already provided in the exception. This allows the error message to be correctly reported as an exception specific to a given attribute. Run the TestPage.jspx
page to try entering valid and invalid social security numbers. Notice that the default <f:convertNumber>
component has been commented out for the <af:inputText>
component for Ssn on the page.applySortCriteria()
method in a custom ADF BC data control to call setSortBy()
on the iterator binding's underlying view object instance instead of the default of calling setOrderByClause()
. The effect is that this leverages the view object's built-in support for performing in-memory sorting described in more detail in section 27.5.2 Sorting View Object Rows In Memory in the ADF Developer's Guide for Forms/4GL Developers. The ViewController
project contains a custom subclass of the ADF BC data control, a custom subclass of the ADF BC Data Control Factory, and a reference to this factory in the DataBindings.cpx
file. The CountryList
view object retrieves its data from the companion CountryList.properties
file. When you run the CountryList.jspx
page, try clicking the table headings to sort ascending or descending by the CountryCode
or the Description
. While not required for the in-memory sorting example, the workspace also includes a few classes required if you don't want your application module to make any database connection at all. The NoDatabaseConnectionApplicationPoolImpl
class that subclasses the default application module pool implementation to return null in a few strategic places to avoid the application module's connection to the database. The custom database transaction factory class NoDatabaseConnectionDatabaseTransactionFactory
is configured to be used by setting the TransactionFactory
property of the configuration, and it creates an instance of the custom NoDatabaseConnectionDBTransactionImpl2
class which overrides the doRollback()
method to do nothing.StaticDataViewObjectSupport
project. All of the view objects in the Model
project specify the oracle.adfbc.staticdata.CSVFileViewObjectImpl
class as their framework base class to inherit the generic functionality of reading their data from a CSV (comma-separated values) file. The ListOfMapsDataProviderViewObjectImpl
framework extension class implements a programmatic view object whose data is populated from a java.util.List
of java.util.Map
objects. The CSVFileViewObjectImpl
extends ListOfMapsDataProviderViewObjectImpl
to supply a "data provider" that reads data from CSV files. By convention, it reads data from the *.csv
file in the same directory in the classpath as the view object's component definition file. So, for example, the model.States
view object reads its data from the model/States.csv
file. The compiler options of the project have been modified to add the *.csv
extension to the list of files that should be copied to the outpath during compilation time. Four different JSPX pages in the ViewController
project allow you to try out master/detail display of the programmatically retrieved data.EmpName
, LowSal
, and HighSal
. The SearchPage.jspx was created by dropping the "ExecuteWithParams" action as a parameter form. It presents the results on a second ResultsPage.jspx. If the results were on the same page, then the ADF data binding layer would preserve the user's search criteria entry across postbacks to the same page. However, since showing the results causes page navigation, when the user returns to the search page, the search criteria are back to their default values. In this case, the named bind variables define their respective default values to be null, so this means the reset criteria is null when you come back to the page. If you want to have the user's search criteria for the named bind variable stay "sticky" across pages, then you can save it in an attribute in the process scope, and then assign the search field binding to the value of that processScope-saved value just before the page is rendered. The example uses a variation on the OnPageLoadBackingBeanBase
class described in Using Custom ADF Page Lifecycle to Invoke an onPageLoad Backing Bean Method section of the ADF Developer's Guide for Forms/4GL Developers. The SearchPage.java
backing bean overrides the onPagePreRender()
method to set the values of the search field bindings to the value of the saved processScope attributes of the same names. The ControllerClass
property of the search page's page definition references the backing bean as its page phase listener using the EL expression #{SearchPage}
. The setActionListener
components on the buttons in the search page declaratively set the values of the processScope variables.DataBindings.cpx
file has been updated to reflect the name of this custom data control factory class. The demo doesn't do anything spectacular, but after clicking on the button in the test page to set a session level attribute, the conditional code in MyDCJboDataControl will invoke a custom method on the application module's custom interface passing in the value of the session attribute.DBTransactionImpl
class which manages the counters in the UserData hashmap in the Session and a custom EntityImpl
class (used by the Emp
EO in the project) which increments the appropriate insert, update, or delete counter in its afterCommit() method.adf.context.securityContext.userName
groovy expression to reference the username of the logged-in user when using ADF Security, I choose a slightly-different example of storing a code representing the user's favorite color. The ViewController project contains a UserInfo
bean with a single favoriteColor
property. This bean is registered in the adfc-config.xml
file as a session-scoped managed bean, and using the JSF managed property feature, the default value of emgreen
is injected into the bean at creation time. The Model
project contains two view objects that each contain a bind variable named VarFavoriteColor
whose default value we'd like to come from the current value of the aforementioned UserInfo managed bean's favoriteColor
property. There are two basic approaches: one will appeal more to those who cherish the cleanest logical separation of view/controller and model layers. The other appeals more to developers who like to use the "simplest thing that works" (as long as it's not bad practice). The ExampleVO2
VO instance in the AM data model is of type ExampleVOUsingADFContextSessionInfoInBindVariable
. This view object's VarFavoriteColor
bind variable references the groovy expression adf.context.sessionScope.UserInfo.favoriteColor
and represents the "simplest thing that works" approach. This takes advantage of the fact that ADFContext
object abstracts access to the four interesting scopes that might be relevant during runtime of an ADF application: applicationScope
, sessionScope
, viewScope
, and requestScope
. When running in the web container, these scopes map onto the obvious matching scopes that you are familiar with from the view/controller layer. When running outside the web container, the ADFContext provides a default implementation of these scopes as a set of static maps that are useful for regression testing purposes. The Test
class in the Testing
project illustrates making use of the ADFContext.getSessionScope()
to setup the session-level information the view object is expecting to find at runtime. The ExampleVO
in the AM's data model is of type ExampleVOUsingControllerSuppliedUserDataHashtableInfoInBindVariable
. This view object's bind variable of the same name references the groovy expression adf.userSession.userData.FavoriteColor
. In this implementation that defines the more clear separation of view/controller layer and business tier - which is the one I personally prefer and recommend - the ADFBC session's userData map is used to store information that the view object's bind variable will reference. In a customized data control implementation class (CustomDCJboDataControl
), the beginRequest
method is overridden to invoke a setSessionFavoriteColor()
method on the ExampleModule
client interface to pass in the value of the UserInfo.favoriteColor
into the business tier. The application module stores this information in the user data map, and makes that information activation/passivation-safe by overriding the passivateState()
and activateState()
methods. The custom data control is configured by setting the fully-qualified name of the CustomDCJboDataControlFactory
class in the ExampleModuleDataControl
entry's FactoryClass
property in the DataBindings.cpx
file (in the dataControlUsages
section). On each request, the view/controller layer passes this information into the business layer, and the ExampleVOUsingControllerSuppliedUserDataHashtableInfoInBindVariable
view object references its value from the userData map. Notice the setDefaultValuesForSessionLevelFavoriteColor()
method in the ExampleModuleImpl
class. This is invoked in an overridden prepareSession()
method to assign a reasonable default value to both of the favoriteColor elements if their value is currently null. This would ensure that both approaches would work if the respective view object were called from a service or a test client that didn't provide any specific value at runtime. In the Testing
project, the Test
class illustrates how you can write a standalone regression test (just a simple Java class in this example, not a JUnit test) which mimics the view/controller layer calling of the setSessionFavoriteColor() API in the one case, and which populates a "mock" sessionScope UserInfo bean with a favoriteColor
property to make the other view object find its expected information. As mentioned above I prefer the approach that uses the userData hash table set explicitly via an application module method, however at least now you have a good example of both techniques in action so you can decide for yourself which you prefer.First.jspx
page. Each of the links in the "Choose your activity" area open a separate task flow in a tab in the UI shell. Clicking a second time on a link will activate an existing task flow tab if it already exists, rather than opening a second instance of the same task flow. The Second.jspx
page (to which you can navigate by clicking on the "Second" tab) illustrates menu items under the "Action" menu that each opens a distinct task flow. In this case, clicking a second time on a given menu item will open additional instances of the same task flow in the shell if one/some happen to already be open. It also illustrates toolbar icons that perform shell functions. The [1] icon opens the same task flow as the first menu item. The [2] icon marks the current tab as being "dirty". If you try to close a dirty tab, you'll see a warning dialog. The [3] icon marks a tab as not-dirty. The Third.jspx
page illustrates opening task flows as the only content in the shell (i.e. no tabs). It also shows that a button in one task flow can open a taskflow in the shell.onDeptnoDnameOrLocValueChange()
method of the Departments backing bean) that is necessary to make the AutoSubmit=true inputText fields in the editable table automatically enable the Commit and Rollback buttons as soon as the first value is changed in the table. The page includes normal declarative PPR (via the partialTriggers
attribute) that causes a click on the (Create) button or the (Delete) button to enable the same two buttons. The expression used in the disabled
attribute is not the default one dropped by the design time because it wasn't working for me to enable the button when only a delete had been performed. This EL expression referencing the transactionDirty attribute seemed to work more reliably.inputFile
component and the backing bean logic to handle inserting the contents of the uploaded file into a BLOB column in the database. Run the CreateTables.sql
script to drop and create the simple UPLOADED_FILES
table. Then, run the UploadFileToBlob.jspx
page. Note that the page uses the af:form
container with its usesUpload
attribute set to true
. Also notice that the inputFile
control is bound to the backing bean property named fileInputComponent
, allowing the backing bean to reference the UI component programmatically. The valueChangeListener
on the inputFile
is mapped via EL to the onFileUploaded()
method in the backing bean. That method accesses the ExampleModule
client interface of the ExampleModule
application module, and invokes the saveUploadedFile()
method on it. It passes in the file name, and a BlobDomain
class representing the contents of the uploaded file. The (Upload File) button's action
property is mapped via EL to the onUploadFileButtonClicked()
method in the backing bean. This method either displays an error message if the filename to be uploaded is bad, or else causes the "Last File Files Uploaded" iterator to be re-executed to retrieve the latest list of the last file files uploaded (by any user). See Configuring ADF Faces File Uploading Servlet Parameters for information on some settings you might need to configure.inputFile
component and the backing bean logic to handle inserting the contents of the uploaded file into a CLOB column in the database. Run the CreateTables.sql
script to drop and create the simple UPLOADED_FILES
table. Then, run the UploadFileToClob.jspx
page. Note that the page uses the af:form
container with its usesUpload
attribute set to true
. Also notice that the inputFile
control is bound to the backing bean property named fileInputComponent
, allowing the backing bean to reference the UI component programmatically. The valueChangeListener
on the inputFile
is mapped via EL to the onFileUploaded()
method in the backing bean. That method accesses the ExampleModule
client interface of the ExampleModule
application module, and invokes the saveUploadedFile()
method on it. It passes in the file name, and a ClobDomain
class representing the contents of the uploaded file. The (Upload File) button's action
property is mapped via EL to the onUploadFileButtonClicked()
method in the backing bean. This method either displays an error message if the filename to be uploaded is bad, or else causes the "Last File Files Uploaded" iterator to be re-executed to retrieve the latest list of the last file files uploaded (by any user). See Configuring ADF Faces File Uploading Servlet Parameters for information on some settings you might need to configure.ORDSYS.ORDDOC
and ORDSYS.ORDIMAGE
and their corresponding ADF Business Components domains that do the right thing to handle working with them.DeptView
view object has its AutoRefresh
property set to true
and the Model
project defines a shared application module instance named AppModule
on the Business Components > Application Module Instances panel of the project properties. The AppModuleDataControl
entry in the dataControlUsages
section of the DataBindings.cpx
file in the ViewController
project has been configured to use the AppModuleShared
configuration so that the UI works with a shared application module instance. As an optimization, the DeptView
view object overrides the processDatabaseChangeNotification()
method to keep track of the System.currentTimeMillis()
in a local member field. The view object's getLastRequery()
is exposed on the client interface and is accessed by the DepartmentPage
backing bean's via a method action binding. That bean's onPollTimerExpired()
method only bothers to add the table UI component as a partial target if the time the view object was last requeried is greater than the time the table was last PPR'd (which it tracks in a viewScope attribute). To try the demo, run the Departments.jspx
page. If you'd like try accessing the same page from several different browsers (e.g. Firefox, Internet Explorer, Chrome) to simulate multiple, distinct user sessions. In SQL Plus (or the JDeveloper SQL Developer worksheet window) try insert, updating, or deleting rows in the DEPT
table and committing the changes. Sometime in the next 15 seconds, the different browser user's should update to reflect the changes automatically.CreateTables.sql
script to create the EMAIL_MESSAGE
and EMAIL_MESSAGE_RECIPIENTS
tables. The example is a simple "Create an Email" application that allows you to create a new email message and add one or more recipients. Each recipient is of a particular RecipientType (P
=Primary, C
=CC, B
=BCC). The rule being enforced is that there must be exactly one recipient of type "Primary". The validation is performed by an entity-level Script Expression validator on the parent Email
entity object. This validator uses Groovy code to work with a view accessor named ValidateOnePrimary
which is of type EmailRecipientsView
and has the design-time-applied view criteria named PrimaryRecipient
(criteria mode "Both") applied to it. This view criteria filters based on the MessageId
and the RecipientType='P'
. The view accessor is configured to pass the value of the current Email
entity's Id
attribute as the value of the view criteria's VarMessageId
bind variable. Notice that the script validator allows multiple error messages to be defined. The validator's Groovy script executes the view accessor's query, raises one error using adf.error.raise(*MESSAGE_KEY*)
if there are no rows returned and another error if more than one row is returned. If the email and its recipients validate successfully, then they are saved to the tables, but note that no real email is sent anywhere. The default expression for the EmailRecipient.RecipientType
attribute uses an alternative approach to the view accessor in order to conditionally assign the default recipient type for a newly created EmailRecipient
entity. The default expression is Email.Recipients.count("RecipientType == 'P' ? 1 : null") == 0 ? 'P' : 'C'
which accesses the parent email message using the reverse association accessor named Email
then accesses its rowset of recipients by references that email entity's Recipients
association accessor attribute. Since the value of that expression is a RowSet
we can use one of the built-in rowset, in-memory aggregation functions to calculate the count
of the child EmailRecipient
entity instances which have a RecipientType
equal to P
. The count()
rowset aggregate function evaluates its String argument as a Groovy expression in the context of each row in the rowset over which it iterates. If the expression evaluates to null, then the count does not include that row. If the expression evaluates to non-null, then that row is counted. Finally, it uses a ternary expression so that if the count of EmailRecipient
instances having RecipientType
equals to P
is zero, then it returns the default value of P
, otherwise it returns the default value of C
to represent a recipient being copied on the mail. The Email
entity object includes the MessageText
attribute of type ClobDomain
, and both the Email.SenderEmail
and EmailRecipient.RecipientEmailAddress
attributes use a custom domain type EmailAddress
which validates the format of an email address. The NewMailMessage.jsff
page fragment uses the special <f:converter converterId="oracle.genericDomain"/>
component to enable JSF to work properly with these three domain-valued attributes. The MailServer
application module defines the custom method createNewMailAndPrimaryRecipient()
which creates a new email message and the first email recipient row. The method is exposed on the client interface so that the default method call activity of the create-new-email
bounded task flow can invoke it declaratively to prepare the service layer for the creation of a new email. The CustomViewObjectImpl
class implement a generic feature to conditionally have rows in a view object's default rowset be inserted at the end. For view objects using this framework extension class as their base class, the feature "kicks in" if the VO has the "InsertRowsAtEnd" custom property set to some non-null value like "true". The EmailRecipientsView
in the example uses this framework extension class as its base class and has this custom property set. To run the example, run the TestPage.jspx
. You'll need to login as either user userone
or usertwo
, both of whose password is welcome1
. Click on the button to create an "email" and then click (Send) to test the validation.Valid
to any view object whose value returns true if the primary entity usage in the row is valid, or false if it is invalid. The CustomViewObjectImpl
class adds the dynamic attribute in its overridden create()
method. The CustomViewRowImpl
class overrides the getAttributeInternal()
method to return the desired value for the dynamic attribute. The Emp
entity object in the example has four different validation methods that enforce business rules like: (1) If Deptno=40, then Job must be SALESMAN or CLERK, (2) Salesmen in department 40 must have salary of 1500, (3) Clerks in department 40 must have salary 1000, and (4) Comm is not greater than Sal. The untitled1.jspx
page in the ViewController project has a ADF Faces table and references the dynamic Valid
attribute to highlight invalid rows in yellow using some appropriate EL expressions in the inlineStyle
attribute of the fields in the table. The untitled1PageDef.xml
file had to be hand-modified to include the dynamic Valid
attribute in the AttrList of the EmpView
table binding so that the EL expression used above of #{row.Valid}
would be resolvable. And finally, the Untitled1PageController
class is registered as a custom page controller for the untitled1.jspx
page (by setting the ControllerClass
attribute at the root of the page def XML document. This custom controller overrides the default way that the validateModelUpdates()
phase of the ADF page lifecycle is implemented to force invalid rows to be revalidated. This works around Bug# 5396224, where clicking a second time on the ADF faces table's "Next" or "Previous" navigation link causes a validation error to not be reported again.CreateTablesAndPopulateData.sql
script that creates a USER_INFO
table with USERNAME
and PASSWORD
columns. As a trivial example of a entity object String-valued attribute stored in encoded format, the corresponding UserInfo
entity object assumes that its Password
should be stored in this way. The encoding technique employeed in the example is simple. A string like "abcd" is encoded when stored in the database as "[dcba]", that is, with its letters reversed and surrounded by brackets. Upon retrieving the entity object data from the database, the example decodes a queried value for the password by turning a string like "[dcba]" back into "abcd". The code is implemented as a CustomEntityImpl
framework extension class, which the UserInfo
entity uses as its base class. The generic encode/decode logic in this class is enabled on an entity object's String-valued attribute by setting the custom attribute property named EncodeValue
, which the UserInfo
entity's Password
attribute has set. I was hoping the code involved would have been more elegant, and if I discover a more elegant approach I'll update this sample to reflect what I learn. Of course, we make the assumption that encoded attributes are not going to be user-queriable, however the example code does nothing to stop the user from trying to query on it.IN
list in a view object's query. For example, imagine that you want a DeptView
view object to feature a WHERE
like DEPTNO IN (:TheDeptNo)
but you need the IN
clause list to allow specifying one or more department numbers at runtime. One approach illustrated by example number 1 below involves writing some code that assigns an array of one or more deptno values for the bind variable. The approach presented here avoids the need for any code in the ADF layer by using a database function instead. Run the CreateTypeAndFunction.sql
script to create the NUM_TABLE
type and the IN_NUMBER_LIST()
function. The function accepts a comma-separate string argument and returns a NUM_TABLE
as its result. This allows the CommaSeparatedListOfDeptno
bind variable in the example's DeptView
view object to be of type String. The view object's WHERE
clause looks like DEPTNO IN (SELECT * FROM TABLE( CAST ( in_number_list(:CommaSeparatedListOfDeptno) as num_table) ) )
. Run the AppModule
in the tester can try entering values like "10" and "10,40" for the bind variable to see it in action.Model
project, one named MasterView
and the other named ListBasedOnTableFunction
. The former is a simple SELECT from DUAL that unions two rows of dummy data. The first row has the value "D" for its LIST_TYPE
column, and the second row has the value "E". The ListForMasterRow
view link defines a link between the master views ListType
attribute and an attribute in the detail view object (which one doesn't actually matter), and it contains a custom view link SQL WHERE clause of simply "1 = 1". The ListBasedOnTableFunction
view object defines a named bind variable Bind_ListType
that has exactly the same name as the bind variable that the framework will automatically add for the view link. The expert-mode SQL statement for this view object references this bind variable as the argument to the GET_NAME_VALUE_LIST()
PL/SQL function whose invocation it wraps in a TABLE()
operator to treat the table-valued function result as if it were a table of data. The CreateTypesAndFunctions.sql
script in the Model project creates the NAME_VALUE_TYPE
object type, the NAME_VALUE_LIST
type, and the GET_NAME_VALUE_LIST()
function. The function is written to return a list of (Dname,Deptno) pairs from the DEPT
table if the value passed in is 'D' and a list of (Ename,Empno) pairs if the value passed in is 'E'. Of course, the implementation of the function could be arbitrarily more interesting and dynamic without affecting the view object. The ViewController
project contains a simple JSF page with a tree control displaying the two rows in the master view and their parameterized detail rows (which are a function of the value of the ListType attribute in each master row). The SwingView
project contains a simple Panel1
panel with a similar Swing tree control showing the same thing.DepartmentDataFromXML
view object that uses the SQL XML support in Oracle 9i Release 2 (or later) to query the data from an XML document into a view object. The view object accepts the XML text in a named bind variable, and uses the xmltype()
constructor to treat that XML text as an XML document. Then it uses the xmlsequence()
operator to treat the results of extract()
-ing the <ROWSET>/<ROW>
elements from the XML document as individual rows of source data. Finally, it uses the table()
operator in combination with the extractValue()
operator to retrieve the data values from each row of <ROW>
fragments, breaking out a DEPTNO
, DNAME
, and LOC
value from the row. The TestPage.jspx in the ViewController project allows you to experiment with passing in different XML as the value of the bind variable.writeXML()
method to produce a multi-level XML message for an HR schema-based data model having Departments -> EmployeesInDepartment -> DirectReports->DirectReportJobHistory with a second detail collection under Departments of Deparments -> EmployeesJobHistory. This XML document is then set as a request attribute so that the JSTL XML library can render the multi-level information using XPath expressions. The sample contains both Struts and Model 1 examples (requires the HR schema).setMaxFetchSize(0)
to prevent a view object from executing before the page has a chance to capture a bind variable value from the end-user.