Skip Headers
Oracle® WebCenter Framework Developer's Guide
10g (10.1.3.2.0)

Part Number B31074-05
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

19 Enhancing Java Portlets

This chapter explains how to enhance Java portlets you created with the Oracle JDeveloper Portlet Wizard, and how to make portlets out of your struts and JSF applications. This chapter contains the following sections:

More on OTN

The source code for many of the examples referenced in this chapter is available as part of the Portlet Developer's Kit (PDK). You can download the PDK from it's page on Oracle Technology Network (OTN):

http://www.oracle.com/technology/products/ias/portal/pdk.html

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

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

You can find the JavaDoc reference for PDK-Java in:

../pdk/jpdk/v2/apidoc

19.1 Enhancing Java Portlet Specification (JPS) Portlets

Once you have built your initial portlet in the Portlet Wizard as described in Section 18.5, "Building JPS-Compliant Portlets with Oracle JDeveloper", you will want to enhance it. Because JPS 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. Some of the more important enhancements that you might want to perform are as follows:

19.1.1 Adding Personalization

In this section, you enhance the portlet you created in Section 18.5, "Building JPS-Compliant Portlets with Oracle JDeveloper" 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 will also see how easily you can redeploy a portlet.

19.1.1.1 Assumptions

The following assumptions are made to perform the tasks in this section:

  • You built a portlet using the wizard, successfully registered the producer, and added the portlet to the page.

  • In the wizard, on the Customization Preferences page, you added a preference called portletcontent.

  • You enabled Oracle ADF security for your application by performing the steps in Section 10.2, "Setting Up Security for Your Application". The user must be logged in to the application as an authenticated user to access the personalization feature.

19.1.1.2 Implementing Personalization

In this section, you add some code to My Java Portlet, redeploy the portlet, and then test it. To do this, perform the following steps:

  1. In Oracle JDeveloper, double-click the view.jsp file for your JPS-Standard portlet in the Application Navigator.

  2. Add the code in Example 19-1 to the Source view of your view.jsp:

    Example 19-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]%><%}%> 
    
  3. Now click the Design tab. Your page should look something like Figure 19-1.

    Figure 19-1 view.jsp in the Design View

    This image shows the Design view of View.jsp.
    Description of "Figure 19-1 view.jsp in the Design View"

  4. Open edit.jsp in the visual designer and click the Design tab. Notice that the JSP consists of a form field, a form input field, and two form button fields, as shown in Figure 19-2.

    Figure 19-2 edit.jsp in the Design View

    This image shows the Design view of Edit.jsp.
    Description of "Figure 19-2 edit.jsp in the Design View"

  5. Add the code that is indicated in bold in the following code excerpt to implement a form field called Content:

    Example 19-2 edit.jsp Sample Code

    <%@ page contentType="text/html"
        pageEncoding="windows-1252"
        import="javax.portlet.*,java.util.*,
        portlet.Portlet2,portlet.resource.Portlet2Bundle"%>
    <%@ 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(Portlet2Bundle.PORTLETTITLE) %>
    </P></TD><TD WIDTH="80%">
    <INPUT CLASS="portlet-form-input-field" TYPE="TEXT"
      NAME="<%= Portlet2.PORTLETTITLE_KEY %>"
      VALUE="<%= prefs.getValue(Portlet2.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="<%= Portlet2.OK_ACTION
    %>"
      VALUE="<%= res.getString(Portlet2Bundle.OK_LABEL) %>">
    <INPUT CLASS="portlet-form-button" TYPE="SUBMIT" NAME="<%= Portlet2.APPLY
    ACTION %>"
      VALUE="<%= res.getString(Portlet2Bundle.APPLY_LABEL) %>">
    </TD></TR>
    </TABLE>
    </FORM>
    
  6. Click the Design tab. Your page should now look similar to Figure 19-3.

    Figure 19-3 Modified edit.jsp in the Design View

    This figure shows edit.jsp with the added field.
    Description of "Figure 19-3 Modified edit.jsp in the Design View"

  7. Open WelcomePortlet.java in the visual editor and insert the following two lines of code (indicated in bold) in 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");
    prefs.setValues("portletContent", buildValueArray(contentParam));
    prefs.store();
    
  8. Redeploy the portlet. Notice that Oracle JDeveloper automatically saves and compiles the code before deploying the portlet. See Section 18.9, "Deploying Your Portlet to an Application Server" for a reminder of how to perform this step.

  9. Reload the page that contains the portlet. The portlet displays the text Portlet Content, which was one of the changes you made in Oracle JDeveloper.

  10. Click the Personalize link. You can see the new form field that you added in Oracle JDeveloper.

  11. Enter the following HTML in the Content field, replacing the words Portlet Content.

  12. <p>Read <em>The Path to Portlet Interoperability</em> by John Edwards in
    <strong>Oracle Magazine</strong>, Nov-Dec 2003. </p>
    <p>It discusses JSR 168 and WSRP open portals. </p>
    
  13. Click Apply and then click Close. The HTML is rendered in the portlet.

19.1.2 Implementing 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 not be necessary any more. Until then, you are required to use the Oracle-specific portlet container and API extensions.

Using the 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.

Adding Public Parameters to JPS Portlets Using WSRP 2.0 Navigational Parameters

To add parameters to your portlet, perform the following steps:

  1. Right-click your project and choose New from the context menu.

  2. From New Galleries, choose the Portlets category and then the Standards-based Java Portlet (JSR 168) item.

  3. When you reach the General Properties page of the wizard, be sure to select the Enable WSRP V2 inter-portlet communication using Oracle extensions check box as shown in Figure 19-4.

    Figure 19-4 General Portlet Properties Page in Portlet Wizard

    Description of Figure 19-4 follows
    Description of "Figure 19-4 General Portlet Properties Page in Portlet Wizard"

  4. Proceed through the wizard as you normally would until you reach the Portlet Navigation Parameters page. See Section 18.5, "Building JPS-Compliant Portlets with Oracle JDeveloper" for basic information about going through the wizard.

  5. When you reach the Portlet Navigation Parameters page (Figure 19-5), click the Add button. A new row is added to the Navigation parameters table.

    Figure 19-5 Portlet Navigation Parameters Page of Portlet Wizard

    Description of Figure 19-5 follows
    Description of "Figure 19-5 Portlet Navigation Parameters Page of Portlet Wizard"

  6. Click the default name that appears in the Name column. Use the Backspace key to delete the default name.

  7. Enter a new parameter name, for example, Parameter_01.

  8. Repeat steps 6 and 7 for the Label and Hint columns.

  9. Repeat steps 5 through 8 for each public parameter that you want to add to the portlet.

  10. Click Next.

  11. Click Finish.

  12. Once you finish the Portlet Wizard, your portlet is created.

  13. In the Applications Navigator, expand the Web Content and WEB-INF nodes. You should see your portlet.xml and web.xml and oracle-portlet.xml files there.

  14. Right-click oracle-portlet.xml and choose Open from the context menu. You should see entries for the parameters that you added on the Portlet Navigation Parameters page in the wizard, as shown in Example 19-3.

    Example 19-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 xml:lang="en">Parameter 1</label>
                <hint xml:lang="en">First parameter.</hint>
            </navigation-parameters>
            <navigation-parameters>
                <name>Parameter_02</name>
                <type>xsi:string</type>
                <label xml:lang="en">Parameter 2</label>
                <hint xml:lang="en">Second parameter.</hint>
            </navigation-parameters>
            <portlet-id>1164232649525</portlet-id>
        </portlet-extension>
    </portlet-app-extension>
    
  15. The sample portlet in Example 19-4 shows you a portlet that contains a form and submits navigation parameters.

    Example 19-4 Form Portlet Submitting Parameters

    /* Copyright (c) 2006, Oracle. All rights reserved.  */
    package oracle.portlet.server.wsrpsample;
    import java.io.IOException;
    import java.io.PrintWriter;
    import javax.portlet.ActionRequest;
    import javax.portlet.ActionResponse;
    import javax.portlet.PortletException;
    import javax.portlet.PortletMode;
    import javax.portlet.PortletPreferences;
    import javax.portlet.PortletSession;
    import javax.portlet.PortletURL;
    import javax.portlet.RenderRequest;
    import javax.portlet.RenderResponse;
    import javax.portlet.WindowState;
    
    public class ParameterFormPortlet extends CustomizablePortlet
    {
      // Form field names
      public static final String PARAMETER1  = "ora_wsrp_navigparam_Parameter1";
      public static final String PARAMETER2  = "ora_wsrp_navigparam_Parameter2";
      public static final String PARAMETER3  = "ora_wsrp_navigparam_Parameter3";
      
      public static final String FORM_PARAMETER1  = "form_Parameter1";
      public static final String FORM_PARAMETER2  = "form_Parameter2";
      public static final String FORM_PARAMETER3  = "form_Parameter3";
      public static final String FORM_SUBMIT      = "dosub";
      public static final String P1PROMPT_KEY      = "p1Prompt";
      public static final String P2PROMPT_KEY      = "p2Prompt";
      public static final String P3PROMPT_KEY      = "p3Prompt";
      
      public static final String   MODE_NAME_PARAM  = "mode";    
      public static final String   MODE_VIEW         = "view";   
      public static final String   MODE_EDITDEFAULTS = "editDefaults";   
      /**
       * Helper method to serve up the VIEW mode.
       *
       * @param     request the portlet request
       * @param     response the portlet response
       *
       * @exception PortletException if the portlet has trouble fulfilling the
       *            request
       * @exception IOException if the streaming causes an I/O problem
       */
      public void doView (RenderRequest request,
                          RenderResponse response) 
          throws PortletException, IOException
      {
        
          String p1Prompt = request.getPreferences().getValue(P1PROMPT_KEY, 
               "Parameter 1:");
          String p2Prompt = request.getPreferences().getValue(P2PROMPT_KEY, 
               "Parameter 2:");
          String p3Prompt = request.getPreferences().getValue(P3PROMPT_KEY, 
               "Parameter 3:");
        
          response.setContentType("text/html; charset=UTF-8");
          PrintWriter out = response.getWriter();
          PortletURL formURL = response.createActionURL();
    
          //Retrieve any values for the nav parameters from the request
          String param1 = request.getParameter(PARAMETER1);
          String param2 = request.getParameter(PARAMETER2);
          String param3 = request.getParameter(PARAMETER3);
          param1 = (param1 != null)?param1:"";
          param2 = (param2 != null)?param2:"";
          param3 = (param3 != null)?param3:"";
          
          out.print("<form method=\"POST\" action=\"");
          out.print(formURL.toString()); 
          out.println("\">"); 
          
          out.print("<center><table>");
          if (!"".equals(p1Prompt))
          {
            out.print("<tr><td class=\"portlet-form-field\">");
            out.print(p1Prompt);
            out.print("</td><td>");
            out.print("<input  class=\"portlet-form-input-field\" 
                  type=\"text\" size=\"20\" name=\"");
            out.print(FORM_PARAMETER1); 
            out.print("\" value=\"");
            out.print(escapeForHTMLAttr(param1));
            out.print("\">");
            out.print("</td></tr>");
          }
          else
          {      
            out.print("<input type=\"hidden\" name=\"");
            out.print(FORM_PARAMETER1);
            out.print("\" value=\"\">");
          }
          if (!"".equals(p2Prompt))
          {
            out.print("<tr><td class=\"portlet-form-field\">");
            out.print(p2Prompt);
            out.print("</td><td>");
            out.print("<input class=\"portlet-form-input-field\" 
                  type=\"text\" size=\"20\" name=\"");
            out.print(FORM_PARAMETER2);
            out.print("\" value=\"");
            out.print(escapeForHTMLAttr(param2));
            out.print("\">");
            out.print("</td></tr>");
          }
          else
          {      
            out.print("<input type=\"hidden\" name=\"");
            out.print(FORM_PARAMETER2);
            out.print("\" value=\"\">");
          }
          if (!"".equals(p3Prompt))
          {
            out.print("<tr><td class=\"portlet-form-field\">");
            out.print(p3Prompt);
            out.print("</td><td>");
            out.print("<input class=\"portlet-form-input-field\" 
                  type=\"text\" size=\"20\" name=\"");
            out.print(FORM_PARAMETER3);
            out.print("\" value=\"");
            out.print(escapeForHTMLAttr(param3));
            out.print("\">");
            out.print("</td></tr>");
          }
          else
          {      
            out.print("<input type=\"hidden\" name=\"");
            out.print(FORM_PARAMETER3);
            out.print("\" value=\"\">");
          }
          out.print("<tr><td colspan=\"2\" align=\"center\">");
          
          out.print("<input type=\"submit\" class=\"portlet-form-button\" name=\"");
          out.print(FORM_SUBMIT);
          out.print("\" value=\"OK\">"); 
          out.print("</td></tr></table>");
          
          out.print("<input type=\"hidden\" name=\"");
          out.print(MODE_NAME_PARAM);
          out.print("\" value=\"");
          out.print(MODE_VIEW);
          out.print("\">");
          
          out.print("</form>");             
          out.println("</center>");
      }
      
      /**
       * Handles actions.
       *
       * @param     request the portlet request
       * @param     actionResponse the portlet response
       *
       * @exception IOException if streaming causes an I/O problem
       * @exception PortletException if something else goes wrong
       */
      public void processAction (ActionRequest request,
                                 ActionResponse actionResponse)
          throws PortletException, IOException
      {
        //
        // Determine what kind of action we have by examining the mode parameter
        //
        boolean editDefaults = MODE_EDITDEFAULTS.equals(request.getParameter
            (MODE_NAME_PARAM));
        boolean viewMode = MODE_VIEW.equals(request.getParameter(MODE_NAME_PARAM));
        String param1 = request.getParameter(FORM_PARAMETER1);
        String param2 = request.getParameter(FORM_PARAMETER2);
        String param3 = request.getParameter(FORM_PARAMETER3);
        
        String title = request.getParameter(TITLE_KEY);
        String p1Prompt = request.getParameter(P1PROMPT_KEY);
        String p2Prompt = request.getParameter(P2PROMPT_KEY);
        String p3Prompt = request.getParameter(P3PROMPT_KEY);
        
        if (viewMode)
        {
          //
          // Set the new parameter values. These will be intepreted by the 
          // container as navigational parameters as the names match the names of
          // the declared parameters.
          //
          actionResponse.setRenderParameter(PARAMETER1, param1);
          actionResponse.setRenderParameter(PARAMETER2, param2);
          actionResponse.setRenderParameter(PARAMETER3, param3);
        }
        else if (editDefaults)
        {
          //
          // In edit defaults we just set the new title and parameter prompts in
          // the preference store.
          //
          PortletPreferences prefs = request.getPreferences();
          prefs.setValue(TITLE_KEY, title);
          prefs.setValue(P1PROMPT_KEY, p1Prompt);
          prefs.setValue(P2PROMPT_KEY, p2Prompt);
          prefs.setValue(P3PROMPT_KEY, p3Prompt);
          prefs.store();
    
          actionResponse.setPortletMode(PortletMode.VIEW);
          actionResponse.setWindowState(WindowState.NORMAL);
        }
        else
        {
          super.processAction(request, actionResponse);
        }
      }  
      
      /**
       * Helper method to serve up the EDIT mode.
       *
       * @param     request the portlet request
       * @param     response the portlet response
       *
       * @exception PortletException if the portlet cannot fulfill the request
       * @exception UnavailableException if the portlet is unavailable to perform edit
       * @exception PortletSecurityException if the portlet cannot fullfill this 
       *            request because of security reasons
       * @exception IOException if the streaming causes an I/O problem
       */
      public void doEdit (RenderRequest request,
                          RenderResponse response)
          throws PortletException, IOException
      {
          response.setContentType("text/html; charset=UTF-8");
          PrintWriter out = response.getWriter();
        
          String p1Prompt = request.getPreferences().getValue(P1PROMPT_KEY, 
                 "Parameter 1:");
          String p2Prompt = request.getPreferences().getValue(P2PROMPT_KEY, 
                 "Parameter 2:");
          String p3Prompt = request.getPreferences().getValue(P3PROMPT_KEY, 
                 "Parameter 3:");
          // create an action link back to this portlet
    
          out.print("<FORM ACTION=\"");
          out.print(getEditFormSubmitURL(request, response));
          out.println("\" METHOD=\"POST\">");
          out.println("<TABLE BORDER=\"0\">");
          out.println("<TR>");
          out.println("<TD WIDTH=\"20%\">");
          out.println("<P CLASS=\"portlet-form-field\" ALIGN=\"RIGHT\">Title:");
          out.println("</TD>");
          out.println("<TD WIDTH=\"80%\">");
          out.print("<INPUT CLASS=\"portlet-form-input-field\" 
                 TYPE=\"TEXT\" NAME=\"");
          out.print(TITLE_KEY);
          out.print("\" VALUE=\"");
          out.print(getTitle(request));
          out.println("\" SIZE=\"20\">");
          out.println("</TD>");
          out.println("</TR>");
          out.println("<TR>");
          out.println("<TD WIDTH=\"20%\">");
          out.println("<P CLASS=\"portlet-form-field\" 
                 ALIGN=\"RIGHT\">Parameter 1 Prompt:");
          out.println("</TD>");
          out.println("<TD WIDTH=\"80%\">");
          out.print("<INPUT CLASS=\"portlet-form-input-field\" 
                 TYPE=\"TEXT\" NAME=\"");
          out.print(P1PROMPT_KEY);
          out.print("\" VALUE=\"");
          out.print(p1Prompt);
          out.println("\" SIZE=\"20\">");
          out.println("</TD>");
          out.println("</TR>");
          out.println("<TR>");
          out.println("<TD WIDTH=\"20%\">");
          out.println("<P CLASS=\"portlet-form-field\" 
                 ALIGN=\"RIGHT\">Parameter 2 Prompt:");
          out.println("</TD>");
          out.println("<TD WIDTH=\"80%\">");
          out.print("<INPUT CLASS=\"portlet-form-input-field\" 
                 TYPE=\"TEXT\" NAME=\"");
          out.print(P2PROMPT_KEY);
          out.print("\" VALUE=\"");
          out.print(p2Prompt);
          out.println("\" SIZE=\"20\">");
          out.println("</TD>");
          out.println("</TR>");
          out.println("<TR>");
          out.println("<TD WIDTH=\"20%\">");
          out.println("<P CLASS=\"portlet-form-field\" 
                 ALIGN=\"RIGHT\">Parameter 3 Prompt:");
          out.println("</TD>");
          out.println("<TD WIDTH=\"80%\">");
          out.print("<INPUT CLASS=\"portlet-form-input-field\" 
                 TYPE=\"TEXT\" NAME=\"");
          out.print(P3PROMPT_KEY);
          out.print("\" VALUE=\"");
          out.print(p3Prompt);
          out.println("\" SIZE=\"20\">");
          out.println("</TD>");
          out.println("</TR>");
          out.println("<TR>");
          out.println("<TD COLSPAN=\"2\" ALIGN=\"CENTER\">");
          out.print("<INPUT CLASS=\"portlet-form-button\" TYPE=\"SUBMIT\" NAME=\"");
          out.print(OK_ACTION);
          out.print("\" VALUE=\"OK\">");
    
          out.print("<input type=\"hidden\" name=\"");
          out.print(MODE_NAME_PARAM);
          out.print("\" value=\"");
          out.print(MODE_EDITDEFAULTS);
          out.print("\">");
    
          out.print("<INPUT CLASS=\"portlet-form-button\" TYPE=\"SUBMIT\" NAME=\"");
          out.print(APPLY_ACTION);
          out.print("\" VALUE=\"Apply\">");
          out.println("</TD>");
          out.println("</TR>");
          out.println("</TABLE>");
          out.println("</FORM>");
      }
      
      protected String escapeForHTMLAttr(String original)
      {
        String escaped = original.replaceAll("&", "&amp;");
        escaped = escaped.replaceAll("<", "&lt;");
        escaped = escaped.replaceAll(">", "&gt;");
        escaped = escaped.replaceAll("\"", "&quot;");
        escaped = escaped.replaceAll("'", "&quot;");
        
        return escaped;
      }
    }
    
  16. 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");
    

    The sample portlet in Example 19-5 reads navigational parameters.

    Example 19-5 Portlet Reading Parameters

    /* Copyright (c) 2006, Oracle. All rights reserved.  */
    package oracle.portlet.server.wsrpsample;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.portlet.PortletException;
    import javax.portlet.PortletURL;
    import javax.portlet.RenderRequest;
    import javax.portlet.RenderResponse;
    
    public class ReadOnlyParameterFormPortlet extends ParameterFormPortlet
    {
      /**
       * Helper method to serve up the VIEW mode.
       *
       * @param     request the portlet request
       * @param     response the portlet response
       *
       * @exception PortletException if the portlet has trouble fulfilling the
       *            request
       * @exception IOException if the streaming causes an I/O problem
       */
      public void doView (RenderRequest request,
                          RenderResponse response) 
          throws PortletException, IOException
      {
        
          String p1Prompt = request.getPreferences().getValue(P1PROMPT_KEY, 
                 "Parameter 1:");
          String p2Prompt = request.getPreferences().getValue(P2PROMPT_KEY, 
                 "Parameter 2:");
          String p3Prompt = request.getPreferences().getValue(P3PROMPT_KEY, 
                 "Parameter 3:");
        
          response.setContentType("text/html; charset=UTF-8");
          PrintWriter out = response.getWriter();
    
          //Retrieve any values for the nav parameters from the request
          String param1 = request.getParameter(PARAMETER1);
          String param2 = request.getParameter(PARAMETER2);
          String param3 = request.getParameter(PARAMETER3);
          param1 = (param1 != null)?param1:"";
          param2 = (param2 != null)?param2:"";
          param3 = (param3 != null)?param3:"";
                
          out.print("<center><table>");
          
          if (!"".equals(p1Prompt))
          {
            out.print("<tr><td class=\"portlet-form-field\">");
            out.print(p1Prompt);
            out.print("</td><td>");
            out.print(escapeForHTMLAttr(param1));
            out.print("</td></tr>");
          }
          
          if (!"".equals(p2Prompt))
          {
            out.print("<tr><td class=\"portlet-form-field\">");
            out.print(p2Prompt);
            out.print("</td><td>");
            out.print(escapeForHTMLAttr(param2));
            out.print("</td></tr>");
          }
          
          if (!"".equals(p3Prompt))
          {
            out.print("<tr><td class=\"portlet-form-field\">");
            out.print(p3Prompt);
            out.print("</td><td>");
            out.print(escapeForHTMLAttr(param3));
            out.print("</td></tr>");
          }
          
          out.println("</table></center>");
      }
    }
    
  17. Register your producer so that these two portlets appear in the Component Palette. Follow the instructions provided in Section 4.3.1.1, "Registering WSRP Portlet Producers".

  18. On a new or existing page, drag and drop the two portlets that you just created from the Component Palette. See Section 4.3.2, "Adding Portlets to a Page" for more information.

  19. Right-click an element of the page in the Structure pane and choose Go to Page Definition. A page definition similar to Example 19-6 should appear. Notice the variables at the page level and the parameters at the portlet level.

    Example 19-6 Page Definition File Sample

    <?xml version="1.0" encoding="UTF-8" ?>
    <pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
                    version="10.1.3.38.82" id="untitled2PageDef"
                    Package="view.pageDefs">
      <parameters/>
      <executables>
        <variableIterator id="variables">
          <variable Name="Parameter_01"    Type="java.lang.Object"/>
          <variable Name="Parameter_02"    Type="java.lang.Object"/>
          <variable Name="Parameter_03"    Type="java.lang.Object"/>
        </variableIterator>
        <portlet id="portlet1"
            portletInstance="/oracle/adf/portlet/Samples_1153150955927/
              applicationPortlets/E10default_7d3d5f76_010c_1000_8004_8a031f56d48d"
            class="oracle.adf.model.portlet.binding.PortletBinding"
            xmlns="http://xmlns.oracle.com/portlet/bindings">
          <parameters>
            <parameter name="ora_wsrp_navigparam_Parameter1"
                       pageVariable="Parameter_01"/>
            <parameter name="ora_wsrp_navigparam_Parameter2"
                       pageVariable="Parameter_02"/>
            <parameter name="ora_wsrp_navigparam_Parameter3"
                       pageVariable="Parameter_03"/>
          </parameters>
        </portlet>
        <portlet id="portlet2"
            portletInstance="/oracle/adf/portlet/Samples_1153150955927/
              applicationPortlets/E11default_7d3d7ef3_010c_1000_8005_8a031f56d48d"
            class="oracle.adf.model.portlet.binding.PortletBinding"
            xmlns="http://xmlns.oracle.com/portlet/bindings">
          <parameters>
            <parameter name="ora_wsrp_navigparam_Parameter1"
                       pageVariable="Parameter_01"/>
            <parameter name="ora_wsrp_navigparam_Parameter2"
                       pageVariable="Parameter_02"/>
            <parameter name="ora_wsrp_navigparam_Parameter3"
                       pageVariable="Parameter_03"/>
          </parameters>
        </portlet>
      </executables>
    </pageDefinition>
    

19.1.3 Implementing 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. 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 will be 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, perform the following steps:

  1. Open the oracle-portlet.xml file for your producer. Add the allow-export and allow-import tags for each portlet and the producer as shown in Example 19-7.

    Example 19-7 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 xml:lang="en">First Parameter</label>
                <hint xml:lang="en">hint0</hint>
            </navigation-parameters>
            <navigation-parameters>
                <name>Parameter_02</name>
                <type>xsi:string</type>
                <label xml:lang="en">Second Parameter</label>
                <hint xml:lang="en">hint1</hint>
            </navigation-parameters>
            <navigation-parameters>
                <name>Parameter_03</name>
                <type>xsi:string</type>
                <label xml:lang="en">Third Parameter</label>
                <hint xml:lang="en">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 xml:lang="en">First Parameter</label>
                <hint xml:lang="en">hint0</hint>
            </navigation-parameters>
            <navigation-parameters>
                <name>Parameter_02</name>
                <type>xsi:string</type>
                <label xml:lang="en">Second Parameter</label>
                <hint xml:lang="en">hint1</hint>
            </navigation-parameters>
            <navigation-parameters>
                <name>Parameter_03</name>
                <type>xsi:string</type>
                <label xml:lang="en">Third Parameter</label>
                <hint xml:lang="en">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>
    

19.1.4 Implementing 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.

You indicate that you want URLs rewritten by setting the PortletRequest attribute, oracle.portlet.server.resourceRequiresRewriting, to true. For example, you might include a code excerpt similar to the one in Example 19-8 to use resource proxying for a URL that you are encoding. Note that typically you would want to encapsulate this code within a method to avoid repeating it for every URL individually.

Example 19-8 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.

19.1.5 Implementing Security for JPS Portlets

You can secure JPS portlets that are deployed to a WSRP producer by configuring security at the WSRP producer end and the client end. See Section 10.10, "Securing Identity Propagation Through WSRP Producers With WS-Security" for information about securing a JPS portlet through its WSRP producer.

19.2 Enhancing PDK-Java Portlets

Once you have built your initial portlet in the Portlet Wizard as described in Section 18.7, "Building PDK-Java Portlets with Oracle JDeveloper", you may perform the following tasks to enhance it:

This section assumes the following:

19.2.1 Adding Portlet Modes

In the Portlet Wizard, you add portlet modes by checking boxes on the wizard pages. See Section 18.7, "Building PDK-Java Portlets with Oracle JDeveloper" for more information about using the wizard. For each portlet mode that you select in the wizard, a basic HelloWorld skeleton is created. If you need to add a portlet mode after creating the portlet, then you can do that manually by updating provider.xml and HTML or JSPs in Oracle JDeveloper. The following sections explain how to add portlet modes to a PDK-Java portlet:

Once you have completed this section, you will be able to implement any portlet mode using RenderManager because the principles are the same for all modes. For example, even though this section does not describe how to implement the About mode in detail, you will understand how to do it, as the process is the same as for Help mode, which is described here.

More on OTN

For more detailed information about the PDK run time classes used in this section, see the JavaDoc on OTN by clicking Java Doc API on the Portlet Development page available at

http://www.oracle.com/technology/products/ias/portal/portlet_development_10g1014.html

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

http://www.oracle.com/technology/products/ias/portal/html/javadoc/xml_tag_reference_v2.html

19.2.1.1 Assumptions

The following assumptions are made to perform the tasks in this section:

  • You built a portlet using the wizard, successfully registered the producer, and added the portlet to the page.

19.2.1.2 Implementing Extra Portlet Modes

Your first task when creating portlet modes manually is to create an HTML file or JSP for each mode. For example, if you want to implement Help mode, then you need to create an HTML file to provide Help content.

To create an HTML file to preview content, perform the following steps:

  1. In Oracle JDeveloper, open the project that contains your portlets.

  2. Under Web Content, htdocs\myportlet, create an HTML page called HelpPage.html. The content of the file could be something similar to the following:

    <p>This is the <i>Help</i> mode of your portlet!</p>
    

Once you have created the HTML file for Help content, you are ready to update the XML producer definition.

19.2.1.3 Updating the XML Producer Definition

When you want to expose additional portlet modes you must update your XML producer definition as follows:

  • Set a Boolean flag that indicates to the PDK Framework that a link or icon to that mode should be rendered.

  • Point to the HTML file or JSP that you created for that mode.

More on OTN

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

http://www.oracle.com/technology/products/ias/portal/html/javadoc/xml_tag_reference_v2.html

For example, if you want to render Help mode, then perform the following steps:

  1. Edit the producer definition file, provider.xml and add the tag to activate Preview mode within the <portlet></portlet> tags:

    <hasHelp>true</hasHelp>
    
  2. Specify the Help page to be the HTML page that you created in Section 19.2.1.2, "Implementing Extra Portlet Modes":

    <helpPage>/htdocs/myportlet/MyPortletHelpPage.html</helpPage>
    
  3. Save the updates to provider.xml.

  4. Redeploy your portlet. See step 8 in Section 18.9, "Deploying Your Portlet to an Application Server".

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

19.2.1.4 Viewing the Portlet

To view the new portlet modes, you must ensure that your updated XML producer definition is reparsed. To do this, perform the following steps:

  1. Copy the HTML file you created in Section 19.2.1.2, "Implementing Extra Portlet Modes" and provider.xml to the OC4J instance where you plan to deploy the portlet.

  2. Refresh the producer.

  3. Refresh the page containing your portlet.

  4. Click the Help link.

19.2.2 Passing Parameters and Submitting Events

PDK-Java and Oracle WebCenter Framework provides public and private portlet parameters, and private events to enable portlet developers to easily write reusable, complex portlets. The Portlet Wizard in Oracle JDeveloper creates portlets that are already set up to use parameters and events. 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 and events, see the following:

This section covers the following:

19.2.2.1 Assumptions

The following assumptions are made to perform the tasks in this section:

  1. You have followed through and understood Section 18.7, "Building PDK-Java Portlets with Oracle JDeveloper".

  2. You built a portlet using the wizard and successfully added it to a page.

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 an excessive number of parameters and events for each portlet, or give them lengthy names and descriptions.

19.2.2.2 Adding Public Parameters

Using the 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 add parameters to your portlet, perform the following tasks:

  1. Right-click your project and choose New from the context menu.

  2. From New Galleries, choose the Portlets category and then the Oracle PDK Java Portlet item.

  3. Proceed through the wizard as you normally would until you reach the Public Portlet Parameters page. See Section 18.7, "Building PDK-Java Portlets with Oracle JDeveloper" for basic information about going through the wizard.

  4. When you reach the Public Portlet Parameters page, as shown in Figure 19-6, click the Add button. A new row is added to the table.

    Figure 19-6 Public Portlet Parameters Page of Portlet Wizard

    Description of Figure 19-6 follows
    Description of "Figure 19-6 Public Portlet Parameters Page of Portlet Wizard"

  5. Click the default name that appears in the Name column. Use the Backspace key to delete the default name.

  6. Enter a new parameter name, for example, Parameter_01.

  7. Repeat steps 6 and 7 for the Display Name and Description columns.

  8. Repeat steps 4 through 7 for each public parameter that you want to add to the portlet.

  9. Click Finish.

  10. Once you finish the Portlet Wizard, your portlet is created.

  11. In the Applications Navigator, expand the Web Content and WEB-INF\providers\providername nodes. You should see your provider.xml file there.

  12. Right-click provider.xml and choose Open from the context menu. You should see entries for the parameters that you added on the Public Portlet Parameters page in the wizard, as shown in Example 19-9.

    Example 19-9 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>
    
    More on OTN

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

    http://www.oracle.com/technology/products/ias/portal/html/javadoc/xml_tag_reference_v2.html
    
  13. The wizard also generates code in the portletnameShowPage.jsp for your portlet. Expand the htdocs\portletname node in the Applications Navigator.

  14. Right-click portletnameShowPage.jsp and choose Open from the context menu. The code should look something like Example 19-10. Notice the code that retrieves the parameters.

    Example 19-10 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>
    
  15. Add logic to your portlet that enables it to submit parameter values entered by users.

  16. Repeat steps 1 through 15 and add logic to this second portlet that enables it to simply display parameter values that it retrieves.

  17. Register your producer so that these two portlets appear in the Component Palette. Follow the instructions provided in Section 4.3.1.2, "Registering PDK-Java Portlet Producers".

  18. On a new or existing page, drag and drop the two portlets that you just created from the Component Palette. See Section 4.3.2, "Adding Portlets to a Page" for more information.

  19. Right-click an element of the page in the Structure pane and choose Go to Page Definition. A page definition similar to Example 19-11 should appear. Notice the variables at the page level and the parameters at the portlet level.

    Example 19-11 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>
    
  20. You should now be able to run your application, enter values in the first portlet you created, and see them displayed in the second portlet

19.2.2.3 Passing Private Portlet 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 multipage portlet, then you can use these private parameters to jump to another resource of the portlet.

This section covers the following:

19.2.2.3.1 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 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. An example URL with the properly qualified thesaurus parameter might look something like the following:

http://host/portal/page?_pageid=33,1&_dad=portal&_schema=PORTAL
   &_piref33_38279_33_1_1.q=Hedgehog

Notice the fully qualified parameter name, _piref33_38279_33_1_1.q. It identifies the parameter and distinguishes it from other parameters on the page. Further, notice that the URL contains some parameters unrelated to any portlet. These parameters are untouched by the portlet because it does not own them.

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 respect to 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 19.2.2.3.3, "Building Links with the Portlet URL Types" and Section 19.2.2.3.4, "Building Forms with the Portlet URL Types".

19.2.2.3.2 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 it's 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 would 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).

19.2.2.3.3 Building Links with the Portlet URL Types

To build links with Portlet URL types, you need to 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 need to 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 one of the supplied portlet URL types, UrlUtils.PAGE_LINK.

The parameter names in the params argument should be fully qualified. Moreover, assuming that 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. See the example in Section 19.2.2.3.4, "Building Forms with the Portlet URL Types" to see the initial submission form that sets the value of q.

<%
    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>
19.2.2.3.4 Building Forms with the Portlet URL Types

Use of portlet parameters in forms is little different from 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.

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

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 one 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 need to 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. See the example in Section 19.2.2.3.3, "Building Links with the Portlet URL Types" for the portlet that results from the submission of this form.

<%
    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 wish 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>
19.2.2.3.5 Implementing Navigation within a Portlet

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

  • 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 19.2.2.3.3, "Building Links with the Portlet URL Types" and Section 19.2.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 it's 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. See Section 19.2.4.2, "Implementing Session Storage" for more information about implementing session storage.

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 the context of a Portal 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>

Notice that 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 will render the resource identified by next_page rather than first.jsp. Note that 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 19.4, "Building Struts Portlets".

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 wish 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>
19.2.2.3.6 Restricting Navigation to Resources

One of the dangers of implementing navigation with private parameters is that users could potentially navigate to portlet resources that you prefer to restrict. To control the resources to which the user may navigate, 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 will be 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 permissible 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 19-12 would only enable resources under /htdocs/multiportlet to be shown. All other resources would be restricted.

Example 19-12 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 will be matched. For an <allowedPath> value of /htdocs/sub1/*, valid values of the private parameter would 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 will end with that file extension. For an <allowedPathvalue> of *.jsp, valid values of the private parameter would include /htdocs/sub1/file.jsp and /htdocs/sub1/file2.jsp.

19.2.2.4 Creating Private Events

In some cases, it is useful for a portlet to complete a transaction before rendering a page on which it resides rather than having the transaction and the rendering executed simultaneously. For example, suppose a portlet has a link that initiates an update of some data value that might effect other portlets on the page. If the transaction occurred simultaneously with a refresh of the page, then other portlets that rely on that data value may or may not be refreshed with the latest value. Furthermore, when transactions and rendering are tied together in this way, an action such as the user hitting Back in their browser could cause the transaction to be repeated, perhaps creating a duplicate record.

In JPS portlets, this situation is solved using the processAction method, which enables an individual portlet to complete a transaction, such as updating a value, before enabling the page rendering to occur. PDK-Java does not have processAction, but you can achieve the same results by submitting data to your servlet through a different mechanism. If you are using Struts for the page flow and control of a portlet, then you could use the form tag and transaction tokens to avoid submitting the same parameter twice. See Section 19.4, "Building Struts Portlets" for more information about Struts portlets.

Another possibility is to submit data through Edit mode rather than Shared Screen mode. Requests based on full page portlet modes, such as Edit mode, are sent only to the portlet that generated the link. Other portlets on the same portal page never even see a render request. Hence, these full page portlet modes provide you with the capability to execute a transaction for a portlet separately from the page and its other portlets.

Once you have processed the portlet's submission, you redirect from the full page portlet mode back to the page using the back URL. This action has two desirable effects, as follows:

  • It returns the user to the page from which they came.

  • It clears all traces of the form submission from the browser.

As a result, any refreshing of the page is guaranteed to occur after the processing of the data submission. Because the page refresh comes after the submission you can be sure that all portlets on the page will access the updated data and not cause a duplicate submission.

This technique is illustrated by a sample portlet in PDK-Java called the private event submission portlet. It demonstrates submitting a simple form to the portlet and logging the contents of the form. In the private event submission portlet, you overload Edit mode to handle both the data submission and the portlet's rendering for personalizations. Note that any of the other full page portlet modes (Help, About, and Edit Defaults) would be equally effective for this purpose.

Edit mode for this portlet includes additional code that first looks for a specific parameter. If this parameter is present, then it means that the request represents a private event. The same mode can handle many different private events by using different values for the distinguishing parameter. If the distinguishing parameter does not exist, then Edit mode falls through to the standard portlet personalization logic.

After handling the private event, this mode redirects to the page using exactly the same logic that Edit mode uses when a user clicks OK. See EditServlet.java in the sample files for the complete source code illustrating this technique.

Note:

When you use this technique, you must take care that the details of your event are persisted somewhere. As portlet rendering does not happen in the same request cycle as the event processing, any data from the event required to render the portlet must be available from storage. Otherwise, the portlet or the page may lack the data it requires the next time it is rendered.

If you need to persist your data, then be sure to store it in a qualified manner (by the user and portlet reference). Otherwise, you may accidentally associate an event from one user/portlet with the rendering of the same portlet for another user on a different page, or even associate the same user/same portlet with a different portlet reference on the same page.

19.2.3 Using 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. PDK-Java provides utilities to enable the retrieval of both producer and non-producer JNDI variables within a J2EE container. To use JNDI variables, you need to perform the following tasks:

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

19.2.3.1.1 Variable Types

In the env-entry-type element, you should supply the fully qualified Java type of the variable, which will be 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 J2EE 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.

19.2.3.1.2 Variable Naming Conventions

The PDK-Java defines a number of 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 should 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. See Section 19.2.3.3, "Retrieving JNDI Variables" for more information about retrieving JNDI variables.

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

19.2.3.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 in one of two ways as follows:

  • If you are using Oracle Enterprise Manager 10g, then you can set the variables from there.

  • If you are using a standalone instance of OC4J, then you need to manually change the variable values in the PDK-Java's orion-web.xml file.

19.2.3.2.1 Setting Values in Oracle Enterprise Manager 10g

To set variable values in Oracle Enterprise Manager 10g, do the following:

  1. In Oracle Enterprise Manager 10g, click the Application Server instance where you have deployed PDK-Java.

  2. Click the Name representing this deployment.

  3. Click the Applications tab.

  4. From the View pulldown list, choose Modules.

  5. Click the Name of the module.

  6. Click the Administration tab.

  7. Click the Go to Task icon for Environment Entries Mapping.

  8. On the Environment Entries Mapping page, all of the environment variables are listed. The default value, if it exists, is listed under the Value heading. To update a value for a particular variable, enter the new value in the text box in that variable's row.

  9. When you have updated all of the variable values that you want, click OK.

  10. Restart the OC4J instance for the new settings to take effect.

19.2.3.2.2 Setting Values Manually

To set variable values manually, do the following:

  1. Open the orion-web.xml file in a text editor. If the file does not exist, then you must create it. For a full Oracle Application Server installation, you can locate this file in:

    ORACLE_HOME\j2ee\OC4J_instance\application-deployments\deployment_name
    

    For a standalone instance of OC4J, you can locate it in:

    ORACLE_HOME\j2ee\home\application-deployments\jpdk\jpdk
    
  2. For each deployment property you want to set, add the following entry:

    <env-entry-mapping name="jndi_var_name">value</env-entry-mapping>
    
  3. Save and close the file. When complete, your file should look something like the following:

    <orion-web-app
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation=
          "http://xmlns.oracle.com/oracleas/schema/orion-web-10_0.xsd"
      deployment-version="10.1.3.0.0"
      deployment-time="1134715871939"
      default-charset="iso-8859-1"
      jsp-cache-directory="./persistence"
      jsp-cache-tlds="standard"
      autojoin-session="true"
      temporary-directory="./temp"
      servlet-webdir="/servlet/"
      context-root="newapp1p"
      schema-major-version="10" schema-minor-version="0" >
        <!-- Uncomment this element to control web application class loader
             behavior.
        <web-app-class-loader search-local-classes-first="true"  
             include-war-manifest-class-path="true" />
    <web-app>
        -->
    <env-entry-mapping name="oracle/portal/sample/rootDirectory">
               D:\prefs</env-entry-mapping>
    <env-entry-mapping name="oracle/portal/sample/definition">
               D:\definitions\def.xml</env-entry-mapping>
    </web-app>
    </orion-web-app>
    
  4. Restart the OC4J instance for the new settings to take effect.

19.2.3.3 Retrieving JNDI Variables

JNDI is a standard J2EE technology. As such, you can access JNDI variables through J2EE 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 J2EE APIs, PDK-Java includes a simple utility class for retrieving the values of variables defined and used by the PDK itself. These variables all conform to the naming convention described in Section 19.2.3.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 would come 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 19-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 19-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 Application Server Portal Configuration Guide 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.


19.2.4 Accessing 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 enables 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).

Note:

If you need to replicate session state across middle tiers, then you must mark the Web application as distributable and create a cluster island for your OC4J servers. For more information, see Oracle Containers for J2EE Servlet Developer's Guide.

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 needs to track 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.

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

19.2.4.1 Assumptions

The following assumptions are made to perform the tasks in this section:

  1. You have followed through and understood Section 18.7, "Building PDK-Java Portlets with Oracle JDeveloper".

  2. You built a portlet using the wizard and successfully added it to a page.

19.2.4.2 Implementing Session Storage

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 need to 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 by clicking Java Doc API on the Portlet Development page available at

http://www.oracle.com/technology/products/ias/portal/portlet_development_10g1014.html

To implement session storage, you need to perform the following tasks:

  • 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 need to 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 back in.

    <%
    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 need to 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 producer JavaDoc on OTN:

    http://www.oracle.com/technology/products/ias/portal/html/javadoc/xml_tag_reference_v2.html
    
  4. Register your producer with session support. For a reminder on how to register your portlet, see Section 18.10, "Registering and Viewing Your Portlet".

19.2.4.3 Viewing the Portlet

If you have not already 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.

19.2.5 Implementing Portlet Security

This section describes the available security services for your Java portlet.

More on OTN

For more detailed information about the PDK classes referred to in this section, see the JavaDoc on OTN by clicking Java Doc API on the Portlet Development page available at

http://www.oracle.com/technology/products/ias/portal/portlet_development_10g1014.html

19.2.5.1 Assumptions

The following assumptions are made to perform the tasks in this section:

  1. You have followed through and understood Section 18.7, "Building PDK-Java Portlets with Oracle JDeveloper".

  2. You built a portlet using the wizard and successfully added it to a page.

19.2.5.2 Introduction to Portlet Security Features

This section introduces the major features that are available to secure your portlet producers.

19.2.5.2.1 Authentication

When a user first logs in, they must enter their password to verify their identity and obtain access. See Chapter 10, "Securing Your WebCenter Application" for more information.

Once the user is authenticated, the producer code has access to the authenticated user's identity from the PortletRenderRequest that is available from the HttpServletRequest object as follows:

PortletRenderRequest pr = (PortletRenderRequest)request.getAttribute
   (HttpCommonConstants.PORTLET_RENDER_REQUEST);
   String userName = pr.getUser().getName();

When using this user identity for sensitive operations, it is important to ensure that you have configured this producer to use basic message authentication to secure the integrity of the identity assertion.

You also have the option of configuring the OC4J containing the JPDK producer to use JAZN-LDAP and then use J2EE security, in which case you can obtain the user's identity with the following method, again through the HttpServletRequest object:

String userName = request.getRemoteUser();

And if you need the user Principal, you can also use:

Principal userPrincipal = request.getUserPrincipal();

When configuring the producer's OC4J container in this manner, make sure to configure this producer to use enhanced message authentication to secure the integrity of this form of identity assertion.

19.2.5.2.2 Authorization

Authorization determines if a particular user may view or interact with a portlet. For more information about authorization checking, see Chapter 10, "Securing Your WebCenter Application".

19.2.5.2.3 Communication Security

To this point, user authentication and authorization are covered, which do not check the authenticity of messages received by a producer. To completely secure your producers, secure the communication with a producer. If the communication is not secured, then it is possible for someone to imitate an application instance and fool the producer into returning sensitive information. There are three types of communication security:

  • Server Authentication restricts access to a producer to a small number of recognized computers. This method compares the IP address or the host name of an incoming HTTP message with a list of trusted hosts. If the IP address or host name is in the list, then the message is passed to the producer. If not, it is rejected before reaching the producer. See Section 19.2.5.5, "Server Security" for more information.

  • Message Authentication appends a checksum based on a shared key to producer messages. When a message is received by the producer, the authenticity of the message is confirmed by calculating the expected value of the checksum and comparing it with the actual value received. If the values are the same, then the message is accepted. If they are different, then the message is rejected without further processing. The checksum includes a time stamp to reduce the chance of a message being illegally recorded in transit and resent later. See Section 19.2.5.6, "Message Authentication" for more information.

  • Message Encryption relies on the use of the HTTPS protocol for communication between applications and producers. Messages are strongly encrypted to protect the data therein. Encryption provides a high level of security, but it incurs a performance penalty due to the additional processing required for each message.

  • User Input Escape causes the application to escape any user input strings and treat them as text only to protect against XSS attacks, where an attacker attempts to pass in malicious scripts through user input forms. For example, if a portlet title is customizable, then an attacker might attempt to pass scripts or commands to the portlet through the title parameter entry. For this reason, the default behavior is to escape user input and thus disable any incoming scripts. See Section 19.2.5.7, "User Input Escape" for more information.

19.2.5.3 Single Sign-On

Portlets act as windows into an application. They display summary information and provide a way to access the full functionality of the application. Portlets display some portions of the application in the WebCenter application and typically enable the user to perform some application tasks.

Note:

Deep links from the portlet into the application are not supported in Oracle WebCenter Framework. Users can only access what is presented within the application portlet itself.

An application may need to authenticate the user accessing the application through the portlet. Following are the possible application authentication methods:

  • External Application. In this case, the Oracle Application Server Portal (OracleAS Portal) user is different from the application user, but the application user name and password are managed by the OracleAS Portal user.

  • No Application Authentication. In this case, the communication between producer and OracleAS Portal is not protected at all.

19.2.5.3.1 External Application

An external application uses a different authentication server than the WebCenter application. This means that when a user is already logged into the WebCenter application, you want to also log them into the external application without having to type in their user name or password.

Note:

Deep links from the portlet into the application are not supported in Oracle WebCenter Framework. Users can only access what is presented within the application portlet itself.

Applications that manage the authentication of users can be loosely integrated with Oracle Single Sign-On if the administrator registers them as external applications. When a user who was previously authenticated by Oracle Single Sign-On accesses an external application for the first time, Oracle Single Sign-On attempts to authenticate the user with the external application. The authentication process submits an HTTP request that combines the registration information and the user's user name and password for the application. If the user has not yet registered their user name and password for the external application, then Oracle Single Sign-On prompts the user for the required information before making the authentication request. When a user supplies a user name and password for an external application, Oracle Single Sign-On maps the new user name and password to the WebCenter application user name and stores them. They will be used the next time the user needs authentication with the external application.

The advantages of an external application implementation are as follows:

  • Enables integration with many portals. If, however, one of the portals is preferred over the others, then the application could be integrated as a partner application of that preferred portal and an external application of the others.

  • Provides a single sign-on experience for users. However, users still must maintain different user names and passwords. In addition, the external application user name mapping must be maintained.

  • Enables integration with multiple portals independent of their user repositories and Oracle Single Sign-On.

  • Avoids the requirement of having access to the application source code.

The disadvantages of an external application implementation are as follows:

  • Does not share the same user repository as the portal, which requires additional maintenance of user information by the end user.

  • Transmits the user name and password to the producer in plain text, unless you implement Secure Sockets Layer (SSL).

19.2.5.3.2 No Application Authentication

The producer trusts the WebCenter application instance sending the request completely. The producer can determine if the user is logged in and the portal user name, but the application has not authenticated the user.

The advantages of no application authentication are as follows:

  • Provides the easiest form of integration and the fastest to implement.

The disadvantages of no application authentication are as follows:

  • Provides the least security.

  • Provides the weakest integration with the WebCenter application.

19.2.5.4 Portlet Security Managers

Portlet security managers are implemented within a producer to verify that a given user may view an instance of the portlet. When a user views a page with a portlet instance on it, security managers determine whether the user has the appropriate privileges to see the portlet. Implementing access control methods in the producer restricts the retrieval of content from a portlet (that is, hides the portlet) from users without the appropriate privileges. Only if the specified characteristics, such as user details and preferences, pass the authorization logic will the content be retrieved for the user. If no portlet security methods are implemented in the producer, then any user name may be passed in, even fictitious, unauthenticated ones.

A producer can implement two portlet security methods as follows:

  • Get a list of portlets.

  • Verify the accessibility of the portlet.

Portlets have access to the WebCenter application user privileges and groups of which the user is a member. The following information can be used by the security methods:

  • The default group of the user

  • The privileges of a user or group

  • The highest available privilege of a user across all groups

  • The objects the user can access (only in database producers)

AuthLevelSecurityManager has access to the following information about authorization level:

  • Strongly authenticated.

    The user has been authenticated by Oracle Single Sign-On in the current WebCenter application session, that is, the user logged in with a valid user name and password, and requested the portlet in the context of that session.

  • Weakly authenticated.

    A user who was previously strongly authenticated returns to view a page without an active session. A persistent cookie (maintained by the user's browser) indicates that in some previous session the user logged on with a valid user name and password.

  • Public or not authenticated.

    The user has not logged in within the context of the current session, and does not have a persistent cookie to indicate that such a state previously existed.

To incorporate these security services into your Java portlet, you simply need to update provider.xml and set the security level to strong, weak, or public. Place the following XML right before the </portlet> tag in provider.xml:

<securityManager class="oracle.portal.provider.v2.security.AuthLevelSecurityManager">
   <securityLevel>strong</securityLevel>
</securityManager>

After you make this change to provider.xml, refresh the producer.

More on OTN

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

http://www.oracle.com/technology/products/ias/portal/html/javadoc/xml_tag_reference_v2.html

The advantage of security methods is as follows:

  • You can enable a portlet to produce different output depending on the level of authorization.

The disadvantage of security methods is as follows:

  • Most security manager implementations will use the authorization level or some other user specific element in an incoming message. A check of this type could be bypassed by an entity imitating a WebCenter application instance.

19.2.5.4.1 Viewing the Portlet

After you add a security manager to a portlet, you can validate it by following these steps:

  1. Ensure you are logged in to a WebCenter application instance with privileges to create pages and add portlets to a page.

  2. Create a new page, ensuring it is visible to PUBLIC.

  3. Add your Java portlet to the page.

  4. Make a note of the direct URL to your new page.

  5. Now log out of the WebCenter application instance by clicking the Logout link.

  6. Directly access the page by entering the URL noted in Step 4 into your browser's address bar.

You will see the page created in Step 2 but not the portlet added in Step 3. When you added the portlet to the page, you were logged in and hence strongly authenticated. The PDK run time detected this and enabled you to add the portlet. When you logged out and viewed the page, you were no longer strongly authenticated and hence the PDK Framework did not enable rendering of the portlet's contents.

If you log in again and view the page, then you will see that the portlet is still there.

19.2.5.4.2 Implementing Your Own Security Manager

If your portlet requires special security arrangements which are not provided by the implementations shipped with the PDK, then you will need to supply your own custom PortletSecurityManager controller class. To do this, extend the oracle.portal.provider.v2.security.PortletSecurityManager class and supply implementations for the two methods specified by the interface. Then replace the class attribute of the securityManager controller element in the XML producer definition with you new class name and configure child elements appropriately.

19.2.5.5 Server Security

One way to prevent unauthorized access to producers is to restrict access to the producer to known client computers at the server level. Because only the identified clients may access the producer, this method defends against denial of service attacks.

The way in which HTTP servers handle this kind of security tends to vary. You need to investigate your particular HTTP server to understand how to implement this level of security.

The advantages of server security are as follows:

  • It limits access to the producer to trusted hosts only.

  • It simplifies configuration.

The disadvantages of server security are as follows:

  • Oracle Web Cache does not have IP address checking capability. If Oracle Web Cache sits in front of a producer, then you have no protection from a client on any host sending show requests to Oracle Web Cache.

  • Restricting access to certain IP addresses and host names may be circumvented by sending messages to a producer containing fake IP addresses and host names. This trick is difficult to perform effectively as return messages go to the computer whose IP address was copied, but it can still cause problems.

19.2.5.6 Message Authentication

PDK-Java supports message authentication so that access may be limited to a specified producer instance or group of producer instances. A producer is registered with a secret shared key known only to the WebCenter application and producer administrators.

The WebCenter application sends a digital signature, calculated using a Hashed Message Authentication Code (HMAC) algorithm, with each message to a producer. A producer may authenticate the message by checking the signature using its own copy of the shared key. This technique may be used in Secure Sockets Layer (SSL) communication with a producer instead of client certificates.

The WebCenter application calculates a signature based on user information, a shared key and a time stamp. The signature and time stamp are then sent as part of the SOAP message. The time stamp is based on UTC (coordinated universal time, the scientific name for Greenwich Mean Time) so that time stamps can be used in messages between computers in different time zones.

When the producer receives this message it generates its own copy of the signature. If the signatures agree, it will then compare the message time stamp with the current time. If the difference between the two is within an acceptable value, then the message is considered authentic and is processed accordingly.

A single producer instance cannot support more than one shared key because it could cause security and administration problems. For instance, if one copy of the shared key is compromised in some way, then the producer administrator has to create a new key and distribute it to all of the application clients, who then must update their producer definitions. The way around this problem is to deploy different producer services, specifying a unique shared key for each service. Each producer service has its own deployment properties file so that each service is configured independently of the others. The overhead of deploying multiple producer services within the same producer adapter is relatively small.

In a producer without Oracle Web Cache in front of it, this use of the same signature cookie over the lifetime of a producer session implies a trade-off between performance and the security provided by authenticating the requests. The signature cookie value is only calculated once after the initial SOAP request establishes the session with the producer. The shorter the producer session timeout, the more often a signature will be calculated providing greater security against a show request being resent illegally. However, the SOAP request required to establish a session incurs a time penalty.

In a producer using Oracle Web Cache to cache show request responses, you have a similar trade-off. Cached content is secured in the sense that incoming requests must include the signature cookie to retrieve it, but caching content for an extended period of time leaves the producer open to illegal show requests.

While the signature element provides protection against interception and resending of messages, it does nothing to prevent interception and reading of message contents. Messages are still transmitted in plain text. If you are concerned about the content of messages being read by unauthorized people, then you should use message authentication in conjunction with SSL.

The advantage of message authentication is as follows:

  • Ensures that the message received by a producer comes from a legitimate WebCenter application instance.

The disadvantages of message authentication are as follows:

  • Causes administration problems if a producer serves more than one portal. Entails performance implications if made very secure by having a short session timeout.

19.2.5.7 User Input Escape

By accepting user input without escaping it to text, you run the risk of an XSS attack, where an attacker attempts to pass in malicious scripts through user input forms. For example, if a portlet title is customizable, then an attacker might attempt to pass scripts or commands to the portlet through the title string. PDK-Java provides the following features to ensure that you can protect your portlets from such attacks:

19.2.5.7.1 Default Container Encoding

To prevent any script inside a portlet title from being executed, the framework default container renderer class encodes any script characters. This default behavior is controlled through a JNDI variable, escapeStrings. When set to true, the markup tags in portlet titles are rendered as visible tag characters. For example, a title customization of <i>title</i> will be rendered as <i>title</i> not title. This mode is secure, but, if it is not the desired behavior, then you can set escapeStrings to false for that producer.

escapeStrings applies to all logical producers within a producer. You can set the value of escapeStrings from the Application Server Control Console as you would any other JNDI variable. See Section 19.2.3.2, "Setting JNDI Variable Values" for more information.

19.2.5.7.2 Escape Methods

If you have code that renders customized values, then you need to ensure that you escape those input values appropriately to avoid XSS attacks. This requirement applies to code for rendering pages in any mode. PDK-Java supplies two new static methods for this purpose. They are in the Java class oracle.portal.provider.v2.url.UrlUtils, and can be described as follows:

  • public static escapeString(string_text) escapes any script characters in a given string. For example, less than < becomes &lt. This method is unaffected by the escapeStrings JNDI variable and is the secure, recommended method to use.

  • public static escapeStringByFlag(string_text) escapes any script characters in a given string. This method is controlled by the escapeStrings JNDI variable and is therefore less secure and not the recommended method to use.

For example:

title = UrlUtils.escapeString(data.getPortletTitle());

19.2.6 Enhancing Portlet Performance with Caching

In the previous sections of this chapter, you learned how to write fully functional Java portlets using the PDK Framework. Once you complete 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 requestor 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 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:

  1. 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. See Section 19.2.6.2, "Activating Caching" and Section 19.2.6.3, "Adding Expiry-Based Caching" for more information.

  2. Invalidation-based Caching: Invalidation-based caching works essentially the same way as expiry-based caching, except that the contents of the cache can expire or become invalid at any time. Invalidation of cache content is usually triggered by an event.

    For example, if you have a calendar portlet that shows your appointments for the day, then the content for the portlet could be generated once, the first time you show the calendar for that day. The content remains cached until something happens to change your schedule for that day, such as the addition of an appointment, the deletion of an existing appointment, or a change of time for an appointment. Each of these change events can trigger an action in the calendar application. When such an event occurred, your calendar application can generate an invalidation request for any cached portlet content affected by the change. The next time you view a page containing your calendar portlet, the cache will not contain an entry. Your producer will be contacted to regenerate the new content with the modified schedule.

    This method is a very efficient way to cache content because the originator of the content, that is, your producer, is contacted only when new content needs to be generated, but you are not bound to a fixed regeneration schedule. See Section 19.2.6.2, "Activating Caching" and Section 19.2.6.4, "Adding Invalidation Based Caching" for more information.

  3. 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 of time. See Section 19.2.6.2, "Activating Caching" and Section 19.2.6.5, "Adding Validation-Based Caching" for more information.

19.2.6.1 Assumptions

The following assumptions are made to perform the tasks in these sections:

  1. You have followed through and understood Section 18.7, "Building PDK-Java Portlets with Oracle JDeveloper".

  2. You built a portlet using the wizard and successfully added it to a page.

19.2.6.2 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 OracleAS Portal administrator is responsible for the configuration details of caching.

For invalidation-based caching, you need to configure Oracle Web Cache in front of the producer. Bear in mind that remote producers often do not have Oracle Web Cache installed. For more information about Oracle Web Cache, see the Oracle Application Server Web Cache Administrator's Guide.

Once you have installed and configured Oracle Web Cache, ensure the following in the Oracle Web Cache Manager:

  • It points to the host name and port of the producer.

  • Caching rules do not cause the caching of producer content. Content should be cached according to the surrogate control headers generated by the producer in its response.

19.2.6.3 Adding Expiry-Based Caching

Expiry-based caching is one of the simplest caching schemes 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, perform the following steps:

  1. After you have used the Portlet Wizard to build a portlet as described in Section 18.7, "Building PDK-Java Portlets with Oracle JDeveloper", edit the provider.xml file and set the pageExpires property tag of showPage to 1. This will set an expires entry of 1 minute for the portlet.

    By default the wizard generates a standard and compressed tag for showPage. You need to 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 producer JavaDoc on OTN:

    http://www.oracle.com/technology/products/ias/portal/html/javadoc/xml_tag_reference_v2.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 1 minute. After the time has expired, the portlet displays the most current time and a new cache is set.

19.2.6.4 Adding Invalidation Based Caching

When using Oracle Web Cache, requests for content are sent using HTTP and content is either returned from the cache or the HTTP request is forwarded to the originator of the content. When content is returned to Oracle Web Cache, it is added to the cache before being returned to the requestor. Cache content is invalidated by sending invalidation requests directly to Oracle Web Cache. PDK-Java uses the Java API for Web Cache (JAWC) to generate invalidation requests. This section describes how to configure Oracle Web Cache and use the invalidation-based caching sample that comes with PDK-Java.

Not all requests sent to Oracle Web Cache are cached. In order for the content to be cached, the content must include directives that tell Oracle Web Cache to cache the content. Usually Oracle Web Cache uses the URL associated with the request as the cache key, but you can specify additional keys by setting special HTTP headers, known as surrogate control headers, on the response.

To configure a Java portlet to use invalidation-based caching, you do the following:

19.2.6.4.1 Configuring the Producer Servlet

To enable invalidation-based caching, you must switch it on at the producer servlet level. The flag is set in an initialization parameter inside the PDK-Java Web application deployment descriptor, web.xml. For example:

<servlet>
...
    <servlet-class>oracle.webdb.provider.v2.adapter.SOAPServlet</servlet-class>
    <init-param>
      <param-name>invalidation_caching</param-name>
      <param-value>true</param-value>
    </init-param>
</servlet>

If the flag is not defined here, then invalidation-based caching is switched off. The status of this flag can be checked by displaying the producer's test page. If the testPageURI property is not set in the sample.properties file, then the producer code displays the test page in the old default style. The old style test page correctly picks up the invalidation caching flag from the web.xml file. The format of the test page URL is as follows:

http://provider_hostname:port/jpdk/providers/sample
19.2.6.4.2 Defining the Oracle Web Cache Invalidation Port

If you are using an Oracle Application Server installation type where PDK-Java was automatically preinstalled, then you should find that Oracle Web Cache invalidation settings have already been preconfigured in MID_TIER_ORACLE_HOME/portal/conf/cache.xml. In this case, you can safely ignore the instructions in this section and proceed to Section 19.2.6.4.3, "Configuring the XML Producer Definition". Otherwise, you will need to configure the invalidation portlet for Oracle Web Cache.

First, you need the user name and password for the invalidation ports of the Oracle Web Cache instances associated with your application server. After you obtain those, use the provided oracle.webdb.provider.v2.cache.Obfuscate Java utility to convert the user name and password into an obfuscated string of the format required by the JAWC API. In a default Oracle Application Server installation, the user name for the invalidation port is usually invalidator and the password is usually the same as the application server administrator's password. For example, suppose you installed Oracle Application Server in D:\as904, with an invalidation port user name of invalidator and a password of welcome. You would run the following command:

Note:

The command that follows assumes that pdkjava.jar and jawc.jar are present in ORACLE_HOME/j2ee/home/shared-lib/oracle.jpdk/1.0, as required by the PDK-Java installation guide.

If you are using a standalone OC4J installation, then you need to substitute in the path to the java executable an external Java 2 SDK installation.

D:\as904\jdk\bin\java -classpath D:\as904\j2ee\home\shared-lib\oracle.jpdk\1.0\pdkjava.jar
  oracle.webdb.provider.v2.cache.Obfuscate invalidator:welcome

If successful, the command should print a hexadecimal string like the following:

0510198d5df8efd5779406342be2528aa0cccb179ea6b77baf49f019f5075a3a11

Now, use this information to create a JAWC configuration file, cache.xml, which specifies one or more Oracle Web Cache instances and their invalidation ports. For example:

<?xml version="1.0"?> 
<webcache> 
<invalidation
 host="cache.mycompany.com"
 port="4001"
authorization="0510198d5df8efd5779406342be2528aa0cccb179ea6b77baf49f019f5075a3a11"/>
</webcache>

JAWC finds this configuration file using the system property oracle.http.configfile. To set this system property for an OC4J instance within an Oracle Application Server installation, simply add an appropriate line to the oc4j.properties file for the particular instance in which PDK-Java is installed (for example, MID_TIER_ORACLE_HOME/j2ee/OC4Jinstance/config/oc4j.properties) and then restart that instance. For example:

oracle.http.configfile=fully_qualified_filename

If you are running OC4J standalone, then you can specify the option in the command line you use to start it.

java -Doracle.http.configfile=<fully_qualified_filename> -jar oc4j.jar

Note:

As the location of the cache configuration file is defined as a system property, only one cache may be defined for each server instance. It is not possible to have two different producer instances behind separate Oracle Web Cache configurations.
19.2.6.4.3 Configuring the XML Producer Definition

Using a combination of tags in provider.xml, you can activate automatic invalidation-based caching for an entire portlet or some of its pages. To turn on automatic invalidation-based caching, you need to declare it as follows either at the level of individual pages or the renderer, to indicate that invalidation-based caching should be activated for all pages:

<useInvalidationCaching>true</useInvalidationCaching>

If your portlet supports personalization (through the Edit or Edit Defaults modes), then you may also want to declare <autoInvalidate>true</autoInvalidate> for your renderer. This declaration causes invalidation of all the cached content for the portlet when you click OK or Apply in Edit mode, thus causing new markup to be generated based on the personalized settings.

The maximum time for holding the page in the cache is the minimum of the following:

  • Maximum expiry time defined in the caching system.

  • producer default (24 hours) if no maximum expiry time is specified.

  • The time in minutes of the <pageExpires> tag, if present.

The following excerpt from provider.xml specifies that this portlet shall be cached for up to 5 minutes and shall be automatically invalidated upon personalization:

<renderer class="oracle.portal.provider.v2.render.RenderManager">
 <contentType>text/html</contentType>
 <renderContainer>true</renderContainer>
 <autoRedirect>true</autoRedirect>
 <autoInvalidate>true</autoInvalidate>
 <showPage class="oracle.portal.provider.v2.render.http.ResourceRenderer">
  <resourcePath>/htdocs/invalidation/invalidation1.jsp</resourcePath>
  <useInvalidationCaching>true</useInvalidationCaching>
  <pageExpires>5</pageExpires>
 </showPage>
 <editPage class="oracle.portal.sample.v2.devguide.invalidation.
   InvalidationEditRenderer"/></renderer>

Note:

The pageExpires tag is also used for normal expiry based caching. These two forms of caching are mutually exclusive. Invalidation-based caching occurred in an Oracle Web Cache instance located in the same place as the producer. Pages stored using expiry based caching are cached in the middle tier.
More on OTN

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

http://www.oracle.com/technology/products/ias/portal/html/javadoc/xml_tag_reference_v2.html
19.2.6.4.4 Manually Invalidating the Cache

You may want the cached version of the portlet invalidated when a request is processed or information somewhere has been updated. In these cases, you may want to manually invalidate the cache.

The invalidation-based caching portlet sample included with PDK-Java contains a single portlet that displays the time the content was cached and a link to trigger an invalidation request. The first time a page request is made to the producer through the cache, the response is cached. Subsequent requests for the portlet content are fulfilled by returning content from Oracle Web Cache. When you click the link at the bottom of the portlet an invalidation request is generated by the producer that removes the portlet from the cache. The next request for the portlet is forwarded to the producer and the producer generates a new portlet with the current time.

To perform invalidation calls to Oracle Web Cache, first you need to have a handle to a ServletInvalidationContext object. You can get this handle by calling the static getServletInvalidationContext() method of the ServletInvalidationContextFactory class.

Once you have the handle, you need to specify the cache key. In the cache key, you need to specify whether you want to invalidate a particular portlet instance, all the instances of a portlet, or all the portlets managed by a producer. To perform this task, you use the methods of the ServletInvalidationContext class or the methods of its super class, InvalidationContext. This is where you can specify whether the portlet should be cached for this particular user (USER level). If there is no user specified in the cache key, then the cached content is returned to all users, which means you are using SYSTEM level caching.

The following example invalidates a portlet instance and calls the setFullProviderPath() and setPortletReference() methods. When the caching key is set, you call the invalidate() method on the InvalidationContext object that sends the invalidation message to Oracle Web Cache.

ServletInvalidationContext inv = 
  ServletInvalidationContextFactory.getServletInvalidationContext();
inv.setFullProviderPath
  ((ServletPortletRenderRequest)pReq);
inv.setPortletReference
  (pReq.getPortletReference());
int num = inv.invalidate();

Review the Web cache sample producer for more information.

19.2.6.5 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 need to 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(), you need to 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 has to be set by calling the HttpPortletRendererUtil.useCachedVersion() method. It also instructs the RenderManager that it won't be 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 will be 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.

19.3 Testing Portlet Personalization

If you have implemented personalization for your portlet, then the Personalize link will only appear 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. See Section 10.6, "Configuring Basic Authentication for Testing Portlet Personalization" for more information.

19.4 Building Struts Portlets

This section describes the framework for building Struts portlets with Oracle JDeveloper for use in a WebCenter application. You will learn how to build a Struts portlet from an existing application by adding the Struts Tag Library from the Oracle Application Server Portal Developer Kit (version 9.0.4.0.2 or later) to Oracle JDeveloper, then use the Oracle PDK Java Portlet wizard to create the Java portlet itself. This sections covers the following tasks:

19.4.1 The Apache Struts Framework

This section discusses the use of the Apache Struts. Struts is an implementation of the Model-View-Controller (MVC) design pattern. The following topics are discussed in this section:

19.4.1.1 Model View Controller Overview

Enterprise applications undertake several distinct tasks, as follows:

  • Data access

  • Business logic implementation

  • User interface display

  • User interaction

  • Application (page) Flow

The Model View Controller (MVC) architecture provides a way of compartmentalizing these tasks, based on the premise that activities, such as data presentation, should be separate from data access. This architecture enables you to easily plug a data source into the application without having to rewrite the user interface. MVC enables the logical separation of an application into three distinct layers, the Model, the View, and the Controller.

The Model

The Model is the repository for the application data and business logic. One facet of the Model's purpose is to retrieve data from and persist data to the database. It is also responsible for exposing the data in such a way that the View can access it, and for implementing a business logic layer to validate and consume the data entered through the View. At the application level, the Model acts as the validation and abstraction layer between the user interface and the business data that is displayed. The database server itself is simply a persistence layer for the Model.

The View

The View is responsible for rendering the Model data using JSPs. The View code does not include a hardcoded application or navigation logic, but may contain some logic to carry out tasks like displaying conditional data based on a user role. When an end user executes an action within the HTML page that the View renders, an event is submitted to the Controller. The Controller then determines the next step.

The Controller

The Controller is the linchpin of the MVC pattern. Every user action carried out in the View is submitted through the Controller. The Controller then performs the next action, based on the content of the request from the browser.

The Controller can be driven in several different ways. For example, you can use URL arguments to route the requests to the correct code. The MVC pattern itself determines the function of the Controller, not how it should work.

Benefits

The MVC architecture provides a clear and modular view of the application and its design. By separating the different components and roles of the application logic, it enables developers to design applications as a series of simple and different components: the Model, the View, and the Controller. This pattern should help to create applications that are easier to maintain and evolve. For example, once you create one view, you can easily create another view using the same business logic. Because of the ease and reusability, the MVC pattern is the most widely used pattern in the context of Web-based application development.

Figure 19-7 shows how the MVC pattern applies to a conventional thin-client Web application:

19.4.1.2 Apache Struts Overview

The Apache Struts framework (http://struts.apache.org) is one of the most popular frameworks for building Web applications, and provides an architecture based on the JSP Model 2 approach of the MVC design paradigm. In the Model 2 approach, end user requests are managed by a servlet that controls the flow, and uses components such as JavaBeans or EJBs to access and manipulate the data. It then uses JSPs to render the application content in a Web browser. This model differs from JSP Model 1, where the JSPs managed the browser request and data access.

The Struts framework provides its own HTTP Servlet as a controller component. The Struts framework is driven by an XML configuration file that contains the page flow of the application. Struts does not provide the Model, but enables developers to integrate it to any data access mechanism, for example EJBs, TopLink, or JDBC. The most common technology for writing View components is JSP and Struts provides various tag libraries to help in writing these, although some of these tags have now been superseded by the Java Standard Tag Library (JSTL), which may also be used.


Note:

For more information about JSTL and JSF, see the FAQ on the Apache Software Foundation Web site (http://struts.apache.org/kickstart.html).

19.4.1.3 OracleAS PDK Integration with Struts

The OracleAS PDK contains numerous examples and documents regarding the usage of the OracleAS Portal APIs, such as personalization and caching. The integration of the application flow and business logic is not part of the portlet APIs. By using the Struts framework, however, you can leverage the MVC architecture to create and publish applications within your enterprise portal.

Oracle Struts Portlet

To create a portlet using the Struts framework, or to generate a portlet from an existing Struts application, you must deploy all the components in the J2EE container. In the context of OracleAS Portal, the Struts application is called by the PPE, and not by the browser as compared to a standalone Struts application. When a portlet show call is made, the page engine sends a request to the Struts portlet renderer, which then forwards the request to the Apache Struts Controller servlet, as shown in Figure 19-8.

Figure 19-8 Integrating Struts Applications with OracleAS Portal

Shows integration of struts with OracleAS Portal.
Description of "Figure 19-8 Integrating Struts Applications with OracleAS Portal"

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 producer JavaDoc on OTN:

http://www.oracle.com/technology/products/ias/portal/html/javadoc/xml_tag_reference_v2.html

The showPage tag defines the business logic that will be executed in the portlet mode of the portlet. The showPage of the Struts portlet contains two important components, which are as follows:

  1. The renderer class (oracle.portal.provider.v2.render.http.StrutsRenderer), which receives the portlet request from the PPE and acts as a proxy to forward the request to the Struts Action Servlet.

  2. The defaultAction tag, which defines the Struts action that will be used by default when the portlet is called for the first time.

The PDK-Java enables you to easily develop a view (Portal 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 OracleAS Portal JSP tags, which are extensions of the default Struts JSP tags. This development process is similar to that of creating a standalone Struts application. To learn how to build a Struts portlet, see Section 19.4.2.1, "Creating a Struts Portlet". Also, as the portlet and struts application must also be in the same Servlet Context, you must create a single Web application that contains both elements. To learn how to easily create this Web application in Oracle JDeveloper, see the next section, Section 19.4.2.1, "Creating a Struts Portlet".

19.4.1.4 Summary

Apache Struts has become the default standard for developing MVC-based J2EE applications, because it offers a clean and simple implementation of the MVC design paradigm. This framework enables you, as the portlet developer, to separate the different components of an application, and to leverage the Struts controller to easily publish an existing Struts application to OracleAS Portal without completely changing the existing business logic.

Note:

For more information about the Oracle Application Server Portal Developer Kit, see Portal Center (http://www.oracle.com/technology/products/ias/portal/pdk.html)

19.4.2 Creating a Struts Portlet

OracleAS PDK contains new 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. To learn more about the Apache Struts framework, see Section 19.4.1, "The Apache Struts Framework". The PDK-Java extensions described in this section rely on Apache Struts 1.1.

This section contains the following steps:

19.4.2.1 Creating a Struts Portlet

To publish a part of an existing Struts application as portlet, Oracle recommends that you first create a new view to serve as the Portal 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 Portal 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 will create a portlet that enables you to add a new entry to a Web Logger (Blog). Figure 19-9 and Figure 19-10 show how you submit a blog and save a blog entry.

Figure 19-9 Submitting a Blog

Shows updating a BLOG.
Description of "Figure 19-9 Submitting a Blog"

Figure 19-10 Saving a Blog Entry

Shows saving a BLOG.
Description of "Figure 19-10 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>
19.4.2.1.1 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 will redirect 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.

19.4.2.1.2 Creating 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 be sure that the portlet meets the following criteria:

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

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

  • Renders portal-aware links and forms. This is necessary to ensure that your Struts portlet renders its content inline, thus keeping your users within the context of the portal page by rendering the requested content within the same portlet container.

To achieve inline rendering in your Struts portlet, you must use OracleAS PDK tags:

<pdk-struts-html:form action="/portal/saveNewBlog.do">
...
...
</pdk-struts-html:form>

During the rendering of the portlet, one of the JSP tags (for example, the pdk-struts-html:form tag), submits the form to the Parallel Page Engine (PPE), which then sends the parameters to the Struts portlet. The Struts controller executes the logic behind these actions and returns the next JSP to the portlet within the portal page.

The PDK contains all the Struts tags, and extends all the tags that are related to URLs. The following is a list of the PDK extended tags:

  • form: creates an HTML form and embeds the portal page context in the form to ensure inline rendering

  • text: renders fields on the form.

  • link and rewrite: create a link to the portal page, and are required for inline rendering

  • img: creates an absolute link that points to the producer. If you want to use this tag in the context of Internet Web sites that have firewalls, then you must make sure the producer is directly accessible from the Internet. If it is not possible, then you can deploy the images to the OracleAS Portal middle tier and use the Apache Struts image link to generate a relative link (relative to the portal, not to the application).

    Note:

    You can register the OracleAS 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.
19.4.2.1.3 Creating a Portlet

You can create your Struts portlet either manually or by using the 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, perform the following steps:

  1. In Oracle JDeveloper, open the Java Portlet Wizard to create an Oracle PDK Java Portlet.

    Note:

    The Java Portlet and Oracle PDK Java Portlet options are used to create JPS-compliant portlets and PDK-Java portlets respectively. Clicking Java Portlet or Oracle PDK Java Portlet opens the Java Portlet Wizard. For more information about opening the wizard, see Section 18.7, "Building PDK-Java Portlets with Oracle JDeveloper".
  2. For the Implementation Style of the show page, select 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 Oracle JDeveloper.

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

    At the producer level, perform the following:

    • If you want users to always return to the same portlet state as when they left the portal 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 portal page, then you should not save the struts action:

      <actionInSession>false</actionInSession>
      

      Note that this setting is the default behavior.

    • 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:

    • 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 producer JavaDoc on OTN:

    http://www.oracle.com/technology/products/ias/portal/html/javadoc/xml_tag_reference_v2.html
    
19.4.2.1.4 Extending 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.

19.4.2.2 Registering 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 18.10, "Registering and Viewing Your Portlet".

19.4.2.3 Summary

Oracle Application Server enables you to easily create Struts portlets using Oracle JDeveloper and publish existing Struts application portlets. For more information about using the Oracle JDeveloper Java Portlet wizards, see Chapter 18, "Creating Java Portlets".

19.4.3 Creating an Oracle Application Development Framework Portlet

Similarly to Struts, Oracle ADF relies on the MVC design pattern. Oracle ADF applications leveraging the Struts controller can be turned into portlets and deployed the same way as Struts applications. See Section 19.4.2, "Creating a Struts Portlet".

Note:

After creating the Oracle ADF portlet, you may find that the JSP page does not display correctly. This is because the Parallel Page Engine request is sent to a producer through a SOAP request (oracle.webdb.provider.v2.adapter.SOAPServlet), which implies that the portal does not serve the page as a standard .JSP page. To resolve this, create the ADFBindingFilter filter.

To create the ADFBindingFilter filter and filter mappings, include the following in your web.xml file:

<filter>
   <filter-name>ADFBindingFilter</filter-name>
   <filter-class>oracle.adf.model.servlet.ADFBindingFilter</filter-class>
   <init-param>
     <param-name>encoding</param-name>
     <param-value>windows-1252</param-value>
   </init-param>
 </filter>
 <filter-mapping>
   <filter-name>ADFBindingFilter</filter-name>
   <url-pattern>*.jsp</url-pattern>
 </filter-mapping>
 <filter-mapping>
   <filter-name>ADFBindingFilter</filter-name>
   <url-pattern>*.jspx</url-pattern>
 </filter-mapping>
 <filter-mapping>
   <filter-name>ADFBindingFilter</filter-name>
   <url-pattern>/*</url-pattern>
 </filter-mapping>
 <filter-mapping>
   <filter-name>ADFBindingFilter</filter-name>
   <url-pattern>*</url-pattern>
 </filter-mapping>
 <filter-mapping>
   <filter-name>ADFBindingFilter</filter-name>
   <servlet-name>action</servlet-name>
 </filter-mapping>
 <filter-mapping>
   <filter-name>ADFBindingFilter</filter-name>
   <servlet-name>jsp</servlet-name>
 </filter-mapping>

19.5 Building Portlets from Oracle ADF Faces Applications (JSF Portlet Bridge)

In some cases, you may want to take an Oracle ADF Faces application that you have built and expose it as a portlet. Such a portlet, sometimes called a JSF portlet bridge, executes Oracle ADF Faces pages as JPS (JSR 168) portlets.

Note:

Unless otherwise noted, the material in this section applies equally to the JSF reference implementation.

This section provides the following information about exposing Oracle ADF Faces applications as JPS portlets:

19.5.1 Creating a JSF Portlet

To create a JSF portlet from an existing application, perform the following steps:

  1. Open your JSF application workspace. The first thing you must do to portletize your application is add the necessary JSF portlet bridge library to your project.

  2. Right-click the project containing the application of which you plan to make a portlet. Choose Project Properties from the context menu.

  3. From the left pane, click Libraries.

  4. Click Add Library.

  5. Select Portlet Faces Bridge.

  6. Click OK.

  7. Click OK. Now you need to create a Portlet Deployment Descriptor that enables you to deploy your application as a portlet producer.

  8. Right-click your project and choose New from the context menu.

  9. Choose All Technologies from the Filter By list.

  10. Expand the General category from the pane on the left.

  11. Click Deployment Descriptors.

  12. Choose portlet.xml (Portlet Deployment Descriptor) from the Items list on the right.

  13. Click OK.

  14. You should now see portlet.xml under Web Content, WEB-INF in the Applications Navigator. Open portlet.xml and go to the Source tab.

  15. Add or modify the necessary init-params in portlet.xml for your particular application. For example, the portlet.xml for an Oracle ADF application that uses ADF binding might look something like the one in Example 19-13. For non-ADF binding, it might look something like the one in Example 19-14.

    In particular, note the init-params in bold. The parameters are as follows:

    • The DefaultPage.view parameter specifies the default page of the application to use as the View mode for the portlet. Its location is relative to the web-app-context-root and always start with a /.

    • The BridgeLifecycleListeners parameter specifies the filter classes to use. You must ensure that your portlet.xml file is configured to match the filters specified in your web.xml file. If you do not have the proper classes defined in portlet.xml, then you will receive a missing class error. You specify the classes in the BridgeLifecycleListeners init-param in your portlet.xml file:

      <init-param>
        <name>BridgeLifecycleListeners</name>
        <value>listener_class_n[,listener_class_n+1,...]</value>
      </init-param>
      

      For example, suppose that your application includes AdfFacesFilter filter in its web.xml:

      <filter>
        <filter-name>adfFaces</filter-name>
        <filter-class>oracle.adf.view.faces.webapp.AdfFacesFilter</filter-class> 
      </filter> 
      

      You would then need to include the following class in your portlet.xml:

      <init-param> 
        <name>BridgeLifecycleListeners</name>
        <value>oracle.portlet.server.bridges.jsf.adf.ADFFacesBridgeLifecycleListener
          </value>
      </init-param>
      

      Similarly, suppose that ADFBindingFilter filter is defined in web.xml as follows:

      <filter>
        <filter-name>adfBindings</filter-name>
        <filter-class>oracle.adf.model.servlet.ADFBindingFilter</filter-class>
      </filter>
      

      You would then need to include the following in your portlet.xml:

      <init-param> 
        <name>BridgeLifecycleListeners</name>
      <value>oracle.portlet.server.bridges.jsf.adf.ADFFacesBridgeLifecycleListener,
      oracle.portlet.server.bridges.jsf.adf.BindingFacesBridgeLifecycleListener
      </value>
      </init-param>
      

      Example 19-13 Sample portlet.xml for JSF Portlet (ADF Binding)

      <?xml version="1.0" encoding="ISO-8859-1"?> 
       <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
         version="1.0"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         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"> 
         <portlet> 
           <description>ADF Faces Demo Portlet</description> 
           <portlet-name>ADFFacesDemo</portlet-name> 
           <display-name>ADF Faces Demo portlet</display-name> 
           <portlet-class>oracle.portlet.server.bridges.jsf.FacesPortlet 
           </portlet-class> 
           <init-param> 
            <name>DefaultPage.view</name>
            <value>/index.jspx</value>
           </init-param> 
           <init-param> 
            <name>BridgeLifecycleListeners</name>
            <value>
             oracle.portlet.server.bridges.jsf.adf.ADFFacesBridgeLifecycleListener,
             oracle.portlet.server.bridges.jsf.adf.BindingFacesBridgeLifecycleListener
            </value>
           </init-param> 
          <supports> 
           <mime-type>text/html</mime-type> 
           <portlet-mode>VIEW</portlet-mode> 
           </supports> 
           <supported-locale>en</supported-locale> 
           <portlet-info> 
            <title>ADF Faces Demo Portlet</title> 
            <short-title>ADFFacesDemo</short-title> 
           </portlet-info> 
         </portlet> 
       </portlet-app>
      

      Example 19-14 Sample portlet.xml for JSF Portlet (Non-ADF Binding)

      <?xml version="1.0" encoding="ISO-8859-1"?> 
       <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" 
         version="1.0"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         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"> 
         <portlet> 
           <description>ADF Faces Demo Portlet</description> 
           <portlet-name>ADFFacesDemo</portlet-name> 
           <display-name>ADF Faces Demo portlet</display-name> 
           <portlet-class>oracle.portlet.server.bridges.jsf.FacesPortlet 
            </portlet-class> 
          <init-param> 
           <name>DefaultPage.view</name> 
           <value>/index.jspx</value> 
          </init-param> 
          <init-param> 
           <name>BridgeLifecycleListeners</name> 
            <value>
             oracle.portlet.server.bridges.jsf.adf.ADFFacesBridgeLifecycleListener 
            </value> 
          </init-param> 
          <supports> 
           <mime-type>text/html</mime-type> 
           <portlet-mode>VIEW</portlet-mode> 
          </supports> 
          <supported-locale>en</supported-locale> 
          <portlet-info> 
           <title>ADF Faces Demo Portlet</title> 
           <short-title>ADFFacesDemo</short-title> 
          </portlet-info> 
         </portlet> 
       </portlet-app>
      
    • Modify the web.xml for your particular application to use client-side state saving method by defining the following in your web.xml:

      <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>server</param-value>
      </context-param>
      
  16. You can now deploy your application as you would any Web application. See Section 18.9, "Deploying Your Portlet to an Application Server".

  17. Once you have successfully deployed your application, you can register it as a portlet producer with any other application. See Section 4.3.1.1, "Registering WSRP Portlet Producers". Note that your application now runs in dual mode. You can continue to access it as regular Web application or consume it as a portlet producer.

  18. Now that your producer is deployed and registered, you can consume your JSF application's portlet as you would any other portlet. See Section 4.3.2, "Adding Portlets to a Page".

19.5.1.1 Passing Parameters

You can enable communication between a JSF Portlet Bridge and the underlying JSF application by configuring the portlet bridge to pass on WSRP 2.0 navigational parameters as request parameters to the underlying application. For this, you must add such parameters as navigational parameters in oracle-portlet.xml as shown in the following example:

<navigation-parameters>
    <name>Parameter_01</name>
    <type>xsi:string</type>
    <label xml:lang="en">Parameter 1</label>
    <hint xml:lang="en">First parameter.</hint>
</navigation-parameters>
 

Example 19-15, Example 19-16, and Example 19-17 show the sample code used to enable parameter passing between a JSF Portlet Bridge and the consuming application. Items of interest are shown in bold.

Example 19-15 Sample Code of JSF Page (JSFParameterForm.zip:JSFParamDisplay.jspx)

<f:view>
    <afh:html>
      <afh:head title="ParamForm">
        <meta http-equiv="Content-Type"
              content="text/html; charset=windows-1252"/>
      </afh:head>
      <afh:body>       <h:form>
        <af:outputLabel value="Parameter 1:"/><af:outputText value="#{param.ora
                                             _wsrp_navigparam_Parameter1}"/>
       </h:form>
      </afh:body>
    </afh:html>
  </f:view>

Example 19-16 Sample Code of oracle-portlet.xml (JSFParameterForm.zip:oracle-portlet.xml)

<?xml version="1.0" encoding="ISO-8859-1"?>
  <portlet-app-extension
       xmlns="http://xmlns.oracle.com/portlet/oracle-portlet-app" version="1.0"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
  <portlet-extension>
    <portlet-name>JSFParameterDisplay</portlet-name>
      <navigation-parameters>
        <name>ora_wsrp_navigparam_Parameter1</name>
        <type>xsi:string</type>
        <label xml:lang="en">First parameter</label>
        <hint xml:lang="en">First parameter set by portlet</hint>
      </navigation-parameters>
      <portlet-id>1</portlet-id>
  </portlet-extension>
</portlet-app-extension>

Example 19-17 Sample Code of Page Definition File of The Consumer Page (InterPortletComm.zip:PortletCommPageDef.xml)

<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
                version="10.1.3.40.66" id="PortletCommPageDef"
                                    Package="view.pageDefs">
 <parameters/>
   <executables>
     <variableIterator id="variables">
        <variable Name="ParameterFormPortlet1_1_ora_wsrp_navigparam_Parameter1"
                                                      Type="java.lang.Object"/>
     </variableIterator>
     <portlet id="JSFParameterDisplayPortlet1_1" 
        portletInstance="/oracle/adf/portlet/jsfParam_1178341548071/ap/E0default
                                      _5aa185c9_0112_1000_8005_8d90409dd9cb"
        class="oracle.adf.model.portlet.binding.PortletBinding"
        retainPortletHeader="false"
        xmlns="http://xmlns.oracle.com/portlet/bindings">
   <parameters>
        <parameter name="ora_wsrp_navigparam_Parameter1"
                 pageVariable="ParameterFormPortlet1_1_ora_wsrp_navigparam
                                                           _Parameter1"/>
   </parameters>
  </portlet>
 </executables>
</pageDefinition>
 

For more information about passing parameters, see Section 4.5.1, "Linking Portlets to Pages".

19.5.2 Guidelines for Oracle ADF Faces Applications

To publish an Oracle ADF Faces page as a portlet, you must first code your Oracle ADF Faces pages such that they emit markup that conforms with JSR 168 portlet markup fragment rules. Fortunately, most of the markup in an Oracle ADF Faces page comes from the Oracle ADF Faces components, most of which naturally render markup in a style that is compatible with 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 enable 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.

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.

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

  • When building a JSF portlet bridge in Oracle JDeveloper, you should build your application with one of the Web Application templates that employs JSF.

  • You cannot portletize applications that already contain portlets.

  • Using the client to manage and save the Faces view state is not supported. Therefore, you must configure your application to have this state managed by the server. Because the Oracle ADF Faces default is client-managed, you must explicitly configure the application to use server-side state management by defining the following in your web.xml:

    <context-param>
      <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
      <param-value>server</param-value>
    </context-param>
    
  • Deep links aren't 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 enable 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 will be established. Applications wanting to share such state need to 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 a number of 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 enable 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 should not contain login links that trigger Java EE authentication.

  • If the consumer of the JSF portlet is OracleAS Portal, then the following may occur:

    • A bug (5526946) causes the portlet not to render on the first request after you bounce OC4J or if the portlet has been idle for a while. The message says:

      Error: The portlet could not be contacted.
      

      You can resolve this message by refreshing the page.

    • Navigation may fail when you have multiple JSF portlet bridges on a single page. This failure usually occurs because the identifiers of HTML elements and the names of JavaScript functions are conflicting. To work around this problem, make sure your applications use unique identifiers and JavaScript variables.

19.5.2.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 will not 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 is meaningless 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 reguestDispatcher.include(). The use of httpServletResponse.sendRedirect() or requestDispatcher.forward() results in exceptions and errors. To work properly in a portlet environment, you must implement navigation with navigation rules in faces-config.xml.

  • Minimize the memory size and use of request scoped data. The JSF portlet bridge internally promotes request scoped data to session scope because the portlet action/render phases are permitted to span two requests. Furthermore, unlike a JSF Web application, a portlet may be rendered many times after an action has been submitted. In each of these cases, the duration of the request data must be longer then a single http request. To minimize overall memory consumption in the application, you should only store the minimal amount of data in the request scope to be able to render successfully.

19.5.2.3 Oracle ADF Faces Guidelines

The Oracle ADF faces guidelines are as follows:

  • Oracle ADF Faces client side implementation is not supported. One weakness of JSR 168 and WSRP 1.0 is that they do not account for portlets using client-side programming techniques, such as Ajax or hidden frames, for partial page rendering. As a result, all Oracle ADF Faces facilities that rely on client-side programming techniques do not work. These facilities include the following:

    • Oracle ADF Faces partial page refresh (PPR) facility

    • The client-rendered aspect of Oracle ADF Faces rich client components

    • Oracle ADF popups, <af:popup>, which fail to work because of their reliance on the PPR facility

    • The Oracle ADF Faces SelectText, SelectDate, and SelectColor components, which use visual assist popups

      To avoid breaking existing applications, the popup is disabled when used in Oracle ADF Faces pages exposed as a portlet. Thus, functionally speaking, these components are equivalent to InputText.

  • File upload is not supported. To support file upload, Oracle ADF Faces requires facilities that do not exist in JSR 168. Hence, a file upload request does not work.

  • The objectMedia component is not supported in portlets.

19.5.2.4 Oracle ADF Guidelines

The Oracle ADF guidelines are as follows:

  • Portlet customization using Oracle Metadata Services is not supported. The JSR 168 preference model is incompatible with an environment that manages its own customizations in its own repository, such as Oracle ADF using Oracle Metadata Services.

  • When a WebCenter application consumes a JSF portlet bridge, the portlet can only use JSR 168 portlet styles (instead of the standard Oracle ADF styles). This may result in lost of fidelity.

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

    The reason for this difference is that the JSF portlet bridge executes 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 JSF portlet bridge executes 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 enables 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. Any references to request parameters in this phase handler will therefore 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 will fail. Instead, move any dependency on a page parameter value into the model before JSF transitions from its execute phases to its render phase.