Skip Headers
Oracle® Application Server Portal Developer's Guide
10g Release 2 (10.1.2)
Part No. B14134-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

6 Building PL/SQL Portlets


Note:

In general, Oracle recommends that you build your portlets using Java rather than PL/SQL. For more information on choosing a technology for building your portlets, refer to Chapter 2, "Portlet Technologies Matrix". For more information on building your portlets using Java, refer to Chapter 5, "Building Java Portlets".

The OracleAS Portal PL/SQL APIs are implemented as a set of PL/SQL packages and objects. Database providers and portlets are deployed to a database schema as PL/SQL packages. This chapter explains how to create PL/SQL portlets based on the Oracle Application Server Portal Developer Kit-PL/SQL (PDK-PL/SQL). To make effective use of this chapter, you should already know PL/SQL and have some familiarity with the PL/SQL Web Toolkit.

This chapter contains the following sections:

6.1 Guidelines for Creating PL/SQL Portlets

When you write your portlets in PL/SQL, you should follow the best practices described in this section:

6.1.1 Portlet Show Modes

Just like a Java portlet, a PL/SQL portlet has a variety of Show modes available to it. A Show mode is an area of functionality provided by a portlet. The available Show modes are described more fully in Chapter 5, "Building Java Portlets":

OracleAS Portal defines the portlet Show mode in the field of the portlet runtime record that is passed to the provider when calling the procedure. To check the selected Show mode, you can use the constants in the wwpro_api_provider package. These constants are listed with their corresponding Show mode in Table 6-1.

Table 6-1 Show Mode Constants in wwpro_api_provider

Show mode Constant
Shared Screen
MODE_SHOW
Edit
MODE_SHOW_EDIT
Edit Defaults
MODE_SHOW_EDIT_DEFAULTS
Preview
MODE_SHOW_PREVIEW
Full Screen
MODE_SHOW_DETAILS
Help
MODE_SHOW_HELP
About
MODE_SHOW_ABOUT
Link
MODE_SHOW_LINK

6.1.2 Recommended Portlet Procedures and Functions

The primary goal of the portlet's code is to generate the HTML output that displays on a page for all of the Show modes required by OracleAS Portal. Although it is possible to implement the portlet as a set of separate PL/SQL stored program units, organizing the portlet's code into a PL/SQL package is the best way of encapsulating related portlet code and data as a single unit in the database. You also achieve better database performance and ease of portlet maintenance.

As you may recall from Section 2.4, "Deployment Type", requests from OracleAS Portal for a particular portlet go through the portlet's provider. To communicate with its portlets, the provider contains a set of required methods that make calls to the portlet code.

When implementing a portlet as a PL/SQL package, it is a good idea to organize the portlet code in parallel with the provider code. For example, when the provider needs to retrieve information about one of its portlets, it uses its get_portlet function. Hence, it makes sense for the portlet to contain a get_portlet_info function that returns the requested information when called by the provider's get_portlet function. Similarly, it is logical for the provider's show_portlet procedure to call the portlet's show procedure, which produces the HTML output for a requested Show mode and returns it to the provider.

The following procedures and functions are recommended for use in PL/SQL portlets to communicate efficiently with the database provider:

  • get_portlet_info returns the portlet record to the provider.

  • show produces HTML output for a requested Show mode and returns it to the provider.

  • register initializes the portlet at the instance level.

  • deregister enables cleanups at the instance level.

  • is_runnable determines whether the portlet can be run. Security checks can be performed in this function.

  • copy copies the customized and default values of portlet preferences from one portlet instance to a new portlet instance when OracleAS Portal makes a copy of the page.

  • describe_parameters returns a list of public portlet parameters.

6.2 Building PL/SQL Portlets with the PL/SQL Generator

To facilitate the development of database providers and PL/SQL portlets, you can use the PL/SQL Generator, a utility that creates installable PL/SQL code for a database provider and its portlets. The PL/SQL Generator is a standalone Web application that receives the provider and portlet definitions in the form of an XML file (similar in format to the provider.xml file). The XML tags used for the provider and portlet definition are a subset of the XML tags used for defining Web providers with PDK-Java. The output of the PL/SQL Generator is a SQL script that can be run from SQL*Plus. The script contains SQL commands for installing the provider and portlet packages in the correct order.

You can download the PL/SQL Generator along with its installation instructions from:

http://www.oracle.com/technology/products/ias/portal/files/plsqlgenerator.zip

The general model for working with the PL/SQL Generator is as follows:

  1. Create an XML file that defines the provider and portlets that you want to build, as described in Section 6.2.1, "Creating the Input XML File".

  2. Run the PL/SQL Generator using the XML file as input, as described in Section 6.2.2, "Running the PL/SQL Generator".

  3. Publish the generated PL/SQL portlet, which includes the following steps:

6.2.1 Creating the Input XML File

The source XML file starts and ends with the <provider> and </provider> tags, and can include one or many portlet definitions. Each portlet definition is bracketed by the <portlet> and </portlet> tags. A portlet definition includes the XML tags that specify values for the portlet record attributes and enable the links in the portlet header. For example, the <name> tag specifies the portlet name in the provider domain and the <title> tag specifies the portlet display name or title. When set to true, the <showEdit> tag enables the Edit mode for the portlet and the corresponding link in the portlet header. Table 6-2 lists the available XML tags for PL/SQL Generator input.

Table 6-2 XML Tags for PL/SQL Generator Input

XML Tag Definition Value Type
provider
Encloses provider definition tags. Not applicable
portlet
Encloses portlet definition tags. Not applicable
id
Specifies the portlet ID in the provider. This value must be unique within the provider. string
name
Specifies the portlet name. The name should not contain any spaces. The generator uses the information provided in the name tag for the portlet package name. string
title
Specifies the portlet display name. string
shortTitle
Specifies the portlet short display name. This tag is useful for mobile portlets. string
description
Specifies the portlet description. string
defaultLocale
Specifies the language the portlet renders by default. The value is the two letter ISO language and country codes expressed as language.country. string
timeout
Specifies the portlet's timeout interval in seconds. number
timeoutMsg
Specifies the message to display when the portlet times out. string
showEdit
Indicates whether the portlet supports Edit mode, which enables the user to customize the portlet's properties. Boolean
showEditDefault
Indicates whether the portlet supports the Edit Defaults mode, which enables page administrators to customize the default values of the portlet's properties. Boolean
showDetails
Indicates whether the portlet can be viewed in Full Screen mode. In this mode, the entire browser window is dedicated to the portlet. Full screen mode enables the portlet to show more details than when it shares the page with other portlets. Boolean
showPreview
Indicates whether the portlet supports the Preview mode. Boolean
hasHelp
Indicates whether the portlet supports the Help mode. Boolean
hasAbout
Indicates whether the portlet supports the About mode. Boolean
language
Defines the portlet's default language (for example, en). string
contentType
Indicates the default content type supported by the portlet. The tag can take one of the following values:
wwpro_api_provider.CONTENT_TYPE_HTML
wwpro_api_provider.CONTENT_TYPE_XML
wwpro_api_provider.CONTENT_TYPE_MOBILE
string
apiVersion
Specifies the version of the OracleAS Portal PL/SQL API to which the portlet conforms. For OracleAS Portal 9.0.4 and earlier releases, the tag value should be wwpro_api_provider.API_VERSION_1. string
callIsRunnable
Indicates whether OracleAS Portal must check for the user's credentials before displaying the portlet. The default value is true. Boolean
callGetPortlet
Indicates whether the portal can use the portlet record data stored in the Portlet Metadata Repository (PMR) instead of contacting the provider for the portlet record. If the portlet record (specified by provider id, portlet id, and language) returned by a provider does not change, then the provider should set the value for call_get_portlet to false. This tells the portal to use the PMR instead of making calls to the provider's get_portlet and get_portlet_list functions. An example of when a provider would not want the portal to use portlet metadata from the PMR is when the value of the portlet records is different for logged on users. The default value is true. Boolean
acceptContentType
Specifies a comma delimited list of content types that the portlet can produce. For example, if a portlet can produce content of both HTML and MOBILEXML type, then the tag value is:
text/html,text/vnd.oracle.mobilexml
string
hasShowLinkMode
Indicates whether the portlet implements the Link mode. If the value is false, the portlet uses its short or full title to display a link label that references the portlet content in a mobile device. Otherwise, a customized link can be generated in the portlet code. The default value is false. Boolean
mobileOnly
Indicates whether the portlet is available only to mobile devices. The default value is false. Boolean
preferenceStorePath
Specifies the base preference store path where the provider has stored the portlet customization information. This path is used when exporting portlets. string
createdOn
Defines the portlet creation date. The default value is sysdate. date
createdBy
Identifies the user who created the portlet record. string
lastUpdatedOn
Defines the most recent date on which the portlet record was changed. The default value is sysdate. date
lastUpdatedBy
Identifies the user who most recently changed the portlet record. string
passAllUrlParams
Indicates parameter passing behavior in the portlet. If the tag value is true, then OracleAS Portal passes all parameters in the URL to the portlet. If the tag value is false, then the portlet receives only those parameters that are intended for the portlet. The default value is true. Boolean
cacheLevel
Indicates a portlet's cache level. It can take one of the following values:
wwpro_api_provider.CACHE_LEVEL_SYSTEM
wwpro_api_provider.CACHE_LEVEL_USER
wwpro_api_provider.CACHE_LEVEL_PTL_SESSION
string
rewriteUrls
Indicates whether or not URL rewriting will be performed on the output from a portlet render request. The default value is false. Boolean

Following is a sample of the input XML for the PL/SQL Generator. Mandatory information is shown in bold.

<!-- This is a sample provider.xml file for the PLSQL Generator 1.2 -->
<provider>
    <portlet>
        <id>1</id>
        <name>Test_Portlet</name>
        <title>Test Portlet Title</title>
        <shortTitle>Short portlet title</shortTitle>
        <description>This is a Test portlet</description>
        <timeout>30</timeout>
        <timeoutMsg>Test Portlet Timed Out</timeoutMsg>
        <showEdit>true</showEdit>
        <showEditDefault>true</showEditDefault>
        <showDetails>true</showDetails>
        <showPreview>true</showPreview>
        <hasHelp>true</hasHelp>
        <hasAbout>true</hasAbout>
        <language>en</language>
        <contentType>wwpro_api_provider.CONTENT_TYPE_HTML</contentType>
        <apiVersion>wwpro_api_provider.API_VERSION_1</apiVersion>
        <callIsRunnable>true</callIsRunnable>
        <callGetPortlet>true</callGetPortlet>
        <acceptContentType>'text/html'</acceptContentType>
        <hasShowLinkMode>false</hasShowLinkMode>
        <mobileOnly>false</mobileOnly>
        <passAllUrlParams>true</passAllUrlParams>
        <cacheLevel>wwpro_api_provider.CACHE_LEVEL_USER</cacheLevel>
        <rewriteUrls>true</rewriteUrls>
    </portlet>
</provider>

6.2.2 Running the PL/SQL Generator

Once you have created a valid XML input file, you can run the PL/SQL Generator to generate the provider and portlet packages in the form of a SQL file:

  1. If you have not already done so, install the PL/SQL Generator according to the instructions that came with the download.

  2. From your browser, go to the URL for the PL/SQL Generator. It should look something like the page shown in Figure 6-1.

    Figure 6-1 PL/SQL Generator Page

    Page where you specify the input XML file and provider name.
  3. Click Browse and select the source XML file for the Source XML File field. Refer to Section 6.2.1, "Creating the Input XML File" for more information on creating the XML file.

  4. In the Provider Name field, enter the name of the provider. The provider name must not contain any spaces in it. The generator uses the value entered in the Provider Name field for the provider package name.

  5. Click Generate to generate the SQL file that contains the installable PL/SQL code for the provider and portlet packages. When the browser prompts you to save or open the file, choose Save.

  6. In the Save dialog box, change the file extension to .sql and revise the file name as you wish.

  7. Save the file.

6.2.3 Publishing the Generated PL/SQL Portlet

After you have run the PL/SQL Generator and obtained a SQL file, you still need to perform the following tasks to make the provider and portlets available to OracleAS Portal:

6.2.3.1 Installing the Packages in the Database

To install the generated provider and portlet packages into the database where you installed OracleAS Portal, perform the following steps:

  1. Start a SQL*Plus session and log in to the PORTAL schema.

  2. Create a new database schema, the provider schema, to store the generated provider and portlet packages by entering the following commands in SQL*Plus:

    create user provider_schema identified by provider_schema_password;
    grant resource, connect to provider_schema;
    
    
  3. Grant the EXECUTE privilege for the OracleAS Portal APIs to the provider schema by running the provsyns.sql script that is located in the MID_TIER_ORACLE_HOME/portal/admin/plsql/wwc directory as follows:

    @provsyns.sql provider_schema
    
    
  4. Log in to the provider schema and run the generated SQL file. It will create the provider and portlet packages in the database.

6.2.3.2 Registering the Database Provider

After creating the provider and portlet packages in the database, you must register the provider with OracleAS Portal before adding the PL/SQL portlet to a portal page:

  1. Log in to OracleAS Portal as an administrator.

  2. From the Portal Builder, click Administer > Portlets.

  3. In the Remote Providers portlet, click Register a Provider.

  4. Fill in the Name, Display Name, Timeout, and Timeout Message as desired.

  5. Choose an Implementation Style of Database.

  6. Click Next and complete the remainder of the wizard.

  7. When you complete the wizard, click Finish.

  8. From the Portlet Repository portlet, click Display Portlet Repository.

  9. Browse the repository and find the provider that you just registered. Typically, new providers appear in the Portlet Staging Area of the repository.

  10. Once you find the provider, confirm that it contains all of the portlets you created in the provider. If the provider or its portlets do not appear, then retrace the steps in this section and the preceding sections (Section 6.2.3.1, "Installing the Packages in the Database", Section 6.2.1, "Creating the Input XML File", and Section 6.2.2, "Running the PL/SQL Generator") to ensure that you correctly created and registered your provider and portlet.

6.2.3.3 Adding Your Portlet to a Page

Once your provider and its portlets appear in the repository, you can add it to a page. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.3 Building PL/SQL Portlets Manually

This section describes how to build a basic PL/SQL portlet using the hello world sample contained in the starter provider sample. The starter provider sample, located in ..\pdkplsql\pdk\plsql\starter in PDK-PL/SQL (pdkplsql.zip), consists of the following files:

The general model for building PL/SQL portlets manually is as follows:

  1. Modify the hello world portlet package specification and body to create your own portlet package, as described in Section 6.3.1, "Implementing the Portlet Package"

  2. Modify the starter provider package specification and body to add your new portlet to a provider, as described in Section 6.3.2, "Implementing the Provider Package"

  3. Add your portlet to a page, as described in Section 6.3.3, "Adding Your Portlet to a Page"

6.3.1 Implementing the Portlet Package

To modify helloworld_portlet.pks and helloworld_portlet.pkb to create your own portlet package, perform the following steps:

  1. Make copies of the package specification, helloworld_portlet.pks, and body, helloworld_portlet.pkb.

  2. Rename the copies to my_first_portlet.pks and my_first_portlet.pkb, respectively.

  3. Open my_first_portlet.pks in an editor and change the name of the package to my_first_portlet:

    CREATE OR REPLACE
    package my_first_portlet
    is
    ...
    
    end my_first_portlet;
    
    
  4. Open my_first_portlet.pkb in an editor and repeat the change that you made in the previous step; that is, change the name of the package to my_first_portlet.

  5. In my_first_portlet.pkb, find the function named get_portlet_info and modify it as follows:

    function get_portlet_info
    (
         p_provider_id in integer
        ,p_language in varchar2
    )
    return wwpro_api_provider.portlet_record
    is
        l_portlet      wwpro_api_provider.portlet_record;
    begin
        l_portlet.id := starter_provider.PORTLET_FIRST;
        l_portlet.provider_id := p_provider_id;
        l_portlet.title := 'My First Portlet';
        l_portlet.name := 'My_First_Portlet';
        ...
    
    
  6. In my_first_portlet.pkb, find the procedure named show and modify it as follows:

    procedure show
    (
       p_portlet_record        wwpro_api_provider.portlet_runtime_record
    )
    is
        l_portlet      wwpro_api_provider.portlet_record;
        l_text_name in varchar2(100);
        l_text in varchar2(200);
    begin
    ...
            /*
            Display the content of the portlet in the show mode.
            Use the wwui_api_portlet.portlet_text() API when
            generating the content of the portlet so that the
            output uses the portlet CSS.
            */
            htp.p(wwui_api_portlet.portlet_text(
                 p_string   =>  'Hello World - Mode Show'
                ,p_level    =>  1
                ));
            /*
            Add the functionality you want here. In this case we are adding
            a welcome message addressed to the current user.
            */
            l_text_name := 'Welcome to my first portlet ' || wwctx_api.get_user;
            l_text := wwui_api_portlet.portlet_text(
                p_string   =>  l_text_name,
                p_level    =>  1        );
            htp.p(l_text);        htp.para;
            if (p_portlet_record.has_border) then
                wwui_api_portlet.close_portlet;
            end if;
    ...
    
    
  7. Save my_first_portlet.pkb.

6.3.2 Implementing the Provider Package

After you implement the portlet package, you must add your portlet to a provider. To modify starter_provider.pks and starter_provider.pkb to add your new portlet to a provider, perform the following steps:

  1. Make copies of the package specification, starter_provider.pks, and body, starter_provider.pkb.

  2. Rename the copies to starter_provider2.pks and starter_provider2.pkb, respectively.


    Note:

    If you want to create a new, empty provider, remove all references to the hello world and snoop portlets from starter_provider2.pks and starter_provider2.pkb before performing the steps that follow.

  3. Open starter_provider2.pks in an editor.

  4. Add a constant called PORTLET_FIRST. This constant is used as the identifier for the portlet within the provider. Hence, the constant's value must be unique within the provider.

    CREATE OR REPLACE
    package STARTER_PROVIDER
    is
      /**
        * This package is used as an example to show how providers can be created
        * in the portal system.
        *
        * This provider contains the following portlets:
        *
        * Hello World (PORTLET_HELLOWORLD)
        * Snoop (PORTLET_SNOOP)
        *
        */
        PORTLET_HELLOWORLD constant integer := 1;
        PORTLET_SNOOP      constant integer := 2;
        PORTLET_FIRST      constant integer := 3;
    
    
  5. Save starter_provider2.pks.

  6. Open starter_provider2.pkb in an editor.

  7. In starter_provider2.pkb, add a call for the new portlet's get_portlet_info function in the get_portlet function of the provider package. This step entails adding the call my_first_portlet.get_portlet_info in the get_portlet function. The get_portlet function allows the portal to retrieve information for the portlet when necessary.

    function get_portlet
         p_provider_id in integer
        ,p_portlet_id in integer
        ,p_language in varchar2
    )
    return wwpro_api_provider.portlet_record
    is
    begin
        if (p_portlet_id = PORTLET_HELLOWORLD) then
            return helloworld_portlet.get_portlet_info(
                 p_provider_id  => p_provider_id
                ,p_language     => p_language
                );
        elsif (p_portlet_id = PORTLET_SNOOP) then
            return snoop_portlet.get_portlet_info(
                 p_provider_id  => p_provider_id
                ,p_language     => p_language
                );
        elsif (p_portlet_id = PORTLET_FIRST) then
            return my_first_portlet.get_portlet_info(
                 p_provider_id  => p_provider_id
                ,p_language     => p_language
                );
        else
            raise wwpro_api_provider.PORTLET_NOT_FOUND_EXCEPTION;
        end if;
    end get_portlet;
    
    
  8. In starter_provider2.pkb, add the new portlet to the list of portlets returned by the provider. This step entails adding the new portlet to the get_portlet_list function of the provider. The get_portlet_list function tells the portal which portlets the provider implements.

    function get_portlet_list
    ...
    begin
        l_cnt := 0;
        if (p_security_level = false ) then            l_cnt := l_cnt + 1;
                l_portlet_list(l_cnt) := get_portlet(
                     p_provider_id  => p_provider_id
                    ,p_portlet_id   => PORTLET_HELLOWORLD
                    ,p_language     => p_language
                    );
                l_cnt := l_cnt + 1;
                l_portlet_list(l_cnt) := get_portlet(
                     p_provider_id  => p_provider_id
                    ,p_portlet_id   => PORTLET_SNOOP
                    ,p_language     => p_language
                    );
                l_cnt := l_cnt + 1;
                l_portlet_list(l_cnt) := get_portlet(
                     p_provider_id  => p_provider_id
                    ,p_portlet_id   => PORTLET_FIRST
                    ,p_language     => p_language
                    );
        else
            if (helloworld_portlet.is_runnable(
                 p_provider_id     =>  p_provider_id
                ,p_reference_path  =>  null)
            ) then
                l_cnt := l_cnt + 1;
                l_portlet_list(l_cnt) := get_portlet(
                     p_provider_id  => p_provider_id
                    ,p_portlet_id   => PORTLET_HELLOWORLD
                    ,p_language     => p_language
                    );        end if;
            if (snoop_portlet.is_runnable
                 p_provider_id     =>  p_provider_id
                ,p_reference_path  =>  null)
            ) then
                l_cnt := l_cnt + 1;
                l_portlet_list(l_cnt) := get_portlet(
                     p_provider_id  => p_provider_id
                        ,p_portlet_id   => PORTLET_SNOOP
                        ,p_language     => p_language
                        );
            end if;
            if (my_first_portlet.is_runnable(
                 p_provider_id     =>  p_provider_id
                ,p_reference_path  =>  null)
            ) then
                l_cnt := l_cnt + 1;
                l_portlet_list(l_cnt) := get_portlet(
                     p_provider_id  => p_provider_id
                    ,p_portlet_id   => PORTLET_FIRST
                    ,p_language     => p_language
                    );
            end if;
        end if;
        return l_portlet_list;
    end get_portlet_list;
    
    
  9. In starter_provider2.pkb, modify the is_portlet_runnable function to add a call to the is_runnable function of the new portlet.

    function is_portlet_runnable
    (
        p_portlet_instance in wwpro_api_provider.portlet_instance_record
    )
    return boolean
    is
    begin
        if (p_portlet_instance.portlet_id = PORTLET_HELLOWORLD) then
            return helloworld_portlet.is_runnable(
                 p_provider_id     =>  p_portlet_instance.provider_id
                ,p_reference_path  =>  p_portlet_instance.reference_path
                );
        elsif (p_portlet_instance.portlet_id = PORTLET_SNOOP) then
            return snoop_portlet.is_runnable(
                 p_provider_id     =>  p_portlet_instance.provider_id
                ,p_reference_path  =>  p_portlet_instance.reference_path
                );
        elsif (p_portlet_instance.portlet_id = PORTLET_FIRST) then
            return my_first_portlet.is_runnable(
                 p_provider_id     =>  p_portlet_instance.provider_id
                ,p_reference_path  =>  p_portlet_instance.reference_path
                );
        else
            raise wwpro_api_provider.PORTLET_NOT_FOUND_EXCEPTION;
        end if;
    end is_portlet_runnable;
    
    
  10. Repeat step 9 according to the information in Table 6-3.

    Table 6-3 Changes to starter_provider2.pkb

    Procedure/Function Addition
    procedure register_portlet
    
    elsif (p_portlet_instance.portlet_id =
      PORTLET_FIRST) then
    my_first_portlet.register(p_portlet_instance)
    
    procedure deregister_portlet
    
    elsif (p_portlet_instance.portlet_id =
      PORTLET_FIRST) then
    my_first_portlet.deregister
      (p_portlet_instance)
    
    function describe_portlet_parameters
    
    elsif (p_portlet_id =
      PORTLET_FIRST) then
    return my_first_portlet.describe_parameters
      (p_provider_id, p_language);
    
    procedure show_portlet
    
    elsif (p_portlet_record.portlet_id =
      PORTLET_FIRST) then
    my_first_portlet.show(p_portlet_record)
    
    procedure copy_portlet
    
    elsif (p_copy_portlet_info.portlet_id =
      PORTLET_FIRST) then
    my_first_portlet.copy(p_portlet_record)
    

  11. Save and close starter_provider2.pkb.

  12. Log in to OracleAS Portal as you normally would.

  13. From the Portal Builder, click Administer > Portlets.

  14. From the Portlet Repository portlet, click Display Portlet Repository.

  15. Browse the repository and find the starter provider (typically it will appear in the Portlet Staging Area of the repository). It should contain its two original portlets: hello world and snoop.

  16. From a command line prompt, start SQL*Plus and connect as the owner of the starter provider schema.

  17. Compile the new and modified PL/SQL packages in the following order:

    • starter_provider2.pks

    • my_first_portlet.pks

    • starter_provider2.pkb

    • my_first_portlet.pkb

  18. If any compilation errors occur, fix and recompile them until all of the packages compile successfully.

  19. From the Portlet Repository portlet, click Display Portlet Repository.

  20. Browse the repository and find the starter provider again. It should now contain your new portlet, my_first_portlet, in addition to its original portlets.


    Note:

    If you make changes to an existing provider or the portlet record, you need to refresh your provider before seeing the changes reflected in your OracleAS Portal instance.

6.3.3 Adding Your Portlet to a Page

Once your portlet appears in the repository, you can add it to a page. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.4 Implementing Information Storage

OracleAS Portal provides APIs for storing and retrieving individual portlet preferences, and storing and manipulating temporary data for the current session:

6.4.1 Implementing a Preference Store

OracleAS Portal provides a set of APIs for storing and retrieving individual preferences for each unique portlet instance in a persistent manner. It provides a unique identifier for each individual, a preference store automatically mapped by user, and access mechanisms for storing and retrieving personalization information in your PL/SQL portlets.

By default, when you enable end-user personalization, Customize appears on the title bar of your portlet. This link displays a form where users can choose settings for that portlet.

End-user personalization options are available through the wwpre_api_name and wwpre_api_value packages.

6.4.1.1 Using a Preference Store

In general, you can set up preference storage as follows:

  1. Create the preference path through wwpre_api_name.create_path.

  2. Create the preference with wwpre_api_name.create_name.

  3. Set the preference values by providing the preference name and scoping level for which you want to set the value. Use wwpre_api_value.set_value_as_varchar2, set_value_as_number, or set_value_as_date for this purpose.

  4. Get preference values by providing the preference name and path whenever you want to retrieve the preference value. Use wwpre_api_value.get_value_as_varchar2, get_value_as_number, or get_value_as_date for this purpose.

6.4.1.2 Creating and Accessing a Preference Store

The services example, located in ..\pdkplsql\pdk\plsql\svcex in PDK-PL/SQL (pdkplsql.zip), illustrates how you can implement preference storage. The objective is to achieve the following functionality:

  • When a user clicks Customize, they can enter text in two fields.

  • The first field prompts for personalized text. The second prompts for a personalized portlet title.

  • The values the user enters for these two fields is stored in the preference store.

  • The personalized text and portlet titles are retrieved whenever that user invokes the portlet instance.

You can browse through this example as follows to see how to create the preference store, store values in it, and retrieve values from it:

  1. Open the services_portlet.pkb file in an editor.

  2. The portlet path and preference names are provided with aliases in the constants part of your portlet definition.

    DOMAIN           constant varchar2(30) := 'provider';
    SUBDOMAIN        constant varchar2(32) := 'services';
    PORTLET_PATH     constant varchar2(256):= 'oracle.portal.pdk.servicesportlet';
    PREFNAME_STRING  constant varchar2(30) := 'services_string';
    PREFNAME_TITLE   constant varchar2(30) := 'services_title';
    
    
  3. Find the register procedure. Your portlet needs to create a path for storing your preferences. To do so, it calls wwpre_api_name.create_path for creating the preference path. It then calls wwpre_api_name.create_name for creating the preference name, taking the portlet path, name, and description as input parameters. Another input parameter is the p_type_name that indicates special value types. The NLSID type indicates that the value stored is a Globalization Support ID. The functions for setting and retrieving this type treat it as a number value. Apart from that, when a preference store value of this type is exported or copied, so are its associated strings. The last input parameter, the language, is obtained from a context API.

    procedure register
    (
        p_portlet_instance in wwpro_api_provider.portlet_instance_record
    )
    is
    begin
        --
        -- Create a path for the portlet instance.  This is used to create
        -- the preferences for the portlet instance in the preference store.
        --
        wwpre_api_name.create_path(
            p_path => PORTLET_PATH || p_portlet_instance.reference_path
            );
        --
        -- Create the names to store the portlet preferences.
        --
        wwpre_api_name.create_name(
            p_path        => PORTLET_PATH
                || p_portlet_instance.reference_path,
            p_name        => PREFNAME_STRING,
            p_description => 'Single custom row in '
                || 'Introductory Example portlet.',
            p_type_name   => 'NLSID',
            p_language    => wwctx_api.get_nls_language);
        wwpre_api_name.create_name(
            p_path        => PORTLET_PATH
                || p_portlet_instance.reference_path,
            p_name        => PREFNAME_TITLE,
            p_description => 'Single custom row in '
                || 'Introductory Example portlet.',
            p_type_name   => 'NLSID',
            p_language    => wwctx_api.get_nls_language);
    exception
        when others then
            raise;
    end register;
    
    
  4. The deregister procedure must eliminate the preference store with a call to wwpre_api_name.delete_name.

    procedure deregister
    (
        p_portlet_instance in wwpro_api_provider.portlet_instance_record
    )
    is
    begin
         --
         -- Delete the path used by the portlet instance.  This will delete
         -- all the names and all the values associated with the path.
         --
         wwpre_api_name.delete_path(
            p_path => PORTLET_PATH || p_portlet_instance.reference_path
            );
    exception
        when others then
            raise;
    end deregister;
    
    
  5. The portlet must also get and set the values in the preference store using wwpre_api_value.set_value and wwpre_api_value.get_value. Find the get_default_preference function. Notice how this function loads the system level default values from the preference store. The default preferences are associated with an instance. The language strings are set in the database.

    function get_default_preference
    ...
    begin
        --
        -- Try to find a previously entered portlet instance string preference,
        -- if any.
        -- A portlet instance string preference is stored in the preference
        -- store and has a level of SYSTEM_LEVEL_TYPE.
        --
            p_path       => PORTLET_PATH || p_reference_path,
        l_prefs.string_id := to_char(wwpre_api_value.get_value_as_number(
            p_name       => PREFNAME_STRING,
            p_level_type => wwpre_api_value.SYSTEM_LEVEL_TYPE
            ));
        --
        -- If the value returned above is null it is an indication that there 
        -- is no default string yet.  Initialize the string id to 0 to indicate 
        -- this and load the default string value.
        --
        if (l_prefs.string_id is null or to_number(l_prefs.string_id) = 0) then
            wwpre_api_value.set_value_as_number(
                p_path       => PORTLET_PATH || p_reference_path,
                p_name       => PREFNAME_STRING,
                p_level_type => wwpre_api_value.SYSTEM_LEVEL_TYPE,
                p_level_name => null,
                p_value      => 0
                );
    ...
    end get_default_preference;
    
    
  6. Find the show procedure. Notice the behavior when the portlet is in Edit Defaults or Edit mode. Note also how p_action is populated when APPLY, CANCEL, or OK is clicked. Once the form is submitted, the show procedure of the portlet is called again and, if the p_action parameter is not null, then the save_prefs procedure is called to save the customizations and redirect to the relevant page.

    procedure show
    (
        p_portlet_record        wwpro_api_provider.portlet_runtime_record
    )
    is
        l_str varchar2(32000);
        l_pref_record preference_record;
        l_action   varchar2(10);
        l_names    owa.vc_arr;
        l_values   owa.vc_arr;
    begin
    ...
        elsif (p_portlet_record.exec_mode = 
                      wwpro_api_provider.MODE_SHOW_EDIT)
            or (p_portlet_record.exec_mode =
                      wwpro_api_provider.MODE_SHOW_EDIT_DEFAULTS)
        then
            wwpro_api_parameters.retrieve(l_names, l_values);
            for i in 1..l_names.count loop
                if (upper(l_names(i)) = upper('p_string')) then
                    l_pref_record.string := l_values(i);
                elsif l_names(i) = 'p_title' then
                    l_pref_record.title_string := l_values(i);
                elsif l_names(i) = 'p_action' then
                    l_action := l_values(i);
                end if;
            end loop;
            if (l_action in (ACTION_OK,ACTION_APPLY,ACTION_CANCEL)) then
                if (p_portlet_record.exec_mode = 
                                      wwpro_api_provider.MODE_SHOW_EDIT) then
                    save_prefs(p_string => l_pref_record.string,
                               p_title  => l_pref_record.title_string,
                               p_action => l_action,
                               p_level  => wwpre_api_value.USER_LEVEL_TYPE,
                               p_portlet_record  => p_portlet_record);
                else
                    save_prefs(p_string => l_pref_record.string,
                               p_title  => l_pref_record.title_string,
                               p_action => l_action,
                               p_level  => wwpre_api_value.SYSTEM_LEVEL_TYPE,
                               p_portlet_record  => p_portlet_record);
                end if;
            else
                show_edit(p_portlet_record  => p_portlet_record);
            end if;
    ...
    end show;
    
    
  7. The show_edit procedure renders the page for Edit or Edit Defaults mode. It renders two text fields that allow the user to change the customizable values in a form with three buttons (Apply, OK, and Cancel). Note that this function uses the wwpro_api_adapter.open_form to create the HTML form with the correct action attribute for the <FORM> tag and with the correct hidden fields. It is important to use this procedure to create the <FORM> tag if you want to use the portlet with the Federated Portal Adapter from remote OracleAS Portal instances.

    procedure show_edit(    p_portlet_record in wwpro_api_provider.portlet_runtime_record)is    l_prefs    preference_record;    l_text_prompt_string  varchar2(30);    l_title_prompt_string varchar2(30);begin
    ...
        htp.centeropen;
        htp.tableOpen(cattributes => 'BORDER="1" WIDTH=90%');
        htp.tableRowOpen;
        htp.p('<TD>');
        --
        --  This procedure call creates the <FORM> tags with a set of
        --  standard parameters.  Using this procedure makes the 
        --  customisation page work through the pl/sql http adapter.
        --
        wwpro_api_adapter.open_form(p_formattr => 'NAME="services"',
                                    p_prr      => p_portlet_record);
        htp.p('</TD>');    htp.tableRowClose;    htp.tableClose;    htp.centerclose;    htp.formclose;end show_edit;
    
    
  8. Review the following procedures and functions, which are related to the preference storage implementation in this example:

    • get_user_preference retrieves the user customized string and title for the portlet.

    • save_prefs is invoked to save the preferences to the preference store when the user clicks OK or Apply after making customization changes.

    • entered_text_is_valid checks to see if the text entered in the customizable text fields is valid.

  9. Optionally, if you want to see this portlet on a page and it is not already in the Portlet Repository, refer to the instructions in Section 6.3.2, "Implementing the Provider Package" for information on how to add it.

  10. Once your portlet appears in the repository, you can add it to a page to test it. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.4.2 Implementing a Session Store

The services example, located in ..\pdkplsql\pdk\plsql\svcex in PDK-PL/SQL (pdkplsql.zip), illustrates how you can implement a session store. The objective is to achieve the following functionality:

  • When a user invokes this portlet, it displays text that reads: "This portlet has rendered x times in this session." x is the number of times the portlet has been rendered.

  • Every time the user invokes the portlet, the counter increases by 1.

  • Clicking Details in the portlet enables the user to reset the counter through Clear. After clearing the counter, the counter starts again from zero.

6.4.2.1 Creating and Accessing a Session Store

You can browse through this example as follows to see how to create the session store, store values in it, and retrieve values from it:

  1. Open the services_portlet.pkb file in an editor.

  2. The domain and subdomain definitions for your session object are provided with aliases in the constants part of your portlet definition.

    DOMAIN           constant varchar2(30) := 'provider';
    SUBDOMAIN        constant varchar2(32) := 'services';
    PORTLET_PATH     constant varchar2(256):= 'oracle.portal.pdk.servicesportlet';
    PREFNAME_STRING  constant varchar2(30) := 'services_string';
    PREFNAME_TITLE   constant varchar2(30) := 'services_title';
    
    
  3. Find the clear_count procedure. clear_count is called from the show procedure when the user clicks Clear to reset the counter. First, clear_count calls wwsto_api_session.load_session to load the session object. Second, it calls wwsto_api_session.set_attribute to set the counter to zero. Third, it saves the session object by calling save_session.

    procedure clear_count
    (
        p_action          in varchar2,
        p_back_url        in varchar2,
        p_reference_path  in varchar2
    )
    is
        ex_counter integer;
        session_parms &&1..wwsto_api_session;
    begin
        --
        -- Clear the display counter.
        --
        if (p_action = ACTION_CLEAR) then
            --
            -- Load the session object that contains the display counter
            --
            session_parms :=
                &&1..wwsto_api_session.load_session (DOMAIN,SUBDOMAIN);
            ex_counter :=
                session_parms.get_attribute_as_number(
                    'ex_counter' || p_reference_path);
            --
            -- Reset the display counter.
            --
            ex_counter := 0;
            session_parms.set_attribute(
            'ex_counter' || p_reference_path, ex_counter);
            --
            -- Save the changes to the database immediately to avoid any
            -- data consistency problems with the data stored in the
            -- session object.
            --
            session_parms.save_session;
        end if;
        owa_util.redirect_url(curl=>p_back_url);
    end clear_count;
    
    
  4. Find the show_contents procedure. show_contents is called from the show procedure to retrieve the counter, increment it by one, and save the value in the session store. Notice how it retrieves the session object to display the number of times the user has rendered the portlet. It also retrieves the counter value with get_attribute_as_number and increments the counter for every invocation of this procedure.

    procedure show_contents
    (
        p_portlet_record  wwpro_api_provider.portlet_runtime_record
    )
    is
        l_prefs preference_record;
        session_parms &&1..wwsto_api_session;
        ex_counter integer;
        l_portlet wwpro_api_provider.portlet_record;
        l_str varchar2(32000);
    begin
        -- 
        -- In this mode a session counter is used to indicate
        -- the number of invocations of this portlet during the
        -- current session.  The counter is stored in the session
        -- store.
        --
        session_parms :=
            &&1..wwsto_api_session.load_session(DOMAIN,SUBDOMAIN);
        ex_counter    :=
            session_parms.get_attribute_as_number(
                'ex_counter' || p_portlet_record.reference_path);
        if (ex_counter is null) then -- first invocation
            session_parms.set_attribute(
                'ex_counter' || p_portlet_record.reference_path,1);
            ex_counter := session_parms.get_attribute_as_number(
                'ex_counter' || p_portlet_record.reference_path);
        else -- on every invocation increase by 1
            ex_counter := ex_counter + 1;
            session_parms.set_attribute(
                'ex_counter'
                || p_portlet_record.reference_path, ex_counter);
        end if;    session_parms.save_session;
    ...
    end show_contents;
    
    
  5. Optionally, if you want to see this portlet on a page and it is not already in the Portlet Repository, refer to the instructions in Section 6.3.2, "Implementing the Provider Package" for information on how to add it.

  6. Once your portlet appears in the repository, you can add it to a page to test it. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.5 Using Parameters

The functionality of portlets can be extended with the help of parameters. The business logic implemented by portlets may produce different HTML output depending on the parameters passed to the page. By using the portlet parameters, you can navigate within the portlet in the Shared Screen mode without changing the current page. Portlets can also communicate with each other through parameters.

Portlet parameters are structured as name-value pairs. These pairs map directly to the URL parameter passing format by using the GET submission method or can use the HTTP message body by using the POST submission method. Portlets can also expose their parameters to OracleAS Portal. When added to a page, these portlets can accept values in the form of page parameters created by the page designer.

Portlets do not have direct access to the URL, the HTTP message body, or the page parameters. To retrieve the parameter values, portlets must call the OracleAS Portal PL/SQL parameter APIs provided in the wwpro_api_parameters package.

OracleAS Portal offers the following types of parameters:


WARNING:

You cannot mix the usage of public and private parameters in a portlet. To enable public parameters for your portlet, you must take steps that preclude the usage of private parameters and vice versa.


For more information about parameters, refer to Section 2.12, "Public Portlet Parameters Support" and Section 2.13, "Private Portlet Parameter Support".

6.5.1 Passing Private Parameters

You can use either GET or POST HTML submission methods when passing private portlet parameters. The GET method uses the URL to pass the parameters, whereas the POST method places the parameters in an HTTP message body. For both methods, you must specify the portlet instance on the portal page, how the parameter is called, and the value of the parameter.

There are two types of private portlet parameters:

  • Qualified parameters ensure that a private portlet parameter is not read by any other portlet on the page. The reference path, which is assigned when the portlet is added to a page, is the unique prefix of the parameter. For example, http://page_url?277_MAP_368673.region=Europe. The qualified parameter's reference path is 277_MAP_368673, the name is region, and the value is Europe. For private parameters, we strongly recommend that you always use qualified parameters.

  • Unqualified parameters have no information about the portlet instance and can be read by any portlet on the page. For example, http://page_url?region=Europe. The unqualified parameter's name is region and its value is Europe. For private parameters, we strongly recommend that you avoid unqualified parameters.

6.5.2 Passing Page Parameters and Mapping Public Portlet Parameters

Public portlet parameters enhance the flexibility of your portlets by enabling page designers to reuse your portlets on multiple pages. As a result, page designers do not have to ask you to make changes to the portlet code when adding the portlet to different pages. By using public portlet parameters, any portlet on a page can easily receive its value from the mapped page parameter, regardless of the portlet parameter name.

For example, suppose you have a page parameter named dept_id. Three portlets need this value, but one portlet calls it dept, another calls it deptno, and still another department_id. Mapping the page parameter enables all three portlets to receive the value from the dept_id parameter and place it in the appropriate portlet parameter. Furthermore, the page designer may set a default value (for example, department 20) that can be customized by users (for example, department 30) and applied to all three portlets.

The general model for passing public and page parameters is as follows:

  1. Enable public parameters in the portlet record by setting pass_all_url_params to false. This ensures that the portlet is only passed parameters intended for that portlet.

  2. Declare the public parameters in the provider's describe_portlet_parameters function. For each of the portlets that belong to the provider, this procedure should return a list of the parameters that the portlet accepts in the form of a PL/SQL table of records of the type:

    type portlet_parameter_table is table ofportlet_parameter_record index by binary_integer;
    
    
  3. Provide descriptive information for the parameters in the portlet's describe_parameters function. For example:

    function describe_parameters
       (p_provider_id in integer, p_language in varchar2)
    return wwpro_api_provider.portlet_parameter_table
       is
    l_params wwpro_api_provider.portlet_parameter_table;
       begin
         l_params(1).name := 'dept_id';
         l_params(1).datatype := wwpro_api_provider.STRING_TYPE;
         l_params(1).description := 'Defines a department ID';
         l_params(1).display_name := 'Department ID';
         return l_params;
    end describe_parameters;
    
    
  4. Assign values to the public parameters. Public parameters typically get their values through page parameters. Page parameters are usually assigned default values by the page designer and the user can then customize the value at runtime. Alternatively, page parameter values can be assigned in the calling URL. For more information about how page designers can use page parameters, refer to the Oracle Application Server Portal User's Guide.

6.5.3 Retrieving Parameter Values

Regardless of whether you are using private or public parameters, you use the same APIs to retrieve their values. Portlets obtain their parameters by calling the PL/SQL parameter APIs in the wwpro_api_parameters package:

  • wwpro_api_parameters_get_value returns the parameter value that is specified by a given parameter name. Parameter names are not case sensitive, whereas parameter values are case sensitive. For example:

    l_region := wwpro_api_parameters.get_value
       (p_name => 'region',
        p_reference_path => p_portlet_record.reference_path);
    
    
  • wwpro_api_parameters_get_values returns an array of parameter values. This function returns all the values that are associated with a single parameter name or an empty list if no matches are found. Some business logic may require multiple selections, when multiple values are passed to the portlet by using the same parameter name. Portlets can take one or more values of the same parameter. For example:

  • wwpro_api_parameters_get_names returns the names of the parameters that are passed on to a specified portlet that is identified by the reference path. The returned list is a PL/SQL table of the owa.vc_ar type that is defined as follows:

    type vc_arr is table of varchar2(32000) index by binary_integer;
    

    Note:

    Portlet parameter names should not start with an underscore (_) because those parameters are reserved for internal use by OracleAS Portal, and are not passed to the portlet.

    For example:

    l_names owa.vc_arr;
    ...
    l_names := wwpro_api_parameters.get_names
       (p_reference_path => p_portlet_record.reference_path);
    
    
  • wwpro_api_parameters.retrieve returns the names and values of all of the portlet's parameters. For example:

    procedure show_portlet
       ( p_portlet_record in out
             wwpro_api_provider.portlet_runtime_record )
    is
       l_names owa.vc_arr;
       l_values owa.vc_arr;
    ...
    begin
    ...
       wwpro_api_parameters.retrieve (l_names, l_values);
       for i in 1..l_names.count loop
         htp.p('Parameter Name: '||l_names(i));
         htp.p('Parameter Value: '||l_values(i));
         htp.br;
       end loop;
    ...
    end show_portlet;
    

6.6 Accessing Context Information

Whenever a user accesses a page in OracleAS Portal, a public session is established. When the user logs in to OracleAS Portal, the public session becomes an authenticated session. This session contains several pieces of context information about the user, such as username, current session ID, IP address, and language preference. It also includes supporting information such as the OracleAS Portal schema currently in use.

Session context services return information about a user's session and are available through the wwctx_api package.

6.6.1 Using Context Information

The general model for working with the session context is as follows:

  1. Identify the piece of information you require for your functionality.

  2. Use the appropriate method from wwctx_api to get and optionally set this value.

Table 6-4 lists the function calls used to obtain the various pieces of session information.


Note:

For more information on the context APIs, see the PL/SQL API Reference. The API Reference can be found on Portal Center (http://portalcenter.oracle.com) or, if you downloaded PDK-PL/SQL (pdkplsql.zip), in ..\pdkplsql\pdk\plsql\doc.

Table 6-4 Context Information Function Calls

Session Information Function Call
Current user wwctx_api.get_user
Login status of user wwctx_api.is_logged_on
Login time wwctx_api.get_login_time
Language wwctx_api.get_nls_language
Current session id wwctx_api.get_sessionid
IP address of user client wwctx_api.get_ip_address
User schema wwctx_api.get_db_user
OracleAS Portal schema wwctx_api.get_product_schema
OracleAS Portal version wwctx_api.get_product_version

6.6.2 Using wwctx_api to Obtain Context Information

The services example, located in ..\pdkplsql\pdk\plsql\svcex in PDK-PL/SQL (pdkplsql.zip), illustrates how you can obtain session information using the wwwctx_api package. You can browse through this example as follows to see how the function calls are implemented in a portlet:

  1. Open the services_portlet.pkb file in an editor.

  2. Find the get_portlet_info function.

  3. Notice the usage of wwctx_api.get_user to derive the user information and set that value in the portlet information record:

    ...
        l_portlet.timeout              := null;
        l_portlet.timeout_msg          := null;
        l_portlet.created_on           := to_date('10/19/2000', 'MM/DD/YYYY');
        l_portlet.created_by           := wwctx_api.get_user;
        l_portlet.last_updated_on      := to_date('10/19/2000', 'MM/DD/YYYY');
        l_portlet.last_updated_by      := wwctx_api.get_user;
        l_portlet.has_show_edit_defaults := true;
        l_portlet.has_show_preview     := true;
        l_portlet.preference_store_path := PORTLET_PATH;
    ...
    
    
  4. wwctx_api.get_user is used similarly in various places throughout services_portlet.pkb. Search the code for other occurrences of wwctx_api.get_user.

  5. Another example of getting context information occurs in the is_runnable function:

    function is_runnable
    (
         p_provider_id in integer
        ,p_reference_path in varchar2
    )
    return boolean
    is
    begin
        --
        -- Portlet security check.  It allows the portlet to be visible
        -- if the user is logged on (that is, the current session is not a
        -- public session).
        --
        return wwctx_api.is_logged_on;
    end is_runnable;
    
    
  6. Close services_portlet.pkb. You can implement session context in similar fashion to what is illustrated here, but based upon your own functional requirements.

  7. Optionally, if you want to see this portlet on a page and it is not already in the Portlet Repository, refer to the instructions in Section 6.3.2, "Implementing the Provider Package" for information on how to add it.

  8. Once your portlet appears in the repository, you can add it to a page to test it. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.7 Implementing Portlet Security

Portlet security refers to the techniques and methods used by portlets to control their access by end users. The portlets leave authentication to OracleAS Portal and trust that the portal will return the portlet to the correct, validated user upon request.

OracleAS Portal strictly controls access to information and applications by assigning specific privileges to users and groups. Portal security services allow you to specify access control programmatically and check for the appropriate privileges at runtime. The security mechanisms used by portlets ensure that only authorized users gain access to these portlets. These security services are available through the wwsec_api package.

Portlet security is invoked when a portlet is displayed on a portal page and when a portlet is returned in a portlet list by the get_portlet_list function for database providers. Security services in the Portal framework have the following key features:

6.7.1 Using Security

To implement PL/SQL portlet security, the portal requires the function is_portlet_runnable be implemented by database providers. The actual implementation of this function is up to the application; that is, the security scheme that determines whether the current user has enough privileges to access the portlet is defined by the individual portlet implementation. The portal also requires the function get_portlet_list for database providers to return the set of portlets that are accessible by the current user.

6.7.1.1 Guidelines for Using the Security APIs

The portlet security mechanism may use the context and security subsystem APIs and infrastructure. The context APIs can be used to retrieve information about the current user. The security subsystem can be used to check the privileges of the current user.


Note:

For more information on the context and security subsystem APIs, see the PL/SQL API Reference. The API Reference can be found on Portal Center (http://portalcenter.oracle.com) or, if you downloaded PDK-PL/SQL (pdkplsql.zip), in ..\pdkplsql\pdk\plsql\doc.

While using these APIs, keep in mind the following:

  • Only authorized users should be able to see your portlet in the Add Portlet dialog. This objective can be accomplished by implementing the is_portlet_runnable function in the provider. You can also allow public access to your portlet.

  • If a portlet does not want to render itself to a user, it should return no HTML or return an exception that the page engine will ignore. It should not return an error message. Doing so adds unnecessarily to the error stack, which has its limits. Refer to Section 6.9, "Implementing Error Handling" for more information.

  • Portlet security allows the portlet to perform a runtime security check to ensure that the current user has the necessary authorization to access the portlet.

  • When a portlet is rendered in Show mode, it may call the is_runnable method for database providers to determine whether the portlet should be displayed for the currently logged on user. The portal does not make the call to this function directly. It is not a requirement, however, for the portlet to make this call. The portlet should make this call in its Show mode only if it implements portlet security.

  • The result of the call to is_runnable determines whether the portlet is actually displayed. If the result is true, the portlet displays, otherwise it does not display. The portlet is rendered in Show mode when it is displayed in a portal page.

  • When a portlet is returned in a portlet list by a call to the provider function get_portlet_list, the value of the p_security_level parameter determines the purpose of the function call. When the call is made from the Portlet Repository refresh operation in order to retrieve the master list of portlets that the provider implements, the parameter p_security_level has a value of false. This setting indicates to the provider that no portlet security check should be made and a master list of all the portlets that the provider implements must be returned. The master list of portlets returned in this case is used to populate the Portlet Repository for that provider.

  • If the value of p_security_level is true, then it is up to the provider implementation to decide whether portlet security should be performed. If portlet security is implemented, the provider may return a different list of portlets depending on the current user.

  • When the Portlet Repository is displayed, OracleAS Portal calls the is_portlet_runnable function for database providers for each of the portlets that exist in the Portlet Repository. This step is done to display only the portlets that the currently logged on user is authorized to see. One example where the Portlet Repository is displayed is in the Add Portlets dialog.

6.7.2 Coding Security

The services example, located in ..\pdkplsql\pdk\plsql\svcex in PDK-PL/SQL (pdkplsql.zip), illustrates how you can implement security. You can browse through this example as follows to see how the security functions are implemented in a portlet:

  1. Open the services_provider.pkb file in an editor.

  2. Find the is_portlet_runnable function. This function calls the security implementation through the portlet's is_runnable function to check portlet access privileges.

    function is_portlet_runnable
    (
        p_portlet_instance in wwpro_api_provider.portlet_instance_record
    )
    return boolean
    is
    begin
        if (p_portlet_instance.portlet_id = SERVICES_PORTLET_ID) then
            return services_portlet.is_runnable(
                 p_provider_id     =>  p_portlet_instance.provider_id
                ,p_reference_path  =>  p_portlet_instance.reference_path
                );
        else
            raise wwpro_api_provider.PORTLET_NOT_FOUND_EXCEPTION;
        end if;
    end is_portlet_runnable;
    
    
  3. Find the get_portlet_list procedure. get_portlet_list allows the portlet to be included in the list of portlets implemented by this provider. get_portlet_list first checks the security flag (p_security_level) to find out whether security is enabled. If the flag is set to true, get_portlet_list uses is_runnable to check whether the portlet is accessible. The value of the p_security_level parameter indicates whether to perform security checks before returning a portlet in the list. When a portlet repository refresh operation retrieves the master list of portlets implemented by the provider, p_security_level has a value of false. A value of false means the provider need not perform a security check and that a master list of all of the portlets implemented by the provider must be returned. The master list of portlets returned is used to populate the portlet repository for that provider. If the value of p_security_level is true, then the provider implementation decides whether to perform portlet security checks. If portlet security is implemented, the provider may return a different list of portlets depending on the currently logged on user.

    function get_portlet_list
    ...
        if (p_security_level = false) then
            l_cnt := l_cnt + 1;
            l_portlet_list(l_cnt) := get_portlet(
                 p_provider_id  => p_provider_id
                ,p_portlet_id   => SERVICES_PORTLET_ID
                ,p_language     => p_language
                );
        else
            if (services_portlet.is_runnable(
                 p_provider_id     =>  p_provider_id
                ,p_reference_path  =>  null)
            ) then
                l_cnt := l_cnt + 1;
                l_portlet_list(l_cnt) := get_portlet(
                     p_provider_id  => p_provider_id
                    ,p_portlet_id   => SERVICES_PORTLET_ID
                    ,p_language     => p_language
                    );        end if;
    ...
    end get_portlet_list;
    
    
  4. Open the services_portlet.pkb file in an editor.

  5. Find the show procedure. Before displaying a portlet, the show procedure runs a security check to determine whether the current user is allowed to see the portlet.

    procedure show
    ...
        -- Perform a security check
        if (not is_runnable(
             p_provider_id     =>  p_portlet_record.provider_id
            ,p_reference_path  =>  p_portlet_record.reference_path)
        ) then
            wwerr_api_error.add(
                      DOMAIN, SUBDOMAIN,
                      'securityerr', 'services_portlet.show');
            raise wwpro_api_provider.PORTLET_SECURITY_EXCEPTION;    end if;
    ...
    end show;
    
    
  6. Find the is_runnable function. is_runnable is the place where you implement your security checks. In this example, the security check is quite simple. If the user is logged on (that is, not in a public session), then the function returns true and the portlet is displayed to the user. For your own purposes, you could, of course, code much more complex security checks in the is_runnable function.

    function is_runnable
    (
         p_provider_id in integer
        ,p_reference_path in varchar2
    )
    return boolean
    is
    begin
        --
        -- Portlet security check.  It allows the portlet to be visible
        -- if the user is logged on (that is, the current session is not a
        -- public session).
        --
        return wwctx_api.is_logged_on;
    end is_runnable;
    
    
  7. Optionally, if you want to see this portlet on a page and it is not already in the Portlet Repository, refer to the instructions in Section 6.3.2, "Implementing the Provider Package" for information on how to add it.

  8. Once your portlet appears in the repository, you can add it to a page to test it. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.8 Improving Portlet Performance with Caching

OracleAS Portal provides for the caching of PL/SQL portlets. This functionality permits PL/SQL portlets to cache their Web content on the middle tier. Subsequent requests for the content may be retrieved from the cache, with or without validation from the database, decreasing the database workload.

OracleAS Portal provides three types of caching for your PL/SQL portlets:

Because OracleAS Portal supports user customization of pages and portlets, the view of a page can vary from one user to another. OracleAS Portal's caching is designed to allow content to vary on a per-user basis, even if the URL is the same across all users. Therefore, portal objects can be cached at either the user level or the system level:

When a database provider issues a request for a portlet, the request is sent to the portlets's show procedure. This procedure accepts the portlet_runtime_record as a parameter. This record structure contains fields that can be examined and set by the portlet to enable caching. The caching control fields of this record are:

6.8.1 Using Caching

The general model for working with portlet caching varies according to the type of caching you choose. To a great extent, the type of caching you choose depends on the portlet content. If the portlet content changes at fairly regular intervals (for example, at the close of business every day), then it probably makes sense to use expiry-based caching. If the portlet content changes at irregular intervals, then validation- or invalidation-based caching is probably best.

6.8.1.1 Validation-Based Caching

If you choose validation-based caching, the general model is as follows:

  1. Set the caching_key field of the portlet_runtime_record parameter. Add a check to compare the value of the current key with the value of the caching_key field of the portlet_runtime_record parameter. Note that the first time the show procedure is called, the key is null and its value must be set.

  2. Determine whether you want to use system or user level caching. Set the caching_level field of the portlet_runtime_record parameter accordingly.

6.8.1.2 Expiry-Based Caching

If you choose expiry-based caching, the general model is as follows:

  1. Set the caching_period field of the portlet_runtime_record parameter to the desired interval for the cache (in minutes).

  2. Determine whether you want to use system or user level caching. Set the caching_level field of the portlet_runtime_record parameter accordingly.

6.8.1.3 Invalidation-Based Caching

If you choose invalidation-based caching, the general model is as follows:

  1. Indicate to OracleAS Portal that it must generate specific headers for OracleAS Web Cache by calling wwpro_api_provider.USE_INVALIDATION.

  2. Determine whether you want to use system or user level caching. Set the caching_level field of the portlet_runtime_record parameter accordingly.

  3. Optionally, set up validation- or expiry-based caching as well.

  4. Add invalidation logic to your portlet where needed (for example, when the portlet is customized) and make appropriate calls to wwpro_api_invalidation.

6.8.2 Configuring and Monitoring the Cache

The Oracle Application Server Portal Configuration Guide describes how to configure caching as well as how to monitor and tune performance.

6.8.3 Implementing Validation-Based Caching

The caching example, located in ..\pdkplsql\pdk\plsql\cache in PDK-PL/SQL (pdkplsql.zip), illustrates how you can implement validation and expiry-based caching. You can browse through this example as follows to see how the validation-based functions are implemented in a portlet:

  1. Open the validcache_portlet.pkb file in an editor.

  2. At the very top of the file, notice the aliases for the caching level constants.

    CREATE OR REPLACE
    package body VALIDCACHE_PORTLET
    is
        -- Caching Constants
        CACHE_LEVEL_SYSTEM constant varchar2(10) := 'SYSTEM';
        CACHE_LEVEL_USER   constant varchar2(10) := 'USER';
    
    
  3. Find the show procedure. Notice first that the p_portlet_record is an in and out parameter for this procedure.

    procedure show
    (
        p_portlet_record   in out     wwpro_api_provider.portlet_runtime_record
    )
    
    
  4. In the procedure's security check, the caching fields of p_portlet_record are set to null if the security check fails.

    begin
        if (not is_runnable(
             p_provider_id      => p_portlet_record.provider_id
            ,p_reference_path   => p_portlet_record.reference_path)
        ) then
            -- Set it to null so that cache does not get used even if exists
            p_portlet_record.caching_level := null;
            p_portlet_record.caching_key := null;
            raise wwpro_api_provider.PORTLET_SECURITY_EXCEPTION;    end if;
    
    
  5. After that, the procedure calls the get_cache_key function to get the cache key's value and assign it to a temporary value:

        --
        -- CACHE IS VALID?
        --
        l_cache_key := get_cache_key();
    
    
  6. Find the get_cache_key function, which is referenced from the show procedure. This function generates a key for the portlet. You can implement your own logic here based upon your portlet's requirements.

    function get_cache_key return varchar2 is    l_date date;begin    select sysdate into l_date from  dual;    return trim(substr(to_char(l_date, 'YYYY:MM:DD:HH:MI:SS'),1,18));exception    when others then        null;end get_cache_key;
    
    
  7. Now return to the show procedure. Notice how the code checks your portlet_runtime_record parameter for the current values of the caching_key and the caching_level. This same piece of code can compare your caching_key values.

        if p_portlet_record.caching_level = CACHE_LEVEL_SYSTEM then
            if l_cache_key is not null then
                -- Cache exists for the user, overwrite it
                p_portlet_record.caching_level := CACHE_LEVEL_USER;
                p_portlet_record.caching_key := l_cache_key;
            else
                return; -- System cache is still valid.
            end if;
        elsif p_portlet_record.caching_level = CACHE_LEVEL_USER then
            if p_portlet_record.caching_key != l_cache_key then
                -- cache has expired. reset it
                p_portlet_record.caching_key := l_cache_key;
            else 
                return; -- User cache is good as gold
            end if;
        elsif p_portlet_record.caching_level is null then
            if p_portlet_record.caching_key is not null then
                -- Cache does not exists for the user, create it
                p_portlet_record.caching_level := CACHE_LEVEL_USER;
                p_portlet_record.caching_key := l_cache_key;
            else
                -- Define a sytem cache. This can happen only once!
                -- the first time the portlet is rendered.
                p_portlet_record.caching_level := CACHE_LEVEL_SYSTEM;
                p_portlet_record.caching_key := 'MY_INITIAL_CACHE_KEY';   
            end if;
        else 
            p_portlet_record.caching_level := CACHE_LEVEL_SYSTEM;
            p_portlet_record.caching_key := 'MY_INITIAL_CACHE_KEY';
        end if;
    
    
  8. Optionally, if you want to see this portlet on a page and it is not already in the Portlet Repository, refer to the instructions in Section 6.3.2, "Implementing the Provider Package" for information on how to add it.

  9. Once your portlet appears in the repository, you can add it to a page to test it. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.8.4 Implementing Expiry-Based Caching

The caching example, located in ..\pdkplsql\pdk\plsql\cache in PDK-PL/SQL (pdkplsql.zip), illustrates how you can implement expiry-based caching. You can browse through this example as follows to see how the expiry-based functions are implemented in a portlet:

  1. Open the expirycache_portlet.pkb file in an editor.

  2. At the very top of the file, notice the aliases for the caching level constants.

    CREATE OR REPLACE
    package body VALIDCACHE_PORTLET
    is
        -- Caching Constants
        CACHE_LEVEL_SYSTEM constant varchar2(10) := 'SYSTEM';
        CACHE_LEVEL_USER   constant varchar2(10) := 'USER';
    
    
  3. Find the show procedure. Notice first that the p_portlet_record is an in and out parameter for this procedure.

    procedure show
    (
        p_portlet_record   in out     wwpro_api_provider.portlet_runtime_record
    )
    
    
  4. In the procedure's security check, the caching fields of p_portlet_record are set to null if the security check fails.

    begin
        if (not is_runnable(
             p_provider_id      => p_portlet_record.provider_id
            ,p_reference_path   => p_portlet_record.reference_path)
        ) then
            -- Set it to null so that cache does not get used even if exists
            p_portlet_record.caching_level := null;
            p_portlet_record.caching_key := null;
            raise wwpro_api_provider.PORTLET_SECURITY_EXCEPTION;    end if;
    
    
  5. After that, the procedure sets the value of the caching period in minutes in a temporary variable. The get_cache_key function to get the cache key's value and assign it to a temporary value:

       -- Set the Caching Period to one minute 
       l_cache_period := 1;
    
    
  6. Next, notice how the code checks your portlet_runtime_record parameter for the current values of the caching_period and sets the caching_period accordingly. This same piece of code can compare your caching_period values.

       if p_portlet_record.caching_level = CACHE_LEVEL_SYSTEM then
                -- Cache does not exists for the user, create it
                p_portlet_record.caching_level := CACHE_LEVEL_USER;
                p_portlet_record.caching_period := l_cache_period;
       elsif p_portlet_record.caching_level = CACHE_LEVEL_USER then
                -- Cache exists for the user, overwrite it
                p_portlet_record.caching_period := l_cache_period;
       elsif p_portlet_record.caching_level is null then
            if p_portlet_record.caching_period  is not null then
                -- Cache does not exists for the user, create it
                p_portlet_record.caching_level := CACHE_LEVEL_USER;
                p_portlet_record.caching_period := l_cache_period;
            else
                -- Define a sytem cache. This can happen only once!
                p_portlet_record.caching_level := CACHE_LEVEL_SYSTEM;
                p_portlet_record.caching_period := l_cache_period;   
            end if;
       else -- p_portlet_record.caching_level value is messed up!
            p_portlet_record.caching_level := CACHE_LEVEL_SYSTEM;
            p_portlet_record.caching_period := l_cache_period;   end if;
    
    
  7. Optionally, if you want to see this portlet on a page and it is not already in the Portlet Repository, refer to the instructions in Section 6.3.2, "Implementing the Provider Package" for information on how to add it.

  8. Once your portlet appears in the repository, you can add it to a page to test it. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.8.5 Implementing Invalidation-Based Caching

Suppose you have a portlet that displays a map of the world, map_portlet.pkb and map_portlet.pks. You would go about adding invalidation-based functions to it as follows:

  1. In the show procedure, you need to add a call to wwpro_api_provider.use_invalidation. This call indicates to OracleAS Portal that the portlet content should be cached by OracleAS Web Cache. Note that we have also specified that the content be cached at the user level and that expiry-based caching be used as well (that is, an expiration interval of one minute has been set).

    procedure show
    ...
        if (p_portlet_record.exec_mode = wwpro_api_provider.MODE_SHOW) then
            p_portlet_record.caching_invalidation := 
              wwpro_api_provider.use_invalidation;
            p_portlet_record.caching_level := 'USER';
            p_portlet_record.caching_period := 1;
    ...
    
    
  2. Create a procedure in your map_portlet.pkb file that invalidates the cache. For example:

    procedure map_invalidation
    (  
    p_provider_id in number,
    p_portlet_id in number,
    p_instance_id in varchar2,
    p_page_url in varchar2
    )
    is
    begin
     wwpro_api_invalidation.invalidate_by_instance
      (p_provider_id => p_provider_id,
       p_portlet_id =>  p_portlet_id,
       p_instance_id => p_instance_id,
       p_user => wwctx_api.get_user);
     owa_util.redirect_url(p_page_url);
    end map_invalidation;
    
    
  3. In the show procedure, add a link for refreshing the portlet before the code that draws the map. For example:

    /* Draw the Refresh Me link */
    htp.anchor(
      curl => wwctx_api.get_user||
        '.map_invalidation?p_provider_id='||p_portlet_record.provider_id||
        '&p_portlet_id='||p_portlet_record.portlet_id||
        '&p_instance_id='||p_portlet_record.reference_path||
        '&p_page_url='||utl_url.escape(
                        url => p_portlet_record.page_url,
                        escape_reserved_chars => TRUE),
      ctext => wwui_api_portlet.portlet_text(
        p_string =>'Refresh Me',
        p_level => 1)
    );
    
    
  4. Optionally, if you want to see this portlet on a page and it is not already in the Portlet Repository, refer to the instructions in Section 6.3.2, "Implementing the Provider Package" for information on how to add it.

  5. Once your portlet appears in the repository, you can add it to a page to test it. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.9 Implementing Error Handling

OracleAS Portal provides the capability for you to trap erroneous input and return meaningful error messages. It manages the internal error stack by tracking the raised exceptions and retaining information about them. OracleAS Portal also includes a set of APIs for presenting errors in a standardized way.

Error handling services are available through the wwerr_api_error and wwerr_api_error_ui packages. These error handling services include the following key features:

6.9.1 Using Error Handling

In general, you set up error handling as follows:

  1. On detecting error conditions, add the error message, with an appropriate domain and sub-domain combination, to the stack using the wwerr_api_error.add procedure.

  2. When necessary (for example, at the end of a routine), expose the error messages using the wwerr_api_error_ui procedures. To display full screen messages, use the procedures show_html, show_xml, or show_text depending on your preferred output type. To display inline messages, use the procedures show_inline_html, show_inline_xml, or show_inline_text, depending on the output type you desire.

6.9.1.1 Guidelines for Error Handling

While implementing error handling, keep in mind the following:

  • While defining your own error messages, use your own error domain for these messages. Never use the WWC, WWV, or WWS domain for your error messages. You will need to write a small loader script to load these into the other language tables.

  • Avoid unnecessary error messages. If you do not want to do anything in a function, just return null rather than an error. For example, suppose you are coding a copy_portlet procedure for your portlet because the provider calls it for all of its other portlets. If you do not wish the copy_portlet procedure for this particular portlet to do anything, then simply have it return null. If you return errors, it will unnecessarily disrupt the portlet functionality.

  • A maximum of ten error messages is kept on the stack. Beyond ten, messages are ignored when a call to wwerr_api_error.add is made.

  • Use the API as a programmatic way of finding the problem. You can use the non-user-interface format for this purpose. For example, when programmatically registering a provider, the exception block can use get_text_stack to get the error messages and print them. This approach helps when debugging calls to public APIs since all of them add errors to the stack for exceptions.

  • Remember to seed the other language strings for your error messages. For more information, refer to Section 6.11, "Writing Multi-Lingual Portlets".

  • The standard user interface for error messages provides a navigation link back to the previous page. It also includes a Help icon for the specified help URL.

6.9.2 Adding Error Handling

The services example, located in ..\pdkplsql\pdk\plsql\svcex in PDK-PL/SQL (pdkplsql.zip), illustrates how you can implement error handling. You can browse through this example as follows to see how the error handling functions are implemented in a portlet:

  1. Open the services_portlet.pkb file in an editor.

  2. The domain and subdomain definitions for your error messages are provided with aliases in the constants part of your portlet definition.

    DOMAIN           constant varchar2(30) := 'provider';
    SUBDOMAIN        constant varchar2(32) := 'services';
    PORTLET_PATH     constant varchar2(256):= 'oracle.portal.pdk.servicesportlet';
    PREFNAME_STRING  constant varchar2(30) := 'services_string';
    PREFNAME_TITLE   constant varchar2(30) := 'services_title';
    
    
  3. Find the show procedure. This procedure performs a security check and, if an error condition arises, it calls wwerr_api_error.add to push the securityerr error message onto the stack.

    procedure show
    (
        p_portlet_record        wwpro_api_provider.portlet_runtime_record
    )
    is
    ...
    begin
        -- Perform a security check
        if (not is_runnable(
             p_provider_id     =>  p_portlet_record.provider_id
            ,p_reference_path  =>  p_portlet_record.reference_path)
        ) then
            wwerr_api_error.add(
                      DOMAIN, SUBDOMAIN,
                      'securityerr', 'services_portlet.show');
            raise wwpro_api_provider.PORTLET_SECURITY_EXCEPTION;
        end if;
    
    
  4. The show procedure also checks for any other kind of execution mode and generates an appropriate error message for an invalid display mode.

    if (p_portlet_record.exec_mode = wwpro_api_provider.MODE_SHOW) then
    ...
    elsif (p_portlet_record.exec_mode = wwpro_api_provider.MODE_SHOW_EDIT)
    ...
    else
        wwerr_api_error.add(DOMAIN, SUBDOMAIN,
            'invaliddispmode', 'services_portlet.show');
        raise wwpro_api_provider.PORTLET_EXECUTION_EXCEPTION;
    end if;
    
    
  5. Lastly, the show procedure implements a general error message in the exception handler to catch any errors not trapped by the preceding conditions.

    exception
        when others then
            wwerr_api_error.add(
                DOMAIN, SUBDOMAIN,
                'generalerr', 'services_portlet.show');
            raise wwpro_api_provider.PORTLET_EXECUTION_EXCEPTION;
    end show;
    
    
  6. Error handling is also implemented in the save_prefs and save_default_prefs procedures. They check whether the error stack is empty and, if it is not, the portlet makes a call to wwerr_api_error.show_html to display the error in full screen mode.

    exception
        when INVALID_TEXT_EXCEPTION then
             l_information := l_user||'%'||l_time
                 ||'%INVALID_TEXT_EXCEPTION%'||p_string;
             l_action      := LOG_FAILED;
             wwlog_api.log (p_domain      => DOMAIN,
                            p_subdomain   => SUBDOMAIN,
                            p_name        => l_user,
                            p_action      => l_action,
                            p_information => l_information,
                            p_url         => l_url,
                            p_row_count   => 0,
                            p_elapsed_time=> l_elapsed_time);
             wwerr_api_error.add(DOMAIN, SUBDOMAIN,
                 'invalid_text', 'services_portlet.save_prefs');
         if (not wwerr_api_error.is_empty) then
             wwerr_api_error_ui.show_html;
         end if;
    end save_prefs;
    
    
  7. Optionally, if you want to see this portlet on a page and it is not already in the Portlet Repository, refer to the instructions in Section 6.3.2, "Implementing the Provider Package" for information on how to add it.

  8. Once your portlet appears in the repository, you can add it to a page to test it. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.10 Implementing Event Logging

OracleAS Portal can log events that occur during transactions with its objects. It stores these logs in the database, which makes them available through standard SQL calls and reporting tools.

You can choose the events you would like to log and organize them categorically based on user-defined domains and subdomains. For the logged events, you can view information about the event, the time the event started and stopped, the host or IP address of the remote user, the browser type, and the language.

Event logging services are available through the wwlog_api and wwlog_api_admin packages. These services include the following key features:

6.10.1 Using Event Logging

In general, you can set up event logging as follows:

  1. Add the event object, with an appropriate domain and subdomain combination, using wwlog_api_admin.add_log_event. Adding the event ensures that lists of values and other user interface components invoked when the user is monitoring the events show this new event in their lists.

  2. Register the log event record by using wwlog_api_admin.add_log_registry. The log registry record represents the events you want to log in the future and provides a means to filter the events that need to be logged.

  3. Use start_log and stop_log to mark the events you want to log in your code. Alternatively, for entering single step event log information, just call the log method to mark that event.

6.10.1.1 Guidelines for Event Logging

While implementing event logging, keep in mind the following:

  • Log only what you really care about to improve performance. You don't want to flood the system with log messages about which you do not care. If events are logged in Show mode, then multiple instances of these portlets mean additional hits to the database.

  • Choose your domain, subdomain, and log events carefully. While using the log APIs, do not use the OracleAS Portal domains like WWC, WWV, or WWS for your log messages. Organize your domains and subdomains hierarchically ensuring that they are unique across portlets. If other portlets happen to use the same domains or subdomains, you will see those log messages interspersed with your own.

  • Create log events that show up in the pop-up lists of values monitoring the logs. You can simply create log registry records that filter the events that would be actually logged, either by specifying particular events or using the generic filters with wild cards (%). Apart from creating log registry records, we recommend that you create log events for events that you want to monitor. This way the lists of values in the user interface show these records for additional functions like monitoring.

  • Provide required privileges to users or user groups who need to monitor the logs. Any logs created by a user can be viewed by that user, the Portal Administrator, and any user with the Edit privilege on the ANY_LOGS object type.

6.10.2 Adding Event Logging

The services example, located in ..\pdkplsql\pdk\plsql\svcex in PDK-PL/SQL (pdkplsql.zip), illustrates how you can implement event logging. You can browse through this example as follows to see how the event logging functions are implemented in a portlet:

  1. Open the services_portlet.pkb file in an editor.

  2. The domain and subdomain definitions for your log messages are provided with aliases in the constants part of your portlet definition.

    DOMAIN           constant varchar2(30) := 'provider';
    SUBDOMAIN        constant varchar2(32) := 'services';
    PORTLET_PATH     constant varchar2(256):= 'oracle.portal.pdk.servicesportlet';
    PREFNAME_STRING  constant varchar2(30) := 'services_string';
    PREFNAME_TITLE   constant varchar2(30) := 'services_title';
    
    
  3. Find the save_prefs procedure. This procedure provides customizable functionality where you can personalize text and the portlet title in Edit mode. save_prefs stores these customizations in the database. While saving the changes, it is advisable to log them. Hence, this procedure provides an ideal example of implementing the logging service. A single step event is logged using wwlog_api.log. The first instance of wwlog_api.log logs the event of personalizing text. The second instance logs the event of personalizing the portlet title.

    procedure save_prefs
    ...
    begin
    ...
        if (l_prefs.string_id is null or to_number(l_prefs.string_id) = 0)
        then
            l_action := LOG_INSERT;
    ...
        else  -- string exists in at least one language so update it
            l_action := LOG_UPDATE;
    ...
        end if;
    -- Log this transaction
    l_information := l_user||'%'||l_time||'%completed%'||p_string;
            wwlog_api.log (p_domain      => DOMAIN,
                           p_subdomain   => SUBDOMAIN,
                           p_name        => l_user,
                           p_action      => l_action,
                           p_information => l_information,
                           p_url         => l_url,
                           p_row_count   => l_row_count,
                           p_elapsed_time=> l_elapsed_time);
    ...
        if (l_prefs.title_id is null or to_number(l_prefs.title_id) = 0)
        then
           l_action := LOG_INSERT;
    ...
        else
           l_action := LOG_UPDATE;
    ...
    -- Log this transaction       l_information := l_user||'%'||l_time||'%completed%'||p_title;
           wwlog_api.log (p_domain      => DOMAIN,
                           p_subdomain   => SUBDOMAIN,
                           p_name        => l_user,
                           p_action      => l_action,
                           p_information => l_information,
                           p_url         => l_url,
                           p_row_count   => l_row_count,
                           p_elapsed_time=> l_elapsed_time);
    ...
    end save_prefs;
    
    
  4. The save_prefs procedure also logs an event with wwlog_api.log when an exception occurs.

    exception
        when INVALID_TEXT_EXCEPTION then
             l_information := l_user||'%'||l_time
                 ||'%INVALID_TEXT_EXCEPTION%'||p_string;
             l_action      := LOG_FAILED;
             wwlog_api.log (p_domain      => DOMAIN,
                            p_subdomain   => SUBDOMAIN,
                            p_name        => l_user,
                            p_action      => l_action,
                            p_information => l_information,
                            p_url         => l_url,
                            p_row_count   => 0,
                            p_elapsed_time=> l_elapsed_time);
    ...
    
    
  5. Optionally, if you want to see this portlet on a page and it is not already in the Portlet Repository, refer to the instructions in Section 6.3.2, "Implementing the Provider Package" for information on how to add it.

  6. Once your portlet appears in the repository, you can add it to a page to test it. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.

6.11 Writing Multi-Lingual Portlets

OracleAS Portal has a robust set of APIs for interacting with OracleAS Portal multi-lingual storage facility. This storage facility provides a mechanism for the storing and retrieving of strings in different languages. These APIs abstract the native multi-lingual functionality and provide developers with a powerful storage mechanism for developing providers that support different language environments.

Multi-lingual services are available through the wwnls_api package. These services include the following key features:

For example, assume that the provider's register procedure loads US and French translations for the portlet title. When the portlet is rendered, the provider implementation retrieves the portlet title string from the table and displays the following results:

6.11.1 Using Multi-Lingual Support

In general, you can set up multi-lingual support as follows:

  1. Load your string definitions into the database using the string equivalents for each language you intend to use. For this purpose, call the wwnls_api.add_string or wwnls_api.set_string with an appropriate domain, subdomain, error message name, and error text combination.

  2. Retrieve the strings you require with wwnls_api.get_string for the language that you desire.

6.11.2 Adding Multi-Lingual Support

To add multi-lingual support, you need to perform the following tasks:

6.11.2.1 Loading Language Strings

Language strings can be loaded by a script that is part of the provider installation. This script calls add_string and set_string to create equivalent strings for different languages.

OracleAS Portal uniquely identifies language strings using a combination of domain, subdomain, and name. The domain and subdomain provide a way to categorize the strings. The domain and subdomain should be unique enough to reasonably preclude conflicts with other users of the APIs.

  • A domain is a particular area of the product. An example of a domain could be provider or page group.

  • A subdomain is a subsystem of the domain. For example, the subdomain could be the provider name (for example, HelloProvider) or subpage name (for example, HelloPage).

The services example, located in ..\pdkplsql\pdk\plsql\svcex in PDK-PL/SQL (pdkplsql.zip), illustrates how you can implement multi-lingual support. You can browse through this example as follows to see how to load strings for multi-lingual support:

  1. Open the services_seed.sql file in an editor.

  2. Notice the add_string call with the parameters for domain name, subdomain name, string name, language, and the actual string text. It returns the String ID for the language string. For setting equivalent strings in other languages, set_string is called with the same parameters.

    set serveroutput on size 1000000
    set define off
    
    declare
        l_string_id  integer;
        l_person_id  integer;
        l_group_id   integer;
    begin
    ...
    -- strings for portlet record fields
    l_string_id := wwnls_api.add_string( 
     'provider','services','ptldefname','us','DatabaseServicesPortlet');
    wwnls_api.set_string(
     'provider','services','ptldefname','d','DatenbankServicesPortlet-d');
    l_string_id := wwnls_api.add_string( 
     'provider','services','ptldeftitle','us','Database Services Portlet');
    wwnls_api.set_string(
     'provider','services','ptldeftitle','d','Datenbank Services Portlet - d');
    l_string_id := wwnls_api.add_string( 
     'provider','services','ptldefdesc','us','This is the database services
    portlet implemented in PL/SQL. It displays 6 show modes.');
    wwnls_api.set_string(
     'provider','services','ptldefdesc','d','Dies ist das Datenbank Service
    Portlet, erstellt in PL/SQL. Es stellt 6 Anzeigemodi dar. - d');
    l_string_id := wwnls_api.add_string( 
     'provider','services','ptldevtmmsg','us','Web Services Portlet Timed
    Out.');
    wwnls_api.set_string(
     'provider','services','ptldevtmmsg','d','Zeitüeberschreitung aufgetreten
    in Web Services Portlet. -d');
    

6.11.2.2 Retrieving Language Strings

The services example, located in ..\pdkplsql\pdk\plsql\svcex in PDK-PL/SQL (pdkplsql.zip), illustrates how you can implement multi-lingual support. You can browse through this example as follows to see how to retrieve strings for multi-lingual support:

  1. Open the services_portlet.pkb file in an editor.

  2. The domain and subdomain definitions for your language strings are provided with aliases in the constants part of your portlet definition.

    DOMAIN           constant varchar2(30) := 'provider';
    SUBDOMAIN        constant varchar2(32) := 'services';
    PORTLET_PATH     constant varchar2(256):= 'oracle.portal.pdk.servicesportlet';
    PREFNAME_STRING  constant varchar2(30) := 'services_string';
    PREFNAME_TITLE   constant varchar2(30) := 'services_title';
    
    
  3. Find the get_portlet_info procedure. Notice the calls to wwnls_api.get_string to populate the portlet title, name, and description.

    function get_portlet_info
    (
         p_provider_id in integer
        ,p_language in varchar2
    )
    return wwpro_api_provider.portlet_record
    is
         l_portlet  wwpro_api_provider.portlet_record;
    begin
        l_portlet.id := services_provider.SERVICES_PORTLET_ID;
        l_portlet.provider_id := p_provider_id;
        l_portlet.language := p_language;
        l_portlet.title :=
            wwnls_api.get_string(
                 p_domain       =>  DOMAIN
                ,p_sub_domain   =>  SUBDOMAIN
                ,p_name         =>  'ptldeftitle'
                ,p_language     =>  p_language
                );
        l_portlet.description :=
            wwnls_api.get_string(
                 p_domain       =>  DOMAIN
                ,p_sub_domain   =>  SUBDOMAIN
                ,p_name         =>  'ptldefdesc'
                ,p_language     =>  p_language
                );
        l_portlet.name :=
            wwnls_api.get_string(
                 p_domain       =>  DOMAIN
                ,p_sub_domain   =>  SUBDOMAIN
                ,p_name         =>  'ptldefname'
                ,p_language     =>  p_language
                );
    ...
    
    
  4. Browse the rest of the file to examine other usage examples ofwwnls_api.get_string, which is used in several other places in services_portlet.pkb.

  5. Optionally, if you want to see this portlet on a page and it is not already in the Portlet Repository, refer to the instructions in Section 6.3.2, "Implementing the Provider Package" for information on how to add it.

  6. Once your portlet appears in the repository, you can add it to a page to test it. To add your portlet to a page, follow the instructions in Section 7.6.2, "Adding Portlets," of the Oracle Application Server Portal User's Guide.