I have discussed ADF Task Flows before in raving terms and with some simple example to prove my point. To be honest, I have never before really digged deep in any article into the use case of creating a bounded task flow that has ADF Business Components inside, deploying it to an ADF Library and reusing that Task Flow from that library in a completely different ADF application. That may not even have Business Components itself. Now is the time to remedy that omission. And also demonstrate how the consumed task flow – originating from somewhere in the Resource Catalog – can be integrated and synchronized with the consuming page.
It is not very difficult, but some steps must be observed to wire it together neatly.
Our objective in this article:
* Create a bounded task flow EmployeesList that uses ADF Business Components (in this particular case to display data from a database table called EMP, you may have hear it mention before)
* Deploy the task flow to an ADF Library to make it reusable in other ADF applications
* Create a new ADF Application in which the task flow EmployeesList is reused.
The steps we go through to accomplish this:
1. Create the Bounded Task Flow
2. Deploy the Bounded Task Flow to an ADF Library
3. Add the ADF Library to the Resource Catalog and then to the consuming application
4. Add the Task Flow to a page and wire it into it.
1. Create the Bounded Task Flow
– create Fusion Application ListEmployeesTaskFlow
– create IDE Connection ScottLocal to the Scott database schema; add connection to application
– option New Gallery: Business Components from Tables; create Entity Object Employee, View Object EmployeesView, Application Module EmployeeService
on hindsight: make sure that you make your default package unique to prevent collisions applications that will be consuming this project; have the eventual name of the taskflow we are creating part of the package name. If nothing else it means that you do not have to refactor the Model.jpx file later on!
– add a bind parameter bind_deptno (type number) to the ViewObject EmployeesView; add a View Criteria EmployeesInDepartmentViewCriteria which sets an equality condition for deptno and bind_deptno
on hindsight: using the ViewCriteria in the way I did, turned out not to work. Instead of using a VC for this, I would now suggest that you create a ViewObject called EmployeesInDepartment with a bind parameter and use that bind parameter in the where clause for the VO, instead of in a VC item.
– edit the ViewObject usage for EmployeesView in the Application Module’s data model to make use of this ViewCriteria instead of the regular view
on hindsight: this would not be necessary anymore
– in order to make this ADF BC project consumable in applications that also have ADF BC, we should not have the Model.jpx file in the default package model, as that is likely to be used by consuming applications or other consumed task flows. We should do something like these steps, largely outside of JDeveloper:
(suppose we are changing the model package from model to nl.amis.taskflows.hrm.model)
* move Model.jpx from /src/model to /src/nl/amis/taskflows/hrm/model
* edit file /src/nl/amis/taskflows/hrm/model/Model.jpx: set attribute PackageName of JboProject to nl.amis.taskflows.hrm.model
also set the value for Attr with name equals _jprName – as the relative location of Model.jpr has now changed:
<Attr Name="_jprName" Value="../../../../../../Model.jpr"/>
* open file(s) common/bc4j.xcfg; change the attribute jbo.project in the AppModuleConfig elements to nl.amis.taskflows.hrm.model.Model (from model.Model)
* open file Model.jpr
set attribute v for value element with attribute n equal to "jbo.JpxName" to nl.amis.taskflows.hrm.model.Model
<value n="jbo.JpxName" v="nl.amis.taskflows.hrm.model.Model"/>
similar for value element with attribute n equal to "defaultPackage"
<value n="defaultPackage" v="nl.amis.taskflows.hrm.model"/>
any other occurences of model or model.Model.jpx should be modified in the same fashion.
on hindsight: if we had changed the value of the default package to something likely to be unique, we would not have had to go through this refactoring exercise
– create Bounded Task Flow listEmployeesTaskFlow
– define an input parameter for the task flow; it should be called departmentIdentifier, the type is oracle.jbo.domain.Number and the value (the EL expression for the location where to store the value of the input parameter) is #{pageFlowScope.departmentId}
– drag the operation ExecuteWithParams under from the EmployeesInDepartmentView collection on the data control palette, drop on the diagram for the listEmployeesTaskFlow; set the value for parameter bind_deptno to #{pageFlowScope.departmentId}; set the fixed outcome to done (or any other other fixed string); ensure that this method call activity ExecuteWithParams is the default activity for the task flow
– add a View Activity to the Task Flow; set its name to listEmployees. Create a Control Flow Case from the methodCall ExecuteWithParams to the View; set a wild card outcome (*)
– double click the ViewActivity to create and edit a JSFF (page fragment). Drag the EmployeesInDepartmentView collection from the Data Control palette to the page fragment and drop as ADF Table.
– save the view/DataBindings.cpx file as view/ListEmployeesTFDataBindings.cpx; in this file, change the value of the id attribute in the Application root element to ListEmployeesTFDataBindings.
– In the file META-INF/adfm.xml, change the path attribute in the DataBindingRegistry element from view/DataBindings.cpx to view/ListEmployeesTFDataBindings.cpx
– delete the file view/DataBindings.cpx
2. Deploy the Bounded Task Flow to an ADF Library
– from the project properties for the ViewController project, select the node Deployment. Click on New to create a new deployment profile. Select the option ADF Library to create a deployment profile fo that particular type. Set the name of the profile to adflibListEmployeesTaskFlow. Make sure that the Model project is selected on the Dependencies tab. On the Connections page, select the option Connection Name Only. Click OK to create the profile.
– from the context menu on the ViewController project node, select the option Deploy. From the submenu, select the option to deploy the project according to the ADF Library deployment descriptor.
– the adflibListEmployeesTaskFlow.jar is created in the deploy directory in the ViewController project. Copy this file to the central ADF Resource directory that is connected to from the Resource Catalog.
Note: if you do not currently have such a directory and connection, follow these steps:
* Create a central directory where reusable ADF resources could be gathered, for example and preferably on a shared file server. In my case, I create directory C:\projects\SharedADFResources. Copy the jar file with the reusable interpreter-service task flow to this directory.
* In JDeveloper, go to the Resource Palette. Create a new File System connection that refers to this directory (C:\projects\SharedADFResources in my case). The ADF Library adflibListEmployeesTaskFlow shows up. It can be expanded to reveal the task flows held within. We can add resources from this resource palette to any project that may need to reuse it.
– close the Application.
3. Add the ADF Library to the Resource Catalog and then to the consuming application
– create a new Fusion Application, for example called SomeAppWhichConsumesEmployeesListTF
– add the IDE Connection ScottLocal to the application. Note: this connection is required by the taskflow we are about to consume
– inspect the Resource Palette; see whether the ADF Library shows up and what is inside it
– add the ADF Library adflibListEmployeesTaskFlow to the ViewController project; refresh the Data Control palette to ensure that the EmployeeServiceDataControl shows up; if your cursor hovers over this data control, you will see from the package name that it is indeed inherited from the ADF Library
4. Add the Task Flow to a page and wire it into it
– create a new JSF page, for example called SomePageWhichConsumesTaskFlowEmployeeList.jspx; set the default layout to a two column layout
– in the components palette you should now see a new option adflibListEmployeesTaskFlow.jar; under this option there are nodes Task Flows and Regions; the region listEmployeesTaskFlow shows up;
drag this region to the page and drop it. A dialog pops up where you should select the option region. Next, you are asked for the value for the input parameter departmentIdentifier. Set the value to 20. Later on we will replace this with a more dynamic value.
The page is created, it consumes a task flow from an ADF Library. The application itself does not use ADF Business Components, but now through the library and the consumed task flow it does. Run the page.
You should see a page with a table of the employees in department 20. Not very exciting. No trace of the task flow. Its contribution to the page is merged into the page – which did not have any content of its own. So all we see for this page is the content of the task flow.
5. Synchronize the consumed task flow with the consuming page
Drag a selectOneRadio component to First Facet in the page. Create a list of select items for 10, 20, 30 and 40 – the values for deptno in table EMP. Set the label attribute to Select Department. Set the value attribute to #{deptSelector.deptno}. Click Finish. Next set the autoSubmit attribyte for the selectOneRadio to true – this will make it send any change in department selection immediately to the server.
In order to benefit from the department selection in the task flow, we have to change the way the input parameter in the region binding is configured. Instead of 20 – the hard coded value we set before. Click on the bindings tab on the bottom of the visual editor. Select the region binding labeled taskflow – listEmloyeesTaskFlow1. Set the value of input parameter departmentIdentifier to #{deptSelector.deptIdentifier}. Also set the Refresh attribute of the Region Binding to ifNeeded. This makes sure that the region is refreshed (and the consumed task flow reinitialized) whenever the value of the input parameter changes (that is: when the selectOneRadio sends a new department identifier value to #{deptSelector.deptno}.
The last thing we need to do is create the managed bean referred to as deptSelector. Create a class SomeContextManager. Add a single property, deptno, of type String and generate accessor methods for that property. Also create a method getDeptIdentifier() that returns an (oracle.jbo.domain.)Number. Create this method as:
public Number getDeptIdentifier() { if (getDeptno() == null) return null; try { return new oracle.jbo.domain.Number(getDeptno()); } catch (SQLException e) { } }
Now configure a session scope managed bean called deptSelector, based on this new class SomeContextManager.
<managed-bean> <managed-bean-name>deptSelector</managed-bean-name> <managed-bean-class>nl.amis.somepackage.SomeContextManager</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean>
Then it is time to run the page again.
on hindsight: the erroneous use of the ViewCriteria has introduced this problem. If we had stayed with the main VO and define the bind parameter on it, we would not have had this problem.
However, when I select a department number in the radio group, I run into an error that I cannot explain. I can only assume it must be a bug. It complains about setting a non-exist bind parameter. However, first of all, when the bind parameter was set based on a hard coded, fixed value, the exact same task flow did not give me this problem. Then, the ViewObject Usage in the Application Module’s Data Model is based on the ViewCriteria that has this very bind parameter. Furthermore, testing the Application Module did not give me any problems at all.
So it seems that when the input parameter of the task flow, that is used to set the bind parameter on the ViewObject Usage, is set based on a EL expression that refers to a bean property – and that bean property is non null – a problem occurs.
I have tried to work around the problem by specifying the same bind parameter directly on the ViewObject – instead of only on the View Criteria. And using it in the ViewObject’s where clause in an unobtrusive way (where (:bind_deptno is null OR :bind_deptno is NOT NULL)). Now the error goes away, but no data shows up either. I try to see the ADF BC detail logging in my console, but I am not getting anything at the moment, despite setting the java option "-Djbo.debugoutput=console" in the Run Configuration. Let’s see if something can be done. For example by creating an Application Module in the consuming Application’s Model Project.
I have created an Application Module HrmServiceModule with a single ViewObject that returns Departments. I have tested the Application Module with the debug written to the the console; that worked fine. Then I dropped the Departments View on my page (left facet) as a read only table for departments. Now when I run the page I am curious as to whether I will see departments (I should) and whether there will be detailed ADF BC logging in the console (there should be too). Then the question is whether the logging will tell about the ADF BC activities with regard to the Task Flow. We’ll see.
First of all, the Departments are there. Then there is also detailed logging, on Departments as well as on the Employee related activities inside the Task Flow and its Application Module and ViewObject.
Unfortunately, it seems to be doing the wrong thing and also getting the wrong results for that wrong thing: no employees are displayed, while the logging indicates:
[995] DCBindingContainer:view_SomePageWhichConsumesTaskFlowEmployeeListPageDef no validations performed [996] *** Using bean introspection to lookup value :regionModel [997] EmployeesInDepartmentView ViewRowSetImpl.doSetWhereClause(-1, bind_deptno, 20) [998] EmployeesInDepartmentView ViewRowSetImpl.execute caused params to be "un"changed [999] Column count: 11 [1000] EmployeesInDepartmentView ViewRowSetImpl.doSetWhereClause(-1, bind_deptno, 20) [1001] ViewObject: EmployeesInDepartmentView close prepared statements... [1002] ViewObject: EmployeesInDepartmentView Created new QUERY statement [1003] EmployeesInDepartmentView>#q computed SQLStmtBufLen: 372, actual=342, storing=372 [1004] SELECT Employee.EMPNO, Employee.ENAME, Employee.JOB, Employee.MGR, Employee.HIREDATE, Employee.SAL , Employee.COMM, Employee.DEPTNO, Employee.TAGS, Employee.INDEX_COLUMN FROM EMP Employee WHERE (:bind_deptno is null OR :bind_deptno is NOT NULL) ORDER BY Employee.JOB DESC [1005] Bind params for ViewObject: EmployeesInDepartmentView [1006] Binding param "bind_deptno": 20
I do not get it. It is still executing the query of the ViewObject and not of the VO Usage we specifically added and edited in the Application Module’s Data Model. So trace of the ViewCriteria. So it is doing the wrong thing. However, if it really performs this query, it should get all 14 Employee records – instead of none at all as is currently the case.
on hindsight: ViewCriteria are not (yet) supposed like this. They are for LOVs and Search Forms, not for plain use as a view on top of a view.
Resources
JDeveloper application for the Task Flow: ListEmployeesTaskFlow.zip .
JDeveloper application for the consuming page SomeAppWhichConsumesEmployeesListTF.zip.
ADF Library with reusable task flow (requires a connection called ScottLocal that connects to a schema that contains an EMP table) adflibListEmployeesTaskFlow.jar .
This post does not mention anything about the deployment view. I do not think it is a very useful post. This is a very simple reuse scenario avilable almost in all ADF examples. How do multiple projects refer to the same common ADF task flow when deployed on Weblogic server?
Great post. This blog contain lot of great information. In this article a simple example of using the Task Flow mechanism for the creation of a reusable service – text translation – that is developed as a bounded task flow that is deployed in an ADF Library to be reused in potentially many applications and pages. Deploy the View Controller project according to this deployment descriptor.
Hi Lucas,
I found that when I export the ADF library into my consuming application, everything is working fine.
When I deployed the ADF library to Weblogic server and trying to consume the ADF library taskflow, it is throwing OracleJSP error.
java.lang.NoClassDefFoundError: org/apache/myfaces/trinidad/event/ReturnEvent
Am I missing something while deploying the ADF library to weblogic server and/or while using library-ref in my weblogic-application.xml file?
Thanks in advance.
Thanks & Regards,
Rama Krishna. P
Hi,
I tried to follow this example. I have made a login module as re-usable ADF library which has bounded login taskflow.
When I tried to consume it in another application which needs login authentication, I am getting the following error.
javax.servlet.ServletException: OracleJSP error:
java.lang.NoClassDefFoundError: org/apache/myfaces/trinidad/event/ReturnEvent
at oracle.jsp.runtimev2.JspReportUtil.reportException(JspReportUtil.java:180)
at oracle.jsp.runtimev2.JspServlet.service(JspServlet.java:657)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
Truncated. see log file for complete stacktrace
Any help would be greatly appreciated.
Thanks & Regards,
Rama Krishna. P
I have created a ADF library jar out of taskflow and a file connection with name test1
Entry in default-catalog.xml
I get the following warning and the taskflow is not shown in resource catalog
WARNING: cannot find resource using repository [application.classpath] and repository path [adflibTaskApplication.jar/ADF_TaskFlow/WEB-INF+Task1.xml#Task1]. The repository may not be accessible or the resource may have been deleted or renamed
Jul 21, 2009 12:40:12 AM oracle.adfinternal.rc.jndi.FolderContext processInvalidElement()
WARNING: Element [Test1] of resource catalog [catalogDefinition] has been excluded from the catalog due to error [cannot resolve reference to resource using repository [application.classpath], repository path [adflibTaskApplication.jar/ADF_TaskFlow/WEB-INF+Task1.xml#Task1] at element [Test1] in catalog [catalogDefinition]].
Hi Edwin,
Thanks for the suggestion. I did try it, but it does not solve the issues. My conclusions at this point are:
I have probably (again I fear) misinterpreted or overestimated what ViewCriteria can do. I had assumed that when the VO Usage in the Application Module’s data model is one with a ViewCriteria active, that I would get a View (the VC) on top of the base VO and I could treat that like a base VO that consists of the fusion of the two. Clearly, that is not (exactly) how it works. Initially it seems to work that way, but at some point the VC part gets lost. ViewCriteria seem (at this point in time) to me meant for QueryForm and List of Values. Not as an additional (even dynamic through bind parameters) on top of a VO.
when I have a bounded task flow with a view/page (fragment) based on a specific ViewCriteria ‘collection’ that depends on a bind parameter, there is a problem:
– the initial selection of data (when the page first shows up) is fine, even with the value of the bind parameter derived from an input parameter to the task flow that is mapped to an EL expression in the Region Binding
– with the Region set to Refresh ifNeeded, changes in the value passed in to the input parameter are not handled very well: at that point the task flow does not seem to know the ViewCriteria anymore. If the ViewObject query itself does not contain the bind parameter, it throws the exception that I am trying to set the value of a bind parameter that does not appear in the query. When I add the bind parameter in some meaningless way to the VO’s main query, I can set the value of the bind parameter, but it has no effect whatsoever.
For now this means for me that I will stay away from using ViewCriteria in bounded task flows that I want to reuse from ADF Libraries (apart from using them for LOVs and Query Search Forms). But I will keep working on this. I still may be doing something very or at least somewhat wrong. Or the functionality of ViewCriteria needs to be extended just a little bit.
Lucas
Hi Lucas, I don’t know if this works but
Can you set value of the bind variable on the viewobject in an application module method. and drag the method on the task flow and makes this the default activity. and pass the task flow input parameter to this method.
thanks edwin