Oracle9iAS Portal Developer Kit
Adding Customization to Java Portlets

PDK Release 2 (9.0.2 and later) - Java

In the article Adding Extra Render Modes to Java Portlets you learned how to use the PDK Provider Framework to render portlet content in various render modes, i.e. Show, Help, About and Show Details. In addition to these, there are two customization modes -- Edit and Edit Defaults. Rendering these modes require special treatment from your renderer and involves the use of another 'controller' object called PortletPersonalizationManager.

The Edit and Edit Defaults modes allow portlet users to change a set of customizable parameters supported by the portlet, which typically drive the way the portlet is rendered in other modes. For a particular instance of a portlet on an Oracle9iAS Portal page, the customizations made in Edit and Edit Defaults mode apply only to that instance of the portlet.

- Edit mode - Customizations are specific to the individual user making the customizations. This mode is activated by clicking the Customize link on the portlet header in Show mode.

- Edit Defaults mode - Customizations apply to all users in the same locale who have not yet made specific customizations to that portlet instance. This mode is generally only available to administrators, and can be activated by following the Customize Page link in the page heading, and then selecting Edit Defaults on the appropriate portlet in the Portlets tab.

When rendering Edit and Edit Defaults modes, a PortletRenderer can carry out either of these tasks to support the customization process:

- Renders the 'edit form': For each of the portlet's customizable parameters, PortletRenderer uses a PortletPersonalizationManager to retrieve the current value and renders a control in an HTML form so the current value can be edited.

- Handles edit form actions: When an OK or Apply button is clicked on the standard edit form header, PortletRenderer uses a PortletPersonalizationManager to store the customized parameters submitted by the edit form and redirects the browser to the appropriate Portal page.

Therefore, the purpose of the PortletPersonalizationManager controller is to enable a PortletRenderer to store and retrieve the current values of customizable parameters that apply to a particular portlet instance and user. The PDK Framework uses the abstraction of a PersonalizationObject as a 'container' for a set of customized parameters and a PortletReference as the 'key' under which a set of customizations are stored. Thus, a PortletPersonalizationManager is simply a mechanism that allows the storage and retrieval of persisted PersonalizationObjects under a given PortletReference.

The PDK currently provides the PrefStorePersonalizationManager which uses a PreferenceStore implementation to persist customized data. There are currently two PreferenceStore implementations DBPreferenceStore and FilePreferenceStore. The DBPreferenceStore persists data using a JDBC compatible relational database and FilePreferenceStore persists data using the filesystem. For more information on using the PreferenceStore, please see the article How to use the Preference Store API.

For more details of these implementations, consult the JavaDoc.

This article teaches you how to add customization functionality to your portlet using PrefStorePersonalizationManager in conjunction with NameValuePersonalizationObject, i.e. the default PersonalizationObject implementation. You will also learn how to render and handle a simple edit form for both the Edit and Edit Defaults modes, by creating a subclass of BaseManagedRenderer. The customization functionality described here is added to the portlet you created in the article How to Build a Java Portlet.

ASSUMPTIONS

1. You have followed through and understood these articles:

   - How to Build a Java Portlet

   - Adding Extra Render Modes to Java Portlets

2. All the assumptions in the above articles apply here too.

IMPLEMENTING CUSTOMIZATION

In this section we use the PrefStorePersonalizationManager in conjunction with the FilePreferenceStore implementation to persist customized data. FilePreferenceStore uses the file system to store data files, one per user customization, below a root directory using a 'reference path' derived from the information in a PortletReference:

- Edit mode - the reference path for a PortletReference corresponding to a specific user's customization (in Edit mode), generally has the form:
  "provider_id/portlet_id/instance_name/user_name.dat"

- Edit Defaults mode - the reference path for a PortletReference corresponding to a 'default' customization (in Edit Defaults mode) generally has the form:
  "provider_id/portlet_id/instance_name/_default.dat"

You could alternatively use the DBPreferenceStore.

If FilePersonalizationManager's optional 'path hashing' feature is activated, the reference path actually includes an extra subdirectory below the instance_name directory whose name is generated by applying a 'hashing function' to the data file name. This results in a more random spread of data files across subdirectories which can improve performance on a network file system.

To persist customizable parameters of your portlet, you will need a PersonalizationObject implementation which is capable of holding all the customizable parameters for your portlets, as well as being able to read them from an InputStream and write them to an OutputStream. The PDK Framework includes a generic NameValuePersonalizationObject implementation that is capable of storing arbitrary parameters as name-value pairs, as well as managing their persistence to and from InputStreams and OutputStreams. If you require a newly created portlet instance to take on a particular set of values as the 'defaults' for its customizable parameters, you simply need to use a subclass of NameValuePersonalizationObject which sets these default values in an override of the init() method. This is the approach we take in our example.

1. Using your favorite text editor, create a Java class called MyPersonalizationObject, which extends NameValuePersonalizationObject and overrides the init method to provide default values for the portlet title and greeting.

   You may wish to provide defaults for other parameters which are to be customizable.

   >     import oracle.portal.provider.v2.*;
   >     Import oracle.portal.provider.v2.personalize.*;

   >     public class MyPersonalizationObject extends NameValuePersonalizationObject
   >     {
   >         // Key to retrieve the customized greeting.
   >         Public static final String GREETING = "mpo_greeting";

   >         // Override the init method to provide default values for the items we wish
   >         // to customize.
   >         Public void init(PortletReference pr)
   >         {
   >             // Always be sure to call super.init() first.
   >             super.init(pr);

   >             // Call super class method to set the default title for this portlet.
   >             setPortletTitle("My First Portlet");

   >             // Write the default greeting into the data object using super class
   >             // method.
   >             putString(GREETING,"Welcome to My First Portlet !!");
   >         }

   >     }

2. Save the file as MyPersonalizationObject.java in the same location as your other custom classes, e.g. C:\MyProvider\MyClasses.

3. Follow the steps outlined in the article How to Build a Java Portlet to compile MyPersonalizationObject.java.

CREATING A RENDERER

In this section you create a subclass of BaseManagedRenderer to render your portlet's Edit and Edit Defaults mode and update your Show mode renderer to display user customizations. For more information on implementing extra render modes, refer to the article Adding Extra Render Modes to Java Portlets.

1. Using your favorite text editor, create a Java class called MyEditRenderer, which extends BaseManagedRenderer and provides an implementation of the renderBody method to supply content for your portlet's Edit and Edit Defaults mode.

   Note: MyEditRenderer has two responsibilities. First to render the HTML edit form so the user can enter customizations and secondly, to check for the action taken on that form and submit the PersonalizationObject back to the Provider for storage.

   >     Import java.io.*;

   >     Import oracle.portal.provider.v2.*;
   >     Import oracle.portal.provider.v2.render.*;
   >     Import oracle.portal.provider.v2.render.http.*;

   >     Public class MyEditRenderer extends BaseManagedRenderer
   >     {

   >         public void renderBody(PortletRenderRequest pr) throws PortletException
   >         {
   >             try
   >             {
   >                 // Retrieve the name of the parameter used to represent the user
   >                 // action taken on the edit form. This name was assigned by
   >                 // RenderManager but can be overridden by the portlet developer.
   >                 String actionParam = PortletRendererUtil.getEditFormParameter(pr);

   >                 // Now retrieve the value of that action parameter.
   >                 String action = pr.getParameter(actionParam);

   >                 // Retrieve the data object for later use.
   >                 MyPersonalizationObject data =
   >                     (MyPersonalizationObject)PortletRendererUtil.getEditData(pr);

   >                 // If the action was non null, then we have to save our newly
   >                 // customized data. Otherwise, we have to render the edit form.
   >                 if (action != null)
   >                 {
   >                     // Write the personalized values into the data object.
   >                     data.setPortletTitle(pr.getParameter("mfp_title"));
   >                     data.putString(MyPersonalizationObject.GREETING,
   >                                    pr.getParameter(MyPersonalizationObject.GREETING));

   >                     // Submit the data object back to the PersonalizationManager
   >                     // for storing.
   >                     PortletRendererUtil.submitEditData(pr, data);
   >                 }
   >                 else
   >                 {
   >                     // Get a writer for the edit form.
   >                     PrintWriter out = pr.getWriter();

   >                     // Render the form.
   >                     out.println("<TABLE BORDER=0 CELLSPACING=0>");
   >                     out.println("<TR><TD>Title:</TD>");
   >                     out.print("<TD><INPUT TYPE=\"text\" NAME=\"mfp_title\" " +
   >                               "SIZE=\"50\" VALUE=\"" + data.getPortletTitle() +
   >                               "\" </TD></TR>");
   >                     out.println("<TR><TD>Greeting:</TD>");
   >                     out.print("<TD><INPUT TYPE=\"text\" NAME=\"" +
   >                               MyPersonalizationObject.GREETING + "\"  SIZE=\"50\" "+
   >                               "VALUE=\"");
   >                     out.print(data.getString(MyPersonalizationObject.GREETING) +
   >                               "\" </TD></TR>");
   >                     out.println("</TABLE>");
   >                 }
   >             }
   >             catch (IOException ioe)
   >             {
   >                 throw new PortletException(ioe);
   >             }
   >         }

   >     }

2. Save the file as MyEditRenderer.java in same location as your other custom classes, e.g. C:\MyProvider\MyClasses.

3. Follow the steps outlined in the article How to Build a Java Portlet to compile MyEditRenderer.java.

4. Open the file MyCustomRenderer.java, i.e. the source code for the BaseManagedRenderer you created in the article How to Build a Java Portlet.

5. Update the code to display a customized greeting.

   The customized title is automatically rendered by the PDK.

   Note: Changes for MyCustomRenderer.java are shown in bold blow. You can display the data differently if you wish.

   >     Import java.io.*;
   >     Import oracle.portal.provider.v2.*;
   >     Import oracle.portal.provider.v2.render.*;
   >     Import oracle.portal.provider.v2.render.http.*;

   >     Public class MyCustomRenderer extends BaseManagedRenderer
   >     {
   >         public void renderBody(PortletRenderRequest pr) throws PortletException
   >         {
   >             try
   >             {
   >                 // Get a writer for this renderer's output
   >                 PrintWriter out = pr.getWriter();

   >                 // Generate output and send to the writer
   >                 out.println("<b>Hi " + pr.getUser().getName() + "!</b><br>");

   >                 // Comment out the following line
   >                 // out.println("This output was generated by my very own renderer!");

   >                 // Retrieve the data object used to store user customizations.
   >                 MyPersonalizationObject data =
   >                     (MyPersonalizationObject)PortletRendererUtil.getEditData(pr);

   >                 // Display the user's personalized greeting.
   >                 out.println(data.getString(MyPersonalizationObject.GREETING));
   >             }
   >             catch (IOException ioe)
   >             {
   >                 throw new PortletException(ioe);
   >             }
   >         }
   >     }

6. Follow the steps outlined in the article How to Build a Java Portlet to recompile MyCustomRenderer.java.

UPDATING THE XML PROVIDER DEFINITION

In this section you update the XML provider definition to expose the two new render modes. You also declare a PersonalizationManager controller for your portlet and provide details such as the class name of the PersonalizationObject you are using to store customizations. Follow the steps below to update your XML provider definition.

1. Open the XML provider definition you created in the article Adding Extra Render Modes to Java Portlets.

2. Update the file as shown below.

   Note: All additions to provider.xml are shown in bold.

   >     <?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
   >     <?providerDefinition version="3.1"?>

   >     <provider class="oracle.portal.provider.v2.DefaultProviderDefinition">

   >        <session>true</session>

   >        <preferenceStore class="oracle.portal.provider.v2.preference.FilePreferenceStore">
   >           <name>MyProviderPrefStore</name>
   >           <useHashing>true</useHashing>
   >        </preferenceStore>

   >        <portlet class="oracle.portal.provider.v2.DefaultPortletDefinition">
   >           <id>1</id>
   >           <name>MyFirstPortlet</name>
   >           <title>My first portlet</title>
   >           <shortTitle>MyFirstPortlet</shortTitle>
   >           <description>My first ever portlet, using my own custom renderer, implementing extra render modes!</description>
   >           <timeout>10</timeout>
   >           <timeoutMessage>Timed out waiting for MyFirstPortlet</timeoutMessage>
   >           <hasHelp>true</hasHelp>
   >           <hasAbout>true</hasAbout>
   >           <showDetails>true</showDetails>
   >           <showEdit>true</showEdit>
   >           <showEditDefault>true</showEditDefault>
   >           <renderer class="oracle.portal.provider.v2.render.RenderManager">
   >              <contentType>text/html</contentType>
   >              &ltautoRedirect&gttrue&ltautoRedirect>
   >              <showPage class="MyCustomRenderer"/>
   >              <editPage class="MyEditRenderer" />
   >              <editDefaultsPage class="MyEditRenderer" />
   >              <helpPage>help.html</helpPage>
   >              <aboutPage>about.jsp</aboutPage>
   >              <showDetailsPage>/servlet/MyShowDetailsServlet</showDetailsPage>
   >           </renderer>
   >           <personalizationManager class="oracle.portal.provider.v2.personalize.PrefStorePersonalizationManager">
   >              <dataClass>MyPersonalizationObject</dataClass>
   >           </personalizationManager>
   >        </portlet>

   >     </provider>

3. Save the updates to provider.xml.

Note: In this XML provider definition we switched on the FilePreferenceStore's 'path hashing' feature mentioned previously. This allows your portlet to store and retrieve user customizations more efficiently, by spreading data files evenly across the file system.

CONFIGURING OC4J

To update your portlet's provider and install that provider into OC4J, see the article "Packaging and deploying Your Provider".

VIEWING THE PORTLET

To view the new render modes you must ensure that the updated XML provider definition is re-parsed. To do this restart OC4J and refresh the Portal page containing your portlet.

1. If you have not already added MyFirstPortlet to a Portal page, do so now.

2. View that page and click the Customize link in the top right corner of the portlet instance to render the Edit mode.

3. Customize the Title and Greeting and click OK.

You will be redirected to the original page where your customizations are rendered in Show mode.

Revision History

- January 21st 2002