32 Coding Portlets

This chapter explains how you can enhance the Java portlets you created with the Oracle JDeveloper Create Java Portlet wizards.

This chapter includes the following sections:

Before You Begin

Before you begin looking through this chapter ensure that:

32.1 Enhancing JSR 168 Java Portlets

When you have built your initial portlet in the Create JSR 168 Java Portlet wizard as described in Section 31.2.1, "How to Create a JSR 168 Java Portlet," the next step is to enhance it. Because JSR 168 portlets adhere to the Java standards, you can find substantial information about enhancing them from many different sources, such as third-party books and web pages

This section includes the following subsections:

32.1.1 How to Add Personalization

As a quick example of adding personalization, you can enhance the portlet you created in Section 31.2.1, "How to Create a JSR 168 Java Portlet" with some code that enables a user in Edit or Edit Defaults mode to paste HTML into a field for the portlet to render. You can then easily redeploy the portlet and then test it.

Before You Begin

The steps that follow assume that you have:

To implement simple personalization:

  1. In the JDeveloper Application Navigator, open the application that contains the portlet.

  2. Expand the project that contains the portlet.

  3. Right-click view.jsp and choose Open.

  4. In the visual editor, click the Source tab and add the code indicated in bold in Example 32-1 to display the portletContent customization preference:

    Example 32-1 view.jsp Sample Code

    <%@ page contentType="text/html"
        import="javax.portlet.*,java.util.*,Portlets.Portlet1,
        Portlets.resource.Portlet1Bundle"%>
    <%@ taglib uri="http://java.sun.com/portlet" prefix="portlet"%>
    
    <portlet:defineObjects/>
    <%
    String[] str = {"Portlet Content"};
    PortletPreferences prefs = renderRequest.getPreferences();
    str = prefs.getValues("portletContent",str);
    for (int i=0; i<str.length; i++)
    {
    %><%=(i<str.length-1)?str[i]+", ":str[i]%><%}%> 
    
  5. In the Application Navigator, right-click edit.jsp and choose Open.

    Notice that the JSP consists of a form field, a form input field, and two form button fields.

  6. In the visual editor, click the Source tab and add the code indicated in bold in Example 32-2 to implement a form field for users to enter a value for the portletContent customization preference:

    Example 32-2 edit.jsp Sample Code

    <%@ page contentType = "text/html; charset=windows-1252"
             pageEncoding = "windows-1252"
             import = "javax.portlet.*, java.util.*,
    Portletenhance.Portletenhance,
    Portletenhance.resource.PortletenhanceBundle"%>
    @ <%@ taglib uri = "http://java.sun.com/portlet" prefix="portlet"%>
    <portlet:defineObjects/>
    <%
        PortletPreferences prefs = renderRequest.getPreferences();
        ResourceBundle res =
            portletConfig.getResourceBundle(renderRequest.getLocale());
    %>
    <form action="<portlet:actionURL/>" method="POST">
      <table border="0">
        <tr>
          <td width="20%">
            <p class="portlet-form-field" align="right">
              <%=  res.getString(PortletenhanceBundle.PORTLETCONTENT) %>
            </p>
          </td>
          <td width="80%">
            <input class="portlet-form-input-field"
                   type="TEXT"
                   name="<%= Portletenhance.PORTLETCONTENT_KEY %>"
                   value="<%= Portletenhance.buildValue(prefs,
    Portletenhance.PORTLETCONTENT_KEY) %>"
                   size="20">
          </td>
        </tr>    <tr>
          <td width="20%">
            <p class="portlet-form-field" align="right">
              <%=  res.getString(PortletenhanceBundle.PORTLETTITLE) %>
            </p>
          </td>
          <td width="80%">
            <input class="portlet-form-input-field"
                   type="TEXT"
                   name="<%= Portletenhance.PORTLETTITLE_KEY %>"
                   value="<%= prefs.getValue(Portletenhance.PORTLETTITLE_KEY,
    res.getString("javax.portlet.title")) %>"
                   size="20">
          </td>
        </tr>
        <%
        String[] str = {"Portlet Content"};
        str = prefs.getValues("portletContent",str);
    %>
    <tr><td width="20%">
    <p class="portlet-form-field" align="right">
    Content
    </p>
    </td><td width="80%">
    <textarea rows="10" cols="60" class="portlet-form-input-field"
      name="portletContent"><%
    for (int i=0; i<str.length; i++)
    {%><%= (i<str.length-1) ? str[i]+", " : str[i] %><%}%>
    </textarea>
    </td></tr>
        <tr>
          <td colspan="2" align="center">
            <input class="portlet-form-button" type="submit"
                                                
    name="<%=Portletenhance.OK_ACTION%>"
                                                
    value="<%=res.getString(PortletenhanceBundle.OK_LABEL)%>">
            <input class="portlet-form-button" type="submit"
                                                
    name="<%=Portletenhance.APPLY_ACTION%>"
                                                
    value="<%=res.getString(PortletenhanceBundle.APPLY_LABEL)%>">
          </td>
        </tr>
      </table>
    </form>
    
  7. In the Application Navigator, right-click portletName.java and choose Open.

  8. In the visual editor, click the Source tab and add the following two lines of code (indicated in bold) to the processAction method:

    // Save the preferences.
    PortletPreferences prefs = request.getPreferences();
    String param = request.getParameter(PORTLETTITLE_KEY);
    prefs.setValues(PORTLETTITLE_KEY, buildValueArray(param));
    String contentParam = request.getParameter("portletContent");
    if (contentParam != null)
    {
      prefs.setValues("portletContent", buildValueArray(contentParam));
    }
    prefs.store();
    
  9. Redeploy the portlet.

    Notice that JDeveloper automatically saves and compiles the code before deploying the portlet. For a reminder of how to perform this step, see Chapter 33, "Testing and Deploying Your Portlets."

  10. Reload the page that contains the portlet and you can see that the portlet now displays the text Portlet Content, which was one of the changes you made.

  11. Click the Personalize link to see the form field that you added. Enter some text in this field and close the dialog. You can see the new text displayed in the portlet.

32.1.2 How to Implement Navigational Parameters (WSRP 2.0)

While JSR 168 and WSRP 1.0 do not address public portlet parameters, WSRP 2.0 introduces navigational parameters to enable inter-portlet communication. With the release of the new portlet Application Programming Interface (API) standard, JSR 286, the need for vendor-specific API extensions will no longer be necessary. Until then, you are required to use the Oracle-specific portlet container and API extensions.

Using the Create JSR 168 Java Portlet wizard, you can easily create a portlet with navigational parameters. When you register the producer and drop the portlet on a page, the portlet's parameters are automatically linked to page variables.

To add public parameters to JSR 168 portlets using WSRP 2.0 navigational parameters:

  1. In the JDeveloper Application Navigator, open the application under which you want to create the portlet.

  2. Right-click the project under which you want to create the portlet and choose New.

  3. In the New Gallery, expand Web Tier, select Portlets and then Standards-based Java Portlet (JSR 168), and click OK.

  4. On the General Portlet Information page of the Create JSR 168 Java Portlet wizard, select Enable inter-portlet communication using Oracle WSRP V2 extensions as shown in Figure 32-1.

    Figure 32-1 Creating a Portlet with Navigational Parameters

    Description of Figure 32-1 follows
    Description of "Figure 32-1 Creating a Portlet with Navigational Parameters"

  5. Proceed through the wizard until you reach the Portlet Navigation Parameters page. For basic information about going through the wizard, see Section 31.2.1, "How to Create a JSR 168 Java Portlet."

  6. On the Portlet Navigation Parameters page (Figure 32-2), click Add.

    This adds a new row to the table of parameters.

    Figure 32-2 Portlet Navigation Parameters Page of Portlet Wizard

    Description of Figure 32-2 follows
    Description of "Figure 32-2 Portlet Navigation Parameters Page of Portlet Wizard"

  7. Replace the default values in the Name, Label, and Hint fields with something more meaningful for your parameter.

  8. Add more parameters as required and then click Finish.

  9. In the Applications Navigator, right-click oracle-portlet.xml and choose Open.

    You should see entries for the parameters that you added when you created the portlet, as shown in Example 32-3.

    Example 32-3 oracle-portlet.xml Sample, Navigational Parameters

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <portlet-app-extension
     xsi:schemaLocation="http://xmlns.oracle.com/portlet/oracle-portlet-app"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns="http://xmlns.oracle.com/portlet/oracle-portlet-app">
        <portlet-extension>
            <portlet-name>portlet1</portlet-name>
            <navigation-parameters>
                <name>Parameter_01</name>
                <type>xsi:string</type>
                <label>Parameter 1</label>
                <hint>First parameter.</hint>
            </navigation-parameters>
            <navigation-parameters>
                <name>Parameter_02</name>
                <type>xsi:string</type>
                <label>Parameter 2</label>
                <hint>Second parameter.</hint>
            </navigation-parameters>
            <portlet-id>1164232649525</portlet-id>
        </portlet-extension>
    </portlet-app-extension>
    
  10. Add code to the portlet that uses the parameters.

    When the parameters are included in the oracle-portlet.xml file, you can access and set them in the same way as any normal render parameter, and the values are automatically exposed by the consumer.

    For an example of a portlet used to set the values of navigation parameters, see the Parameter Form portlet, provided with the sample WSRP producer.

  11. You can use the following APIs to read parameters passed to the portlet.

    String param1 = request.getParameter("Parameter_01");
    String param2 = request.getParameter("Parameter_02");
    

    For an example of a portlet used to read and display the values of navigation parameters, see the Parameter Display portlet, provided with the sample WSRP producer.

  12. Application developers can add these portlets to a page and link them using the navigational parameters. For more information, see Section 34.7, "Contextually Linking WSRP 2.0 Portlets."

32.1.3 How to Implement Export/Import of Customizations (WSRP 2.0)

Another new feature that arrives with WSRP 2.0 is the ability to keep customizations with portlets when moving them from one deployment to another. Customizations are portlet preferences that are set in edit defaults mode. For example, suppose that you create a portlet and then customize its title within your development environment. If you have enabled export and import for that portlet and its producer, then the customized title is transported along with the portlet when you deploy it in production environment. If you do not enable export and import, then all customizations are lost when you transport the portlet from one deployment environment to another.

To implement export or import for a portlet and its producer:

  1. In the JDeveloper Application Navigator, open the application that contains the portlet.

  2. Expand the project that contains the portlet.

  3. Right-click the oracle-portlet.xml file for the portlet's producer and choose Open.

  4. Ensure that the allow-export and allow-import tags are included for each portlet and the producer as shown in Example 32-4. Add them if necessary.

    Example 32-4 oracle-portlet.xml Sample, Export/Import

    <portlet-app-extension xsi:schemaLocation="./oracle-portlet-app.xsd"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="./oracle-portlet-app.xsd">
        <portlet-extension>
            <portlet-name>portlet1</portlet-name>
            <navigation-parameters>
                <name>Parameter_01</name>
                <type>xsi:string</type>
                <label>First Parameter</label>
                <hint>hint0</hint>
            </navigation-parameters>
            <navigation-parameters>
                <name>Parameter_02</name>
                <type>xsi:string</type>
                <label>Second Parameter</label>
                <hint>hint1</hint>
            </navigation-parameters>
            <navigation-parameters>
                <name>Parameter_03</name>
                <type>xsi:string</type>
                <label>Third Parameter</label>
                <hint>hint2</hint>
            </navigation-parameters>
            <portlet-id>1</portlet-id>
            <allow-export>true</allow-export>
            <allow-import>true</allow-import>
            <portlet-name>portlet2</portlet-name>
            <navigation-parameters>
                <name>Parameter_01</name>
                <type>xsi:string</type>
                <label>First Parameter</label>
                <hint>hint0</hint>
            </navigation-parameters>
            <navigation-parameters>
                <name>Parameter_02</name>
                <type>xsi:string</type>
                <label>Second Parameter</label>
                <hint>hint1</hint>
            </navigation-parameters>
            <navigation-parameters>
                <name>Parameter_03</name>
                <type>xsi:string</type>
                <label>Third Parameter</label>
                <hint>hint2</hint>
            </navigation-parameters>
            <portlet-id>2</portlet-id>
            <allow-export>true</allow-export>
            <allow-import>true</allow-import>
        </portlet-extension>
        <allow-export>true</allow-export>
        <allow-import>true</allow-import>
    </portlet-app-extension>
    

32.1.4 How to Implement Rewritten URLs for Resource Proxy

Resource proxying is the standard way to retrieve resources with WSRP. To avoid problems with URLs within your portlet, you can set a flag to rewrite all of the URLs within a specified resource. For example, if have an HTML fragment that contains URLs, then you could set this flag to rewrite its URLs taking into account the WSRP resource proxying.

To indicate that URLs should be rewritten, set the PortletRequest attribute, oracle.portlet.server.resourceRequiresRewriting, to true. For example, you might include code similar to the excerpt in Example 32-5 to use resource proxying for a URL that you are encoding. Encapsulate this code within a method to avoid repeating it for every URL individually.

Example 32-5 Resource Proxy for WSRP

request.setAttribute("oracle.portlet.server.resourceRequiresRewriting", 
     Boolean.TRUE); 
String url = response.encodeURL(pathToResourceForRewriting); 
request.removeAttribute("oracle.portlet.server.resourceRequiresRewriting");

If you do not specifically set oracle.portlet.server.resourceRequiresRewriting, then it defaults to false, meaning that URLs are not rewritten. You must explicitly activate the feature by setting this attribute to true.

32.1.5 How to Implement Stateless Resource Proxying

If you have out of protocol resources that do not require rewriting, you may want to use stateless resource proxying. Stateless resource proxying means that the URLs returned to the browser do not require portlet IDs or any other contextual information. This increases the cache hit ratio for such resources. You might find stateless resource proxying useful for functionality such as static JavaScript files, static images, and so on.

To indicate that stateless proxying is required, set the PortletRequest attribute oracle.portlet.server.useStatelessProxying to true. For example, you might include code similar to the excerpt in Example 32-6 to use stateless proxying for a URL that you are encoding. Encapsulate this code within a method to avoid repeating it for every URL individually.

Example 32-6 Stateless Resource Proxying

request.setAttribute("oracle.portlet.server.useStatelessProxying", Boolean.TRUE);
String url = response.encodeURL(pathToResource);
request.removeAttribute("oracle.portlet.server.useStatelessProxying");

You can also use the following constant:

oracle.portlet.server.containerimpl.PortletRequestImpl.USE_STATELESS_PROXYING

This is shown in Example 32-7.

Example 32-7 Stateless Resource Proxying Using Constant

request.setAttribute(PortletRequestImpl.USE_STATELESS_PROXYING, Boolean.TRUE);
String url = response.encodeURL(pathToResource);
request.removeAttribute(PortletRequestImpl.USE_STATELESS_PROXYING);

If you do not specifically set oracle.portlet.server.useStatelessProxying, it defaults to false. You must explicitly activate the feature by setting this attribute to true.

32.1.6 How to Disable Java Object Cache for Preference Store Access

In some cases, you may prefer to avoid the use of the Java Object Cache by your WSRP preference store. You can configure the use of caching by the WSRP preference store with the following JNDI variable:

oracle/portal/wsrp/server/disableJavaObjectCache

By default, this variable is set to false. You can set the variable yourself in the web.xml file for you portlet application as follows:

<env-entry>
 <env-entry-name>oracle/portal/wsrp/server/disableJavaObjectCache</env-entry-name>
 <env-entry-type>java.lang.Boolean</env-entry-type>
 <env-entry-value>true</env-entry-value>
</env-entry>

For more information about setting JNDI variables, see Section 32.2.4.2, "Setting JNDI Variable Values."

32.1.7 How to Implement Security for JSR 168 Portlets

You can secure JSR 168 portlets that are deployed to a WSRP producer by configuring security at the WSRP producer end and the client end. For information about securing a JSR 168 portlet through its WSRP producer, see Section 37.12, "Securing Identity Propagation Through WSRP Producers with WS-Security."

32.2 Enhancing PDK-Java Portlets

When you have built your initial portlet in the Create Oracle PDK-Java Portlet wizard as described in Section 31.2.3, "How to Create a PDK-Java Portlet," the next step is to enhance it. You can find the JavaDoc reference for the PDK-Java on OTN at:

http://www.oracle.com/technology/products/webcenter/portlet_download.html

This section includes the following subsections that describe some enhancements that you might want to perform:

The source code for many of the examples referenced in this section is available as part of the Portlet Developer's Kit (PDK).

When you unzip PDK-Java, find the examples in:

../pdk/jpdk/v2/src/oracle/portal/sample/v2/devguide

32.2.1 How to Add Portlet Modes

In the Create Oracle PDK-Java Portlet wizard, you add portlet modes by checking boxes on the wizard pages. For more information about using the wizard, see Section 31.2.3, "How to Create a PDK-Java Portlet." For each portlet mode that you select in the wizard, a basic skeleton is created. If you want to add a portlet mode after creating the portlet, you can do that manually by updating provider.xml and HTML or JSPs in JDeveloper.

The principles of implementing portlet modes using RenderManager are the same for all modes.

More on OTN

For more detailed information about the PDK runtime classes used in this section, see the JavaDoc on OTN:

http://www.oracle.com/technology/products/webcenter/portlet_download.html

For more information about the syntax of provider.xml, see the provider JavaDoc, also available on OTN.

Before You Begin

The steps that follow assume that you have:

  • Built a portlet using the Create Oracle PDK-Java Portlet wizard

  • Successfully registered the portlet's producer

  • Added the portlet to a page

To add a portlet mode:

  1. In the JDeveloper Application Navigator, open the application that contains the portlet.

  2. Expand the project that contains the portlet.

  3. Expand the Web Content node and then the htdocs node and then the node for the producer.

  4. Right-click the node for the portlet and choose New.

    The node for the portlet is located under Web Content > htdocs > provider.

  5. In the New Gallery, expand Web Tier, select HTML or JSP, and click OK.

    You must create an HTML file or a JSP for each mode to add to your portlet. For example, to implement Help mode, create an HTML file to provide the help content.

  6. In the resulting dialog, enter a file name for the HTML file or JSP and click OK.

  7. In the visual editor, edit the content of the page to implement the desired functionality.

    For example, for Help mode, you could add the following HTML:

    <p>This is the <i>Help</i> mode of your portlet!</p>
    
  8. In the Application Navigator, right-click the provider.xml file for the provider that owns the portlet and choose Open.

    The provider.xml file is located under Web Content > WEB-INF > providers > provider.

  9. Change the value of the appropriate tag for the mode you are adding to true.

    For example, if you are adding Help mode, change the hasHelp tag as follows:

    <hasHelp>true</hasHelp>
    

    This indicates to the PDK Framework that a link or icon to that mode should be rendered.

  10. Add the code to point to the HTML page or JSP that you created earlier for the mode.

    For example, for the Help page, add the following code:

    <helpPage>/htdocs/myprovider/myportlet/myHelpPage.html</helpPage>
    
  11. Save your changes.

  12. Redeploy your portlet. For more information, see Chapter 33, "Testing and Deploying Your Portlets."

    When you redeploy, JDeveloper automatically saves and compiles the code before deploying the portlet.

  13. Copy the HTML file or JSP you created and the updated provider.xml file to the WLS instance where you plan to deploy the portlet.

    This step is not necessary if you have redeployed the producer application to a server instance.

  14. Refresh the producer.

  15. Refresh the page containing your portlet.

    You should now be able to access the new mode. For example, if you added Help mode, you should be able to click the Help link.

32.2.2 How to Implement Public Parameters

PDK-Java and Oracle WebCenter Framework provide public and private portlet parameters to enable portlet developers to easily write reusable, complex portlets. The Create Oracle PDK-Java Portlet wizard in JDeveloper creates portlets that are set up to use parameters. This feature enables you to focus solely on adding business logic to your portlets and does not require any changes to provider.xml.

For an overview of parameters, see Section 29.2.11, "Public Portlet Parameter Support" and Section 29.2.12, "Private Portlet Parameter Support."

Before You Begin

The steps that follow assume that you have:

Note:

Each portlet is limited to 4K of data. The lengths of parameter and event names, display names, and descriptions all contribute toward this 4K limit. Hence, you should not use too many parameters and events for each portlet, or give them lengthy names and descriptions.

Using the Create Oracle PDK-Java Portlet wizard, you can easily create a portlet with public parameters. When you register the producer and drop the portlet on a page, the portlet's parameters are automatically linked to page variables.

To create a portlet with public parameters to your portlet:

  1. In the JDeveloper Application Navigator, open the application that contains the portlet.

  2. Right-click the project under which you want to create your portlet, and choose New.

    Note:

    To create the portlet in an existing producer, right-click the producer's provider.xml file and choose Add Portlet. This takes you directly to the General Portlet Information page of the Create Oracle PDK-Java Portlet wizard.
  3. In the New Gallery, expand Web Tier, select Portlets and then Oracle PDK-Java Portlet, and click OK.

  4. Proceed through the Create Oracle PDK-Java Portlet wizard until you reach the Public Portlet Parameters page.

    See Section 31.2.3, "How to Create a PDK-Java Portlet" for basic information about going through the wizard.

  5. On the Public Portlet Parameters page (Figure 32-3), click Add.

    This adds a new row to the table of parameters.

    Figure 32-3 Public Portlet Parameters Page of Portlet Wizard

    Description of Figure 32-3 follows
    Description of "Figure 32-3 Public Portlet Parameters Page of Portlet Wizard"

  6. Replace the default values in the Name, Display Name, and Description fields with something more meaningful for your parameter.

  7. Add more parameters as required and then click Finish.

  8. In the Application Navigator, right-click the provider.xml file for the provider that owns the portlet and choose Open.

    The provider.xml file is located under Web Content > WEB-INF > providers > provider.

    You should see entries for the parameters that you added on the Public Portlet Parameters page in the wizard, for example, as shown in Example 32-8.

    Example 32-8 provider.xml Sample, Public Parameters

    <?xml version = '1.0' encoding = 'UTF-8'?><?providerDefinition version="3.1"?>
    <provider class="oracle.portal.provider.v2.DefaultProviderDefinition">
      <session>false</session>
      <passAllUrlParams>false</passAllUrlParams>
      <preferenceStore class=
         "oracle.portal.provider.v2.preference.FilePreferenceStore">
       <name>prefStore1</name>
       <useHashing>true</useHashing>
      </preferenceStore>
      <portlet class="oracle.portal.provider.v2.DefaultPortletDefinition">
          <id>1</id>
          <name>MyPortlet</name>
          <title>My Portlet</title>
          <description>My Portlet Description</description>
          <timeout>40</timeout>
          <showEditToPublic>false</showEditToPublic>
          <hasAbout>false</hasAbout>
          <showEdit>true</showEdit>
          <hasHelp>false</hasHelp>
          <showEditDefault>false</showEditDefault>
          <showDetails>false</showDetails>
          <inputParameter class=
            "oracle.portal.provider.v2.DefaultParameterDefinition">
           <name>Parameter_01</name>
           <displayName>Parameter_01</displayName>
           <description>My first parameter</description>
          </inputParameter>
          <inputParameter class=
            "oracle.portal.provider.v2.DefaultParameterDefinition">
           <name>Parameter_02</name>
           <displayName>Parameter_02</displayName>
           <description>My second parameter</description>
          </inputParameter>
          <inputParameter class=
            "oracle.portal.provider.v2.DefaultParameterDefinition">
           <name>Parameter_03</name>
           <displayName>Parameter_03</displayName>
          </inputParameter>
          <renderer class="oracle.portal.provider.v2.render.RenderManager">
             <renderContainer>true</renderContainer>
             <renderCustomize>true</renderCustomize>
             <autoRedirect>true</autoRedirect>
             <contentType>text/html</contentType>
             <showPage>/htdocs/myportlet/MyPortletShowPage.jsp</showPage>
             <editPage>/htdocs/myportlet/MyPortletEditPage.jsp</editPage>
          </renderer>
          <personalizationManager class=
            "oracle.portal.provider.v2.personalize.PrefStorePersonalizationManager">
           <dataClass>
            oracle.portal.provider.v2.personalize.NameValuePersonalizationObject
           </dataClass>
          </personalizationManager>
       </portlet>
    </provider>
    
  9. In the Application Navigator, right-click the portletnameShowPage.jsp for your portlet and choose Open.

    The file is located under Web Content > htdocs > provider > portlet.

    The portlet includes logic for retrieving the parameters, for example, as shown in Figure 32-3.

    Example 32-9 ShowPage.jsp Sample

    <%@page contentType="text/html; charset=windows-1252"
            import="oracle.portal.provider.v2.render.PortletRenderRequest"
            import="oracle.portal.provider.v2.http.HttpCommonConstants"
            import="oracle.portal.provider.v2.ParameterDefinition"
    %>
    <%
       PortletRenderRequest pReq = (PortletRenderRequest)
          request.getAttribute(HttpCommonConstants.PORTLET_RENDER_REQUEST);
    %>
    <P>Hello <%= pReq.getUser().getName() %>.</P>
    <P>This is the <b><i>Show</i></b> render mode!</P>
    <%
       ParameterDefinition[] params =
           pReq.getPortletDefinition().getInputParameters();
    %>
    <p>This portlet's input parameters are...</p>
    <table align="left" width="50%" ><tr><td><span
    class="PortletHeading1">Name</span></td><td><span
    class="PortletHeading1">Value</span></td></tr>
    <%
       String name = null;
       String value = null;
       String[] values = null;
       for (int i = 0; i < params.length; i++)
       {
           name = params[i].getName();
           values = pReq.getParameterValues(name);
           if (values != null)
           {
               StringBuffer temp = new StringBuffer();
               for (int j = 0; j < values.length; j++)
               {
                   temp.append(values[j]);
                   if (j + 1 != values.length)
                   {
                       temp.append(", ");
                   }
               }
               value = temp.toString();
           }
           else
           {
               value = "No values have been submitted yet.";
           }
    %>
    <tr>
      <td><span class="PortletText2"><%= name %></span></td>
      <td><span class="PortletText2"><%= value %></span></td>
    </tr>
    <%
       }
    %>
    </table>
    
  10. Add logic to your portlet that allows it to submit parameter values entered by users.

  11. Create a second portlet using the same steps that simply displays parameter values that it retrieves.

  12. Register the producer with a custom WebCenter application. For more information, see Section 34.2.3, "How to Register an Oracle PDK-Java Portlet Producer."

  13. Add the two portlets to a page in the application. For more information, see Section 34.3, "Adding Portlets to a Page."

  14. In the Structure window of the Application Navigator, right-click an element of the page and choose Go to Page Definition.

    The page definition should look similar to Example 32-10. Notice the variables at the page level and the parameters at the portlet level (indicated in bold).

    Example 32-10 Page Definition File Sample

    <?xml version="1.0" encoding="UTF-8"?>
    <pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
                    version="10.1.3.38.90" id="untitled1PageDef"
                    Package="view.pageDefs">
      <executables>
        <variableIterator id="variables">
          <variable Name="portlet1_Parameter_01" Type="java.lang.Object"/>
          <variable Name="portlet1_Parameter_02" Type="java.lang.Object"/>
          <variable Name="portlet1_Parameter_03" Type="java.lang.Object"/>
        </variableIterator>
        <portlet id="portlet1"       portletInstance="/oracle/adf/portlet/PdkPortletProducer1_1153936627784
              /applicationPortlets/Portlet1_abfc5a10_010c_1000_8003_82235f50d831"
            class="oracle.adf.model.portlet.binding.PortletBinding"
            xmlns="http://xmlns.oracle.com/portlet/bindings">
          <parameters>
            <parameter name="Parameter_01" pageVariable="portlet1_Parameter_01"/>
            <parameter name="Parameter_02" pageVariable="portlet1_Parameter_02"/>
            <parameter name="Parameter_03" pageVariable="portlet1_Parameter_03"/>
          </parameters>
        </portlet>
      </executables>
    </pageDefinition>
    
  15. Run the application.

  16. Enter values in the first portlet and the same values should be displayed in the second portlet.

32.2.3 How to Implement Private Parameters

In some cases, you might need a parameter that is known only to the portlet instance. These parameters are known as private parameters because they have no connection to the page and are known only to the portlet. Private parameters often come in handy when you are building navigation for your portlet. For example, if you have a portlet made up of multiple pages, then you can use these private parameters to jump to another resource of the portlet.

This section includes the following subsections:

32.2.3.1 About Private Parameters

Private parameters are used in classic web applications to pass information from links or forms in the browser back to the server. The server in turn takes actions and returns the appropriate content. For example, if the user of a dictionary web site asks for information about hedgehogs, then the URL submitted to the server might append a private parameter as follows:

http://dictionary.reference.com/search?q=Hedgehog

If the server is responsible for rendering the whole page and the client communicates directly with the server, then this form of URL works well. In a custom WebCenter application, the client does not communicate directly with portlets. Instead, Oracle WebCenter Framework mediates between the client and the portlet. Moreover, because most pages have multiple portlets, Oracle WebCenter Framework communicates with multiple portlets.

For example, suppose a page contains two portlets, a thesaurus portlet and a dictionary portlet. Both portlets use q as a parameter to record the search queries made by the user. If the user queries the thesaurus portlet, then the URL used to rerequest the page with the updated thesaurus portlet must contain the thesaurus portlet's parameter, q. The thesaurus parameter must also be distinguished from dictionary portlet parameter 1, which performs the same function for that portlet.

You must ensure that the portlet meets the following criteria:

  • It properly qualifies its own parameters when they are built into links and forms.

  • It leaves unchanged any parameters that do not belong to it.

The following API call transforms an unqualified parameter name into a qualified parameter name:

HttpPortletRendererUtil.portletParameter(HttpServletRequest request, String param);

HttpPortletRendererUtil is in the package oracle.portal.provider.v2.render.http.

For example:

qualParamQ = HttpPortletRendererUtil.portletParameter(r, "q");

To fetch the value of a portlet parameter from the incoming request, you can use the following API:

Note:

The API converts the parameter name into the qualified parameter name before fetching the value from the incoming request. Hence, you need not perform this step.
PortletRenderRequest.getQualifiedParameter(String name)

PortletRenderRequest is in the package oracle.portal.provider.v2.render.

For example:

valueQ = r.getQualifiedParameter("q");

The other aspect of a portlet's responsibilities with private parameters is to not disturb the parameters on the URL that it does not own. The utilities you may use to ensure adherence to this rule are discussed in Section 32.2.3.3, "Building Links with the Portlet URL Types" and Section 32.2.3.4, "Building Forms with the Portlet URL Types."

32.2.3.2 About Portlet URL Types

When a portlet renders itself, Oracle WebCenter Framework passes it various URLs, which the portlet can then use to render links. You can fetch and manipulate these URLs to simplify the task of creating links. The following is a list of the URLs provided to portlets:

  • PAGE_LINK is a URL to the page upon which the portlet instance resides. You use this URL as the basis for all intraportlet links. If the portlet renders a link that navigates the user to another section of the same portlet, then this navigation must be encoded as a set of parameters using the PAGE_LINK.

  • DESIGN_LINK is a URL to the portlet's personalization (Edit mode) page. A portlet's Edit and Edit Defaults modes are not rendered on the same page as the portlet. The Edit and Edit Defaults modes take over the entire browser window. The portlet's Edit and Edit Defaults modes are not necessarily accessible to every user. It represents a minimal, static framework in which the portlet is free to render its personalization options. This URL is only of use when rendering Personalize links.

  • BACK_LINK is a URL to a useful return point from the current page where the portlet renders itself. For example, when the portlet is rendering its personalization page (Edit mode), this link refers to the page on which the portlet resides and from which the user navigated to the personalization page. Consequently, it is the link you encode in the buttons that accept or cancel the pending action. This URL is only useful for the desktop rendering of portlets (usually in Edit or Edit Defaults mode).

32.2.3.3 Building Links with the Portlet URL Types

To build links with Portlet URL types, you must access them and use them when writing portlet rendering code. To fetch the URL for a link, you call the following APIs in PDK-Java:

portletRenderRequest.getRenderContext().getPageURL()
portletRenderRequest.getRenderContext().getEventURL()
portletRenderRequest.getRenderContext().getDesignURL()
portletRenderRequest.getRenderContext().getLoginServerURL()
portletRenderRequest.getRenderContext().getBackURL()

In portlet navigation, you must add (or update) your portlet's parameters in the page URL. To perform this task, you can use the following API to build a suitable URL:

UrlUtils.constructLink(
    PortletRenderRequest pr,
    int linkType, -- UrlUtils.PAGE_LINK in this case
    NameValue[] params,
    boolean encodeParams,
    boolean replaceParams)

UrlUtils resides in the package called oracle.portal.provider.v2.url. Notice that you do not actually fetch the page URL yourself. Rather you use the supplied portlet URL type, UrlUtils.PAGE_LINK.

The parameter names in the params argument should be fully qualified. Moreover, if you properly qualify the parameters, UrlUtils.constructLink with the appropriate linkType does not disturb other URL parameters that are not owned by the portlet.

An alternative version of UrlUtils.contructLink accepts a URL as the basis for the returned URL. If you require an HTML link, then you can use UrlUtils.constructHTMLLink to produce a complete anchor element.

The following example portlet, ThesaurusLink.jsp, uses the parameter q to identify the word for which to search the thesaurus. It then creates links on the found, related words that the user may follow to get the thesaurus to operate on that new word. To see the initial submission form that sets the value of q, see the example in Section 32.2.3.4, "Building Forms with the Portlet URL Types."

<%
    String paramNameQ = "q";
    String qualParamNameQ = 
    HttpPortletRendererUtil.portletParameter(paramNameQ);
    PortletRenderRequest pRequest = (PortletRenderRequest)
        request.getAttribute(HttpCommonConstants.PORTLET_RENDER_REQUEST);
    String paramValueQ = pRequest.getQualifiedParameter(paramNameQ);
%>
<!-- Output the HTML content -->
<center>
    Words similar to <%= paramValueQ %>
    <br>
    Click the link to search for words related to that word.
    <br>
    <ul>
<%
        String[] relatedWords = Thesaurus.getRelatedWords(paramValueQ);
        NameValue[] linkParams = new NameValue[1]; 
        for (int i=0; i<=relatedWords.length; i++)
        {
            linkParams[0] = new NameValue(
                qualParamNameQ, relatedWords[i]);
%>
            <li>
            <b> <%= relatedWords[i] %> </b>
            <%= UrlUtils.constructHTMLLink(
                pRequest,
                UrlUtils.PAGE_LINK,
                "(words related to " + relatedWords[i] + ")",
                "",
                linkParams,
                true,
                true)%>
           </li>
<%
        }
%>
   </ul>
</center>

32.2.3.4 Building Forms with the Portlet URL Types

The use of portlet parameters in forms is similar to their use in links. The following two fundamental rules continue to apply:

  • Qualify the portlet's parameter names.

  • Do not manipulate or remove the other parameters on the incoming URL.

In terms of markup and behavior, forms and links differ quite considerably. However, just as with links, PDK-Java contains utilities for complying with these two basic rules.

A parameter name is just a string, whether it is a link on a page or the name of a form element. Therefore, the code for properly qualifying the portlet's parameter name is the same as that described in Section 32.2.3.3, "Building Links with the Portlet URL Types."

Forms differ from links in the way you ensure that the other parameters in the URL remain untouched. Once you open the form in the markup, you can make use of the following APIs:

UrlUtils.htmlFormHiddenFields(pRequest,UrlUtils.PAGE_LINK, formName);
UrlUtils.htmlFormHiddenFields(someURL);

where formName = UrlUtils.htmlFormName(pRequest,null).

Note:

Just as parameters in URLs and element names in forms require qualification to avoid clashing with other portlets on the page, form names must be fully qualified because any given page might have several forms on it.

The htmlFormHiddenFields utility writes HTML hidden form elements into the form, one form element for each parameter on the specified URL that is not owned by the portlet.

<INPUT TYPE="hidden" name="paramName" value="paramValue">

Thus, you need only to add their portlet's parameters to the form.

The other item of which you should be aware is how to derive the submission target of your form. In most cases, the submission target is the current page:

formTarget = UrlUtils.htmlFormActionLink(pRequest,UrlUtils.PAGE_LINK)

The value of formTarget can be the action attribute in an HTML form or the target attribute in a SimpleForm. Even though the method name includes HTML, it actually just returns a URL and thus you can use it in mobile portlets, too.

The following example form renders the thesaurus portlet's submission form. For the portlet that results from the submission of this form, see the example in Section 32.2.3.3, "Building Links with the Portlet URL Types."

<%
    String paramNameSubmit = "submit";
    String paramNameQ = "q";
    String qualParamNameQ =
        HttpPortletRendererUtil.portletParameter(paramNameQ);
    String qualParamNameSubmit = 
    HttpPortletRendererUtil.portletParameter(paramNameSubmit);
    PortletRenderRequest pRequest = (PortletRenderRequest)
        request.getAttribute(HttpCommonConstants.PORTLET_RENDER_REQUEST);
    String formName = UrlUtils.htmlFormName(pRequest,"query_form");
%>
<!-- Output the HTML content -->
<center>
    <b>Thesaurus</b>
    Enter the word you want to search for
    <form name="<%= formName %>" method="POST" 
        action="<%= UrlUtils.htmlFormActionLink(pRequest,UrlUtils.PAGE_LINK) %>"> 
        <%= UrlUtils.htmlFormHiddenFields(pRequest,UrlUtils.PAGE_LINK, formName)%> 
        <table><tr><td>
            Word of interest:
        </td><td>
            <input
                type="text"
                size="20"
                name="<%= qualParamNameQ %>"
                value="">
        </td></tr></table>
        <input type=submit name="<%= qualParamNameSubmit %>" Value="Search">
    </form>
</center>

32.2.3.5 Implementing Navigation within a Portlet

You can implement navigation within a portlet in one of three ways:

  • Pass navigation information in rendered URLs using private portlet parameters. Branching logic within the portlet code then determines which section of the portlet to render based on the URL. This option represents a small extension to the thesaurus example presented in Section 32.2.3.3, "Building Links with the Portlet URL Types" and Section 32.2.3.4, "Building Forms with the Portlet URL Types." Basically, instead of performing thesaurus search operations using the value of parameter q, the portlet branches based on the parameter value and renders different content accordingly.

  • Pass navigation information as described in the previous item but use PDK-Java to interpret the parameter and thus branch on its value. This option requires some further changes to the thesaurus example and is more fully explained later in this section.

  • Use session storage to record the portlet state and private parameters to represent actions rather than explicit navigation. This method provides the only way that you can restore the portlet to its previous state when the user navigates off the page containing the portlet. Once the user leaves the page, all private portlet parameters are lost and you can only restore the state from session storage, assuming you previously stored it there. This option requires that you understand and implement session storage. For more information about implementing session storage, see Section 32.2.5, "How to Access Session Information."

The following portlet code comes from the multipage example in the sample producer of PDK-Java:

<portlet>
    <id>11</id> 
    <name>Multipage</name> 
    <title>MultiPage Sample</title> 
    <shortTitle>MultiPage</shortTitle> 
    <description>
        This portlet depicts switching between two screens all 
        in an application page.
    </description> 
    <timeout>40</timeout> 
    <timeoutMessage>MultiPage Sample timed out</timeoutMessage> 
    <renderer class="oracle.portal.provider.v2.render.RenderManager">
        <contentType>text/html</contentType> 
        <showPage>/htdocs/multipage/first.jsp</showPage> 
        <pageParameterName>next_page</pageParameterName> 
    </renderer>
</portlet>

Note:

The value of pageParameterName is the name of a portlet parameter, next_page, that the PDK-Java framework intercepts and interprets as an override to the value of the showPage parameter. If the PDK-Java framework encounters the qualified version of the parameter when the multipage portlet is requested, then it renders the resource identified by next_page rather than first.jsp. PDK-Java does not render the parameter within the portlet, that responsibility falls to the portlet.

You can modify the thesaurus example to operate with the use of this parameter. Specifically, you can use the form submission portlet to be the input for the thesaurus (the first page of the portlet), then navigate the user to the results page, which contains links to drill further into the thesaurus. The following examples illustrate these changes.

Note:

The example that follows is most useful for relatively simple cases, such as this thesaurus example. If your requirements are more complex (for example, you want to build a wizard experience), then you should consider using an MVC framework such as Struts. For information about how to build portlets from struts applications, see Section 32.4, "Creating a Struts Portlet."

ThesaurusForm.jsp:

<%
    PortletRenderRequest pRequest = (PortletRenderRequest)
        request.getAttribute(HttpCommonConstants.PORTLET_RENDER_REQUEST);
    String paramNameSubmit = "submit";
    String paramNameQ = "q";
    String qualParamNameQ =
        HttpPortletRendererUtil.portletParameter(pRequest, paramNameQ);
    String qualParamNameSubmit = 
    HttpPortletRendererUtil.portletParameter(pRequest, paramNameSubmit);
    String formName = UrlUtils.htmlFormName(pRequest,"query_form");
%>
<!-- Output the HTML content -->
<center>
    <b>Thesaurus</b>
    Enter the word you want to search for
    <form name="<%= formName %>" method="POST" 
        action="<%= UrlUtils.htmlFormActionLink(pRequest,UrlUtils.PAGE_LINK) %>"> 
        <%= UrlUtils.htmlFormHiddenFields(pRequest,UrlUtils.PAGE_LINK, formName)
%> 
        <%= UrlUtils.emitHiddenField(
                HttpPortletRendererUtil.portletParameter(request, "next_page"),
                "htdocs/path/ThesaurusLink.jsp" ) %>
        <table><tr><td>
            Word of interest:
        </td><td>
            <input
                type="text"
                size="20"
                name="<%= qualParamNameQ %>"
                value="">
        </td></tr></table>
        <input type=submit name="<%= qualParamNameSubmit %>" Value="Search">
    </form>
</center>

Notice how next_page must be explicitly set to point to ThesaurusLink.jsp. If you do not explicitly set next_page in this way, then it defaults to the resource registered in provider.xml, which is ThesaurusForm.jsp.

ThesaurusLink.jsp:

<%
    PortletRenderRequest pRequest = (PortletRenderRequest)
        request.getAttribute(HttpCommonConstants.PORTLET_RENDER_REQUEST);
    String paramNameQ = "q";
    String paramNameNextPage = "next_page";
    String qualParamNameQ = 
    HttpPortletRendererUtil.portletParameter(pRequest, paramNameQ);
    String qualParamNameNextPage = 
        HttpPortletRendererUtil.portletParameter(pRequest, paramNameNextPage);
    String paramValueQ = pRequest.getQualifiedParameter(paramNameQ);
%>
<!-- Output the HTML content -->
<center>
    Words similar to <%= paramValueQ %>
    <br>
    Click the link to search for words related to that word.
    <br>
    <ul>
<%
        Thesaurus t = new Thesaurus();
        String[] relatedWords = t.getRelatedWords(paramValueQ);
        NameValue[] linkParams = new NameValue[2]; 
        linkParams[0] = new NameValue(
            qualParamNameNextPage, "htdocs/path/ThesaurusLink.jsp");
        for (int i=0; i<relatedWords.length; i++)
        {
            linkParams[1] = new NameValue(
                qualParamNameQ, relatedWords[i]);
%>
            <li>
            <b> <%= relatedWords[i] %> </b>
            <%= UrlUtils.constructHTMLLink(
                pRequest,
                UrlUtils.PAGE_LINK,
                "(words related to " + relatedWords[i] + ")",
                "",
                linkParams,
                true,
                true)%>
           </li>
<%
        }
%>
   </ul>
   <a href="<%=XMLUtil.escapeXMLAttribute
               (pRequest.getRenderContext().getPageURL())%>">
      Reset Portlet
   </a>
</center>

32.2.3.6 Restricting Navigation to Resources

One limitation of implementing navigation with private parameters is that users could potentially navigate to portlet resources that you prefer to restrict. To control navigation to restricted resources, you can create a whitelist of acceptable resources to which a user may navigate. If you do not construct a whitelist to restrict navigation, then your portlet's resources are accessible according to the following default rules:

  • Any path immediately beneath the servlet root context is navigable. For example, /index.jsp is accessible but /WEB-INF/web.xml is not.

  • Any path under the htdocs directory is navigable. For example, both /htdocs/multipage/first.jsp and /htdocs/lottery/lotto.jsp are accessible.

To change this default behavior, you can add allowable path values to the provider definition file, provider.xml. For example, suppose you have a portlet where a JSP is used as a controller to forward requests to other pages depending on the pageParameterName private parameter. The XML excerpt in Example 32-11 allows resources under /htdocs/multiportlet to be shown. All other resources are restricted.

Example 32-11 Whitelist Excerpt from the provider.xml File

<portlet class="oracle.portal.provider.v2.DefaultPortletDefinition">
   <id>1</id>
   <name>Multipage</name>
   <title>A MultiPage Portlet</title>
   ...
   <renderer class="oracle.portal.provider.v2.render.RenderManager">
          <contentType>text/html</contentType>
          <showPage>/htdocs/multiportlet/controller.jsp</showPage>
          <pageParameterName>show_page</pageParameterName>
          <allowedPath>/htdocs/multiportlet/*</allowedPath>
   </renderer>
</portlet>

The pattern matching rules for this feature are similar to URL pattern matching in web.xml files. The rules are as follows:

  • To match the defined patterns, the resource path must exactly match unless wildcards are used.

  • The first wildcard is for path matching and consists of a string beginning with / and ending with /*. Any resource whose path starts with this string is matched. For an <allowedPath> value of /htdocs/sub1/*, valid values of the private parameter include /htdocs/sub1/file.jsp and /htdocs/sub1/sub2/file2.jsp.

  • The second wildcard is for file type matching and consists of a string starting with *. and ending with a file extension. Valid values for the page parameter end with that file extension. For an <allowedPathvalue> of *.jsp, valid values of the private parameter include /htdocs/sub1/file.jsp and /htdocs/sub1/file2.jsp.

32.2.4 How to Use JNDI Variables

When writing Java portlets, you may set deployment-specific properties through the JNDI service such that their values may be retrieved from your producer code. In this way, you can specify any property in a producer deployment and then easily access it anywhere in your producer code.

You can use JNDI variables to change producer property values after the producer has been deployed. The environment entry must be declared in web.xml. It can then be updated on deployment using a deployment plan.

PDK-Java provides utilities to enable the retrieval of both producer and non-producer JNDI variables within a Java EE container.

This section includes the following subsections:

32.2.4.1 Declaring JNDI Variables

You declare JNDI variables in the web.xml file for your producer. The format for declaring a JNDI variable is as follows:

<env-entry>
    <env-entry-name>variableName</env-entry-name>
    <env-entry-type>variableType</env-entry-type>
    <env-entry-value>variableValue</env-entry-value> 
</env-entry>

The env-entry-name element contains the name by which you want identify the variable. env-entry-type contains the fully qualified Java type of the variable. env-entry-value contains the variable's default value.

This section includes the following subsections:

32.2.4.1.1 Variable Types

In the env-entry-type element, you must supply the fully qualified Java type of the variable, which is expected by your Java code. The Java types you may use in your JNDI variables are as follows:

  • java.lang.Boolean

  • java.lang.String

  • java.lang.Integer

  • java.lang.Double

  • java.lang.Float

The Java EE container uses these type declarations to automatically construct an object of the specified type and gives it the specified value when you retrieve that variable in your code.

32.2.4.1.2 Variable Naming Conventions

The PDK-Java defines environment variables that can be set at the individual producer service level or at the web application level. To avoid naming conflicts between different producer services or different application components packaged in the same web application, Oracle recommends you devise some naming convention.

Note:

If you use the EnvLookup method, then you must use oracle/portal/provider/service/property. You cannot substitute your own company name or component in this case.

For example:

  • Producer service-specific names should be of the form:

    company/component name/producer name/variable name
    
  • Shared names must be of the form:

    company/component name/producer name/global
    

where:

  • company is the name of the company owning the application.

  • component name is the name of the application or component with which the producer is associated.

  • producer name is the service name of the producer.

  • variable name is the name of the variable itself.

As you can see, these naming conventions are similar to those used for Java packages. This approach minimizes the chance of name collisions between applications or application components. PDK-Java provides utilities that enable you to retrieve variables in this form without hard coding the service name of the producer into your servlets or JSPs. The service name need only be defined in the producer's WAR file. For more information about retrieving JNDI variables, see Section 32.2.4.3, "Retrieving JNDI Variables."

32.2.4.1.3 Examples

The following examples illustrate producer variable names:

oracle/portal/myProvider/myDeploymentProperty
oracle/portal/myprovider/myProperties/myProperty

The following example illustrates non-producer variable names:

oracle/portal/myOtherProperty

32.2.4.2 Setting JNDI Variable Values

In your producer deployment, you may want to set a new value for some or all of your JNDI variables. You can perform this task by setting the values manually within a WLS deployment plan. Deployment plans can be created for the producer deployment through the WLS console.

To set variable values manually within a deployment plan:

  1. Go to the producer deployment with the WLS console and create a new deployment plan if one does not exist.

  2. Edit the deployment plan XML file. For each deployment property you want to set, add the following variable definition directly under the <deployment-plan> tag:

    <variable-definition>
      <variable>
        <name>jndi_var_def</name>
        <value>false</value>
      </variable>
    </variable-definition>
    
  3. To tie this variable definition to the actual JNDI variable, add the following for each property under the WEB-INF/web.xml module descriptor (oracle/portal/sample/rootDirectory is used as an example):

    <module-descriptor external="false">
      <root-element>web-app</root-element>
      <uri>WEB-INF/web.xml</uri>
      <variable-assignment>
        <name>jndi_var_def</name>
        <xpath>/web-app/env-entry/[env-entry-name="oracle/portal/sample/rootDirectory"]/env-entry-value</xpath>
       </variable-assignment>
    </module-descriptor>
    
  4. Save and close the file.

  5. Select Update on the producer deployment to apply the deployment plan for the new settings to take effect.

32.2.4.3 Retrieving JNDI Variables

JNDI is a standard Java EE technology. As such, you can access JNDI variables through Java EE APIs. For example:

String myVarName = "oracle/portal/myProvider/myVar"
String myVar = null;
try 
{
   InitialContext ic = new InitialContext();
   myVar = (String)ic.lookup("java:env/" + myVarName);
}
catch(NamingException ne)
{
   exception handling logic
}

In addition to the basic Java EE APIs, PDK-Java includes a simple utility class for retrieving the values of variables defined and used by the PDK itself. These variables conform to the naming convention described in Section 32.2.4.1.2, "Variable Naming Conventions" and are of the form:

oracle/portal/provider_service_name/variable_name
oracle/portal/variable_name

To use these APIs, you need only provide the provider_service_name and the variable_name. The utilities construct the full JNDI variable name, based on the information you provide, and look up the variable using code similar to that shown earlier and return the value of the variable.

The EnvLookup class (oracle.portal.utils.EnvLookup) provides two lookup() methods. One retrieves producer variables and the other retrieves non-producer variables. Both methods return a java.lang.Object, which can be cast to the Java type you are expecting.

The following code example illustrates the retrieval of a producer variable:

EnvLookup el = new EnvLookup();
String s = (String)el.lookup(myProviderName, myVariableName);

myProviderName represents the service name for your producer, which makes up part of the variable name. myVariableName represents the portion of the variable name that comes after the producer's service name. The example assumes the variable being retrieved is of type java.lang.String.

To retrieve a non-producer variable, you use the same code, you pass only one parameter, the variable name, to the lookup(), again excluding the oracle/portal prefix.

EnvLookup el = new EnvLookup();Object o = el.lookup(myVariableName);

Table 32-1 shows the JNDI variables provided by default with PDK-Java. If you do not declare these variables, then PDK-Java looks for their values in their original locations (web.xml and the deployment properties file).

Table 32-1 PDK-Java JNDI Variables

Variable Description
oracle/portal/provider/provider_name/autoReload

Boolean auto reload flag. Defaults to true.

oracle/portal/provider/provider_name/definition

Location of producer's definition file.

oracle/portal/provider/global/log/logLevel

Log setting (0 through 8). 0 being no logging and 8 the most possible logging.

oracle/portal/provider/provider_name/maxTimeDifference

Producer's HMAC time difference.

oracle/portal/provider/<service_name>/resourceUrlKey

Authentication key for resource proxying through the Parallel Page Engine. See Oracle Fusion Middleware Administrator's Guide for Oracle Portal for more information.

oracle/portal/provider/provider_name/rootDirectory

Location for producer personalizations. No default value.

oracle/portal/provider/provider_name/sharedKey

HMAC shared key. No default value.

oracle/portal/provider/provider_name/showTestPage

(non-producer) A Boolean flag that determines if a producer's test page is accessible. Defaults to true.

oracle/portal/provider/global/transportEnabled

A Boolean flag that determines whether Edit Defaults personalizations may be exported and imported.


32.2.5 How to Access Session Information

When a user accesses a page, it initiates a public, unauthenticated session and tracks information about the session across requests. If the user logs in, then this session becomes an authenticated session of the logged-in user. This session terminates when any of the following occur:

  • The browser session terminates (that is, the user closes all the browser windows).

  • The user explicitly logs out.

  • The session times out because the user's idle time exceeds the configured limit.

As part of the metadata generation, all of the producers that contribute portlets to the page are contacted, if they specified during registration that they be called for some special processing. This call allows producers to do processing based on the user session, log the user in the producer's application if needed, and establish producer sessions. For producers, this call is referred to as initSession. As most web-enabled applications track sessions using cookies, this API call enables the producer of the application to return cookies.

You can use the session store to save and retrieve information that persists during the portal session. This information is only available, and useful, to you during the life of the session. You should store only temporary information in the session store. Application developers may use the session store to save information related to the current user session. Data in the session store can be shared across portlets.

If the information you want to store must persist across sessions, then you may want to store it in the preference store instead. Some common applications of the session store are as follows:

  • To cache data that is expensive to load or calculate (for example, search results).

  • To cache the current state of a portlet (for example, the current range, or page, of search results displayed in the portlet, or sequence of events performed by user).

Before you implement session storage, you should carefully consider the performance costs. Because portlets and producers are remote, it can be a relatively expensive operation to create and maintain even a small amount of information in the session store. For this reason, you may want to avoid altogether any session storage for public pages that are accessed frequently by many users.

Furthermore, while using the session store with producers, you create a stateful application that tracks state information in memory. Similarly, you create a stateful application if you use the file-system implementation of preference store.

If scalability is an important concern for you, then a stateful application may cause you problems. Stateful applications can affect the load-balancing and failover mechanism for your configuration. Even though you may deploy multiple middle-tiers, you must implement sticky routing (where the same node handles subsequent requests in the same session) to track state. Sticky routing may result in lopsided load-balancing or loss of session data in case a node crashes, affecting failover. This issue is one reason why many developers prefer to build stateless applications. However, if scalability is not a concern, then a stateful application should present no problems for you.

The PDK Framework represents the session with a ProviderSession object, which is established during the call to the Provider Instance's initSession method. This object is associated with the ProviderUser. To make data persistent between requests, you must write data into the session object using the setAttribute method on the ProviderSession object. This method maps a java.lang.Object to a java.lang.String and stores that mapping inside the session object. The String can then be used to retrieve the Object during a subsequent request, provided the session is still valid.

A producer session may become invalid for the following reasons:

  • The session times out.

  • The invalidate method on ProviderSession is called.

  • The JVM process running the servlet container is terminated.

All portlets contained by the same ProviderInstance share the same session for a particular ProviderUser. Therefore, data unique to a particular portlet instance must be mapped to a unique String in the session. This is accomplished using the portletParameter method in the PortletRendererUtil class. This method makes a supplied String parameter or attribute name unique to a PortletInstance, by prefixing it with a generated identifier for that instance. You can use the returned instance-specific name to write portlet instance data into the session.

More on OTN

For more detailed information about the PDK Framework classes, see the JavaDoc on OTN:

http://www.oracle.com/technology/products/webcenter/portlet_download.html

In the example in this section, session storage is used to count the number of times your portlet has rendered in Shared Screen mode.

Before You Begin

The steps that follow assume that you have:

To implement session storage:

  • Import ProviderSession, PortletRendererUtil, and HttpPortletRendererUtil.

  • Retrieve the producer session.

  • Read and write the session by accessing it from within your Java portlet.

  • Set the session to true in provider.xml.

  • Register the producer for session storage and set the Login Frequency.

The steps that follow describe how to add a session count to your portlet that displays how many times the portlet has been rendered for the current session.

  1. After using the wizard to create a portlet, you can edit the JSP for the Show page in Oracle JDeveloper. You must import the following classes:

    <%@page contentType="text/html; charset=windows-1252"
    import="oracle.portal.provider.v2.render.PortletRenderRequest"
    import="oracle.portal.provider.v2.http.HttpCommonConstants"
    import="oracle.portal.provider.v2.ProviderSession"
    import="oracle.portal.provider.v2.render.PortletRendererUtil"
    import="oracle.portal.provider.v2.render.http.HttpPortletRendererUtil"
    %>
    
  2. Insert code that checks for a valid session first and then increments the count and displays it. If the session is valid and a previously stored value exists, then you display the value, increment the count, and store the new value. If the session is valid but no previously stored value exists, then you initialize a new count starting with 1, and display and store the value. You also want to obtain the unique string key for this portlet and then use an it in an array to count the session. If no session information was received, then you want to provide information to the user indicating they may need to log in again.

    <%
    PortletRenderRequest pReq = (PortletRenderRequest)
    request.getAttribute(HttpCommonConstants.PORTLET_RENDER_REQUEST);
    ProviderSession pSession = pReq.getSession();
      if (pSession != null)
      {
        String key = PortletRendererUtil.portletParameter(pReq, "count");
        Integer i = (Integer)pSession.getAttribute(key);
        if (i == null)
        {
          i = new Integer(0);
        }
        i = new Integer(i.intValue()+1);
        pSession.setAttribute(key, i);
    %>
    
    <p>Render count in this session: <%=i%> </p>
    
    <% 
      } 
      else 
      {
    %>
    
    <p>The session has become invalid</p>
    <br>
    Please log out and log in again.
    <%
      } 
    %>
    
  3. By default, the wizard does not set session to true in provider.xml. You must update this flag in order for the producer to receive session information from the portal. You should only set this tag to true if you are using session information in your producer or portlets. By setting this flag to true, extra load is added to the producer calls.

    <provider class="oracle.portal.provider.v2.DefaultProviderDefinition">
    <session>true</session>
    
    More on OTN

    For more information about the syntax of provider.xml, see the provider JavaDoc on OTN:

    http://www.oracle.com/technology/products/webcenter/portlet_download.html

  4. Register your producer with session support. For a reminder on how to register your portlet, see Section 33.4, "Registering and Viewing Your Portlet."

  5. If you have not added your Java portlet to a page, then do so now. Ensure that you perform the following tasks:

    • Refresh the producer to accept the new changes.

    • Re-login in case your session is no longer valid.

32.2.6 How to Enhance Portlet Performance with Caching

Once you have completed the basic functionality of your portlet, you may want to turn your attention to portlet performance.

Caching is a common technique for enhancing the performance of web sites that include a great deal of dynamic content. The overhead involved in retrieving data and generating the output for dynamic content can be significantly reduced by proxying requests through a local agent backed by a large, low-latency data store known as a cache. The cache agent responds to a request in one of two ways, as follows:

  • If a valid version of the requested content exists in the cache, then the agent simply returns the existing cached copy, thus skipping the costly process of content retrieval and generation. This condition is called a cache hit.

  • If a valid version of the requested content does not exist in the cache, then the agent forwards the request to its destination and awaits the return of the content. The agent returns the content to the requester and stores a local copy in its cache for reuse if a subsequent request for the same content arises. This condition is called a cache miss.

Producers generate dynamic content (that is, portlets) and they reside remotely from the custom WebCenter application instance on which they are deployed. As such, caching might improve their performance. The architecture lends itself well to caching. You can cache the portlets rendered by your producer and reuse the cached copies to handle subsequent requests, minimizing the overhead your producer imposes on page assembly.

The producer can use any one of three different caching methods, depending upon which one is best suited to the application. The methods differ chiefly in how they determine whether content is still valid. Following are the three caching methods:

  • Expiry-based Caching: When a producer receives a render request, it stamps its response with an expiry time. The rendered response remains in the cache and fills all subsequent requests for the same content until its expiry time passes. This caching scheme is perhaps the simplest and most performant because the test for cache validity requires very little overhead and does not involve any network round trips. Expiry-based caching suits applications where the content has a well-defined life span. For content with a less certain life span, however, expiry-based caching is less effective. For more information, see Section 32.2.6.1, "Activating Caching" and Section 32.2.6.2, "Adding Expiry-Based Caching."

  • Validation-based Caching: When a producer receives a render request, it stamps its response with a version identifier (or E Tag). The response goes into the cache, but, before the PPE can reuse the cached response, it must determine whether the cached version is still valid. It sends the producer a render request that includes the version identifier of the cached content. The producer determines whether the version identifier remains valid. If the version identifier is still valid, then the producer immediately sends a lightweight response to the PPE without any content, which indicates the cached version can be used. Otherwise, the producer generates new content with a new version identifier, which replaces the previously cached version. In this form of caching, the PPE must always confirm with the producer whether the content is up to date. The validity of the cached copy is determined by some logic in the producer. The advantage of this approach is that the producer controls the use of the cached content rather than relying on a fixed period. For more information, see Section 32.2.6.1, "Activating Caching" and Section 32.2.6.3, "Adding Validation-Based Caching."

This section includes the following subsections:

Before You Begin

The steps that follow assume that you have:

32.2.6.1 Activating Caching

To use the caching features in your producers, you must first activate the middle tier cache. This cache is known as the PL/SQL Cache because it is the same cache used by mod_plsql, the Oracle HTTP Server plug-in that calls database procedures, and hence database producers, over HTTP.

Usually, your administrator is responsible for the configuration details of caching.

32.2.6.2 Adding Expiry-Based Caching

Expiry-based caching is a simple caching scheme to implement, and can be activated declaratively in your XML producer definition. You can set an expiry time for the output of any ManagedRenderer you use by setting its pageExpires property to the number of minutes you want the output to be cached for. For example, suppose you want portlet output to be cached for one minute.

To add expiry-based caching:

  1. After you have used the Create Oracle PDK-Java Portlet wizard to build a portlet as described in Section 31.2.3, "How to Create a PDK-Java Portlet," edit the provider.xml file and set the pageExpires property tag of showPage to 1. This sets an expiry entry of one minute for the portlet.

    By default the wizard generates a standard and compressed tag for showPage. Expand the tag to include a subtag of pageExpires:

    <showPage class="oracle.portal.provider.v2.render.http.ResourceRenderer">
       <resourcePath>/htdocs/mycacheportlet/MyCachePortletShowPage.jsp
          </resourcePath>
       <pageExpires>1</pageExpires>
    </showPage>
    
    More on OTN

    For more information about the syntax of provider.xml, see the provider JavaDoc on OTN:

    http://www.oracle.com/technology/products/webcenter/portlet_download.html

  2. Test that the portlet is cached for 1 minute by adding some JSP code to your show page. You can simply add the current time to your JSP.

    <%@page contentType="text/html; charset=windows-1252"
       import="oracle.portal.provider.v2.render.PortletRenderRequest"
       import="oracle.portal.provider.v2.http.HttpCommonConstants"
       import="java.util.Date"
       import="java.text.DateFormat"
    %>
    
    <%
     PortletRenderRequest pReq = (PortletRenderRequest)
       request.getAttribute(HttpCommonConstants.PORTLET_RENDER_REQUEST);
     DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG,
       DateFormat.LONG,pReq.getLocale());
     String time = df.format(new Date());
    %>
    
    <P>Hello <%=pReq.getUser().getName() %>.</P>
    <P>This is the <b><i>Edit</i></b> render mode!</P>
    <P>This information is correct as of <%=time%>.</P>
    

    When viewing the portlet, you see that the time (including seconds) is constant for one minute. After the time has expired, the portlet displays the most current time and a new cache is set.

32.2.6.3 Adding Validation-Based Caching

Adding validation-based caching requires slightly more effort, but gives you explicit control over exactly which requests to your producer are cache hits. As an example, you may want to update the cache only when data within the portlet has changed. To implement this algorithm, you must override the prepareResponse method. The signature of the BaseManagedRenderer.prepareResponse method is:

public boolean prepareResponse(PortletRenderRequest pr)
                        throws PortletException,
                               PortletNotFoundException

In your version of prepareResponse(), do the following:

  • Retrieve the cached version identifier set by the PPE in the render request by calling the HttpPortletRendererUtil.getCachedVersion() method:

    public static java.lang.String getCachedVersion
        (PortletRenderRequest request)
    
  • If the portlet finds the previously cached version valid, then the appropriate header must be set by calling the HttpPortletRendererUtil.useCachedVersion() method. It also instructs the RenderManager that it is not necessary to call renderBody() to render the portlet body.

    public static void useCachedVersion(PortletRenderRequest request)
    

    Otherwise, use HttpPortletRendererUtil.setCachedVersion() to generate a new version of the portlet, which is cached. It also indicates to the PPE that the renderBody() method has to be called to regenerate the portlet content.

    public static void setCachedVersion(PortletRenderRequest request,
                                        java.lang.String version,
                                        int level)
                                 throws java.lang.IllegalArgumentException
    

For validation-based caching, you need not update provider.xml. You can view the portlet by refreshing the page or adding the portlet to a page and updating the content. If the content has changed, then the portlet shows the new content. If the content has not changed, then a cached version of the portlet is displayed.

32.3 Testing Portlet Personalization

If you have implemented personalization for your portlet, then the Personalize link only appears on the portlet for authenticated users. Hence, to test the personalization of a portlet (the Personalize link), you must have some form of security implemented for the application consuming the portlet. For testing purposes, you may prefer to just configure the most basic authentication possible. For more information, see Section 37.8, "Configuring Basic Authentication for Testing Portlet Personalization."

32.4 Creating a Struts Portlet

Oracle PDK contains extensions to integrate Apache Struts applications. This section explains how to build a portlet from an existing struts application. You can also follow these steps to create a portlet that uses the Model View Controller paradigm. The PDK-Java extensions described in this section rely on Apache Struts 1.1.

You can use the Struts framework to enhance portlet behavior. For example, a Struts controller may be used for page flow within a portlet. A portlet renderer is used as a bridge between portlet render requests and Struts servlet requests

The following code shows a portion of the producer definition file (provider.xml):

...
<renderContainer>true</renderContainer>
    <renderCustomize>true</renderCustomize>
    <autoRedirect>true</autoRedirect>
    <contentType>text/html</contentType>
    <showPage class="oracle.portal.provider.v2.render.http.StrutsRenderer">
        <defaultAction>showCustomer.do</defaultAction>
    </showPage>
</renderer>
...
More on OTN

For more information about the syntax of provider.xml, see the provider JavaDoc on OTN:

http://www.oracle.com/technology/products/webcenter/portlet_download.html

The showPage of the Struts portlet contains two important components:

  • The renderer class (oracle.portal.provider.v2.render.http.StrutsRenderer) receives the render requests and forwards them to the Struts Action Servlet.

  • The defaultAction tag defines the Struts action that is used by default when the portlet is called for the first time.

The PDK-Java enables you to easily develop a view (portlet view) of your Struts application. This view enforces a consistent look and feel of your Struts portlet using portal styles, and enables the end user to use the application within the portal.

To create a Struts portlet, you must use the PDK-Java Struts tags, which are extensions of the default Struts JSP tags. This development process is similar to that of creating a standalone Struts application. For portlets in a producer application to use the Struts framework, the Struts components must be part of the same web application.

To publish a part of an existing Struts application as a portlet, Oracle recommends that you first create a new view to serve as the portlet view of your application. This view uses existing objects (Actions, ActionForm, and so on) with a new mapping and new JSPs.

Note:

Although Oracle recommends that you create a portlet view of your application, you could alternatively replace your application's struts tags with PDK-Java struts tags. This approach enables your application to run both as a standalone struts application and a portlet.

In this example, you create a portlet that enables you to add a new entry to a blog. Figure 32-4 and Figure 32-5 show how you submit a blog and save a blog entry.

Figure 32-4 Submitting a Blog

Shows updating a BLOG.
Description of "Figure 32-4 Submitting a Blog"

Figure 32-5 Saving a Blog Entry

Shows saving a BLOG.
Description of "Figure 32-5 Saving a Blog Entry"

prepareNewBlog is a simple empty action that redirects the request to the enterNewBlog.jsp page. This page shows a form for submitting a new blog.

The corresponding entry in the struts-config.xml is:

<action path="/prepareNewBlog" scope="request"
    type="view.PrepareNewBlogAction" >
    <forward name="success" path="/view/enterNewBlog.jsp"/>
</action>
<action path="/saveNewBlog" name="blogForm" scope="request"
    type="view.SaveNewBlogAction" input"/view/enterNewBlog.jsp"  >
    <forward name="success" path="/view/newBlogConfirmation.jsp"/>
</action>

This section includes the following subsections:

32.4.1 How to Create a New Flow and View to Host the Portlet Actions

To create a new view, first create a new set of ActionMappings (page flow) that redirects the various actions and requests to Portal-specific JSPs.

<action path="/portal/prepareNewBlog" scope="request"
    type="view.PrepareNewBlogAction" >
    <forward name="success" path="/view/portal/enterNewBlog.jsp"/>
</action>
<action path="/portal/saveNewBlog" name="blogForm" scope="request"
    type="view.SaveNewBlogAction" input="/view/enterNewBlog.jsp"  >
    <forward name="success" path="/view/portal/newBlogConfirmation.jsp"/>
</action>

As you can see, only the path attributes are modified. The FormBean Action responsible for the application business logic remains unchanged.

32.4.2 How to Create the New JSPs

As specified in the previous step, the actions forward the request to new JSPs, which are responsible for rendering the portlet content. Your new portlet view JSPs can share the HTML with the standalone view, but ensure the portlet meets the following criteria:

  • Uses styles that enforce a consistent look and feel with the rest of the page.

  • Contains HTML code that is allowed in HTML table cells (that is, no <html>, <body>, and <frame> tags).

  • Uses the PDK-Java version of the Struts HTML tag library. This renders URLs in the correct format to work in a portlet context.

The PDK Struts HTML tag library contains versions of the Struts HTML tags that can be used in portlet markup.

Note:

You can register the Oracle PDK with Oracle JDeveloper so that you can drop the tags from the Oracle JDeveloper Components Palette. For more information, see the Registering a Custom Tag Library in JDeveloper section in the Oracle JDeveloper online Help.

32.4.3 How to Create a Portlet

You can create your Struts portlet either manually or by using the Create Oracle PDK-Java Portlet wizard. Although the wizard does not explicitly offer Struts support, you can use the wizard to build your Struts portlet.

To create a portlet:

  1. In JDeveloper, open the Oracle PDK-Java Portlet wizard.

    For more information, see Section 31.2.3, "How to Create a PDK-Java Portlet."

  2. On the View Modes page of the wizard, for the Show Page mode, select an Implementation Style of Java Class.

  3. For the Package Name, enter oracle.portal.provider.v2.render.http.

  4. For the Class Name, enter StrutsRenderer. This generates the skeleton of the portlet renderer class, StrutsRenderer.

  5. As the StrutsRenderer is part of the PDK, you do not need this generated file. So, when you finish the wizard, you must delete the file generated by the wizard. To do so, click the file in the System Navigator window, then from the File menu, select Erase from Disk in JDeveloper.

  6. Edit the provider.xml and change the following properties:

    At the producer level, perform the following:

    • If the Struts application uses sessions (for example, the form sysnchronizer token mechanism is used or <actionInSession> is set to true), then enable session handling:

      <session>true</session>
      

    At the portlet level, perform the following:

    • If you want users to always return to the same portlet state as when they left the container page, then you can configure the struts renderer to save the struts action in the struts context:

      <actionInSession>true</actionInSession>
      

      If you prefer that users always start from the beginning of the portlet when they return from outside the container page, then you should not save the struts action:

      <actionInSession>false</actionInSession>
      

      Note that this setting is the default behavior.

    • Specify the first action to raise when the portlet is called. Use the following code:

      <showPage class="oracle.portal.provider.v2.render.http.StrutsRenderer">
      <defaultAction>/portal/prepareNewBlog.do</defaultAction>
      </showPage>
      
    More on OTN

    For more information about the syntax of provider.xml, see the provider JavaDoc on OTN:

    http://www.oracle.com/technology/products/webcenter/portlet_download.html

32.4.4 How to Extend the Portlet to Add Business Logic

In your application, you should add code specific to your environment, such as the user's information, personalization, and localization. To do so, you can create a new Action class that is only called in context, and handles all business logic.

32.4.5 How to Register the Producer

Now that your portlet is ready to be used by consumers, you must make it accessible by registering it. For information about how to register your PDK-Java portlet, see Section 33.4, "Registering and Viewing Your Portlet."