Introduction to ADF reusable task flows – consume a bounded task flow that gets synchronized with input parameter changes

1

Task Flows are the primary mechanism in ADF 11g for organizing the workload in manageable chunks that can be developed in a decoupled fashion in relative isolation, then merged together in web pages that implement the desired functionality. Task Flows are also one of the key mechanisms for reuse – along with but for other purposes than Page Templates, Page Fragments and Declarative Components. Task Flows are the key vehicle for creating encapsulated, stand alone services with a User Interface that can be consumed in multiple pages in various applications.

Task Flows come with their own managed beans, navigation rules, data binding, user interface – behind a fairly well defined interface which consists of input parameters and input/output events.

A few things that might be added to even improve a little on task flows:
- a structured method for documentation and discovery, something like WSDL or JavaDoc to describe the meaning of the Task Flow and its input parameters
- support for facets through which additional content can be injected into (views of) the task flow
- support for page templates with facets predefined by the taskflow that can be associated with the views of the task flow
- ability to register listeners with a task flow for specific events (the contextual event framework is quite awkward)

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.

The steps for creating this InterpreterService are pretty straightforward

1. Create a new Fusion Web Application called ReusableTaskFlows, with standard Model and ViewController projects. Note that we will use this application for the development of multiple reusable task flows that will be deployed and published together in an ADF Task Flow. Alternatively, we could use a separate application for each reusable task flow – this is probably a better approach, at least from a management perspective

2. Create a Java Class – Interpreter – that has properties called currentLanguage, source and target. The class has accessor methods for these properties. It also has a method initSource() which can be invoked to set the (initial) value of the source (the string to be translated).

3. Create a new Task Flow, from the New Gallery, under Web Tier, JSF select the option ADF TaskFlow. Call the task flow interpreter-service.

4. Configure the interpreter-service task-flow:

a) add a methodCall activity – with id initializeInterpreterBean
b) add a view activity – with id translationBox
c) add a control flow case from the methodCall to the view, with a wildcard for the outcome

specify a single input parameter for the task flow – as consumers of the task flow can pass in a single value – the string that needs translation.

The input parameter could be called stringToTranslate. Its type is java.lang.String (note: we have to be diligent in specifying the type as ADF is sensitive in this area). The value – the EL reference to the location where the value of the input parameter is stored – wil typically be in the pageFlowScope, for example: #{pageFlowScope.sourceText}. This means that when the task flow has been initialized, the value of the input parameter can be found in that location.

Configure a managed bean, based on the Interpreter class. Call the bean interpreterBean, attach it to the pageFlow scope.

Ensure that the methodCall initializeInterpreterBean is the default activity for the task flow.

5. Configure the methodCall initializeInterpreterBean

The method to be called by this activity is the initSource method on the initializeInterpreterBean. This is specified through the EL expression #{pageFlowScope.interpreterBean.initSource}. Specify the input parameter, both its type – java.lang.String – and its value (the EL Expression that should be evaluated to construct the value passed to the method): #{pageFlowScope.sourceText}. Also set any value, for example ‘ok’, as the fixed-outcome for the methodCall activity.

6. Flesh out the View’s page fragment

Double click the View activity translationBox to start editing the file. Accept the proposed filename of translationBox.jsff.

Drag a panelFormLayout to the page. Insert it, add one selectOneChoice and two inputText components.

Have the (select)items in the selectOneChoice derived from the getLanguagesSI() method on the interpreterBean. Associate the value attribute with the currentLanguage attribute on the same bean and have the selectOneChoice autoSubmit:

    <af:selectOneChoice label="Language"
                        value="#{pageFlowScope.interpreterBean.currentLanguage}"
                        id="soc1" autoSubmit="true">
      <f:selectItems value="#{pageFlowScope.interpreterBean.languagesSI}"
                     id="si1"/>
    </af:selectOneChoice>

 

 The two inputText components are both read only; one shows the value of the source property in the interpreterBean, the other one the translated result in the target property. The target component should be refreshed whenever the current language is changed – or whenever the selectOneChoice is autoSubmitted. Add the value soc1  – the id of the selectOneChoice – to the partialTriggers attribute in the inputText for target:

    <af:inputText label="Source Text" id="it1" readOnly="true"
                  value="#{pageFlowScope.interpreterBean.source}"/>
    <af:inputText label="Translation" id="it2" readOnly="true"
                  value="#{pageFlowScope.interpreterBean.target}"
                  partialTriggers="soc1"/>

This completes the creation of the Task Flow. We can now deploy it, add it to the resource catalog and make it thus available to other projects.

Deployment and Publication

From the New Gallery, create a new Deployment Descriptor (under General) of type ADF Library. Set a meaningful name for the deployment descriptor, such as ADFLibReusableTaskFlows. Remove the Dependency on the Model project (as there is currently no such dependency).

Deploy the ViewController project according to this deployment descriptor. A jar file (ADFLibReusableTaskFlows.jar) is created on the file system, in the ReusableTaskFlows\ViewController\deploy directory.

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 ADFLibReusableTaskFlows 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.
 

Consuming the Interpreter Service task flow
 

If we have a page in which we would like to reuse the interpreter-service task flow, we have a rather simple task at hand. Suppose we have a very simple page that has a form with two inputText items that refer to properties in some managed bean in the application:

            <af:panelFormLayout id="pfl1">
              <f:facet name="footer"/>
              <af:inputText label="Property One" id="it1"
                            value="#{someBean.property1}" autoSubmit="true"/>
              <af:inputText label="Property Two" id="it2"
                            value="#{someBean.property2}"/>
            </af:panelFormLayout>

The managed bean definiton in faces-config.xml:

  <managed-bean>
    <managed-bean-name>someBean</managed-bean-name>
    <managed-bean-class>nl.amis.someapp.beans.SomeBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>

The bean’s source code:

public class SomeBean {

    String property1;
    String property2;

    public void setProperty1(String property1) {
        this.property1 = property1;
    }

    public String getProperty1() {
        return property1;
    }

    public void setProperty2(String property2) {
        this.property2 = property2;
    }

    public String getProperty2() {
        return property2;
    }
}

When property1 is changed, the new value is immediately and asynchronouysly sent to the server – because of the autoSubmit="true" setting.

Let’s now add the interpreter service to the page, to support translation of property one. Let’s first embed the inputText for property one in a panelLabelAndMessage and a horizontal panelGroupLayout:

   <af:panelLabelAndMessage label="Property One" id="plam1">
     <af:panelGroupLayout id="pgl1">
       <af:inputText simple="true" id="it1"
                     value="#{someBean.property1}"
                     autoSubmit="true"/>
       <af:spacer id="sp1" width="10"/>
       <af:panelBox id="translatorBox" text="Translation" disclosed="false"></af:panelBox>
     </af:panelGroupLayout>
   </af:panelLabelAndMessage>

Select Region from the popup menu. Allow JDeveloper to add the ADF Library to the project, as per the dialog that appears:

Next set the value for the stringToTranslate input parameter for the task flow. The value for the input parameter must be the value entered for propertyOne. Therefore, the parameter mapping is set to #{someBean.property1}. Press OK.

We are almost there now. What we still need to do, is to make sure that the taskflow is refreshed (reinitialized/reexecuted) every time the value in propertyOne is changed. When that happens, the propertyOne item auto submits to inform the server.  That means that propert1 in someBean is updated as well. In order to make the task flow refresh, and synchronize with the new value in #{someBean.property1}, we have to set the Refresh property on the Region Binding to ifNeeded.

Click on the bindings tab at the bottom of the JSPX editor. Click on the taskFlow binding. Set the refresh property in the property inspector.

Time to eat the pudding: run the page.

Change the value in Property One and the change is reflected in the task flow instantaneously:

Conclusion: creating a bounded task flow is relative simple. It is like creating a small ADF application, with classes and beans, navigation rules and pages. In addition, there are input parameters that make up the interface of the reusable component.

Deploying the task flow and then reusing it is very simple. Largely drag and drop or menu guided operations. Piece of cake!

What would now be interesting – apart from implementing a real interpreter service – is support for passing the translation result back to the page, for example into property2. We will investigate that challenge in a next article, discussing contextual events in ADF.

 

Resources

Download zip file with the JDeveloper application ReusableTaskFlows: ReusableTaskFlows.zip.

Next is the zip file for the consuming application: WebAppConsumingInterpreterService.zip .

The ADF Library with the reusable taskflow (pretty useless thing by the way, the interpreter-service…): ADFLibReusableTaskFlows.zip.

Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

1 Comment

  1. Hi Lucas,
    congratulations for this very interesting and well done example.
    What do you think about the possibility to deploy all reusable task flows as a shared library? In this way the consumer applications need only to reference this library without importing all adf reusable libraries in their deployment profile.
    I tried to deploy the application containing the Interpreter Service Task Flow as a shared library on a wls domain.
    After this, I  modified the consumer application adding the weblogic-application.xml file with the reference to the shared library. I deployed the consumer application, but when I try to access it, I have an exception on wls  indicating that it cannot access to the definition of task flow:
     
    Caused By: oracle.adf.controller.ControllerException: ADFC-02001: ADF controller could not find ‘/WEB-INF/interpreter-service.xml’.
    at oracle.adfinternal.controller.metadata.provider.MdsMetadataResourceProvider.getMetadataObjects(MdsMetadataResourceProvider.java:4
    67)
    at oracle.adfinternal.controller.metadata.provider.MdsMetadataResourceProvider.loadUnmutalbeMetadataResources(MdsMetadataResourcePro
    vider.java:370)

     
    In which am I wrong? Can you please help me?
    Thank you,
    Alessandro