30 Creating Portlets with the Oracle JSF Portlet Bridge

This chapter explains how to use the Oracle JSF Portlet Bridge to expose an application as a portlet.

This chapter includes the following sections:

30.1 Introduction to the Oracle JSF Portlet Bridge

The Oracle JSF Portlet Bridge enables application developers to quickly and easily expose their existing JSF applications, and Oracle ADF applications and task flows as JSR 168 portlets.

Note:

Unless otherwise noted, the term JSF applications encompasses Oracle ADF applications as well.

The Oracle JSF Portlet Bridge:

  • Simplifies portlet development by enabling you to provide portlet functionality using JSF rather than relying on the JSR 168 portlet APIs.

  • Simplifies exposing your JSF application to JSR 168 portlet consumers, such as Oracle Portal and Oracle WebCenter Spaces.

  • Eliminates the requirement to store, maintain, and deploy your portlets separately from your application by enabling the application to run simultaneously as a regular web application and as a portlet from the same installation.

  • Enables you to create portlets at a more granular level by exposing task flows as portlets. Because portletized task flows are WSRP portlets, this also enables you to use distributed task flows.

Note:

The Oracle JSF Portlet Bridge is based on and conforms to JSR 301. JSR 301 is the standards effort to define the functionality for the Portlet 1.0 Bridge for JavaServer Faces. Oracle is the specification lead for this standard. More information is available at:
http://www.jcp.org/en/jsr/detail?id=301

30.2 Creating a Portlet from a JSF Application

The Oracle JSF Portlet Bridge enables you to expose a JSF application or task flow as a portlet. You do this declaratively, using the Create Portlet Entry dialog; no coding is required. Using the Create Portlet Entry dialog, you can configure Oracle JSF Portlet Bridge on a JSF application to expose the application as a JSR 168 portlet. As part of this configuration, you indicate the initial JSF view (or task flow view) to invoke when the portlet is rendered. From that point on the Oracle JSF Portlet Bridge works with the JSF application to navigate through the additional views that are reachable from this initial view. So in the typical situation when you are exposing the entire JSF application as the portlet, you configure the Oracle JSF Portlet Bridge to render the application's initial view in the portlet and the rest of the navigation works naturally within that same portlet.

This section includes the following subsections:

30.2.1 How to Create a JSF Portlet Based on a Page

The simplest way to create a portlet from a JSF application is to generate a portlet based upon a page.

To create a JSF portlet from an existing application page:

  1. In the JDeveloper Application Navigator, open the application that contains the JSF page to portletize.

  2. Right-click the page to portletize and choose Create Portlet Entry.

  3. In the Create Portlet Entry dialog (Figure 30-1), in the Portlet Name field, enter a name for the portlet.

    Figure 30-1 The Create Portlet Entry Dialog for a Page

    Description of Figure 30-1 follows
    Description of "Figure 30-1 The Create Portlet Entry Dialog for a Page"

  4. In the Display Name field, enter a descriptive name for your portlet.

  5. In the Portlet Title field, enter a descriptive title for your portlet.

    The portlet title is displayed in the Resource Palette or Application Resources panel, so make the title something to help users decide whether the portlet is useful to them. The portlet title is also displayed on the portlet header when the portlet appears on a page.

  6. In the Short Title field, enter a shorter title for your portlet. This short title is displayed on the portlet header when the portlet appears on a page on a mobile device.

  7. In the Description field, enter a description for your portlet.

  8. Select Create navigation parameters for events to create navigation parameters for any events exposed by the page.

    Navigation parameters enable a portlet to communicate with the page on which it resides and with other portlets on that page. If you select this option, navigation parameters are added to the oracle-portlet.xml file.

  9. Click OK.

    The files portlet.xml and oracle-portlet.xml are created.

    The portlet.xml file contains the portlet entry (Example 30-1) and is opened ready for viewing or editing.

    Example 30-1 Generated Portlet Entry for a Page

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <portlet-app version="1.0" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/
      portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd">
        <portlet id="adf_jsf__MyJSFPage_jspx">
            <description>myApp_myPage_jspx</description>
            <portlet-name>myApp_myPage_jspx</portlet-name>
            <display-name>myApp_myPage_jspx</display-name>
            <portlet-class>
             oracle.portlet.bridge.adf.application.ADFBridgePortlet
            </portlet-class>
            <init-param>
                <name>javax.portlet.faces.defaultViewId.view</name>
                <value>/myPage.jspx</value>
            </init-param>
            <supports>
                <mime-type>text/html</mime-type>
                <portlet-mode>VIEW</portlet-mode>
            </supports>
            <supported-locale>en</supported-locale>
            <portlet-info>
                <title>myApp_myPage_jspx</title>
                <short-title>myApp_myPage_jspx</short-title>
            </portlet-info>
        </portlet>
        <custom-portlet-mode>
            <portlet-mode>about</portlet-mode>
        </custom-portlet-mode>
        <custom-portlet-mode>
            <portlet-mode>config</portlet-mode>
        </custom-portlet-mode>
        <custom-portlet-mode>
            <portlet-mode>edit_defaults</portlet-mode>
        </custom-portlet-mode>
        <custom-portlet-mode>
            <portlet-mode>preview</portlet-mode>
        </custom-portlet-mode>
        <custom-portlet-mode>
            <portlet-mode>print</portlet-mode>
        </custom-portlet-mode>
    </portlet-app>
    

    The page you selected earlier is used as the entry point for the portlet View mode. This is indicated in the portlet.xml file by the javax.portlet.faces.defaultViewId.view initialization parameter. You can manually edit the portlet.xml file to define the pages for other default portlet modes:

    • Edit mode: javax.portlet.faces.defaultViewId.edit

    • Help mode: javax.portlet.faces.defaultViewId.help

    Note:

    The value for the defaultViewId is relative to the application context root and must always start with a /. For example, in Example 30-1, the value for defaultViewId.view is /myPage.jspx.

    If you add defaultViewId for other portlet modes, then ensure that you also add the mode to the <supports> tag. For example, <portlet-mode>HELP</portlet-mode>.

    The oracle-portlet.xml file is an Oracle extension of portlet.xml to support WSRP 2.0 features such as navigation parameters used for inter-portlet communication. If you selected Create navigation parameters for events, navigation parameters are added to this file to contain the payload for any events exposed by the page.

30.2.2 How to Create a JSF Portlet Based on a Task Flow

An advantage of using Oracle ADF is task flows that provide a modular approach for defining control flow in an application. Instead of representing an application as a single large JSF page flow, you can break it up into a collection of reusable task flows. In each task flow, you identify application activities, the work units that must be performed in order for the application to complete. An activity represents a piece of work that can be performed when running the task flow.

Task flows can be unbounded or bounded:

  • An unbounded task flow is a set of activities, control flow rules, and managed beans interacting to allow a user to complete a task. An unbounded task flow consists of all activities and control flows in an application that are not included within any bounded task flow.

  • A bounded task flow is a specialized form of task flow, having a single entry point and one or more exit points. It contains its own set of private control flow rules, activities, and managed beans. An Oracle ADF bounded task flow allows reuse, parameters, transaction management, and reentry. It can have zero to many exit points.

A typical application is a combination of an unbounded and one or more bounded task flows. The application can then call bounded task flows from activities within the unbounded task flow. For more detailed information about bounded and unbounded task flows, see the Oracle Fusion Middleware Fusion Developer's Guide for Oracle Application Development Framework.

This section includes the following subsections:

30.2.2.1 Creating a Portlet From a Task Flow Using the Create Portlet Entry Dialog

Use the Create Portlet Entry dialog to create a portlet from a single task flow.

To make a portlet from a task flow using the Create Portlet Entry dialog:

  1. In the JDeveloper Application Navigator, open the JSF application that contains the task flow from which you want to make a portlet.

  2. Right-click the task flow to portletize and choose Create Portlet Entry.

  3. In the Create Portlet Entry dialog (Figure 30-2), in the Portlet Name field, enter a name for the portlet.

    Figure 30-2 The Create Portlet Entry Dialog for a Task Flow

    Description of Figure 30-2 follows
    Description of "Figure 30-2 The Create Portlet Entry Dialog for a Task Flow"

  4. If the task flow is unbounded, the Entry Point View dropdown list displays all the view activities of the task flow. From this dropdown list, choose the view activity to use as the entry point for the portlet. By default, the first view activity in the task flow is chosen.

    If the task flow is bounded, you do not see the Entry Point View dropdown list.

  5. In the Display Name field, enter a descriptive name for your portlet.

  6. In the Portlet Title field, enter a descriptive title for your portlet.

    The portlet title is displayed in the Resource Palette or Application Resources panel, so make the title something to help users decide whether the portlet is useful to them. The portlet title is also displayed on the portlet header when the portlet appears on a page.

  7. In the Short Title field, enter a shorter title for your portlet. This short title is displayed on the portlet header when the portlet appears on a page on a mobile device.

  8. In the Description field, enter a description for your portlet.

  9. Select Create navigation parameters for events to create navigation parameters for any events exposed by the task flow.

    Navigation parameters enable a portlet to communicate with the page on which it resides and with other portlets on that page. If you select this option, navigation parameters are added to the oracle-portlet.xml file.

  10. Click OK.

    When your portlet has been created, you should receive a message that says:

    New portlet has been successfully created
    

    In addition, the files portlet.xml and oracle-portlet.xml are created. The portlet.xml file contains the portlet entry (Example 30-2) and is opened ready for viewing or editing.

    Example 30-2 Generated Portlet Entry for a Task Flow

    <portlet id="adf_taskflow_task-flow-definition">
      <description>task-flow-definition</description>
      <portlet-name>task-flow-definition</portlet-name>
      <display-name>task-flow-definition</display-name>
      <portlet-class>
        oracle.portlet.bridge.adf.application.ADFBridgePortlet
      </portlet-class>
        <init-param>
         <name>javax.portlet.faces.defaultViewId.view</name>
         <value>/adf.task-flow?_document=
          /WEB-INF/adfp-portlet-bridge-container.xml&amp;
          _id=adfp-portlet-bridge-container&amp;_fragmentTaskFlowDoc=
          /WEB-INF/task-flow-definition.xml&amp;_fragmentTaskFlowId=
          task-flow-definition
         </value>
        </init-param>
        <supports>
          <mime-type>text/html</mime-type>
          <portlet-mode>VIEW</portlet-mode>
        </supports>
        <supported-locale>en</supported-locale>
        <portlet-info>
          <title>task-flow-definition</title>
          <short-title>task-flow-definition</short-title>
        </portlet-info>
    </portlet>
    

    Note:

    The portlet.xml file can include multiple portlets and can have a combination of pages and task flows exposed as portlets.

    The oracle-portlet.xml file is an Oracle extension of portlet.xml to support WSRP 2.0 features such as navigation parameters used for inter-portlet communication. If you selected Create navigation parameters for events, navigation parameters are added to this file to contain the payload for any events exposed by the task flow. If your task flow includes input parameters, corresponding portlet navigation parameters are created in oracle-portlet.xml to be used for inter-portlet communication.

30.2.2.2 Creating a Portlet From a Task Flow Using the Manage Portlet Entries of Task Flows Dialog

If your project includes a lot of task flows, you may find it easier to select the task flows to create as portlets from a list. You can do this using the Manage Portlet Entries of Task Flows dialog. This dialog also lets you create portlets from multiple task flows at the same time, rather than having to create them individually.

To create a portlet from a task flow using the Manage Portlet Entries of Task Flows dialog

  1. From the main menu, choose File and then New.

  2. In the New Gallery, expand Web Tier, select Portlets and then Manage Portlet Entries of Task Flows, and click OK.

  3. In the Manage Portlet Entries of Task Flows dialog, use the shuttle buttons to select which task flows you want to create as portlets.

    Figure 30-3 The Manage Portlet Entries of Task Flows Dialog

    Description of Figure 30-3 follows
    Description of "Figure 30-3 The Manage Portlet Entries of Task Flows Dialog"

  4. Click OK to create portlets for the selected task flows.

  5. If the task flow exposes any events, you must manually add navigation parameters to the oracle-portlet.xml file to contain the payload for those events.

30.2.3 How to Test a JSF Portlet

When you have created your JSF portlet you can test it using the Integrated WebLogic Server that comes packaged with JDeveloper.

To test a JSF portlet:

  1. From the main menu, choose Run and then Start Server Instance.

    It may take a few moments for the Integrated WLS to start. When the instance has started, you should see a message similar to the following in your Log panel:

    DefaultServer started
    

    For more information, see Section 3.9, "Using Integrated WLS."

  2. You are now ready to run or deploy your portlet application to the Integrated WLS. Because your web application and portlet application are one and the same (the portlet application is your existing web application with additional portlet artifacts), you can run or deploy your web application as you normally would or you can run or deploy your portlet following the instructions in Section 33.2.1, "How to Test JSR 168 Portlets on Integrated WebLogic Server."

    Note the distinction between Run and Deploy in the note in that section. Deploy provides a more persistent testing scenario.

  3. When deployed, you can view the Producer Test Page by going to:

    http://host:port/context-root/info
    

    Post deployment you must verify that the application works correctly as a web application before it is consumed as a portlet application. For example, verify that the page you portletized earlier works:

    http://localhost:7101/myApp-ViewController-context-root/faces/myPage.jspx
    
  4. Once you have successfully deployed the application containing the portlet, you can register it as a portlet producer with any other application. For more information see Section 34.2.1, "How to Register a WSRP Portlet Producer."

    Note:

    Because the Oracle JSF Portlet Bridge uses WSRP v2 features, you must register the producer using the WSRP v2 WSDL URL listed in the WSRP Producer Test Page.

    You can continue to access the application as a regular web application or consume it as a portlet producer.

  5. Now that your portlet producer is deployed and registered, you can consume your JSF portlet as you would any other portlet. For more information, see Section 34.3, "Adding Portlets to a Page."

30.2.4 What Happens at Runtime

After having created and tested the JSF portlet you can deploy the application to your production environment.

Note:

Ensure that you deploy your application to a Java EE container with the Oracle Portlet Container installed. The Integrated WLS has the container installed, which is why it is recommended for testing.

After successful deployment, you can access both the application and the portlet producer test page. For example, the application URL would be similar to:

http://host:port/appcontextroot/faces/pagename.jspx

And the portlet producer test URL would be similar to:

http://host:port/appcontextroot/info

For more information, see Section 34.2.1, "How to Register a WSRP Portlet Producer."

30.2.5 What You May Need to Know When Creating a JSF Portlet

You must code your JSF pages such that they produce markup that conforms with JSR 168 portlet markup fragment rules. If you have chosen to use Oracle ADF, the markup in your page comes mainly from the Oracle ADF Faces components, most of which naturally render markup in a style that is compatible with JSF portlets.

For those components that might cause problems in a portlet environment, the application developer must take special care. Some components generate markup that conflicts with the portlet environment and hence restricts their use. Other components may allow program control (inputs) that enable developers to introduce values that conflict with the portlet environment. In this latter case, you as the developer must be aware of the potential to publish the page as a portlet and therefore properly encode a value.

This section includes the following subsections:

30.2.5.1 General Guidelines

The guidelines that follow lay out the issues of which you should be aware as you code Oracle ADF pages that you may later choose to publish as portlets.

  • Prior to creating a portlet from your JSF application, the application, including all of its pages and task flows, must run properly after you deploy it to Integrated WLS. If it cannot run as a regular web application, it cannot run as a portlet producer either.

  • When writing an application that is supposed to run in both servlet and portlet environments, avoid casting to HttpServlet objects (or you get a ClassCastException when running as a portlet). Instead, use the abstraction provided by the Faces ExternalContext object. For example, instead of:

    HttpServletRequest request = FacesContext().getCurrentInstance().getExternalContext().getRequest();
    String localContextPath = request.getContextPath();
    

    Use the following:

    String localContextPath=
    FacesContext().getCurrentInstance().getExternalContext().getRequestContextPath();
    

    To the extent that ExternalContext does not provide that abstraction for you, you can write servlet or portlet specific code. You can use the following method to check if the application runs as portlet:

    public static boolean isPortletRequest() {
        Map<String, Object> m =
            FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
        Object phase = m.get("javax.portlet.faces.phase");
        if (phase != null) {
            return true;
        }
        else {
            return false;
        }
    }
    
  • When consuming a JSF Portlet, the consumer application automatically renders the portlet content within an inline frame (IFRAME). The implication is that any inline popup is then confined within the IFRAME. You should take this into consideration when specifying the size of the portlet.

  • If your application is secured, ensure that you have secured identity propagation as described in Section 37.12, "Securing Identity Propagation Through WSRP Producers with WS-Security."

  • Deep links are not directly supported. A by-product of the JSR 168 portlet container implementation on WSRP is that session cookie management is proxied by the consuming application rather than the client. Portlets that deep link to their full service application usually rely on shared session state to allow the transition from the current portlet context. As most applications rely on maintaining session context with a cookie, the current architecture prevents such state sharing. A deep link from the client directly to the producer server to invoke the application does not establish a session cookie between the consumer and the producer, hence a second session is established. Applications that are required to share such state must implement their own schemes for transferring the data between the two contexts. A common implementation is to write this state to a reachable location and pass a reference to this state in the deep link.

  • Java EE login is not supported. Java EE applications can be configured with different authentication techniques, such as Basic and Digest. As portlets are fragments incorporated into a consumer's page, it is generally expected that direct authentication occurs between the client and the consumer, not the client and the portlet producer. As a result, these authentication techniques are not supported in JSR 168. In the JSR 168 case, Java EE authentication occurs through WS-Security mechanisms that allow the Web service consumer to be authenticated and verified by the producer, and propagate the user identity for user authentication and authorization. Published Oracle ADF artifacts must not contain login links that trigger Java EE authentication.

  • For applications that take a long time to render, consider increasing the time out period when you register a producer that was created by the Oracle JSF Portlet Bridge. Note however that the time out period specified during producer registration is limited by the maximum time out period (maximumTimeout element) specified in adf-config-xml.

30.2.5.2 Portlet Guidelines

The portlet guidelines are as follows:

  • For resources and links, you must specify the location relative to the web-app-context-root. Otherwise, your images and other resources cannot be found by the portlet. Do not use relative (../) path notation. Portlets in Oracle WebCenter Framework run remotely and are accessed using a SOAP protocol (WSRP). The latter means that the regular Web application concept of request path does not apply in a JSR 168 container. The JSR 168 specification reflects this by mandating that all resource URLs either be absolute or context path relative.

  • Do not redirect or forward a request within your JSP. JSR 168 only supports requestDispatcher.include(). The use of httpServletResponse.sendRedirect() or requestDispatcher.forward() results in exceptions and errors. To work properly in a portlet environment, you must implement JSF navigation rules in faces-config.xml or Oracle ADF task flow control flow rules.

  • To minimize overall memory consumption in the application when running as a portlet, you should only store the minimal amount of data in the request scope.

  • If you portletize an application that contains downloadable resources, the portlet container may rewrite the file name in such a way that it may be too long for the browser to open or save. In such a case, you can save the resource to a different name and open the file using the appropriate program directly.

30.2.5.3 Security Guidelines

The security guidelines are as follows:

  • When you use the Create Portlet Entry or Manage Portlet Entries of Task Flows dialog to portletize a task flow in an Oracle ADF secured application (the security scheme being Authentication and Authorization), you must manually grant permission to the following wrapper task flow in the jazn-data.xml file of the producer application:

    /WEB-INF/adfp-portlet-bridge-container.xml#adfp-portlet-bridge-container
    

    If you do not grant the permissions, the portlet does not render.

    For example, if you have an application with two roles: authenticated-role, with view permission; and testrole, with customize, edit, grant, personalize, and view permissions, you must add the following entries inside the <permissions> tag of each role:

    • For authenticated-role:

      <permission>
        <class>oracle.adf.controller.security.TaskFlowPermission</class>
        <name>
        /WEB-INF/adfp-portlet-bridge-container.xml#adfp-portlet-bridge-container
        </name>
        <actions>view</actions>
      </permission>
      
    • For testrole:

      <permission>
        <class>oracle.adf.controller.security.TaskFlowPermission</class>
        <name>
          /WEB-INF/adfp-portlet-bridge-container.xml#adfp-portlet-bridge-container
        </name>
        <actions>customize,edit,grant,personalize,view</actions>
      </permission>
      
  • If you are portletizing a task flow or page that has role based authorization (that is, the task flow or page has been granted certain roles), WS-Security is required to propagate the user correctly. If you set up WS-Security, the JSF portlet does not render the content due to lack of permissions.

    For information about setting up WS-Security on the producer, see "Securing a WSRP Producer with WS-Security" in the Oracle Fusion Middleware Administrator's Guide for Oracle WebCenter.

    When registering the producer with the consuming application, you must ensure that you set the appropriate security properties. For more information, see Section 34.2.1, "How to Register a WSRP Portlet Producer."

30.2.5.4 JSF Guidelines

The JSF guidelines are as follows:

  • When using the h:commandLink JSF standard HTML component ensure that you set the following context-param in web.xml so that the JSF Reference Implementation does not generate any external JavaScript resource. This is to work around an issue in the JSF Reference Implementation where the reference to the JavaScript resource is not properly encoded.

    <context-param>
        <param-name>com.sun.faces.externalizeJavaScript</param-name>
        <param-value>false</param-value>
    </context-param>
    

30.2.5.5 Oracle ADF Guidelines

The Oracle ADF guidelines are as follows:

  • If you are portletizing an Oracle ADF task flow, ensure that you are able to consume that task flow by dropping it onto a page as a region and running the page. This ensures that the task flow runs correctly before portletizing it.

  • ADF Faces Dialog Framework is not supported. If your application includes any buttons or icons that launch secondary browser windows, then the contents of the new windows are not displayed properly when the application is run as a portlet. As such, you should avoid using these components if you plan to portletize your application. Examples of components that launch secondary windows are:

    • <tr:inputDate>

    • <tr:inputColor>

    • <tr:popup>

    • the useWindow attribute of <af:commandButton>

  • Portletization of pages that contain Oracle Composer components is not supported.

  • The <af.fileDownloadActionListener> component is not supported.

  • Oracle ADF components/code that handle prepareModel must be idempotent. Any code running during the prepareModel phase must be rerunnable without effect to the underlying data/business logic. When running in the portlet environment, prepareModel is called twice during the complete JSF life cycle as opposed to being called once when running within a regular web application.

    The reason for this difference is that the Oracle JSF Portlet Bridge runs JSF in two requests not one. It is implemented as if every JSF request redirected before the render phase. The consequence of this approach is that the JSF restoreView phase is called both when the request is first submitted and then again when request to render is received.

  • Do not access or reference request parameters from model code except in page parameters. Besides being a cleaner MVC2 implementation, following this guideline avoids problems when such artifacts are published as portlets. Where regular JSF artifacts run their entire lifecyle in a single request, the Oracle JSF Portlet Bridge runs these artifacts in two requests, as if every JSF request redirected before the render phase.

    This two phase model enables the clearing of submitted parameters before rendering in a manner that allows such clearing to be communicated all the way back to the client, which makes this request something you could bookmark. As a result, request parameters do not exist during the render phase. As described previously, prepareModel is invoked again in this rendering phase. Therefore, any references to request parameters in this phase handler fail. You should avoid code like either of the following fragments:

    <invokeAction id="doExecuteWithParams" 
                  Binds="ExecuteWithParams" 
                  Refresh="prepareModel" 
                  RefreshCondition="${param.id != null}"
    />
    
    <invokeAction id="doExecuteWithParams" 
                  Binds="ExecuteWithParams" 
                  Refresh="renderModel" 
                  RefreshCondition="${param.id != null}"
    />
    
  • Do not reference page parameters in the prepareModel phase. This issue relates to the same problem just described for request parameters. Generally, page parameters depend on request parameters and are evaluated during the restoreView phase. As this phase is called a second time during portlet rendering and request parameters are not available, such use fail. Instead, move any dependency on a page parameter value into the model before JSF transitions from its execute phases to its render phase.

  • If you are portletizing an application that contains only Trinidad components (<tr: > tags only), then you must manually include the following libraries in your project:

    • jdeveloper\modules\oracle.adf.view_11.1.1\adf-richclient-api-11.jar

    • jdeveloper\modules\oracle.adf.view_11.1.1\adf-richclient-impl-11.jar

    This is so that the URLs to icons in the style sheet generated for the producer are encoded correctly.

  • Because a portlet is a naming container, special consideration should be taken when using ADF Faces client-side APIs to find components. An example component ID:

    • When run as a regular web application: id="demoTemplate:popup"

    • When run as a portlet application: id="__ns12345678:demoTemplate:popup"

    Things to watch out for specifically are:

    • Avoid using the AdfPage.PAGE.findComponentByAbsoluteId() API. Use getSource() and findComponent() methods instead. For example:

      <trh:script text="
      function showPopup(event) {
          event.cancel();
          // var popup =
          //  AdfPage.PAGE.findComponentByAbsoluteId("demoTemplate:popup");
          var source = event.getSource();
          var popup = source.findComponent("popup");
          popup.show({align:"after_end", alignId:"button"});
      }
      "/>
      
    • Use a relative path for clientId instead of an absolute path (starting with a ':'). For example, use:

      <af:showPopupBehavior popupId="demoTemplate:iteratorpop"
                  triggerType="mouseHover"/>
      

      Instead of:

      <af:showPopupBehavior popupId=":demoTemplate:iteratorpop"
                  triggerType="mouseHover"/>
      
    • For more information, see "What You May Need to Know About Naming Containers" in the Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework.

30.3 Creating JSF Portlets: Example

This section includes the following subsections that step you through an example of creating JSF portlets:

30.3.1 How to Create the Example Application

The example application consists of a master task flow for entering a department number, and a detail task flow that displays the employees of the department provided in the master task flow. As you work through the example, remember to periodically save your progress.

To create the example application:

  1. From the JDeveloper main menu, choose File and then New.

  2. In the New Gallery, expand General, select Applications and then Fusion Web Application (ADF), and click OK.

  3. On the Name your application page of the Create Fusion Web Application (ADF) wizard, in the Application Name field, enter PortletBridgeApplication and then click Finish.

  4. In the Application Navigator, right-click the ViewController project and choose New.

  5. In the New Gallery, expand General, select Java and then Java Class, and click OK.

  6. In the Create Java Class dialog, in the Name field, enter Department.

  7. In the Package field, enter hr and click OK.

  8. Replace the code for Department.java with that in Example 30-3.

    Example 30-3 Department.java

    package hr;
    public class Department {
        private int deptno = 0;
        public Department() {
            super();
        }
        public void setDeptno(int deptno) {
            this.deptno = deptno;
        }
        public int getDeptno() {
          return deptno;
        }
    }
    
  9. Repeat steps 4 through 7 to create another Java class with the name DepartmentBean.

  10. Replace the code for DepartmentBean.java with that in Example 30-4.

    Example 30-4 DepartmentBean.java

    package hr;
    import java.util.Map;
    import oracle.adf.share.ADFContext;
    public class DepartmentBean {
        private static String DEPARTMENT_KEY = "HR_DEPARTMENT";
        private static String DEPARTMENT_DEFAULT_VALUE = "10";
        public DepartmentBean() {
            Department dept = new Department();
            dept.setDeptno(10);
            Map sessionScope = ADFContext.getCurrent().getSessionScope();
            if (sessionScope.get(DEPARTMENT_KEY) == null)
                sessionScope.put(DEPARTMENT_KEY, dept);
        }
        public int getDeptno() {
            Map sessionScope = ADFContext.getCurrent().getSessionScope();
            Department dept = (Department)sessionScope.get(DEPARTMENT_KEY);
            return dept.getDeptno();
        }
        public void setDeptno(int deptno) {
            Map sessionScope = ADFContext.getCurrent().getSessionScope();
            Department dept = (Department)sessionScope.get(DEPARTMENT_KEY);
            dept.setDeptno(deptno);
        }
        public String selectDepartmentString() {
            Map sessionScope = ADFContext.getCurrent().getSessionScope();
            Department dept = (Department)sessionScope.get(DEPARTMENT_KEY);
            String deptno =
                (dept == null) ? DEPARTMENT_DEFAULT_VALUE : 
                  String.valueOf(dept.getDeptno());
            System.out.println("DepartmentBean.selectDepartmentString:Department number is " + deptno);
            return deptno;
        }
    }
    
  11. Repeat steps 4 through 7 again to create another Java class with the name Employee.

  12. Replace the code for Employee.java with that in Example 30-5.

    Example 30-5 Employee.java

    package hr;
    public class Employee {
        private String firstName;
        private String lastName;
        private String title;
        private int deptno;
        public Employee(String firstName, String lastName, String title,
                        int deptno) {
            setFirstName(firstName);
            setLastName(lastName);
            setTitle(title);
            setDeptno(deptno);
        }
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
        public String getFirstName() {
            return firstName;
        }
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
        public String getLastName() {
            return lastName;
        }
        public void setTitle(String title) {
            this.title = title;
        }
        public String getTitle() {
            return title;
        }
        public void setDeptno(int deptno) {
            this.deptno = deptno;
        }
        public int getDeptno() {
            return deptno;
        }
    }
    
  13. Repeat steps 4 through 7 again to create another Java class with the name EmployeesBean.

  14. Replace the code for EmployeesBean.java with that in Example 30-6.

    Example 30-6 EmployeesBean.java

    package hr;
    import java.util.ArrayList;
    import oracle.adf.view.rich.context.AdfFacesContext;
    public class EmployeesBean {
        private static final String DEPARTMENT_NUMBER_KEY = "DEPTNO";
        private static final int DEPARTMENT_NUMBER_NULL_VALUE = -1;
        private static Employee[] employees =
        { new Employee("Neil", "Russell", "Clerk", 10),
          new Employee("Terrence", "Bennett", "Manager", 10),
          new Employee("Blair", "Palmer", "VP", 10),
          new Employee("Cory", "O'Reilly", "Reporter", 20),
          new Employee("Tony", "McConnell", "Editor", 20),
          new Employee("Jennifer", "Kuffner", "VP", 30)
        };
        public EmployeesBean() {
        }
        public void setDepartmentNumber(String deptno) {
            selectDepartment(deptno);
        }
        public int findDepartmentValue(String defaultValue) {
            AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();
            String deptno =
                (defaultValue == null ? (String)afContext.getPageFlowScope().get(DEPARTMENT_NUMBER_KEY) :
                 defaultValue);
            return (deptno == null ? DEPARTMENT_NUMBER_NULL_VALUE :
                    Integer.valueOf(deptno));
        }
        public void selectDepartment(String deptno) {
            AdfFacesContext afContext = AdfFacesContext.getCurrentInstance();
            afContext.getPageFlowScope().put(DEPARTMENT_NUMBER_KEY, deptno);
        }
        public Employee[] getEmployees(String deptno) {
            int filterDeptno = findDepartmentValue(deptno);
            Employee[] filteredEmployees = null;
            if (filterDeptno == DEPARTMENT_NUMBER_NULL_VALUE) {
                filteredEmployees = employees;
            } else {
                ArrayList empsInDept = new ArrayList();
                for (int i = 0; i < employees.length; i++) {
                    if (employees[i].getDeptno() == filterDeptno) {
                        empsInDept.add(employees[i]);
                    }
                }
                
                filteredEmployees = new Employee[empsInDept.size()];
                empsInDept.toArray(filteredEmployees);
            }
            return filteredEmployees;
        }
    }
    
  15. In the Application Navigator, right-click DepartmentBean.java and choose Create Data Control.

  16. Right-click EmployeesBean.java and choose Create Data Control.

    You should be able to see the two new data controls listed in the Data Controls panel.

  17. Right-click the ViewController project and choose New.

  18. In the New Gallery, expand Web Tier, select JSF and then ADF Task Flow, and click OK.

  19. In the Create Task Flow dialog, in the File Name field, enter department.

  20. Ensure that Create as Bounded Task Flow and Create with Page Fragments are selected and click OK.

    Note:

    A task flow can raise ADFm events. When a task flow is turned into a portlet, all events that can be raised are exposed through navigational parameters. You can edit the associated oracle-portlet.xml file to remove any events that you do not want to be exposed.
  21. In the Component Palette, select View from the list of activities, drag it onto department.xml and name it dept.

  22. Double-click the view activity and click OK in the Create New JSF Page Fragment dialog to create the corresponding dept.jsff page.

  23. In the Data Controls panel of the Application Navigator, expand the DepartmentBean data control.

  24. Select the deptno element and drag it onto dept.jsff, choosing Text and then ADF Input Field w/Label from the context menu.

  25. Select the selectDepartmentString() method and drag it onto dept.jsff, choosing ADF Button from the context menu.

    Note:

    While ADFm event payloads can be any object type, when you are exposing ADFm events through WSRP, only payloads of type String or a collection of strings can be supported.
  26. Right-click dept.jsff and choose Go to Page Definition.

    If you view the source code, you can see methodAction created for the selectDepartmentString() method.

  27. In the Structure window of the Application Navigator, right-click selectDepartmentString and choose Insert inside selectDepartmentString and then events.

  28. Right-click the events tag and choose Insert inside events and then event.

  29. In the Insert event dialog, in the name field, enter DepartmentSelectedEvent and then click OK.

    This event is raised whenever this methodAction runs. This can happen through a UI action, such as clicking the button associated with the methodAction, or through an invokeAction call to call this methodAction directly.

    You can create several event entries for a methodAction and all are raised when the methodAction fires.

    The payload for the event is the return value for the underlying method. If you examine at the selectDepartmentString() method in DepartmentBean.java (Example 30-4), you can see that it returns a String representation of the department number.

  30. In the Application Navigator, right-click the ViewController project and choose New.

  31. In the New Gallery, expand Web Tier, select JSF and then ADF Task Flow, and click OK.

  32. In the Create Task Flow dialog, in the File Name field, enter employees.

  33. Ensure that Create as Bounded Task Flow and Create with Page Fragments are selected and click OK.

    Note:

    A detail task flow can contain event consumers to handle ADFm events. However, when a task flow is exposed as a portlet, the only context that can be passed to the task flow is through portlet parameters which are mapped to task flow parameters.
  34. In the Component Palette, select View from the list of activities, drag it onto employees.xml and name it emp.

  35. Double-click the view activity and click OK in the Create New JSF Page Fragment dialog to create the corresponding emp.jsff page.

  36. In the Data Controls panel of the Application Navigator, expand the EmployeesBean data control.

  37. Select the getEmployees(String) method and drag it onto emp.jsff, choosing Method and then ADF Button from the context menu.

    Note:

    Adding the button to the page is a simple way of creating a methodAction binding. As it is not required, you can remove the button from the code after the binding has been created.
  38. In the Edit Action Binding dialog, click OK.

  39. Right-click emp.jsff and choose Go to Page Definition.

    If you view the source code, you can see the event consumer methodAction binding.

    When a methodAction binding receives an event, the underlying method is called with the parameters for the event passed to the parameters for the method.

    Note:

    The methodAction binding can be both an event producer and an event consumer. For a methodAction to be an event producer, it must have an event tag defined in the metadata, like the one you created earlier.

    All methodAction bindings are event consumers and require no additional metadata.

  40. In the source code of the emp.jsff page, delete the button that you added earlier.

    <af:commandButton actionListener="#{bindings.getEmployees.execute}"
                      text="getEmployees"
                      disabled="#{!bindings.getEmployees.enabled}" id="cb1"/>
    

    Deleting the button from the source view means the methodAction is not deleted from the page definition.

  41. In the Data Controls panel of the Application Navigator, expand the getEmployees(String) method.

  42. Select Employee and drag it onto emp.jsff, choosing Table and then ADF Read-only Table from the context menu.

  43. In the Edit Table Columns dialog, click OK.

  44. In employees.xml, click the Source tab.

  45. Create a managed-bean entry for the EmployeesBean class.

    The code should now look like that in Example 30-7.

    Example 30-7 EmployeesBean Managed Bean

    <?xml version="1.0" encoding="windows-1252" ?>
    <adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
      <task-flow-definition id="employees">
        <default-activity>employees</default-activity>
        <managed-bean>
          <managed-bean-name>contextProvider</managed-bean-name>
          <managed-bean-class>hr.EmployeesBean</managed-bean-class>
          <managed-bean-scope>pageFlow</managed-bean-scope>
        </managed-bean>
        <view id="emp">
          <page>/emp.jsff</page>
        </view>
        <use-page-fragments/>
      </task-flow-definition>
    </adfc-config>
    
  46. Create an input-parameter-definition entry and map the value of the parameter to the appropriate EmployeesBean method to store this value.

    This is needed so that the task flow can consume WSRP navigational parameters when it is exposed as a portlet. When you turn this task flow into a portlet, the task flow parameter is converted into a portlet parameter.

    The code should now look like that in Example 30-8.

    Example 30-8 Input Parameter Definition

    <?xml version="1.0" encoding="windows-1252" ?>
    <adfc-config xmlns="http://xmlns.oracle.com/adf/controller" version="1.2">
      <task-flow-definition id="employees">
        <default-activity>employees</default-activity>
        <input-parameter-definition>
          <description>Main context parameter</description>
          <display-name>Department Number</display-name>
          <name>deptno</name>
          <value>#{pageFlowScope.contextProvider.departmentNumber}</value>
          <class>java.lang.String</class>
        </input-parameter-definition>
        <managed-bean>
          <managed-bean-name>contextProvider</managed-bean-name>
          <managed-bean-class>hr.EmployeesBean</managed-bean-class>
          <managed-bean-scope>pageFlow</managed-bean-scope>
        </managed-bean>
        <view id="emp">
          <page>/emp.jsff</page>
        </view>
        <use-page-fragments/>
      </task-flow-definition>
    </adfc-config>
    
  47. Before you can turn your task flows into portlets, you must first ensure that they work within the original application. To quickly test this, you can create a page on which you can place the page fragments.

    In the Application Navigator, right-click the ViewController project and choose New.

  48. In the New Gallery, expand Web Tier, select JSF and then JSF page, and click OK.

  49. In the Create JSF Page dialog, in the File Name field, enter testPage.jspx.

  50. Select Create as XML Document (*.jspx) and click OK.

  51. In the Application Navigator, expand the Page Flows node.

  52. Drag the department task flow onto the page and choose Region from the context menu.

  53. Drag the employees task flow onto the page and choose Region from the context menu.

  54. In the Edit Task Flow Binding dialog, in the Value field for the deptno input parameter, enter ${'10'} and click OK.

  55. In the Application Navigator, right-click testPage.jspx and choose Go to Page Definition.

  56. In the Structure window of the Application Navigator, right-click testPagePageDef and choose Edit Event Map.

  57. In the Event Map Editor dialog, click the Add a New Event Entry icon.

  58. In the Add a New EventMap Entry dialog, in the Producer field, drill down to select deptPageDef.selectDepartmentString.

  59. In the Event Name field, make sure DepartmentSelectedEvent is selected.

  60. In the Consumer field, drill down to select empPageDef.getEmployees.

  61. In the Consumer Params section, click the Add Consumer Parameter icon.

  62. In the Param Name field, enter department.

  63. In the Param Value field, enter ${payLoad}.

    Note:

    The parameter name is not actually used. You can call your parameter anything. ADFm eventing just uses the position to pass these parameter values to the input parameters of the underlying methodAction method.

    payLoad is a keyword. If you invoked the expression builder when in this field, you can also see payLoad as an option to choose. In this example, the payload is a scalar value (the department number as a string). If it was an object, you can further dereference it or pass the object as a whole. You can pass any valid EL for the value.

  64. Click OK to close the Add a New EventMap dialog.

  65. Click OK to close the Event Map Editor dialog.

  66. In the Application Navigator, right-click testPage.jspx and choose Run.

    You should be able to enter a value of 10, 20, 30 in the deptno field of the master department task flow and see the employees task flow update accordingly when you click selectDepartmentString (Figure 30-4).

    Figure 30-4 Example Application Running as a Web Application

    Description of Figure 30-4 follows
    Description of "Figure 30-4 Example Application Running as a Web Application"

30.3.2 How to Create Portlets from the Task Flows in the Example Application

After creating the example application and verifying that it runs correctly, you can create portlets from the task flows so that they can be consumed in other applications.

To create portlets from task flows:

  1. In the Application Navigator, under the Page Flows node, right-click the department task flow and choose Create Portlet Entry.

  2. In the Create Portlet Entry dialog, you can accept the default values for the name, title, and description fields, but ensure that you select the Create navigation parameters for events checkbox before you click OK.

    Selecting this checkbox creates a navigation parameter for the event raised by the task flow. When the event is raised by the task flow, the payload for the event is passed to the navigation parameter.

  3. In the generated portlet.xml file, you can see the portlet-class, which shows that this is an ADFBridgePortlet. Otherwise this file contains the standard WSRP information and nothing about the ADFm event that is in the task flow.

  4. In the Application Navigator, right-click oracle-portlet.xml and choose Open.

    In this file you can see the navigation-parameter created for the ADFm event. It is exposed through the bridge as _adf_event_DepartmentSelectedEvent. When this event is raised by the task flow, the value of this parameter contains the payload for the event. The portlet binding code automatically raises this ADFm event on the consumer side which it is received.

  5. Right-click the employees task flow and choose Create Portlet Entry.

  6. You can accept the default values for the name, title, and description fields, and click OK.

    You have now made both of your task flows available as portlets from within this application.

  7. Right-click testPage.jspx and choose Run.

    Running the application has an implicit deployment. When you deploy an application containing portlet.xml, WebCenter automatically generates the appropriate WSRP entry point for the application. So, after you run the page, you can access the application through the WSRP entry point and through HTTP.

  8. To access the WSRP Producer Test Page for your portlets, when your application displays in your browser, add /info after the context root for your application (typically, this means replacing the end of the URL, starting with /faces/..., with /info).

  9. To check that the WSDL is accessible, click WSRP v2 WSDL in the WSRP Producer Test Page.

    Copy the WSDL URL of this page so that you can use it to register the producer later.

30.3.3 How to Consume the JSF Portlets from the Example Application

Now that you have created the portlets, you can use them in another application.

To consume JSF portlets:

  1. From the main menu, choose File and then New.

  2. In the New Gallery, expand General, select Applications and then WebCenter Application, and click OK.

  3. On the Name your application page of the Create WebCenter Application wizard, in the Application Name field, enter PortletBridgeConsumerApplication and then click Finish.

  4. Right-click the ViewController project and click New.

  5. In the New Gallery, expand Web Tier, select JSF and then JSF Page, and click OK.

  6. In the Create JSF Page dialog, in the File Name field, enter PortletsRaisingEvents.jspx.

  7. Select Create as XML Document (*.jspx) and click OK.

    This is the page on which you will place the portlets. Before placing portlets on a page, you must register the portlet producer with the application.

  8. In the Application Resources panel of the Application Navigator, right-click Connections, choose New Connection and then choose WSRP Producer.

  9. On the Specify Producer Name page of the Register WSRP Portlet Producer wizard, in the Producer Registration Name field, enter PortletBridgeTestProducer and click Next.

  10. On the Specify Connection Details page, in the WSDL URL field, enter the URL you copied in the previous section then click Next.

  11. On the Specify Additional Registration Details page, click Finish.

  12. In the Application Resources panel, expand the Connections node and then WSRP Producer.

    You can see the new WSRP producer. Expand this to see the portlets that you created from the department and employees task flows.

  13. Select the department portlet and drag it onto the PortletsRaisingEvents page.

  14. Select the employees portlet and drag it onto the page, just below the department portlet.

  15. Right-click PortletsRaisingEvents.jspx and choose Go to Page Definition.

    In the source code you can see an entry for the department portlet that includes the event that you defined for the task flow. This event is raised whenever the underlying task flow raises the event.

    You can also see an entry for the employees portlet that includes the parameter that you defined for the task flow. This is used to pass data from anywhere on the page back through the portlet to the task flow.

    The page definition also includes a variable iterator that defines a page variable that was created because a portlet with navigational parameters was placed on the page. Portlets or components on the page that do not create ADFm events can pass context to portlets using this page variable.

  16. Add a default value for the page variable as shown in Example 30-9.

    Example 30-9 Default Value for Deptno Page Variable

    <variableIterator id="variables">
      <variable Name="PortletBridgeApplicationemployees1_1_deptno"
                Type="java.lang.Object
                DefaultValue="${'20'}"/>
    </variableIterator>
    
  17. In the Structure panel, right-click PortletsRaisingEventsPageDef and choose Edit Event Map.

  18. In the Event Map Editor dialog, click the Add a New Event Entry icon.

  19. In the Add New EventMap Entry dialog, in the Producer field, drill down to select PortletBridgeApplicationdepartment1_1.

  20. In the Event Name field, make sure DepartmentSelectedEvent is selected.

  21. In the Consumer field, drill down to select PortletBridgeApplicationemployees1_1.

  22. In the Consumer Params section, click the Add Consumer Parameter icon.

  23. In the Param Name field, enter deptno.

  24. In the Param Value field, enter ${payLoad}.

    Note:

    No payload or a null payload is propagated across the wire from the event producer portlet to the event consumer as an empty string. If the consumer must differentiate between an empty string and null payload, you can encode the null value in the payload in the event producer. The event consumer must also look for this custom encoding to detect the null payload.
  25. Click OK to close the Add New EventMap Entry dialog.

  26. Click OK to close the Event Map Editor dialog.

    You should now be able to see a new event map entry in the source code of the page definition.

  27. In the PortletsRaisingEvents page, select the employees portlet.

  28. In the Property Inspector, expand the Common tab if necessary, and in the PartialTriggers field, enter portlet1 (the ID of the department portlet).

    This makes sure that the employees portlet refreshes to reflect any changes made in the department portlet (portlet1).

  29. In the Application Navigator, right-click PortletsRaisingEvents.jspx and choose Run.

  30. In the deptno field of the department portlet, enter 20 and click selectDepartmentString.

    The employees portlet refreshes to show the employee details for department 20 (Figure 30-5).

    Figure 30-5 Portletized Task Flows in an Application

    Description of Figure 30-5 follows
    Description of "Figure 30-5 Portletized Task Flows in an Application"